Exemple #1
0
func Test_BuiltinList(t *testing.T) {
	blist := BuiltinList{}
	fn, ok := blist.Lookup("foo")
	assert.False(t, ok)
	assert.Nil(t, fn)

	callable := types.NewFixedFunction("foo", 3, nil)
	blist.builtins = append(blist.builtins, callable)
	fn, ok = blist.Lookup("foo")
	assert.True(t, ok)
	assert.Equal(t, callable, fn)

	blist.builtins = append(
		blist.builtins, types.NewFixedFunction("bar", 0, nil))
	blist.builtins = append(
		blist.builtins, types.NewFixedFunction("bop", 0, nil))
	blist.builtins = append(
		blist.builtins, types.NewFixedFunction("bam", 0, nil))
	blist.builtins = append(
		blist.builtins, types.NewFixedFunction("pow", 0, nil))

	assert.Equal(t, 5, blist.NumBuiltins())
	actual := make([]string, 0, 5)
	visit := func(name string, code types.FuObject) error {
		actual = append(actual, name)
		if name == "bam" {
			return errors.New("bam!")
		}
		return nil
	}
	err := blist.ForEach(visit)
	assert.Equal(t, []string{"foo", "bar", "bop", "bam"}, actual)
	assert.Equal(t, "bam!", err.Error())
}
Exemple #2
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())
}
Exemple #3
0
// Return a new namespace containing all builtin functions.
func defineBuiltins() BuiltinList {
	builtins := []types.FuCallable{
		types.NewVariadicFunction("println", 0, -1, fn_println),
		types.NewVariadicFunction("mkdir", 0, -1, fn_mkdir),
		types.NewVariadicFunction("remove", 0, -1, fn_remove),

		types.NewFixedFunction("build", 3, fn_build),

		// node factories
		types.NewFixedFunction("FileNode", 1, fn_FileNode),
		types.NewFixedFunction("ActionNode", 1, fn_ActionNode),
	}
	return BuiltinList{builtins}
}
Exemple #4
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)
}
Exemple #5
0
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)
}
Exemple #6
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)
	}
}
Exemple #7
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)
	}
}
Exemple #8
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)
}