183 lines
5.7 KiB
Go
183 lines
5.7 KiB
Go
package wasm
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
|
. "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1"
|
|
)
|
|
|
|
func argsSizesGet(ctx context.Context, mod api.Module, resultArgc, resultArgvBufSize uint32) uint32 {
|
|
return 0
|
|
}
|
|
|
|
func fdWrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) uint32 {
|
|
return 0
|
|
}
|
|
|
|
func swap(ctx context.Context, x, y uint32) (uint32, uint32) {
|
|
return y, x
|
|
}
|
|
|
|
func TestNewHostModule(t *testing.T) {
|
|
swapName := "swap"
|
|
|
|
tests := []struct {
|
|
name, moduleName string
|
|
nameToGoFunc map[string]interface{}
|
|
funcToNames map[string]*HostFuncNames
|
|
expected *Module
|
|
}{
|
|
{
|
|
name: "empty",
|
|
expected: &Module{},
|
|
},
|
|
{
|
|
name: "only name",
|
|
moduleName: "test",
|
|
expected: &Module{NameSection: &NameSection{ModuleName: "test"}},
|
|
},
|
|
{
|
|
name: "funcs",
|
|
moduleName: InternalModuleName,
|
|
nameToGoFunc: map[string]interface{}{
|
|
ArgsSizesGetName: argsSizesGet,
|
|
FdWriteName: fdWrite,
|
|
},
|
|
funcToNames: map[string]*HostFuncNames{
|
|
ArgsSizesGetName: {
|
|
Name: ArgsSizesGetName,
|
|
ParamNames: []string{"result.argc", "result.argv_len"},
|
|
ResultNames: []string{"errno"},
|
|
},
|
|
FdWriteName: {
|
|
Name: FdWriteName,
|
|
ParamNames: []string{"fd", "iovs", "iovs_len", "result.size"},
|
|
ResultNames: []string{"errno"},
|
|
},
|
|
},
|
|
expected: &Module{
|
|
TypeSection: []FunctionType{
|
|
{Params: []ValueType{i32, i32}, Results: []ValueType{i32}},
|
|
{Params: []ValueType{i32, i32, i32, i32}, Results: []ValueType{i32}},
|
|
},
|
|
FunctionSection: []Index{0, 1},
|
|
CodeSection: []*Code{MustParseGoReflectFuncCode(argsSizesGet), MustParseGoReflectFuncCode(fdWrite)},
|
|
ExportSection: []Export{
|
|
{Name: ArgsSizesGetName, Type: ExternTypeFunc, Index: 0},
|
|
{Name: FdWriteName, Type: ExternTypeFunc, Index: 1},
|
|
},
|
|
NameSection: &NameSection{
|
|
ModuleName: InternalModuleName,
|
|
FunctionNames: NameMap{
|
|
{Index: 0, Name: ArgsSizesGetName},
|
|
{Index: 1, Name: FdWriteName},
|
|
},
|
|
LocalNames: IndirectNameMap{
|
|
{Index: 0, NameMap: NameMap{
|
|
{Index: 0, Name: "result.argc"},
|
|
{Index: 1, Name: "result.argv_len"},
|
|
}},
|
|
{Index: 1, NameMap: NameMap{
|
|
{Index: 0, Name: "fd"},
|
|
{Index: 1, Name: "iovs"},
|
|
{Index: 2, Name: "iovs_len"},
|
|
{Index: 3, Name: "result.size"},
|
|
}},
|
|
},
|
|
ResultNames: IndirectNameMap{
|
|
{Index: 0, NameMap: NameMap{{Index: 0, Name: "errno"}}},
|
|
{Index: 1, NameMap: NameMap{{Index: 0, Name: "errno"}}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multi-value",
|
|
moduleName: "swapper",
|
|
nameToGoFunc: map[string]interface{}{
|
|
swapName: swap,
|
|
},
|
|
funcToNames: map[string]*HostFuncNames{swapName: {}},
|
|
expected: &Module{
|
|
TypeSection: []FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{MustParseGoReflectFuncCode(swap)},
|
|
ExportSection: []Export{{Name: "swap", Type: ExternTypeFunc, Index: 0}},
|
|
NameSection: &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, tc.funcToNames, api.CoreFeaturesV2)
|
|
require.NoError(t, e)
|
|
requireHostModuleEquals(t, tc.expected, m)
|
|
require.True(t, m.IsHostModule)
|
|
})
|
|
}
|
|
}
|
|
|
|
func requireHostModuleEquals(t *testing.T, expected, actual *Module) {
|
|
// `require.Equal(t, expected, actual)` fails reflect pointers don't match, so brute compare:
|
|
require.Equal(t, expected.TypeSection, actual.TypeSection)
|
|
require.Equal(t, expected.ImportSection, actual.ImportSection)
|
|
require.Equal(t, expected.FunctionSection, actual.FunctionSection)
|
|
require.Equal(t, expected.TableSection, actual.TableSection)
|
|
require.Equal(t, expected.MemorySection, actual.MemorySection)
|
|
require.Equal(t, expected.GlobalSection, actual.GlobalSection)
|
|
require.Equal(t, expected.ExportSection, actual.ExportSection)
|
|
require.Equal(t, expected.StartSection, actual.StartSection)
|
|
require.Equal(t, expected.ElementSection, actual.ElementSection)
|
|
require.Equal(t, expected.DataSection, actual.DataSection)
|
|
require.Equal(t, expected.NameSection, actual.NameSection)
|
|
|
|
// Special case because reflect.Value can't be compared with Equals
|
|
// TODO: This is copy/paste with /builder_test.go
|
|
require.Equal(t, len(expected.CodeSection), len(actual.CodeSection))
|
|
for i, c := range expected.CodeSection {
|
|
actualCode := actual.CodeSection[i]
|
|
require.Equal(t, c.GoFunc, actualCode.GoFunc)
|
|
|
|
// Not wasm
|
|
require.Nil(t, actualCode.Body)
|
|
require.Nil(t, actualCode.LocalTypes)
|
|
}
|
|
}
|
|
|
|
func TestNewHostModule_Errors(t *testing.T) {
|
|
tests := []struct {
|
|
name, moduleName string
|
|
nameToGoFunc map[string]interface{}
|
|
funcToNames map[string]*HostFuncNames
|
|
expectedErr string
|
|
}{
|
|
{
|
|
name: "not a function",
|
|
nameToGoFunc: map[string]interface{}{"fn": t},
|
|
funcToNames: map[string]*HostFuncNames{"fn": {}},
|
|
expectedErr: "func[.fn] kind != func: ptr",
|
|
},
|
|
{
|
|
name: "function has multiple results",
|
|
nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }},
|
|
funcToNames: map[string]*HostFuncNames{"fn": {}},
|
|
expectedErr: "func[.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, tc.funcToNames, api.CoreFeaturesV1)
|
|
require.EqualError(t, e, tc.expectedErr)
|
|
})
|
|
}
|
|
}
|