Repository: tetratelabs/wazero
Pull request #278 (2022-02-22 15:48:41)

wasi: backfill docs and tests of fd_read and fd_write
This PR backfills the docs and tests of `fd_write`, and backfills the tests of `fd_read`.

codefromthecrypt: polished the docs, will do the unit tests next
codefromthecrypt: ok done rewriting the tests also
codefromthecrypt: Since this was a cleanup PR anyway, I'll go ahead and merge my revisions to this. Thanks for the first stab @nullpo-head and looking forward to the next!

Patch:
diff --git a/internal/wasi/wasi.go b/internal/wasi/wasi.go
index 2abf6236..f9dc3568 100644
--- a/internal/wasi/wasi.go
+++ b/internal/wasi/wasi.go
@@ -113,14 +113,28 @@ const (
 	ImportFdPrestatDirName = `(import "wasi_snapshot_preview1" "fd_prestat_dir_name"
     (func $wasi.fd_prestat_dir_name (param $fd i32) (param $path i32) (param $path_len i32) (result (;errno;) i32)))`
 
-	FunctionFdPwrite             = "fd_pwrite"
-	FunctionFdRead               = "fd_read"
-	FunctionFdReaddir            = "fd_readdir"
-	FunctionFdRenumber           = "fd_renumber"
-	FunctionFdSeek               = "fd_seek"
-	FunctionFdSync               = "fd_sync"
-	FunctionFdTell               = "fd_tell"
-	FunctionFdWrite              = "fd_write"
+	FunctionFdPwrite = "fd_pwrite"
+
+	// FunctionFdRead read bytes from a file descriptor
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_read
+	FunctionFdRead = "fd_read"
+	// ImportFdRead is the WebAssembly 1.0 (MVP) Text format import of FunctionFdPrestatGet
+	ImportFdRead = `(import "wasi_snapshot_preview1" "fd_read"
+    (func $wasi.fd_read (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $result.size i32) (result (;errno;) i32)))`
+
+	FunctionFdReaddir  = "fd_readdir"
+	FunctionFdRenumber = "fd_renumber"
+	FunctionFdSeek     = "fd_seek"
+	FunctionFdSync     = "fd_sync"
+	FunctionFdTell     = "fd_tell"
+
+	// FunctionFdWrite write bytes to a file descriptor
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_write
+	FunctionFdWrite = "fd_write"
+	// ImportFdWrite is the WebAssembly 1.0 (MVP) Text format import of FunctionFdPrestatGet
+	ImportFdWrite = `(import "wasi_snapshot_preview1" "fd_write"
+    (func $wasi.fd_write (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $result.size i32) (result (;errno;) i32)))`
+
 	FunctionPathCreateDirectory  = "path_create_directory"
 	FunctionPathFilestatGet      = "path_filestat_get"
 	FunctionPathFilestatSetTimes = "path_filestat_set_times"
@@ -169,9 +183,10 @@ const (
 // parameter is a memory offset to write the result to. As memory offsets are uint32, each parameter representing a
 // result is uint32.
 //
-// Note: The WASI specification is sometimes ambiguous. Please see internal/wasi/RATIONALE.md for rationale on decisions that impact portability.
-//
-// Note: Errno mappings are not defined in WASI, yet, so these mappings are best efforts by maintainers.
+// Note: The WASI specification is sometimes ambiguous resulting in some runtimes interpreting the same function
+// ways. wasi.Errno mappings are not defined in WASI, yet, so these mappings are best efforts by maintainers. When in
+// doubt about portability, first look at internal/wasi/RATIONALE.md and if needed an issue on
+// https://github.com/WebAssembly/WASI/issues
 //
 // Note: In WebAssembly 1.0 (MVP), there may be up to one Memory per store, which means wasm.Memory is always the
 // wasm.Store Memories index zero: `store.Memories[0].Buffer`
@@ -191,7 +206,7 @@ type SnapshotPreview1 interface {
 	//   * ArgsSizesGet result argv_buf_size bytes are written to this offset
 	//
 	// For example, if ArgsSizesGet wrote argc=2 and argvBufSize=5 for arguments: "a" and "bc"
-	//   and ArgsGet results argv=7 and argvBuf=1, we expect `ctx.Memory` to contain:
+	//    parameters argv=7 and argvBuf=1, this function writes the below to `ctx.Memory`:
 	//
 	//               argvBufSize          uint32le    uint32le
 	//            +----------------+     +--------+  +--------+
@@ -219,7 +234,7 @@ type SnapshotPreview1 interface {
 	// * resultArgvBufSize - is the offset to write the null-terminated argument length to ctx.Memory
 	//
 	// For example, if Args are []string{"a","bc"} and
-	//   ArgsSizesGet parameters resultArgc=1 and resultArgvBufSize=6, we expect `ctx.Memory` to contain:
+	//    parameters resultArgc=1 and resultArgvBufSize=6, this function writes the below to `ctx.Memory`:
 	//
 	//                   uint32le       uint32le
 	//                  +--------+     +--------+
@@ -248,7 +263,7 @@ type SnapshotPreview1 interface {
 	//   * EnvironSizesGet result environBufSize bytes are written to this offset
 	//
 	// For example, if EnvironSizesGet wrote environc=2 and environBufSize=9 for environment variables: "a=b", "b=cd"
-	//   and EnvironGet parameters environ=11 and environBuf=1, we expect `ctx.Memory` to contain:
+	//   and parameters environ=11 and environBuf=1, this function writes the below to `ctx.Memory`:
 	//
 	//                           environBufSize                 uint32le    uint32le
 	//              +------------------------------------+     +--------+  +--------+
@@ -275,7 +290,7 @@ type SnapshotPreview1 interface {
 	// * resultEnvironBufSize - is the offset to write the null-terminated environment variable length to ctx.Memory
 	//
 	// For example, if Environ is []string{"a=b","b=cd"} and
-	//   EnvironSizesGet parameters are resultEnvironc=1 and resultEnvironBufSize=6, we expect `ctx.Memory` to contain:
+	//    parameters resultEnvironc=1 and resultEnvironBufSize=6, this function writes the below to `ctx.Memory`:
 	//
 	//                   uint32le       uint32le
 	//                  +--------+     +--------+
@@ -303,7 +318,7 @@ type SnapshotPreview1 interface {
 	//   * the timestamp is epoch nanoseconds encoded as a uint64 little-endian encoding.
 	//
 	// For example, if time.Now returned exactly midnight UTC 2022-01-01 (1640995200000000000), and
-	//   ClockTimeGet resultTimestamp=1, we expect `ctx.Memory` to contain:
+	//   parameters resultTimestamp=1, this function writes the below to `ctx.Memory`:
 	//
 	//                                      uint64le
 	//                    +------------------------------------------+
@@ -322,7 +337,7 @@ type SnapshotPreview1 interface {
 
 	// FdClose is the WASI function to close a file descriptor. This returns ErrnoBadf if the fd is invalid.
 	//
-	// * fd - the file decriptor to close
+	// * fd - the file descriptor to close
 	//
 	// Note: ImportFdClose shows this signature in the WebAssembly 1.0 (MVP) Text Format.
 	// Note: This is similar to `close` in POSIX.
@@ -340,117 +355,162 @@ type SnapshotPreview1 interface {
 	// TODO: wasi.FdPread
 
 	// FdPrestatGet is the WASI function to return the prestat data of a file descriptor.
-	// This returns ErrnoBadf if the fd is invalid.
+	// This returns wasi.ErrnoBadf if the fd is invalid.
 	//
-	// * fd - the file decriptor to get the prestat
+	// * fd - the file descriptor to get the prestat
 	// * resultPrestat - the offset to write the result prestat data
 	//
-	// A prestat is a union data which consists of two fields, uint8 `tag` indicating the type of the prestat data,
-	// and uint32 contents data that varies according to `tag`. They have the following memory layout.
-	// * tag - uint8. offset 0
-	// * contents - uint32. offset 4
-	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#prestat
+	// prestat byte layout is 8 bytes, beginning with an 8-bit tag and 3 pad bytes. The only valid tag is `prestat_dir`,
+	// which is tag zero. This simplifies the byte layout to 4 empty bytes followed by the uint32le encoded path length.
 	//
-	// For example, suppose fd 3 is a file descriptor with a prestat data of tag = `prestat_dir` (value: 0).
-	// For this tag, the contents is prNameLen. Suppose it has value 3 in this example.
-	// Now, if FdPrestatGet parameters fd = 3 and resultPrestat = 1, we expect `ctx.Memory` to contain:
+	// For example, the directory name corresponding with `fd` was "/tmp" and
+	//    parameter resultPrestat=1, this function writes the below to `ctx.Memory`:
 	//
 	//                     padding   uint32le
 	//          uint8 --+  +-----+  +--------+
 	//                  |  |     |  |        |
-	//        []byte{?, 0, 0, 0, 0, 3, 0, 0, 0, ?}
+	//        []byte{?, 0, 0, 0, 0, 4, 0, 0, 0, ?}
 	//  resultPrestat --^           ^
 	//            tag --+           |
-	//                              +-- prNameLen
+	//                              +-- size in bytes of the string "/tmp"
 	//
 	// Note: ImportFdPrestatGet shows this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// See FdPrestatDirName
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#prestat
 	// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_prestat_get
 	FdPrestatGet(ctx wasm.ModuleContext, fd uint32, resultPrestat uint32) wasi.Errno
 
 	// FdPrestatDirName is the WASI function to return the path of the pre-opened directory of a file descriptor.
 	//
-	// * fd - the file decriptor to get the path of the pre-opened directory
+	// * fd - the file descriptor to get the path of the pre-opened directory
 	// * path - the offset in `ctx.Memory` to write the result path
-	// * pathLen - FdPrestatDirName writes the result path string to `path` offset for the length of `pathLen`.
+	// * pathLen - the count of bytes to write to `path`
+	//   * This should match the uint32le FdPrestatGet writes to offset `resultPrestat`+4
 	//
-	// FdPrestatDirName returns the following errors.
-	// * ErrnoBadf - if `fd` is invalid
-	// * ErrnoFault - if `path` is an invalid offset due to the memory constraint
-	// * ErrnoNametoolong - if `pathLen` is longer than the actual length of the result path
-	// TODO: FdPrestatDirName may have to return ErrnoNotdir if the type of the prestat data of `fd` is not a PrestatDir.
+	// The wasi.Errno returned is wasi.ErrnoSuccess except the following error conditions:
+	// * wasi.ErrnoBadf - if `fd` is invalid
+	// * wasi.ErrnoFault - if `path` is an invalid offset due to the memory constraint
+	// * wasi.ErrnoNametoolong - if `pathLen` is longer than the actual length of the result path
 	//
-	// For example, if fd 3 is a file with path = "test",
-	// and FdPrestatDirName parameters fd = 3, path = 1, and pathLen = 3, we expect `ctx.Memory` to contain:
+	// For example, the directory name corresponding with `fd` was "/tmp" and
+	//    parameters path=1 pathLen=4 (correct), this function will write the below to `ctx.Memory`:
 	//
-	//                pathLen
-	//              +---------+
-	//              |         |
-	//   []byte{?, 't', 'e', 's', ?, ?, ?}
+	//                  pathLen
+	//              +--------------+
+	//              |              |
+	//   []byte{?, '/', 't', 'm', 'p', ?}
 	//       path --^
 	//
-	// Note: `prNameLen` field of the result of FdPrestatGet has the exact length of the actual path.
-	// See FdPrestatGet
-	// Note: Some runtimes may have another semantics. See internal/wasi/RATIONALE.md#FdPrestatDirName
 	// Note: ImportFdPrestatDirName shows this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// See FdPrestatGet
 	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_prestat_dir_name
 	FdPrestatDirName(ctx wasm.ModuleContext, fd uint32, path uint32, pathLen uint32) wasi.Errno
+	// TODO: FdPrestatDirName may have to return ErrnoNotdir if the type of the prestat data of `fd` is not a PrestatDir.
 
 	// TODO: wasi.FdPwrite
 
-	// TODO: wasi.FdRead
 	// FdRead is the WASI function to read from a file descriptor.
 	//
-	// * fd - the file decriptor to read from
-	// * iovs - the offset in `ctx.Memory` that contains a series of `iovec`, which indicates where to write the bytes read.
-	// * iovsLen - the number of `ioveC`s in `iovs`
+	// * fd - an opened file descriptor to read data from
+	// * iovs - the offset in `ctx.Memory` to read offset, size pairs representing where to write file data.
+	//   * Both offset and length are encoded as uint32le.
+	// * iovsLen - the count of memory offset, size pairs to read sequentially starting at iovs.
 	// * resultSize - the offset in `ctx.Memory` to write the number of bytes read
-	// `iovec` is a memory offset / length pair, which are named buf/bufLen, encoded sequentially as uint32le.
-	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#iovec
 	//
-	// FdRead returns the following errors.
-	// * ErrnoBadf - if `fd` is invalid
-	// * ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
-	// * ErrnoIo - if an IO related error happens during the operation
+	// The wasi.Errno returned is wasi.ErrnoSuccess except the following error conditions:
+	// * wasi.ErrnoBadf - if `fd` is invalid
+	// * wasi.ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
+	// * wasi.ErrnoIo - if an IO related error happens during the operation
 	//
-	// For example, suppose fd 3 is a file with the contents "test",
-	// and FdRead parameters fd = 3, iovs = 1, and iovsLen = 2, resultSize = 24
-	// and two `iovec`s are {buf: 18, bufLen: 2} and {buf: 21, bufLen: 2}.
-	// Now, `ctx.Memory` contains
+	// For example, this function needs to first read `iovs` to determine where to write contents. If
+	//    parameters iovs=1 iovsLen=2, this function reads two offset/length pairs from `ctx.Memory`:
 	//
 	//                      iovs[0]                  iovs[1]
-	//              +--------------------+   +--------------------+
-	//              |uint32le    uint32le|   |uint32le    uint32le|
-	//              +--------+  +--------+   +--------+  +--------+
-	//              |        |  |        |   |        |  |        |
-	//   []byte{?, 18, 0, 0, 0, 2, 0, 0, 0, 21, 0, 0, 0, 2, 0, 0, 0, ?... }
-	//       iovs --^           ^            ^           ^    17th --^
-	//              |           |            |           |
-	//        buf --+  bufLen --+      buf --+  bufLen --+
-	//
-	// After FdRead completes, we expect `ctx.Memory` to contain:
-	//
-	//                             iovs[0]      iovs[1]
-	//                             bufLen       bufLen       uint32le
-	//                             +----+       +----+      +--------+
-	//                             |    |       |    |      |        |
-	//   []byte{ same as above.., 't', 'e', ?, 's', 't', ?, 4, 0, 0, 0 }
-	//               iovs[0] buf --^            ^           ^
-	//                  (= 18th)  iovs[1] buf --+           |
-	//                               (= 21st)  resultSize --+
-	//                                           (= 24th)
+	//              +---------------------+   +--------------------+
+	//              | uint32le    uint32le|   |uint32le    uint32le|
+	//              +---------+  +--------+   +--------+  +--------+
+	//              |         |  |        |   |        |  |        |
+	//    []byte{?, 18, 0, 0, 0, 4, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, ?... }
+	//       iovs --^            ^            ^           ^
+	//              |            |            |           |
+	//     offset --+   length --+   offset --+  length --+
+	//
+	// If the contents of the `fd` parameter was "wazero" (6 bytes) and
+	//    parameter resultSize=26, this function writes the below to `ctx.Memory`:
+	//
+	//                       iovs[0].length        iovs[1].length
+	//                      +--------------+       +----+       uint32le
+	//                      |              |       |    |      +--------+
+	//   []byte{ 0..16, ?, 'w', 'a', 'z', 'e', ?, 'r', 'o', ?, 6, 0, 0, 0 }
+	//     iovs[0].offset --^                      ^           ^
+	//                            iovs[1].offset --+           |
+	//                                            resultSize --+
 	//
 	// Note: ImportFdRead shows this signature in the WebAssembly 1.0 (MVP) Text Format.
-	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_read
 	// Note: This is similar to `readv` in POSIX.
+	// See FdWrite
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_read
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#iovec
 	// See https://linux.die.net/man/3/readv
-	// FdRead(ctx wasm.ModuleContext, fd uint32, iovs uint32, iovsLen uint32, resultSize uint32) wasi.Errno
+	FdRead(ctx wasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno
 
 	// TODO: wasi.FdReaddir
 	// TODO: wasi.FdRenumber
 	// TODO: wasi.FdSeek
 	// TODO: wasi.FdSync
 	// TODO: wasi.FdTell
-	// TODO: wasi.FdWrite
+
+	// FdWrite is the WASI function to write to a file descriptor.
+	//
+	// * fd - an opened file descriptor to write data to
+	// * iovs - the offset in `ctx.Memory` to read offset, size pairs representing the data to write to `fd`
+	//   * Both offset and length are encoded as uint32le.
+	// * iovsLen - the count of memory offset, size pairs to read sequentially starting at iovs.
+	// * resultSize - the offset in `ctx.Memory` to write the number of bytes written
+	//
+	// The wasi.Errno returned is wasi.ErrnoSuccess except the following error conditions:
+	// * wasi.ErrnoBadf - if `fd` is invalid
+	// * wasi.ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
+	// * wasi.ErrnoIo - if an IO related error happens during the operation
+	//
+	// For example, this function needs to first read `iovs` to determine what to write to `fd`. If
+	//    parameters iovs=1 iovsLen=2, this function reads two offset/length pairs from `ctx.Memory`:
+	//
+	//                      iovs[0]                  iovs[1]
+	//              +---------------------+   +--------------------+
+	//              | uint32le    uint32le|   |uint32le    uint32le|
+	//              +---------+  +--------+   +--------+  +--------+
+	//              |         |  |        |   |        |  |        |
+	//    []byte{?, 18, 0, 0, 0, 4, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, ?... }
+	//       iovs --^            ^            ^           ^
+	//              |            |            |           |
+	//     offset --+   length --+   offset --+  length --+
+	//
+	// This function reads those chunks `ctx.Memory` into the `fd` sequentially.
+	//
+	//                       iovs[0].length        iovs[1].length
+	//                      +--------------+       +----+
+	//                      |              |       |    |
+	//   []byte{ 0..16, ?, 'w', 'a', 'z', 'e', ?, 'r', 'o', ? }
+	//     iovs[0].offset --^                      ^
+	//                            iovs[1].offset --+
+	//
+	// Since "wazero" was written, if parameter resultSize=26, this function writes the below to `ctx.Memory`:
+	//
+	//                      uint32le
+	//                     +--------+
+	//                     |        |
+	//   []byte{ 0..24, ?, 6, 0, 0, 0', ? }
+	//        resultSize --^
+	//
+	// Note: ImportFdWrite shows this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// Note: This is similar to `writev` in POSIX.
+	// See FdRead
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#ciovec
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_write
+	// See https://linux.die.net/man/3/writev
+	FdWrite(ctx wasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno
+
 	// TODO: PathCreateDirectory
 	// TODO: PathFilestatGet
 	// TODO: PathFilestatSetTimes
@@ -543,13 +603,13 @@ func SnapshotPreview1Functions(opts ...Option) (nameToGoFunc map[string]interfac
 		FunctionFdPrestatGet:     a.FdPrestatGet,
 		FunctionFdPrestatDirName: a.FdPrestatDirName,
 		// TODO: FunctionFdPwrite
-		FunctionFdRead: a.fd_read,
+		FunctionFdRead: a.FdRead,
 		// TODO: FunctionFdReaddir
 		// TODO: FunctionFdRenumber
 		FunctionFdSeek: a.fd_seek,
 		// TODO: FunctionFdSync
 		// TODO: FunctionFdTell
-		FunctionFdWrite: a.fd_write,
+		FunctionFdWrite: a.FdWrite,
 		// TODO: FunctionPathCreateDirectory
 		// TODO: FunctionPathFilestatGet
 		// TODO: FunctionPathFilestatSetTimes
@@ -693,7 +753,8 @@ func (a *wasiAPI) FdPrestatDirName(ctx wasm.ModuleContext, fd uint32, pathPtr ui
 	return wasi.ErrnoSuccess
 }
 
-func (a *wasiAPI) fd_read(ctx wasm.ModuleContext, fd uint32, iovsPtr uint32, iovsLen uint32, nreadPtr uint32) wasi.Errno {
+// FdRead implements SnahpshotPreview1.FdRead
+func (a *wasiAPI) FdRead(ctx wasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno {
 	var reader io.Reader
 
 	switch fd {
@@ -709,7 +770,7 @@ func (a *wasiAPI) fd_read(ctx wasm.ModuleContext, fd uint32, iovsPtr uint32, iov
 
 	var nread uint32
 	for i := uint32(0); i < iovsLen; i++ {
-		iovPtr := iovsPtr + i*8
+		iovPtr := iovs + i*8
 		offset, ok := ctx.Memory().ReadUint32Le(iovPtr)
 		if !ok {
 			return wasi.ErrnoFault
@@ -730,7 +791,7 @@ func (a *wasiAPI) fd_read(ctx wasm.ModuleContext, fd uint32, iovsPtr uint32, iov
 			return wasi.ErrnoIo
 		}
 	}
-	if !ctx.Memory().WriteUint32Le(nreadPtr, nread) {
+	if !ctx.Memory().WriteUint32Le(resultSize, nread) {
 		return wasi.ErrnoFault
 	}
 	return wasi.ErrnoSuccess
@@ -740,7 +801,8 @@ func (a *wasiAPI) fd_seek(ctx wasm.ModuleContext, fd uint32, offset uint64, when
 	return wasi.ErrnoNosys // TODO: implement
 }
 
-func (a *wasiAPI) fd_write(ctx wasm.ModuleContext, fd uint32, iovsPtr uint32, iovsLen uint32, nwrittenPtr uint32) wasi.Errno {
+// FdWrite implements SnapshotPreview1.FdWrite
+func (a *wasiAPI) FdWrite(ctx wasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno {
 	var writer io.Writer
 
 	switch fd {
@@ -758,7 +820,7 @@ func (a *wasiAPI) fd_write(ctx wasm.ModuleContext, fd uint32, iovsPtr uint32, io
 
 	var nwritten uint32
 	for i := uint32(0); i < iovsLen; i++ {
-		iovPtr := iovsPtr + i*8
+		iovPtr := iovs + i*8
 		offset, ok := ctx.Memory().ReadUint32Le(iovPtr)
 		if !ok {
 			return wasi.ErrnoFault
@@ -773,11 +835,11 @@ func (a *wasiAPI) fd_write(ctx wasm.ModuleContext, fd uint32, iovsPtr uint32, io
 		}
 		n, err := writer.Write(b)
 		if err != nil {
-			panic(err)
+			return wasi.ErrnoIo
 		}
 		nwritten += uint32(n)
 	}
-	if !ctx.Memory().WriteUint32Le(nwrittenPtr, nwritten) {
+	if !ctx.Memory().WriteUint32Le(resultSize, nwritten) {
 		return wasi.ErrnoFault
 	}
 	return wasi.ErrnoSuccess


Test patch:
diff --git a/internal/wasi/wasi_test.go b/internal/wasi/wasi_test.go
index 4c8230ca..af143e49 100644
--- a/internal/wasi/wasi_test.go
+++ b/internal/wasi/wasi_test.go
@@ -1,10 +1,12 @@
 package internalwasi
 
 import (
+	"bytes"
 	"context"
 	_ "embed"
 	"errors"
 	"fmt"
+	"io"
 	"math/rand"
 	"testing"
 
@@ -14,9 +16,12 @@ import (
 	"github.com/tetratelabs/wazero/internal/wasm/interpreter"
 	"github.com/tetratelabs/wazero/internal/wasm/text"
 	"github.com/tetratelabs/wazero/wasi"
+	publicwasm "github.com/tetratelabs/wazero/wasm"
 )
 
-func TestNewAPI_Args(t *testing.T) {
+const moduleName = "test"
+
+func TestNewSnapshotPreview1_Args(t *testing.T) {
 	t.Run("valid", func(t *testing.T) {
 		o, err := Args("a", "bc")
 		require.NoError(t, err)
@@ -35,12 +40,11 @@ func TestNewAPI_Args(t *testing.T) {
 	})
 }
 
-func TestAPI_ArgsGet(t *testing.T) {
+func TestSnapshotPreview1_ArgsGet(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
 	argv := uint32(7)    // arbitrary offset
 	argvBuf := uint32(1) // arbitrary offset
-	maskLength := 16     // number of bytes to write '?' to tell what we've written
 	expectedMemory := []byte{
 		'?',                 // argvBuf is after this
 		'a', 0, 'b', 'c', 0, // null terminated "a", "bc"
@@ -50,31 +54,31 @@ func TestAPI_ArgsGet(t *testing.T) {
 		'?', // stopped after encoding
 	}
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionArgsGet, ImportArgsGet, "test", args)
+	store, ctx, fn := instantiateWasmStore(t, FunctionArgsGet, ImportArgsGet, moduleName, args)
 
-	t.Run("ArgsGet", func(t *testing.T) {
-		maskMemory(store, maskLength)
+	t.Run("SnapshotPreview1.ArgsGet", func(t *testing.T) {
+		maskMemory(store, len(expectedMemory))
 
 		// Invoke ArgsGet directly and check the memory side effects.
 		errno := newAPI(args).ArgsGet(ctx, argv, argvBuf)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 
 	t.Run(FunctionArgsGet, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		results, err := store.Engine.Call(ctx, fn, uint64(argv), uint64(argvBuf))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(results[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_ArgsGet_Errors(t *testing.T) {
+func TestSnapshotPreview1_ArgsGet_Errors(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
-	store, ctx, fn := instantiateWasmStore(t, FunctionArgsGet, ImportArgsGet, "test", args)
+	store, ctx, fn := instantiateWasmStore(t, FunctionArgsGet, ImportArgsGet, moduleName, args)
 
 	memorySize := uint32(len(store.Memories[0].Buffer))
 	validAddress := uint32(0) // arbitrary valid address as arguments to args_get. We chose 0 here.
@@ -119,12 +123,11 @@ func TestAPI_ArgsGet_Errors(t *testing.T) {
 	}
 }
 
-func TestAPI_ArgsSizesGet(t *testing.T) {
+func TestSnapshotPreview1_ArgsSizesGet(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
 	resultArgc := uint32(1)        // arbitrary offset
 	resultArgvBufSize := uint32(6) // arbitrary offset
-	maskLength := 11               // number of bytes to write '?' to tell what we've written
 	expectedMemory := []byte{
 		'?',                // resultArgc is after this
 		0x2, 0x0, 0x0, 0x0, // little endian-encoded arg count
@@ -133,32 +136,32 @@ func TestAPI_ArgsSizesGet(t *testing.T) {
 		'?', // stopped after encoding
 	}
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionArgsSizesGet, ImportArgsSizesGet, "test", args)
+	store, ctx, fn := instantiateWasmStore(t, FunctionArgsSizesGet, ImportArgsSizesGet, moduleName, args)
 
-	t.Run("ArgsSizesGet", func(t *testing.T) {
-		maskMemory(store, maskLength)
+	t.Run("SnapshotPreview1.ArgsSizesGet", func(t *testing.T) {
+		maskMemory(store, len(expectedMemory))
 
 		// Invoke ArgsSizesGet directly and check the memory side effects.
 		errno := newAPI(args).ArgsSizesGet(ctx, resultArgc, resultArgvBufSize)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 
 	t.Run(FunctionArgsSizesGet, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		results, err := store.Engine.Call(ctx, fn, uint64(resultArgc), uint64(resultArgvBufSize))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(results[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_ArgsSizesGet_Errors(t *testing.T) {
+func TestSnapshotPreview1_ArgsSizesGet_Errors(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionArgsSizesGet, ImportArgsSizesGet, "test", args)
+	store, ctx, fn := instantiateWasmStore(t, FunctionArgsSizesGet, ImportArgsSizesGet, moduleName, args)
 	memorySize := uint32(len(store.Memories[0].Buffer))
 	validAddress := uint32(0) // arbitrary valid address as arguments to args_sizes_get. We chose 0 here.
 
@@ -185,7 +188,7 @@ func TestAPI_ArgsSizesGet_Errors(t *testing.T) {
 		{
 			name:        "argvBufSize exceeds the maximum valid size by 1",
 			argc:        validAddress,
-			argvBufSize: memorySize - 4 + 1, // 4 is the size of uint32, the type of the buffer size
+			argvBufSize: memorySize - 4 + 1, // 4 is count of bytes to encode uint32le
 		},
 	}
 
@@ -200,7 +203,7 @@ func TestAPI_ArgsSizesGet_Errors(t *testing.T) {
 	}
 }
 
-func TestNewAPI_Environ(t *testing.T) {
+func TestNewSnapshotPreview1_Environ(t *testing.T) {
 	t.Run("valid", func(t *testing.T) {
 		o, err := Environ("a=b", "b=cd")
 		require.NoError(t, err)
@@ -236,12 +239,11 @@ func TestNewAPI_Environ(t *testing.T) {
 	}
 }
 
-func TestAPI_EnvironGet(t *testing.T) {
+func TestSnapshotPreview1_EnvironGet(t *testing.T) {
 	envOpt, err := Environ("a=b", "b=cd")
 	require.NoError(t, err)
 	resultEnviron := uint32(11)   // arbitrary offset
 	resultEnvironBuf := uint32(1) // arbitrary offset
-	maskLength := 20              // number of bytes to write '?' to tell what we've written
 	expectedMemory := []byte{
 		'?',              // environBuf is after this
 		'a', '=', 'b', 0, // null terminated "a=b",
@@ -252,32 +254,32 @@ func TestAPI_EnvironGet(t *testing.T) {
 		'?', // stopped after encoding
 	}
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironGet, ImportEnvironGet, "test", envOpt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironGet, ImportEnvironGet, moduleName, envOpt)
 
-	t.Run("EnvironGet", func(t *testing.T) {
-		maskMemory(store, maskLength)
+	t.Run("SnapshotPreview1.EnvironGet", func(t *testing.T) {
+		maskMemory(store, len(expectedMemory))
 
 		// Invoke EnvironGet directly and check the memory side effects.
 		errno := newAPI(envOpt).EnvironGet(ctx, resultEnviron, resultEnvironBuf)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 
 	t.Run(FunctionEnvironGet, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		results, err := store.Engine.Call(ctx, fn, uint64(resultEnviron), uint64(resultEnvironBuf))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(results[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_EnvironGet_Errors(t *testing.T) {
+func TestSnapshotPreview1_EnvironGet_Errors(t *testing.T) {
 	envOpt, err := Environ("a=bc", "b=cd")
 	require.NoError(t, err)
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironGet, ImportEnvironGet, "test", envOpt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironGet, ImportEnvironGet, moduleName, envOpt)
 	memorySize := uint32(len(store.Memories[0].Buffer))
 	validAddress := uint32(0) // arbitrary valid address as arguments to environ_get. We chose 0 here.
 
@@ -298,7 +300,7 @@ func TestAPI_EnvironGet_Errors(t *testing.T) {
 		},
 		{
 			name: "environPtr exceeds the maximum valid address by 1",
-			// 4*envCount is the expected buffer size for environPtr, 4 is the size of uint32
+			// 4*envCount is the expected length for environPtr, 4 is the size of uint32
 			environ:    memorySize - 4*2 + 1,
 			environBuf: validAddress,
 		},
@@ -321,12 +323,11 @@ func TestAPI_EnvironGet_Errors(t *testing.T) {
 	}
 }
 
-func TestAPI_EnvironSizesGet(t *testing.T) {
+func TestSnapshotPreview1_EnvironSizesGet(t *testing.T) {
 	envOpt, err := Environ("a=b", "b=cd")
 	require.NoError(t, err)
 	resultEnvironc := uint32(1)       // arbitrary offset
 	resultEnvironBufSize := uint32(6) // arbitrary offset
-	maskLength := 11                  // number of bytes to write '?' to tell what we've written
 	expectedMemory := []byte{
 		'?',                // resultEnvironc is after this
 		0x2, 0x0, 0x0, 0x0, // little endian-encoded environment variable count
@@ -335,32 +336,32 @@ func TestAPI_EnvironSizesGet(t *testing.T) {
 		'?', // stopped after encoding
 	}
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironSizesGet, ImportEnvironSizesGet, "test", envOpt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironSizesGet, ImportEnvironSizesGet, moduleName, envOpt)
 
-	t.Run("EnvironSizesGet", func(t *testing.T) {
-		maskMemory(store, maskLength)
+	t.Run("SnapshotPreview1.EnvironSizesGet", func(t *testing.T) {
+		maskMemory(store, len(expectedMemory))
 
 		// Invoke EnvironSizesGet directly and check the memory side effects.
 		errno := newAPI(envOpt).EnvironSizesGet(ctx, resultEnvironc, resultEnvironBufSize)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 
 	t.Run(FunctionEnvironSizesGet, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		results, err := store.Engine.Call(ctx, fn, uint64(resultEnvironc), uint64(resultEnvironBufSize))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(results[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_EnvironSizesGet_Errors(t *testing.T) {
+func TestSnapshotPreview1_EnvironSizesGet_Errors(t *testing.T) {
 	envOpt, err := Environ("a=b", "b=cd")
 	require.NoError(t, err)
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironSizesGet, ImportEnvironSizesGet, "test", envOpt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionEnvironSizesGet, ImportEnvironSizesGet, moduleName, envOpt)
 	memorySize := uint32(len(store.Memories[0].Buffer))
 	validAddress := uint32(0) // arbitrary valid address as arguments to environ_sizes_get. We chose 0 here.
 
@@ -387,7 +388,7 @@ func TestAPI_EnvironSizesGet_Errors(t *testing.T) {
 		{
 			name:           "environBufSizePtr exceeds the maximum valid size by 1",
 			environc:       validAddress,
-			environBufSize: memorySize - 4 + 1, // 4 is the size of uint32, the type of the buffer size
+			environBufSize: memorySize - 4 + 1, // 4 is count of bytes to encode uint32le
 		},
 	}
 
@@ -402,46 +403,45 @@ func TestAPI_EnvironSizesGet_Errors(t *testing.T) {
 	}
 }
 
-// TODO TestAPI_ClockResGet TestAPI_ClockResGet_Errors
+// TODO TestSnapshotPreview1_ClockResGet TestSnapshotPreview1_ClockResGet_Errors
 
-func TestAPI_ClockTimeGet(t *testing.T) {
+func TestSnapshotPreview1_ClockTimeGet(t *testing.T) {
 	epochNanos := uint64(1640995200000000000) // midnight UTC 2022-01-01
 	resultTimestamp := uint32(1)              // arbitrary offset
-	maskLength := 10                          // number of bytes to write '?' to tell what we've written
 	expectedMemory := []byte{
 		'?',                                          // resultTimestamp is after this
 		0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, // little endian-encoded epochNanos
 		'?', // stopped after encoding
-	} // tr
+	}
 
 	clockOpt := func(api *wasiAPI) {
 		api.timeNowUnixNano = func() uint64 { return epochNanos }
 	}
-	store, ctx, fn := instantiateWasmStore(t, FunctionClockTimeGet, ImportClockTimeGet, "test", clockOpt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionClockTimeGet, ImportClockTimeGet, moduleName, clockOpt)
 
-	t.Run("ClockTimeGet", func(t *testing.T) {
-		maskMemory(store, maskLength)
+	t.Run("SnapshotPreview1.ClockTimeGet", func(t *testing.T) {
+		maskMemory(store, len(expectedMemory))
 
 		// invoke ClockTimeGet directly and check the memory side effects!
 		errno := newAPI(clockOpt).ClockTimeGet(ctx, 0 /* TODO: id */, 0 /* TODO: precision */, resultTimestamp)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 
 	t.Run(FunctionClockTimeGet, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		results, err := store.Engine.Call(ctx, fn, 0 /* TODO: id */, 0 /* TODO: precision */, uint64(resultTimestamp))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(results[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_ClockTimeGet_Errors(t *testing.T) {
+func TestSnapshotPreview1_ClockTimeGet_Errors(t *testing.T) {
 	epochNanos := uint64(1640995200000000000) // midnight UTC 2022-01-01
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionClockTimeGet, ImportClockTimeGet, "test", func(api *wasiAPI) {
+	store, ctx, fn := instantiateWasmStore(t, FunctionClockTimeGet, ImportClockTimeGet, moduleName, func(api *wasiAPI) {
 		api.timeNowUnixNano = func() uint64 { return epochNanos }
 	})
 	memorySize := uint32(len(store.Memories[0].Buffer))
@@ -473,19 +473,20 @@ func TestAPI_ClockTimeGet_Errors(t *testing.T) {
 	}
 }
 
-// TODO: TestAPI_FdAdvise TestAPI_FdAdvise_Errors
-// TODO: TestAPI_FdAllocate TestAPI_FdAllocate_Errors
+// TODO: TestSnapshotPreview1_FdAdvise TestSnapshotPreview1_FdAdvise_Errors
+// TODO: TestSnapshotPreview1_FdAllocate TestSnapshotPreview1_FdAllocate_Errors
 
-func TestAPI_FdClose(t *testing.T) {
+func TestSnapshotPreview1_FdClose(t *testing.T) {
 	fdToClose := uint32(3) // arbitrary fd
 	fdToKeep := uint32(4)  // another arbitrary fd
+
 	setupFD := func() (*wasm.Store, *wasm.ModuleContext, *wasm.FunctionInstance, *wasiAPI) {
 		var api *wasiAPI
-		store, ctx, fn := instantiateWasmStore(t, FunctionFdClose, ImportFdClose, "test", func(a *wasiAPI) {
+		store, ctx, fn := instantiateWasmStore(t, FunctionFdClose, ImportFdClose, moduleName, func(a *wasiAPI) {
 			memFs := &MemFS{}
 			a.opened = map[uint32]fileEntry{
 				fdToClose: {
-					path:    "test",
+					path:    "/tmp",
 					fileSys: memFs,
 				},
 				fdToKeep: {
@@ -520,62 +521,61 @@ func TestAPI_FdClose(t *testing.T) {
 	})
 }
 
-// TODO: TestAPI_FdDataSync TestAPI_FdDataSync_Errors
-// TODO: TestAPI_FdFdstatGet TestAPI_FdFdstatGet_Errors
-// TODO: TestAPI_FdFdstatSetFlags TestAPI_FdFdstatSetFlags_Errors
-// TODO: TestAPI_FdFdstatSetRights TestAPI_FdFdstatSetRights_Errors
-// TODO: TestAPI_FdFilestatGet TestAPI_FdFilestatGet_Errors
-// TODO: TestAPI_FdFilestatSetSize TestAPI_FdFilestatSetSize_Errors
-// TODO: TestAPI_FdFilestatSetTimes TestAPI_FdFilestatSetTimes_Errors
-// TODO: TestAPI_FdPread TestAPI_FdPread_Errors
-// TODO: TestAPI_FdPrestatGet TestAPI_FdPrestatGet_Errors
-
-func TestAPI_FdPrestatDirName(t *testing.T) {
+// TODO: TestSnapshotPreview1_FdDataSync TestSnapshotPreview1_FdDataSync_Errors
+// TODO: TestSnapshotPreview1_FdFdstatGet TestSnapshotPreview1_FdFdstatGet_Errors
+// TODO: TestSnapshotPreview1_FdFdstatSetFlags TestSnapshotPreview1_FdFdstatSetFlags_Errors
+// TODO: TestSnapshotPreview1_FdFdstatSetRights TestSnapshotPreview1_FdFdstatSetRights_Errors
+// TODO: TestSnapshotPreview1_FdFilestatGet TestSnapshotPreview1_FdFilestatGet_Errors
+// TODO: TestSnapshotPreview1_FdFilestatSetSize TestSnapshotPreview1_FdFilestatSetSize_Errors
+// TODO: TestSnapshotPreview1_FdFilestatSetTimes TestSnapshotPreview1_FdFilestatSetTimes_Errors
+// TODO: TestSnapshotPreview1_FdPread TestSnapshotPreview1_FdPread_Errors
+// TODO: TestSnapshotPreview1_FdPrestatGet TestSnapshotPreview1_FdPrestatGet_Errors
+
+func TestSnapshotPreview1_FdPrestatDirName(t *testing.T) {
 	fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
 	var api *wasiAPI
-	store, ctx, fn := instantiateWasmStore(t, FunctionFdPrestatDirName, ImportFdPrestatDirName, "test", func(a *wasiAPI) {
+	store, ctx, fn := instantiateWasmStore(t, FunctionFdPrestatDirName, ImportFdPrestatDirName, moduleName, func(a *wasiAPI) {
 		a.opened[fd] = fileEntry{
-			path:    "test",
+			path:    "/tmp",
 			fileSys: &MemFS{},
 		}
 		api = a // for later tests
 	})
 
 	path := uint32(1)    // arbitrary offset
-	pathLen := uint32(3) // shorter than len("test") to test the path is written for the length of pathLen
-	maskLength := 7      // number of bytes to write '?' to tell what we've written
+	pathLen := uint32(3) // shorter than len("/tmp") to test the path is written for the length of pathLen
 	expectedMemory := []byte{
 		'?',
-		't', 'e', 's',
+		'/', 't', 'm',
 		'?', '?', '?',
 	}
 
 	t.Run("SnapshotPreview1.FdPrestatDirName", func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		errno := api.FdPrestatDirName(ctx, fd, path, pathLen)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
+
 	t.Run(FunctionFdPrestatDirName, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
 		ret, err := store.Engine.Call(ctx, fn, uint64(fd), uint64(path), uint64(pathLen))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(ret[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_FdPrestatDirName_Errors(t *testing.T) {
-	dirName := "test"
+func TestSnapshotPreview1_FdPrestatDirName_Errors(t *testing.T) {
+	dirName := "/tmp"
 	opt := Preopen(dirName, &MemFS{})
-	store, ctx, fn := instantiateWasmStore(t, FunctionFdPrestatDirName, ImportFdPrestatDirName, "test", opt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionFdPrestatDirName, ImportFdPrestatDirName, moduleName, opt)
 
 	memorySize := uint32(len(store.Memories[0].Buffer))
-	validAddress := uint32(0)     // Arbitrary valid address as arguments to fd_prestat_dir_name. We chose 0 here.
-	actualPathLen := len(dirName) // Actual length of the dirName as a valid pathLen.
-	fd := uint32(3)               // fd 3 will be opened for the "test" directory after 0, 1, and 2, that are stdin/out/err
+	validAddress := uint32(0) // Arbitrary valid address as arguments to fd_prestat_dir_name. We chose 0 here.
+	fd := uint32(3)           // fd 3 will be opened for the "/tmp" directory after 0, 1, and 2, that are stdin/out/err
 
 	tests := []struct {
 		name          string
@@ -588,28 +588,28 @@ func TestAPI_FdPrestatDirName_Errors(t *testing.T) {
 			name:          "out-of-memory path",
 			fd:            fd,
 			path:          memorySize,
-			pathLen:       uint32(actualPathLen),
+			pathLen:       uint32(len(dirName)),
 			expectedErrno: wasi.ErrnoFault,
 		},
 		{
 			name:          "path exceeds the maximum valid address by 1",
 			fd:            fd,
-			path:          memorySize - uint32(actualPathLen) + 1,
-			pathLen:       uint32(actualPathLen),
+			path:          memorySize - uint32(len(dirName)) + 1,
+			pathLen:       uint32(len(dirName)),
 			expectedErrno: wasi.ErrnoFault,
 		},
 		{
-			name:          "pathLen exceeds the actual length of the dir name",
+			name:          "pathLen exceeds the length of the dir name",
 			fd:            fd,
 			path:          validAddress,
-			pathLen:       uint32(actualPathLen) + 1,
+			pathLen:       uint32(len(dirName)) + 1,
 			expectedErrno: wasi.ErrnoNametoolong,
 		},
 		{
 			name:          "invalid fd",
 			fd:            42, // arbitrary invalid fd
 			path:          validAddress,
-			pathLen:       uint32(actualPathLen) + 1,
+			pathLen:       uint32(len(dirName)) + 1,
 			expectedErrno: wasi.ErrnoBadf,
 		},
 	}
@@ -625,27 +625,348 @@ func TestAPI_FdPrestatDirName_Errors(t *testing.T) {
 	}
 }
 
-// TODO: TestAPI_FdPwrite TestAPI_FdPwrite_Errors
-// TODO: TestAPI_FdRead TestAPI_FdRead_Errors
-// TODO: TestAPI_FdReaddir TestAPI_FdReaddir_Errors
-// TODO: TestAPI_FdRenumber TestAPI_FdRenumber_Errors
-// TODO: TestAPI_FdSeek TestAPI_FdSeek_Errors
-// TODO: TestAPI_FdSync TestAPI_FdSync_Errors
-// TODO: TestAPI_FdTell TestAPI_FdTell_Errors
-// TODO: TestAPI_FdWrite TestAPI_FdWrite_Errors
-// TODO: TestAPI_PathCreateDirectory TestAPI_PathCreateDirectory_Errors
-// TODO: TestAPI_PathFilestatGet TestAPI_PathFilestatGet_Errors
-// TODO: TestAPI_PathFilestatSetTimes TestAPI_PathFilestatSetTimes_Errors
-// TODO: TestAPI_PathLink TestAPI_PathLink_Errors
-// TODO: TestAPI_PathOpen TestAPI_PathOpen_Errors
-// TODO: TestAPI_PathReadlink TestAPI_PathReadlink_Errors
-// TODO: TestAPI_PathRemoveDirectory TestAPI_PathRemoveDirectory_Errors
-// TODO: TestAPI_PathRename TestAPI_PathRename_Errors
-// TODO: TestAPI_PathSymlink TestAPI_PathSymlink_Errors
-// TODO: TestAPI_PathUnlinkFile TestAPI_PathUnlinkFile_Errors
-// TODO: TestAPI_PollOneoff TestAPI_PollOneoff_Errors
-
-func TestAPI_ProcExit(t *testing.T) {
+func TestSnapshotPreview1_FdRead(t *testing.T) {
+	fd := uint32(3)   // arbitrary fd after 0, 1, and 2, that are stdin/out/err
+	iovs := uint32(1) // arbitrary offset
+	initialMemory := []byte{
+		'?',         // `iovs` is after this
+		18, 0, 0, 0, // = iovs[0].offset
+		4, 0, 0, 0, // = iovs[0].length
+		23, 0, 0, 0, // = iovs[1].offset
+		2, 0, 0, 0, // = iovs[1].length
+		'?',
+	}
+	iovsLen := uint32(2)     // The length of iovs
+	resultSize := uint32(26) // arbitrary offset
+	expectedMemory := append(
+		initialMemory,
+		'w', 'a', 'z', 'e', // iovs[0].length bytes
+		'?',      // iovs[1].offset is after this
+		'r', 'o', // iovs[1].length bytes
+		'?',        // resultSize is after this
+		6, 0, 0, 0, // sum(iovs[...].length) == length of "wazero"
+		'?',
+	)
+
+	var api *wasiAPI
+	store, ctx, fn := instantiateWasmStore(t, FunctionFdRead, ImportFdRead, moduleName, func(a *wasiAPI) {
+		api = a // for later tests
+	})
+
+	// TestSnapshotPreview1_FdRead uses a matrix because setting up test files is complicated and has to be clean each time.
+	type fdReadFn func(ctx publicwasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno
+	tests := []struct {
+		name   string
+		fdRead func() fdReadFn
+	}{
+		{"SnapshotPreview1.FdRead", func() fdReadFn {
+			return api.FdRead
+		}},
+		{FunctionFdRead, func() fdReadFn {
+			return func(ctx publicwasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno {
+				ret, err := store.Engine.Call(ctx.(*wasm.ModuleContext), fn, uint64(fd), uint64(iovs), uint64(iovsLen), uint64(resultSize))
+				require.NoError(t, err)
+				return wasi.Errno(ret[0])
+			}
+		}},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+		t.Run(tc.name, func(t *testing.T) {
+			// Create a fresh file to read the contents from
+			file, memFS := createFile(t, "test_path", []byte("wazero"))
+			api.opened[fd] = fileEntry{
+				path:    "test_path",
+				fileSys: memFS,
+				file:    file,
+			}
+			maskMemory(store, len(expectedMemory))
+			copy(store.Memories[0].Buffer[0:], initialMemory)
+
+			errno := tc.fdRead()(ctx, fd, iovs, iovsLen, resultSize)
+			require.Equal(t, wasi.ErrnoSuccess, errno)
+			require.Equal(t, expectedMemory, store.Memories[0].Buffer)
+		})
+	}
+}
+
+func TestSnapshotPreview1_FdRead_Errors(t *testing.T) {
+	validFD := uint64(3)                                // arbitrary valid fd after 0, 1, and 2, that are stdin/out/err
+	file, memFS := createFile(t, "test_path", []byte{}) // file with empty contents
+	store, ctx, fn := instantiateWasmStore(t, FunctionFdRead, ImportFdRead, moduleName, func(a *wasiAPI) {
+		a.opened[uint32(validFD)] = fileEntry{
+			path:    "test_path",
+			fileSys: memFS,
+			file:    file,
+		}
+	})
+
+	tests := []struct {
+		name                          string
+		fd, iovs, iovsLen, resultSize uint64
+		memory                        []byte
+		expectedErrno                 wasi.Errno
+	}{
+		{
+			name:          "invalid fd",
+			fd:            42, // arbitrary invalid fd
+			expectedErrno: wasi.ErrnoBadf,
+		},
+		{
+			name:          "out-of-memory reading iovs[0].offset",
+			fd:            validFD,
+			iovs:          1,
+			memory:        []byte{'?'},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "out-of-memory reading iovs[0].length",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "iovs[0].offset is outside memory",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset = one past the size of this memory
+				1, 0, 0, 0, // = iovs[0].length
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "length to read exceeds memory by 1",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset
+				2, 0, 0, 0, // = iovs[0].length = one past the size of this memory
+				'?',
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "resultSize offset is outside memory",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			resultSize: 10, // 1 past memory
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset
+				1, 0, 0, 0, // = iovs[0].length
+				'?',
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+		t.Run(tc.name, func(t *testing.T) {
+			store.Memories[0].Buffer = tc.memory
+
+			results, err := store.Engine.Call(ctx, fn, tc.fd, tc.iovs, tc.iovsLen, tc.resultSize)
+			require.NoError(t, err)
+			require.Equal(t, tc.expectedErrno, wasi.Errno(results[0])) // results[0] is the errno
+		})
+	}
+}
+
+// TODO: TestSnapshotPreview1_FdReaddir TestSnapshotPreview1_FdReaddir_Errors
+// TODO: TestSnapshotPreview1_FdRenumber TestSnapshotPreview1_FdRenumber_Errors
+// TODO: TestSnapshotPreview1_FdSeek TestSnapshotPreview1_FdSeek_Errors
+// TODO: TestSnapshotPreview1_FdSync TestSnapshotPreview1_FdSync_Errors
+// TODO: TestSnapshotPreview1_FdTell TestSnapshotPreview1_FdTell_Errors
+
+func TestSnapshotPreview1_FdWrite(t *testing.T) {
+	fd := uint32(3)   // arbitrary fd after 0, 1, and 2, that are stdin/out/err
+	iovs := uint32(1) // arbitrary offset
+	initialMemory := []byte{
+		'?',         // `iovs` is after this
+		18, 0, 0, 0, // = iovs[0].offset
+		4, 0, 0, 0, // = iovs[0].length
+		23, 0, 0, 0, // = iovs[1].offset
+		2, 0, 0, 0, // = iovs[1].length
+		'?',                // iovs[0].offset is after this
+		'w', 'a', 'z', 'e', // iovs[0].length bytes
+		'?',      // iovs[1].offset is after this
+		'r', 'o', // iovs[1].length bytes
+		'?',
+	}
+	iovsLen := uint32(2)     // The length of iovs
+	resultSize := uint32(26) // arbitrary offset
+	expectedMemory := append(
+		initialMemory,
+		6, 0, 0, 0, // sum(iovs[...].length) == length of "wazero"
+		'?',
+	)
+
+	var api *wasiAPI
+	store, ctx, fn := instantiateWasmStore(t, FunctionFdWrite, ImportFdWrite, moduleName, func(a *wasiAPI) {
+		api = a // for later tests
+	})
+
+	// TestSnapshotPreview1_FdWrite uses a matrix because setting up test files is complicated and has to be clean each time.
+	type fdWriteFn func(ctx publicwasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno
+	tests := []struct {
+		name    string
+		fdWrite func() fdWriteFn
+	}{
+		{"SnapshotPreview1.FdWrite", func() fdWriteFn {
+			return api.FdWrite
+		}},
+		{FunctionFdWrite, func() fdWriteFn {
+			return func(ctx publicwasm.ModuleContext, fd, iovs, iovsLen, resultSize uint32) wasi.Errno {
+				ret, err := store.Engine.Call(ctx.(*wasm.ModuleContext), fn, uint64(fd), uint64(iovs), uint64(iovsLen), uint64(resultSize))
+				require.NoError(t, err)
+				return wasi.Errno(ret[0])
+			}
+		}},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+		t.Run(tc.name, func(t *testing.T) {
+			// Create a fresh file to write the contents to
+			file, memFS := createFile(t, "test_path", []byte{})
+			api.opened[fd] = fileEntry{
+				path:    "test_path",
+				fileSys: memFS,
+				file:    file,
+			}
+			maskMemory(store, len(expectedMemory))
+			copy(store.Memories[0].Buffer[0:], initialMemory)
+
+			errno := tc.fdWrite()(ctx, fd, iovs, iovsLen, resultSize)
+			require.Equal(t, wasi.ErrnoSuccess, errno)
+			require.Equal(t, expectedMemory, store.Memories[0].Buffer)
+			require.Equal(t, []byte("wazero"), file.buf.Bytes()) // verify the file was actually written
+		})
+	}
+}
+
+func TestSnapshotPreview1_FdWrite_Errors(t *testing.T) {
+	validFD := uint64(3)                                // arbitrary valid fd after 0, 1, and 2, that are stdin/out/err
+	file, memFS := createFile(t, "test_path", []byte{}) // file with empty contents
+	store, ctx, fn := instantiateWasmStore(t, FunctionFdWrite, ImportFdWrite, moduleName, func(a *wasiAPI) {
+		a.opened[uint32(validFD)] = fileEntry{
+			path:    "test_path",
+			fileSys: memFS,
+			file:    file,
+		}
+	})
+
+	tests := []struct {
+		name                          string
+		fd, iovs, iovsLen, resultSize uint64
+		memory                        []byte
+		expectedErrno                 wasi.Errno
+	}{
+		{
+			name:          "invalid fd",
+			fd:            42, // arbitrary invalid fd
+			expectedErrno: wasi.ErrnoBadf,
+		},
+		{
+			name:          "out-of-memory reading iovs[0].offset",
+			fd:            validFD,
+			iovs:          1,
+			memory:        []byte{'?'},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "out-of-memory reading iovs[0].length",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "iovs[0].offset is outside memory",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset = one past the size of this memory
+				1, 0, 0, 0, // = iovs[0].length
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "length to write exceeds memory by 1",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset
+				2, 0, 0, 0, // = iovs[0].length = one past the size of this memory
+				'?',
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+		{
+			name: "resultSize offset is outside memory",
+			fd:   validFD,
+			iovs: 1, iovsLen: 1,
+			resultSize: 10, // 1 past memory
+			memory: []byte{
+				'?',        // `iovs` is after this
+				9, 0, 0, 0, // = iovs[0].offset
+				1, 0, 0, 0, // = iovs[0].length
+				'?',
+			},
+			expectedErrno: wasi.ErrnoFault,
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+		t.Run(tc.name, func(t *testing.T) {
+			store.Memories[0].Buffer = tc.memory
+
+			results, err := store.Engine.Call(ctx, fn, tc.fd, tc.iovs, tc.iovsLen, tc.resultSize)
+			require.NoError(t, err)
+			require.Equal(t, tc.expectedErrno, wasi.Errno(results[0])) // results[0] is the errno
+		})
+	}
+}
+
+func createFile(t *testing.T, path string, contents []byte) (*memFile, *MemFS) {
+	memFS := &MemFS{}
+	f, err := memFS.OpenWASI(0, path, wasi.O_CREATE|wasi.O_TRUNC, wasi.R_FD_WRITE, 0, 0)
+	require.NoError(t, err)
+
+	if _, err := io.Copy(f, bytes.NewBuffer(contents)); err != nil {
+		require.NoError(t, err)
+	}
+
+	return f.(*memFile), memFS
+}
+
+// TODO: TestSnapshotPreview1_PathCreateDirectory TestSnapshotPreview1_PathCreateDirectory_Errors
+// TODO: TestSnapshotPreview1_PathFilestatGet TestSnapshotPreview1_PathFilestatGet_Errors
+// TODO: TestSnapshotPreview1_PathFilestatSetTimes TestSnapshotPreview1_PathFilestatSetTimes_Errors
+// TODO: TestSnapshotPreview1_PathLink TestSnapshotPreview1_PathLink_Errors
+// TODO: TestSnapshotPreview1_PathOpen TestSnapshotPreview1_PathOpen_Errors
+// TODO: TestSnapshotPreview1_PathReadlink TestSnapshotPreview1_PathReadlink_Errors
+// TODO: TestSnapshotPreview1_PathRemoveDirectory TestSnapshotPreview1_PathRemoveDirectory_Errors
+// TODO: TestSnapshotPreview1_PathRename TestSnapshotPreview1_PathRename_Errors
+// TODO: TestSnapshotPreview1_PathSymlink TestSnapshotPreview1_PathSymlink_Errors
+// TODO: TestSnapshotPreview1_PathUnlinkFile TestSnapshotPreview1_PathUnlinkFile_Errors
+// TODO: TestSnapshotPreview1_PollOneoff TestSnapshotPreview1_PollOneoff_Errors
+
+func TestSnapshotPreview1_ProcExit(t *testing.T) {
 	tests := []struct {
 		name     string
 		exitCode uint32
@@ -661,7 +982,7 @@ func TestAPI_ProcExit(t *testing.T) {
 		},
 	}
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionProcExit, ImportProcExit, "test")
+	store, ctx, fn := instantiateWasmStore(t, FunctionProcExit, ImportProcExit, moduleName)
 
 	for _, tt := range tests {
 		tc := tt
@@ -676,19 +997,18 @@ func TestAPI_ProcExit(t *testing.T) {
 	}
 }
 
-// TODO: TestAPI_ProcRaise TestAPI_ProcRaise_Errors
-// TODO: TestAPI_SchedYield TestAPI_SchedYield_Errors
+// TODO: TestSnapshotPreview1_ProcRaise TestSnapshotPreview1_ProcRaise_Errors
+// TODO: TestSnapshotPreview1_SchedYield TestSnapshotPreview1_SchedYield_Errors
 
-func TestAPI_RandomGet(t *testing.T) {
-	maskLength := 7 // number of bytes to write '?' to tell what we've written
+func TestSnapshotPreview1_RandomGet(t *testing.T) {
 	expectedMemory := []byte{
-		'?',                          // random bytes in `buf` is after this
+		'?',                          // `offset` is after this
 		0x53, 0x8c, 0x7f, 0x96, 0xb1, // random data from seed value of 42
 		'?', // stopped after encoding
-	} // tr
+	}
 
-	var bufLen = uint32(5) // arbitrary buffer size,
-	var buf = uint32(1)    // offset,
+	var length = uint32(5) // arbitrary length,
+	var offset = uint32(1) // offset,
 	var seed = int64(42)   // and seed value
 
 	randOpt := func(api *wasiAPI) {
@@ -701,48 +1021,48 @@ func TestAPI_RandomGet(t *testing.T) {
 		}
 	}
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionRandomGet, ImportRandomGet, "test", randOpt)
+	store, ctx, fn := instantiateWasmStore(t, FunctionRandomGet, ImportRandomGet, moduleName, randOpt)
 
-	t.Run("RandomGet", func(t *testing.T) {
-		maskMemory(store, maskLength)
+	t.Run("SnapshotPreview1.RandomGet", func(t *testing.T) {
+		maskMemory(store, len(expectedMemory))
 
 		// invoke RandomGet directly and check the memory side effects!
-		errno := newAPI(randOpt).RandomGet(ctx, buf, bufLen)
+		errno := newAPI(randOpt).RandomGet(ctx, offset, length)
 		require.Equal(t, wasi.ErrnoSuccess, errno)
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 
 	t.Run(FunctionRandomGet, func(t *testing.T) {
-		maskMemory(store, maskLength)
+		maskMemory(store, len(expectedMemory))
 
-		results, err := store.Engine.Call(ctx, fn, uint64(buf), uint64(bufLen))
+		results, err := store.Engine.Call(ctx, fn, uint64(offset), uint64(length))
 		require.NoError(t, err)
 		require.Equal(t, wasi.ErrnoSuccess, wasi.Errno(results[0])) // cast because results are always uint64
-		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer)
 	})
 }
 
-func TestAPI_RandomGet_Errors(t *testing.T) {
+func TestSnapshotPreview1_RandomGet_Errors(t *testing.T) {
 	validAddress := uint32(0) // arbitrary valid address
 
-	store, ctx, fn := instantiateWasmStore(t, FunctionRandomGet, ImportRandomGet, "test")
+	store, ctx, fn := instantiateWasmStore(t, FunctionRandomGet, ImportRandomGet, moduleName)
 	memorySize := uint32(len(store.Memories[0].Buffer))
 
 	tests := []struct {
 		name   string
-		buf    uint32
-		bufLen uint32
+		offset uint32
+		length uint32
 	}{
 		{
-			name:   "random buffer out-of-memory",
-			buf:    memorySize,
-			bufLen: 1,
+			name:   "out-of-memory",
+			offset: memorySize,
+			length: 1,
 		},
 
 		{
-			name:   "random buffer size exceeds maximum valid address by 1",
-			buf:    validAddress,
-			bufLen: memorySize + 1,
+			name:   "random length exceeds maximum valid address by 1",
+			offset: validAddress,
+			length: memorySize + 1,
 		},
 	}
 
@@ -750,28 +1070,28 @@ func TestAPI_RandomGet_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			results, err := store.Engine.Call(ctx, fn, uint64(tc.buf), uint64(tc.bufLen))
+			results, err := store.Engine.Call(ctx, fn, uint64(tc.offset), uint64(tc.length))
 			require.NoError(t, err)
 			require.Equal(t, uint64(wasi.ErrnoFault), results[0]) // results[0] is the errno
 		})
 	}
 }
 
-func TestAPI_RandomGet_SourceError(t *testing.T) {
-	store, ctx, fn := instantiateWasmStore(t, FunctionRandomGet, ImportRandomGet, "test", func(api *wasiAPI) {
+func TestSnapshotPreview1_RandomGet_SourceError(t *testing.T) {
+	store, ctx, fn := instantiateWasmStore(t, FunctionRandomGet, ImportRandomGet, moduleName, func(api *wasiAPI) {
 		api.randSource = func(p []byte) error {
 			return errors.New("random source error")
 		}
 	})
 
-	results, err := store.Engine.Call(ctx, fn, uint64(1), uint64(5)) // arbitrary offset and buffer size
+	results, err := store.Engine.Call(ctx, fn, uint64(1), uint64(5)) // arbitrary offset and length
 	require.NoError(t, err)
 	require.Equal(t, uint64(wasi.ErrnoIo), results[0]) // results[0] is the errno
 }
 
-// TODO: TestAPI_SockRecv TestAPI_SockRecv_Errors
-// TODO: TestAPI_SockSend TestAPI_SockSend_Errors
-// TODO: TestAPI_SockShutdown TestAPI_SockShutdown_Errors
+// TODO: TestSnapshotPreview1_SockRecv TestSnapshotPreview1_SockRecv_Errors
+// TODO: TestSnapshotPreview1_SockSend TestSnapshotPreview1_SockSend_Errors
+// TODO: TestSnapshotPreview1_SockShutdown TestSnapshotPreview1_SockShutdown_Errors
 
 func instantiateWasmStore(t *testing.T, wasiFunction, wasiImport, moduleName string, opts ...Option) (*wasm.Store, *wasm.ModuleContext, *wasm.FunctionInstance) {
 	mod, err := text.DecodeModule([]byte(fmt.Sprintf(`(module
@@ -802,12 +1122,12 @@ func instantiateWasmStore(t *testing.T, wasiFunction, wasiImport, moduleName str
 	return store, instantiated.Context, wasiFn
 }
 
-// maskMemory overwrites the first memory in the store with '?', so tests can see what's written.
-// As the memory can be very large, this only masks up to the given length.
+// maskMemory sets the first memory in the store to '?' * size, so tests can see what's written.
 //
-// Note: WebAssembly 1.0 (MVP) can have only up to one memory, so which is unambiguous.
-func maskMemory(store *wasm.Store, maskLength int) {
-	for i := 0; i < maskLength; i++ {
+// Note: WebAssembly 1.0 (MVP) can have only up to one memory, so index zero is unambiguous.
+func maskMemory(store *wasm.Store, size int) {
+	store.Memories[0].Buffer = make([]byte, size)
+	for i := 0; i < size; i++ {
 		store.Memories[0].Buffer[i] = '?'
 	}
 }