Example #1
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 #2
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 #3
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 #4
0
func Test_ListNode_Add(t *testing.T) {
	nodelist := newListNode()
	otherlist := types.MakeFuList()

	actual, err := nodelist.Add(otherlist)
	testutils.NoError(t, err)
	if _, ok := actual.(*ListNode); !ok {
		t.Fatalf("expected object of type *ListNode, but got %T", actual)
	}
	assert.Equal(t, 0, len(actual.List()))

	node0 := NewStubNode("bla")
	node1 := NewStubNode("pog")
	otherlist = types.MakeFuList(node0, node1)
	actual, err = nodelist.Add(otherlist)
	testutils.NoError(t, err)
	nodes := actual.(*ListNode).List()
	if len(nodes) != 2 {
		t.Errorf("expected ListNode with 2 nodes, but got %v", actual)
	} else {
		assert.Equal(t, []types.FuObject{node0, node1}, nodes)
	}

	otherlist = types.MakeStringList("foo")
	actual, err = nodelist.Add(otherlist)
	assert.Nil(t, actual)
	assert.Equal(t,
		"unsupported operation: cannot add list to ListNode "+
			"(second operand contains string)",
		err.Error())
}
Example #5
0
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())
}
Example #6
0
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)
}
Example #7
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 #8
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 #9
0
func Test_ActionNode(t *testing.T) {
	args := RuntimeArgs{
		BasicArgs: types.MakeBasicArgs(nil, types.MakeStringList("test/x").List(), nil),
		runtime:   minimalRuntime(),
	}
	node0, errs := fn_ActionNode(args)
	assert.Equal(t, 0, len(errs))

	_ = node0.(*dag.ActionNode)
	assert.Equal(t, "test/x:action", node0.ValueString())
	assert.Equal(t, "test/x:action", node0.(dag.Node).Name())

	node1, errs := fn_ActionNode(args)
	assert.True(t, node0 == node1)
}
Example #10
0
func Test_FileNode(t *testing.T) {
	args := RuntimeArgs{
		BasicArgs: types.MakeBasicArgs(nil, types.MakeStringList("a.txt").List(), nil),
		runtime:   minimalRuntime(),
	}
	node0, errs := fn_FileNode(args)
	assert.Equal(t, 0, len(errs))
	node1, errs := fn_FileNode(args)
	assert.Equal(t, 0, len(errs))

	// panic on unexpected type
	_ = node0.(*dag.FileNode)
	_ = node1.(*dag.FileNode)

	assert.Equal(t, "a.txt", node0.(dag.Node).Name())
	assert.True(t, node0.Equal(node1))

	// FileNode is a factory: it will return existing node objects
	// rather than create new ones
	assert.True(t, node0 == node1)
}
Example #11
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 #12
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)
	}
}