1 #![cfg(not(miri))] 2 3 use anyhow::{bail, Result}; 4 use std::fs::File; 5 use std::io::Write; 6 use std::path::Path; 7 use std::process::{Command, Output, Stdio}; 8 use tempfile::{NamedTempFile, TempDir}; 9 10 // Run the wasmtime CLI with the provided args and return the `Output`. 11 // If the `stdin` is `Some`, opens the file and redirects to the child's stdin. 12 pub fn run_wasmtime_for_output(args: &[&str], stdin: Option<&Path>) -> Result<Output> { 13 let mut cmd = get_wasmtime_command()?; 14 cmd.args(args); 15 if let Some(file) = stdin { 16 cmd.stdin(File::open(file)?); 17 } 18 cmd.output().map_err(Into::into) 19 } 20 21 /// Get the Wasmtime CLI as a [Command]. 22 pub fn get_wasmtime_command() -> Result<Command> { 23 // Figure out the Wasmtime binary from the current executable. 24 let runner = std::env::vars() 25 .filter(|(k, _v)| k.starts_with("CARGO_TARGET") && k.ends_with("RUNNER")) 26 .next(); 27 let mut me = std::env::current_exe()?; 28 me.pop(); // chop off the file name 29 me.pop(); // chop off `deps` 30 me.push("wasmtime"); 31 32 // If we're running tests with a "runner" then we might be doing something 33 // like cross-emulation, so spin up the emulator rather than the tests 34 // itself, which may not be natively executable. 35 let mut cmd = if let Some((_, runner)) = runner { 36 let mut parts = runner.split_whitespace(); 37 let mut cmd = Command::new(parts.next().unwrap()); 38 for arg in parts { 39 cmd.arg(arg); 40 } 41 cmd.arg(&me); 42 cmd 43 } else { 44 Command::new(&me) 45 }; 46 47 // Ignore this if it's specified in the environment to allow tests to run in 48 // "default mode" by default. 49 cmd.env_remove("WASMTIME_NEW_CLI"); 50 51 Ok(cmd) 52 } 53 54 // Run the wasmtime CLI with the provided args and, if it succeeds, return 55 // the standard output in a `String`. 56 fn run_wasmtime(args: &[&str]) -> Result<String> { 57 let output = run_wasmtime_for_output(args, None)?; 58 if !output.status.success() { 59 bail!( 60 "Failed to execute wasmtime with: {:?}\n{}", 61 args, 62 String::from_utf8_lossy(&output.stderr) 63 ); 64 } 65 Ok(String::from_utf8(output.stdout).unwrap()) 66 } 67 68 fn build_wasm(wat_path: impl AsRef<Path>) -> Result<NamedTempFile> { 69 let mut wasm_file = NamedTempFile::new()?; 70 let wasm = wat::parse_file(wat_path)?; 71 wasm_file.write(&wasm)?; 72 Ok(wasm_file) 73 } 74 75 // Very basic use case: compile binary wasm file and run specific function with arguments. 76 #[test] 77 fn run_wasmtime_simple() -> Result<()> { 78 let wasm = build_wasm("tests/all/cli_tests/simple.wat")?; 79 run_wasmtime(&[ 80 "run", 81 "--invoke", 82 "simple", 83 "-Ccache=n", 84 wasm.path().to_str().unwrap(), 85 "4", 86 ])?; 87 Ok(()) 88 } 89 90 // Wasmtime shall fail when not enough arguments were provided. 91 #[test] 92 fn run_wasmtime_simple_fail_no_args() -> Result<()> { 93 let wasm = build_wasm("tests/all/cli_tests/simple.wat")?; 94 assert!( 95 run_wasmtime(&[ 96 "run", 97 "-Ccache=n", 98 "--invoke", 99 "simple", 100 wasm.path().to_str().unwrap(), 101 ]) 102 .is_err(), 103 "shall fail" 104 ); 105 Ok(()) 106 } 107 108 #[test] 109 fn run_coredump_smoketest() -> Result<()> { 110 let wasm = build_wasm("tests/all/cli_tests/coredump_smoketest.wat")?; 111 let coredump_file = NamedTempFile::new()?; 112 let coredump_arg = format!("-Dcoredump={}", coredump_file.path().display()); 113 let err = run_wasmtime(&[ 114 "run", 115 "--invoke", 116 "a", 117 "-Ccache=n", 118 &coredump_arg, 119 wasm.path().to_str().unwrap(), 120 ]) 121 .unwrap_err(); 122 assert!(err.to_string().contains(&format!( 123 "core dumped at {}", 124 coredump_file.path().display() 125 ))); 126 Ok(()) 127 } 128 129 // Running simple wat 130 #[test] 131 fn run_wasmtime_simple_wat() -> Result<()> { 132 let wasm = build_wasm("tests/all/cli_tests/simple.wat")?; 133 run_wasmtime(&[ 134 "run", 135 "--invoke", 136 "simple", 137 "-Ccache=n", 138 wasm.path().to_str().unwrap(), 139 "4", 140 ])?; 141 assert_eq!( 142 run_wasmtime(&[ 143 "run", 144 "--invoke", 145 "get_f32", 146 "-Ccache=n", 147 wasm.path().to_str().unwrap(), 148 ])?, 149 "100\n" 150 ); 151 assert_eq!( 152 run_wasmtime(&[ 153 "run", 154 "--invoke", 155 "get_f64", 156 "-Ccache=n", 157 wasm.path().to_str().unwrap(), 158 ])?, 159 "100\n" 160 ); 161 Ok(()) 162 } 163 164 // Running a wat that traps. 165 #[test] 166 fn run_wasmtime_unreachable_wat() -> Result<()> { 167 let wasm = build_wasm("tests/all/cli_tests/unreachable.wat")?; 168 let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?; 169 170 assert_ne!(output.stderr, b""); 171 assert_eq!(output.stdout, b""); 172 assert!(!output.status.success()); 173 174 let code = output 175 .status 176 .code() 177 .expect("wasmtime process should exit normally"); 178 179 // Test for the specific error code Wasmtime uses to indicate a trap return. 180 #[cfg(unix)] 181 assert_eq!(code, 128 + libc::SIGABRT); 182 #[cfg(windows)] 183 assert_eq!(code, 3); 184 Ok(()) 185 } 186 187 // Run a simple WASI hello world, snapshot0 edition. 188 #[test] 189 fn hello_wasi_snapshot0() -> Result<()> { 190 let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?; 191 for preview2 in ["-Spreview2=n", "-Spreview2=y"] { 192 let stdout = run_wasmtime(&["-Ccache=n", preview2, wasm.path().to_str().unwrap()])?; 193 assert_eq!(stdout, "Hello, world!\n"); 194 } 195 Ok(()) 196 } 197 198 // Run a simple WASI hello world, snapshot1 edition. 199 #[test] 200 fn hello_wasi_snapshot1() -> Result<()> { 201 let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?; 202 let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; 203 assert_eq!(stdout, "Hello, world!\n"); 204 Ok(()) 205 } 206 207 #[test] 208 fn timeout_in_start() -> Result<()> { 209 let wasm = build_wasm("tests/all/cli_tests/iloop-start.wat")?; 210 let output = run_wasmtime_for_output( 211 &[ 212 "run", 213 "-Wtimeout=1ms", 214 "-Ccache=n", 215 wasm.path().to_str().unwrap(), 216 ], 217 None, 218 )?; 219 assert!(!output.status.success()); 220 assert_eq!(output.stdout, b""); 221 let stderr = String::from_utf8_lossy(&output.stderr); 222 assert!( 223 stderr.contains("wasm trap: interrupt"), 224 "bad stderr: {}", 225 stderr 226 ); 227 Ok(()) 228 } 229 230 #[test] 231 fn timeout_in_invoke() -> Result<()> { 232 let wasm = build_wasm("tests/all/cli_tests/iloop-invoke.wat")?; 233 let output = run_wasmtime_for_output( 234 &[ 235 "run", 236 "-Wtimeout=1ms", 237 "-Ccache=n", 238 wasm.path().to_str().unwrap(), 239 ], 240 None, 241 )?; 242 assert!(!output.status.success()); 243 assert_eq!(output.stdout, b""); 244 let stderr = String::from_utf8_lossy(&output.stderr); 245 assert!( 246 stderr.contains("wasm trap: interrupt"), 247 "bad stderr: {}", 248 stderr 249 ); 250 Ok(()) 251 } 252 253 // Exit with a valid non-zero exit code, snapshot0 edition. 254 #[test] 255 fn exit2_wasi_snapshot0() -> Result<()> { 256 let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot0.wat")?; 257 258 for preview2 in ["-Spreview2=n", "-Spreview2=y"] { 259 let output = run_wasmtime_for_output( 260 &["-Ccache=n", preview2, wasm.path().to_str().unwrap()], 261 None, 262 )?; 263 assert_eq!(output.status.code().unwrap(), 2); 264 } 265 Ok(()) 266 } 267 268 // Exit with a valid non-zero exit code, snapshot1 edition. 269 #[test] 270 fn exit2_wasi_snapshot1() -> Result<()> { 271 let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot1.wat")?; 272 let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; 273 assert_eq!(output.status.code().unwrap(), 2); 274 Ok(()) 275 } 276 277 // Exit with a valid non-zero exit code, snapshot0 edition. 278 #[test] 279 fn exit125_wasi_snapshot0() -> Result<()> { 280 let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot0.wat")?; 281 for preview2 in ["-Spreview2=n", "-Spreview2=y"] { 282 let output = run_wasmtime_for_output( 283 &["-Ccache=n", preview2, wasm.path().to_str().unwrap()], 284 None, 285 )?; 286 dbg!(&output); 287 if cfg!(windows) { 288 assert_eq!(output.status.code().unwrap(), 1); 289 } else { 290 assert_eq!(output.status.code().unwrap(), 125); 291 } 292 } 293 Ok(()) 294 } 295 296 // Exit with a valid non-zero exit code, snapshot1 edition. 297 #[test] 298 fn exit125_wasi_snapshot1() -> Result<()> { 299 let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot1.wat")?; 300 let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; 301 if cfg!(windows) { 302 assert_eq!(output.status.code().unwrap(), 1); 303 } else { 304 assert_eq!(output.status.code().unwrap(), 125); 305 } 306 Ok(()) 307 } 308 309 // Exit with an invalid non-zero exit code, snapshot0 edition. 310 #[test] 311 fn exit126_wasi_snapshot0() -> Result<()> { 312 let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot0.wat")?; 313 314 for preview2 in ["-Spreview2=n", "-Spreview2=y"] { 315 let output = run_wasmtime_for_output( 316 &["-Ccache=n", preview2, wasm.path().to_str().unwrap()], 317 None, 318 )?; 319 assert_eq!(output.status.code().unwrap(), 1); 320 assert!(output.stdout.is_empty()); 321 assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status")); 322 } 323 Ok(()) 324 } 325 326 // Exit with an invalid non-zero exit code, snapshot1 edition. 327 #[test] 328 fn exit126_wasi_snapshot1() -> Result<()> { 329 let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot1.wat")?; 330 let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?; 331 assert_eq!(output.status.code().unwrap(), 1); 332 assert!(output.stdout.is_empty()); 333 assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status")); 334 Ok(()) 335 } 336 337 // Run a minimal command program. 338 #[test] 339 fn minimal_command() -> Result<()> { 340 let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?; 341 let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; 342 assert_eq!(stdout, ""); 343 Ok(()) 344 } 345 346 // Run a minimal reactor program. 347 #[test] 348 fn minimal_reactor() -> Result<()> { 349 let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?; 350 let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; 351 assert_eq!(stdout, ""); 352 Ok(()) 353 } 354 355 // Attempt to call invoke on a command. 356 #[test] 357 fn command_invoke() -> Result<()> { 358 let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?; 359 run_wasmtime(&[ 360 "run", 361 "--invoke", 362 "_start", 363 "-Ccache=n", 364 wasm.path().to_str().unwrap(), 365 ])?; 366 Ok(()) 367 } 368 369 // Attempt to call invoke on a command. 370 #[test] 371 fn reactor_invoke() -> Result<()> { 372 let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?; 373 run_wasmtime(&[ 374 "run", 375 "--invoke", 376 "_initialize", 377 "-Ccache=n", 378 wasm.path().to_str().unwrap(), 379 ])?; 380 Ok(()) 381 } 382 383 // Run the greeter test, which runs a preloaded reactor and a command. 384 #[test] 385 fn greeter() -> Result<()> { 386 let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?; 387 let stdout = run_wasmtime(&[ 388 "run", 389 "-Ccache=n", 390 "--preload", 391 "reactor=tests/all/cli_tests/greeter_reactor.wat", 392 wasm.path().to_str().unwrap(), 393 ])?; 394 assert_eq!( 395 stdout, 396 "Hello _initialize\nHello _start\nHello greet\nHello done\n" 397 ); 398 Ok(()) 399 } 400 401 // Run the greeter test, but this time preload a command. 402 #[test] 403 fn greeter_preload_command() -> Result<()> { 404 let wasm = build_wasm("tests/all/cli_tests/greeter_reactor.wat")?; 405 let stdout = run_wasmtime(&[ 406 "run", 407 "-Ccache=n", 408 "--preload", 409 "reactor=tests/all/cli_tests/hello_wasi_snapshot1.wat", 410 wasm.path().to_str().unwrap(), 411 ])?; 412 assert_eq!(stdout, "Hello _initialize\n"); 413 Ok(()) 414 } 415 416 // Run the greeter test, which runs a preloaded reactor and a command. 417 #[test] 418 fn greeter_preload_callable_command() -> Result<()> { 419 let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?; 420 let stdout = run_wasmtime(&[ 421 "run", 422 "-Ccache=n", 423 "--preload", 424 "reactor=tests/all/cli_tests/greeter_callable_command.wat", 425 wasm.path().to_str().unwrap(), 426 ])?; 427 assert_eq!(stdout, "Hello _start\nHello callable greet\nHello done\n"); 428 Ok(()) 429 } 430 431 // Ensure successful WASI exit call with FPR saving frames on stack for Windows x64 432 // See https://github.com/bytecodealliance/wasmtime/issues/1967 433 #[test] 434 fn exit_with_saved_fprs() -> Result<()> { 435 let wasm = build_wasm("tests/all/cli_tests/exit_with_saved_fprs.wat")?; 436 let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; 437 assert_eq!(output.status.code().unwrap(), 0); 438 assert!(output.stdout.is_empty()); 439 Ok(()) 440 } 441 442 #[test] 443 fn run_cwasm() -> Result<()> { 444 let td = TempDir::new()?; 445 let cwasm = td.path().join("foo.cwasm"); 446 let stdout = run_wasmtime(&[ 447 "compile", 448 "tests/all/cli_tests/simple.wat", 449 "-o", 450 cwasm.to_str().unwrap(), 451 ])?; 452 assert_eq!(stdout, ""); 453 let stdout = run_wasmtime(&["run", "--allow-precompiled", cwasm.to_str().unwrap()])?; 454 assert_eq!(stdout, ""); 455 Ok(()) 456 } 457 458 #[cfg(unix)] 459 #[test] 460 fn hello_wasi_snapshot0_from_stdin() -> Result<()> { 461 // Run a simple WASI hello world, snapshot0 edition. 462 // The module is piped from standard input. 463 let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?; 464 for preview2 in ["-Spreview2=n", "-Spreview2=y"] { 465 let stdout = { 466 let path = wasm.path(); 467 let args: &[&str] = &["-Ccache=n", preview2, "-"]; 468 let output = run_wasmtime_for_output(args, Some(path))?; 469 if !output.status.success() { 470 bail!( 471 "Failed to execute wasmtime with: {:?}\n{}", 472 args, 473 String::from_utf8_lossy(&output.stderr) 474 ); 475 } 476 Ok::<_, anyhow::Error>(String::from_utf8(output.stdout).unwrap()) 477 }?; 478 assert_eq!(stdout, "Hello, world!\n"); 479 } 480 Ok(()) 481 } 482 483 #[test] 484 fn specify_env() -> Result<()> { 485 // By default no env is inherited 486 let output = get_wasmtime_command()? 487 .args(&["run", "tests/all/cli_tests/print_env.wat"]) 488 .env("THIS_WILL_NOT", "show up in the output") 489 .output()?; 490 assert!(output.status.success()); 491 assert_eq!(String::from_utf8_lossy(&output.stdout), ""); 492 493 // Specify a single env var 494 let output = get_wasmtime_command()? 495 .args(&[ 496 "run", 497 "--env", 498 "FOO=bar", 499 "tests/all/cli_tests/print_env.wat", 500 ]) 501 .output()?; 502 assert!(output.status.success()); 503 assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n"); 504 505 // Inherit a single env var 506 let output = get_wasmtime_command()? 507 .args(&["run", "--env", "FOO", "tests/all/cli_tests/print_env.wat"]) 508 .env("FOO", "bar") 509 .output()?; 510 assert!(output.status.success()); 511 assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n"); 512 513 // Inherit a nonexistent env var 514 let output = get_wasmtime_command()? 515 .args(&[ 516 "run", 517 "--env", 518 "SURELY_THIS_ENV_VAR_DOES_NOT_EXIST_ANYWHERE_RIGHT", 519 "tests/all/cli_tests/print_env.wat", 520 ]) 521 .output()?; 522 assert!(!output.status.success()); 523 524 Ok(()) 525 } 526 527 #[cfg(unix)] 528 #[test] 529 fn run_cwasm_from_stdin() -> Result<()> { 530 use std::process::Stdio; 531 532 let td = TempDir::new()?; 533 let cwasm = td.path().join("foo.cwasm"); 534 let stdout = run_wasmtime(&[ 535 "compile", 536 "tests/all/cli_tests/simple.wat", 537 "-o", 538 cwasm.to_str().unwrap(), 539 ])?; 540 assert_eq!(stdout, ""); 541 542 // If stdin is literally the file itself then that should work 543 let args: &[&str] = &["run", "--allow-precompiled", "-"]; 544 let output = get_wasmtime_command()? 545 .args(args) 546 .stdin(File::open(&cwasm)?) 547 .output()?; 548 assert!(output.status.success(), "a file as stdin should work"); 549 550 // If stdin is a pipe, that should also work 551 let input = std::fs::read(&cwasm)?; 552 let mut child = get_wasmtime_command()? 553 .args(args) 554 .stdin(Stdio::piped()) 555 .stdout(Stdio::piped()) 556 .stderr(Stdio::piped()) 557 .spawn()?; 558 let mut stdin = child.stdin.take().unwrap(); 559 let t = std::thread::spawn(move || { 560 let _ = stdin.write_all(&input); 561 }); 562 let output = child.wait_with_output()?; 563 assert!(output.status.success()); 564 t.join().unwrap(); 565 Ok(()) 566 } 567 568 #[cfg(feature = "wasi-threads")] 569 #[test] 570 fn run_threads() -> Result<()> { 571 let wasm = build_wasm("tests/all/cli_tests/threads.wat")?; 572 let stdout = run_wasmtime(&[ 573 "run", 574 "-Wthreads", 575 "-Sthreads", 576 "-Ccache=n", 577 wasm.path().to_str().unwrap(), 578 ])?; 579 580 assert!( 581 stdout 582 == "Called _start\n\ 583 Running wasi_thread_start\n\ 584 Running wasi_thread_start\n\ 585 Running wasi_thread_start\n\ 586 Done\n" 587 ); 588 Ok(()) 589 } 590 591 #[cfg(feature = "wasi-threads")] 592 #[test] 593 fn run_simple_with_wasi_threads() -> Result<()> { 594 // We expect to be able to run Wasm modules that do not have correct 595 // wasi-thread entry points or imported shared memory as long as no threads 596 // are spawned. 597 let wasm = build_wasm("tests/all/cli_tests/simple.wat")?; 598 let stdout = run_wasmtime(&[ 599 "run", 600 "-Wthreads", 601 "-Sthreads", 602 "-Ccache=n", 603 "--invoke", 604 "simple", 605 wasm.path().to_str().unwrap(), 606 "4", 607 ])?; 608 assert_eq!(stdout, "4\n"); 609 Ok(()) 610 } 611 612 #[test] 613 fn wasm_flags() -> Result<()> { 614 // Any argument after the wasm module should be interpreted as for the 615 // command itself 616 let stdout = run_wasmtime(&[ 617 "run", 618 "--", 619 "tests/all/cli_tests/print-arguments.wat", 620 "--argument", 621 "-for", 622 "the", 623 "command", 624 ])?; 625 assert_eq!( 626 stdout, 627 "\ 628 print-arguments.wat\n\ 629 --argument\n\ 630 -for\n\ 631 the\n\ 632 command\n\ 633 " 634 ); 635 let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "-"])?; 636 assert_eq!( 637 stdout, 638 "\ 639 print-arguments.wat\n\ 640 -\n\ 641 " 642 ); 643 let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "--"])?; 644 assert_eq!( 645 stdout, 646 "\ 647 print-arguments.wat\n\ 648 --\n\ 649 " 650 ); 651 let stdout = run_wasmtime(&[ 652 "run", 653 "--", 654 "tests/all/cli_tests/print-arguments.wat", 655 "--", 656 "--", 657 "-a", 658 "b", 659 ])?; 660 assert_eq!( 661 stdout, 662 "\ 663 print-arguments.wat\n\ 664 --\n\ 665 --\n\ 666 -a\n\ 667 b\n\ 668 " 669 ); 670 Ok(()) 671 } 672 673 #[test] 674 fn name_same_as_builtin_command() -> Result<()> { 675 // a bare subcommand shouldn't run successfully 676 let output = get_wasmtime_command()? 677 .current_dir("tests/all/cli_tests") 678 .arg("run") 679 .output()?; 680 assert!(!output.status.success()); 681 682 // a `--` prefix should let everything else get interpreted as a wasm 683 // module and arguments, even if the module has a name like `run` 684 let output = get_wasmtime_command()? 685 .current_dir("tests/all/cli_tests") 686 .arg("--") 687 .arg("run") 688 .output()?; 689 assert!(output.status.success(), "expected success got {output:#?}"); 690 691 // Passing options before the subcommand should work and doesn't require 692 // `--` to disambiguate 693 let output = get_wasmtime_command()? 694 .current_dir("tests/all/cli_tests") 695 .arg("-Ccache=n") 696 .arg("run") 697 .output()?; 698 assert!(output.status.success(), "expected success got {output:#?}"); 699 Ok(()) 700 } 701 702 #[test] 703 #[cfg(unix)] 704 fn run_just_stdin_argument() -> Result<()> { 705 let output = get_wasmtime_command()? 706 .arg("-") 707 .stdin(File::open("tests/all/cli_tests/simple.wat")?) 708 .output()?; 709 assert!(output.status.success()); 710 Ok(()) 711 } 712 713 #[test] 714 fn wasm_flags_without_subcommand() -> Result<()> { 715 let output = get_wasmtime_command()? 716 .current_dir("tests/all/cli_tests/") 717 .arg("print-arguments.wat") 718 .arg("-foo") 719 .arg("bar") 720 .output()?; 721 assert!(output.status.success()); 722 assert_eq!( 723 String::from_utf8_lossy(&output.stdout), 724 "\ 725 print-arguments.wat\n\ 726 -foo\n\ 727 bar\n\ 728 " 729 ); 730 Ok(()) 731 } 732 733 #[test] 734 fn wasi_misaligned_pointer() -> Result<()> { 735 let output = get_wasmtime_command()? 736 .arg("./tests/all/cli_tests/wasi_misaligned_pointer.wat") 737 .output()?; 738 assert!(!output.status.success()); 739 let stderr = String::from_utf8_lossy(&output.stderr); 740 assert!( 741 stderr.contains("Pointer not aligned"), 742 "bad stderr: {stderr}", 743 ); 744 Ok(()) 745 } 746 747 #[test] 748 #[cfg_attr(not(feature = "component-model"), ignore)] 749 fn hello_with_preview2() -> Result<()> { 750 let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?; 751 let stdout = run_wasmtime(&["-Ccache=n", "-Spreview2", wasm.path().to_str().unwrap()])?; 752 assert_eq!(stdout, "Hello, world!\n"); 753 Ok(()) 754 } 755 756 #[test] 757 #[cfg_attr(not(feature = "component-model"), ignore)] 758 fn component_missing_feature() -> Result<()> { 759 let path = "tests/all/cli_tests/empty-component.wat"; 760 let wasm = build_wasm(path)?; 761 let output = get_wasmtime_command()? 762 .arg("-Ccache=n") 763 .arg(wasm.path()) 764 .output()?; 765 assert!(!output.status.success()); 766 let stderr = String::from_utf8_lossy(&output.stderr); 767 assert!( 768 stderr.contains("cannot execute a component without `--wasm component-model`"), 769 "bad stderr: {stderr}" 770 ); 771 772 // also tests with raw *.wat input 773 let output = get_wasmtime_command()? 774 .arg("-Ccache=n") 775 .arg(path) 776 .output()?; 777 assert!(!output.status.success()); 778 let stderr = String::from_utf8_lossy(&output.stderr); 779 assert!( 780 stderr.contains("cannot execute a component without `--wasm component-model`"), 781 "bad stderr: {stderr}" 782 ); 783 784 Ok(()) 785 } 786 787 // If the text format is invalid then the filename should be mentioned in the 788 // error message. 789 #[test] 790 fn bad_text_syntax() -> Result<()> { 791 let output = get_wasmtime_command()? 792 .arg("-Ccache=n") 793 .arg("tests/all/cli_tests/bad-syntax.wat") 794 .output()?; 795 assert!(!output.status.success()); 796 let stderr = String::from_utf8_lossy(&output.stderr); 797 assert!( 798 stderr.contains("--> tests/all/cli_tests/bad-syntax.wat"), 799 "bad stderr: {stderr}" 800 ); 801 Ok(()) 802 } 803 804 #[test] 805 #[cfg_attr(not(feature = "component-model"), ignore)] 806 fn run_basic_component() -> Result<()> { 807 let path = "tests/all/cli_tests/component-basic.wat"; 808 let wasm = build_wasm(path)?; 809 810 // Run both the `*.wasm` binary and the text format 811 run_wasmtime(&[ 812 "-Ccache=n", 813 "-Wcomponent-model", 814 wasm.path().to_str().unwrap(), 815 ])?; 816 run_wasmtime(&["-Ccache=n", "-Wcomponent-model", path])?; 817 818 Ok(()) 819 } 820 821 #[test] 822 #[cfg_attr(not(feature = "component-model"), ignore)] 823 fn run_precompiled_component() -> Result<()> { 824 let td = TempDir::new()?; 825 let cwasm = td.path().join("component-basic.cwasm"); 826 let stdout = run_wasmtime(&[ 827 "compile", 828 "tests/all/cli_tests/component-basic.wat", 829 "-o", 830 cwasm.to_str().unwrap(), 831 "-Wcomponent-model", 832 ])?; 833 assert_eq!(stdout, ""); 834 let stdout = run_wasmtime(&[ 835 "run", 836 "-Wcomponent-model", 837 "--allow-precompiled", 838 cwasm.to_str().unwrap(), 839 ])?; 840 assert_eq!(stdout, ""); 841 842 Ok(()) 843 } 844 845 #[test] 846 fn memory_growth_failure() -> Result<()> { 847 let output = get_wasmtime_command()? 848 .args(&[ 849 "run", 850 "-Wmemory64", 851 "-Wtrap-on-grow-failure", 852 "tests/all/cli_tests/memory-grow-failure.wat", 853 ]) 854 .output()?; 855 assert!(!output.status.success()); 856 let stderr = String::from_utf8_lossy(&output.stderr); 857 assert!( 858 stderr.contains("forcing a memory growth failure to be a trap"), 859 "bad stderr: {stderr}" 860 ); 861 Ok(()) 862 } 863 864 #[test] 865 fn table_growth_failure() -> Result<()> { 866 let output = get_wasmtime_command()? 867 .args(&[ 868 "run", 869 "-Wtrap-on-grow-failure", 870 "tests/all/cli_tests/table-grow-failure.wat", 871 ]) 872 .output()?; 873 assert!(!output.status.success()); 874 let stderr = String::from_utf8_lossy(&output.stderr); 875 assert!( 876 stderr.contains("forcing trap when growing table"), 877 "bad stderr: {stderr}" 878 ); 879 Ok(()) 880 } 881 882 #[test] 883 fn table_growth_failure2() -> Result<()> { 884 let output = get_wasmtime_command()? 885 .args(&[ 886 "run", 887 "-Wtrap-on-grow-failure", 888 "tests/all/cli_tests/table-grow-failure2.wat", 889 ]) 890 .output()?; 891 assert!(!output.status.success()); 892 let stderr = String::from_utf8_lossy(&output.stderr); 893 assert!( 894 stderr.contains("forcing a table growth failure to be a trap"), 895 "bad stderr: {stderr}" 896 ); 897 Ok(()) 898 } 899 900 #[test] 901 fn option_group_help() -> Result<()> { 902 run_wasmtime(&["run", "-Whelp"])?; 903 run_wasmtime(&["run", "-O", "help"])?; 904 run_wasmtime(&["run", "--codegen", "help"])?; 905 run_wasmtime(&["run", "--debug=help"])?; 906 run_wasmtime(&["run", "-Shelp"])?; 907 run_wasmtime(&["run", "-Whelp-long"])?; 908 Ok(()) 909 } 910 911 #[test] 912 fn option_group_comma_separated() -> Result<()> { 913 run_wasmtime(&[ 914 "run", 915 "-Wrelaxed-simd,simd", 916 "tests/all/cli_tests/simple.wat", 917 ])?; 918 Ok(()) 919 } 920 921 #[test] 922 fn option_group_boolean_parsing() -> Result<()> { 923 run_wasmtime(&["run", "-Wrelaxed-simd", "tests/all/cli_tests/simple.wat"])?; 924 run_wasmtime(&["run", "-Wrelaxed-simd=n", "tests/all/cli_tests/simple.wat"])?; 925 run_wasmtime(&["run", "-Wrelaxed-simd=y", "tests/all/cli_tests/simple.wat"])?; 926 run_wasmtime(&["run", "-Wrelaxed-simd=no", "tests/all/cli_tests/simple.wat"])?; 927 run_wasmtime(&[ 928 "run", 929 "-Wrelaxed-simd=yes", 930 "tests/all/cli_tests/simple.wat", 931 ])?; 932 run_wasmtime(&[ 933 "run", 934 "-Wrelaxed-simd=true", 935 "tests/all/cli_tests/simple.wat", 936 ])?; 937 run_wasmtime(&[ 938 "run", 939 "-Wrelaxed-simd=false", 940 "tests/all/cli_tests/simple.wat", 941 ])?; 942 Ok(()) 943 } 944 945 #[test] 946 fn preview2_stdin() -> Result<()> { 947 let test = "tests/all/cli_tests/count-stdin.wat"; 948 let cmd = || -> Result<_> { 949 let mut cmd = get_wasmtime_command()?; 950 cmd.arg("--invoke=count").arg("-Spreview2").arg(test); 951 Ok(cmd) 952 }; 953 954 // read empty pipe is ok 955 let output = cmd()?.output()?; 956 assert!(output.status.success()); 957 assert_eq!(String::from_utf8_lossy(&output.stdout), "0\n"); 958 959 // read itself is ok 960 let file = File::open(test)?; 961 let size = file.metadata()?.len(); 962 let output = cmd()?.stdin(File::open(test)?).output()?; 963 assert!(output.status.success()); 964 assert_eq!(String::from_utf8_lossy(&output.stdout), format!("{size}\n")); 965 966 // read piped input ok is ok 967 let mut child = cmd()? 968 .stdin(Stdio::piped()) 969 .stdout(Stdio::piped()) 970 .stderr(Stdio::piped()) 971 .spawn()?; 972 let mut stdin = child.stdin.take().unwrap(); 973 std::thread::spawn(move || { 974 stdin.write_all(b"hello").unwrap(); 975 }); 976 let output = child.wait_with_output()?; 977 assert!(output.status.success()); 978 assert_eq!(String::from_utf8_lossy(&output.stdout), "5\n"); 979 980 let count_up_to = |n: usize| -> Result<_> { 981 let mut child = get_wasmtime_command()? 982 .arg("--invoke=count-up-to") 983 .arg("-Spreview2") 984 .arg(test) 985 .arg(n.to_string()) 986 .stdin(Stdio::piped()) 987 .stdout(Stdio::piped()) 988 .stderr(Stdio::piped()) 989 .spawn()?; 990 let mut stdin = child.stdin.take().unwrap(); 991 let t = std::thread::spawn(move || { 992 let mut written = 0; 993 let bytes = [0; 64 * 1024]; 994 loop { 995 written += match stdin.write(&bytes) { 996 Ok(n) => n, 997 Err(_) => break written, 998 }; 999 } 1000 }); 1001 let output = child.wait_with_output()?; 1002 assert!(output.status.success()); 1003 let written = t.join().unwrap(); 1004 let read = String::from_utf8_lossy(&output.stdout) 1005 .trim() 1006 .parse::<usize>() 1007 .unwrap(); 1008 // The test reads in 1000 byte chunks so make sure that it doesn't read 1009 // more than 1000 bytes than requested. 1010 assert!(read < n + 1000, "test read too much {read}"); 1011 Ok(written) 1012 }; 1013 1014 // wasmtime shouldn't eat information that the guest never actually tried to 1015 // read. 1016 // 1017 // NB: this may be a bit flaky. Exactly how much we wrote in the above 1018 // helper thread depends on how much the OS buffers for us. For now give 1019 // some some slop and assume that OSes are unlikely to buffer more than 1020 // that. 1021 let slop = 256 * 1024; 1022 for amt in [0, 100, 100_000] { 1023 let written = count_up_to(amt)?; 1024 assert!(written < slop + amt, "wrote too much {written}"); 1025 } 1026 Ok(()) 1027 } 1028 1029 #[test] 1030 fn old_cli_warn_if_ambiguous_flags() -> Result<()> { 1031 // This is accepted in the old CLI parser and the new but it's interpreted 1032 // differently so a warning should be printed. 1033 let output = get_wasmtime_command()? 1034 .args(&["tests/all/cli_tests/simple.wat", "--invoke", "get_f32"]) 1035 .output()?; 1036 assert_eq!(String::from_utf8_lossy(&output.stdout), "100\n"); 1037 assert_eq!( 1038 String::from_utf8_lossy(&output.stderr), 1039 "\ 1040 warning: this CLI invocation of Wasmtime will be parsed differently in future 1041 Wasmtime versions -- see this online issue for more information: 1042 https://github.com/bytecodealliance/wasmtime/issues/7384 1043 1044 Wasmtime will now execute with the old (<= Wasmtime 13) CLI parsing, 1045 however this behavior can also be temporarily configured with an 1046 environment variable: 1047 1048 - WASMTIME_NEW_CLI=0 to indicate old semantics are desired and silence this warning, or 1049 - WASMTIME_NEW_CLI=1 to indicate new semantics are desired and use the latest behavior 1050 warning: using `--invoke` with a function that returns values is experimental and may break in the future 1051 " 1052 ); 1053 1054 // Test disabling the warning 1055 let output = get_wasmtime_command()? 1056 .args(&["tests/all/cli_tests/simple.wat", "--invoke", "get_f32"]) 1057 .env("WASMTIME_NEW_CLI", "0") 1058 .output()?; 1059 assert_eq!(String::from_utf8_lossy(&output.stdout), "100\n"); 1060 assert_eq!( 1061 String::from_utf8_lossy(&output.stderr), 1062 "\ 1063 warning: using `--invoke` with a function that returns values is experimental and may break in the future 1064 " 1065 ); 1066 1067 // Test forcing the new behavior where nothing happens because the file is 1068 // invoked with `--invoke` as its own argument. 1069 let output = get_wasmtime_command()? 1070 .args(&["tests/all/cli_tests/simple.wat", "--invoke", "get_f32"]) 1071 .env("WASMTIME_NEW_CLI", "1") 1072 .output()?; 1073 assert_eq!(String::from_utf8_lossy(&output.stdout), ""); 1074 assert_eq!(String::from_utf8_lossy(&output.stderr), ""); 1075 1076 // This is unambiguous 1077 let output = get_wasmtime_command()? 1078 .args(&["--invoke", "get_f32", "tests/all/cli_tests/simple.wat"]) 1079 .output()?; 1080 assert_eq!(String::from_utf8_lossy(&output.stdout), "100\n"); 1081 assert_eq!( 1082 String::from_utf8_lossy(&output.stderr), 1083 "\ 1084 warning: using `--invoke` with a function that returns values is experimental and may break in the future 1085 " 1086 ); 1087 1088 // This fails to parse in the old but succeeds in the new, so it should run 1089 // under the new semantics with no warning. 1090 let output = get_wasmtime_command()? 1091 .args(&["run", "tests/all/cli_tests/print-arguments.wat", "--arg"]) 1092 .output()?; 1093 assert_eq!( 1094 String::from_utf8_lossy(&output.stdout), 1095 "print-arguments.wat\n--arg\n" 1096 ); 1097 assert_eq!(String::from_utf8_lossy(&output.stderr), ""); 1098 1099 // Old behavior can be forced however 1100 let output = get_wasmtime_command()? 1101 .args(&["run", "tests/all/cli_tests/print-arguments.wat", "--arg"]) 1102 .env("WASMTIME_NEW_CLI", "0") 1103 .output()?; 1104 assert!(!output.status.success()); 1105 1106 // This works in both the old and the new, so no warnings 1107 let output = get_wasmtime_command()? 1108 .args(&["run", "tests/all/cli_tests/print-arguments.wat", "arg"]) 1109 .output()?; 1110 assert_eq!( 1111 String::from_utf8_lossy(&output.stdout), 1112 "print-arguments.wat\narg\n" 1113 ); 1114 assert_eq!(String::from_utf8_lossy(&output.stderr), ""); 1115 1116 // This works in both the old and the new, so no warnings 1117 let output = get_wasmtime_command()? 1118 .args(&[ 1119 "run", 1120 "--", 1121 "tests/all/cli_tests/print-arguments.wat", 1122 "--arg", 1123 ]) 1124 .output()?; 1125 assert_eq!( 1126 String::from_utf8_lossy(&output.stdout), 1127 "print-arguments.wat\n--arg\n" 1128 ); 1129 assert_eq!(String::from_utf8_lossy(&output.stderr), ""); 1130 1131 // Old flags still work, but with a warning 1132 let output = get_wasmtime_command()? 1133 .args(&[ 1134 "run", 1135 "--max-wasm-stack", 1136 "1000000", 1137 "tests/all/cli_tests/print-arguments.wat", 1138 ]) 1139 .output()?; 1140 assert_eq!( 1141 String::from_utf8_lossy(&output.stdout), 1142 "print-arguments.wat\n" 1143 ); 1144 assert_eq!( 1145 String::from_utf8_lossy(&output.stderr), 1146 "\ 1147 warning: this CLI invocation of Wasmtime is going to break in the future -- for 1148 more information see this issue online: 1149 https://github.com/bytecodealliance/wasmtime/issues/7384 1150 1151 Wasmtime will now execute with the old (<= Wasmtime 13) CLI parsing, 1152 however this behavior can also be temporarily configured with an 1153 environment variable: 1154 1155 - WASMTIME_NEW_CLI=0 to indicate old semantics are desired and silence this warning, or 1156 - WASMTIME_NEW_CLI=1 to indicate new semantics are desired and see the error 1157 " 1158 ); 1159 1160 // Old flags warning is suppressible. 1161 let output = get_wasmtime_command()? 1162 .args(&[ 1163 "run", 1164 "--max-wasm-stack", 1165 "1000000", 1166 "tests/all/cli_tests/print-arguments.wat", 1167 ]) 1168 .env("WASMTIME_NEW_CLI", "0") 1169 .output()?; 1170 assert_eq!( 1171 String::from_utf8_lossy(&output.stdout), 1172 "print-arguments.wat\n" 1173 ); 1174 assert_eq!(String::from_utf8_lossy(&output.stderr), ""); 1175 1176 // the `--dir` flag prints no warning when used with `::` 1177 let dir = tempfile::tempdir()?; 1178 std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?; 1179 let output = get_wasmtime_command()? 1180 .args(&[ 1181 "run", 1182 &format!("--dir={}::/", dir.path().to_str().unwrap()), 1183 test_programs_artifacts::CLI_FILE_READ, 1184 ]) 1185 .output()?; 1186 assert_eq!(String::from_utf8_lossy(&output.stdout), ""); 1187 assert_eq!(String::from_utf8_lossy(&output.stderr), ""); 1188 1189 Ok(()) 1190 } 1191 1192 #[test] 1193 fn float_args() -> Result<()> { 1194 let result = run_wasmtime(&[ 1195 "--invoke", 1196 "echo_f32", 1197 "tests/all/cli_tests/simple.wat", 1198 "1.0", 1199 ])?; 1200 assert_eq!(result, "1\n"); 1201 let result = run_wasmtime(&[ 1202 "--invoke", 1203 "echo_f64", 1204 "tests/all/cli_tests/simple.wat", 1205 "1.1", 1206 ])?; 1207 assert_eq!(result, "1.1\n"); 1208 Ok(()) 1209 } 1210 1211 mod test_programs { 1212 use super::{get_wasmtime_command, run_wasmtime}; 1213 use anyhow::Result; 1214 use std::io::{Read, Write}; 1215 use std::process::Stdio; 1216 use test_programs_artifacts::*; 1217 1218 macro_rules! assert_test_exists { 1219 ($name:ident) => { 1220 #[allow(unused_imports)] 1221 use self::$name as _; 1222 }; 1223 } 1224 foreach_cli!(assert_test_exists); 1225 1226 #[test] 1227 fn cli_hello_stdout() -> Result<()> { 1228 run_wasmtime(&[ 1229 "run", 1230 "-Wcomponent-model", 1231 CLI_HELLO_STDOUT_COMPONENT, 1232 "gussie", 1233 "sparky", 1234 "willa", 1235 ])?; 1236 Ok(()) 1237 } 1238 1239 #[test] 1240 fn cli_args() -> Result<()> { 1241 run_wasmtime(&[ 1242 "run", 1243 "-Wcomponent-model", 1244 CLI_ARGS_COMPONENT, 1245 "hello", 1246 "this", 1247 "", 1248 "is an argument", 1249 "with emoji", 1250 ])?; 1251 Ok(()) 1252 } 1253 1254 #[test] 1255 fn cli_stdin() -> Result<()> { 1256 let mut child = get_wasmtime_command()? 1257 .args(&["run", "-Wcomponent-model", CLI_STDIN_COMPONENT]) 1258 .stdout(Stdio::piped()) 1259 .stderr(Stdio::piped()) 1260 .stdin(Stdio::piped()) 1261 .spawn()?; 1262 child 1263 .stdin 1264 .take() 1265 .unwrap() 1266 .write_all(b"So rested he by the Tumtum tree") 1267 .unwrap(); 1268 let output = child.wait_with_output()?; 1269 println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); 1270 println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); 1271 assert!(output.status.success()); 1272 Ok(()) 1273 } 1274 1275 #[test] 1276 fn cli_splice_stdin() -> Result<()> { 1277 let mut child = get_wasmtime_command()? 1278 .args(&["run", "-Wcomponent-model", CLI_SPLICE_STDIN_COMPONENT]) 1279 .stdout(Stdio::piped()) 1280 .stderr(Stdio::piped()) 1281 .stdin(Stdio::piped()) 1282 .spawn()?; 1283 let msg = "So rested he by the Tumtum tree"; 1284 child 1285 .stdin 1286 .take() 1287 .unwrap() 1288 .write_all(msg.as_bytes()) 1289 .unwrap(); 1290 let output = child.wait_with_output()?; 1291 assert!(output.status.success()); 1292 let stdout = String::from_utf8_lossy(&output.stdout); 1293 let stderr = String::from_utf8_lossy(&output.stderr); 1294 if !stderr.is_empty() { 1295 eprintln!("{stderr}"); 1296 } 1297 1298 assert_eq!( 1299 format!( 1300 "before splice\n{msg}\ncompleted splicing {} bytes\n", 1301 msg.as_bytes().len() 1302 ), 1303 stdout 1304 ); 1305 Ok(()) 1306 } 1307 1308 #[test] 1309 fn cli_env() -> Result<()> { 1310 run_wasmtime(&[ 1311 "run", 1312 "-Wcomponent-model", 1313 "--env=frabjous=day", 1314 "--env=callooh=callay", 1315 CLI_ENV_COMPONENT, 1316 ])?; 1317 Ok(()) 1318 } 1319 1320 #[test] 1321 fn cli_file_read() -> Result<()> { 1322 let dir = tempfile::tempdir()?; 1323 1324 std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?; 1325 1326 run_wasmtime(&[ 1327 "run", 1328 "-Wcomponent-model", 1329 &format!("--dir={}::/", dir.path().to_str().unwrap()), 1330 CLI_FILE_READ_COMPONENT, 1331 ])?; 1332 Ok(()) 1333 } 1334 1335 #[test] 1336 fn cli_file_append() -> Result<()> { 1337 let dir = tempfile::tempdir()?; 1338 1339 std::fs::File::create(dir.path().join("bar.txt"))? 1340 .write_all(b"'Twas brillig, and the slithy toves.\n")?; 1341 1342 run_wasmtime(&[ 1343 "run", 1344 "-Wcomponent-model", 1345 &format!("--dir={}::/", dir.path().to_str().unwrap()), 1346 CLI_FILE_APPEND_COMPONENT, 1347 ])?; 1348 1349 let contents = std::fs::read(dir.path().join("bar.txt"))?; 1350 assert_eq!( 1351 std::str::from_utf8(&contents).unwrap(), 1352 "'Twas brillig, and the slithy toves.\n\ 1353 Did gyre and gimble in the wabe;\n\ 1354 All mimsy were the borogoves,\n\ 1355 And the mome raths outgrabe.\n" 1356 ); 1357 Ok(()) 1358 } 1359 1360 #[test] 1361 fn cli_file_dir_sync() -> Result<()> { 1362 let dir = tempfile::tempdir()?; 1363 1364 std::fs::File::create(dir.path().join("bar.txt"))? 1365 .write_all(b"'Twas brillig, and the slithy toves.\n")?; 1366 1367 run_wasmtime(&[ 1368 "run", 1369 "-Wcomponent-model", 1370 &format!("--dir={}::/", dir.path().to_str().unwrap()), 1371 CLI_FILE_DIR_SYNC_COMPONENT, 1372 ])?; 1373 1374 Ok(()) 1375 } 1376 1377 #[test] 1378 fn cli_exit_success() -> Result<()> { 1379 run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_SUCCESS_COMPONENT])?; 1380 Ok(()) 1381 } 1382 1383 #[test] 1384 fn cli_exit_default() -> Result<()> { 1385 run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_DEFAULT_COMPONENT])?; 1386 Ok(()) 1387 } 1388 1389 #[test] 1390 fn cli_exit_failure() -> Result<()> { 1391 let output = get_wasmtime_command()? 1392 .args(&["run", "-Wcomponent-model", CLI_EXIT_FAILURE_COMPONENT]) 1393 .output()?; 1394 assert!(!output.status.success()); 1395 assert_eq!(output.status.code(), Some(1)); 1396 Ok(()) 1397 } 1398 1399 #[test] 1400 fn cli_exit_panic() -> Result<()> { 1401 let output = get_wasmtime_command()? 1402 .args(&["run", "-Wcomponent-model", CLI_EXIT_PANIC_COMPONENT]) 1403 .output()?; 1404 assert!(!output.status.success()); 1405 let stderr = String::from_utf8_lossy(&output.stderr); 1406 assert!(stderr.contains("Curiouser and curiouser!")); 1407 Ok(()) 1408 } 1409 1410 #[test] 1411 fn cli_directory_list() -> Result<()> { 1412 let dir = tempfile::tempdir()?; 1413 1414 std::fs::File::create(dir.path().join("foo.txt"))?; 1415 std::fs::File::create(dir.path().join("bar.txt"))?; 1416 std::fs::File::create(dir.path().join("baz.txt"))?; 1417 std::fs::create_dir(dir.path().join("sub"))?; 1418 std::fs::File::create(dir.path().join("sub").join("wow.txt"))?; 1419 std::fs::File::create(dir.path().join("sub").join("yay.txt"))?; 1420 1421 run_wasmtime(&[ 1422 "run", 1423 "-Wcomponent-model", 1424 &format!("--dir={}::/", dir.path().to_str().unwrap()), 1425 CLI_DIRECTORY_LIST_COMPONENT, 1426 ])?; 1427 Ok(()) 1428 } 1429 1430 #[test] 1431 fn cli_default_clocks() -> Result<()> { 1432 run_wasmtime(&["run", "-Wcomponent-model", CLI_DEFAULT_CLOCKS_COMPONENT])?; 1433 Ok(()) 1434 } 1435 1436 #[test] 1437 fn cli_export_cabi_realloc() -> Result<()> { 1438 run_wasmtime(&[ 1439 "run", 1440 "-Wcomponent-model", 1441 CLI_EXPORT_CABI_REALLOC_COMPONENT, 1442 ])?; 1443 Ok(()) 1444 } 1445 1446 #[test] 1447 fn run_wasi_http_component() -> Result<()> { 1448 let output = super::run_wasmtime_for_output( 1449 &[ 1450 "-Ccache=no", 1451 "-Wcomponent-model", 1452 "-Scommon,http,preview2", 1453 HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT, 1454 ], 1455 None, 1456 )?; 1457 println!("{}", String::from_utf8_lossy(&output.stderr)); 1458 let stdout = String::from_utf8_lossy(&output.stdout); 1459 println!("{}", stdout); 1460 assert!(stdout.starts_with("Called _start\n")); 1461 assert!(stdout.ends_with("Done\n")); 1462 assert!(output.status.success()); 1463 Ok(()) 1464 } 1465 1466 // Test to ensure that prints in the guest aren't buffered on the host by 1467 // accident. The test here will print something without a newline and then 1468 // wait for input on stdin, and the test here is to ensure that the 1469 // character shows up here even as the guest is waiting on input via stdin. 1470 #[test] 1471 fn cli_stdio_write_flushes() -> Result<()> { 1472 fn run(args: &[&str]) -> Result<()> { 1473 println!("running {args:?}"); 1474 let mut child = get_wasmtime_command()? 1475 .args(args) 1476 .stdin(Stdio::piped()) 1477 .stdout(Stdio::piped()) 1478 .spawn()?; 1479 let mut stdout = child.stdout.take().unwrap(); 1480 let mut buf = [0; 10]; 1481 match stdout.read(&mut buf) { 1482 Ok(2) => assert_eq!(&buf[..2], b"> "), 1483 e => panic!("unexpected read result {e:?}"), 1484 } 1485 drop(stdout); 1486 drop(child.stdin.take().unwrap()); 1487 let status = child.wait()?; 1488 assert!(status.success()); 1489 Ok(()) 1490 } 1491 1492 run(&["run", "-Spreview2=n", CLI_STDIO_WRITE_FLUSHES])?; 1493 run(&["run", "-Spreview2=y", CLI_STDIO_WRITE_FLUSHES])?; 1494 run(&[ 1495 "run", 1496 "-Wcomponent-model", 1497 CLI_STDIO_WRITE_FLUSHES_COMPONENT, 1498 ])?; 1499 Ok(()) 1500 } 1501 1502 #[test] 1503 fn cli_no_tcp() -> Result<()> { 1504 let output = super::run_wasmtime_for_output( 1505 &[ 1506 "-Wcomponent-model", 1507 // Turn on network but turn off TCP 1508 "-Sinherit-network,tcp=no", 1509 CLI_NO_TCP_COMPONENT, 1510 ], 1511 None, 1512 )?; 1513 println!("{}", String::from_utf8_lossy(&output.stderr)); 1514 assert!(output.status.success()); 1515 Ok(()) 1516 } 1517 1518 #[test] 1519 fn cli_no_udp() -> Result<()> { 1520 let output = super::run_wasmtime_for_output( 1521 &[ 1522 "-Wcomponent-model", 1523 // Turn on network but turn off UDP 1524 "-Sinherit-network,udp=no", 1525 CLI_NO_UDP_COMPONENT, 1526 ], 1527 None, 1528 )?; 1529 println!("{}", String::from_utf8_lossy(&output.stderr)); 1530 assert!(output.status.success()); 1531 Ok(()) 1532 } 1533 } 1534