github.com/tetratelabs/wazero@v1.7.1/cmd/wazero/wazero_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 _ "embed" 6 "flag" 7 "fmt" 8 "log" 9 "os" 10 "os/exec" 11 "path" 12 "path/filepath" 13 "strings" 14 "testing" 15 16 "github.com/tetratelabs/wazero/api" 17 "github.com/tetratelabs/wazero/experimental/logging" 18 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 19 "github.com/tetratelabs/wazero/internal/internalapi" 20 "github.com/tetratelabs/wazero/internal/platform" 21 "github.com/tetratelabs/wazero/internal/testing/require" 22 "github.com/tetratelabs/wazero/internal/version" 23 "github.com/tetratelabs/wazero/sys" 24 ) 25 26 //go:embed testdata/infinite_loop.wasm 27 var wasmInfiniteLoop []byte 28 29 //go:embed testdata/wasi_arg.wasm 30 var wasmWasiArg []byte 31 32 //go:embed testdata/wasi_env.wasm 33 var wasmWasiEnv []byte 34 35 //go:embed testdata/wasi_fd.wasm 36 var wasmWasiFd []byte 37 38 //go:embed testdata/wasi_random_get.wasm 39 var wasmWasiRandomGet []byte 40 41 //go:embed testdata/cat/cat-tinygo.wasm 42 var wasmCatTinygo []byte 43 44 //go:embed testdata/exit_on_start_unstable.wasm 45 var wasmWasiUnstable []byte 46 47 func TestMain(m *testing.M) { 48 cmd := exec.Command("go", "version") 49 if _, err := cmd.CombinedOutput(); err != nil { 50 log.Println("main: cli test is only supported on a machine with Go installed") 51 os.Exit(0) 52 } 53 os.Exit(m.Run()) 54 } 55 56 func TestCompile(t *testing.T) { 57 tmpDir, oldwd := requireChdirToTemp(t) 58 defer os.Chdir(oldwd) //nolint 59 60 wasmPath := filepath.Join(tmpDir, "test.wasm") 61 require.NoError(t, os.WriteFile(wasmPath, wasmWasiArg, 0o600)) 62 63 existingDir1 := filepath.Join(tmpDir, "existing1") 64 require.NoError(t, os.Mkdir(existingDir1, 0o700)) 65 existingDir2 := filepath.Join(tmpDir, "existing2") 66 require.NoError(t, os.Mkdir(existingDir2, 0o700)) 67 68 cpuProfile := filepath.Join(t.TempDir(), "cpu.out") 69 memProfile := filepath.Join(t.TempDir(), "mem.out") 70 71 tests := []struct { 72 name string 73 wazeroOpts []string 74 test func(t *testing.T) 75 }{ 76 { 77 name: "no opts", 78 }, 79 { 80 name: "cachedir existing absolute", 81 wazeroOpts: []string{"--cachedir=" + existingDir1}, 82 test: func(t *testing.T) { 83 entries, err := os.ReadDir(existingDir1) 84 require.NoError(t, err) 85 require.True(t, len(entries) > 0) 86 }, 87 }, 88 { 89 name: "cachedir existing relative", 90 wazeroOpts: []string{"--cachedir=existing2"}, 91 test: func(t *testing.T) { 92 entries, err := os.ReadDir(existingDir2) 93 require.NoError(t, err) 94 require.True(t, len(entries) > 0) 95 }, 96 }, 97 { 98 name: "cachedir new absolute", 99 wazeroOpts: []string{"--cachedir=" + path.Join(tmpDir, "new1")}, 100 test: func(t *testing.T) { 101 entries, err := os.ReadDir("new1") 102 require.NoError(t, err) 103 require.True(t, len(entries) > 0) 104 }, 105 }, 106 { 107 name: "cachedir new relative", 108 wazeroOpts: []string{"--cachedir=new2"}, 109 test: func(t *testing.T) { 110 entries, err := os.ReadDir("new2") 111 require.NoError(t, err) 112 require.True(t, len(entries) > 0) 113 }, 114 }, 115 { 116 name: "enable cpu profiling", 117 wazeroOpts: []string{"-cpuprofile=" + cpuProfile}, 118 test: func(t *testing.T) { 119 require.NoError(t, exist(cpuProfile)) 120 }, 121 }, 122 { 123 name: "enable memory profiling", 124 wazeroOpts: []string{"-memprofile=" + memProfile}, 125 test: func(t *testing.T) { 126 require.NoError(t, exist(memProfile)) 127 }, 128 }, 129 } 130 131 for _, tc := range tests { 132 tt := tc 133 t.Run(tt.name, func(t *testing.T) { 134 args := append([]string{"compile"}, tt.wazeroOpts...) 135 args = append(args, wasmPath) 136 exitCode, stdout, stderr := runMain(t, "", args) 137 require.Zero(t, stderr) 138 require.Equal(t, 0, exitCode, stderr) 139 require.Zero(t, stdout) 140 if test := tt.test; test != nil { 141 test(t) 142 } 143 }) 144 } 145 } 146 147 func requireChdirToTemp(t *testing.T) (string, string) { 148 tmpDir := t.TempDir() 149 oldwd, err := os.Getwd() 150 require.NoError(t, err) 151 require.NoError(t, os.Chdir(tmpDir)) 152 return tmpDir, oldwd 153 } 154 155 func TestCompile_Errors(t *testing.T) { 156 tmpDir := t.TempDir() 157 158 wasmPath := filepath.Join(tmpDir, "test.wasm") 159 require.NoError(t, os.WriteFile(wasmPath, wasmWasiArg, 0o600)) 160 161 notWasmPath := filepath.Join(tmpDir, "bears.wasm") 162 require.NoError(t, os.WriteFile(notWasmPath, []byte("pooh"), 0o600)) 163 164 tests := []struct { 165 message string 166 args []string 167 }{ 168 { 169 message: "missing path to wasm file", 170 args: []string{}, 171 }, 172 { 173 message: "error reading wasm binary", 174 args: []string{"non-existent.wasm"}, 175 }, 176 { 177 message: "error compiling wasm binary", 178 args: []string{notWasmPath}, 179 }, 180 { 181 message: "invalid cachedir", 182 args: []string{"--cachedir", notWasmPath, wasmPath}, 183 }, 184 } 185 186 for _, tc := range tests { 187 tt := tc 188 t.Run(tt.message, func(t *testing.T) { 189 exitCode, _, stderr := runMain(t, "", append([]string{"compile"}, tt.args...)) 190 191 require.Equal(t, 1, exitCode) 192 require.Contains(t, stderr, tt.message) 193 }) 194 } 195 } 196 197 func TestRun(t *testing.T) { 198 tmpDir, oldwd := requireChdirToTemp(t) 199 defer os.Chdir(oldwd) //nolint 200 201 // Restore env logic borrowed from TestClearenv 202 defer func(origEnv []string) { 203 for _, pair := range origEnv { 204 // Environment variables on Windows can begin with = 205 // https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx 206 i := strings.Index(pair[1:], "=") + 1 207 if err := os.Setenv(pair[:i], pair[i+1:]); err != nil { 208 t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err) 209 } 210 } 211 }(os.Environ()) 212 213 // Clear the environment first, so we can make strict assertions. 214 os.Clearenv() 215 os.Setenv("ANIMAL", "kitten") 216 os.Setenv("INHERITED", "wazero") 217 218 // We can't rely on the mtime from git because in CI, only the last commit 219 // is checked out. Instead, grab the effective mtime. 220 bearDir := filepath.Join(oldwd, "testdata", "fs") 221 bearPath := filepath.Join(bearDir, "bear.txt") 222 bearStat, err := os.Stat(bearPath) 223 require.NoError(t, err) 224 bearMtimeNano := bearStat.ModTime().UnixNano() 225 226 existingDir1 := filepath.Join(tmpDir, "existing1") 227 require.NoError(t, os.Mkdir(existingDir1, 0o700)) 228 existingDir2 := filepath.Join(tmpDir, "existing2") 229 require.NoError(t, os.Mkdir(existingDir2, 0o700)) 230 231 cpuProfile := filepath.Join(t.TempDir(), "cpu.out") 232 memProfile := filepath.Join(t.TempDir(), "mem.out") 233 234 type test struct { 235 name string 236 wazeroOpts []string 237 workdir string 238 wasm []byte 239 wasmArgs []string 240 expectedStdout string 241 expectedStderr string 242 expectedExitCode int 243 test func(t *testing.T) 244 } 245 246 tests := []test{ 247 { 248 name: "args", 249 wasm: wasmWasiArg, 250 wasmArgs: []string{"hello world"}, 251 // Executable name is first arg so is printed. 252 expectedStdout: "test.wasm\x00hello world\x00", 253 }, 254 { 255 name: "-- args", 256 wasm: wasmWasiArg, 257 wasmArgs: []string{"--", "hello world"}, 258 // Executable name is first arg so is printed. 259 expectedStdout: "test.wasm\x00hello world\x00", 260 }, 261 { 262 name: "env", 263 wasm: wasmWasiEnv, 264 wazeroOpts: []string{"--env=ANIMAL=bear", "--env=FOOD=sushi"}, 265 expectedStdout: "ANIMAL=bear\x00FOOD=sushi\x00", 266 }, 267 { 268 name: "env-inherit", 269 wasm: wasmWasiEnv, 270 wazeroOpts: []string{"-env-inherit"}, 271 expectedStdout: "ANIMAL=kitten\x00INHERITED=wazero\u0000", 272 }, 273 { 274 name: "env-inherit with env", 275 wasm: wasmWasiEnv, 276 wazeroOpts: []string{"-env-inherit", "--env=ANIMAL=bear"}, 277 expectedStdout: "ANIMAL=bear\x00INHERITED=wazero\u0000", // not ANIMAL=kitten 278 }, 279 { 280 name: "interpreter", 281 wasm: wasmWasiArg, 282 wazeroOpts: []string{"--interpreter"}, // just test it works 283 expectedStdout: "test.wasm\x00", 284 }, 285 { 286 name: "wasi", 287 wasm: wasmWasiFd, 288 wazeroOpts: []string{fmt.Sprintf("--mount=%s:/", bearDir)}, 289 expectedStdout: "pooh\n", 290 }, 291 { 292 name: "wasi readonly", 293 wasm: wasmWasiFd, 294 wazeroOpts: []string{fmt.Sprintf("--mount=%s:/:ro", bearDir)}, 295 expectedStdout: "pooh\n", 296 }, 297 { 298 name: "wasi non root", 299 wasm: wasmCatTinygo, 300 wazeroOpts: []string{fmt.Sprintf("--mount=%s:/animals:ro", bearDir)}, 301 wasmArgs: []string{"/animals/bear.txt"}, 302 expectedStdout: "pooh\n", 303 }, 304 { 305 name: "wasi hostlogging=all", 306 wasm: wasmWasiRandomGet, 307 wazeroOpts: []string{"--hostlogging=all"}, 308 expectedStderr: `--> .$1() 309 ==> wasi_snapshot_preview1.random_get(buf=0,buf_len=1000) 310 <== errno=ESUCCESS 311 <-- 312 `, 313 }, 314 { 315 name: "wasi hostlogging=proc", 316 wasm: wasmCatTinygo, 317 wazeroOpts: []string{"--hostlogging=proc", fmt.Sprintf("--mount=%s:/animals:ro", bearDir)}, 318 wasmArgs: []string{"/animals/not-bear.txt"}, 319 expectedStderr: `==> wasi_snapshot_preview1.proc_exit(rval=1) 320 `, // ^^ proc_exit panics, which short-circuits the logger. Hence, no "<==". 321 expectedExitCode: 1, 322 }, 323 { 324 name: "wasi hostlogging=filesystem", 325 wasm: wasmCatTinygo, 326 wazeroOpts: []string{"--hostlogging=filesystem", fmt.Sprintf("--mount=%s:/animals:ro", bearDir)}, 327 wasmArgs: []string{"/animals/bear.txt"}, 328 expectedStderr: fmt.Sprintf(`==> wasi_snapshot_preview1.fd_prestat_get(fd=3) 329 <== (prestat={pr_name_len=8},errno=ESUCCESS) 330 ==> wasi_snapshot_preview1.fd_prestat_dir_name(fd=3) 331 <== (path=/animals,errno=ESUCCESS) 332 ==> wasi_snapshot_preview1.fd_prestat_get(fd=4) 333 <== (prestat=,errno=EBADF) 334 ==> wasi_snapshot_preview1.fd_fdstat_get(fd=3) 335 <== (stat={filetype=DIRECTORY,fdflags=,fs_rights_base=FD_DATASYNC|FDSTAT_SET_FLAGS|FD_SYNC|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK,fs_rights_inheriting=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK},errno=ESUCCESS) 336 ==> wasi_snapshot_preview1.path_open(fd=3,dirflags=SYMLINK_FOLLOW,path=bear.txt,oflags=,fs_rights_base=FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_ADVISE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK|PATH_RENAME_SOURCE|PATH_RENAME_TARGET|PATH_FILESTAT_GET|PATH_FILESTAT_SET_SIZE|PATH_FILESTAT_SET_TIMES|FD_FILESTAT_GET|FD_FILESTAT_SET_TIMES|PATH_SYMLINK|PATH_REMOVE_DIRECTORY|PATH_UNLINK_FILE|POLL_FD_READWRITE,fs_rights_inheriting=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE|PATH_CREATE_DIRECTORY|PATH_CREATE_FILE|PATH_LINK_SOURCE|PATH_LINK_TARGET|PATH_OPEN|FD_READDIR|PATH_READLINK|PATH_RENAME_SOURCE|PATH_RENAME_TARGET|PATH_FILESTAT_GET|PATH_FILESTAT_SET_SIZE|PATH_FILESTAT_SET_TIMES|FD_FILESTAT_GET|FD_FILESTAT_SET_SIZE|FD_FILESTAT_SET_TIMES|PATH_SYMLINK|PATH_REMOVE_DIRECTORY|PATH_UNLINK_FILE|POLL_FD_READWRITE,fdflags=) 337 <== (opened_fd=4,errno=ESUCCESS) 338 ==> wasi_snapshot_preview1.fd_filestat_get(fd=4) 339 <== (filestat={filetype=REGULAR_FILE,size=5,mtim=%d},errno=ESUCCESS) 340 ==> wasi_snapshot_preview1.fd_read(fd=4,iovs=64744,iovs_len=1) 341 <== (nread=5,errno=ESUCCESS) 342 ==> wasi_snapshot_preview1.fd_read(fd=4,iovs=64744,iovs_len=1) 343 <== (nread=0,errno=ESUCCESS) 344 ==> wasi_snapshot_preview1.fd_close(fd=4) 345 <== errno=ESUCCESS 346 `, bearMtimeNano), 347 expectedStdout: "pooh\n", 348 }, 349 { 350 name: "wasi hostlogging=random", 351 wasm: wasmWasiRandomGet, 352 wazeroOpts: []string{"--hostlogging=random"}, 353 expectedStderr: `==> wasi_snapshot_preview1.random_get(buf=0,buf_len=1000) 354 <== errno=ESUCCESS 355 `, 356 }, 357 { 358 name: "cachedir existing absolute", 359 wazeroOpts: []string{"--cachedir=" + existingDir1}, 360 wasm: wasmWasiArg, 361 wasmArgs: []string{"hello world"}, 362 // Executable name is first arg so is printed. 363 expectedStdout: "test.wasm\x00hello world\x00", 364 test: func(t *testing.T) { 365 entries, err := os.ReadDir(existingDir1) 366 require.NoError(t, err) 367 require.True(t, len(entries) > 0) 368 }, 369 }, 370 { 371 name: "cachedir existing relative", 372 wazeroOpts: []string{"--cachedir=existing2"}, 373 wasm: wasmWasiArg, 374 wasmArgs: []string{"hello world"}, 375 // Executable name is first arg so is printed. 376 expectedStdout: "test.wasm\x00hello world\x00", 377 test: func(t *testing.T) { 378 entries, err := os.ReadDir(existingDir2) 379 require.NoError(t, err) 380 require.True(t, len(entries) > 0) 381 }, 382 }, 383 { 384 name: "cachedir new absolute", 385 wazeroOpts: []string{"--cachedir=" + path.Join(tmpDir, "new1")}, 386 wasm: wasmWasiArg, 387 wasmArgs: []string{"hello world"}, 388 // Executable name is first arg so is printed. 389 expectedStdout: "test.wasm\x00hello world\x00", 390 test: func(t *testing.T) { 391 entries, err := os.ReadDir("new1") 392 require.NoError(t, err) 393 require.True(t, len(entries) > 0) 394 }, 395 }, 396 { 397 name: "cachedir new relative", 398 wazeroOpts: []string{"--cachedir=new2"}, 399 wasm: wasmWasiArg, 400 wasmArgs: []string{"hello world"}, 401 // Executable name is first arg so is printed. 402 expectedStdout: "test.wasm\x00hello world\x00", 403 test: func(t *testing.T) { 404 entries, err := os.ReadDir("new2") 405 require.NoError(t, err) 406 require.True(t, len(entries) > 0) 407 }, 408 }, 409 { 410 name: "timeout: a binary that exceeds the deadline should print an error", 411 wazeroOpts: []string{"-timeout=1ms"}, 412 wasm: wasmInfiniteLoop, 413 expectedStderr: "error: module closed with context deadline exceeded (timeout 1ms)\n", 414 expectedExitCode: int(sys.ExitCodeDeadlineExceeded), 415 test: func(t *testing.T) { 416 require.NoError(t, err) 417 }, 418 }, 419 { 420 name: "timeout: a binary that ends before the deadline should not print a timeout error", 421 wazeroOpts: []string{"-timeout=10s"}, 422 wasm: wasmWasiRandomGet, 423 test: func(t *testing.T) { 424 require.NoError(t, err) 425 }, 426 }, 427 { 428 name: "should run wasi_unstable", 429 wasm: wasmWasiUnstable, 430 expectedExitCode: 2, 431 test: func(t *testing.T) { 432 require.NoError(t, err) 433 }, 434 }, 435 { 436 name: "enable cpu profiling", 437 wazeroOpts: []string{"-cpuprofile=" + cpuProfile}, 438 wasm: wasmWasiRandomGet, 439 test: func(t *testing.T) { 440 require.NoError(t, exist(cpuProfile)) 441 }, 442 }, 443 { 444 name: "enable memory profiling", 445 wazeroOpts: []string{"-memprofile=" + memProfile}, 446 wasm: wasmWasiRandomGet, 447 test: func(t *testing.T) { 448 require.NoError(t, exist(memProfile)) 449 }, 450 }, 451 } 452 453 for _, tt := range tests { 454 tc := tt 455 456 if tc.wasm == nil { 457 // We should only skip when the runtime is a scratch image. 458 require.False(t, platform.CompilerSupported()) 459 continue 460 } 461 t.Run(tc.name, func(t *testing.T) { 462 wasmPath := filepath.Join(tmpDir, "test.wasm") 463 require.NoError(t, os.WriteFile(wasmPath, tc.wasm, 0o700)) 464 465 args := append([]string{"run"}, tc.wazeroOpts...) 466 args = append(args, wasmPath) 467 args = append(args, tc.wasmArgs...) 468 exitCode, stdout, stderr := runMain(t, tc.workdir, args) 469 470 require.Equal(t, tc.expectedStderr, stderr) 471 require.Equal(t, tc.expectedExitCode, exitCode, stderr) 472 require.Equal(t, tc.expectedStdout, stdout) 473 if test := tc.test; test != nil { 474 test(t) 475 } 476 }) 477 } 478 } 479 480 func TestVersion(t *testing.T) { 481 exitCode, stdout, stderr := runMain(t, "", []string{"version"}) 482 require.Equal(t, 0, exitCode) 483 require.Equal(t, version.GetWazeroVersion()+"\n", stdout) 484 require.Equal(t, "", stderr) 485 } 486 487 func TestRun_Errors(t *testing.T) { 488 wasmPath := filepath.Join(t.TempDir(), "test.wasm") 489 require.NoError(t, os.WriteFile(wasmPath, wasmWasiArg, 0o700)) 490 491 notWasmPath := filepath.Join(t.TempDir(), "bears.wasm") 492 require.NoError(t, os.WriteFile(notWasmPath, []byte("pooh"), 0o700)) 493 494 tests := []struct { 495 message string 496 args []string 497 }{ 498 { 499 message: "missing path to wasm file", 500 args: []string{}, 501 }, 502 { 503 message: "error reading wasm binary", 504 args: []string{"non-existent.wasm"}, 505 }, 506 { 507 message: "error compiling wasm binary", 508 args: []string{notWasmPath}, 509 }, 510 { 511 message: "invalid environment variable", 512 args: []string{"--env=ANIMAL", "testdata/wasi_env.wasm"}, 513 }, 514 { 515 message: "invalid mount", // not found 516 args: []string{"--mount=te", "testdata/wasi_env.wasm"}, 517 }, 518 { 519 message: "invalid cachedir", 520 args: []string{"--cachedir", notWasmPath, wasmPath}, 521 }, 522 { 523 message: "timeout duration may not be negative", 524 args: []string{"-timeout=-10s", wasmPath}, 525 }, 526 } 527 528 for _, tc := range tests { 529 tt := tc 530 t.Run(tt.message, func(t *testing.T) { 531 exitCode, _, stderr := runMain(t, "", append([]string{"run"}, tt.args...)) 532 533 require.Equal(t, 1, exitCode) 534 require.Contains(t, stderr, tt.message) 535 }) 536 } 537 } 538 539 var _ api.FunctionDefinition = importer{} 540 541 type importer struct { 542 internalapi.WazeroOnlyType 543 moduleName, name string 544 } 545 546 func (i importer) ModuleName() string { return "" } 547 func (i importer) Index() uint32 { return 0 } 548 func (i importer) Import() (moduleName, name string, isImport bool) { 549 return i.moduleName, i.name, true 550 } 551 func (i importer) ExportNames() []string { return nil } 552 func (i importer) Name() string { return "" } 553 func (i importer) DebugName() string { return "" } 554 func (i importer) GoFunction() interface{} { return nil } 555 func (i importer) ParamTypes() []api.ValueType { return nil } 556 func (i importer) ParamNames() []string { return nil } 557 func (i importer) ResultTypes() []api.ValueType { return nil } 558 func (i importer) ResultNames() []string { return nil } 559 560 func Test_detectImports(t *testing.T) { 561 tests := []struct { 562 message string 563 imports []api.FunctionDefinition 564 mode importMode 565 }{ 566 { 567 message: "no imports", 568 }, 569 { 570 message: "other imports", 571 imports: []api.FunctionDefinition{ 572 importer{internalapi.WazeroOnlyType{}, "env", "emscripten_notify_memory_growth"}, 573 }, 574 }, 575 { 576 message: "wasi", 577 imports: []api.FunctionDefinition{ 578 importer{internalapi.WazeroOnlyType{}, wasi_snapshot_preview1.ModuleName, "fd_read"}, 579 }, 580 mode: modeWasi, 581 }, 582 { 583 message: "unstable_wasi", 584 imports: []api.FunctionDefinition{ 585 importer{internalapi.WazeroOnlyType{}, "wasi_unstable", "fd_read"}, 586 }, 587 mode: modeWasiUnstable, 588 }, 589 } 590 591 for _, tc := range tests { 592 tt := tc 593 t.Run(tt.message, func(t *testing.T) { 594 mode := detectImports(tc.imports) 595 require.Equal(t, tc.mode, mode) 596 }) 597 } 598 } 599 600 func Test_logScopesFlag(t *testing.T) { 601 tests := []struct { 602 name string 603 values []string 604 expected logging.LogScopes 605 }{ 606 { 607 name: "defaults to none", 608 expected: logging.LogScopeNone, 609 }, 610 { 611 name: "ignores empty", 612 values: []string{""}, 613 expected: logging.LogScopeNone, 614 }, 615 { 616 name: "all", 617 values: []string{"all"}, 618 expected: logging.LogScopeAll, 619 }, 620 { 621 name: "clock", 622 values: []string{"clock"}, 623 expected: logging.LogScopeClock, 624 }, 625 { 626 name: "filesystem", 627 values: []string{"filesystem"}, 628 expected: logging.LogScopeFilesystem, 629 }, 630 { 631 name: "memory", 632 values: []string{"memory"}, 633 expected: logging.LogScopeMemory, 634 }, 635 { 636 name: "poll", 637 values: []string{"poll"}, 638 expected: logging.LogScopePoll, 639 }, 640 { 641 name: "random", 642 values: []string{"random"}, 643 expected: logging.LogScopeRandom, 644 }, 645 { 646 name: "clock filesystem poll random", 647 values: []string{"clock", "filesystem", "poll", "random"}, 648 expected: logging.LogScopeClock | logging.LogScopeFilesystem | logging.LogScopePoll | logging.LogScopeRandom, 649 }, 650 { 651 name: "clock,filesystem poll,random", 652 values: []string{"clock,filesystem", "poll,random"}, 653 expected: logging.LogScopeClock | logging.LogScopeFilesystem | logging.LogScopePoll | logging.LogScopeRandom, 654 }, 655 { 656 name: "all random", 657 values: []string{"all", "random"}, 658 expected: logging.LogScopeAll, 659 }, 660 } 661 662 for _, tt := range tests { 663 tc := tt 664 t.Run(tc.name, func(t *testing.T) { 665 f := logScopesFlag(0) 666 for _, v := range tc.values { 667 require.NoError(t, f.Set(v)) 668 } 669 require.Equal(t, tc.expected, logging.LogScopes(f)) 670 }) 671 } 672 } 673 674 func TestHelp(t *testing.T) { 675 exitCode, _, stderr := runMain(t, "", []string{"-h"}) 676 require.Equal(t, 0, exitCode) 677 fmt.Println(stderr) 678 require.Equal(t, `wazero CLI 679 680 Usage: 681 wazero <command> 682 683 Commands: 684 compile Pre-compiles a WebAssembly binary 685 run Runs a WebAssembly binary 686 version Displays the version of wazero CLI 687 `, stderr) 688 } 689 690 func runMain(t *testing.T, workdir string, args []string) (int, string, string) { 691 t.Helper() 692 693 // Use a workdir override if supplied. 694 if workdir != "" { 695 oldcwd, err := os.Getwd() 696 require.NoError(t, err) 697 698 require.NoError(t, os.Chdir(workdir)) 699 defer func() { 700 require.NoError(t, os.Chdir(oldcwd)) 701 }() 702 } 703 704 oldArgs := os.Args 705 t.Cleanup(func() { 706 os.Args = oldArgs 707 }) 708 os.Args = append([]string{"wazero"}, args...) 709 flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 710 711 stdout := new(bytes.Buffer) 712 stderr := new(bytes.Buffer) 713 exitCode := doMain(stdout, stderr) 714 715 return exitCode, stdout.String(), stderr.String() 716 } 717 718 func exist(path string) error { 719 f, err := os.Open(path) 720 if err != nil { 721 return err 722 } 723 return f.Close() 724 }