wa-lang.org/wazero@v1.0.2/internal/wasm/host_test.go (about) 1 package wasm 2 3 import ( 4 "context" 5 "testing" 6 7 "wa-lang.org/wazero/api" 8 "wa-lang.org/wazero/internal/testing/require" 9 ) 10 11 func argsSizesGet(ctx context.Context, mod api.Module, resultArgc, resultArgvBufSize uint32) uint32 { 12 return 0 13 } 14 15 func fdWrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) uint32 { 16 return 0 17 } 18 19 func swap(ctx context.Context, x, y uint32) (uint32, uint32) { 20 return y, x 21 } 22 23 func TestNewHostModule(t *testing.T) { 24 functionArgsSizesGet := "args_sizes_get" 25 functionFdWrite := "fd_write" 26 functionSwap := "swap" 27 28 tests := []struct { 29 name, moduleName string 30 nameToGoFunc map[string]interface{} 31 expected *Module 32 }{ 33 { 34 name: "empty", 35 expected: &Module{}, 36 }, 37 { 38 name: "only name", 39 moduleName: "test", 40 expected: &Module{NameSection: &NameSection{ModuleName: "test"}}, 41 }, 42 { 43 name: "funcs", 44 moduleName: "wasi_snapshot_preview1", 45 nameToGoFunc: map[string]interface{}{ 46 functionArgsSizesGet: argsSizesGet, 47 functionFdWrite: fdWrite, 48 }, 49 expected: &Module{ 50 TypeSection: []*FunctionType{ 51 {Params: []ValueType{i32, i32}, Results: []ValueType{i32}}, 52 {Params: []ValueType{i32, i32, i32, i32}, Results: []ValueType{i32}}, 53 }, 54 FunctionSection: []Index{0, 1}, 55 CodeSection: []*Code{MustParseGoReflectFuncCode(argsSizesGet), MustParseGoReflectFuncCode(fdWrite)}, 56 ExportSection: []*Export{ 57 {Name: "args_sizes_get", Type: ExternTypeFunc, Index: 0}, 58 {Name: "fd_write", Type: ExternTypeFunc, Index: 1}, 59 }, 60 NameSection: &NameSection{ 61 ModuleName: "wasi_snapshot_preview1", 62 FunctionNames: NameMap{ 63 {Index: 0, Name: "args_sizes_get"}, 64 {Index: 1, Name: "fd_write"}, 65 }, 66 }, 67 }, 68 }, 69 { 70 name: "multi-value", 71 moduleName: "swapper", 72 nameToGoFunc: map[string]interface{}{ 73 functionSwap: swap, 74 }, 75 expected: &Module{ 76 TypeSection: []*FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}}, 77 FunctionSection: []Index{0}, 78 CodeSection: []*Code{MustParseGoReflectFuncCode(swap)}, 79 ExportSection: []*Export{{Name: "swap", Type: ExternTypeFunc, Index: 0}}, 80 NameSection: &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}}, 81 }, 82 }, 83 } 84 85 for _, tt := range tests { 86 tc := tt 87 88 t.Run(tc.name, func(t *testing.T) { 89 m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, 90 api.CoreFeaturesV1|api.CoreFeatureMultiValue) 91 require.NoError(t, e) 92 requireHostModuleEquals(t, tc.expected, m) 93 }) 94 } 95 } 96 97 func requireHostModuleEquals(t *testing.T, expected, actual *Module) { 98 // `require.Equal(t, expected, actual)` fails reflect pointers don't match, so brute compare: 99 require.Equal(t, expected.TypeSection, actual.TypeSection) 100 require.Equal(t, expected.ImportSection, actual.ImportSection) 101 require.Equal(t, expected.FunctionSection, actual.FunctionSection) 102 require.Equal(t, expected.TableSection, actual.TableSection) 103 require.Equal(t, expected.MemorySection, actual.MemorySection) 104 require.Equal(t, expected.GlobalSection, actual.GlobalSection) 105 require.Equal(t, expected.ExportSection, actual.ExportSection) 106 require.Equal(t, expected.StartSection, actual.StartSection) 107 require.Equal(t, expected.ElementSection, actual.ElementSection) 108 require.Equal(t, expected.DataSection, actual.DataSection) 109 require.Equal(t, expected.NameSection, actual.NameSection) 110 111 // Special case because reflect.Value can't be compared with Equals 112 // TODO: This is copy/paste with /builder_test.go 113 require.Equal(t, len(expected.CodeSection), len(actual.CodeSection)) 114 for i, c := range expected.CodeSection { 115 actualCode := actual.CodeSection[i] 116 require.True(t, actualCode.IsHostFunction) 117 require.Equal(t, c.GoFunc, actualCode.GoFunc) 118 119 // Not wasm 120 require.Nil(t, actualCode.Body) 121 require.Nil(t, actualCode.LocalTypes) 122 } 123 } 124 125 func TestNewHostModule_Errors(t *testing.T) { 126 tests := []struct { 127 name, moduleName string 128 nameToGoFunc map[string]interface{} 129 expectedErr string 130 }{ 131 { 132 name: "not a function", 133 nameToGoFunc: map[string]interface{}{"fn": t}, 134 expectedErr: "func[.fn] kind != func: ptr", 135 }, 136 { 137 name: "function has multiple results", 138 nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }}, 139 expectedErr: "func[.fn] multiple result types invalid as feature \"multi-value\" is disabled", 140 }, 141 } 142 143 for _, tt := range tests { 144 tc := tt 145 146 t.Run(tc.name, func(t *testing.T) { 147 _, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, api.CoreFeaturesV1) 148 require.EqualError(t, e, tc.expectedErr) 149 }) 150 } 151 }