func Test_PythonPlugin_builtins(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() // this isn't really the builtin Fubsy println() function: we can't // use it because it's in the runtime package, and we don't want // to because it has side-effects... but we're stuck with a // hardcoded set of builtin function names for now, so we have to // reuse one of them calls := []string{} fn_println := func(args types.ArgSource) (types.FuObject, []error) { s := args.Args()[0].ValueString() calls = append(calls, s) return nil, nil } builtins := StubBuiltinList{types.NewFixedFunction("println", 1, fn_println)} pp, err := LoadMetaPlugin("python2", builtins) testutils.NoError(t, err) values, err := pp.Run(` fubsy.println("ding") fubsy.println("dong") `) _ = values expect := []string{"ding", "dong"} testutils.NoError(t, err) assert.Equal(t, expect, calls) }
func Test_KyotoDB_key_prefix(t *testing.T) { // make sure we write the key exactly as expected, byte-for-byte cleanup := testutils.Chtemp() defer cleanup() db, err := OpenKyotoDB("test1.kch", true) if err != nil { t.Fatal(err) } assert.NotNil(t, db.kcdb) rec1 := NewBuildRecord() rec1.SetTargetSignature([]byte{}) db.WriteNode("f", rec1) db.WriteNode("foobar", rec1) db.WriteNode("foo", rec1) // hmmm: does Kyoto Cabinet guarantee key order? it seems to // preserve insertion order from what I can tell, but I'm not // sure if that's reliable expect := []string{ "\x00\x00\x00\x00version", "\x00\x00\x00\x01f", "\x00\x00\x00\x01foobar", "\x00\x00\x00\x01foo", } keychan := db.kcdb.Keys() for i, expectstr := range expect { expectkey := ([]byte)(expectstr) actualkey := <-keychan if !bytes.Equal(expectkey, actualkey) { t.Errorf("key %d: expected\n%v\nbut got\n%v", i, expectkey, actualkey) } } }
func Test_FinderNode_Expand_single_include(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() testutils.TouchFiles( "lib1/foo.c", "lib1/sub/blah.c", "include/bop.h", "include/bip.h") finder := NewFinderNode("*/*.c") assertExpand(t, nil, []string{"lib1/foo.c"}, finder) finder = NewFinderNode("**/*.c") assertExpand(t, nil, []string{"lib1/foo.c", "lib1/sub/blah.c"}, finder) finder = NewFinderNode("l?b?/**/*.c") assertExpand(t, nil, []string{"lib1/foo.c", "lib1/sub/blah.c"}, finder) finder = NewFinderNode("in?lu?e/*.h") assertExpand(t, nil, []string{"include/bip.h", "include/bop.h"}, finder) finder = NewFinderNode("inc*/?i*.h") assertExpand(t, nil, []string{"include/bip.h"}, finder) // adding new files changes nothing, because FinderNode caches the // result of Expand() testutils.TouchFiles("include/mip.h", "include/fibbb.h") assertExpand(t, nil, []string{"include/bip.h"}, finder) // but a new FileFinder instance will see them finder = NewFinderNode("inc*/?i*.h") assertExpand(t, nil, []string{"include/bip.h", "include/fibbb.h", "include/mip.h"}, finder) }
func Test_FinderNode_Expand_vars(t *testing.T) { // imagine code like this: // srcdir = "src/stuff" // files = <$srcdir/*.c> // "myapp": "$srcdir/main.c" { // "cc -c $files" // } // ...i.e. a FinderNode that is not in the DAG, so variable // references do not get expanded by DAG.ExpandNodes(). This is // clearly a bogus build script, but that's beside the point. We // need to ensure that the wildcard expanded is not "$srcdir/*.c" // but "src/stuff/*.c". cleanup := testutils.Chtemp() defer cleanup() testutils.TouchFiles( "lib1/foo.c", "lib1/sub/blah.c", "include/bop.h", "include/bip.h") ns := types.NewValueMap() ns.Assign("libsrc", types.MakeFuString("lib1")) finder := NewFinderNode("$libsrc/**/*.c") expect := []string{ "lib1/foo.c", "lib1/sub/blah.c", } assertExpand(t, ns, expect, finder) }
func Test_FinderNode_FindFiles_prune(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() testutils.TouchFiles( "src/a/1.c", "src/a/2.c", "src/a/2.h", "src/a/3.h", "src/b/1.c", "src/b/2.c", "src/b/2.h", "src/b/3.h", "src/b/b/1.c", "src/b/b/2.c", "src/b/b/2.h", "lib/x.c", "lib/sub/x.c") var finder *FinderNode var expect []string test := func(expect []string) { actual, err := finder.FindFiles() assert.Nil(t, err) if !reflect.DeepEqual(expect, actual) { t.Errorf("includes = %v, prune = %v:\nexpected:\n%#v\nbut got:\n%#v", finder.includes, finder.prune, expect, actual) } // wipe the cache so this finder can be used again finder.matches = nil } finder = NewFinderNode("src/**/*.c", "src/b/**/*.h") finder.Prune("src/a") expect = []string{ "src/b/1.c", "src/b/2.c", "src/b/b/1.c", "src/b/b/2.c", "src/b/2.h", "src/b/3.h", "src/b/b/2.h"} test(expect) // successive calls to Prune() build up the prune set finder = NewFinderNode("*/*.c") finder.Prune("src") expect = []string{"lib/x.c"} test(expect) finder.Prune("lib") expect = []string{} test(expect) finder = NewFinderNode("*/*/?.c") expect = []string{ "lib/sub/x.c", "src/a/1.c", "src/a/2.c", "src/b/1.c", "src/b/2.c"} test(expect) finder.Prune("src/b") expect = []string{ "lib/sub/x.c", "src/a/1.c", "src/a/2.c"} test(expect) finder = NewFinderNode("**/b/?.h") expect = []string{"src/b/2.h", "src/b/3.h", "src/b/b/2.h"} test(expect) finder.Prune("src/b/b") expect = []string{"src/b/2.h", "src/b/3.h"} test(expect) finder.Prune("src/b") expect = []string{} test(expect) }
func Test_mkdir(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() // mkdir() happily accepts an empty argument list, to allow for // cases where a user-defined list becomes the arg list, and it // just happens to be empty pargs := []types.FuObject{} args := RuntimeArgs{ BasicArgs: types.MakeBasicArgs(nil, pargs, nil), } result, errs := fn_mkdir(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) assert.Equal(t, []string{}, dirContents(".")) // easiest case: create a single dir pargs = types.MakeStringList("foo").List() args.SetArgs(pargs) result, errs = fn_mkdir(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) assert.Equal(t, []string{"foo"}, dirContents(".")) assert.True(t, isDir("foo")) // create multiple dirs, including "foo" which already exists pargs = types.MakeStringList("meep/meep/meep", "foo", "meep/beep").List() args.SetArgs(pargs) result, errs = fn_mkdir(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) assert.Equal(t, []string{"foo", "meep"}, dirContents(".")) assert.True(t, isDir("foo")) assert.True(t, isDir("meep/meep")) assert.True(t, isDir("meep/meep/meep")) assert.True(t, isDir("meep/beep")) // now with an error in the middle of the list (*but* we still // create the other requested dirs!) testutils.TouchFiles("meep/zap") pargs = types.MakeStringList("meep/bap", "meep/zap/zip", "foo/bar").List() args.SetArgs(pargs) result, errs = fn_mkdir(args) assert.Nil(t, result) assert.Equal(t, 1, len(errs)) assert.Equal(t, "mkdir meep/zap: not a directory", errs[0].Error()) assert.True(t, isDir("meep/bap")) assert.True(t, isDir("foo/bar")) // finally, with multiple errors pargs = append(pargs, types.MakeFuString("meep/zap/blop")) args.SetArgs(pargs) result, errs = fn_mkdir(args) assert.Nil(t, result) assert.Equal(t, 2, len(errs)) assert.Equal(t, "mkdir meep/zap: not a directory", errs[0].Error()) assert.Equal(t, "mkdir meep/zap: not a directory", errs[1].Error()) }
func Test_println(t *testing.T) { cleanup1 := testutils.Chtemp() defer cleanup1() rfile, err := os.Create("stdout") if err != nil { panic(err) } // save a copy of stdout in another fd stdout_fd := int(os.Stdout.Fd()) save_stdout, err := syscall.Dup(stdout_fd) if err != nil { panic(err) } // redirect stdout to rfile err = syscall.Dup2(int(rfile.Fd()), stdout_fd) if err != nil { panic(err) } cleanup2 := func() { rfile.Close() err = syscall.Dup2(save_stdout, stdout_fd) if err != nil { panic(err) } syscall.Close(save_stdout) } defer cleanup2() args := RuntimeArgs{ BasicArgs: types.MakeBasicArgs(nil, []types.FuObject{}, nil), } result, errs := fn_println(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) data, err := ioutil.ReadFile("stdout") assert.Nil(t, err) assert.Equal(t, "\n", string(data)) rfile.Truncate(0) rfile.Seek(0, 0) args.SetArgs(types.MakeStringList("hello", "world").List()) fn_println(args) data, err = ioutil.ReadFile("stdout") assert.Nil(t, err) assert.Equal(t, "hello world\n", string(data)) rfile.Truncate(0) rfile.Seek(0, 0) }
func Test_FinderNode_Add_Expand(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() testutils.TouchFiles( "src/foo.c", "src/foo.h", "main.c", "include/bop.h", "doc.txt", "doc/stuff.txt", "doc/blahblah.rst") finder1 := NewFinderNode("**/*.c") finder2 := NewFinderNode("doc/*.txt") // sum = <**/*.c> + <doc/*.txt> expect := []string{ "main.c", "src/foo.c", "doc/stuff.txt"} sum, err := finder1.Add(finder2) assert.Nil(t, err) assertExpand(t, nil, expect, sum) // sum = sum + <"*c*/?o?.h"> finder3 := NewFinderNode("*c*/?o?.h") expect = append(expect, "include/bop.h", "src/foo.h") sum, err = sum.Add(finder3) assert.Nil(t, err) assertExpand(t, nil, expect, sum) // sum = <*c*/?o?.h> + <**/*.c> expect = []string{ "include/bop.h", "src/foo.h", "main.c", "src/foo.c"} sum, err = finder3.Add(finder1) assert.Nil(t, err) assertExpand(t, nil, expect, sum) // sum = <doc/*.txt> + sum // (effectively: sum = <doc/*.txt> + (<*c*/?o?.h> + <**/*.c>)) expect = append([]string{"doc/stuff.txt"}, expect...) sum, err = finder2.Add(sum) assert.Nil(t, err) assertExpand(t, nil, expect, sum) // sum = <**/*.c> + "urgh" expect = []string{ "main.c", "src/foo.c", "urgh"} sum, err = finder1.Add(types.MakeFuString("urgh")) assert.Nil(t, err) assertExpand(t, nil, expect, sum) // sum = <**/*.c> + ["a", "b", "c"] expect = []string{ "main.c", "src/foo.c", "a", "b", "c"} list := types.MakeStringList("a", "b", "c") sum, err = finder1.Add(list) assert.Nil(t, err) assertExpand(t, nil, expect, sum) }
func Test_KyotoDB_lookup_fail(t *testing.T) { // looking up a non-existent key is not an error cleanup := testutils.Chtemp() defer cleanup() db, err := OpenKyotoDB("test.kch", true) if err != nil { t.Fatal(err) } record, err := db.LookupNode("nosuchnode") assert.Nil(t, record) assert.Nil(t, err) }
func Test_FileNode_Signature(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() testutils.Mkdirs("d1", "d2") testutils.Mkfile("d1", "empty", "") testutils.Mkfile("d2", "stuff", "foo\n") node1 := NewFileNode("d1/empty") node2 := NewFileNode("d2/stuff") node3 := NewFileNode("nonexistent") expect := []byte{} hash := fnv.New64a() assert.Equal(t, 8, hash.Size()) expect = hash.Sum(expect) sig, err := node1.Signature() assert.Nil(t, err) assert.Equal(t, expect, sig) hash.Write([]byte{'f', 'o', 'o', '\n'}) expect = expect[:0] expect = hash.Sum(expect) sig, err = node2.Signature() assert.Nil(t, err) assert.Equal(t, expect, sig) // make sure it's cached, i.e. changes to the file are not seen by // the same FileNode object in the same process testutils.Mkfile("d2", "stuff", "fooo\n") sig, err = node2.Signature() assert.Nil(t, err) assert.Equal(t, expect, sig) // in fact, even if the file disappears, we still have its signature err = os.Remove("d2/stuff") if err != nil { panic(err) } sig, err = node2.Signature() assert.Nil(t, err) assert.Equal(t, expect, sig) sig, err = node3.Signature() assert.NotNil(t, err) assert.Equal(t, "open nonexistent: no such file or directory", err.Error()) }
func Test_findScripts(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() var script string var err error // case 1: user specifies the script to run, regardless of whether // it exists or not script, err = findScript("foo") assert.Equal(t, "foo", script) assert.Nil(t, err) // case 2: no *.fubsy files in current dir script, err = findScript("") assert.Equal(t, "main.fubsy not found (and no other *.fubsy files found)", err.Error()) // case 3: only main.fubsy exists testutils.TouchFiles("main.fubsy") script, err = findScript("") assert.Equal(t, "main.fubsy", script) assert.Nil(t, err) // case 4: multiple *.fubsy files exist, including main.fubsy testutils.TouchFiles("a.fubsy", "b.fubsy") script, err = findScript("") assert.Equal(t, "main.fubsy", script) assert.Nil(t, err) // case 5: multiple *.fubsy files exist, not including main.fubsy remove("main.fubsy") script, err = findScript("") assert.Equal(t, "", script) assert.True(t, strings.HasPrefix( err.Error(), "main.fubsy not found, and multiple *.fubsy files exist", )) // case 6: exactly one *.fubsy file exists, and it's not main.fubsy remove("a.fubsy") script, err = findScript("") assert.Equal(t, "b.fubsy", script) assert.Nil(t, err) }
func Test_remove(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() args := RuntimeArgs{ BasicArgs: types.MakeBasicArgs(nil, []types.FuObject{}, nil), } // remove() doesn't care about empty arg list (same reason as mkdir()) result, errs := fn_remove(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) // remove() ignores non-existent files args.SetArgs(types.MakeStringList("foo", "bar/bleep/meep", "qux").List()) result, errs = fn_remove(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) // remove() removes regular files testutils.TouchFiles("foo", "bar/bleep/meep", "bar/bleep/feep", "qux") args.SetArgs(types.MakeStringList("foo", "bar/bleep/meep", "bogus").List()) result, errs = fn_remove(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) assert.Equal(t, []string{"bar", "qux"}, dirContents(".")) assert.Equal(t, []string{"bleep"}, dirContents("bar")) assert.Equal(t, []string{"feep"}, dirContents("bar/bleep")) // remove() removes files and directories too testutils.TouchFiles("foo", "bar/bleep/meep", "qux") args.SetArgs(types.MakeStringList("bogus", "bar", "morebogus", "qux").List()) result, errs = fn_remove(args) assert.Nil(t, result) assert.Equal(t, 0, len(errs)) assert.Equal(t, []string{"foo"}, dirContents(".")) // remove() fails if it tries to delete from an unwriteable directory testutils.TouchFiles("bar/bong", "qux/bip") testutils.ChmodRO("bar") defer testutils.ChmodOwnerAll("bar") args.SetArgs(types.MakeStringList("bar", "qux").List()) result, errs = fn_remove(args) assert.Nil(t, result) assert.Equal(t, "remove bar/bong: permission denied", errs[0].Error()) }
func Test_FinderNode_Expand_empty(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() // no patterns, no files: of course we find nothing finder := &FinderNode{} assertExpand(t, nil, []string{}, finder) // patterns, but no files: still nothing finder.includes = []string{"**/*.c", "include/*.h", "*/*.txt"} assertExpand(t, nil, []string{}, finder) // no patterns, some files: still nothing finder.includes = finder.includes[0:0] testutils.TouchFiles( "lib1/foo.c", "lib1/sub/blah.c", "include/bop.h", "include/bip.h") assertExpand(t, nil, []string{}, finder) }
func Test_KyotoDB_basics(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() db, err := OpenKyotoDB("test1.kch", true) assert.NotNil(t, db.kcdb) if err != nil { t.Fatal(err) } rec1 := NewBuildRecord() rec1.SetTargetSignature([]byte{}) err = db.WriteNode("node0", rec1) assert.Nil(t, err) rec2, err := db.LookupNode("node0") assert.Nil(t, err) assert.True(t, rec1.Equal(rec2)) // Make sure it works across database close/reopen. rec1.AddParent("node1", []byte{34}) rec1.AddParent("node2", []byte{54, 63}) rec1.SetTargetSignature([]byte{200, 150, 100}) enc, err := rec1.encode() assert.Nil(t, err) rec2 = NewBuildRecord() err = rec2.decode(enc) err = db.WriteNode("node0", rec1) assert.Nil(t, err) err = db.Close() assert.Nil(t, err) db, err = OpenKyotoDB("test1.kch", false) // open read-only if err != nil { t.Fatal(err) } rec2, err = db.LookupNode("node0") assert.Nil(t, err) assert.True(t, rec1.Equal(rec2), "wrote record:\n%#v\nand got back:\n%#v", rec1, rec2) }
func Test_FinderNode_Expand_double_recursion(t *testing.T) { // Java programmers love this sort of insanely deep structure, and // Ant supports patterns with multiple occurences of "**" ... so I // guess Fubsy has to support them too! cleanup := testutils.Chtemp() defer cleanup() testutils.TouchFiles( "app1/src/main/org/example/app1/App1.java", "app1/src/main/org/example/app1/Util.java", "app1/src/main/org/example/app1/doc.txt", "app1/src/main/org/example/app1/subpkg/Stuff.java", "app1/src/main/org/example/app1/subpkg/MoreStuff.java", "app1/src/test/org/example/app1/StuffTest.java", "misc/app2/src/main/org/example/app2/App2.java", "misc/app3/src/main/org/example/app3/App3.java", "misc/app3/src/main/org/example/app3/Helpers.java", "misc/app3/src/test/org/example/app3/TestHelpers.java", "misc/app3/src/test/org/example/app3/testdata", ) var finder *FinderNode var expect []string finder = NewFinderNode("**/test/**/*.java") expect = []string{ "app1/src/test/org/example/app1/StuffTest.java", "misc/app3/src/test/org/example/app3/TestHelpers.java", } assertExpand(t, nil, expect, finder) finder = NewFinderNode("**/test/**/*") expect = []string{ "app1/src/test/org/example/app1/StuffTest.java", "misc/app3/src/test/org/example/app3/TestHelpers.java", "misc/app3/src/test/org/example/app3/testdata", } assertExpand(t, nil, expect, finder) finder = NewFinderNode("**/test/**") assertExpand(t, nil, expect, finder) }
func Test_FileNode_Changed(t *testing.T) { // this is really a test of Signature() + Changed() together, because // Changed() itself is so trivial that testing it is no challenge cleanup := testutils.Chtemp() defer cleanup() testutils.Mkfile(".", "stuff.txt", "blah blah blah\n") node := NewFileNode("stuff.txt") osig, err := node.Signature() assert.Nil(t, err) // construct a new FileNode so the cache is lost node = NewFileNode("stuff.txt") nsig, err := node.Signature() assert.Nil(t, err) assert.False(t, node.Changed(osig, nsig)) // modify the file and repeat testutils.Mkfile(".", "stuff.txt", "blah blah blah\nblah") node = NewFileNode("stuff.txt") nsig, err = node.Signature() assert.Nil(t, err) assert.True(t, node.Changed(osig, nsig)) }
func Test_FileNode_Exists(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() testutils.TouchFiles("foo.txt", "a/a/a/a/foo.txt", "a/b/unreadable") testutils.ChmodNoAccess("a/b") defer testutils.ChmodOwnerAll("a/b") dag := NewDAG() tests := []struct { name string exists bool err string }{ {"foo.txt", true, ""}, {"a/a/a", false, "stat a/a/a: is a directory, not a regular file"}, {"a/a/a/bogus", false, ""}, {"a/a/a/a/foo.txt", true, ""}, {"a/b/unreadable", false, "stat a/b/unreadable: permission denied"}, } for _, test := range tests { node := MakeFileNode(dag, test.name) exists, err := node.Exists() if test.err != "" { assert.NotNil(t, err) assert.Equal(t, test.err, err.Error()) } if test.exists && !exists { t.Errorf("%v: expected Exists() true, got false", node) } else if !test.exists && exists { t.Errorf("%v: expected Exists() false, got true", node) } } }
func Test_KyotoDB_checkVersion(t *testing.T) { cleanup := testutils.Chtemp() defer cleanup() // brand-new empty DB with no version number in it db := KyotoDB{} db.kcdb = cabinet.New() filename := "test.kch" err := db.kcdb.Open(filename, cabinet.KCOWRITER|cabinet.KCOCREATE) if err != nil { t.Fatal(err) } // checkVersion() on an empty DB in read-only mode fails, because // it can neither read nor write the version number err = db.checkVersion(filename, false, 0, 0, 0) expect := "database test.kch has no version number" if err == nil || err.Error() != expect { t.Errorf("expected error %s, but got %v", expect, err) } // in write mode it's OK, and sets the version number in the file err = db.checkVersion(filename, true, 513, 513, 513) assert.Nil(t, err) versionkey := makekey(PREFIX_META, "version") val, err := db.kcdb.Get(versionkey) assert.Nil(t, err) assert.Equal(t, "\x00\x00\x02\x01", string(val)) // once the version number is in the file, write mode doesn't // matter -- now it's down to comparing with the supported range // of versions err = db.checkVersion(filename, false, 513, 134, 231) expect = "database test.kch is from the future (database version = 513, but max supported version = 231)" if err == nil || err.Error() != expect { t.Errorf("expected error\n%s\nbut got\n%v", expect, err) } err = db.checkVersion(filename, false, 513, 534, 546) expect = "database test.kch is too old (database version = 513, but min supported version = 534)" if err == nil || err.Error() != expect { t.Errorf("expected error\n%s\nbut got\n%v", expect, err) } err = db.checkVersion(filename, false, 513, 512, 514) assert.Nil(t, err) err = db.checkVersion(filename, false, 513, 513, 513) assert.Nil(t, err) err = db.checkVersion(filename, false, 513, 512, 513) assert.Nil(t, err) err = db.checkVersion(filename, false, 513, 513, 514) assert.Nil(t, err) // corrupt version number err = db.kcdb.Set(versionkey, []byte{0, 0x43, 0}) assert.Nil(t, err) err = db.checkVersion(filename, false, 513, 513, 513) expect = "database test.kch: unable to decode version number (004300)" if err == nil || err.Error() != expect { t.Errorf("expected error\n%s\nbut got\n%v", expect, err) } }