func Test_FileNode_Expand(t *testing.T) { ns := types.NewValueMap() node := NewFileNode("foobar") xnode, err := node.ActionExpand(ns, nil) assert.Nil(t, err) assert.Equal(t, node, xnode) err = node.NodeExpand(ns) assert.Nil(t, err) assert.Equal(t, "foobar", node.Name()) // test that ActionExpand() follows variable references node = NewFileNode("$foo$bar") xnode, err = node.ActionExpand(ns, nil) assert.Equal(t, "undefined variable 'foo' in string", err.Error()) // make it so "$foo$bar" expands to "$foo", and ensure that // expansion stops there // XXX argh: currently this expands to "'$'foo": hmmmmm // ns.Assign("foo", types.MakeFuString("$")) // ns.Assign("bar", types.MakeFuString("foo")) // xnode, err = node.ActionExpand(ns, nil) // assert.Nil(t, err) // assert.Equal(t, "$foo", xnode.String()) }
func (self PythonPlugin) Run(content string) (types.ValueMap, error) { var ccontent = C.CString(content) var cerror *C.char var cvalues *C.valuelist_t defer C.free(unsafe.Pointer(ccontent)) if C.run_python(ccontent, &cerror, &cvalues) < 0 { return nil, errors.New(C.GoString(cerror)) } defer C.free_valuelist(cvalues) valuemap := types.NewValueMap() names := uintptr(unsafe.Pointer(cvalues.names)) values := uintptr(unsafe.Pointer(cvalues.values)) var cname *C.char var name string var cvalue *C.PyObject for i := uintptr(0); i < uintptr(cvalues.numitems); i++ { offset := i * unsafe.Sizeof(i) cname = *(**C.char)(unsafe.Pointer(names + offset)) cvalue = *(**C.PyObject)(unsafe.Pointer(values + offset)) name = C.GoString(cname) valuemap.Assign(name, PythonCallable{name: name, callable: cvalue}) } return valuemap, nil }
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) }
// return a barebones Runtime with almost nothing in it -- variable // assignment and lookup works, but not much else func minimalRuntime() *Runtime { stack := types.NewValueStack() locals := types.NewValueMap() stack.Push(locals) return &Runtime{ stack: &stack, dag: dag.NewDAG(), } }
func NewBuildRule(runtime *Runtime, targets, sources []dag.Node) *BuildRule { rule := &BuildRule{ runtime: runtime, targets: dag.ListNodeFromNodes(targets), sources: dag.ListNodeFromNodes(sources), } rule.attrs = types.NewValueMap() rule.attrs["targets"] = rule.targets rule.attrs["sources"] = rule.sources return rule }
func (self *BuildRule) Execute() ([]dag.Node, []error) { stack := self.runtime.stack locals := types.NewValueMap() stack.Push(locals) defer stack.Pop() self.setLocals(locals) log.Debug(log.BUILD, "value stack:") log.DebugDump(log.BUILD, stack) err := self.action.Execute(self.runtime) return self.targets.Nodes(), err }
func assertExpand( t *testing.T, ns types.Namespace, expect []string, obj types.FuObject) { if ns == nil { ns = types.NewValueMap() } actualobj, err := obj.ActionExpand(ns, nil) assert.Nil(t, err) // convert FuList of FileNode to slice of string actualstr := make([]string, len(actualobj.List())) for i, obj := range actualobj.List() { actualstr[i] = obj.ValueString() } assert.Equal(t, expect, actualstr) }
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()) }
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()) }
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) }
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) } }
func NewRuntime( options build.BuildOptions, script string, ast *dsl.ASTRoot) *Runtime { stack := types.NewValueStack() builtins := defineBuiltins() stack.Push(builtins) // Local variables are per-script, but we only support a single // script right now. So might as well initialize the script-local // namespace right here. locals := types.NewValueMap() stack.Push(locals) return &Runtime{ options: options, script: script, ast: ast, builtins: builtins, stack: &stack, dag: dag.NewDAG(), } }
func Test_BuildRule_setLocals(t *testing.T) { targets := []dag.Node{dag.NewStubNode("foo")} sources := []dag.Node{dag.NewStubNode("bar"), dag.NewStubNode("qux")} ns := types.NewValueMap() rule := NewBuildRule(nil, targets, sources) rule.setLocals(ns) var val types.FuObject var ok bool val, ok = ns.Lookup("whatever") assert.False(t, ok) val, ok = ns.Lookup("target") assert.False(t, ok) val, ok = ns.Lookup("targets") assert.False(t, ok) val, ok = ns.Lookup("TARGET") assert.True(t, ok) assert.Equal(t, "foo", val.ValueString()) assert.Equal(t, "foo", val.(*dag.StubNode).Name()) val, ok = ns.Lookup("SOURCE") assert.True(t, ok) assert.Equal(t, "bar", val.ValueString()) assert.Equal(t, "bar", val.(*dag.StubNode).Name()) val, ok = ns.Lookup("TARGETS") assert.True(t, ok) assert.Equal(t, 1, len(val.List())) assert.Equal(t, `["foo"]`, val.String()) val, ok = ns.Lookup("SOURCES") assert.True(t, ok) assert.Equal(t, 2, len(val.List())) assert.Equal(t, `["bar", "qux"]`, val.String()) }
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) } }