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  }