Commit ad9fabd7 authored by Austin Clements's avatar Austin Clements

Interpreter unit tests for statements and expressions

R=rsc
APPROVED=rsc
DELTA=1003  (1003 added, 0 deleted, 0 changed)
OCL=34223
CL=34227
parent 2364f8c3
// 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";
"fmt";
"go/parser";
"go/scanner";
"go/token";
"log";
"os";
"reflect";
"testing";
)
// Print each statement or expression before parsing it
const noisy = true
/*
* Generic statement/expression test framework
*/
type test struct {
code string;
rterr string;
exprs []exprTest;
cerr string;
}
type exprTest struct {
code string;
val interface{};
rterr string;
}
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) {
sc := newTestScope();
var fr *Frame;
var cerr os.Error;
if a.code != "" {
if noisy {
println(a.code);
}
// Compile statements
asts, err := parser.ParseStmtList(name, a.code);
if err != nil && cerr == nil {
cerr = err;
}
code, err := CompileStmts(sc, asts);
if err != nil && cerr == nil {
cerr = err;
}
// Execute statements
if cerr == nil {
fr = sc.NewFrame(nil);
rterr := code.Exec(fr);
if a.rterr == "" && rterr != nil {
t.Errorf("%s: expected %s to run, got runtime error %v", name, a.code, rterr);
return;
} else if !checkRTError(t, name, a.code, rterr, a.rterr) {
return;
}
}
}
if fr == nil {
fr = sc.NewFrame(nil);
}
for _, e := range a.exprs {
if cerr != nil {
break;
}
if noisy {
println(e.code);
}
// Compile expression
ast, err := parser.ParseExpr(name, e.code);
if err != nil && cerr == nil {
cerr = err;
}
code, err := CompileExpr(sc, ast);
if err != nil && cerr == nil {
cerr = err;
}
// Evaluate expression
if cerr == nil {
val, rterr := code.Eval(fr);
if e.rterr == "" && rterr != nil {
t.Errorf("%s: expected %q to have value %T(%v), got runtime error %v", name, e.code, e.val, e.val, rterr);
} else if !checkRTError(t, name, e.code, rterr, e.rterr) {
continue;
}
if e.val != nil {
wantval := toValue(e.val);
if !reflect.DeepEqual(val, wantval) {
t.Errorf("%s: expected %q to have value %T(%v), got %T(%v)", name, e.code, wantval, wantval, val, val);
}
}
}
}
// Check compile errors
switch {
case cerr == nil && a.cerr == "":
// Good
case cerr == nil && a.cerr != "":
t.Errorf("%s: expected compile error matching %q, got no errors", name, a.cerr);
case cerr != nil && a.cerr == "":
t.Errorf("%s: expected no compile error, got error %v", name, cerr);
case cerr != nil && a.cerr != "":
cerr := cerr.(scanner.ErrorList);
if len(cerr) > 1 {
t.Errorf("%s: expected 1 compile error matching %q, got %v", name, a.cerr, cerr);
break;
}
m, err := testing.MatchString(a.cerr, cerr.String());
if err != "" {
t.Fatalf("%s: failed to compile regexp %q: %s", name, a.cerr, err);
}
if !m {
t.Errorf("%s: expected compile error matching %q, got compile error %v", name, a.cerr, cerr);
}
}
}
func checkRTError(t *testing.T, name string, code string, rterr os.Error, pat string) bool {
switch {
case rterr == nil && pat == "":
return true;
case rterr == nil && pat != "":
t.Errorf("%s: expected %s to fail with runtime error matching %q, got no error", name, code, pat);
return false;
case rterr != nil && pat != "":
m, err := testing.MatchString(pat, rterr.String());
if err != "" {
t.Fatalf("%s: failed to compile regexp %q: %s", name, pat, err);
}
if !m {
t.Errorf("%s: expected runtime error matching %q, got runtime error %v", name, pat, rterr);
return false;
}
return true;
}
panic("rterr != nil && pat == \"\" should have been handled by the caller");
}
/*
* Test constructors
*/
// Expression compile error
func EErr(expr string, cerr string) test {
return test{"", "", []exprTest{exprTest{expr, nil, ""}}, cerr};
}
// Expression runtime error
func ERTErr(expr string, rterr string) test {
return test{"", "", []exprTest{exprTest{expr, nil, rterr}}, ""};
}
// Expression value
func Val(expr string, val interface{}) test {
return test{"", "", []exprTest{exprTest{expr, val, ""}}, ""};
}
// Statement compile error
func SErr(stmts string, cerr string) test {
return test{stmts, "", nil, cerr};
}
// Statement runtime error
func SRTErr(stmts string, rterr string) test {
return test{stmts, rterr, nil, ""};
}
// Statement runs without error
func SRuns(stmts string) test {
return test{stmts, "", nil, ""};
}
// Statement runs and test one expression's value
func Val1(stmts string, expr1 string, val1 interface{}) test {
return test{stmts, "", []exprTest{exprTest{expr1, val1, ""}}, ""};
}
// Statement runs and test two expressions' values
func Val2(stmts string, expr1 string, val1 interface{}, expr2 string, val2 interface{}) test {
return test{stmts, "", []exprTest{exprTest{expr1, val1, ""}, exprTest{expr2, 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(fr *Frame) {
n := fr.Vars[0].(IntValue).Get();
res := n + 1;
fr.Vars[1].(IntValue).Set(res);
}
type oneTwoFunc struct {};
func (*oneTwoFunc) NewFrame() *Frame {
return &Frame{nil, &[2]Value {}};
}
func (*oneTwoFunc) Call(fr *Frame) {
fr.Vars[0].(IntValue).Set(1);
fr.Vars[1].(IntValue).Set(2);
}
type voidFunc struct {};
func (*voidFunc) NewFrame() *Frame {
return &Frame{nil, []Value {}};
}
func (*voidFunc) Call(fr *Frame) {
}
func newTestScope() *Scope {
sc := universe.ChildScope();
p := token.Position{"<testScope>", 0, 0, 0};
def := func(name string, t Type, val interface{}) {
v, _ := sc.DefineVar(name, p, t);
v.Init = toValue(val);
};
sc.DefineConst("c", p, 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 sc;
}
// 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";
"testing";
)
var undefined = "undefined"
var typeAsExpr = "type .* used as expression"
var badCharLit = "character literal"
var illegalEscape = "illegal char escape"
var opTypes = "illegal (operand|argument) type|cannot index into"
var badAddrOf = "cannot take the address"
var constantTruncated = "constant [^ ]* truncated"
var constantUnderflows = "constant [^ ]* underflows"
var constantOverflows = "constant [^ ]* overflows"
var implLimit = "implementation limit"
var mustBeUnsigned = "must be unsigned"
var divByZero = "divide by zero"
var hugeInteger = bignum.Int(1).Shl(64);
var exprTests = []test {
Val("i", 1),
EErr("zzz", undefined),
// TODO(austin) Test variable in constant context
//EErr("t", typeAsExpr),
Val("'a'", bignum.Int('a')),
Val("'\\uffff'", bignum.Int('\uffff')),
Val("'\\n'", bignum.Int('\n')),
EErr("''+x", badCharLit),
// Produces two parse errors
//EErr("'''", ""),
EErr("'\n'", badCharLit),
EErr("'\\z'", illegalEscape),
EErr("'ab'", badCharLit),
Val("1.0", bignum.Rat(1, 1)),
Val("1.", bignum.Rat(1, 1)),
Val(".1", bignum.Rat(1, 10)),
Val("1e2", bignum.Rat(100, 1)),
Val("\"abc\"", "abc"),
Val("\"\"", ""),
Val("\"\\n\\\"\"", "\n\""),
EErr("\"\\z\"", illegalEscape),
EErr("\"abc", "string not terminated"),
Val("\"abc\" \"def\"", "abcdef"),
EErr("\"abc\" \"\\z\"", illegalEscape),
Val("(i)", 1),
Val("ai[0]", 1),
Val("(&ai)[0]", 1),
Val("ai[1]", 2),
Val("ai[i]", 2),
Val("ai[u]", 2),
EErr("ai[f]", opTypes),
EErr("ai[0][0]", opTypes),
EErr("ai[2]", "index 2 exceeds"),
EErr("ai[1+1]", "index 2 exceeds"),
EErr("ai[-1]", "negative index"),
ERTErr("ai[i+i]", "index 2 exceeds"),
ERTErr("ai[-i]", "negative index"),
EErr("i[0]", opTypes),
EErr("f[0]", opTypes),
Val("aai[0][0]", 1),
Val("aai[1][1]", 4),
EErr("aai[2][0]", "index 2 exceeds"),
EErr("aai[0][2]", "index 2 exceeds"),
Val("sli[0]", 1),
Val("sli[1]", 2),
EErr("sli[-1]", "negative index"),
ERTErr("sli[-i]", "negative index"),
ERTErr("sli[2]", "index 2 exceeds"),
Val("s[0]", uint8('a')),
Val("s[1]", uint8('b')),
EErr("s[-1]", "negative index"),
ERTErr("s[-i]", "negative index"),
ERTErr("s[3]", "index 3 exceeds"),
EErr("1(2)", "cannot call"),
EErr("fn(1,2)", "too many"),
EErr("fn()", "not enough"),
EErr("fn(true)", opTypes),
EErr("fn(true)", "function call"),
// Single argument functions don't say which argument.
//EErr("fn(true)", "argument 1"),
Val("fn(1)", 2),
Val("fn(1.0)", 2),
EErr("fn(1.5)", constantTruncated),
Val("fn(i)", 2),
EErr("fn(u)", opTypes),
EErr("void()+2", opTypes),
EErr("oneTwo()+2", opTypes),
Val("cap(ai)", 2),
Val("cap(&ai)", 2),
Val("cap(aai)", 2),
Val("cap(sli)", 3),
EErr("cap(0)", opTypes),
EErr("cap(i)", opTypes),
EErr("cap(s)", opTypes),
Val("len(s)", 3),
Val("len(ai)", 2),
Val("len(&ai)", 2),
Val("len(aai)", 2),
Val("len(sli)", 2),
// TODO(austin) Test len of map
EErr("len(0)", opTypes),
EErr("len(i)", opTypes),
EErr("*i", opTypes),
Val("*&i", 1),
Val("*&(i)", 1),
EErr("&1", badAddrOf),
EErr("&c", badAddrOf),
Val("*(&ai[0])", 1),
Val("+1", bignum.Int(+1)),
Val("+1.0", bignum.Rat(1, 1)),
EErr("+\"x\"", opTypes),
Val("-42", bignum.Int(-42)),
Val("-i", -1),
Val("-f", -1.0),
// 6g bug?
//Val("-(f-1)", -0.0),
EErr("-\"x\"", opTypes),
// TODO(austin) Test unary !
Val("^2", bignum.Int(^2)),
Val("^(-2)", bignum.Int(^(-2))),
EErr("^2.0", opTypes),
EErr("^2.5", opTypes),
Val("^i", ^1),
Val("^u", ^uint(1)),
EErr("^f", opTypes),
Val("1+i", 2),
Val("1+u", uint(2)),
Val("3.0+i", 4),
Val("1+1", bignum.Int(2)),
Val("f+f", 2.0),
Val("1+f", 2.0),
Val("1.0+1", bignum.Rat(2, 1)),
Val("\"abc\" + \"def\"", "abcdef"),
EErr("i+u", opTypes),
EErr("-1+u", constantUnderflows),
// TODO(austin) Test named types
Val("2-1", bignum.Int(1)),
Val("2.0-1", bignum.Rat(1, 1)),
Val("f-2", -1.0),
// TOOD(austin) bignum can't do negative 0?
//Val("-0.0", XXX),
Val("2*2", bignum.Int(4)),
Val("2*i", 2),
Val("3/2", bignum.Int(1)),
Val("3/i", 3),
EErr("1/0", divByZero),
EErr("1.0/0", divByZero),
ERTErr("i/0", divByZero),
Val("3%2", bignum.Int(1)),
Val("i%2", 1),
EErr("3%0", divByZero),
EErr("3.0%0", opTypes),
ERTErr("i%0", divByZero),
// Examples from "Arithmetic operators"
Val("5/3", bignum.Int(1)),
Val("(i+4)/(i+2)", 1),
Val("5%3", bignum.Int(2)),
Val("(i+4)%(i+2)", 2),
Val("-5/3", bignum.Int(-1)),
Val("(i-6)/(i+2)", -1),
Val("-5%3", bignum.Int(-2)),
Val("(i-6)%(i+2)", -2),
Val("5/-3", bignum.Int(-1)),
Val("(i+4)/(i-4)", -1),
Val("5%-3", bignum.Int(2)),
Val("(i+4)%(i-4)", 2),
Val("-5/-3", bignum.Int(1)),
Val("(i-6)/(i-4)", 1),
Val("-5%-3", bignum.Int(-2)),
Val("(i-6)%(i-4)", -2),
// Examples from "Arithmetic operators"
Val("11/4", bignum.Int(2)),
Val("(i+10)/4", 2),
Val("11%4", bignum.Int(3)),
Val("(i+10)%4", 3),
Val("11>>2", bignum.Int(2)),
Val("(i+10)>>2", 2),
Val("11&3", bignum.Int(3)),
Val("(i+10)&3", 3),
Val("-11/4", bignum.Int(-2)),
Val("(i-12)/4", -2),
Val("-11%4", bignum.Int(-3)),
Val("(i-12)%4", -3),
Val("-11>>2", bignum.Int(-3)),
Val("(i-12)>>2", -3),
Val("-11&3", bignum.Int(1)),
Val("(i-12)&3", 1),
// TODO(austin) Test bit ops
// For shift, we try nearly every combination of positive
// ideal int, negative ideal int, big ideal int, ideal
// fractional float, ideal non-fractional float, int, uint,
// and float.
Val("2<<2", bignum.Int(2<<2)),
EErr("2<<(-1)", constantUnderflows),
EErr("2<<0x10000000000000000", constantOverflows),
EErr("2<<2.5", constantTruncated),
Val("2<<2.0", bignum.Int(2<<2.0)),
EErr("2<<i", mustBeUnsigned),
Val("2<<u", 2<<1),
EErr("2<<f", opTypes),
Val("-2<<2", bignum.Int(-2<<2)),
EErr("-2<<(-1)", constantUnderflows),
EErr("-2<<0x10000000000000000", constantOverflows),
EErr("-2<<2.5", constantTruncated),
Val("-2<<2.0", bignum.Int(-2<<2.0)),
EErr("-2<<i", mustBeUnsigned),
Val("-2<<u", -2<<1),
EErr("-2<<f", opTypes),
Val("0x10000000000000000<<2", hugeInteger.Shl(2)),
EErr("0x10000000000000000<<(-1)", constantUnderflows),
EErr("0x10000000000000000<<0x10000000000000000", constantOverflows),
EErr("0x10000000000000000<<2.5", constantTruncated),
Val("0x10000000000000000<<2.0", hugeInteger.Shl(2)),
EErr("0x10000000000000000<<i", mustBeUnsigned),
EErr("0x10000000000000000<<u", constantOverflows),
EErr("0x10000000000000000<<f", opTypes),
EErr("2.5<<2", opTypes),
EErr("2.0<<2", opTypes),
Val("i<<2", 1<<2),
EErr("i<<(-1)", constantUnderflows),
EErr("i<<0x10000000000000000", constantOverflows),
EErr("i<<2.5", constantTruncated),
Val("i<<2.0", 1<<2),
EErr("i<<i", mustBeUnsigned),
Val("i<<u", 1<<1),
EErr("i<<f", opTypes),
Val("i<<u", 1<<1),
Val("u<<2", uint(1<<2)),
EErr("u<<(-1)", constantUnderflows),
EErr("u<<0x10000000000000000", constantOverflows),
EErr("u<<2.5", constantTruncated),
Val("u<<2.0", uint(1<<2)),
EErr("u<<i", mustBeUnsigned),
Val("u<<u", uint(1<<1)),
EErr("u<<f", opTypes),
Val("u<<u", uint(1<<1)),
EErr("f<<2", opTypes),
// <, <=, >, >=
Val("1<2", 1<2),
Val("1<=2", 1<=2),
Val("2<=2", 2<=2),
Val("1>2", 1>2),
Val("1>=2", 1>=2),
Val("2>=2", 2>=2),
Val("i<2", 1<2),
Val("i<=2", 1<=2),
Val("i+1<=2", 2<=2),
Val("i>2", 1>2),
Val("i>=2", 1>=2),
Val("i+1>=2", 2>=2),
Val("u<2", 1<2),
Val("f<2", 1<2),
Val("s<\"b\"", true),
Val("s<\"a\"", false),
Val("s<=\"abc\"", true),
Val("s>\"aa\"", true),
Val("s>\"ac\"", false),
Val("s>=\"abc\"", true),
EErr("i<u", opTypes),
EErr("i<f", opTypes),
EErr("i<s", opTypes),
EErr("&i<&i", opTypes),
EErr("ai<ai", opTypes),
// ==, !=
Val("1==1", true),
Val("1!=1", false),
Val("1==2", false),
Val("1!=2", true),
Val("1.0==1", true),
Val("1.5==1", false),
Val("i==1", true),
Val("i!=1", false),
Val("i==2", false),
Val("i!=2", true),
Val("u==1", true),
Val("f==1", true),
Val("s==\"abc\"", true),
Val("s!=\"abc\"", false),
Val("s==\"abcd\"", false),
Val("s!=\"abcd\"", true),
Val("&i==&i", true),
Val("&i==&i2", false),
Val("fn==fn", true),
Val("fn==func(int)int{return 0}", false),
EErr("i==u", opTypes),
EErr("i==f", opTypes),
EErr("&i==&f", opTypes),
EErr("ai==ai", opTypes),
EErr("t==t", opTypes),
EErr("fn==oneTwo", opTypes),
}
func TestExpr(t *testing.T) {
runTests(t, "exprTests", exprTests);
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment