242 lines
5 KiB
Go
242 lines
5 KiB
Go
package stdlib_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/d5/tengo/v2"
|
|
"github.com/d5/tengo/v2/require"
|
|
"github.com/d5/tengo/v2/stdlib"
|
|
)
|
|
|
|
type ARR = []interface{}
|
|
type MAP = map[string]interface{}
|
|
type IARR []interface{}
|
|
type IMAP map[string]interface{}
|
|
|
|
func TestAllModuleNames(t *testing.T) {
|
|
names := stdlib.AllModuleNames()
|
|
require.Equal(t,
|
|
len(stdlib.BuiltinModules)+len(stdlib.SourceModules),
|
|
len(names))
|
|
}
|
|
|
|
func TestModulesRun(t *testing.T) {
|
|
// os.File
|
|
expect(t, `
|
|
os := import("os")
|
|
out := ""
|
|
|
|
write_file := func(filename, data) {
|
|
file := os.create(filename)
|
|
if !file { return file }
|
|
|
|
if res := file.write(bytes(data)); is_error(res) {
|
|
return res
|
|
}
|
|
|
|
return file.close()
|
|
}
|
|
|
|
read_file := func(filename) {
|
|
file := os.open(filename)
|
|
if !file { return file }
|
|
|
|
data := bytes(100)
|
|
cnt := file.read(data)
|
|
if is_error(cnt) {
|
|
return cnt
|
|
}
|
|
|
|
file.close()
|
|
return data[:cnt]
|
|
}
|
|
|
|
if write_file("./temp", "foobar") {
|
|
out = string(read_file("./temp"))
|
|
}
|
|
|
|
os.remove("./temp")
|
|
`, "foobar")
|
|
|
|
// exec.command
|
|
expect(t, `
|
|
out := ""
|
|
os := import("os")
|
|
cmd := os.exec("echo", "foo", "bar")
|
|
if !is_error(cmd) {
|
|
out = cmd.output()
|
|
}
|
|
`, []byte("foo bar\n"))
|
|
|
|
}
|
|
|
|
func TestGetModules(t *testing.T) {
|
|
mods := stdlib.GetModuleMap()
|
|
require.Equal(t, 0, mods.Len())
|
|
|
|
mods = stdlib.GetModuleMap("os")
|
|
require.Equal(t, 1, mods.Len())
|
|
require.NotNil(t, mods.Get("os"))
|
|
|
|
mods = stdlib.GetModuleMap("os", "rand")
|
|
require.Equal(t, 2, mods.Len())
|
|
require.NotNil(t, mods.Get("os"))
|
|
require.NotNil(t, mods.Get("rand"))
|
|
|
|
mods = stdlib.GetModuleMap("text", "text")
|
|
require.Equal(t, 1, mods.Len())
|
|
require.NotNil(t, mods.Get("text"))
|
|
|
|
mods = stdlib.GetModuleMap("nonexisting", "text")
|
|
require.Equal(t, 1, mods.Len())
|
|
require.NotNil(t, mods.Get("text"))
|
|
}
|
|
|
|
type callres struct {
|
|
t *testing.T
|
|
o interface{}
|
|
e error
|
|
}
|
|
|
|
func (c callres) call(funcName string, args ...interface{}) callres {
|
|
if c.e != nil {
|
|
return c
|
|
}
|
|
|
|
var oargs []tengo.Object
|
|
for _, v := range args {
|
|
oargs = append(oargs, object(v))
|
|
}
|
|
|
|
switch o := c.o.(type) {
|
|
case *tengo.BuiltinModule:
|
|
m, ok := o.Attrs[funcName]
|
|
if !ok {
|
|
return callres{t: c.t, e: fmt.Errorf(
|
|
"function not found: %s", funcName)}
|
|
}
|
|
|
|
f, ok := m.(*tengo.UserFunction)
|
|
if !ok {
|
|
return callres{t: c.t, e: fmt.Errorf(
|
|
"non-callable: %s", funcName)}
|
|
}
|
|
|
|
res, err := f.Value(oargs...)
|
|
return callres{t: c.t, o: res, e: err}
|
|
case *tengo.UserFunction:
|
|
res, err := o.Value(oargs...)
|
|
return callres{t: c.t, o: res, e: err}
|
|
case *tengo.ImmutableMap:
|
|
m, ok := o.Value[funcName]
|
|
if !ok {
|
|
return callres{t: c.t, e: fmt.Errorf("function not found: %s", funcName)}
|
|
}
|
|
|
|
f, ok := m.(*tengo.UserFunction)
|
|
if !ok {
|
|
return callres{t: c.t, e: fmt.Errorf("non-callable: %s", funcName)}
|
|
}
|
|
|
|
res, err := f.Value(oargs...)
|
|
return callres{t: c.t, o: res, e: err}
|
|
default:
|
|
panic(fmt.Errorf("unexpected object: %v (%T)", o, o))
|
|
}
|
|
}
|
|
|
|
func (c callres) expect(expected interface{}, msgAndArgs ...interface{}) {
|
|
require.NoError(c.t, c.e, msgAndArgs...)
|
|
require.Equal(c.t, object(expected), c.o, msgAndArgs...)
|
|
}
|
|
|
|
func (c callres) expectError() {
|
|
require.Error(c.t, c.e)
|
|
}
|
|
|
|
func module(t *testing.T, moduleName string) callres {
|
|
mod := stdlib.GetModuleMap(moduleName).GetBuiltinModule(moduleName)
|
|
if mod == nil {
|
|
return callres{t: t, e: fmt.Errorf("module not found: %s", moduleName)}
|
|
}
|
|
|
|
return callres{t: t, o: mod}
|
|
}
|
|
|
|
func object(v interface{}) tengo.Object {
|
|
switch v := v.(type) {
|
|
case tengo.Object:
|
|
return v
|
|
case string:
|
|
return &tengo.String{Value: v}
|
|
case int64:
|
|
return &tengo.Int{Value: v}
|
|
case int: // for convenience
|
|
return &tengo.Int{Value: int64(v)}
|
|
case bool:
|
|
if v {
|
|
return tengo.TrueValue
|
|
}
|
|
return tengo.FalseValue
|
|
case rune:
|
|
return &tengo.Char{Value: v}
|
|
case byte: // for convenience
|
|
return &tengo.Char{Value: rune(v)}
|
|
case float64:
|
|
return &tengo.Float{Value: v}
|
|
case []byte:
|
|
return &tengo.Bytes{Value: v}
|
|
case MAP:
|
|
objs := make(map[string]tengo.Object)
|
|
for k, v := range v {
|
|
objs[k] = object(v)
|
|
}
|
|
|
|
return &tengo.Map{Value: objs}
|
|
case ARR:
|
|
var objs []tengo.Object
|
|
for _, e := range v {
|
|
objs = append(objs, object(e))
|
|
}
|
|
|
|
return &tengo.Array{Value: objs}
|
|
case IMAP:
|
|
objs := make(map[string]tengo.Object)
|
|
for k, v := range v {
|
|
objs[k] = object(v)
|
|
}
|
|
|
|
return &tengo.ImmutableMap{Value: objs}
|
|
case IARR:
|
|
var objs []tengo.Object
|
|
for _, e := range v {
|
|
objs = append(objs, object(e))
|
|
}
|
|
|
|
return &tengo.ImmutableArray{Value: objs}
|
|
case time.Time:
|
|
return &tengo.Time{Value: v}
|
|
case []int:
|
|
var objs []tengo.Object
|
|
for _, e := range v {
|
|
objs = append(objs, &tengo.Int{Value: int64(e)})
|
|
}
|
|
|
|
return &tengo.Array{Value: objs}
|
|
}
|
|
|
|
panic(fmt.Errorf("unknown type: %T", v))
|
|
}
|
|
|
|
func expect(t *testing.T, input string, expected interface{}) {
|
|
s := tengo.NewScript([]byte(input))
|
|
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
|
c, err := s.Run()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, c)
|
|
v := c.Get("out")
|
|
require.NotNil(t, v)
|
|
require.Equal(t, expected, v.Value())
|
|
}
|