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 TestParse_internal_newlines(t *testing.T) { // newlines in a function call are invisible to the parser tmpdir, cleanup := testutils.Mktemp() defer cleanup() script := ` main { x( a.b ) } ` fn := testutils.Mkfile(tmpdir, "newlines.fubsy", script) ast, err := Parse(fn) assert.Equal(t, 0, len(err)) expect := &ASTRoot{ children: []ASTNode{ &ASTPhase{ name: "main", children: []ASTNode{ &ASTFunctionCall{ function: &ASTName{name: "x"}, args: []ASTExpression{ &ASTSelection{ container: &ASTName{name: "a"}, member: "b", }}}}, }}} assertASTEqual(t, expect, ast) }
func TestParse_valid_sequence(t *testing.T) { tmpdir, cleanup := testutils.Mktemp() defer cleanup() // sequence of top-level children script := ` main { "boo" } plugin foo {{{ o'malley & friends }}} blob { "meep" }` fn := testutils.Mkfile(tmpdir, "valid_2.fubsy", script) ast, errs := Parse(fn) testutils.NoErrors(t, errs) expect := &ASTRoot{children: []ASTNode{ &ASTPhase{ name: "main", children: []ASTNode{&ASTString{value: "boo"}}}, &ASTInline{ lang: "foo", content: "o'malley & friends"}, &ASTPhase{ name: "blob", children: []ASTNode{&ASTString{value: "meep"}}}, }} assertASTEqual(t, expect, ast) }
// this one tries to exercise many token types and grammar rules func TestParse_omnibus_1(t *testing.T) { tmpdir, cleanup := testutils.Mktemp() defer cleanup() script := ` # start with a comment import foo import foo.bar.baz # blank lines are OK! plugin funky {{{ any ol' crap! "bring it on, dude" ... }}} main { a =("foo") + b c=(d.e) () x.y.z < lib1/*.c lib2/**/*.c > } ` fn := testutils.Mkfile(tmpdir, "omnibus_1.fubsy", script) ast, err := Parse(fn) assert.Equal(t, 0, len(err)) expect := `ASTRoot { ASTImport[foo] ASTImport[foo.bar.baz] ASTInline[funky] {{{ any ol' crap! "bring it on, dude" ... }}} ASTPhase[main] { ASTAssignment[a] ASTAdd op1: ASTString[foo] op2: ASTName[b] ASTAssignment[c] ASTFunctionCall[d.e] (0 args) ASTSelection[x.y: z] ASTFileFinder[lib1/*.c lib2/**/*.c] } } ` var actual_ bytes.Buffer ast.Dump(&actual_, "") actual := actual_.String() if expect != actual { t.Errorf("expected AST:\n%s\nbut got:\n%s", expect, actual) } }
func TestParse_omnibus_2(t *testing.T) { tmpdir, cleanup := testutils.Mktemp() defer cleanup() script := ` main { headers = <*.h> a + b : <*.c> + headers { x = a "cc $x" f(a, b) } } ` fn := testutils.Mkfile(tmpdir, "omnibus_2.fubsy", script) ast, err := Parse(fn) assert.Equal(t, 0, len(err)) expect := `ASTRoot { ASTPhase[main] { ASTAssignment[headers] ASTFileFinder[*.h] ASTBuildRule { targets: ASTAdd op1: ASTName[a] op2: ASTName[b] sources: ASTAdd op1: ASTFileFinder[*.c] op2: ASTName[headers] actions: ASTAssignment[x] ASTName[a] ASTString[cc $x] ASTFunctionCall[f] (2 args) ASTName[a] ASTName[b] } } } ` var actual_ bytes.Buffer ast.Dump(&actual_, "") actual := actual_.String() if expect != actual { t.Errorf("expected AST:\n%s\nbut got:\n%s", expect, actual) } }
func TestParse_invalid_1(t *testing.T) { tmpdir, cleanup := testutils.Mktemp() defer cleanup() // invalid: no closing rbracket script := ` main{ "borf" ` fn := testutils.Mkfile(tmpdir, "invalid_1.fubsy", script) _, err := Parse(fn) expect := fn + ":4: syntax error (near EOF)" assertOneError(t, expect, err) }
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 TestParse_invalid_2(t *testing.T) { tmpdir, cleanup := testutils.Mktemp() defer cleanup() // invalid: bad token script := ` main{ *&! "whizz" }` fn := testutils.Mkfile(tmpdir, "invalid_2.fubsy", script) _, err := Parse(fn) expect := fn + ":3: syntax error (near *&!)" assertOneError(t, expect, err) reset() }
func TestParse_valid_1(t *testing.T) { tmpdir, cleanup := testutils.Mktemp() defer cleanup() // dead simple: a single top-level element script := ` main { <meep> } ` fn := testutils.Mkfile(tmpdir, "valid_1.fubsy", script) expect := &ASTRoot{children: []ASTNode{ &ASTPhase{name: "main", children: []ASTNode{ &ASTFileFinder{patterns: []string{"meep"}}}}}} ast, err := Parse(fn) assert.Equal(t, 0, len(err)) assertASTEqual(t, expect, ast) eofloc := ast.EOF().Location() assert.True(t, strings.HasSuffix(eofloc.ErrorPrefix(), "valid_1.fubsy:6: ")) }