github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/imports/wasi_snapshot_preview1/poll_test.go (about) 1 package wasi_snapshot_preview1_test 2 3 import ( 4 "io/fs" 5 "os" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/bananabytelabs/wazero" 11 "github.com/bananabytelabs/wazero/api" 12 experimentalsys "github.com/bananabytelabs/wazero/experimental/sys" 13 "github.com/bananabytelabs/wazero/internal/fsapi" 14 "github.com/bananabytelabs/wazero/internal/sys" 15 "github.com/bananabytelabs/wazero/internal/testing/require" 16 "github.com/bananabytelabs/wazero/internal/wasip1" 17 "github.com/bananabytelabs/wazero/internal/wasm" 18 sysapi "github.com/bananabytelabs/wazero/sys" 19 ) 20 21 func Test_pollOneoff(t *testing.T) { 22 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 23 defer r.Close(testCtx) 24 25 mem := []byte{ 26 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 27 wasip1.EventTypeClock, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // event type and padding 28 wasip1.ClockIDMonotonic, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // clockID 29 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // timeout (ns) 30 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // precision (ns) 31 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // flags (relative) 32 '?', // stopped after encoding 33 } 34 35 expectedMem := []byte{ 36 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 37 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 38 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 39 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 40 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '?', // stopped after encoding 41 } 42 43 in := uint32(0) // past in 44 out := uint32(128) // past in 45 nsubscriptions := uint32(1) 46 resultNevents := uint32(512) // past out 47 48 maskMemory(t, mod, 1024) 49 mod.Memory().Write(in, mem) 50 51 requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.PollOneoffName, uint64(in), uint64(out), uint64(nsubscriptions), 52 uint64(resultNevents)) 53 require.Equal(t, ` 54 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=1) 55 <== (nevents=1,errno=ESUCCESS) 56 `, "\n"+log.String()) 57 58 outMem, ok := mod.Memory().Read(out, uint32(len(expectedMem))) 59 require.True(t, ok) 60 require.Equal(t, expectedMem, outMem) 61 62 nevents, ok := mod.Memory().ReadUint32Le(resultNevents) 63 require.True(t, ok) 64 require.Equal(t, nsubscriptions, nevents) 65 } 66 67 func Test_pollOneoff_Errors(t *testing.T) { 68 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 69 defer r.Close(testCtx) 70 71 tests := []struct { 72 name string 73 in, out, nsubscriptions, resultNevents uint32 74 mem []byte // at offset in 75 expectedErrno wasip1.Errno 76 expectedMem []byte // at offset out 77 expectedLog string 78 }{ 79 { 80 name: "in out of range", 81 in: wasm.MemoryPageSize, 82 nsubscriptions: 1, 83 out: 128, // past in 84 resultNevents: 512, // past out 85 expectedErrno: wasip1.ErrnoFault, 86 expectedLog: ` 87 ==> wasi_snapshot_preview1.poll_oneoff(in=65536,out=128,nsubscriptions=1) 88 <== (nevents=,errno=EFAULT) 89 `, 90 }, 91 { 92 name: "out out of range", 93 out: wasm.MemoryPageSize, 94 resultNevents: 512, // past out 95 nsubscriptions: 1, 96 expectedErrno: wasip1.ErrnoFault, 97 expectedLog: ` 98 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=65536,nsubscriptions=1) 99 <== (nevents=,errno=EFAULT) 100 `, 101 }, 102 { 103 name: "resultNevents out of range", 104 resultNevents: wasm.MemoryPageSize, 105 nsubscriptions: 1, 106 expectedErrno: wasip1.ErrnoFault, 107 expectedLog: ` 108 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=0,nsubscriptions=1) 109 <== (nevents=,errno=EFAULT) 110 `, 111 }, 112 { 113 name: "nsubscriptions zero", 114 out: 128, // past in 115 resultNevents: 512, // past out 116 expectedErrno: wasip1.ErrnoInval, 117 expectedLog: ` 118 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=0) 119 <== (nevents=,errno=EINVAL) 120 `, 121 }, 122 } 123 124 for _, tt := range tests { 125 tc := tt 126 t.Run(tc.name, func(t *testing.T) { 127 defer log.Reset() 128 129 maskMemory(t, mod, 1024) 130 131 if tc.mem != nil { 132 mod.Memory().Write(tc.in, tc.mem) 133 } 134 135 requireErrnoResult(t, tc.expectedErrno, mod, wasip1.PollOneoffName, uint64(tc.in), uint64(tc.out), 136 uint64(tc.nsubscriptions), uint64(tc.resultNevents)) 137 require.Equal(t, tc.expectedLog, "\n"+log.String()) 138 139 out, ok := mod.Memory().Read(tc.out, uint32(len(tc.expectedMem))) 140 require.True(t, ok) 141 require.Equal(t, tc.expectedMem, out) 142 143 // Events should be written on success regardless of nested failure. 144 if tc.expectedErrno == wasip1.ErrnoSuccess { 145 nevents, ok := mod.Memory().ReadUint32Le(tc.resultNevents) 146 require.True(t, ok) 147 require.Equal(t, uint32(1), nevents) 148 _ = nevents 149 } 150 }) 151 } 152 } 153 154 func Test_pollOneoff_Stdin(t *testing.T) { 155 w, r, err := os.Pipe() 156 require.NoError(t, err) 157 defer w.Close() 158 defer r.Close() 159 _, _ = w.Write([]byte("wazero")) 160 161 tests := []struct { 162 name string 163 in, out, nsubscriptions, resultNevents uint32 164 mem []byte // at offset in 165 stdin fsapi.File 166 expectedErrno wasip1.Errno 167 expectedMem []byte // at offset out 168 expectedLog string 169 expectedNevents uint32 170 }{ 171 { 172 name: "Read without explicit timeout (no tty)", 173 nsubscriptions: 1, 174 expectedNevents: 1, 175 stdin: &sys.StdinFile{Reader: strings.NewReader("test")}, 176 mem: fdReadSub, 177 expectedErrno: wasip1.ErrnoSuccess, 178 out: 128, // past in 179 resultNevents: 512, // past out 180 expectedMem: []byte{ 181 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 182 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 183 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 184 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 185 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 186 0x0, 0x0, 187 188 '?', // stopped after encoding 189 }, 190 expectedLog: ` 191 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=1) 192 <== (nevents=1,errno=ESUCCESS) 193 `, 194 }, 195 { 196 name: "20ms timeout, fdread on tty (buffer ready): both events are written", 197 nsubscriptions: 2, 198 expectedNevents: 2, 199 stdin: &ttyStdinFile{StdinFile: sys.StdinFile{Reader: strings.NewReader("test")}}, 200 mem: concat( 201 clockNsSub(20*1000*1000), 202 fdReadSub, 203 ), 204 expectedErrno: wasip1.ErrnoSuccess, 205 out: 128, // past in 206 resultNevents: 512, // past out 207 expectedMem: []byte{ 208 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 209 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 210 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 211 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 212 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 213 0x0, 0x0, 214 215 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 216 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 217 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 218 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 219 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 220 0x0, 0x0, 221 222 '?', // stopped after encoding 223 }, 224 expectedLog: ` 225 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 226 <== (nevents=2,errno=ESUCCESS) 227 `, 228 }, 229 { 230 name: "0ns timeout, fdread on tty (buffer ready): both are written", 231 nsubscriptions: 2, 232 expectedNevents: 2, 233 stdin: &ttyStdinFile{StdinFile: sys.StdinFile{Reader: strings.NewReader("test")}}, 234 mem: concat( 235 clockNsSub(20*1000*1000), 236 fdReadSub, 237 ), 238 expectedErrno: wasip1.ErrnoSuccess, 239 out: 128, // past in 240 resultNevents: 512, // past out 241 expectedMem: []byte{ 242 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 243 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 244 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 245 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 246 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 247 0x0, 0x0, 248 249 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 250 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 251 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 252 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 253 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 254 0x0, 0x0, 255 256 '?', // stopped after encoding 257 }, 258 expectedLog: ` 259 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 260 <== (nevents=2,errno=ESUCCESS) 261 `, 262 }, 263 { 264 name: "0ns timeout, fdread on regular file: both events are written", 265 nsubscriptions: 2, 266 expectedNevents: 2, 267 stdin: &sys.StdinFile{Reader: strings.NewReader("test")}, 268 mem: concat( 269 clockNsSub(20*1000*1000), 270 fdReadSub, 271 ), 272 expectedErrno: wasip1.ErrnoSuccess, 273 out: 128, // past in 274 resultNevents: 512, // past out 275 expectedMem: []byte{ 276 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 277 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 278 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 279 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 280 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 281 0x0, 0x0, 282 283 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 284 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 285 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 286 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 287 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 288 0x0, 0x0, 289 290 '?', // stopped after encoding 291 }, 292 expectedLog: ` 293 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 294 <== (nevents=2,errno=ESUCCESS) 295 `, 296 }, 297 { 298 name: "1ns timeout, fdread on regular file: both events are written", 299 nsubscriptions: 2, 300 expectedNevents: 2, 301 stdin: &sys.StdinFile{Reader: strings.NewReader("test")}, 302 mem: concat( 303 clockNsSub(20*1000*1000), 304 fdReadSub, 305 ), 306 expectedErrno: wasip1.ErrnoSuccess, 307 out: 128, // past in 308 resultNevents: 512, // past out 309 expectedMem: []byte{ 310 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 311 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 312 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 313 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 314 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 315 0x0, 0x0, 316 317 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 318 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 319 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 320 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 321 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 322 0x0, 0x0, 323 324 '?', // stopped after encoding 325 }, 326 expectedLog: ` 327 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 328 <== (nevents=2,errno=ESUCCESS) 329 `, 330 }, 331 { 332 name: "20ms timeout, fdread on blocked tty: only clock event is written", 333 nsubscriptions: 2, 334 expectedNevents: 1, 335 stdin: &neverReadyTtyStdinFile{StdinFile: sys.StdinFile{Reader: newBlockingReader(t)}}, 336 mem: concat( 337 clockNsSub(20*1000*1000), 338 fdReadSub, 339 ), 340 341 expectedErrno: wasip1.ErrnoSuccess, 342 out: 128, // past in 343 resultNevents: 512, // past out 344 expectedMem: []byte{ 345 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 346 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 347 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 348 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 349 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 350 0x0, 0x0, 351 352 // 32 empty bytes 353 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 354 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 355 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 356 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 357 358 '?', // stopped after encoding 359 }, 360 expectedLog: ` 361 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 362 <== (nevents=1,errno=ESUCCESS) 363 `, 364 }, 365 { 366 name: "pollable pipe, multiple subs, events returned out of order", 367 nsubscriptions: 3, 368 expectedNevents: 3, 369 mem: concat( 370 fdReadSub, 371 clockNsSub(20*1000*1000), 372 // Illegal file fd with custom user data to recognize it in the event buffer. 373 fdReadSubFdWithUserData(100, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77})), 374 stdin: &sys.StdinFile{Reader: w}, 375 expectedErrno: wasip1.ErrnoSuccess, 376 out: 128, // past in 377 resultNevents: 512, // past out 378 expectedMem: []byte{ 379 // Clock is acknowledged first. 380 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 381 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 382 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 383 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 384 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 385 0x0, 0x0, 386 387 // Then an illegal file with custom user data. 388 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // userdata 389 byte(wasip1.ErrnoBadf), 0x0, // errno is 16 bit 390 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 391 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 392 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 393 0x0, 0x0, 394 395 // Stdin pipes are delayed to invoke sysfs.poll 396 // thus, they are written back last. 397 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 398 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 399 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 400 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 401 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 402 0x0, 0x0, 403 404 '?', // stopped after encoding 405 }, 406 expectedLog: ` 407 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=3) 408 <== (nevents=3,errno=ESUCCESS) 409 `, 410 }, 411 } 412 413 for _, tt := range tests { 414 tc := tt 415 t.Run(tc.name, func(t *testing.T) { 416 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 417 defer r.Close(testCtx) 418 defer log.Reset() 419 420 setStdin(t, mod, tc.stdin) 421 422 maskMemory(t, mod, 1024) 423 if tc.mem != nil { 424 mod.Memory().Write(tc.in, tc.mem) 425 } 426 427 requireErrnoResult(t, tc.expectedErrno, mod, wasip1.PollOneoffName, uint64(tc.in), uint64(tc.out), 428 uint64(tc.nsubscriptions), uint64(tc.resultNevents)) 429 require.Equal(t, tc.expectedLog, "\n"+log.String()) 430 431 out, ok := mod.Memory().Read(tc.out, uint32(len(tc.expectedMem))) 432 require.True(t, ok) 433 require.Equal(t, tc.expectedMem, out) 434 435 // Events should be written on success regardless of nested failure. 436 if tc.expectedErrno == wasip1.ErrnoSuccess { 437 nevents, ok := mod.Memory().ReadUint32Le(tc.resultNevents) 438 require.True(t, ok) 439 require.Equal(t, tc.expectedNevents, nevents) 440 _ = nevents 441 } 442 }) 443 } 444 } 445 446 func setStdin(t *testing.T, mod api.Module, stdin fsapi.File) { 447 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 448 f, ok := fsc.LookupFile(sys.FdStdin) 449 require.True(t, ok) 450 f.File = stdin 451 } 452 453 func Test_pollOneoff_Zero(t *testing.T) { 454 poller := &pollStdinFile{StdinFile: sys.StdinFile{Reader: strings.NewReader("test")}, ready: true} 455 456 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 457 defer r.Close(testCtx) 458 defer log.Reset() 459 460 setStdin(t, mod, poller) 461 462 maskMemory(t, mod, 1024) 463 464 out := uint32(128) 465 nsubscriptions := 2 466 resultNevents := uint32(512) 467 468 mod.Memory().Write(0, 469 concat( 470 clockNsSub(20*1000*1000), 471 fdReadSub, 472 ), 473 ) 474 475 expectedMem := []byte{ 476 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 477 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 478 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 479 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 480 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 481 482 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 483 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 484 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 485 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 486 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 487 0x0, 0x0, 488 489 '?', // stopped after encoding 490 } 491 492 requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.PollOneoffName, uint64(0), uint64(out), 493 uint64(nsubscriptions), uint64(resultNevents)) 494 495 outMem, ok := mod.Memory().Read(out, uint32(len(expectedMem))) 496 require.True(t, ok) 497 require.Equal(t, expectedMem, outMem) 498 499 // Events should be written on success regardless of nested failure. 500 nevents, ok := mod.Memory().ReadUint32Le(resultNevents) 501 require.True(t, ok) 502 require.Equal(t, uint32(2), nevents) 503 504 // second run: simulate no more data on the fd 505 poller.ready = false 506 507 mod.Memory().Write(0, 508 concat( 509 clockNsSub(20*1000*1000), 510 fdReadSub, 511 ), 512 ) 513 514 expectedMem = []byte{ 515 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 516 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 517 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 518 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 519 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 520 521 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 522 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 523 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 524 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 525 526 '?', // stopped after encoding 527 } 528 529 requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.PollOneoffName, uint64(0), uint64(out), 530 uint64(nsubscriptions), uint64(resultNevents)) 531 532 outMem, ok = mod.Memory().Read(out, uint32(len(expectedMem))) 533 require.True(t, ok) 534 require.Equal(t, expectedMem, outMem) 535 536 nevents, ok = mod.Memory().ReadUint32Le(resultNevents) 537 require.True(t, ok) 538 require.Equal(t, uint32(1), nevents) 539 } 540 541 func concat(bytes ...[]byte) []byte { 542 var res []byte 543 for i := range bytes { 544 res = append(res, bytes[i]...) 545 } 546 return res 547 } 548 549 // subscription for a given timeout in ns 550 func clockNsSub(ns uint64) []byte { 551 return []byte{ 552 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 553 wasip1.EventTypeClock, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // event type and padding 554 wasip1.ClockIDMonotonic, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 555 byte(ns), byte(ns >> 8), byte(ns >> 16), byte(ns >> 24), 556 byte(ns >> 32), byte(ns >> 40), byte(ns >> 48), byte(ns >> 56), // timeout (ns) 557 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // precision (ns) 558 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // flags 559 } 560 } 561 562 // subscription for an EventTypeFdRead on a given fd 563 func fdReadSubFd(fd byte) []byte { 564 return []byte{ 565 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 566 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 567 fd, 0x0, 0x0, 0x0, // valid readable FD 568 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 569 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 570 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 571 0x0, 0x0, 0x0, 0x0, // pad to 32 bytes 572 } 573 } 574 575 func fdReadSubFdWithUserData(fd byte, userdata []byte) []byte { 576 return concat( 577 userdata, 578 []byte{ 579 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 580 fd, 0x0, 0x0, 0x0, // valid readable FD 581 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 582 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 583 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 584 0x0, 0x0, 0x0, 0x0, // pad to 32 bytes 585 }) 586 } 587 588 // subscription for an EventTypeFdRead on stdin 589 var fdReadSub = fdReadSubFd(byte(sys.FdStdin)) 590 591 // ttyStat returns fs.ModeCharDevice | fs.ModeCharDevice as an approximation 592 // for isatty. 593 // 594 // See go-isatty for a more specific approach: 595 // https://github.com/mattn/go-isatty/blob/v0.0.18/isatty_tcgets.go#LL11C1-L12C1 596 type ttyStat struct{} 597 598 // Stat implements the same method as documented on sys.File 599 func (ttyStat) Stat() (sysapi.Stat_t, experimentalsys.Errno) { 600 return sysapi.Stat_t{ 601 Mode: fs.ModeDevice | fs.ModeCharDevice, 602 Nlink: 1, 603 }, 0 604 } 605 606 type ttyStdinFile struct { 607 sys.StdinFile 608 ttyStat 609 } 610 611 type neverReadyTtyStdinFile struct { 612 sys.StdinFile 613 ttyStat 614 } 615 616 // Poll implements the same method as documented on sys.File 617 func (neverReadyTtyStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) { 618 if flag != fsapi.POLLIN { 619 return false, experimentalsys.ENOTSUP 620 } 621 switch { 622 case timeoutMillis <= 0: 623 return 624 } 625 time.Sleep(time.Duration(timeoutMillis) * time.Millisecond) 626 return false, 0 627 } 628 629 type pollStdinFile struct { 630 sys.StdinFile 631 ttyStat 632 ready bool 633 } 634 635 // Poll implements the same method as documented on sys.File 636 func (p *pollStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) { 637 if flag != fsapi.POLLIN { 638 return false, experimentalsys.ENOTSUP 639 } 640 return p.ready, 0 641 }