xref: /wasmtime-44.0.1/tests/all/cli_tests.rs (revision cccc4e64)
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, PathBuf};
7 use std::process::{Command, ExitStatus, 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 bin = get_wasmtime_path()?;
25     let runner = std::env::vars()
26         .filter(|(k, _v)| k.starts_with("CARGO_TARGET") && k.ends_with("RUNNER"))
27         .next();
28 
29     // If we're running tests with a "runner" then we might be doing something
30     // like cross-emulation, so spin up the emulator rather than the tests
31     // itself, which may not be natively executable.
32     let mut cmd = if let Some((_, runner)) = runner {
33         let mut parts = runner.split_whitespace();
34         let mut cmd = Command::new(parts.next().unwrap());
35         for arg in parts {
36             cmd.arg(arg);
37         }
38         cmd.arg(&bin);
39         cmd
40     } else {
41         Command::new(&bin)
42     };
43 
44     // Ignore this if it's specified in the environment to allow tests to run in
45     // "default mode" by default.
46     cmd.env_remove("WASMTIME_NEW_CLI");
47 
48     Ok(cmd)
49 }
50 
51 fn get_wasmtime_path() -> Result<PathBuf> {
52     let mut path = std::env::current_exe()?;
53     path.pop(); // chop off the file name
54     path.pop(); // chop off `deps`
55     path.push("wasmtime");
56     Ok(path)
57 }
58 
59 // Run the wasmtime CLI with the provided args and, if it succeeds, return
60 // the standard output in a `String`.
61 pub fn run_wasmtime(args: &[&str]) -> Result<String> {
62     let output = run_wasmtime_for_output(args, None)?;
63     if !output.status.success() {
64         bail!(
65             "Failed to execute wasmtime with: {:?}\nstatus: {}\n{}",
66             args,
67             output.status,
68             String::from_utf8_lossy(&output.stderr)
69         );
70     }
71     Ok(String::from_utf8(output.stdout).unwrap())
72 }
73 
74 fn build_wasm(wat_path: impl AsRef<Path>) -> Result<NamedTempFile> {
75     let mut wasm_file = NamedTempFile::new()?;
76     let wasm = wat::parse_file(wat_path)?;
77     wasm_file.write(&wasm)?;
78     Ok(wasm_file)
79 }
80 
81 // Very basic use case: compile binary wasm file and run specific function with arguments.
82 #[test]
83 fn run_wasmtime_simple() -> Result<()> {
84     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
85     run_wasmtime(&[
86         "run",
87         "--invoke",
88         "simple",
89         "-Ccache=n",
90         wasm.path().to_str().unwrap(),
91         "4",
92     ])?;
93     Ok(())
94 }
95 
96 // Wasmtime shall fail when not enough arguments were provided.
97 #[test]
98 fn run_wasmtime_simple_fail_no_args() -> Result<()> {
99     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
100     assert!(
101         run_wasmtime(&[
102             "run",
103             "-Ccache=n",
104             "--invoke",
105             "simple",
106             wasm.path().to_str().unwrap(),
107         ])
108         .is_err(),
109         "shall fail"
110     );
111     Ok(())
112 }
113 
114 #[test]
115 fn run_coredump_smoketest() -> Result<()> {
116     let wasm = build_wasm("tests/all/cli_tests/coredump_smoketest.wat")?;
117     let coredump_file = NamedTempFile::new()?;
118     let coredump_arg = format!("-Dcoredump={}", coredump_file.path().display());
119     let err = run_wasmtime(&[
120         "run",
121         "--invoke",
122         "a",
123         "-Ccache=n",
124         &coredump_arg,
125         wasm.path().to_str().unwrap(),
126     ])
127     .unwrap_err();
128     assert!(err.to_string().contains(&format!(
129         "core dumped at {}",
130         coredump_file.path().display()
131     )));
132     Ok(())
133 }
134 
135 // Running simple wat
136 #[test]
137 fn run_wasmtime_simple_wat() -> Result<()> {
138     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
139     run_wasmtime(&[
140         "run",
141         "--invoke",
142         "simple",
143         "-Ccache=n",
144         wasm.path().to_str().unwrap(),
145         "4",
146     ])?;
147     assert_eq!(
148         run_wasmtime(&[
149             "run",
150             "--invoke",
151             "get_f32",
152             "-Ccache=n",
153             wasm.path().to_str().unwrap(),
154         ])?,
155         "100\n"
156     );
157     assert_eq!(
158         run_wasmtime(&[
159             "run",
160             "--invoke",
161             "get_f64",
162             "-Ccache=n",
163             wasm.path().to_str().unwrap(),
164         ])?,
165         "100\n"
166     );
167     Ok(())
168 }
169 
170 // Running a wat that traps.
171 #[test]
172 fn run_wasmtime_unreachable_wat() -> Result<()> {
173     let wasm = build_wasm("tests/all/cli_tests/unreachable.wat")?;
174     let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?;
175 
176     assert_ne!(output.stderr, b"");
177     assert_eq!(output.stdout, b"");
178 
179     assert_trap_code(&output.status);
180     Ok(())
181 }
182 
183 fn assert_trap_code(status: &ExitStatus) {
184     let code = status
185         .code()
186         .expect("wasmtime process should exit normally");
187 
188     // Test for the specific error code Wasmtime uses to indicate a trap return.
189     #[cfg(unix)]
190     assert_eq!(code, 128 + libc::SIGABRT);
191     #[cfg(windows)]
192     assert_eq!(code, 3);
193 }
194 
195 // Run a simple WASI hello world, snapshot0 edition.
196 #[test]
197 fn hello_wasi_snapshot0() -> Result<()> {
198     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?;
199     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
200         let stdout = run_wasmtime(&["-Ccache=n", preview2, wasm.path().to_str().unwrap()])?;
201         assert_eq!(stdout, "Hello, world!\n");
202     }
203     Ok(())
204 }
205 
206 // Run a simple WASI hello world, snapshot1 edition.
207 #[test]
208 fn hello_wasi_snapshot1() -> Result<()> {
209     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?;
210     let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
211     assert_eq!(stdout, "Hello, world!\n");
212     Ok(())
213 }
214 
215 #[test]
216 fn timeout_in_start() -> Result<()> {
217     let wasm = build_wasm("tests/all/cli_tests/iloop-start.wat")?;
218     let output = run_wasmtime_for_output(
219         &[
220             "run",
221             "-Wtimeout=1ms",
222             "-Ccache=n",
223             wasm.path().to_str().unwrap(),
224         ],
225         None,
226     )?;
227     assert!(!output.status.success());
228     assert_eq!(output.stdout, b"");
229     let stderr = String::from_utf8_lossy(&output.stderr);
230     assert!(
231         stderr.contains("wasm trap: interrupt"),
232         "bad stderr: {stderr}"
233     );
234     Ok(())
235 }
236 
237 #[test]
238 fn timeout_in_invoke() -> Result<()> {
239     let wasm = build_wasm("tests/all/cli_tests/iloop-invoke.wat")?;
240     let output = run_wasmtime_for_output(
241         &[
242             "run",
243             "-Wtimeout=1ms",
244             "-Ccache=n",
245             wasm.path().to_str().unwrap(),
246         ],
247         None,
248     )?;
249     assert!(!output.status.success());
250     assert_eq!(output.stdout, b"");
251     let stderr = String::from_utf8_lossy(&output.stderr);
252     assert!(
253         stderr.contains("wasm trap: interrupt"),
254         "bad stderr: {stderr}"
255     );
256     Ok(())
257 }
258 
259 // Exit with a valid non-zero exit code, snapshot0 edition.
260 #[test]
261 fn exit2_wasi_snapshot0() -> Result<()> {
262     let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot0.wat")?;
263 
264     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
265         let output = run_wasmtime_for_output(
266             &["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
267             None,
268         )?;
269         assert_eq!(output.status.code().unwrap(), 2);
270     }
271     Ok(())
272 }
273 
274 // Exit with a valid non-zero exit code, snapshot1 edition.
275 #[test]
276 fn exit2_wasi_snapshot1() -> Result<()> {
277     let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot1.wat")?;
278     let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
279     assert_eq!(output.status.code().unwrap(), 2);
280     Ok(())
281 }
282 
283 // Exit with a valid non-zero exit code, snapshot0 edition.
284 #[test]
285 fn exit125_wasi_snapshot0() -> Result<()> {
286     let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot0.wat")?;
287     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
288         let output = run_wasmtime_for_output(
289             &["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
290             None,
291         )?;
292         dbg!(&output);
293         assert_eq!(output.status.code().unwrap(), 125);
294     }
295     Ok(())
296 }
297 
298 // Exit with a valid non-zero exit code, snapshot1 edition.
299 #[test]
300 fn exit125_wasi_snapshot1() -> Result<()> {
301     let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot1.wat")?;
302     let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
303     assert_eq!(output.status.code().unwrap(), 125);
304     Ok(())
305 }
306 
307 // Exit with an invalid non-zero exit code, snapshot0 edition.
308 #[test]
309 fn exit126_wasi_snapshot0() -> Result<()> {
310     let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot0.wat")?;
311 
312     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
313         let output = run_wasmtime_for_output(
314             &["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
315             None,
316         )?;
317         assert_eq!(output.status.code().unwrap(), 1);
318         assert!(output.stdout.is_empty());
319         assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status"));
320     }
321     Ok(())
322 }
323 
324 // Exit with an invalid non-zero exit code, snapshot1 edition.
325 #[test]
326 fn exit126_wasi_snapshot1() -> Result<()> {
327     let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot1.wat")?;
328     let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?;
329     assert_eq!(output.status.code().unwrap(), 1);
330     assert!(output.stdout.is_empty());
331     assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status"));
332     Ok(())
333 }
334 
335 // Run a minimal command program.
336 #[test]
337 fn minimal_command() -> Result<()> {
338     let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?;
339     let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
340     assert_eq!(stdout, "");
341     Ok(())
342 }
343 
344 // Run a minimal reactor program.
345 #[test]
346 fn minimal_reactor() -> Result<()> {
347     let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?;
348     let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
349     assert_eq!(stdout, "");
350     Ok(())
351 }
352 
353 // Attempt to call invoke on a command.
354 #[test]
355 fn command_invoke() -> Result<()> {
356     let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?;
357     run_wasmtime(&[
358         "run",
359         "--invoke",
360         "_start",
361         "-Ccache=n",
362         wasm.path().to_str().unwrap(),
363     ])?;
364     Ok(())
365 }
366 
367 // Attempt to call invoke on a command.
368 #[test]
369 fn reactor_invoke() -> Result<()> {
370     let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?;
371     run_wasmtime(&[
372         "run",
373         "--invoke",
374         "_initialize",
375         "-Ccache=n",
376         wasm.path().to_str().unwrap(),
377     ])?;
378     Ok(())
379 }
380 
381 // Run the greeter test, which runs a preloaded reactor and a command.
382 #[test]
383 fn greeter() -> Result<()> {
384     let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?;
385     let stdout = run_wasmtime(&[
386         "run",
387         "-Ccache=n",
388         "--preload",
389         "reactor=tests/all/cli_tests/greeter_reactor.wat",
390         wasm.path().to_str().unwrap(),
391     ])?;
392     assert_eq!(
393         stdout,
394         "Hello _initialize\nHello _start\nHello greet\nHello done\n"
395     );
396     Ok(())
397 }
398 
399 // Run the greeter test, but this time preload a command.
400 #[test]
401 fn greeter_preload_command() -> Result<()> {
402     let wasm = build_wasm("tests/all/cli_tests/greeter_reactor.wat")?;
403     let stdout = run_wasmtime(&[
404         "run",
405         "-Ccache=n",
406         "--preload",
407         "reactor=tests/all/cli_tests/hello_wasi_snapshot1.wat",
408         wasm.path().to_str().unwrap(),
409     ])?;
410     assert_eq!(stdout, "Hello _initialize\n");
411     Ok(())
412 }
413 
414 // Run the greeter test, which runs a preloaded reactor and a command.
415 #[test]
416 fn greeter_preload_callable_command() -> Result<()> {
417     let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?;
418     let stdout = run_wasmtime(&[
419         "run",
420         "-Ccache=n",
421         "--preload",
422         "reactor=tests/all/cli_tests/greeter_callable_command.wat",
423         wasm.path().to_str().unwrap(),
424     ])?;
425     assert_eq!(stdout, "Hello _start\nHello callable greet\nHello done\n");
426     Ok(())
427 }
428 
429 // Ensure successful WASI exit call with FPR saving frames on stack for Windows x64
430 // See https://github.com/bytecodealliance/wasmtime/issues/1967
431 #[test]
432 fn exit_with_saved_fprs() -> Result<()> {
433     let wasm = build_wasm("tests/all/cli_tests/exit_with_saved_fprs.wat")?;
434     let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
435     assert_eq!(output.status.code().unwrap(), 0);
436     assert!(output.stdout.is_empty());
437     Ok(())
438 }
439 
440 #[test]
441 fn run_cwasm() -> Result<()> {
442     let td = TempDir::new()?;
443     let cwasm = td.path().join("foo.cwasm");
444     let stdout = run_wasmtime(&[
445         "compile",
446         "tests/all/cli_tests/simple.wat",
447         "-o",
448         cwasm.to_str().unwrap(),
449     ])?;
450     assert_eq!(stdout, "");
451     let stdout = run_wasmtime(&["run", "--allow-precompiled", cwasm.to_str().unwrap()])?;
452     assert_eq!(stdout, "");
453     Ok(())
454 }
455 
456 #[cfg(unix)]
457 #[test]
458 fn hello_wasi_snapshot0_from_stdin() -> Result<()> {
459     // Run a simple WASI hello world, snapshot0 edition.
460     // The module is piped from standard input.
461     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?;
462     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
463         let stdout = {
464             let path = wasm.path();
465             let args: &[&str] = &["-Ccache=n", preview2, "-"];
466             let output = run_wasmtime_for_output(args, Some(path))?;
467             if !output.status.success() {
468                 bail!(
469                     "Failed to execute wasmtime with: {:?}\n{}",
470                     args,
471                     String::from_utf8_lossy(&output.stderr)
472                 );
473             }
474             Ok::<_, anyhow::Error>(String::from_utf8(output.stdout).unwrap())
475         }?;
476         assert_eq!(stdout, "Hello, world!\n");
477     }
478     Ok(())
479 }
480 
481 #[test]
482 fn specify_env() -> Result<()> {
483     // By default no env is inherited
484     let output = get_wasmtime_command()?
485         .args(&["run", "tests/all/cli_tests/print_env.wat"])
486         .env("THIS_WILL_NOT", "show up in the output")
487         .output()?;
488     assert!(output.status.success());
489     assert_eq!(String::from_utf8_lossy(&output.stdout), "");
490 
491     // Specify a single env var
492     let output = get_wasmtime_command()?
493         .args(&[
494             "run",
495             "--env",
496             "FOO=bar",
497             "tests/all/cli_tests/print_env.wat",
498         ])
499         .output()?;
500     assert!(output.status.success());
501     assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n");
502 
503     // Inherit a single env var
504     let output = get_wasmtime_command()?
505         .args(&["run", "--env", "FOO", "tests/all/cli_tests/print_env.wat"])
506         .env("FOO", "bar")
507         .output()?;
508     assert!(output.status.success());
509     assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n");
510 
511     // Inherit a nonexistent env var
512     let output = get_wasmtime_command()?
513         .args(&[
514             "run",
515             "--env",
516             "SURELY_THIS_ENV_VAR_DOES_NOT_EXIST_ANYWHERE_RIGHT",
517             "tests/all/cli_tests/print_env.wat",
518         ])
519         .output()?;
520     assert!(output.status.success());
521 
522     // Inherit all env vars
523     let output = get_wasmtime_command()?
524         .args(&["run", "-Sinherit-env", "tests/all/cli_tests/print_env.wat"])
525         .env("FOO", "bar")
526         .output()?;
527     assert!(output.status.success());
528     let stdout = String::from_utf8_lossy(&output.stdout);
529     assert!(stdout.contains("FOO=bar"), "bad output: {stdout}");
530 
531     Ok(())
532 }
533 
534 #[cfg(unix)]
535 #[test]
536 fn run_cwasm_from_stdin() -> Result<()> {
537     use std::process::Stdio;
538 
539     let td = TempDir::new()?;
540     let cwasm = td.path().join("foo.cwasm");
541     let stdout = run_wasmtime(&[
542         "compile",
543         "tests/all/cli_tests/simple.wat",
544         "-o",
545         cwasm.to_str().unwrap(),
546     ])?;
547     assert_eq!(stdout, "");
548 
549     // If stdin is literally the file itself then that should work
550     let args: &[&str] = &["run", "--allow-precompiled", "-"];
551     let output = get_wasmtime_command()?
552         .args(args)
553         .stdin(File::open(&cwasm)?)
554         .output()?;
555     assert!(output.status.success(), "a file as stdin should work");
556 
557     // If stdin is a pipe, that should also work
558     let input = std::fs::read(&cwasm)?;
559     let mut child = get_wasmtime_command()?
560         .args(args)
561         .stdin(Stdio::piped())
562         .stdout(Stdio::piped())
563         .stderr(Stdio::piped())
564         .spawn()?;
565     let mut stdin = child.stdin.take().unwrap();
566     let t = std::thread::spawn(move || {
567         let _ = stdin.write_all(&input);
568     });
569     let output = child.wait_with_output()?;
570     assert!(output.status.success());
571     t.join().unwrap();
572     Ok(())
573 }
574 
575 #[cfg(feature = "wasi-threads")]
576 #[test]
577 fn run_threads() -> Result<()> {
578     // Skip this test on platforms that don't support threads.
579     if crate::threads::engine().is_none() {
580         return Ok(());
581     }
582     let wasm = build_wasm("tests/all/cli_tests/threads.wat")?;
583     let stdout = run_wasmtime(&[
584         "run",
585         "-Wthreads",
586         "-Sthreads",
587         "-Ccache=n",
588         wasm.path().to_str().unwrap(),
589     ])?;
590 
591     assert!(
592         stdout
593             == "Called _start\n\
594     Running wasi_thread_start\n\
595     Running wasi_thread_start\n\
596     Running wasi_thread_start\n\
597     Done\n"
598     );
599     Ok(())
600 }
601 
602 #[cfg(feature = "wasi-threads")]
603 #[test]
604 fn run_simple_with_wasi_threads() -> Result<()> {
605     // Skip this test on platforms that don't support threads.
606     if crate::threads::engine().is_none() {
607         return Ok(());
608     }
609     // We expect to be able to run Wasm modules that do not have correct
610     // wasi-thread entry points or imported shared memory as long as no threads
611     // are spawned.
612     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
613     let stdout = run_wasmtime(&[
614         "run",
615         "-Wthreads",
616         "-Sthreads",
617         "-Ccache=n",
618         "--invoke",
619         "simple",
620         wasm.path().to_str().unwrap(),
621         "4",
622     ])?;
623     assert_eq!(stdout, "4\n");
624     Ok(())
625 }
626 
627 #[test]
628 fn wasm_flags() -> Result<()> {
629     // Any argument after the wasm module should be interpreted as for the
630     // command itself
631     let stdout = run_wasmtime(&[
632         "run",
633         "--",
634         "tests/all/cli_tests/print-arguments.wat",
635         "--argument",
636         "-for",
637         "the",
638         "command",
639     ])?;
640     assert_eq!(
641         stdout,
642         "\
643             print-arguments.wat\n\
644             --argument\n\
645             -for\n\
646             the\n\
647             command\n\
648         "
649     );
650     let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "-"])?;
651     assert_eq!(
652         stdout,
653         "\
654             print-arguments.wat\n\
655             -\n\
656         "
657     );
658     let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "--"])?;
659     assert_eq!(
660         stdout,
661         "\
662             print-arguments.wat\n\
663             --\n\
664         "
665     );
666     let stdout = run_wasmtime(&[
667         "run",
668         "--",
669         "tests/all/cli_tests/print-arguments.wat",
670         "--",
671         "--",
672         "-a",
673         "b",
674     ])?;
675     assert_eq!(
676         stdout,
677         "\
678             print-arguments.wat\n\
679             --\n\
680             --\n\
681             -a\n\
682             b\n\
683         "
684     );
685     Ok(())
686 }
687 
688 #[test]
689 fn name_same_as_builtin_command() -> Result<()> {
690     // a bare subcommand shouldn't run successfully
691     let output = get_wasmtime_command()?
692         .current_dir("tests/all/cli_tests")
693         .arg("run")
694         .output()?;
695     assert!(!output.status.success());
696 
697     // a `--` prefix should let everything else get interpreted as a wasm
698     // module and arguments, even if the module has a name like `run`
699     let output = get_wasmtime_command()?
700         .current_dir("tests/all/cli_tests")
701         .arg("--")
702         .arg("run")
703         .output()?;
704     assert!(output.status.success(), "expected success got {output:#?}");
705 
706     // Passing options before the subcommand should work and doesn't require
707     // `--` to disambiguate
708     let output = get_wasmtime_command()?
709         .current_dir("tests/all/cli_tests")
710         .arg("-Ccache=n")
711         .arg("run")
712         .output()?;
713     assert!(output.status.success(), "expected success got {output:#?}");
714     Ok(())
715 }
716 
717 #[test]
718 #[cfg(unix)]
719 fn run_just_stdin_argument() -> Result<()> {
720     let output = get_wasmtime_command()?
721         .arg("-")
722         .stdin(File::open("tests/all/cli_tests/simple.wat")?)
723         .output()?;
724     assert!(output.status.success());
725     Ok(())
726 }
727 
728 #[test]
729 fn wasm_flags_without_subcommand() -> Result<()> {
730     let output = get_wasmtime_command()?
731         .current_dir("tests/all/cli_tests/")
732         .arg("print-arguments.wat")
733         .arg("-foo")
734         .arg("bar")
735         .output()?;
736     assert!(output.status.success());
737     assert_eq!(
738         String::from_utf8_lossy(&output.stdout),
739         "\
740             print-arguments.wat\n\
741             -foo\n\
742             bar\n\
743         "
744     );
745     Ok(())
746 }
747 
748 #[test]
749 fn wasi_misaligned_pointer() -> Result<()> {
750     let output = get_wasmtime_command()?
751         .arg("./tests/all/cli_tests/wasi_misaligned_pointer.wat")
752         .output()?;
753     assert!(!output.status.success());
754     let stderr = String::from_utf8_lossy(&output.stderr);
755     assert!(
756         stderr.contains("Pointer not aligned"),
757         "bad stderr: {stderr}",
758     );
759     Ok(())
760 }
761 
762 #[test]
763 #[cfg_attr(not(feature = "component-model"), ignore)]
764 fn hello_with_preview2() -> Result<()> {
765     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?;
766     let stdout = run_wasmtime(&["-Ccache=n", "-Spreview2", wasm.path().to_str().unwrap()])?;
767     assert_eq!(stdout, "Hello, world!\n");
768     Ok(())
769 }
770 
771 #[test]
772 #[cfg_attr(not(feature = "component-model"), ignore)]
773 fn component_missing_feature() -> Result<()> {
774     let path = "tests/all/cli_tests/empty-component.wat";
775     let wasm = build_wasm(path)?;
776     let output = get_wasmtime_command()?
777         .arg("-Ccache=n")
778         .arg("-Wcomponent-model=n")
779         .arg(wasm.path())
780         .output()?;
781     assert!(!output.status.success());
782     let stderr = String::from_utf8_lossy(&output.stderr);
783     assert!(
784         stderr.contains("cannot execute a component without `--wasm component-model`"),
785         "bad stderr: {stderr}"
786     );
787 
788     // also tests with raw *.wat input
789     let output = get_wasmtime_command()?
790         .arg("-Ccache=n")
791         .arg("-Wcomponent-model=n")
792         .arg(path)
793         .output()?;
794     assert!(!output.status.success());
795     let stderr = String::from_utf8_lossy(&output.stderr);
796     assert!(
797         stderr.contains("cannot execute a component without `--wasm component-model`"),
798         "bad stderr: {stderr}"
799     );
800 
801     Ok(())
802 }
803 
804 #[test]
805 #[cfg_attr(not(feature = "component-model"), ignore)]
806 fn component_enabled_by_default() -> Result<()> {
807     let path = "tests/all/cli_tests/component-basic.wat";
808     let wasm = build_wasm(path)?;
809     let output = get_wasmtime_command()?
810         .arg("-Ccache=n")
811         .arg(wasm.path())
812         .output()?;
813     assert!(output.status.success());
814 
815     // also tests with raw *.wat input
816     let output = get_wasmtime_command()?
817         .arg("-Ccache=n")
818         .arg(path)
819         .output()?;
820     assert!(output.status.success());
821 
822     Ok(())
823 }
824 
825 // If the text format is invalid then the filename should be mentioned in the
826 // error message.
827 #[test]
828 fn bad_text_syntax() -> Result<()> {
829     let output = get_wasmtime_command()?
830         .arg("-Ccache=n")
831         .arg("tests/all/cli_tests/bad-syntax.wat")
832         .output()?;
833     assert!(!output.status.success());
834     let stderr = String::from_utf8_lossy(&output.stderr);
835     assert!(
836         stderr.contains("--> tests/all/cli_tests/bad-syntax.wat"),
837         "bad stderr: {stderr}"
838     );
839     Ok(())
840 }
841 
842 #[test]
843 #[cfg_attr(not(feature = "component-model"), ignore)]
844 fn run_basic_component() -> Result<()> {
845     let path = "tests/all/cli_tests/component-basic.wat";
846     let wasm = build_wasm(path)?;
847 
848     // Run both the `*.wasm` binary and the text format
849     run_wasmtime(&[
850         "-Ccache=n",
851         "-Wcomponent-model",
852         wasm.path().to_str().unwrap(),
853     ])?;
854     run_wasmtime(&["-Ccache=n", "-Wcomponent-model", path])?;
855 
856     Ok(())
857 }
858 
859 #[test]
860 #[cfg_attr(not(feature = "component-model"), ignore)]
861 fn run_precompiled_component() -> Result<()> {
862     let td = TempDir::new()?;
863     let cwasm = td.path().join("component-basic.cwasm");
864     let stdout = run_wasmtime(&[
865         "compile",
866         "tests/all/cli_tests/component-basic.wat",
867         "-o",
868         cwasm.to_str().unwrap(),
869         "-Wcomponent-model",
870     ])?;
871     assert_eq!(stdout, "");
872     let stdout = run_wasmtime(&[
873         "run",
874         "-Wcomponent-model",
875         "--allow-precompiled",
876         cwasm.to_str().unwrap(),
877     ])?;
878     assert_eq!(stdout, "");
879 
880     Ok(())
881 }
882 
883 #[test]
884 fn memory_growth_failure() -> Result<()> {
885     let output = get_wasmtime_command()?
886         .args(&[
887             "run",
888             "-Wmemory64",
889             "-Wtrap-on-grow-failure",
890             "tests/all/cli_tests/memory-grow-failure.wat",
891         ])
892         .output()?;
893     assert!(!output.status.success());
894     let stderr = String::from_utf8_lossy(&output.stderr);
895     assert!(
896         stderr.contains("forcing a memory growth failure to be a trap"),
897         "bad stderr: {stderr}"
898     );
899     Ok(())
900 }
901 
902 #[test]
903 fn table_growth_failure() -> Result<()> {
904     let output = get_wasmtime_command()?
905         .args(&[
906             "run",
907             "-Wtrap-on-grow-failure",
908             "tests/all/cli_tests/table-grow-failure.wat",
909         ])
910         .output()?;
911     assert!(!output.status.success());
912     let stderr = String::from_utf8_lossy(&output.stderr);
913     assert!(
914         stderr.contains("forcing trap when growing table"),
915         "bad stderr: {stderr}"
916     );
917     Ok(())
918 }
919 
920 #[test]
921 fn table_growth_failure2() -> Result<()> {
922     let output = get_wasmtime_command()?
923         .args(&[
924             "run",
925             "-Wtrap-on-grow-failure",
926             "tests/all/cli_tests/table-grow-failure2.wat",
927         ])
928         .output()?;
929     assert!(!output.status.success());
930     let stderr = String::from_utf8_lossy(&output.stderr);
931     let expected = if cfg!(target_pointer_width = "32") {
932         "overflow calculating new table size"
933     } else {
934         "forcing trap when growing table to 4294967296 elements"
935     };
936     assert!(stderr.contains(expected), "bad stderr: {stderr}");
937     Ok(())
938 }
939 
940 #[test]
941 fn option_group_help() -> Result<()> {
942     run_wasmtime(&["run", "-Whelp"])?;
943     run_wasmtime(&["run", "-O", "help"])?;
944     run_wasmtime(&["run", "--codegen", "help"])?;
945     run_wasmtime(&["run", "--debug=help"])?;
946     run_wasmtime(&["run", "-Shelp"])?;
947     run_wasmtime(&["run", "-Whelp-long"])?;
948     Ok(())
949 }
950 
951 #[test]
952 fn option_group_comma_separated() -> Result<()> {
953     run_wasmtime(&[
954         "run",
955         "-Wrelaxed-simd,simd",
956         "tests/all/cli_tests/simple.wat",
957     ])?;
958     Ok(())
959 }
960 
961 #[test]
962 fn option_group_boolean_parsing() -> Result<()> {
963     run_wasmtime(&["run", "-Wrelaxed-simd", "tests/all/cli_tests/simple.wat"])?;
964     run_wasmtime(&["run", "-Wrelaxed-simd=n", "tests/all/cli_tests/simple.wat"])?;
965     run_wasmtime(&["run", "-Wrelaxed-simd=y", "tests/all/cli_tests/simple.wat"])?;
966     run_wasmtime(&["run", "-Wrelaxed-simd=no", "tests/all/cli_tests/simple.wat"])?;
967     run_wasmtime(&[
968         "run",
969         "-Wrelaxed-simd=yes",
970         "tests/all/cli_tests/simple.wat",
971     ])?;
972     run_wasmtime(&[
973         "run",
974         "-Wrelaxed-simd=true",
975         "tests/all/cli_tests/simple.wat",
976     ])?;
977     run_wasmtime(&[
978         "run",
979         "-Wrelaxed-simd=false",
980         "tests/all/cli_tests/simple.wat",
981     ])?;
982     Ok(())
983 }
984 
985 #[test]
986 fn preview2_stdin() -> Result<()> {
987     let test = "tests/all/cli_tests/count-stdin.wat";
988     let cmd = || -> Result<_> {
989         let mut cmd = get_wasmtime_command()?;
990         cmd.arg("--invoke=count").arg("-Spreview2").arg(test);
991         Ok(cmd)
992     };
993 
994     // read empty pipe is ok
995     let output = cmd()?.output()?;
996     assert!(output.status.success());
997     assert_eq!(String::from_utf8_lossy(&output.stdout), "0\n");
998 
999     // read itself is ok
1000     let file = File::open(test)?;
1001     let size = file.metadata()?.len();
1002     let output = cmd()?.stdin(File::open(test)?).output()?;
1003     assert!(output.status.success());
1004     assert_eq!(String::from_utf8_lossy(&output.stdout), format!("{size}\n"));
1005 
1006     // read piped input ok is ok
1007     let mut child = cmd()?
1008         .stdin(Stdio::piped())
1009         .stdout(Stdio::piped())
1010         .stderr(Stdio::piped())
1011         .spawn()?;
1012     let mut stdin = child.stdin.take().unwrap();
1013     std::thread::spawn(move || {
1014         stdin.write_all(b"hello").unwrap();
1015     });
1016     let output = child.wait_with_output()?;
1017     assert!(output.status.success());
1018     assert_eq!(String::from_utf8_lossy(&output.stdout), "5\n");
1019 
1020     let count_up_to = |n: usize| -> Result<_> {
1021         let mut child = get_wasmtime_command()?
1022             .arg("--invoke=count-up-to")
1023             .arg("-Spreview2")
1024             .arg(test)
1025             .arg(n.to_string())
1026             .stdin(Stdio::piped())
1027             .stdout(Stdio::piped())
1028             .stderr(Stdio::piped())
1029             .spawn()?;
1030         let mut stdin = child.stdin.take().unwrap();
1031         let t = std::thread::spawn(move || {
1032             let mut written = 0;
1033             let bytes = [0; 64 * 1024];
1034             loop {
1035                 written += match stdin.write(&bytes) {
1036                     Ok(n) => n,
1037                     Err(_) => break written,
1038                 };
1039             }
1040         });
1041         let output = child.wait_with_output()?;
1042         assert!(output.status.success());
1043         let written = t.join().unwrap();
1044         let read = String::from_utf8_lossy(&output.stdout)
1045             .trim()
1046             .parse::<usize>()
1047             .unwrap();
1048         // The test reads in 1000 byte chunks so make sure that it doesn't read
1049         // more than 1000 bytes than requested.
1050         assert!(read < n + 1000, "test read too much {read}");
1051         Ok(written)
1052     };
1053 
1054     // wasmtime shouldn't eat information that the guest never actually tried to
1055     // read.
1056     //
1057     // NB: this may be a bit flaky. Exactly how much we wrote in the above
1058     // helper thread depends on how much the OS buffers for us. For now give
1059     // some some slop and assume that OSes are unlikely to buffer more than
1060     // that.
1061     let slop = 256 * 1024;
1062     for amt in [0, 100, 100_000] {
1063         let written = count_up_to(amt)?;
1064         assert!(written < slop + amt, "wrote too much {written}");
1065     }
1066     Ok(())
1067 }
1068 
1069 #[test]
1070 fn float_args() -> Result<()> {
1071     let result = run_wasmtime(&[
1072         "--invoke",
1073         "echo_f32",
1074         "tests/all/cli_tests/simple.wat",
1075         "1.0",
1076     ])?;
1077     assert_eq!(result, "1\n");
1078     let result = run_wasmtime(&[
1079         "--invoke",
1080         "echo_f64",
1081         "tests/all/cli_tests/simple.wat",
1082         "1.1",
1083     ])?;
1084     assert_eq!(result, "1.1\n");
1085     Ok(())
1086 }
1087 
1088 #[test]
1089 fn mpk_without_pooling() -> Result<()> {
1090     let output = get_wasmtime_command()?
1091         .args(&[
1092             "run",
1093             "-O",
1094             "memory-protection-keys=y",
1095             "--invoke",
1096             "echo_f32",
1097             "tests/all/cli_tests/simple.wat",
1098             "1.0",
1099         ])
1100         .env("WASMTIME_NEW_CLI", "1")
1101         .output()?;
1102     assert!(!output.status.success());
1103     Ok(())
1104 }
1105 
1106 // Very basic use case: compile binary wasm file and run specific function with arguments.
1107 #[test]
1108 fn increase_stack_size() -> Result<()> {
1109     run_wasmtime(&[
1110         "run",
1111         "--invoke",
1112         "simple",
1113         &format!("-Wmax-wasm-stack={}", 5 << 20),
1114         "-Ccache=n",
1115         "tests/all/cli_tests/simple.wat",
1116         "4",
1117     ])?;
1118     Ok(())
1119 }
1120 
1121 mod test_programs {
1122     use super::{get_wasmtime_command, run_wasmtime};
1123     use anyhow::{bail, Context, Result};
1124     use http_body_util::BodyExt;
1125     use hyper::header::HeaderValue;
1126     use std::io::{BufRead, BufReader, Read, Write};
1127     use std::net::SocketAddr;
1128     use std::process::{Child, Command, Stdio};
1129     use test_programs_artifacts::*;
1130     use tokio::net::TcpStream;
1131 
1132     macro_rules! assert_test_exists {
1133         ($name:ident) => {
1134             #[allow(unused_imports)]
1135             use self::$name as _;
1136         };
1137     }
1138     foreach_cli!(assert_test_exists);
1139 
1140     #[test]
1141     fn cli_hello_stdout() -> Result<()> {
1142         run_wasmtime(&[
1143             "run",
1144             "-Wcomponent-model",
1145             CLI_HELLO_STDOUT_COMPONENT,
1146             "gussie",
1147             "sparky",
1148             "willa",
1149         ])?;
1150         Ok(())
1151     }
1152 
1153     #[test]
1154     fn cli_args() -> Result<()> {
1155         run_wasmtime(&[
1156             "run",
1157             "-Wcomponent-model",
1158             CLI_ARGS_COMPONENT,
1159             "hello",
1160             "this",
1161             "",
1162             "is an argument",
1163             "with �� emoji",
1164         ])?;
1165         Ok(())
1166     }
1167 
1168     #[test]
1169     fn cli_stdin_empty() -> Result<()> {
1170         let mut child = get_wasmtime_command()?
1171             .args(&["run", "-Wcomponent-model", CLI_STDIN_EMPTY_COMPONENT])
1172             .stdout(Stdio::piped())
1173             .stderr(Stdio::piped())
1174             .stdin(Stdio::piped())
1175             .spawn()?;
1176         child
1177             .stdin
1178             .take()
1179             .unwrap()
1180             .write_all(b"not to be read")
1181             .unwrap();
1182         let output = child.wait_with_output()?;
1183         println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
1184         println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
1185         assert!(output.status.success());
1186         Ok(())
1187     }
1188 
1189     #[test]
1190     fn cli_stdin() -> Result<()> {
1191         let mut child = get_wasmtime_command()?
1192             .args(&["run", "-Wcomponent-model", CLI_STDIN_COMPONENT])
1193             .stdout(Stdio::piped())
1194             .stderr(Stdio::piped())
1195             .stdin(Stdio::piped())
1196             .spawn()?;
1197         child
1198             .stdin
1199             .take()
1200             .unwrap()
1201             .write_all(b"So rested he by the Tumtum tree")
1202             .unwrap();
1203         let output = child.wait_with_output()?;
1204         println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
1205         println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
1206         assert!(output.status.success());
1207         Ok(())
1208     }
1209 
1210     #[test]
1211     fn cli_splice_stdin() -> Result<()> {
1212         let mut child = get_wasmtime_command()?
1213             .args(&["run", "-Wcomponent-model", CLI_SPLICE_STDIN_COMPONENT])
1214             .stdout(Stdio::piped())
1215             .stderr(Stdio::piped())
1216             .stdin(Stdio::piped())
1217             .spawn()?;
1218         let msg = "So rested he by the Tumtum tree";
1219         child
1220             .stdin
1221             .take()
1222             .unwrap()
1223             .write_all(msg.as_bytes())
1224             .unwrap();
1225         let output = child.wait_with_output()?;
1226         assert!(output.status.success());
1227         let stdout = String::from_utf8_lossy(&output.stdout);
1228         let stderr = String::from_utf8_lossy(&output.stderr);
1229         if !stderr.is_empty() {
1230             eprintln!("{stderr}");
1231         }
1232 
1233         assert_eq!(
1234             format!(
1235                 "before splice\n{msg}\ncompleted splicing {} bytes\n",
1236                 msg.as_bytes().len()
1237             ),
1238             stdout
1239         );
1240         Ok(())
1241     }
1242 
1243     #[test]
1244     fn cli_env() -> Result<()> {
1245         run_wasmtime(&[
1246             "run",
1247             "-Wcomponent-model",
1248             "--env=frabjous=day",
1249             "--env=callooh=callay",
1250             CLI_ENV_COMPONENT,
1251         ])?;
1252         Ok(())
1253     }
1254 
1255     #[test]
1256     fn cli_file_read() -> Result<()> {
1257         let dir = tempfile::tempdir()?;
1258 
1259         std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?;
1260 
1261         run_wasmtime(&[
1262             "run",
1263             "-Wcomponent-model",
1264             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1265             CLI_FILE_READ_COMPONENT,
1266         ])?;
1267         Ok(())
1268     }
1269 
1270     #[test]
1271     fn cli_file_append() -> Result<()> {
1272         let dir = tempfile::tempdir()?;
1273 
1274         std::fs::File::create(dir.path().join("bar.txt"))?
1275             .write_all(b"'Twas brillig, and the slithy toves.\n")?;
1276 
1277         run_wasmtime(&[
1278             "run",
1279             "-Wcomponent-model",
1280             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1281             CLI_FILE_APPEND_COMPONENT,
1282         ])?;
1283 
1284         let contents = std::fs::read(dir.path().join("bar.txt"))?;
1285         assert_eq!(
1286             std::str::from_utf8(&contents).unwrap(),
1287             "'Twas brillig, and the slithy toves.\n\
1288                    Did gyre and gimble in the wabe;\n\
1289                    All mimsy were the borogoves,\n\
1290                    And the mome raths outgrabe.\n"
1291         );
1292         Ok(())
1293     }
1294 
1295     #[test]
1296     fn cli_file_dir_sync() -> Result<()> {
1297         let dir = tempfile::tempdir()?;
1298 
1299         std::fs::File::create(dir.path().join("bar.txt"))?
1300             .write_all(b"'Twas brillig, and the slithy toves.\n")?;
1301 
1302         run_wasmtime(&[
1303             "run",
1304             "-Wcomponent-model",
1305             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1306             CLI_FILE_DIR_SYNC_COMPONENT,
1307         ])?;
1308 
1309         Ok(())
1310     }
1311 
1312     #[test]
1313     fn cli_exit_success() -> Result<()> {
1314         run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_SUCCESS_COMPONENT])?;
1315         Ok(())
1316     }
1317 
1318     #[test]
1319     fn cli_exit_default() -> Result<()> {
1320         run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_DEFAULT_COMPONENT])?;
1321         Ok(())
1322     }
1323 
1324     #[test]
1325     fn cli_exit_failure() -> Result<()> {
1326         let output = get_wasmtime_command()?
1327             .args(&["run", "-Wcomponent-model", CLI_EXIT_FAILURE_COMPONENT])
1328             .output()?;
1329         assert!(!output.status.success());
1330         assert_eq!(output.status.code(), Some(1));
1331         Ok(())
1332     }
1333 
1334     #[test]
1335     fn cli_exit_with_code() -> Result<()> {
1336         let output = get_wasmtime_command()?
1337             .args(&[
1338                 "run",
1339                 "-Wcomponent-model",
1340                 "-Scli-exit-with-code",
1341                 CLI_EXIT_WITH_CODE_COMPONENT,
1342             ])
1343             .output()?;
1344         assert!(!output.status.success());
1345         assert_eq!(output.status.code(), Some(42));
1346         Ok(())
1347     }
1348 
1349     #[test]
1350     fn cli_exit_panic() -> Result<()> {
1351         let output = get_wasmtime_command()?
1352             .args(&["run", "-Wcomponent-model", CLI_EXIT_PANIC_COMPONENT])
1353             .output()?;
1354         assert!(!output.status.success());
1355         let stderr = String::from_utf8_lossy(&output.stderr);
1356         assert!(stderr.contains("Curiouser and curiouser!"));
1357         Ok(())
1358     }
1359 
1360     #[test]
1361     fn cli_directory_list() -> Result<()> {
1362         let dir = tempfile::tempdir()?;
1363 
1364         std::fs::File::create(dir.path().join("foo.txt"))?;
1365         std::fs::File::create(dir.path().join("bar.txt"))?;
1366         std::fs::File::create(dir.path().join("baz.txt"))?;
1367         std::fs::create_dir(dir.path().join("sub"))?;
1368         std::fs::File::create(dir.path().join("sub").join("wow.txt"))?;
1369         std::fs::File::create(dir.path().join("sub").join("yay.txt"))?;
1370 
1371         run_wasmtime(&[
1372             "run",
1373             "-Wcomponent-model",
1374             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1375             CLI_DIRECTORY_LIST_COMPONENT,
1376         ])?;
1377         Ok(())
1378     }
1379 
1380     #[test]
1381     fn cli_default_clocks() -> Result<()> {
1382         run_wasmtime(&["run", "-Wcomponent-model", CLI_DEFAULT_CLOCKS_COMPONENT])?;
1383         Ok(())
1384     }
1385 
1386     #[test]
1387     fn cli_export_cabi_realloc() -> Result<()> {
1388         run_wasmtime(&[
1389             "run",
1390             "-Wcomponent-model",
1391             CLI_EXPORT_CABI_REALLOC_COMPONENT,
1392         ])?;
1393         Ok(())
1394     }
1395 
1396     #[test]
1397     fn run_wasi_http_component() -> Result<()> {
1398         let output = super::run_wasmtime_for_output(
1399             &[
1400                 "-Ccache=no",
1401                 "-Wcomponent-model",
1402                 "-Scli,http,preview2",
1403                 HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT,
1404             ],
1405             None,
1406         )?;
1407         println!("{}", String::from_utf8_lossy(&output.stderr));
1408         let stdout = String::from_utf8_lossy(&output.stdout);
1409         println!("{stdout}");
1410         assert!(stdout.starts_with("Called _start\n"));
1411         assert!(stdout.ends_with("Done\n"));
1412         assert!(output.status.success());
1413         Ok(())
1414     }
1415 
1416     // Test to ensure that prints in the guest aren't buffered on the host by
1417     // accident. The test here will print something without a newline and then
1418     // wait for input on stdin, and the test here is to ensure that the
1419     // character shows up here even as the guest is waiting on input via stdin.
1420     #[test]
1421     fn cli_stdio_write_flushes() -> Result<()> {
1422         fn run(args: &[&str]) -> Result<()> {
1423             println!("running {args:?}");
1424             let mut child = get_wasmtime_command()?
1425                 .args(args)
1426                 .stdin(Stdio::piped())
1427                 .stdout(Stdio::piped())
1428                 .spawn()?;
1429             let mut stdout = child.stdout.take().unwrap();
1430             let mut buf = [0; 10];
1431             match stdout.read(&mut buf) {
1432                 Ok(2) => assert_eq!(&buf[..2], b"> "),
1433                 e => panic!("unexpected read result {e:?}"),
1434             }
1435             drop(stdout);
1436             drop(child.stdin.take().unwrap());
1437             let status = child.wait()?;
1438             assert!(status.success());
1439             Ok(())
1440         }
1441 
1442         run(&["run", "-Spreview2=n", CLI_STDIO_WRITE_FLUSHES])?;
1443         run(&["run", "-Spreview2=y", CLI_STDIO_WRITE_FLUSHES])?;
1444         run(&[
1445             "run",
1446             "-Wcomponent-model",
1447             CLI_STDIO_WRITE_FLUSHES_COMPONENT,
1448         ])?;
1449         Ok(())
1450     }
1451 
1452     #[test]
1453     fn cli_no_tcp() -> Result<()> {
1454         let output = super::run_wasmtime_for_output(
1455             &[
1456                 "-Wcomponent-model",
1457                 // Turn on network but turn off TCP
1458                 "-Sinherit-network,tcp=no",
1459                 CLI_NO_TCP_COMPONENT,
1460             ],
1461             None,
1462         )?;
1463         println!("{}", String::from_utf8_lossy(&output.stderr));
1464         assert!(output.status.success());
1465         Ok(())
1466     }
1467 
1468     #[test]
1469     fn cli_no_udp() -> Result<()> {
1470         let output = super::run_wasmtime_for_output(
1471             &[
1472                 "-Wcomponent-model",
1473                 // Turn on network but turn off UDP
1474                 "-Sinherit-network,udp=no",
1475                 CLI_NO_UDP_COMPONENT,
1476             ],
1477             None,
1478         )?;
1479         println!("{}", String::from_utf8_lossy(&output.stderr));
1480         assert!(output.status.success());
1481         Ok(())
1482     }
1483 
1484     #[test]
1485     fn cli_no_ip_name_lookup() -> Result<()> {
1486         let output = super::run_wasmtime_for_output(
1487             &[
1488                 "-Wcomponent-model",
1489                 // Turn on network but ensure name lookup is disabled
1490                 "-Sinherit-network,allow-ip-name-lookup=no",
1491                 CLI_NO_IP_NAME_LOOKUP_COMPONENT,
1492             ],
1493             None,
1494         )?;
1495         println!("{}", String::from_utf8_lossy(&output.stderr));
1496         assert!(output.status.success());
1497         Ok(())
1498     }
1499 
1500     #[test]
1501     fn cli_sleep() -> Result<()> {
1502         run_wasmtime(&["run", CLI_SLEEP])?;
1503         run_wasmtime(&["run", CLI_SLEEP_COMPONENT])?;
1504         Ok(())
1505     }
1506 
1507     #[test]
1508     fn cli_sleep_forever() -> Result<()> {
1509         for timeout in [
1510             // Tests still pass when we race with going to sleep.
1511             "-Wtimeout=1ns",
1512             // Tests pass when we wait till the Wasm has (likely) gone to sleep.
1513             "-Wtimeout=250ms",
1514         ] {
1515             let e = run_wasmtime(&["run", timeout, CLI_SLEEP_FOREVER]).unwrap_err();
1516             let e = e.to_string();
1517             println!("Got error: {e}");
1518             assert!(e.contains("interrupt"));
1519 
1520             let e = run_wasmtime(&["run", timeout, CLI_SLEEP_FOREVER_COMPONENT]).unwrap_err();
1521             let e = e.to_string();
1522             println!("Got error: {e}");
1523             assert!(e.contains("interrupt"));
1524         }
1525 
1526         Ok(())
1527     }
1528 
1529     /// Helper structure to manage an invocation of `wasmtime serve`
1530     struct WasmtimeServe {
1531         child: Option<Child>,
1532         addr: SocketAddr,
1533     }
1534 
1535     impl WasmtimeServe {
1536         /// Creates a new server which will serve the wasm component pointed to
1537         /// by `wasm`.
1538         ///
1539         /// A `configure` callback is provided to specify how `wasmtime serve`
1540         /// will be invoked and configure arguments such as headers.
1541         fn new(wasm: &str, configure: impl FnOnce(&mut Command)) -> Result<WasmtimeServe> {
1542             // Spawn `wasmtime serve` on port 0 which will randomly assign it a
1543             // port.
1544             let mut cmd = super::get_wasmtime_command()?;
1545             cmd.arg("serve").arg("--addr=127.0.0.1:0").arg(wasm);
1546             configure(&mut cmd);
1547             Self::spawn(&mut cmd)
1548         }
1549 
1550         fn spawn(cmd: &mut Command) -> Result<WasmtimeServe> {
1551             cmd.stdin(Stdio::null());
1552             cmd.stdout(Stdio::piped());
1553             cmd.stderr(Stdio::piped());
1554             let mut child = cmd.spawn()?;
1555 
1556             // Read the first line of stderr which will say which address it's
1557             // listening on.
1558             //
1559             // NB: this intentionally discards any extra buffered data in the
1560             // `BufReader` once the newline is found. The server shouldn't print
1561             // anything interesting other than the address so once we get a line
1562             // all remaining output is left to be captured by future requests
1563             // send to the server.
1564             let mut line = String::new();
1565             let mut reader = BufReader::new(child.stderr.take().unwrap());
1566             reader.read_line(&mut line)?;
1567 
1568             match line.find("127.0.0.1").and_then(|addr_start| {
1569                 let addr = &line[addr_start..];
1570                 let addr_end = addr.find("/")?;
1571                 addr[..addr_end].parse().ok()
1572             }) {
1573                 Some(addr) => {
1574                     assert!(reader.buffer().is_empty());
1575                     child.stderr = Some(reader.into_inner());
1576                     Ok(WasmtimeServe {
1577                         child: Some(child),
1578                         addr,
1579                     })
1580                 }
1581                 None => {
1582                     child.kill()?;
1583                     child.wait()?;
1584                     reader.read_to_string(&mut line)?;
1585                     bail!("failed to start child: {line}")
1586                 }
1587             }
1588         }
1589 
1590         /// Completes this server gracefully by printing the output on failure.
1591         fn finish(mut self) -> Result<(String, String)> {
1592             let mut child = self.child.take().unwrap();
1593 
1594             // If the child process has already exited then collect the output
1595             // and test if it succeeded. Otherwise it's still running so kill it
1596             // and then reap it. Assume that if it's still running then the test
1597             // has otherwise passed so no need to print the output.
1598             let known_failure = if child.try_wait()?.is_some() {
1599                 false
1600             } else {
1601                 child.kill()?;
1602                 true
1603             };
1604             let output = child.wait_with_output()?;
1605             if !known_failure && !output.status.success() {
1606                 bail!("child failed {output:?}");
1607             }
1608 
1609             Ok((
1610                 String::from_utf8_lossy(&output.stdout).into_owned(),
1611                 String::from_utf8_lossy(&output.stderr).into_owned(),
1612             ))
1613         }
1614 
1615         /// Send a request to this server and wait for the response.
1616         async fn send_request(&self, req: http::Request<String>) -> Result<http::Response<String>> {
1617             let (mut send, conn_task) = self.start_requests().await?;
1618 
1619             let response = send
1620                 .send_request(req)
1621                 .await
1622                 .context("error sending request")?;
1623             drop(send);
1624             let (parts, body) = response.into_parts();
1625 
1626             let body = body.collect().await.context("failed to read body")?;
1627             assert!(body.trailers().is_none());
1628             let body = std::str::from_utf8(&body.to_bytes())?.to_string();
1629 
1630             conn_task.await??;
1631 
1632             Ok(http::Response::from_parts(parts, body))
1633         }
1634 
1635         async fn start_requests(
1636             &self,
1637         ) -> Result<(
1638             hyper::client::conn::http1::SendRequest<String>,
1639             tokio::task::JoinHandle<hyper::Result<()>>,
1640         )> {
1641             let tcp = TcpStream::connect(&self.addr)
1642                 .await
1643                 .context("failed to connect")?;
1644             let tcp = wasmtime_wasi_http::io::TokioIo::new(tcp);
1645             let (send, conn) = hyper::client::conn::http1::handshake(tcp)
1646                 .await
1647                 .context("failed http handshake")?;
1648             Ok((send, tokio::task::spawn(conn)))
1649         }
1650     }
1651 
1652     // Don't leave child processes running by accident so kill the child process
1653     // if our server goes away.
1654     impl Drop for WasmtimeServe {
1655         fn drop(&mut self) {
1656             let mut child = match self.child.take() {
1657                 Some(child) => child,
1658                 None => return,
1659             };
1660             if child.kill().is_err() {
1661                 return;
1662             }
1663             let output = match child.wait_with_output() {
1664                 Ok(output) => output,
1665                 Err(_) => return,
1666             };
1667 
1668             println!("server status: {}", output.status);
1669             if !output.stdout.is_empty() {
1670                 println!(
1671                     "server stdout:\n{}",
1672                     String::from_utf8_lossy(&output.stdout)
1673                 );
1674             }
1675             if !output.stderr.is_empty() {
1676                 println!(
1677                     "server stderr:\n{}",
1678                     String::from_utf8_lossy(&output.stderr)
1679                 );
1680             }
1681         }
1682     }
1683 
1684     #[tokio::test]
1685     async fn cli_serve_echo_env() -> Result<()> {
1686         let server = WasmtimeServe::new(CLI_SERVE_ECHO_ENV_COMPONENT, |cmd| {
1687             cmd.arg("--env=FOO=bar");
1688             cmd.arg("--env=BAR");
1689             cmd.arg("-Scli");
1690             cmd.env_remove("BAR");
1691         })?;
1692 
1693         let foo_env = server
1694             .send_request(
1695                 hyper::Request::builder()
1696                     .uri("http://localhost/")
1697                     .header("env", "FOO")
1698                     .body(String::new())
1699                     .context("failed to make request")?,
1700             )
1701             .await?;
1702 
1703         assert!(foo_env.status().is_success());
1704         assert!(foo_env.body().is_empty());
1705         let headers = foo_env.headers();
1706         assert_eq!(headers.get("env"), Some(&HeaderValue::from_static("bar")));
1707 
1708         let bar_env = server
1709             .send_request(
1710                 hyper::Request::builder()
1711                     .uri("http://localhost/")
1712                     .header("env", "BAR")
1713                     .body(String::new())
1714                     .context("failed to make request")?,
1715             )
1716             .await?;
1717 
1718         assert!(bar_env.status().is_success());
1719         assert!(bar_env.body().is_empty());
1720         let headers = bar_env.headers();
1721         assert_eq!(headers.get("env"), None);
1722 
1723         server.finish()?;
1724         Ok(())
1725     }
1726 
1727     #[tokio::test]
1728     async fn cli_serve_outgoing_body_config() -> Result<()> {
1729         let server = WasmtimeServe::new(CLI_SERVE_ECHO_ENV_COMPONENT, |cmd| {
1730             cmd.arg("-Scli");
1731             cmd.arg("-Shttp-outgoing-body-buffer-chunks=2");
1732             cmd.arg("-Shttp-outgoing-body-chunk-size=1024");
1733         })?;
1734 
1735         let resp = server
1736             .send_request(
1737                 hyper::Request::builder()
1738                     .uri("http://localhost/")
1739                     .header("env", "FOO")
1740                     .body(String::new())
1741                     .context("failed to make request")?,
1742             )
1743             .await?;
1744 
1745         assert!(resp.status().is_success());
1746 
1747         server.finish()?;
1748         Ok(())
1749     }
1750 
1751     #[tokio::test]
1752     #[ignore] // TODO: printing stderr in the child and killing the child at the
1753               // end of this test race so the stderr may be present or not. Need
1754               // to implement a more graceful shutdown routine for `wasmtime
1755               // serve`.
1756     async fn cli_serve_respect_pooling_options() -> Result<()> {
1757         let server = WasmtimeServe::new(CLI_SERVE_ECHO_ENV_COMPONENT, |cmd| {
1758             cmd.arg("-Opooling-total-memories=0").arg("-Scli");
1759         })?;
1760 
1761         let result = server
1762             .send_request(
1763                 hyper::Request::builder()
1764                     .uri("http://localhost/")
1765                     .header("env", "FOO")
1766                     .body(String::new())
1767                     .context("failed to make request")?,
1768             )
1769             .await;
1770         assert!(result.is_err());
1771         let (_, stderr) = server.finish()?;
1772         assert!(
1773             stderr.contains("maximum concurrent memory limit of 0 reached"),
1774             "bad stderr: {stderr}",
1775         );
1776         Ok(())
1777     }
1778 
1779     #[test]
1780     fn cli_large_env() -> Result<()> {
1781         for wasm in [CLI_LARGE_ENV, CLI_LARGE_ENV_COMPONENT] {
1782             println!("run {wasm:?}");
1783             let mut cmd = get_wasmtime_command()?;
1784             cmd.arg("run").arg("-Sinherit-env").arg(wasm);
1785 
1786             let debug_cmd = format!("{cmd:?}");
1787             for i in 0..512 {
1788                 let var = format!("KEY{i}");
1789                 let val = (0..1024).map(|_| 'x').collect::<String>();
1790                 cmd.env(&var, &val);
1791             }
1792             let output = cmd.output()?;
1793             if !output.status.success() {
1794                 bail!(
1795                     "Failed to execute wasmtime with: {debug_cmd}\n{}",
1796                     String::from_utf8_lossy(&output.stderr)
1797                 );
1798             }
1799         }
1800         Ok(())
1801     }
1802 
1803     #[tokio::test]
1804     async fn cli_serve_only_one_process_allowed() -> Result<()> {
1805         let wasm = CLI_SERVE_ECHO_ENV_COMPONENT;
1806         let server = WasmtimeServe::new(wasm, |cmd| {
1807             cmd.arg("-Scli");
1808         })?;
1809 
1810         let err = WasmtimeServe::spawn(
1811             super::get_wasmtime_command()?
1812                 .arg("serve")
1813                 .arg("-Scli")
1814                 .arg(format!("--addr={}", server.addr))
1815                 .arg(wasm),
1816         )
1817         .err()
1818         .expect("server spawn should have failed but it succeeded");
1819         drop(server);
1820 
1821         let err = format!("{err:?}");
1822         println!("{err}");
1823         assert!(err.contains("os error"));
1824         Ok(())
1825     }
1826 
1827     // Technically this test is a little racy. This binds port 0 to acquire a
1828     // random port, issues a single request to this port, but then kills this
1829     // server while the request is still processing. The port is then rebound
1830     // in the next process while it technically could be stolen by another
1831     // process.
1832     #[tokio::test]
1833     async fn cli_serve_quick_rebind_allowed() -> Result<()> {
1834         let wasm = CLI_SERVE_ECHO_ENV_COMPONENT;
1835         let server = WasmtimeServe::new(wasm, |cmd| {
1836             cmd.arg("-Scli");
1837         })?;
1838         let addr = server.addr;
1839 
1840         // Start up a `send` and `conn_task` which represents a connection to
1841         // this server.
1842         let (mut send, conn_task) = server.start_requests().await?;
1843         let _ = send
1844             .send_request(
1845                 hyper::Request::builder()
1846                     .uri("http://localhost/")
1847                     .header("env", "FOO")
1848                     .body(String::new())
1849                     .context("failed to make request")?,
1850             )
1851             .await;
1852 
1853         // ... once a response has been received (or at least the status
1854         // code/headers) then kill the server. THis is done while `conn_task`
1855         // and `send` are still alive so we're guaranteed that the other side
1856         // got a request (we got a response) and our connection is still open.
1857         //
1858         // This forces the address/port into the `TIME_WAIT` state. The rebind
1859         // below in the next process will fail if `SO_REUSEADDR` isn't set.
1860         drop(server);
1861         drop(send);
1862         let _ = conn_task.await;
1863 
1864         // If this is successfully bound then we'll create `WasmtimeServe`
1865         // which reads off the first line of output to know which address was
1866         // bound.
1867         let _server2 = WasmtimeServe::spawn(
1868             super::get_wasmtime_command()?
1869                 .arg("serve")
1870                 .arg("-Scli")
1871                 .arg(format!("--addr={addr}"))
1872                 .arg(wasm),
1873         )?;
1874 
1875         Ok(())
1876     }
1877 
1878     #[tokio::test]
1879     async fn cli_serve_with_print() -> Result<()> {
1880         let server = WasmtimeServe::new(CLI_SERVE_WITH_PRINT_COMPONENT, |cmd| {
1881             cmd.arg("-Scli");
1882         })?;
1883 
1884         for _ in 0..2 {
1885             let resp = server
1886                 .send_request(
1887                     hyper::Request::builder()
1888                         .uri("http://localhost/")
1889                         .body(String::new())
1890                         .context("failed to make request")?,
1891                 )
1892                 .await?;
1893             assert!(resp.status().is_success());
1894         }
1895 
1896         let (out, err) = server.finish()?;
1897         assert_eq!(
1898             out,
1899             "\
1900 stdout [0] :: this is half a print to stdout
1901 stdout [0] :: \n\
1902 stdout [0] :: after empty
1903 stdout [1] :: this is half a print to stdout
1904 stdout [1] :: \n\
1905 stdout [1] :: after empty
1906 "
1907         );
1908         assert_eq!(
1909             err,
1910             "\
1911 stderr [0] :: this is half a print to stderr
1912 stderr [0] :: \n\
1913 stderr [0] :: after empty
1914 stderr [1] :: this is half a print to stderr
1915 stderr [1] :: \n\
1916 stderr [1] :: after empty
1917 "
1918         );
1919 
1920         Ok(())
1921     }
1922 
1923     #[tokio::test]
1924     async fn cli_serve_with_print_no_prefix() -> Result<()> {
1925         let server = WasmtimeServe::new(CLI_SERVE_WITH_PRINT_COMPONENT, |cmd| {
1926             cmd.arg("-Scli");
1927             cmd.arg("--no-logging-prefix");
1928         })?;
1929 
1930         for _ in 0..2 {
1931             let resp = server
1932                 .send_request(
1933                     hyper::Request::builder()
1934                         .uri("http://localhost/")
1935                         .body(String::new())
1936                         .context("failed to make request")?,
1937                 )
1938                 .await?;
1939             assert!(resp.status().is_success());
1940         }
1941 
1942         let (out, err) = server.finish()?;
1943         assert_eq!(
1944             out,
1945             "\
1946 this is half a print to stdout
1947 \n\
1948 after empty
1949 this is half a print to stdout
1950 \n\
1951 after empty
1952 "
1953         );
1954         assert_eq!(
1955             err,
1956             "\
1957 this is half a print to stderr
1958 \n\
1959 after empty
1960 this is half a print to stderr
1961 \n\
1962 after empty
1963 "
1964         );
1965 
1966         Ok(())
1967     }
1968 
1969     #[tokio::test]
1970     async fn cli_serve_authority_and_scheme() -> Result<()> {
1971         let server = WasmtimeServe::new(CLI_SERVE_AUTHORITY_AND_SCHEME_COMPONENT, |cmd| {
1972             cmd.arg("-Scli");
1973         })?;
1974 
1975         let resp = server
1976             .send_request(
1977                 hyper::Request::builder()
1978                     .uri("/")
1979                     .header("Host", "localhost")
1980                     .body(String::new())
1981                     .context("failed to make request")?,
1982             )
1983             .await?;
1984         assert!(resp.status().is_success());
1985 
1986         let resp = server
1987             .send_request(
1988                 hyper::Request::builder()
1989                     .method("CONNECT")
1990                     .uri("http://localhost/")
1991                     .body(String::new())
1992                     .context("failed to make request")?,
1993             )
1994             .await?;
1995         assert!(resp.status().is_success());
1996 
1997         Ok(())
1998     }
1999 
2000     #[test]
2001     fn cli_argv0() -> Result<()> {
2002         run_wasmtime(&["run", "--argv0=a", CLI_ARGV0, "a"])?;
2003         run_wasmtime(&["run", "--argv0=b", CLI_ARGV0_COMPONENT, "b"])?;
2004         run_wasmtime(&["run", "--argv0=foo.wasm", CLI_ARGV0, "foo.wasm"])?;
2005         Ok(())
2006     }
2007 
2008     #[tokio::test]
2009     async fn cli_serve_config() -> Result<()> {
2010         let server = WasmtimeServe::new(CLI_SERVE_CONFIG_COMPONENT, |cmd| {
2011             cmd.arg("-Scli");
2012             cmd.arg("-Sconfig");
2013             cmd.arg("-Sconfig-var=hello=world");
2014         })?;
2015 
2016         let resp = server
2017             .send_request(
2018                 hyper::Request::builder()
2019                     .uri("http://localhost/")
2020                     .body(String::new())
2021                     .context("failed to make request")?,
2022             )
2023             .await?;
2024 
2025         assert!(resp.status().is_success());
2026         assert_eq!(resp.body(), "world");
2027         Ok(())
2028     }
2029 
2030     #[test]
2031     fn cli_config() -> Result<()> {
2032         run_wasmtime(&[
2033             "run",
2034             "-Sconfig",
2035             "-Sconfig-var=hello=world",
2036             CONFIG_GET_COMPONENT,
2037         ])?;
2038         Ok(())
2039     }
2040 
2041     #[tokio::test]
2042     async fn cli_serve_keyvalue() -> Result<()> {
2043         let server = WasmtimeServe::new(CLI_SERVE_KEYVALUE_COMPONENT, |cmd| {
2044             cmd.arg("-Scli");
2045             cmd.arg("-Skeyvalue");
2046             cmd.arg("-Skeyvalue-in-memory-data=hello=world");
2047         })?;
2048 
2049         let resp = server
2050             .send_request(
2051                 hyper::Request::builder()
2052                     .uri("http://localhost/")
2053                     .body(String::new())
2054                     .context("failed to make request")?,
2055             )
2056             .await?;
2057 
2058         assert!(resp.status().is_success());
2059         assert_eq!(resp.body(), "world");
2060         Ok(())
2061     }
2062 
2063     #[test]
2064     fn cli_keyvalue() -> Result<()> {
2065         run_wasmtime(&[
2066             "run",
2067             "-Skeyvalue",
2068             "-Skeyvalue-in-memory-data=atomics_key=5",
2069             KEYVALUE_MAIN_COMPONENT,
2070         ])?;
2071         Ok(())
2072     }
2073 
2074     #[test]
2075     fn cli_multiple_preopens() -> Result<()> {
2076         run_wasmtime(&[
2077             "run",
2078             "--dir=/::/a",
2079             "--dir=/::/b",
2080             "--dir=/::/c",
2081             CLI_MULTIPLE_PREOPENS_COMPONENT,
2082         ])?;
2083         Ok(())
2084     }
2085 }
2086 
2087 #[test]
2088 fn settings_command() -> Result<()> {
2089     // Skip this test on platforms that Cranelift doesn't support.
2090     if cranelift_native::builder().is_err() {
2091         return Ok(());
2092     }
2093     let output = run_wasmtime(&["settings"])?;
2094     assert!(output.contains("Cranelift settings for target"));
2095     Ok(())
2096 }
2097 
2098 #[cfg(target_arch = "x86_64")]
2099 #[test]
2100 fn profile_with_vtune() -> Result<()> {
2101     if !is_vtune_available() {
2102         println!("> `vtune` is not available on the system path; skipping test");
2103         return Ok(());
2104     }
2105 
2106     let mut bin = Command::new("vtune");
2107     bin.args(&[
2108         // Configure VTune...
2109         "-verbose",
2110         "-collect",
2111         "hotspots",
2112         "-user-data-dir",
2113         &std::env::temp_dir().to_string_lossy(),
2114         // ...then run Wasmtime with profiling enabled:
2115         &get_wasmtime_path()?.to_string_lossy(),
2116         "--profile=vtune",
2117         "tests/all/cli_tests/simple.wat",
2118     ]);
2119 
2120     println!("> executing: {bin:?}");
2121     let output = bin.output()?;
2122 
2123     assert!(output.status.success());
2124     let stdout = String::from_utf8_lossy(&output.stdout);
2125     let stderr = String::from_utf8_lossy(&output.stderr);
2126     println!("> stdout:\n{stdout}");
2127     assert!(stdout.contains("CPU Time"));
2128     println!("> stderr:\n{stderr}");
2129     assert!(!stderr.contains("Error"));
2130     Ok(())
2131 }
2132 
2133 #[cfg(target_arch = "x86_64")]
2134 fn is_vtune_available() -> bool {
2135     Command::new("vtune").arg("-version").output().is_ok()
2136 }
2137 
2138 #[test]
2139 fn unreachable_without_wasi() -> Result<()> {
2140     let output = run_wasmtime_for_output(
2141         &[
2142             "-Scli=n",
2143             "-Ccache=n",
2144             "tests/all/cli_tests/unreachable.wat",
2145         ],
2146         None,
2147     )?;
2148 
2149     assert_ne!(output.stderr, b"");
2150     assert_eq!(output.stdout, b"");
2151     assert_trap_code(&output.status);
2152     Ok(())
2153 }
2154 
2155 #[test]
2156 fn config_cli_flag() -> Result<()> {
2157     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
2158 
2159     // Test some valid TOML values
2160     let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2161     cfg.write_all(
2162         br#"
2163         [optimize]
2164         opt_level = 2
2165         regalloc_algorithm = "single-pass"
2166         signals_based_traps = false
2167 
2168         [codegen]
2169         collector = "null"
2170 
2171         [debug]
2172         debug_info = true
2173 
2174         [wasm]
2175         max_wasm_stack = 65536
2176 
2177         [wasi]
2178         cli = true
2179         "#,
2180     )?;
2181     let output = run_wasmtime(&[
2182         "run",
2183         "--config",
2184         cfg_path.to_str().unwrap(),
2185         "--invoke",
2186         "get_f64",
2187         wasm.path().to_str().unwrap(),
2188     ])?;
2189     assert_eq!(output, "100\n");
2190 
2191     // Make sure CLI flags overrides TOML values
2192     let output = run_wasmtime(&[
2193         "run",
2194         "--config",
2195         cfg_path.to_str().unwrap(),
2196         "--invoke",
2197         "get_f64",
2198         "-W",
2199         "max-wasm-stack=0", // should override TOML value 65536 specified above and execution should fail
2200         wasm.path().to_str().unwrap(),
2201     ]);
2202     assert!(
2203         output
2204             .as_ref()
2205             .unwrap_err()
2206             .to_string()
2207             .contains("max_wasm_stack size cannot be zero"),
2208         "'{output:?}' did not contain expected error message",
2209     );
2210 
2211     // Test invalid TOML key
2212     let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2213     cfg.write_all(
2214         br#"
2215         [optimize]
2216         this_key_does_not_exist = true
2217         "#,
2218     )?;
2219     let output = run_wasmtime(&[
2220         "run",
2221         "--config",
2222         cfg_path.to_str().unwrap(),
2223         wasm.path().to_str().unwrap(),
2224     ]);
2225     assert!(
2226         output
2227             .as_ref()
2228             .unwrap_err()
2229             .to_string()
2230             .contains("unknown field `this_key_does_not_exist`"),
2231         "'{output:?}' did not contain expected error message"
2232     );
2233 
2234     // Test invalid TOML table
2235     let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2236     cfg.write_all(
2237         br#"
2238         [invalid_table]
2239         "#,
2240     )?;
2241     let output = run_wasmtime(&[
2242         "run",
2243         "--config",
2244         cfg_path.to_str().unwrap(),
2245         wasm.path().to_str().unwrap(),
2246     ]);
2247     assert!(
2248         output
2249             .as_ref()
2250             .unwrap_err()
2251             .to_string()
2252             .contains("unknown field `invalid_table`, expected one of `optimize`, `codegen`, `debug`, `wasm`, `wasi`"),
2253         "'{output:?}' did not contain expected error message",
2254     );
2255 
2256     Ok(())
2257 }
2258