github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/imports/wasi_snapshot_preview1/clock_test.go (about)

     1  package wasi_snapshot_preview1_test
     2  
     3  import (
     4  	_ "embed"
     5  	"testing"
     6  
     7  	"github.com/tetratelabs/wazero"
     8  	"github.com/tetratelabs/wazero/internal/testing/require"
     9  	"github.com/tetratelabs/wazero/internal/wasip1"
    10  )
    11  
    12  func Test_clockResGet(t *testing.T) {
    13  	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
    14  	defer r.Close(testCtx)
    15  
    16  	expectedMemoryMicro := []byte{
    17  		'?',                                     // resultResolution is after this
    18  		0xe8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // little endian-encoded resolution (fixed to 1000).
    19  		'?', // stopped after encoding
    20  	}
    21  
    22  	expectedMemoryNano := []byte{
    23  		'?',                                    // resultResolution is after this
    24  		0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // little endian-encoded resolution (fixed to 1000).
    25  		'?', // stopped after encoding
    26  	}
    27  
    28  	tests := []struct {
    29  		name           string
    30  		clockID        uint32
    31  		expectedMemory []byte
    32  		expectedLog    string
    33  	}{
    34  		{
    35  			name:           "Realtime",
    36  			clockID:        wasip1.ClockIDRealtime,
    37  			expectedMemory: expectedMemoryMicro,
    38  			expectedLog: `
    39  ==> wasi_snapshot_preview1.clock_res_get(id=realtime)
    40  <== (resolution=1000,errno=ESUCCESS)
    41  `,
    42  		},
    43  		{
    44  			name:           "Monotonic",
    45  			clockID:        wasip1.ClockIDMonotonic,
    46  			expectedMemory: expectedMemoryNano,
    47  			expectedLog: `
    48  ==> wasi_snapshot_preview1.clock_res_get(id=monotonic)
    49  <== (resolution=1,errno=ESUCCESS)
    50  `,
    51  		},
    52  	}
    53  
    54  	for _, tt := range tests {
    55  		tc := tt
    56  
    57  		t.Run(tc.name, func(t *testing.T) {
    58  			defer log.Reset()
    59  
    60  			resultResolution := 16 // arbitrary offset
    61  			maskMemory(t, mod, resultResolution+len(tc.expectedMemory))
    62  
    63  			requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.ClockResGetName, uint64(tc.clockID), uint64(resultResolution))
    64  			require.Equal(t, tc.expectedLog, "\n"+log.String())
    65  
    66  			actual, ok := mod.Memory().Read(uint32(resultResolution-1), uint32(len(tc.expectedMemory)))
    67  			require.True(t, ok)
    68  			require.Equal(t, tc.expectedMemory, actual)
    69  		})
    70  	}
    71  }
    72  
    73  func Test_clockResGet_Unsupported(t *testing.T) {
    74  	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
    75  	defer r.Close(testCtx)
    76  
    77  	tests := []struct {
    78  		name          string
    79  		clockID       uint32
    80  		expectedErrno wasip1.Errno
    81  		expectedLog   string
    82  	}{
    83  		{
    84  			name:          "process cputime",
    85  			clockID:       2,
    86  			expectedErrno: wasip1.ErrnoInval,
    87  			expectedLog: `
    88  ==> wasi_snapshot_preview1.clock_res_get(id=2)
    89  <== (resolution=,errno=EINVAL)
    90  `,
    91  		},
    92  		{
    93  			name:          "thread cputime",
    94  			clockID:       3,
    95  			expectedErrno: wasip1.ErrnoInval,
    96  			expectedLog: `
    97  ==> wasi_snapshot_preview1.clock_res_get(id=3)
    98  <== (resolution=,errno=EINVAL)
    99  `,
   100  		},
   101  		{
   102  			name:          "undefined",
   103  			clockID:       100,
   104  			expectedErrno: wasip1.ErrnoInval,
   105  			expectedLog: `
   106  ==> wasi_snapshot_preview1.clock_res_get(id=100)
   107  <== (resolution=,errno=EINVAL)
   108  `,
   109  		},
   110  	}
   111  	for _, tt := range tests {
   112  		tc := tt
   113  
   114  		t.Run(tc.name, func(t *testing.T) {
   115  			defer log.Reset()
   116  
   117  			resultResolution := 16 // arbitrary offset
   118  			requireErrnoResult(t, tc.expectedErrno, mod, wasip1.ClockResGetName, uint64(tc.clockID), uint64(resultResolution))
   119  			require.Equal(t, tc.expectedLog, "\n"+log.String())
   120  		})
   121  	}
   122  }
   123  
   124  func Test_clockTimeGet(t *testing.T) {
   125  	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
   126  	defer r.Close(testCtx)
   127  
   128  	tests := []struct {
   129  		name           string
   130  		clockID        uint32
   131  		expectedMemory []byte
   132  		expectedLog    string
   133  	}{
   134  		{
   135  			name:    "Realtime",
   136  			clockID: wasip1.ClockIDRealtime,
   137  			expectedMemory: []byte{
   138  				'?',                                          // resultTimestamp is after this
   139  				0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, // little endian-encoded epochNanos
   140  				'?', // stopped after encoding
   141  			},
   142  			expectedLog: `
   143  ==> wasi_snapshot_preview1.clock_time_get(id=realtime,precision=0)
   144  <== (timestamp=1640995200000000000,errno=ESUCCESS)
   145  `,
   146  		},
   147  		{
   148  			name:    "Monotonic",
   149  			clockID: wasip1.ClockIDMonotonic,
   150  			expectedMemory: []byte{
   151  				'?',                                    // resultTimestamp is after this
   152  				0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // fake nanotime starts at zero
   153  				'?', // stopped after encoding
   154  			},
   155  			expectedLog: `
   156  ==> wasi_snapshot_preview1.clock_time_get(id=monotonic,precision=0)
   157  <== (timestamp=0,errno=ESUCCESS)
   158  `,
   159  		},
   160  	}
   161  
   162  	for _, tt := range tests {
   163  		tc := tt
   164  		t.Run(tc.name, func(t *testing.T) {
   165  			defer log.Reset()
   166  
   167  			resultTimestamp := 16 // arbitrary offset
   168  			maskMemory(t, mod, resultTimestamp+len(tc.expectedMemory))
   169  
   170  			requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.ClockTimeGetName, uint64(tc.clockID), 0 /* TODO: precision */, uint64(resultTimestamp))
   171  			require.Equal(t, tc.expectedLog, "\n"+log.String())
   172  
   173  			actual, ok := mod.Memory().Read(uint32(resultTimestamp-1), uint32(len(tc.expectedMemory)))
   174  			require.True(t, ok)
   175  			require.Equal(t, tc.expectedMemory, actual)
   176  		})
   177  	}
   178  }
   179  
   180  // Similar to https://github.com/WebAssembly/wasi-testsuite/blob/dc7f8d27be1030cd4788ebdf07d9b57e5d23441e/tests/c/testsuite/clock_gettime-monotonic.c
   181  func Test_clockTimeGet_monotonic(t *testing.T) {
   182  	mod, r, _ := requireProxyModule(t, wazero.NewModuleConfig().
   183  		// Important not to use fake time!
   184  		WithSysNanotime())
   185  	defer r.Close(testCtx)
   186  
   187  	getMonotonicTime := func() uint64 {
   188  		const offset uint32 = 0
   189  		requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.ClockTimeGetName, uint64(wasip1.ClockIDMonotonic),
   190  			0 /* TODO: precision */, uint64(offset))
   191  		timestamp, ok := mod.Memory().ReadUint64Le(offset)
   192  		require.True(t, ok)
   193  		return timestamp
   194  	}
   195  
   196  	t1 := getMonotonicTime()
   197  	t2 := getMonotonicTime()
   198  	t3 := getMonotonicTime()
   199  	t4 := getMonotonicTime()
   200  
   201  	require.True(t, t1 < t2)
   202  	require.True(t, t2 < t3)
   203  	require.True(t, t3 < t4)
   204  }
   205  
   206  func Test_clockTimeGet_Unsupported(t *testing.T) {
   207  	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
   208  	defer r.Close(testCtx)
   209  
   210  	tests := []struct {
   211  		name          string
   212  		clockID       uint32
   213  		expectedErrno wasip1.Errno
   214  		expectedLog   string
   215  	}{
   216  		{
   217  			name:          "process cputime",
   218  			clockID:       2,
   219  			expectedErrno: wasip1.ErrnoInval,
   220  			expectedLog: `
   221  ==> wasi_snapshot_preview1.clock_time_get(id=2,precision=0)
   222  <== (timestamp=,errno=EINVAL)
   223  `,
   224  		},
   225  		{
   226  			name:          "thread cputime",
   227  			clockID:       3,
   228  			expectedErrno: wasip1.ErrnoInval,
   229  			expectedLog: `
   230  ==> wasi_snapshot_preview1.clock_time_get(id=3,precision=0)
   231  <== (timestamp=,errno=EINVAL)
   232  `,
   233  		},
   234  		{
   235  			name:          "undefined",
   236  			clockID:       100,
   237  			expectedErrno: wasip1.ErrnoInval,
   238  			expectedLog: `
   239  ==> wasi_snapshot_preview1.clock_time_get(id=100,precision=0)
   240  <== (timestamp=,errno=EINVAL)
   241  `,
   242  		},
   243  	}
   244  
   245  	for _, tt := range tests {
   246  		tc := tt
   247  
   248  		t.Run(tc.name, func(t *testing.T) {
   249  			defer log.Reset()
   250  
   251  			resultTimestamp := 16 // arbitrary offset
   252  			requireErrnoResult(t, tc.expectedErrno, mod, wasip1.ClockTimeGetName, uint64(tc.clockID), uint64(0) /* TODO: precision */, uint64(resultTimestamp))
   253  			require.Equal(t, tc.expectedLog, "\n"+log.String())
   254  		})
   255  	}
   256  }
   257  
   258  func Test_clockTimeGet_Errors(t *testing.T) {
   259  	mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
   260  	defer r.Close(testCtx)
   261  
   262  	memorySize := mod.Memory().Size()
   263  
   264  	tests := []struct {
   265  		name                     string
   266  		resultTimestamp, argvLen uint32
   267  		expectedLog              string
   268  	}{
   269  		{
   270  			name:            "resultTimestamp OOM",
   271  			resultTimestamp: memorySize,
   272  			expectedLog: `
   273  ==> wasi_snapshot_preview1.clock_time_get(id=realtime,precision=0)
   274  <== (timestamp=,errno=EFAULT)
   275  `,
   276  		},
   277  		{
   278  			name:            "resultTimestamp exceeds the maximum valid address by 1",
   279  			resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
   280  			expectedLog: `
   281  ==> wasi_snapshot_preview1.clock_time_get(id=realtime,precision=0)
   282  <== (timestamp=,errno=EFAULT)
   283  `,
   284  		},
   285  	}
   286  
   287  	for _, tt := range tests {
   288  		tc := tt
   289  
   290  		t.Run(tc.name, func(t *testing.T) {
   291  			defer log.Reset()
   292  
   293  			requireErrnoResult(t, wasip1.ErrnoFault, mod, wasip1.ClockTimeGetName, uint64(0) /* TODO: id */, uint64(0) /* TODO: precision */, uint64(tc.resultTimestamp))
   294  			require.Equal(t, tc.expectedLog, "\n"+log.String())
   295  		})
   296  	}
   297  }