f72f416913
From-SVN: r171732
264 lines
5.9 KiB
Go
264 lines
5.9 KiB
Go
// 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 (
|
|
"big"
|
|
"flag"
|
|
"fmt"
|
|
"go/token"
|
|
"log"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"testing"
|
|
)
|
|
|
|
// All tests are done using the same file set.
|
|
var fset = token.NewFileSet()
|
|
|
|
// 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) {
|
|
delta := 1
|
|
if testing.Short() {
|
|
delta = 16
|
|
}
|
|
for i := 0; i < len(tests); i += delta {
|
|
name := fmt.Sprintf("%s[%d]", baseName, i)
|
|
tests[i].run(t, name)
|
|
}
|
|
}
|
|
|
|
func (a test) run(t *testing.T, name string) {
|
|
w := newTestWorld()
|
|
for _, j := range a {
|
|
src := j.code + ";" // trailing semicolon to finish statement
|
|
if noisy {
|
|
println("code:", src)
|
|
}
|
|
|
|
code, err := w.Compile(fset, 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, err1 := regexp.MatchString(pat, err.String())
|
|
if err1 != nil {
|
|
t.Fatalf("compile regexp %s: %v", pat, err1)
|
|
}
|
|
return ok
|
|
}
|
|
|
|
|
|
/*
|
|
* Test constructors
|
|
*/
|
|
|
|
// Expression compile error
|
|
func CErr(expr string, cerr string) test { return test([]job{{code: expr, cerr: cerr}}) }
|
|
|
|
// Expression runtime error
|
|
func RErr(expr string, rterr string) test { return test([]job{{code: expr, rterr: rterr}}) }
|
|
|
|
// Expression value
|
|
func Val(expr string, val interface{}) test {
|
|
return test([]job{{code: expr, val: toValue(val)}})
|
|
}
|
|
|
|
// Statement runs without error
|
|
func Run(stmts string) test { return test([]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{{code: stmt1, noval: true}, {code: stmt2, noval: true}})
|
|
}
|
|
|
|
// Statement runs and test one expression's value
|
|
func Val1(stmts string, expr1 string, val1 interface{}) test {
|
|
return test([]job{
|
|
{code: stmts, noval: true},
|
|
{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{
|
|
{code: stmts, noval: true},
|
|
{code: expr1, val: toValue(val1)},
|
|
{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 *big.Int:
|
|
return &idealIntV{val}
|
|
case float64:
|
|
r := float64V(val)
|
|
return &r
|
|
case *big.Rat:
|
|
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.Panicf("toValue(%T) not implemented", val)
|
|
panic("unreachable")
|
|
}
|
|
|
|
/*
|
|
* Default test scope
|
|
*/
|
|
|
|
type testFunc struct{}
|
|
|
|
func (*testFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} }
|
|
|
|
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, make([]Value, 2)} }
|
|
|
|
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(big.NewInt(1)))
|
|
def("i", IntType, 1)
|
|
def("i2", IntType, 2)
|
|
def("u", UintType, uint(1))
|
|
def("f", Float64Type, 1.0)
|
|
def("s", StringType, "abc")
|
|
def("t", NewStructType([]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
|
|
}
|