Repository: tetratelabs/wazero
Pull request #224 (2022-02-10 09:19:38)

wasi: refactors existing logic and tests of clock_time_get
This doesn't add new functionality, just ports it to current style


Patch:
diff --git a/wasi/types.go b/wasi/types.go
index 6fe666e7..fd9fabbf 100644
--- a/wasi/types.go
+++ b/wasi/types.go
@@ -324,27 +324,27 @@ const (
 	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
 	FunctionClockTimeGet = "clock_time_get"
 
-	FunctionFDAdvise             = "fd_advise"
-	FunctionFDAllocate           = "fd_allocate"
-	FunctionFDClose              = "fd_close"
-	FunctionFDDataSync           = "fd_datasync"
-	FunctionFDFDStatGet          = "fd_fdstat_get"
-	FunctionFDFDStatSetFlags     = "fd_fdstat_set_flags"
-	FunctionFDFDStatSetRights    = "fd_fdstat_set_rights"
-	FunctionFDFilestatGet        = "fd_filestat_get"
-	FunctionFDFilestatSetSize    = "fd_filestat_set_size"
-	FunctionFDFilestatSetTimes   = "fd_filestat_set_times"
-	FunctionFDPread              = "fd_pread"
-	FunctionFDPrestatGet         = "fd_prestat_get"
-	FunctionFDPrestatDirName     = "fd_prestat_dir_name"
-	FunctionFDPwrite             = "fd_pwrite"
-	FunctionFDRead               = "fd_read"
-	FunctionFDReaddir            = "fd_readdir"
-	FunctionFDRenumber           = "fd_renumber"
-	FunctionFDSeek               = "fd_seek"
-	FunctionFDSync               = "fd_sync"
-	FunctionFDTell               = "fd_tell"
-	FunctionFDWrite              = "fd_write"
+	FunctionFdAdvise             = "fd_advise"
+	FunctionFdAllocate           = "fd_allocate"
+	FunctionFdClose              = "fd_close"
+	FunctionFdDataSync           = "fd_datasync"
+	FunctionFdFdstatGet          = "fd_fdstat_get"
+	FunctionFdFdstatSetFlags     = "fd_fdstat_set_flags"
+	FunctionFdFdstatSetRights    = "fd_fdstat_set_rights"
+	FunctionFdFilestatGet        = "fd_filestat_get"
+	FunctionFdFilestatSetSize    = "fd_filestat_set_size"
+	FunctionFdFilestatSetTimes   = "fd_filestat_set_times"
+	FunctionFdPread              = "fd_pread"
+	FunctionFdPrestatGet         = "fd_prestat_get"
+	FunctionFdPrestatDirName     = "fd_prestat_dir_name"
+	FunctionFdPwrite             = "fd_pwrite"
+	FunctionFdRead               = "fd_read"
+	FunctionFdReaddir            = "fd_readdir"
+	FunctionFdRenumber           = "fd_renumber"
+	FunctionFdSeek               = "fd_seek"
+	FunctionFdSync               = "fd_sync"
+	FunctionFdTell               = "fd_tell"
+	FunctionFdWrite              = "fd_write"
 	FunctionPathCreateDirectory  = "path_create_directory"
 	FunctionPathFilestatGet      = "path_filestat_get"
 	FunctionPathFilestatSetTimes = "path_filestat_set_times"
diff --git a/wasi/wasi.go b/wasi/wasi.go
index 1299e7cb..bcf4227f 100644
--- a/wasi/wasi.go
+++ b/wasi/wasi.go
@@ -29,27 +29,26 @@ import (
 type API interface {
 	// ArgsGet is the WASI function that reads command-line argument data (Args).
 	//
-	// There are two parameters and an Errno result. Both parameters are offsets in the wasm.HostFunctionCallContext
-	// Memory Buffer to write offsets. These are encoded uint32 little-endian.
+	// There are two parameters. Both are offsets in the wasm.HostFunctionCallContext Memory Buffer.
 	//
-	// * argv - is the offset to begin writing argument offsets to the wasm.MemoryInstance
+	// * argv - is the offset to begin writing argument offsets in uint32 little-endian encoding.
 	//   * ArgsSizesGet result argc * 4 bytes are written to this offset
-	// * argvBuf - is the offset to write the null terminated arguments to the wasm.MemoryInstance
+	// * argvBuf - is the offset to write the null terminated arguments to wasm.MemoryInstance
 	//   * 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.Buffer` to contain:
 	//
-	//               argvBufSize                argc * 4
-	//            +----------------+     +--------------------+
-	//            |                |     |                    |
+	//               argvBufSize          uint32le    uint32le
+	//            +----------------+     +--------+  +--------+
+	//            |                |     |        |  |        |
 	// []byte{?, 'a', 0, 'b', 'c', 0, ?, 1, 0, 0, 0, 3, 0, 0, 0, ?}
 	//  argvBuf --^                      ^           ^
 	//                            argv --|           |
 	//          offset that begins "a" --+           |
 	//                     offset that begins "bc" --+
 	//
-	// Note: FunctionArgsGet documentation has an example of this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// Note: FunctionArgsGet documentation shows this signature in the WebAssembly 1.0 (MVP) Text Format.
 	// See ArgsSizesGet
 	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_get
 	// See https://en.wikipedia.org/wiki/Null-terminated_string
@@ -57,16 +56,16 @@ type API interface {
 
 	// ArgsSizesGet is a WASI function that reads command-line argument data (Args) sizes.
 	//
-	// There are two parameters and an Errno result. Both parameters are offsets in the wasm.HostFunctionCallContext
-	// Memory Buffer to write the corresponding sizes in uint32 little-endian encoding.
+	// There are two result parameters: these are offsets in the wasm.HostFunctionCallContext Memory Buffer to write
+	// corresponding sizes in uint32 little-endian encoding.
 	//
-	// * argc - is the offset to write the argument count to the wasm.MemoryInstance Buffer
-	// * argvBufSize - is the offset to write the null terminated argument length to the wasm.MemoryInstance Buffer
+	// * resultArgc - is the offset to write the argument count to wasm.MemoryInstance Buffer
+	// * resultArgvBufSize - is the offset to write the null-terminated argument length to wasm.MemoryInstance Buffer
 	//
 	// For example, if Args are []string{"a","bc"} and
 	//   ArgsSizesGet parameters resultArgc=1 and resultArgvBufSize=6, we expect `ctx.Memory.Buffer` to contain:
 	//
-	//                    uint32         uint32
+	//                   uint32le       uint32le
 	//                  +--------+     +--------+
 	//                  |        |     |        |
 	//        []byte{?, 2, 0, 0, 0, ?, 5, 0, 0, 0, ?}
@@ -75,7 +74,7 @@ type API interface {
 	//             resultArgvBufSize --|
 	//   len([]byte{'a',0,'b',c',0}) --+
 	//
-	// Note: FunctionArgsSizesGet documentation has an example of this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// Note: FunctionArgsSizesGet documentation shows this signature in the WebAssembly 1.0 (MVP) Text Format.
 	// See ArgsGet
 	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_sizes_get
 	// See https://en.wikipedia.org/wiki/Null-terminated_string
@@ -87,29 +86,49 @@ type API interface {
 
 	// TODO: ClockResGet(ctx *wasm.HostFunctionCallContext, id, resultResolution uint32) Errno
 
-	// TODO: ClockTimeGet(ctx *wasm.HostFunctionCallContext, id uint32, precision uint64, resultTimestamp uint32) Errno
-
-	// TODO: FDAdvise
-	// TODO: FDAllocate
-	// TODO: FDClose
-	// TODO: FDDataSync
-	// TODO: FDFDStatGet
-	// TODO: FDFDStatSetFlags
-	// TODO: FDFDStatSetRights
-	// TODO: FDFilestatGet
-	// TODO: FDFilestatSetSize
-	// TODO: FDFilestatSetTimes
-	// TODO: FDPread
-	// TODO: FDPrestatGet
-	// TODO: FDPrestatDirName
-	// TODO: FDPwrite
-	// TODO: FDRead
-	// TODO: FDReaddir
-	// TODO: FDRenumber
-	// TODO: FDSeek
-	// TODO: FDSync
-	// TODO: FDTell
-	// TODO: FDWrite
+	// ClockTimeGet is a WASI function that returns the time value of a clock (time.Now).
+	//
+	// * id - The clock id for which to return the time.
+	// * precision - The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
+	// * resultTimestamp - the offset to write the timestamp to wasm.MemoryInstance Buffer
+	//   * 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.Buffer` to contain:
+	//
+	//                                      uint64le
+	//                    +------------------------------------------+
+	//                    |                                          |
+	//          []byte{?, 0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, ?}
+	//  resultTimestamp --^
+	//
+	// Note: FunctionClockTimeGet documentation shows this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// Note: This is similar to `clock_gettime` in POSIX.
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
+	// See https://linux.die.net/man/3/clock_gettime
+	ClockTimeGet(ctx *wasm.HostFunctionCallContext, id uint32, precision uint64, resultTimestamp uint32) Errno
+
+	// TODO: FdAdvise
+	// TODO: FdAllocate
+	// TODO: FdClose
+	// TODO: FdDataSync
+	// TODO: FdFdstatGet
+	// TODO: FdFdstatSetFlags
+	// TODO: FdFdstatSetRights
+	// TODO: FdFilestatGet
+	// TODO: FdFilestatSetSize
+	// TODO: FdFilestatSetTimes
+	// TODO: FdPread
+	// TODO: FdPrestatGet
+	// TODO: FdPrestatDirName
+	// TODO: FdPwrite
+	// TODO: FdRead
+	// TODO: FdReaddir
+	// TODO: FdRenumber
+	// TODO: FdSeek
+	// TODO: FdSync
+	// TODO: FdTell
+	// TODO: FdWrite
 	// TODO: PathCreateDirectory
 	// TODO: PathFilestatGet
 	// TODO: PathFilestatSetTimes
@@ -140,8 +159,9 @@ type api struct {
 	stdin io.Reader
 	stdout,
 	stderr io.Writer
-	opened         map[uint32]fileEntry
-	getTimeNanosFn func() uint64
+	opened map[uint32]fileEntry
+	// timeNowUnixNano is mutable for testing
+	timeNowUnixNano func() uint64
 }
 
 func (a *api) register(store *wasm.Store) (err error) {
@@ -156,28 +176,28 @@ func (a *api) register(store *wasm.Store) (err error) {
 		{FunctionEnvironGet, environ_get},
 		{FunctionEnvironSizesGet, environ_sizes_get},
 		// TODO: FunctionClockResGet
-		{FunctionClockTimeGet, a.clock_time_get},
-		// TODO: FunctionFDAdvise
-		// TODO: FunctionFDAllocate
-		{FunctionFDClose, a.fd_close},
-		// TODO: FunctionFDDataSync
-		{FunctionFDFDStatGet, a.fd_fdstat_get},
-		// TODO: FunctionFDFDStatSetFlags
-		// TODO: FunctionFDFDStatSetRights
-		// TODO: FunctionFDFilestatGet
-		// TODO: FunctionFDFilestatSetSize
-		// TODO: FunctionFDFilestatSetTimes
-		// TODO: FunctionFDPread
-		{FunctionFDPrestatGet, a.fd_prestat_get},
-		{FunctionFDPrestatDirName, a.fd_prestat_dir_name},
-		// TODO: FunctionFDPwrite
-		{FunctionFDRead, a.fd_read},
-		// TODO: FunctionFDReaddir
-		// TODO: FunctionFDRenumber
-		{FunctionFDSeek, a.fd_seek},
-		// TODO: FunctionFDSync
-		// TODO: FunctionFDTell
-		{FunctionFDWrite, a.fd_write},
+		{FunctionClockTimeGet, a.ClockTimeGet},
+		// TODO: FunctionFdAdvise
+		// TODO: FunctionFdAllocate
+		{FunctionFdClose, a.fd_close},
+		// TODO: FunctionFdDataSync
+		{FunctionFdFdstatGet, a.fd_fdstat_get},
+		// TODO: FunctionFdFdstatSetFlags
+		// TODO: FunctionFdFdstatSetRights
+		// TODO: FunctionFdFilestatGet
+		// TODO: FunctionFdFilestatSetSize
+		// TODO: FunctionFdFilestatSetTimes
+		// TODO: FunctionFdPread
+		{FunctionFdPrestatGet, a.fd_prestat_get},
+		{FunctionFdPrestatDirName, a.fd_prestat_dir_name},
+		// TODO: FunctionFdPwrite
+		{FunctionFdRead, a.fd_read},
+		// TODO: FunctionFdReaddir
+		// TODO: FunctionFdRenumber
+		{FunctionFdSeek, a.fd_seek},
+		// TODO: FunctionFdSync
+		// TODO: FunctionFdTell
+		{FunctionFdWrite, a.fd_write},
 		// TODO: FunctionPathCreateDirectory
 		// TODO: FunctionPathFilestatGet
 		// TODO: FunctionPathFilestatSetTimes
@@ -211,6 +231,48 @@ func (a *api) register(store *wasm.Store) (err error) {
 	return nil
 }
 
+// ArgsGet implements API.ArgsGet
+func (a *api) ArgsGet(ctx *wasm.HostFunctionCallContext, argv, argvBuf uint32) Errno {
+	if !ctx.Memory.ValidateAddrRange(argv, uint64(len(a.args.nullTerminatedValues))*4) /*4 is the size of uint32*/ ||
+		!ctx.Memory.ValidateAddrRange(argvBuf, uint64(a.args.totalBufSize)) {
+		return ErrnoInval
+	}
+	for _, arg := range a.args.nullTerminatedValues {
+		binary.LittleEndian.PutUint32(ctx.Memory.Buffer[argv:], argvBuf)
+		argv += 4 // size of uint32
+		argvBuf += uint32(copy(ctx.Memory.Buffer[argvBuf:], arg))
+	}
+
+	return ErrnoSuccess
+}
+
+// ArgsSizesGet implements API.ArgsSizesGet
+func (a *api) ArgsSizesGet(ctx *wasm.HostFunctionCallContext, resultArgc, resultArgvBufSize uint32) Errno {
+	if !ctx.Memory.PutUint32(resultArgc, uint32(len(a.args.nullTerminatedValues))) {
+		return ErrnoInval
+	}
+	if !ctx.Memory.PutUint32(resultArgvBufSize, a.args.totalBufSize) {
+		return ErrnoInval
+	}
+
+	return ErrnoSuccess
+}
+
+// TODO: func (a *api) EnvironGet
+
+// TODO: func (a *api) EnvironSizesGet
+
+// TODO: func (a *api) FunctionClockResGet
+
+// ClockTimeGet implements API.ClockTimeGet
+func (a *api) ClockTimeGet(ctx *wasm.HostFunctionCallContext, id uint32, precision uint64, resultTimestamp uint32) Errno {
+	// TODO: id and precision are currently ignored.
+	if !ctx.Memory.PutUint64(resultTimestamp, a.timeNowUnixNano()) {
+		return ErrnoInval
+	}
+	return ErrnoSuccess
+}
+
 type fileEntry struct {
 	path    string
 	fileSys FS
@@ -280,7 +342,7 @@ func newAPI(opts ...Option) *api {
 		stdout: os.Stdout,
 		stderr: os.Stderr,
 		opened: map[uint32]fileEntry{},
-		getTimeNanosFn: func() uint64 {
+		timeNowUnixNano: func() uint64 {
 			return uint64(time.Now().UnixNano())
 		},
 	}
@@ -441,33 +503,6 @@ func (a *api) fd_close(ctx *wasm.HostFunctionCallContext, fd uint32) (err Errno)
 	return ErrnoSuccess
 }
 
-// ArgsSizesGet implements API.ArgsSizesGet
-func (a *api) ArgsSizesGet(ctx *wasm.HostFunctionCallContext, resultArgc, resultArgvBufSize uint32) Errno {
-	if !ctx.Memory.PutUint32(resultArgc, uint32(len(a.args.nullTerminatedValues))) {
-		return ErrnoInval
-	}
-	if !ctx.Memory.PutUint32(resultArgvBufSize, a.args.totalBufSize) {
-		return ErrnoInval
-	}
-
-	return ErrnoSuccess
-}
-
-// ArgsGet implements API.ArgsGet
-func (a *api) ArgsGet(ctx *wasm.HostFunctionCallContext, argv, argvBuf uint32) Errno {
-	if !ctx.Memory.ValidateAddrRange(argv, uint64(len(a.args.nullTerminatedValues))*4) /*4 is the size of uint32*/ ||
-		!ctx.Memory.ValidateAddrRange(argvBuf, uint64(a.args.totalBufSize)) {
-		return ErrnoInval
-	}
-	for _, arg := range a.args.nullTerminatedValues {
-		binary.LittleEndian.PutUint32(ctx.Memory.Buffer[argv:], argvBuf)
-		argv += 4 // size of uint32
-		argvBuf += uint32(copy(ctx.Memory.Buffer[argvBuf:], arg))
-	}
-
-	return ErrnoSuccess
-}
-
 func proc_exit(*wasm.HostFunctionCallContext, uint32) {
 	// TODO: implement
 }
@@ -479,17 +514,3 @@ func environ_sizes_get(*wasm.HostFunctionCallContext, uint32, uint32) (err Errno
 func environ_get(*wasm.HostFunctionCallContext, uint32, uint32) (err Errno) {
 	return ErrnoNosys // TODO: implement
 }
-
-// clock_time_get is a WASI API that returns the time value of a clock. Note: This is similar to clock_gettime in POSIX.
-// * id: The clock id for which to return the time.
-// * precision: timestamp The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
-// * timestampPtr: a pointer to an address of uint64 type. The time value of the clock in nanoseconds is written there.
-//
-// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
-func (a *api) clock_time_get(ctx *wasm.HostFunctionCallContext, id uint32, precision uint64, timestampPtr uint32) (err Errno) {
-	// TODO: The clock id and precision are currently ignored.
-	if !ctx.Memory.PutUint64(timestampPtr, a.getTimeNanosFn()) {
-		return ErrnoInval
-	}
-	return ErrnoSuccess
-}


Test patch:
diff --git a/tests/codec/codec_test.go b/tests/codec/codec_test.go
index 34a910a2..ce24e6c0 100644
--- a/tests/codec/codec_test.go
+++ b/tests/codec/codec_test.go
@@ -46,7 +46,7 @@ func newExample() *wasm.Module {
 				Kind:     wasm.ImportKindFunc,
 				DescFunc: 0,
 			}, {
-				Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+				Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 				Kind:     wasm.ImportKindFunc,
 				DescFunc: 2,
 			},
diff --git a/wasi/wasi_test.go b/wasi/wasi_test.go
index 83b03f19..1cbbef32 100644
--- a/wasi/wasi_test.go
+++ b/wasi/wasi_test.go
@@ -3,11 +3,8 @@ package wasi
 import (
 	"context"
 	_ "embed"
-	"encoding/binary"
-	"math"
 	"testing"
 
-	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
 	"github.com/tetratelabs/wazero/wasm"
@@ -38,11 +35,9 @@ func TestNewAPI_Args(t *testing.T) {
 //go:embed testdata/args.wat
 var argsWat []byte
 
-func TestApi_ArgsGet(t *testing.T) {
-	ctx := context.Background()
+func TestAPI_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
@@ -60,7 +55,7 @@ func TestApi_ArgsGet(t *testing.T) {
 		maskMemory(store, maskLength)
 
 		// provide a host context we call directly
-		hContext := wasm.NewHostFunctionCallContext(ctx, store.Memories[0])
+		hContext := wasm.NewHostFunctionCallContext(context.Background(), store.Memories[0])
 
 		// invoke ArgsGet directly and check the memory side-effects!
 		errno := wasiAPI.ArgsGet(hContext, argv, argvBuf)
@@ -70,15 +65,14 @@ func TestApi_ArgsGet(t *testing.T) {
 	t.Run(FunctionArgsGet, func(t *testing.T) {
 		maskMemory(store, maskLength)
 
-		ret, _, err := store.CallFunction(ctx, "test", FunctionArgsGet, uint64(argv), uint64(argvBuf))
+		ret, _, err := store.CallFunction(context.Background(), "test", FunctionArgsGet, uint64(argv), uint64(argvBuf))
 		require.NoError(t, err)
 		require.Equal(t, ErrnoSuccess, Errno(ret[0])) // cast because results are always uint64
 		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
 	})
 }
 
-func TestArgsGet_Errors(t *testing.T) {
-	ctx := context.Background()
+func TestAPI_ArgsGet_Errors(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
 	store, wasiAPI := instantiateWasmStore(t, argsWat, "test", args)
@@ -118,15 +112,14 @@ func TestArgsGet_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			ret, _, err := store.CallFunction(ctx, "test", FunctionArgsGet, uint64(tc.argv), uint64(tc.argvBuf))
+			ret, _, err := store.CallFunction(context.Background(), "test", FunctionArgsGet, uint64(tc.argv), uint64(tc.argvBuf))
 			require.NoError(t, err)
 			require.Equal(t, uint64(ErrnoInval), ret[0]) // ret[0] is returned errno
 		})
 	}
 }
 
-func TestApi_ArgsSizesGet(t *testing.T) {
-	ctx := context.Background()
+func TestAPI_ArgsSizesGet(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
 	resultArgc := uint32(1)        // arbitrary offset
@@ -145,7 +138,7 @@ func TestApi_ArgsSizesGet(t *testing.T) {
 		maskMemory(store, maskLength)
 
 		// provide a host context we call directly
-		hContext := wasm.NewHostFunctionCallContext(ctx, store.Memories[0])
+		hContext := wasm.NewHostFunctionCallContext(context.Background(), store.Memories[0])
 
 		// invoke ArgsSizesGet directly and check the memory side effects!
 		errno := wasiAPI.ArgsSizesGet(hContext, resultArgc, resultArgvBufSize)
@@ -155,15 +148,14 @@ func TestApi_ArgsSizesGet(t *testing.T) {
 	t.Run(FunctionArgsSizesGet, func(t *testing.T) {
 		maskMemory(store, maskLength)
 
-		ret, _, err := store.CallFunction(ctx, "test", FunctionArgsSizesGet, uint64(resultArgc), uint64(resultArgvBufSize))
+		ret, _, err := store.CallFunction(context.Background(), "test", FunctionArgsSizesGet, uint64(resultArgc), uint64(resultArgvBufSize))
 		require.NoError(t, err)
 		require.Equal(t, ErrnoSuccess, Errno(ret[0])) // cast because results are always uint64
 		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
 	})
 }
 
-func TestArgsSizesGet_Errors(t *testing.T) {
-	ctx := context.Background()
+func TestAPI_ArgsSizesGet_Errors(t *testing.T) {
 	args, err := Args("a", "bc")
 	require.NoError(t, err)
 	store, _ := instantiateWasmStore(t, argsWat, "test", args)
@@ -202,120 +194,128 @@ func TestArgsSizesGet_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			ret, _, err := store.CallFunction(ctx, "test", FunctionArgsSizesGet, uint64(tc.argc), uint64(tc.argvBufSize))
+			ret, _, err := store.CallFunction(context.Background(), "test", FunctionArgsSizesGet, uint64(tc.argc), uint64(tc.argvBufSize))
 			require.NoError(t, err)
 			require.Equal(t, uint64(ErrnoInval), ret[0]) // ret[0] is returned errno
 		})
 	}
 }
 
-// TODO TestEnvironGet TestEnvironGet_Errors
-// TODO TestEnvironSizesGet TestEnvironSizesGet_Errors
-// TODO TestClockResGet TestClockResGet_Errors
+// TODO TestAPI_EnvironGet TestAPI_EnvironGet_Errors
+// TODO TestAPI_EnvironSizesGet TestAPI_EnvironSizesGet_Errors
+// TODO TestAPI_ClockResGet TestAPI_ClockResGet_Errors
 
 // clockWat is a wasm module to call clock_time_get.
 //go:embed testdata/clock.wat
 var clockWat []byte
 
-// TODO: rewrite
-func TestClockGetTime(t *testing.T) {
-	ctx := context.Background()
+func TestAPI_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
+
 	store, wasiAPI := instantiateWasmStore(t, clockWat, "test")
+	wasiAPI.(*api).timeNowUnixNano = func() uint64 { return epochNanos }
+
+	t.Run("API.ClockTimeGet", func(t *testing.T) {
+		maskMemory(store, maskLength)
+
+		// provide a host context we call directly
+		hContext := wasm.NewHostFunctionCallContext(context.Background(), store.Memories[0])
+
+		// invoke ClockTimeGet directly and check the memory side effects!
+		errno := wasiAPI.ClockTimeGet(hContext, 0 /* TODO: id */, 0 /* TODO: precision */, resultTimestamp)
+		require.Equal(t, ErrnoSuccess, errno)
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+	})
+	t.Run(FunctionClockTimeGet, func(t *testing.T) {
+		maskMemory(store, maskLength)
+
+		ret, _, err := store.CallFunction(context.Background(), "test", FunctionClockTimeGet, 0 /* TODO: id */, 0 /* TODO: precision */, uint64(resultTimestamp))
+		require.NoError(t, err)
+		require.Equal(t, ErrnoSuccess, Errno(ret[0])) // cast because results are always uint64
+		require.Equal(t, expectedMemory, store.Memories[0].Buffer[0:maskLength])
+	})
+}
+
+func TestAPI_ClockTimeGet_Errors(t *testing.T) {
+	epochNanos := uint64(1640995200000000000) // midnight UTC 2022-01-01
+	store, wasiAPI := instantiateWasmStore(t, clockWat, "test")
+	wasiAPI.(*api).timeNowUnixNano = func() uint64 { return epochNanos }
+
 	memorySize := uint32(len(store.Memories[0].Buffer))
-	validAddress := uint32(0) // arbitrary valid address as arguments to args_get. We chose 0 here.
 
 	tests := []struct {
-		name         string
-		timestampVal uint64
-		timestampPtr uint32
-		result       Errno
+		name            string
+		resultTimestamp uint32
+		argvBufSize     uint32
 	}{
 		{
-			name:         "zero uint64 value",
-			timestampVal: 0,
-			timestampPtr: validAddress,
-			result:       ErrnoSuccess,
-		},
-		{
-			name:         "low uint64 value",
-			timestampVal: 12345,
-			timestampPtr: validAddress,
-			result:       ErrnoSuccess,
-		},
-		{
-			name:         "high uint64 value - no truncation",
-			timestampVal: math.MaxUint64,
-			timestampPtr: validAddress,
-			result:       ErrnoSuccess,
-		},
-		{
-			name:         "with an endian-sensitive uint64 val - no truncation",
-			timestampVal: math.MaxUint64 - 1,
-			timestampPtr: validAddress,
-			result:       ErrnoSuccess,
+			name:            "resultTimestamp out-of-memory",
+			resultTimestamp: memorySize,
 		},
+
 		{
-			name:         "timestampPtr exceeds the maximum valid address by 1",
-			timestampVal: math.MaxUint64,
-			timestampPtr: memorySize - 8 + 1,
-			result:       ErrnoInval,
+			name:            "resultTimestamp exceeds the maximum valid address by 1",
+			resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
 		},
 	}
 
 	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			wasiAPI.(*api).getTimeNanosFn = func() uint64 { return tt.timestampVal }
-			ret, _, err := store.CallFunction(ctx, "test", FunctionClockTimeGet, uint64(0), uint64(0), uint64(tt.timestampPtr))
+		tc := tt
+
+		t.Run(tc.name, func(t *testing.T) {
+			ret, _, err := store.CallFunction(context.Background(), "test", FunctionClockTimeGet, 0 /* TODO: id */, 0 /* TODO: precision */, uint64(tc.resultTimestamp))
 			require.NoError(t, err)
-			errno := Errno(ret[0])
-			require.Equal(t, tt.result, errno) // ret[0] is returned errno
-			if errno == ErrnoSuccess {
-				nanos := binary.LittleEndian.Uint64(store.Memories[0].Buffer)
-				assert.Equal(t, tt.timestampVal, nanos)
-			}
+			require.Equal(t, uint64(ErrnoInval), ret[0]) // ret[0] is returned errno
 		})
 	}
 }
 
-// TODO: TestFDAdvise TestFDAdvise_Errors
-// TODO: TestFDAllocate TestFDAllocate_Errors
-// TODO: TestFDClose TestFDClose_Errors
-// TODO: TestFDDataSync TestFDDataSync_Errors
-// TODO: TestFDFDStatGet TestFDFDStatGet_Errors
-// TODO: TestFDFDStatSetFlags TestFDFDStatSetFlags_Errors
-// TODO: TestFDFDStatSetRights TestFDFDStatSetRights_Errors
-// TODO: TestFDFilestatGet TestFDFilestatGet_Errors
-// TODO: TestFDFilestatSetSize TestFDFilestatSetSize_Errors
-// TODO: TestFDFilestatSetTimes TestFDFilestatSetTimes_Errors
-// TODO: TestFDPread TestFDPread_Errors
-// TODO: TestFDPrestatGet TestFDPrestatGet_Errors
-// TODO: TestFDPrestatDirName TestFDPrestatDirName_Errors
-// TODO: TestFDPwrite TestFDPwrite_Errors
-// TODO: TestFDRead TestFDRead_Errors
-// TODO: TestFDReaddir TestFDReaddir_Errors
-// TODO: TestFDRenumber TestFDRenumber_Errors
-// TODO: TestFDSeek TestFDSeek_Errors
-// TODO: TestFDSync TestFDSync_Errors
-// TODO: TestFDTell TestFDTell_Errors
-// TODO: TestFDWrite TestFDWrite_Errors
-// TODO: TestPathCreateDirectory TestPathCreateDirectory_Errors
-// TODO: TestPathFilestatGet TestPathFilestatGet_Errors
-// TODO: TestPathFilestatSetTimes TestPathFilestatSetTimes_Errors
-// TODO: TestPathLink TestPathLink_Errors
-// TODO: TestPathOpen TestPathOpen_Errors
-// TODO: TestPathReadlink TestPathReadlink_Errors
-// TODO: TestPathRemoveDirectory TestPathRemoveDirectory_Errors
-// TODO: TestPathRename TestPathRename_Errors
-// TODO: TestPathSymlink TestPathSymlink_Errors
-// TODO: TestPathUnlinkFile TestPathUnlinkFile_Errors
-// TODO: TestPollOneoff TestPollOneoff_Errors
-// TODO: TestProcExit TestProcExit_Errors
-// TODO: TestProcRaise TestProcRaise_Errors
-// TODO: TestSchedYield TestSchedYield_Errors
-// TODO: TestRandomGet TestRandomGet_Errors
-// TODO: TestSockRecv TestSockRecv_Errors
-// TODO: TestSockSend TestSockSend_Errors
-// TODO: TestSockShutdown TestSockShutdown_Errors
+// TODO: TestAPI_FdAdvise TestAPI_FdAdvise_Errors
+// TODO: TestAPI_FdAllocate TestAPI_FdAllocate_Errors
+// TODO: TestAPI_FdClose TestAPI_FdClose_Errors
+// 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
+// TODO: TestAPI_FdPrestatDirName TestAPI_FdPrestatDirName_Errors
+// 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
+// TODO: TestAPI_ProcExit TestAPI_ProcExit_Errors
+// TODO: TestAPI_ProcRaise TestAPI_ProcRaise_Errors
+// TODO: TestAPI_SchedYield TestAPI_SchedYield_Errors
+// TODO: TestAPI_RandomGet TestAPI_RandomGet_Errors
+// TODO: TestAPI_SockRecv TestAPI_SockRecv_Errors
+// TODO: TestAPI_SockSend TestAPI_SockSend_Errors
+// TODO: TestAPI_SockShutdown TestAPI_SockShutdown_Errors
 
 func instantiateWasmStore(t *testing.T, wat []byte, moduleName string, opts ...Option) (*wasm.Store, API) {
 	mod, err := text.DecodeModule(wat)
diff --git a/wasm/text/decoder_test.go b/wasm/text/decoder_test.go
index 01ce4b14..43ab766e 100644
--- a/wasm/text/decoder_test.go
+++ b/wasm/text/decoder_test.go
@@ -51,7 +51,7 @@ func TestDecodeModule(t *testing.T) {
 					i32i32i32i32_i32,
 				},
 				ImportSection: []*wasm.Import{{
-					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 					Kind:     wasm.ImportKindFunc,
 					DescFunc: 1,
 				}},
@@ -161,7 +161,7 @@ func TestDecodeModule(t *testing.T) {
 			expected: &wasm.Module{
 				TypeSection: []*wasm.FunctionType{i32i32i32i32_i32},
 				ImportSection: []*wasm.Import{{
-					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 					Kind:     wasm.ImportKindFunc,
 					DescFunc: 0,
 				}},
@@ -178,7 +178,7 @@ func TestDecodeModule(t *testing.T) {
 			expected: &wasm.Module{
 				TypeSection: []*wasm.FunctionType{i32i32i32i32_i32},
 				ImportSection: []*wasm.Import{{
-					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 					Kind:     wasm.ImportKindFunc,
 					DescFunc: 0,
 				}},
@@ -213,7 +213,7 @@ func TestDecodeModule(t *testing.T) {
 			expected: &wasm.Module{
 				TypeSection: []*wasm.FunctionType{i32i32i32i32_i32},
 				ImportSection: []*wasm.Import{{
-					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 					Kind:     wasm.ImportKindFunc,
 					DescFunc: 0,
 				}},
@@ -294,7 +294,7 @@ func TestDecodeModule(t *testing.T) {
 					Kind:     wasm.ImportKindFunc,
 					DescFunc: 0,
 				}, {
-					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+					Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 					Kind:     wasm.ImportKindFunc,
 					DescFunc: 1,
 				}},
@@ -327,7 +327,7 @@ func TestDecodeModule(t *testing.T) {
 						Kind:     wasm.ImportKindFunc,
 						DescFunc: 1,
 					}, {
-						Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+						Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 						Kind:     wasm.ImportKindFunc,
 						DescFunc: 2,
 					},
@@ -357,7 +357,7 @@ func TestDecodeModule(t *testing.T) {
 						Kind:     wasm.ImportKindFunc,
 						DescFunc: 1,
 					}, {
-						Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+						Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 						Kind:     wasm.ImportKindFunc,
 						DescFunc: 2,
 					},
@@ -534,7 +534,7 @@ func TestDecodeModule(t *testing.T) {
 						Kind:     wasm.ImportKindFunc,
 						DescFunc: 1,
 					}, {
-						Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
+						Module: "wasi_snapshot_preview1", Name: wasi.FunctionFdWrite,
 						Kind:     wasm.ImportKindFunc,
 						DescFunc: 2,
 					},