summaryrefslogtreecommitdiff
path: root/src/pkg/exp/eval/eval_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/exp/eval/eval_test.go')
-rw-r--r--src/pkg/exp/eval/eval_test.go270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/pkg/exp/eval/eval_test.go b/src/pkg/exp/eval/eval_test.go
new file mode 100644
index 000000000..192a2e782
--- /dev/null
+++ b/src/pkg/exp/eval/eval_test.go
@@ -0,0 +1,270 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package eval
+
+import (
+ "bignum";
+ "flag";
+ "fmt";
+ "log";
+ "os";
+ "reflect";
+ "testing";
+)
+
+// Print each statement or expression before parsing it
+var noisy = false
+func init() {
+ flag.BoolVar(&noisy, "noisy", false, "chatter during eval tests");
+}
+
+/*
+ * Generic statement/expression test framework
+ */
+
+type test []job
+
+type job struct {
+ code string;
+ cerr string;
+ rterr string;
+ val Value;
+ noval bool;
+}
+
+func runTests(t *testing.T, baseName string, tests []test) {
+ for i, test := range tests {
+ name := fmt.Sprintf("%s[%d]", baseName, i);
+ test.run(t, name);
+ }
+}
+
+func (a test) run(t *testing.T, name string) {
+ w := newTestWorld();
+ for _, j := range a {
+ src := j.code;
+ if noisy {
+ println("code:", src);
+ }
+
+ code, err := w.Compile(src);
+ if err != nil {
+ if j.cerr == "" {
+ t.Errorf("%s: Compile %s: %v", name, src, err);
+ break;
+ }
+ if !match(t, err, j.cerr) {
+ t.Errorf("%s: Compile %s = error %s; want %v", name, src, err, j.cerr);
+ break;
+ }
+ continue;
+ }
+ if j.cerr != "" {
+ t.Errorf("%s: Compile %s succeeded; want %s", name, src, j.cerr);
+ break;
+ }
+
+ val, err := code.Run();
+ if err != nil {
+ if j.rterr == "" {
+ t.Errorf("%s: Run %s: %v", name, src, err);
+ break;
+ }
+ if !match(t, err, j.rterr) {
+ t.Errorf("%s: Run %s = error %s; want %v", name, src, err, j.rterr);
+ break;
+ }
+ continue;
+ }
+ if j.rterr != "" {
+ t.Errorf("%s: Run %s succeeded; want %s", name, src, j.rterr);
+ break;
+ }
+
+ if !j.noval && !reflect.DeepEqual(val, j.val) {
+ t.Errorf("%s: Run %s = %T(%v) want %T(%v)", name, src, val, val, j.val, j.val);
+ }
+ }
+}
+
+func match(t *testing.T, err os.Error, pat string) bool {
+ ok, errstr := testing.MatchString(pat, err.String());
+ if errstr != "" {
+ t.Fatalf("compile regexp %s: %v", pat, errstr);
+ }
+ return ok;
+}
+
+
+/*
+ * Test constructors
+ */
+
+// Expression compile error
+func CErr(expr string, cerr string) test {
+ return test([]job{job{code: expr, cerr: cerr}})
+}
+
+// Expression runtime error
+func RErr(expr string, rterr string) test {
+ return test([]job{job{code: expr, rterr: rterr}})
+}
+
+// Expression value
+func Val(expr string, val interface{}) test {
+ return test([]job{job{code: expr, val: toValue(val)}})
+}
+
+// Statement runs without error
+func Run(stmts string) test {
+ return test([]job{job{code: stmts, noval: true}})
+}
+
+// Two statements without error.
+// TODO(rsc): Should be possible with Run but the parser
+// won't let us do both top-level and non-top-level statements.
+func Run2(stmt1, stmt2 string) test {
+ return test([]job{job{code: stmt1, noval: true}, job{code: stmt2, noval: true}})
+}
+
+// Statement runs and test one expression's value
+func Val1(stmts string, expr1 string, val1 interface{}) test {
+ return test([]job{
+ job{code: stmts, noval: true},
+ job{code: expr1, val: toValue(val1)}
+ })
+}
+
+// Statement runs and test two expressions' values
+func Val2(stmts string, expr1 string, val1 interface{}, expr2 string, val2 interface{}) test {
+ return test([]job{
+ job{code: stmts, noval: true},
+ job{code: expr1, val: toValue(val1)},
+ job{code: expr2, val: toValue(val2)}
+ })
+}
+
+/*
+ * Value constructors
+ */
+
+type vstruct []interface{}
+
+type varray []interface{}
+
+type vslice struct {
+ arr varray;
+ len, cap int;
+}
+
+func toValue(val interface{}) Value {
+ switch val := val.(type) {
+ case bool:
+ r := boolV(val);
+ return &r;
+ case uint8:
+ r := uint8V(val);
+ return &r;
+ case uint:
+ r := uintV(val);
+ return &r;
+ case int:
+ r := intV(val);
+ return &r;
+ case *bignum.Integer:
+ return &idealIntV{val};
+ case float:
+ r := floatV(val);
+ return &r;
+ case *bignum.Rational:
+ return &idealFloatV{val};
+ case string:
+ r := stringV(val);
+ return &r;
+ case vstruct:
+ elems := make([]Value, len(val));
+ for i, e := range val {
+ elems[i] = toValue(e);
+ }
+ r := structV(elems);
+ return &r;
+ case varray:
+ elems := make([]Value, len(val));
+ for i, e := range val {
+ elems[i] = toValue(e);
+ }
+ r := arrayV(elems);
+ return &r;
+ case vslice:
+ return &sliceV{Slice{toValue(val.arr).(ArrayValue), int64(val.len), int64(val.cap)}};
+ case Func:
+ return &funcV{val};
+ }
+ log.Crashf("toValue(%T) not implemented", val);
+ panic();
+}
+
+/*
+ * Default test scope
+ */
+
+type testFunc struct {};
+
+func (*testFunc) NewFrame() *Frame {
+ return &Frame{nil, &[2]Value {}};
+}
+
+func (*testFunc) Call(t *Thread) {
+ n := t.f.Vars[0].(IntValue).Get(t);
+
+ res := n + 1;
+
+ t.f.Vars[1].(IntValue).Set(t, res);
+}
+
+type oneTwoFunc struct {};
+
+func (*oneTwoFunc) NewFrame() *Frame {
+ return &Frame{nil, &[2]Value {}};
+}
+
+func (*oneTwoFunc) Call(t *Thread) {
+ t.f.Vars[0].(IntValue).Set(t, 1);
+ t.f.Vars[1].(IntValue).Set(t, 2);
+}
+
+type voidFunc struct {};
+
+func (*voidFunc) NewFrame() *Frame {
+ return &Frame{nil, []Value {}};
+}
+
+func (*voidFunc) Call(t *Thread) {
+}
+
+func newTestWorld() *World {
+ w := NewWorld();
+
+ def := func(name string, t Type, val interface{}) {
+ w.DefineVar(name, t, toValue(val));
+ };
+
+ w.DefineConst("c", IdealIntType, toValue(bignum.Int(1)));
+ def("i", IntType, 1);
+ def("i2", IntType, 2);
+ def("u", UintType, uint(1));
+ def("f", FloatType, 1.0);
+ def("s", StringType, "abc");
+ def("t", NewStructType([]StructField {StructField{"a", IntType, false}}), vstruct{1});
+ def("ai", NewArrayType(2, IntType), varray{1, 2});
+ def("aai", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{1,2}, varray{3,4}});
+ def("aai2", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{5,6}, varray{7,8}});
+ def("fn", NewFuncType([]Type{IntType}, false, []Type {IntType}), &testFunc{});
+ def("oneTwo", NewFuncType([]Type{}, false, []Type {IntType, IntType}), &oneTwoFunc{});
+ def("void", NewFuncType([]Type{}, false, []Type {}), &voidFunc{});
+ def("sli", NewSliceType(IntType), vslice{varray{1, 2, 3}, 2, 3});
+
+ return w;
+}