Example #1
0
func Test_FileNode_Add(t *testing.T) {
	node0 := NewFileNode("foo/bar")
	node1 := NewFileNode("foo/baz")
	obj0 := types.MakeFuString(".c")
	obj1 := types.MakeStringList("a", "b")

	var err error
	var expect types.FuObject
	var actual types.FuObject

	// node + node = list of nodes
	expect = types.MakeFuList(node0, node1)
	actual, err = node0.Add(node1)
	assert.Nil(t, err)
	assert.True(t, expect.Equal(actual))

	// node + string = new node
	expect = NewFileNode("foo/bar.c")
	actual, err = node0.Add(obj0)
	assert.Nil(t, err)
	assert.True(t, expect.Equal(actual))

	// node + list = flattened list
	expect = types.MakeFuList(
		node0, types.MakeFuString("a"), types.MakeFuString("b"))
	actual, err = node0.Add(obj1)
	assert.Nil(t, err)
	assert.True(t, expect.Equal(actual))
}
Example #2
0
func Test_nodify(t *testing.T) {
	sval1 := types.MakeFuString("hello.txt")
	sval2 := types.MakeFuString("foo.c")
	lval1 := types.MakeFuList(sval1, sval2)
	finder1 := dag.NewFinderNode("*.c", "*.h")
	finder2 := dag.NewFinderNode("**/*.java")

	rt := NewRuntime(build.BuildOptions{}, "", nil)
	nodes := rt.nodify(sval1)
	assert.Equal(t, 1, len(nodes))
	assert.Equal(t, "hello.txt", nodes[0].(*dag.FileNode).Name())

	nodes = rt.nodify(lval1)
	assert.Equal(t, 2, len(nodes))
	assert.Equal(t, "hello.txt", nodes[0].(*dag.FileNode).Name())
	assert.Equal(t, "foo.c", nodes[1].(*dag.FileNode).Name())

	nodes = rt.nodify(finder1)
	assert.Equal(t, 1, len(nodes))
	assert.Equal(t, "<*.c *.h>", nodes[0].(*dag.FinderNode).String())

	findersum, err := finder1.Add(finder2)
	assert.Nil(t, err)
	nodes = rt.nodify(findersum)
	if len(nodes) == 2 {
		assert.Equal(t, "<*.c *.h>", nodes[0].(*dag.FinderNode).String())
		assert.Equal(t, "<**/*.java>", nodes[1].(*dag.FinderNode).String())
	} else {
		t.Errorf("expected nodify(%s) to return 2 nodes, but got %d: %v",
			findersum, len(nodes), nodes)
	}
}
Example #3
0
// evaluate more complex expressions
func Test_evaluate_complex(t *testing.T) {
	// a + b evaluates to various things, depending on the value
	// of those two variables
	addnode := dsl.NewASTAdd(
		dsl.NewASTName("a", dsl.NewStubLocation("loc1")),
		dsl.NewASTName("b", dsl.NewStubLocation("loc2")))

	// case 1: two strings just get concatenated
	rt := minimalRuntime()
	ns := rt.Namespace()
	ns.Assign("a", types.MakeFuString("foo"))
	ns.Assign("b", types.MakeFuString("bar"))
	expect := types.MakeFuString("foobar")
	assertEvaluateOK(t, rt, expect, addnode)

	// case 2: adding a function to a string fails
	ns.Assign("b", types.NewFixedFunction("b", 0, nil))
	assertEvaluateFail(t, rt,
		"loc1loc2: unsupported operation: cannot add function to string",
		addnode)

	// case 3: undefined name
	delete((*ns.(*types.ValueStack))[0].(types.ValueMap), "b")
	assertEvaluateFail(t, rt, "loc2: name not defined: 'b'", addnode)
}
Example #4
0
func Test_prepareCall(t *testing.T) {
	// this is never going to be called, so it's OK that it's nil
	var fn_dummy func(argsource types.ArgSource) (types.FuObject, []error)
	var dummy1, dummy2 types.FuCallable
	dummy1 = types.NewFixedFunction("dummy1", 0, fn_dummy)
	dummy2 = types.NewFixedFunction("dummy1", 1, fn_dummy)

	rt := minimalRuntime()
	ns := rt.Namespace()
	ns.Assign("dummy1", dummy1)
	ns.Assign("dummy2", dummy2)
	ns.Assign("x", types.MakeFuString("whee!"))

	noargs := []dsl.ASTExpression{}
	onearg := []dsl.ASTExpression{dsl.NewASTString("\"meep\"")}

	var astcall *dsl.ASTFunctionCall
	var callable types.FuCallable
	var args RuntimeArgs
	var errs []error

	// correct (no args) call to dummy1()
	astcall = dsl.NewASTFunctionCall(dsl.NewASTName("dummy1"), noargs)
	callable, args, errs = rt.prepareCall(astcall)
	assert.Equal(t, 0, len(errs))
	assert.Equal(t, dummy1, callable)
	assert.Equal(t, []types.FuObject{}, args.Args())

	// and to dummy2()
	astcall = dsl.NewASTFunctionCall(dsl.NewASTName("dummy2"), onearg)
	callable, args, errs = rt.prepareCall(astcall)
	assert.Equal(t, 0, len(errs))
	assert.Equal(t, dummy2, callable)
	assert.Equal(t, []types.FuObject{types.MakeFuString("meep")}, args.Args())

	// attempt to call dummy2() incorrectly (1 arg, but it's an undefined name)
	astcall = dsl.NewASTFunctionCall(
		dsl.NewASTName("dummy2"),
		[]dsl.ASTExpression{dsl.NewASTName("bogus")})
	callable, _, errs = rt.prepareCall(astcall)
	assert.Equal(t, 1, len(errs))
	assert.Equal(t, "name not defined: 'bogus'", errs[0].Error())

	// attempt to call non-existent function
	astcall = dsl.NewASTFunctionCall(dsl.NewASTName("bogus"), noargs)
	callable, _, errs = rt.prepareCall(astcall)
	assert.Nil(t, callable)
	assert.Equal(t, 1, len(errs))
	assert.Equal(t, "name not defined: 'bogus'", errs[0].Error())

	// attempt to call something that is not a function
	astcall = dsl.NewASTFunctionCall(dsl.NewASTName("x"), noargs)
	callable, _, errs = rt.prepareCall(astcall)
	assert.Nil(t, callable)
	assert.Equal(t, 1, len(errs))
	assert.Equal(t, "not a function or method: 'x'", errs[0].Error())
}
Example #5
0
func Test_FinderNode_expand_cycle(t *testing.T) {
	ns := types.NewValueMap()
	ns.Assign("a", types.MakeFuString("$b"))
	ns.Assign("b", types.MakeFuString("$c$d"))
	ns.Assign("c", types.MakeFuString("$a"))

	var err error
	finder := NewFinderNode("src/$a/*.h")

	_, err = finder.ActionExpand(ns, nil)
	assert.Equal(t, "cyclic variable reference: a -> b -> c -> a", err.Error())

	err = finder.NodeExpand(ns)
	assert.Equal(t, "cyclic variable reference: a -> b -> c -> a", err.Error())
}
Example #6
0
func Test_ListNode_expand_cycle(t *testing.T) {
	ns := types.NewValueMap()
	ns.Assign("a", types.MakeFuString("$b"))
	ns.Assign("b", types.MakeFuString("$a"))

	var err error
	inner := NewStubNode("foo/$a")
	list := newListNode(inner)

	// XXX ooops, ActionExpand() does not expand variable refs!
	// _, err = list.ActionExpand(ns, nil)
	// assert.Equal(t, "cyclic variable reference: a -> b -> a", err.Error())

	err = list.NodeExpand(ns)
	assert.Equal(t, "cyclic variable reference: a -> b -> a", err.Error())
}
Example #7
0
func (self *Runtime) evaluate(
	expr_ dsl.ASTExpression) (
	result types.FuObject, errs []error) {
	switch expr := expr_.(type) {
	case *dsl.ASTString:
		result = types.MakeFuString(expr.Value())
	case *dsl.ASTList:
		result, errs = self.evaluateList(expr)
	case *dsl.ASTName:
		result, errs = self.evaluateName(expr)
	case *dsl.ASTFileFinder:
		result = dag.NewFinderNode(expr.Patterns()...)
	case *dsl.ASTAdd:
		result, errs = self.evaluateAdd(expr)
	case *dsl.ASTFunctionCall:
		var callable types.FuCallable
		var args RuntimeArgs
		callable, args, errs = self.prepareCall(expr)
		if len(errs) == 0 {
			result, errs = self.evaluateCall(callable, args, nil)
		}
	case *dsl.ASTSelection:
		_, result, errs = self.evaluateLookup(expr)
	default:
		return nil, []error{unsupportedAST(expr_)}
	}
	for i, err := range errs {
		errs[i] = MakeLocationError(expr_, err)
	}
	return
}
Example #8
0
func Test_Runtime_runMainPhase_valid(t *testing.T) {
	script := "" +
		"main {\n" +
		"  src = \"foo.c\"\n" +
		"  \"foo\": src {\n" +
		"    \"cc -o $TARGET $src\"\n" +
		"  }\n" +
		"}\n"
	rt := parseScript(t, "test.fubsy", script)
	errors := rt.runMainPhase()
	assert.Equal(t, 0, len(errors))
	val, ok := rt.stack.Lookup("src")
	assert.True(t, ok)
	assert.Equal(t, types.MakeFuString("foo.c"), val)
	assert.NotNil(t, rt.dag)

	// this seems *awfully* detailed and brittle, but DAG doesn't
	// provide a good way to query what's in it (yet...)
	expect := `0000: FileNode "foo" (state UNKNOWN)
  action: "cc -o $TARGET $src"
  parents:
    0001: foo.c
0001: FileNode "foo.c" (state UNKNOWN)
`
	var buf bytes.Buffer
	rt.dag.Dump(&buf, "")
	actual := buf.String()
	if expect != actual {
		t.Errorf("dag.Dump(): expected\n%v\nbut got\n%v", expect, actual)
	}
}
Example #9
0
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)
}
Example #10
0
// evaluate simple expressions (no operators)
func Test_evaluate_simple(t *testing.T) {
	// the expression "meep" evaluates to the string "meep"
	var expect types.FuObject
	snode := stringnode("meep")
	rt := minimalRuntime()
	ns := rt.Namespace()
	expect = types.MakeFuString("meep")
	assertEvaluateOK(t, rt, expect, snode)

	// the expression foo evaluates to the string "meep" if foo is set
	// to that string
	ns.Assign("foo", expect)
	nnode := dsl.NewASTName("foo")
	assertEvaluateOK(t, rt, expect, nnode)

	// ... and to an error if the variable is not defined
	location := dsl.NewStubLocation("hello, sailor")
	nnode = dsl.NewASTName("boo", location)
	assertEvaluateFail(t, rt, "hello, sailor: name not defined: 'boo'", nnode)

	// expression <*.c blah> evaluates to a FinderNode with two
	// include patterns
	patterns := []string{"*.c", "blah"}
	fnode := dsl.NewASTFileFinder(patterns)
	expect = dag.NewFinderNode("*.c", "blah")
	assertEvaluateOK(t, rt, expect, fnode)
}
Example #11
0
func Test_PythonCallable_callPython(t *testing.T) {
	plugin, err := NewPythonPlugin()
	testutils.NoError(t, err)

	// Setup: define a Python function and make sure that Run() finds
	// it, so it can be added to a Fubsy namespace and used from Fubsy
	// code.
	pycode := `
def reverse_strings(*args):
    '''takes N strings, reverses each one, then returns the reversed
    strings concatenated into a single string with + between each
    one'''
    return '+'.join(''.join(reversed(arg)) for arg in args)`
	values, err := plugin.Run(pycode)
	testutils.NoError(t, err)
	value, ok := values.Lookup("reverse_strings")
	assert.True(t, ok)
	pycallable := value.(PythonCallable)
	assert.Equal(t, "reverse_strings", pycallable.Name())

	// Call the Python function with 3 strings.
	args := types.MakeStringList("foo", "blob", "pingpong").List()
	argsource := types.MakeBasicArgs(nil, args, nil)
	value, errs := pycallable.callPython(argsource)
	testutils.NoErrors(t, errs)

	// And test the returned value.
	expect := types.MakeFuString("oof+bolb+gnopgnip")
	assert.True(t, expect.Equal(value),
		"expected %s, but got %s", expect, value)
}
Example #12
0
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())
}
Example #13
0
func Test_FinderNode_Add(t *testing.T) {
	finder1 := NewFinderNode("*.c")
	finder2 := NewFinderNode("*.h")
	str1 := types.MakeFuString("foo.txt")
	str2 := types.MakeFuString("bar.txt")
	file1 := NewFileNode("foo.txt")
	file2 := NewFileNode("bar.txt")
	list := types.MakeFuList(str1, str2)
	stub := types.NewStubObject("bla", nil)

	sum, err := finder1.Add(finder2)
	testutils.NoError(t, err)
	assert.Equal(t, []types.FuObject{finder1, finder2}, sum.List())

	sum, err = finder1.Add(file1)
	testutils.NoError(t, err)
	assert.Equal(t, []types.FuObject{finder1, file1}, sum.List())

	sum, err = finder1.Add(str1)
	testutils.NoError(t, err)
	assert.Equal(t, []types.FuObject{finder1, file1}, sum.List())

	sum, err = finder1.Add(list)
	testutils.NoError(t, err)
	assert.Equal(t, []types.FuObject{finder1, file1, file2}, sum.List())

	list = types.MakeFuList(file2, file1)
	sum, err = finder1.Add(list)
	testutils.NoError(t, err)
	assert.Equal(t, []types.FuObject{finder1, file2, file1}, sum.List())

	sum, err = finder1.Add(stub)
	assert.Equal(t,
		"unsupported operation: cannot add stub to FinderNode", err.Error())
	assert.Nil(t, sum)

	list = types.MakeFuList(str1, stub)
	sum, err = finder1.Add(list)
	assert.Equal(t,
		"unsupported operation: cannot add list to FinderNode "+
			"(second operand contains stub)",
		err.Error())
	assert.Nil(t, sum)
}
Example #14
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)
}
Example #15
0
func Test_DAG_ExpandNodes(t *testing.T) {
	ns := types.NewValueMap()
	ns.Assign("sdir", types.MakeFuString("src/tool1"))
	ns.Assign("ext", types.MakeFuString("cpp"))

	tdag := NewTestDAG()
	tdag.Add("bin/tool1", "$sdir/main.$ext", "$sdir/util.$ext", "$sdir/foo.h")
	tdag.Add("$sdir/foo.h", "$sdir/foo.h.in")
	tdag.Add("$sdir/main.$ext")
	tdag.Add("$sdir/util.$ext", "$sdir/foo.h")
	tdag.Add("$sdir/foo.h.in")
	dag := tdag.Finish()

	expect := []string{
		"bin/tool1",
		"src/tool1/foo.h",
		"src/tool1/main.cpp",
		"src/tool1/util.cpp",
		"src/tool1/foo.h.in",
	}
	errs := dag.ExpandNodes(ns)
	assert.Equal(t, 0, len(errs))
	for i, node := range dag.nodes {
		assert.Equal(t, expect[i], node.Name())
	}
	assert.Equal(t, len(expect), len(dag.nodes))

	tdag = NewTestDAG()
	tdag.Add("foo/$bogus/blah", "bam")
	tdag.Add("bam", "$flop/bop")
	tdag.Add("$flop/bop")
	dag = tdag.Finish()
	errs = dag.ExpandNodes(ns)
	if len(errs) == 2 {
		assert.Equal(t, "undefined variable 'bogus' in string", errs[0].Error())
		assert.Equal(t, "undefined variable 'flop' in string", errs[1].Error())
	} else {
		t.Errorf("expected %d errors, but got %d:\n%v", 2, len(errs), errs)
	}
}
Example #16
0
func Test_evaluateCall_no_expand(t *testing.T) {
	calls := 0
	fn_foo := func(argsource types.ArgSource) (types.FuObject, []error) {
		calls++
		return types.MakeFuString("arg: " + argsource.Args()[0].ValueString()), nil
	}
	foo := types.NewFixedFunction("foo", 1, fn_foo)
	rt := minimalRuntime()
	args := RuntimeArgs{runtime: rt}

	// call bar() with an arg that needs to be expanded to test that
	// expansion does *not* happen -- evaluateCall() doesn't know
	// which phase it's in, so it has to rely on someone else to
	// ActionExpand() each value in the build phase
	args.SetArgs([]types.FuObject{types.MakeFuString(">$src<")})
	result, errs := rt.evaluateCall(foo, args, nil)
	assert.Equal(t, 1, calls)
	assert.Equal(t, types.MakeFuString("arg: >$src<"), result)
	if len(errs) != 0 {
		t.Errorf("expected no errors, but got: %v", errs)
	}

	// now make a value that expands to three values
	expansion := types.MakeStringList("a", "b", "c")
	var val types.FuObject = types.NewStubObject("val", expansion)
	valexp, _ := val.ActionExpand(nil, nil)
	assert.Equal(t, expansion, valexp) // this actually tests StubObject

	// call foo() with that expandable value, and make sure it is
	// really called with the unexpanded value
	args.SetArgs([]types.FuObject{val})
	result, errs = rt.evaluateCall(foo, args, nil)
	assert.Equal(t, 2, calls)
	assert.Equal(t, types.MakeFuString("arg: val"), result)
	if len(errs) != 0 {
		t.Errorf("expected no errors, but got: %v", errs)
	}
}
Example #17
0
func Test_evaluate_list(t *testing.T) {
	rt := minimalRuntime()
	rt.stack.Assign("blop", types.MakeFuString("bar"))
	elements := []dsl.ASTExpression{
		dsl.NewASTString("\"foo\""),
		dsl.NewASTName("blop"),
		dsl.NewASTString("\"baz\""),
	}
	astlist := dsl.NewASTList(elements)

	actual, errs := rt.evaluate(astlist)
	testutils.NoErrors(t, errs)
	expect := types.MakeStringList("foo", "bar", "baz")
	assert.Equal(t, expect, actual)
}
Example #18
0
func Test_assign(t *testing.T) {
	// AST for a = "foo"
	node := dsl.NewASTAssignment("a", stringnode("foo"))
	rt := minimalRuntime()

	errs := rt.assign(node)
	assert.Equal(t, 0, len(errs))
	expect := types.MakeFuString("foo")
	assertIn(t, rt.Namespace(), "a", expect)

	// AST for a = foo (another variable, to provoke an error)
	node = dsl.NewASTAssignment("b", dsl.NewASTName("foo"))
	errs = rt.assign(node)
	assert.Equal(t, "name not defined: 'foo'", errs[0].Error())
	_, ok := rt.Lookup("b")
	assert.False(t, ok)
}
Example #19
0
func Test_ListNode_ActionExpand(t *testing.T) {
	ns := types.NewValueMap()
	assertExpand := func(expect []Node, list *ListNode) {
		actualobj, err := list.ActionExpand(ns, nil)
		assert.Nil(t, err)
		actual := make([]Node, len(actualobj.List()))
		for i, obj := range actualobj.List() {
			actual[i] = obj.(Node)
		}
		if len(expect) == len(actual) {
			for i, enode := range expect {
				anode := actual[i]
				if !enode.Equal(anode) {
					t.Errorf("ListNode[%d]: expected <%T: %s> but got <%T: %s>",
						i, enode, enode, anode, anode)
				}
			}
		} else {
			t.Errorf(
				"ListNode %v: expected ActionExpand() to return %d Nodes, "+
					"but got %d: %v",
				list, len(expect), len(actual), actual)
		}
	}

	// a single empty ListNode yields an empty slice of Nodes
	list := newListNode()
	assertExpand([]Node{}, list)

	// a ListNode containing boring ordinary non-expanding nodes just
	// returns them
	node0 := NewStubNode("0")
	node1 := NewStubNode("1")
	list = newListNode(node0, node1)
	assertExpand([]Node{node0, node1}, list)

	// a ListNode with expanding nodes expands them (and flattens the
	// resulting list)
	list = newListNode(node1, list, node0)
	assertExpand([]Node{node1, node0, node1, node0}, list)

	ns.Assign("a", types.MakeFuString("argghh"))
	list = newListNode(node1, NewStubNode("say $a"), node0)
	assertExpand([]Node{node1, NewStubNode("say argghh"), node0}, list)
}
Example #20
0
func Test_evaluate_list_errors(t *testing.T) {
	rt := minimalRuntime()
	rt.stack.Assign("src", types.MakeFuString("foo.c"))
	elements := []dsl.ASTExpression{
		dsl.NewASTString("\"whee!\""),
		dsl.NewASTName("bogus"),
		dsl.NewASTName("src"),
		dsl.NewASTString("\"bop\""),
		dsl.NewASTName("morebogus"),
	}
	astlist := dsl.NewASTList(elements)

	actual, errs := rt.evaluate(astlist)
	if len(errs) != 2 {
		t.Fatalf("expected 2 errors, but got %d: %v", len(errs), errs)
	}
	assert.Equal(t, "name not defined: 'bogus'", errs[0].Error())
	assert.Equal(t, "name not defined: 'morebogus'", errs[1].Error())
	assert.Nil(t, actual)
}
Example #21
0
// Walk the filesystem for files matching this FinderNode's include
// patterns. Return the list of matching filenames as a FuList of
// FileNode.
func (self *FinderNode) ActionExpand(
	ns types.Namespace, ctx *types.ExpandContext) (
	types.FuObject, error) {

	// if case this node was not already expanded by
	// DAG.ExpandNodes(), do it now so variable references are
	// followed
	var err error
	err = self.NodeExpand(ns)
	if err != nil {
		return nil, err
	}
	filenames, err := self.FindFiles()
	if err != nil {
		return nil, err
	}
	var values []types.FuObject
	for _, filename := range filenames {
		values = append(values, types.MakeFuString(filename))
	}
	return types.MakeFuList(values...), nil
}
Example #22
0
func (self PythonCallable) callPython(argsource types.ArgSource) (types.FuObject, []error) {
	args := argsource.Args()

	// build a slice of strings, which will then be converted to
	// Python strings in C (this way we only copy the bytes once, at
	// the cost of C code knowing the internals of Go slices and
	// strings)
	sargs := make([]string, len(args))
	for i, arg := range args {
		switch arg.(type) {
		case types.FuString:
			sargs[i] = arg.ValueString()
		default:
			err := fmt.Errorf(
				"bad argument type: all arguments must be strings, "+
					"but argument %d is %s %s",
				i+1, arg.Typename(), arg.String())
			return nil, []error{err}
		}
	}

	var cerror *C.char
	var cresult *C.char
	var result types.FuObject
	cresult = C.call_python(self.callable, unsafe.Pointer(&sargs), &cerror)

	if cerror != nil {
		err := C.GoString(cerror)
		return nil, []error{errors.New(err)}
	}
	if cresult != nil {
		result = types.MakeFuString(C.GoString(cresult))
	}

	return result, nil
}
Example #23
0
//export callBuiltin
func callBuiltin(
	pfunc unsafe.Pointer, numargs C.int, cargs unsafe.Pointer) (
	*C.char, *C.char) {

	log.Debug(log.PLUGINS, "callBuiltin: calling Go function at %p", pfunc)
	var fn types.FuCode

	fuargs := make([]types.FuObject, numargs)
	for i := uintptr(0); i < uintptr(numargs); i++ {
		// cargs is really a C char **, i.e. a pointer to an array of
		// char *. argp is a pointer to the i'th member of cargs. This
		// is just C-style array lookup with pointer arithmetic, but
		// in Go syntax.
		argp := unsafe.Pointer(uintptr(cargs) + i*unsafe.Sizeof(cargs))
		arg := C.GoString(*(**C.char)(argp))
		fuargs[i] = types.MakeFuString(arg)
	}
	args := types.MakeBasicArgs(nil, fuargs, nil)

	fn = *(*types.FuCode)(unsafe.Pointer(&pfunc))
	log.Debug(log.PLUGINS, "followed unsafe.Pointer to get %p", fn)
	result, err := fn(args)

	if len(err) > 0 {
		errmsgs := make([]string, len(err))
		for i, err := range err {
			errmsgs[i] = err.Error()
		}
		return nil, C.CString(strings.Join(errmsgs, "\n"))
	}
	var cresult *C.char
	if result != nil {
		cresult = C.CString(result.String())
	}
	return cresult, nil
}
Example #24
0
func (self *SequenceAction) AddCommand(command *dsl.ASTString) {
	raw := types.MakeFuString(command.Value())
	self.AddAction(NewCommandAction(raw))
}
Example #25
0
func Test_evaluateCall_method(t *testing.T) {
	// construct AST for "a.b.c(x)"
	astargs := []dsl.ASTExpression{dsl.NewASTName("x")}
	astcall := dsl.NewASTFunctionCall(
		dsl.NewASTSelection(
			dsl.NewASTSelection(dsl.NewASTName("a"), "b"), "c"),
		astargs)

	// make sure a is an object with attributes, and b is one of them
	// (N.B. having FileNodes be attributes of one another is weird
	// and would never happen in a real Fubsy script, but it's a
	// convenient way to setup this method call)
	aobj := dag.NewFileNode("a.txt")
	bobj := dag.NewFileNode("b.txt")
	aobj.ValueMap = types.NewValueMap()
	aobj.Assign("b", bobj)

	// make sure a.b.c is a method
	calls := make([]string, 0) // list of function names
	var meth_c types.FuCode
	meth_c = func(argsource types.ArgSource) (types.FuObject, []error) {
		args := argsource.Args()
		if len(args) != 1 {
			panic("c() called with wrong number of args")
		}
		calls = append(calls, "c")
		robj := argsource.Receiver()
		return nil, []error{
			fmt.Errorf("c failed: receiver: %s %v, arg: %s %v",
				robj.Typename(), robj, args[0].Typename(), args[0])}
	}
	bobj.ValueMap = types.NewValueMap()
	bobj.Assign("c", types.NewFixedFunction("c", 1, meth_c))

	rt := minimalRuntime()
	ns := rt.Namespace()
	ns.Assign("a", aobj)
	ns.Assign("x", types.MakeFuString("hello"))

	// what the hell, let's test the precall feature too
	var precalledCallable types.FuCallable
	var precalledArgs types.ArgSource
	precall := func(callable types.FuCallable, argsource types.ArgSource) {
		precalledCallable = callable
		precalledArgs = argsource
	}

	callable, args, errs := rt.prepareCall(astcall)
	assert.Equal(t, "c", callable.(*types.FuFunction).Name())
	assert.True(t, args.Receiver() == bobj)
	assert.Equal(t, 0, len(errs))

	result, errs := rt.evaluateCall(callable, args, precall)
	assert.Equal(t, precalledCallable, callable)
	assert.Equal(t, precalledArgs.Args(), types.MakeStringList("hello").List())
	assert.Nil(t, result)
	if len(errs) == 1 {
		assert.Equal(t,
			"c failed: receiver: FileNode \"b.txt\", arg: string \"hello\"",
			errs[0].Error())
	} else {
		t.Errorf("expected exactly 1 error, but got: %v", errs)
	}
}
Example #26
0
func Test_evaluateCall(t *testing.T) {
	// foo() takes no args and always succeeds;
	// bar() takes exactly one arg and always fails
	calls := make([]string, 0) // list of function names

	fn_foo := func(argsource types.ArgSource) (types.FuObject, []error) {
		if len(argsource.Args()) != 0 {
			panic("foo() called with wrong number of args")
		}
		calls = append(calls, "foo")
		return types.MakeFuString("foo!"), nil
	}
	fn_bar := func(argsource types.ArgSource) (types.FuObject, []error) {
		if len(argsource.Args()) != 1 {
			panic("bar() called with wrong number of args")
		}
		calls = append(calls, "bar")
		return nil, []error{
			fmt.Errorf("bar failed (%s)", argsource.Args()[0])}
	}
	var foo, bar types.FuCallable
	foo = types.NewFixedFunction("foo", 0, fn_foo)
	bar = types.NewFixedFunction("bar", 1, fn_bar)

	rt := minimalRuntime()
	args := RuntimeArgs{runtime: rt}

	var result types.FuObject
	var errs []error

	// call foo() correctly (no args)
	args.SetArgs([]types.FuObject{})
	result, errs = rt.evaluateCall(foo, args, nil)
	assert.Equal(t, types.MakeFuString("foo!"), result)
	assert.Equal(t, 0, len(errs))
	assert.Equal(t, []string{"foo"}, calls)

	// call foo() incorrectly (1 arg)
	args.SetArgs([]types.FuObject{types.MakeFuString("meep")})
	result, errs = rt.evaluateCall(foo, args, nil)
	assert.Equal(t, 1, len(errs))
	assert.Equal(t,
		"function foo() takes no arguments (got 1)", errs[0].Error())
	assert.Equal(t, []string{"foo"}, calls)

	// call bar() correctly (1 arg)
	result, errs = rt.evaluateCall(bar, args, nil)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errs))
	assert.Equal(t, "bar failed (\"meep\")", errs[0].Error())
	assert.Equal(t, []string{"foo", "bar"}, calls)

	// call bar() incorrectly (no args)
	args.SetArgs(nil)
	result, errs = rt.evaluateCall(bar, args, nil)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errs))
	assert.Equal(t,
		"function bar() takes exactly 1 arguments (got 0)", errs[0].Error())

	// check the sequence of calls
	assert.Equal(t, []string{"foo", "bar"}, calls)
}