This adds a function to get an exported memory by name, allowing end users to check ahead of time if memory writes might fail. This also renames memory.len to size to as that's what the spec calls it. Signed-off-by: Adrian Cole <adrian@tetrate.io>
451 lines
12 KiB
Go
451 lines
12 KiB
Go
package internalwasm
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMemoryInstance_HasLen(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: make([]byte, 100)}
|
|
|
|
tests := []struct {
|
|
name string
|
|
offset uint32
|
|
sizeInBytes uint64
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "simple valid arguments",
|
|
offset: 0, // arbitrary valid offset
|
|
sizeInBytes: 8, // arbitrary valid size
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "maximum valid sizeInBytes",
|
|
offset: memory.Size() - 8,
|
|
sizeInBytes: 8,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "sizeInBytes exceeds the valid size by 1",
|
|
offset: 100, // arbitrary valid offset
|
|
sizeInBytes: uint64(memory.Size() - 99),
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "offset exceeds the memory size",
|
|
offset: memory.Size(),
|
|
sizeInBytes: 1, // arbitrary size
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.expected, memory.hasSize(tc.offset, uint32(tc.sizeInBytes)))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_ReadUint32Le(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
memory []byte
|
|
offset uint32
|
|
expected uint32
|
|
expectedOk bool
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.MaxUint32,
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
memory: []byte{0xfe, 0xff, 0xff, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.MaxUint32 - 1,
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: 1,
|
|
memory: []byte{0x00, 0x1, 0x00, 0x00, 0x00},
|
|
expected: 1, // arbitrary valid v
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff},
|
|
offset: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: tc.memory}
|
|
|
|
v, ok := memory.ReadUint32Le(tc.offset)
|
|
require.Equal(t, tc.expectedOk, ok)
|
|
require.Equal(t, tc.expected, v)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_ReadUint64Le(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
memory []byte
|
|
offset uint32
|
|
expected uint64
|
|
expectedOk bool
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.MaxUint64,
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
memory: []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.MaxUint64 - 1,
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: 1,
|
|
memory: []byte{0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
expected: 1, // arbitrary valid v
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
offset: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: tc.memory}
|
|
|
|
v, ok := memory.ReadUint64Le(tc.offset)
|
|
require.Equal(t, tc.expectedOk, ok)
|
|
require.Equal(t, tc.expected, v)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_ReadFloat32Le(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
memory []byte
|
|
offset uint32
|
|
expected float32
|
|
expectedOk bool
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
memory: []byte{0xff, 0x00, 0x00, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.Float32frombits(uint32(0xff0000ff)),
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
memory: []byte{0xfe, 0x00, 0x00, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.Float32frombits(uint32(0xff0000fe)),
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: 1,
|
|
memory: []byte{0x00, 0xcd, 0xcc, 0xcc, 0x3d},
|
|
expected: 0.1, // arbitrary valid v
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff},
|
|
offset: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: tc.memory}
|
|
|
|
v, ok := memory.ReadFloat32Le(tc.offset)
|
|
require.Equal(t, tc.expectedOk, ok)
|
|
require.Equal(t, tc.expected, v)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_ReadFloat64Le(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
memory []byte
|
|
offset uint32
|
|
expected float64
|
|
expectedOk bool
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
memory: []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.Float64frombits(uint64(0xff000000000000ff)),
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f},
|
|
offset: 0, // arbitrary valid offset.
|
|
expected: math.MaxFloat64, // arbitrary valid v
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: 1,
|
|
memory: []byte{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f},
|
|
expected: math.MaxFloat64, // arbitrary valid v
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
offset: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: tc.memory}
|
|
|
|
v, ok := memory.ReadFloat64Le(tc.offset)
|
|
require.Equal(t, tc.expectedOk, ok)
|
|
require.Equal(t, tc.expected, v)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_WriteUint32Le(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: make([]byte, 100)}
|
|
|
|
tests := []struct {
|
|
name string
|
|
offset uint32
|
|
v uint32
|
|
expectedOk bool
|
|
expectedBytes []byte
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.MaxUint32,
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff},
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.MaxUint32 - 1,
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xfe, 0xff, 0xff, 0xff},
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: memory.Size() - 4, // 4 is the size of uint32
|
|
v: 1, // arbitrary valid v
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0x1, 0x00, 0x00, 0x00},
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
offset: memory.Size() - 4 + 1, // 4 is the size of uint32
|
|
v: 1, // arbitrary valid v
|
|
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedOk, memory.WriteUint32Le(tc.offset, tc.v))
|
|
if tc.expectedOk {
|
|
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of uint32
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_WriteUint64Le(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: make([]byte, 100)}
|
|
tests := []struct {
|
|
name string
|
|
offset uint32
|
|
v uint64
|
|
expectedOk bool
|
|
expectedBytes []byte
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.MaxUint64,
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.MaxUint64 - 1,
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: memory.Size() - 8, // 8 is the size of uint64
|
|
v: 1, // arbitrary valid v
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
offset: memory.Size() - 8 + 1, // 8 is the size of uint64
|
|
v: 1, // arbitrary valid v
|
|
expectedOk: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedOk, memory.WriteUint64Le(tc.offset, tc.v))
|
|
if tc.expectedOk {
|
|
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of uint64
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_WriteFloat32Le(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: make([]byte, 100)}
|
|
|
|
tests := []struct {
|
|
name string
|
|
offset uint32
|
|
v float32
|
|
expectedOk bool
|
|
expectedBytes []byte
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.Float32frombits(uint32(0xff0000ff)),
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xff, 0x00, 0x00, 0xff},
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.Float32frombits(uint32(0xff0000fe)), // arbitrary valid v
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xfe, 0x00, 0x00, 0xff},
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: memory.Size() - 4, // 4 is the size of float32
|
|
v: 0.1, // arbitrary valid v
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xcd, 0xcc, 0xcc, 0x3d},
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
offset: memory.Size() - 4 + 1, // 4 is the size of float32
|
|
v: math.MaxFloat32, // arbitrary valid v
|
|
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedOk, memory.WriteFloat32Le(tc.offset, tc.v))
|
|
if tc.expectedOk {
|
|
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of float32
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMemoryInstance_WriteFloat64Le(t *testing.T) {
|
|
memory := &MemoryInstance{Buffer: make([]byte, 100)}
|
|
tests := []struct {
|
|
name string
|
|
offset uint32
|
|
v float64
|
|
expectedOk bool
|
|
expectedBytes []byte
|
|
}{
|
|
{
|
|
name: "valid offset with an endian-insensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.Float64frombits(uint64(0xff000000000000ff)),
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff},
|
|
},
|
|
{
|
|
name: "valid offset with an endian-sensitive v",
|
|
offset: 0, // arbitrary valid offset.
|
|
v: math.MaxFloat64, // arbitrary valid v
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f},
|
|
},
|
|
{
|
|
name: "maximum boundary valid offset",
|
|
offset: memory.Size() - 8, // 8 is the size of float64
|
|
v: math.MaxFloat64, // arbitrary valid v
|
|
expectedOk: true,
|
|
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f},
|
|
},
|
|
{
|
|
name: "offset exceeds the maximum valid offset by 1",
|
|
offset: memory.Size() - 8 + 1, // 8 is the size of float64
|
|
v: math.MaxFloat64, // arbitrary valid v
|
|
expectedOk: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedOk, memory.WriteFloat64Le(tc.offset, tc.v))
|
|
if tc.expectedOk {
|
|
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of float64
|
|
}
|
|
})
|
|
}
|
|
}
|