Files
wazero/internal/wasm/binary/names_test.go
Crypt Keeper 329ccca6b1 Switches from gofmt to gofumpt (#848)
This switches to gofumpt and applies changes, as I've noticed working
in dapr (who uses this) that it finds some things that are annoying,
such as inconsistent block formatting in test tables.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-11-09 05:48:24 +01:00

313 lines
9.7 KiB
Go

package binary
import (
"bytes"
"testing"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
)
func TestEncodeNameSectionData(t *testing.T) {
tests := []struct {
name string
input *wasm.NameSection
expected []byte
}{
{
name: "empty",
input: &wasm.NameSection{},
},
{
name: "only module",
// e.g. (module $simple )
input: &wasm.NameSection{ModuleName: "simple"},
expected: []byte{
subsectionIDModuleName, 0x07, // 7 bytes
0x06, // the Module name simple is 6 bytes long
's', 'i', 'm', 'p', 'l', 'e',
},
},
{
name: "module and function name",
// (module $simple
// (import "" "Hello" (func $hello))
// (start $hello)
// )
input: &wasm.NameSection{
ModuleName: "simple",
FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "hello"}},
},
expected: []byte{
subsectionIDModuleName, 0x07, // 7 bytes
0x06, // the Module name simple is 6 bytes long
's', 'i', 'm', 'p', 'l', 'e',
subsectionIDFunctionNames, 0x08, // 8 bytes
0x01, // one function name
0x00, // the function index is zero
0x05, // the function name hello is 5 bytes long
'h', 'e', 'l', 'l', 'o',
},
},
{
name: "two function names", // e.g. TinyGo which at one point didn't set a module name
// (module
// (import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi.args_sizes_get (param i32, i32) (result i32)))
// (import "wasi_snapshot_preview1" "fd_write" (func $wasi.fd_write (param i32, i32, i32, i32) (result i32)))
// )
input: &wasm.NameSection{
FunctionNames: wasm.NameMap{
{Index: wasm.Index(0), Name: "wasi.args_sizes_get"},
{Index: wasm.Index(1), Name: "wasi.fd_write"},
},
},
expected: []byte{
subsectionIDFunctionNames, 0x25, // 37 bytes
0x02, // two function names
0x00, // the function index is zero
0x13, // the function name wasi.args_sizes_get is 19 bytes long
'w', 'a', 's', 'i', '.', 'a', 'r', 'g', 's', '_', 's', 'i', 'z', 'e', 's', '_', 'g', 'e', 't',
0x01, // the function index is one
0x0d, // the function name wasi.fd_write is 13 bytes long
'w', 'a', 's', 'i', '.', 'f', 'd', '_', 'w', 'r', 'i', 't', 'e',
},
},
{
name: "function with local names",
// (module
// (import "Math" "Mul" (func $mul (param $x f32) (param $y f32) (result f32)))
// (import "Math" "Add" (func $add (param $l f32) (param $r f32) (result f32)))
// )
input: &wasm.NameSection{
FunctionNames: wasm.NameMap{
{Index: wasm.Index(0), Name: "mul"},
{Index: wasm.Index(1), Name: "add"},
},
LocalNames: wasm.IndirectNameMap{
{Index: wasm.Index(0), NameMap: wasm.NameMap{
{Index: wasm.Index(0), Name: "x"},
{Index: wasm.Index(1), Name: "y"},
}},
{Index: wasm.Index(1), NameMap: wasm.NameMap{
{Index: wasm.Index(0), Name: "l"},
{Index: wasm.Index(1), Name: "r"},
}},
},
},
expected: []byte{
subsectionIDFunctionNames, 0x0b, // 7 bytes
0x02, // two function names
0x00, 0x03, 'm', 'u', 'l', // index 0, size of "mul", "mul"
0x01, 0x03, 'a', 'd', 'd', // index 1, size of "add", "add"
subsectionIDLocalNames, 0x11, // 17 bytes
0x02, // two functions
0x00, 0x02, // index 0 has 2 locals
0x00, 0x01, 'x', // index 0, size of "x", "x"
0x01, 0x01, 'y', // index 1, size of "y", "y"
0x01, 0x02, // index 1 has 2 locals
0x00, 0x01, 'l', // index 0, size of "l", "l"
0x01, 0x01, 'r', // index 1, size of "r", "r"
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
bytes := encodeNameSectionData(tc.input)
require.Equal(t, tc.expected, bytes)
})
}
}
func TestEncodeNameSubsection(t *testing.T) {
subsectionID := uint8(1)
name := []byte("simple")
require.Equal(t, []byte{
subsectionID,
byte(1 + 6), // 1 is the size of 6 in LEB128 encoding
6, 's', 'i', 'm', 'p', 'l', 'e',
}, encodeNameSubsection(subsectionID, encodeSizePrefixed(name)))
}
func TestEncodeNameAssoc(t *testing.T) {
na := &wasm.NameAssoc{Index: 1, Name: "hello"}
require.Equal(t, []byte{byte(na.Index), 5, 'h', 'e', 'l', 'l', 'o'}, encodeNameAssoc(na))
}
func TestEncodeNameMap(t *testing.T) {
na := &wasm.NameAssoc{Index: 1, Name: "hello"}
m := wasm.NameMap{na}
require.Equal(t, []byte{byte(1), byte(na.Index), 5, 'h', 'e', 'l', 'l', 'o'}, encodeNameMap(m))
}
func TestEncodeSizePrefixed(t *testing.T) {
// We expect size in bytes (LEB128 encoded) then the bytes
require.Equal(t, []byte{5, 'h', 'e', 'l', 'l', 'o'}, encodeSizePrefixed([]byte("hello")))
}
// TestDecodeNameSection relies on unit tests for NameSection.EncodeData, specifically that the encoding is
// both known and correct. This avoids having to copy/paste or share variables to assert against byte arrays.
func TestDecodeNameSection(t *testing.T) {
tests := []struct {
name string
input *wasm.NameSection // round trip test!
}{
{
name: "empty",
input: &wasm.NameSection{},
},
{
name: "only module",
input: &wasm.NameSection{ModuleName: "simple"},
},
{
name: "module and function name",
input: &wasm.NameSection{
ModuleName: "simple",
FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "wasi.hello"}},
},
},
{
name: "two function names",
input: &wasm.NameSection{
FunctionNames: wasm.NameMap{
{Index: wasm.Index(0), Name: "wasi.args_sizes_get"},
{Index: wasm.Index(1), Name: "wasi.fd_write"},
},
},
},
{
name: "function with local names",
input: &wasm.NameSection{
FunctionNames: wasm.NameMap{
{Index: wasm.Index(0), Name: "mul"},
{Index: wasm.Index(1), Name: "add"},
},
LocalNames: wasm.IndirectNameMap{
{Index: wasm.Index(0), NameMap: wasm.NameMap{
{Index: wasm.Index(0), Name: "x"},
{Index: wasm.Index(1), Name: "y"},
}},
{Index: wasm.Index(1), NameMap: wasm.NameMap{
{Index: wasm.Index(0), Name: "l"},
{Index: wasm.Index(1), Name: "r"},
}},
},
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
data := encodeNameSectionData(tc.input)
ns, err := decodeNameSection(bytes.NewReader(data), uint64(len(data)))
require.NoError(t, err)
require.Equal(t, tc.input, ns)
})
}
}
func TestDecodeNameSection_Errors(t *testing.T) {
// currently, we ignore the size of known subsections
ignoredSubsectionSize := byte(50)
tests := []struct {
name string
input []byte
expectedErr string
}{
{
name: "EOF after module name subsection ID",
input: []byte{subsectionIDModuleName},
expectedErr: "failed to read the size of subsection[0]: EOF",
},
{
name: "EOF after function names subsection ID",
input: []byte{subsectionIDFunctionNames},
expectedErr: "failed to read the size of subsection[1]: EOF",
},
{
name: "EOF after local names subsection ID",
input: []byte{subsectionIDLocalNames},
expectedErr: "failed to read the size of subsection[2]: EOF",
},
{
name: "EOF after unknown subsection ID",
input: []byte{4},
expectedErr: "failed to read the size of subsection[4]: EOF",
},
{
name: "EOF after module name subsection size",
input: []byte{subsectionIDModuleName, ignoredSubsectionSize},
expectedErr: "failed to read module name size: EOF",
},
{
name: "EOF after function names subsection size",
input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize},
expectedErr: "failed to read the function count of subsection[1]: EOF",
},
{
name: "EOF after local names subsection size",
input: []byte{subsectionIDLocalNames, ignoredSubsectionSize},
expectedErr: "failed to read the function count of subsection[2]: EOF",
},
{
name: "EOF skipping unknown subsection size",
input: []byte{4, 100},
expectedErr: "failed to skip subsection[4]: EOF",
},
{
name: "EOF after module name size",
input: []byte{subsectionIDModuleName, ignoredSubsectionSize, 5},
expectedErr: "failed to read module name: EOF",
},
{
name: "EOF after function name count",
input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize, 2},
expectedErr: "failed to read a function index in subsection[1]: EOF",
},
{
name: "EOF after local names function count",
input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2},
expectedErr: "failed to read a function index in subsection[2]: EOF",
},
{
name: "EOF after function name index",
input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize, 2, 0},
expectedErr: "failed to read function[0] name size: EOF",
},
{
name: "EOF after local names function index",
input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2, 0},
expectedErr: "failed to read the local count for function[0]: EOF",
},
{
name: "EOF after function name size",
input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize, 2, 0, 5},
expectedErr: "failed to read function[0] name: EOF",
},
{
name: "EOF after local names count for a function index",
input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2, 0, 2},
expectedErr: "failed to read a local index of function[0]: EOF",
},
{
name: "EOF after local name size",
input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2, 0, 2, 1},
expectedErr: "failed to read function[0] local[1] name size: EOF",
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
_, err := decodeNameSection(bytes.NewReader(tc.input), uint64(len(tc.input)))
require.EqualError(t, err, tc.expectedErr)
})
}
}