1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
package interp_test
import (
"reflect"
"testing"
"github.com/traefik/yaegi/interp"
)
type Helloer interface {
Hello()
}
func Hi(h Helloer) {
println("In Hi:")
h.Hello()
}
// A Wrap represents the wrapper which allows to use objects created by
// the interpreter as Go interfaces (despite limitations in reflect which
// forbid dynamic method creation).
//
// All the struct fields are functions, where the fied name corresponds to
// the method name prefixed by "Do". The function signature must be the
// same as the interface one.
//
// A corresponding Wrap method Xyz which satisfies the interface must exist and
// must invoke the DoXyz function.
//
// To be usable, the interpreter should return a Wrap instance with the relevant
// function fields filled. The application can then invoke methods on it.
// The method calls will be forwarded to the interpreter.
//
// Only the Wrap type definition needs to be exported to the interpreter (not
// the interfaces and methods definitions).
type Wrap struct {
DoHello func() // related to the Hello() method.
// Other interface method wrappers...
}
func (w Wrap) Hello() { w.DoHello() }
func TestExportsSemantics(t *testing.T) {
Foo := &struct{}{}
t.Run("Correct", func(t *testing.T) {
t.Skip()
i := interp.New(interp.Options{})
err := i.Use(interp.Exports{
"foo/foo": {"Foo": reflect.ValueOf(Foo)},
})
if err != nil {
t.Fatal(err)
}
i.ImportUsed()
res, err := i.Eval("foo.Foo")
if err != nil {
t.Fatal(err)
}
if res.Interface() != Foo {
t.Fatalf("expected foo.Foo to equal local Foo")
}
})
t.Run("Incorrect", func(t *testing.T) {
i := interp.New(interp.Options{})
err := i.Use(interp.Exports{
"foo": {"Foo": reflect.ValueOf(Foo)},
})
if err == nil {
t.Fatal("expected error for incorrect Use semantics")
}
})
}
func TestInterface(t *testing.T) {
i := interp.New(interp.Options{})
// export the Wrap type to the interpreter under virtual "wrap" package
err := i.Use(interp.Exports{
"wrap/wrap": {
"Wrap": reflect.ValueOf((*Wrap)(nil)),
},
})
if err != nil {
t.Fatal(err)
}
eval(t, i, `
import "wrap"
type MyInt int
func (m MyInt) Hello() { println("hello from Myint", m) }
func NewMyInt(i int) wrap.Wrap {
m := MyInt(i)
return wrap.Wrap{DoHello: m.Hello}
}
`)
NewMyInt := eval(t, i, "NewMyInt").Interface().(func(int) Wrap)
w := NewMyInt(4)
Hi(w)
}
type T struct{}
func (t T) Bar(s ...string) {}
func TestCallBinVariadicMethod(t *testing.T) {
i := interp.New(interp.Options{})
err := i.Use(interp.Exports{
"mypkg/mypkg": {
"T": reflect.ValueOf((*T)(nil)),
},
})
if err != nil {
t.Fatal(err)
}
eval(t, i, `
package p
import "mypkg"
func Foo(x mypkg.T) { x.Bar("s") }
`)
v := eval(t, i, "p.Foo")
bar := v.Interface().(func(t T))
bar(T{})
}
|