package gojs_test import ( "apigo.cc/apigo/gojs" "apigo.cc/apigo/plugin" "fmt" "github.com/ssgo/log" "github.com/ssgo/u" "runtime" "strings" "testing" "time" ) type Object struct { id string plus func(int, int) int baseNumber int } type PlusConfig struct { BaseNumber int Action func(int, int) int } func (obj *Object) GetId() string { return obj.id } func (obj *Object) SetPlusFuncAndRun(cb func(int, int) int) int { obj.plus = cb return obj.plus(1, 2) } func (obj *Object) SetPlusFunc(cb func(int, int) int) { obj.plus = cb } func (obj *Object) SetPlusFunc2(conf PlusConfig) { obj.baseNumber = conf.BaseNumber obj.plus = conf.Action } func (obj *Object) Plus(a ...int) int { return obj.plus(a[0], a[1]) } func (obj *Object) PlusX(a int, b int) int { return obj.plus(a, b) + obj.baseNumber } func (obj *Object) AsyncPlus(a int, b int) int { ch := make(chan bool, 1) r := 0 go func() { time.Sleep(300 * time.Millisecond) r = obj.plus(a, b) ch <- true }() <-ch return r } type TestBirthday struct { Year int Month int Day int } type TestBaseUser struct { Id int Name string } type TestUser struct { TestBaseUser Birthday *TestBirthday } func (b *TestBirthday) String() string { return fmt.Sprintln(b.Year, b.Month, b.Day) } func (u *TestUser) GetBirthdayString() string { return u.Birthday.String() } func init() { defaultObject := Object{id: "o-00"} plg := plugin.Plugin{ Id: "obj", Name: "test obj plugin", Objects: map[string]interface{}{ "name": "1233", "list1": []interface{}{1, "2", map[string]interface{}{"aaa": 111, "bbb": "222"}, true}, "log": log.DefaultLogger.Info, "getId": defaultObject.GetId, "new": func(id string) interface{} { return &Object{id: id} }, "test1": func(id int, id2 uint16, id3 float64) *TestUser { return nil }, "echo": func(text string, echoFunc func(text string, t2 int) string, t3 []uint32, t4 *bool) interface{} { return echoFunc(text, 0) }, "echoTimes": func(echoFunc func(text string)) { for i := 0; i < 5; i++ { //time.Sleep(100 * time.Millisecond) echoFunc(fmt.Sprint(i)) } }, }, } plugin.Register(plg) gojs.SetPluginsConfig(map[string]plugin.Config{ "obj": plugin.Config{}, }) } func test(t *testing.T, name string, check bool, extArgs ...interface{}) { if check { fmt.Println(u.Green(name), u.BGreen("[OK]")) } else { fmt.Println(u.Red(name), u.BRed("[Failed]"), fmt.Sprintln(extArgs...)) t.Error(name) } } func TestTS(t *testing.T) { plg := plugin.Get("obj") exportCode, _ := gojs.MakePluginCode(plg) test(t, "ts code", strings.Contains(exportCode, "(echoFunc: (text: string) => void)"), exportCode) } func TestGlobal(t *testing.T) { code := ` import lg from 'logger' import console from 'console' log('test', 'name', 'log') lg.info('test', {name:'log'}) console.info('test', {name:'log'}) return plus(number,2) ` globals := map[string]interface{}{ "number": 9, "log": log.DefaultLogger.Info, "plus": func(i, j int) int { return i + j }, } r, _ := gojs.Run(code, &gojs.RuntimeOption{ Globals: globals, }) test(t, "call", u.Int(r) == 11, r) } func TestPlugin(t *testing.T) { rt := gojs.New(nil) rt.Exec("import obj from 'obj'") r, _ := rt.Run("return obj.getId()") test(t, "obj.getId()", u.String(r) == "o-00", r) r, _ = rt.Run(` o = obj.new('o-01') return o.getId() `) test(t, "new obj.getId()", u.String(r) == "o-01", r) t1 := time.Now() r, _ = rt.Run(` out = '' obj.echo('123', function(text){ out = text }, null) return out `) t2 := time.Now() fmt.Println("time:", t2.UnixMicro()-t1.UnixMicro()) test(t, "callback", u.String(r) == "123", r) t1 = time.Now() r, _ = rt.Run(` out = '' obj.echoTimes(function(text){ out += text }) return out `) t2 = time.Now() fmt.Println("time:", t2.UnixMicro()-t1.UnixMicro()) test(t, "callbacks", u.String(r) == "01234", r) } func TestCallback(t *testing.T) { r, _ := gojs.Run(` import obj from 'obj' o = obj.new('cb') return o.setPlusFuncAndRun(function (a, b){ return a+b }) `, nil) test(t, "plus with callback1", u.String(r) == "3", r) r, _ = gojs.Run(` import obj from 'obj' o = obj.new('cb') o.setPlusFunc(function(a, b){ return a+b }) return o.plus(1, 2) `, nil) test(t, "plus with callback2.1", u.String(r) == "3", r) r, _ = gojs.Run(` import obj from 'obj' o = obj.new('cb') function plus(a, b){ return a+b } o.setPlusFunc(plus) return o.plus(1, 2) `, nil) test(t, "plus with callback2.2", u.String(r) == "3", r) r, _ = gojs.Run(` import obj from 'obj' o = obj.new('cb') o.setPlusFunc2({action: function(a, b){ return a+b }}) return o.plus(1, 2) `, nil) test(t, "plus with callback3.1", u.String(r) == "3", r) r, _ = gojs.Run(` import obj from 'obj' o = obj.new('cb') function plus(a, b){ return a+b } o.setPlusFunc2({baseNumber:10, action: plus}) return o.plusX(1, 2) `, nil) test(t, "plus with callback3.2", u.String(r) == "13", r) rt := gojs.New(nil) rt.Exec(` import obj from 'obj' o = obj.new('cb') `) rt.Run(` o.setPlusFunc2({action: function(a, b){ return a+b }}) `) r, _ = rt.Run(` return o.plus(1, 2) `) test(t, "plus with callback4.1", u.String(r) == "3", r) r, _ = rt.Run(` return o.asyncPlus(1, 2) `) // unexpected, maybe get exception in other go coroutine test(t, "plus with callback4.2", u.String(r) == "3" || u.String(r) == "0", r) rt.Close() } func TestInnerModule(t *testing.T) { rt := gojs.New(nil) rt.Exec(` import {loadFile} from "std" import * as os from "os" import console from 'console' globalThis.goMod = loadFile('go.mod') globalThis.platform = os.platform `) goModStr, _ := rt.Run(` return goMod `) platformStr, _ := rt.Run(` return platform `) test(t, "check std.loadFile", strings.Contains(u.String(goModStr), "apigo"), goModStr) test(t, "check os.platform", u.String(platformStr) != "", platformStr) rt.Close() } func TestAsync(t *testing.T) { rt := gojs.New(nil) rt.Start(` import console from 'console' let t1 = new Date().getTime() new Promise(resolve => { setTimeout(resolve, 200) }).then(()=>{ globalThis.promiseValue = new Date().getTime() - t1 }) setTimeout(()=>{ globalThis.timeoutValue = new Date().getTime() - t1 }, 300) `) timeoutValue, _ := rt.Run(` return timeoutValue `) promiseValue, _ := rt.Run(` return promiseValue `) test(t, "check timeoutValue", u.Int(timeoutValue) >= 300 && u.Int(timeoutValue) <= 400, timeoutValue) test(t, "check promiseValue", u.Int(promiseValue) >= 200 && u.Int(promiseValue) <= 300, promiseValue) rt.Close() } func BenchmarkEcho(tb *testing.B) { tb.StopTimer() ms1 := runtime.MemStats{} runtime.ReadMemStats(&ms1) tb.StartTimer() for i := 0; i < tb.N; i++ { gojs.Run(`return 1`, nil) } tb.StopTimer() ms2 := runtime.MemStats{} runtime.ReadMemStats(&ms2) runtime.GC() ms3 := runtime.MemStats{} runtime.ReadMemStats(&ms3) fmt.Println(">>", ms1.HeapInuse, ms2.HeapInuse, ms3.HeapInuse) } func BenchmarkCallback(tb *testing.B) { tb.StopTimer() ms1 := runtime.MemStats{} runtime.ReadMemStats(&ms1) tb.StartTimer() for i := 0; i < tb.N; i++ { gojs.Start(` import obj from 'obj' out = '' obj.echoTimes(function(text){ out += text }) `, nil) } tb.StopTimer() ms2 := runtime.MemStats{} runtime.ReadMemStats(&ms2) runtime.GC() ms3 := runtime.MemStats{} runtime.ReadMemStats(&ms3) fmt.Println(">>", ms1.HeapInuse, ms2.HeapInuse, ms3.HeapInuse) }