xref: /wasmtime-44.0.1/tests/all/cli_tests.rs (revision bbd12e92)
1 #![cfg(not(miri))]
2 
3 use anyhow::{Result, bail};
4 use std::fs::File;
5 use std::io::Write;
6 use std::path::Path;
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     let mut cmd = wasmtime_test_util::command(get_wasmtime_path());
24 
25     // Ignore this if it's specified in the environment to allow tests to run in
26     // "default mode" by default.
27     cmd.env_remove("WASMTIME_NEW_CLI");
28 
29     Ok(cmd)
30 }
31 
32 fn get_wasmtime_path() -> &'static str {
33     env!("CARGO_BIN_EXE_wasmtime")
34 }
35 
36 // Run the wasmtime CLI with the provided args and, if it succeeds, return
37 // the standard output in a `String`.
38 pub fn run_wasmtime(args: &[&str]) -> Result<String> {
39     let output = run_wasmtime_for_output(args, None)?;
40     if !output.status.success() {
41         bail!(
42             "Failed to execute wasmtime with: {:?}\nstatus: {}\n{}",
43             args,
44             output.status,
45             String::from_utf8_lossy(&output.stderr)
46         );
47     }
48     Ok(String::from_utf8(output.stdout).unwrap())
49 }
50 
51 fn build_wasm(wat_path: impl AsRef<Path>) -> Result<NamedTempFile> {
52     let mut wasm_file = NamedTempFile::new()?;
53     let wasm = wat::parse_file(wat_path)?;
54     wasm_file.write(&wasm)?;
55     Ok(wasm_file)
56 }
57 
58 // Very basic use case: compile binary wasm file and run specific function with arguments.
59 #[test]
60 fn run_wasmtime_simple() -> Result<()> {
61     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
62     run_wasmtime(&[
63         "run",
64         "--invoke",
65         "simple",
66         "-Ccache=n",
67         wasm.path().to_str().unwrap(),
68         "4",
69     ])?;
70     Ok(())
71 }
72 
73 // Wasmtime shall fail when not enough arguments were provided.
74 #[test]
75 fn run_wasmtime_simple_fail_no_args() -> Result<()> {
76     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
77     assert!(
78         run_wasmtime(&[
79             "run",
80             "-Ccache=n",
81             "--invoke",
82             "simple",
83             wasm.path().to_str().unwrap(),
84         ])
85         .is_err(),
86         "shall fail"
87     );
88     Ok(())
89 }
90 
91 #[test]
92 fn run_coredump_smoketest() -> Result<()> {
93     let wasm = build_wasm("tests/all/cli_tests/coredump_smoketest.wat")?;
94     let coredump_file = NamedTempFile::new()?;
95     let coredump_arg = format!("-Dcoredump={}", coredump_file.path().display());
96     let err = run_wasmtime(&[
97         "run",
98         "--invoke",
99         "a",
100         "-Ccache=n",
101         &coredump_arg,
102         wasm.path().to_str().unwrap(),
103     ])
104     .unwrap_err();
105     assert!(err.to_string().contains(&format!(
106         "core dumped at {}",
107         coredump_file.path().display()
108     )));
109     Ok(())
110 }
111 
112 // Running simple wat
113 #[test]
114 fn run_wasmtime_simple_wat() -> Result<()> {
115     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
116     run_wasmtime(&[
117         "run",
118         "--invoke",
119         "simple",
120         "-Ccache=n",
121         wasm.path().to_str().unwrap(),
122         "4",
123     ])?;
124     assert_eq!(
125         run_wasmtime(&[
126             "run",
127             "--invoke",
128             "get_f32",
129             "-Ccache=n",
130             wasm.path().to_str().unwrap(),
131         ])?,
132         "100\n"
133     );
134     assert_eq!(
135         run_wasmtime(&[
136             "run",
137             "--invoke",
138             "get_f64",
139             "-Ccache=n",
140             wasm.path().to_str().unwrap(),
141         ])?,
142         "100\n"
143     );
144     Ok(())
145 }
146 
147 // Running a wat that traps.
148 #[test]
149 fn run_wasmtime_unreachable_wat() -> Result<()> {
150     let wasm = build_wasm("tests/all/cli_tests/unreachable.wat")?;
151     let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?;
152 
153     assert_ne!(output.stderr, b"");
154     assert_eq!(output.stdout, b"");
155 
156     assert_trap_code(&output.status);
157     Ok(())
158 }
159 
160 fn assert_trap_code(status: &ExitStatus) {
161     let code = status
162         .code()
163         .expect("wasmtime process should exit normally");
164 
165     // Test for the specific error code Wasmtime uses to indicate a trap return.
166     #[cfg(unix)]
167     assert_eq!(code, 128 + libc::SIGABRT);
168     #[cfg(windows)]
169     assert_eq!(code, 3);
170 }
171 
172 // Run a simple WASI hello world, snapshot0 edition.
173 #[test]
174 fn hello_wasi_snapshot0() -> Result<()> {
175     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?;
176     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
177         let stdout = run_wasmtime(&["-Ccache=n", preview2, wasm.path().to_str().unwrap()])?;
178         assert_eq!(stdout, "Hello, world!\n");
179     }
180     Ok(())
181 }
182 
183 // Run a simple WASI hello world, snapshot1 edition.
184 #[test]
185 fn hello_wasi_snapshot1() -> Result<()> {
186     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?;
187     let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
188     assert_eq!(stdout, "Hello, world!\n");
189     Ok(())
190 }
191 
192 #[test]
193 fn timeout_in_start() -> Result<()> {
194     let wasm = build_wasm("tests/all/cli_tests/iloop-start.wat")?;
195     let output = run_wasmtime_for_output(
196         &[
197             "run",
198             "-Wtimeout=1ms",
199             "-Ccache=n",
200             wasm.path().to_str().unwrap(),
201         ],
202         None,
203     )?;
204     assert!(!output.status.success());
205     assert_eq!(output.stdout, b"");
206     let stderr = String::from_utf8_lossy(&output.stderr);
207     assert!(
208         stderr.contains("wasm trap: interrupt"),
209         "bad stderr: {stderr}"
210     );
211     Ok(())
212 }
213 
214 #[test]
215 fn timeout_in_invoke() -> Result<()> {
216     let wasm = build_wasm("tests/all/cli_tests/iloop-invoke.wat")?;
217     let output = run_wasmtime_for_output(
218         &[
219             "run",
220             "-Wtimeout=1ms",
221             "-Ccache=n",
222             wasm.path().to_str().unwrap(),
223         ],
224         None,
225     )?;
226     assert!(!output.status.success());
227     assert_eq!(output.stdout, b"");
228     let stderr = String::from_utf8_lossy(&output.stderr);
229     assert!(
230         stderr.contains("wasm trap: interrupt"),
231         "bad stderr: {stderr}"
232     );
233     Ok(())
234 }
235 
236 // Exit with a valid non-zero exit code, snapshot0 edition.
237 #[test]
238 fn exit2_wasi_snapshot0() -> Result<()> {
239     let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot0.wat")?;
240 
241     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
242         let output = run_wasmtime_for_output(
243             &["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
244             None,
245         )?;
246         assert_eq!(output.status.code().unwrap(), 2);
247     }
248     Ok(())
249 }
250 
251 // Exit with a valid non-zero exit code, snapshot1 edition.
252 #[test]
253 fn exit2_wasi_snapshot1() -> Result<()> {
254     let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot1.wat")?;
255     let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
256     assert_eq!(output.status.code().unwrap(), 2);
257     Ok(())
258 }
259 
260 // Exit with a valid non-zero exit code, snapshot0 edition.
261 #[test]
262 fn exit125_wasi_snapshot0() -> Result<()> {
263     let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot0.wat")?;
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         dbg!(&output);
270         assert_eq!(output.status.code().unwrap(), 125);
271     }
272     Ok(())
273 }
274 
275 // Exit with a valid non-zero exit code, snapshot1 edition.
276 #[test]
277 fn exit125_wasi_snapshot1() -> Result<()> {
278     let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot1.wat")?;
279     let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
280     assert_eq!(output.status.code().unwrap(), 125);
281     Ok(())
282 }
283 
284 // Exit with an invalid non-zero exit code, snapshot0 edition.
285 #[test]
286 fn exit126_wasi_snapshot0() -> Result<()> {
287     let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot0.wat")?;
288 
289     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
290         let output = run_wasmtime_for_output(
291             &["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
292             None,
293         )?;
294         assert_eq!(output.status.code().unwrap(), 1);
295         assert!(output.stdout.is_empty());
296         assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status"));
297     }
298     Ok(())
299 }
300 
301 // Exit with an invalid non-zero exit code, snapshot1 edition.
302 #[test]
303 fn exit126_wasi_snapshot1() -> Result<()> {
304     let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot1.wat")?;
305     let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?;
306     assert_eq!(output.status.code().unwrap(), 1);
307     assert!(output.stdout.is_empty());
308     assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status"));
309     Ok(())
310 }
311 
312 // Run a minimal command program.
313 #[test]
314 fn minimal_command() -> Result<()> {
315     let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?;
316     let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
317     assert_eq!(stdout, "");
318     Ok(())
319 }
320 
321 // Run a minimal reactor program.
322 #[test]
323 fn minimal_reactor() -> Result<()> {
324     let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?;
325     let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
326     assert_eq!(stdout, "");
327     Ok(())
328 }
329 
330 // Attempt to call invoke on a command.
331 #[test]
332 fn command_invoke() -> Result<()> {
333     let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?;
334     run_wasmtime(&[
335         "run",
336         "--invoke",
337         "_start",
338         "-Ccache=n",
339         wasm.path().to_str().unwrap(),
340     ])?;
341     Ok(())
342 }
343 
344 // Attempt to call invoke on a command.
345 #[test]
346 fn reactor_invoke() -> Result<()> {
347     let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?;
348     run_wasmtime(&[
349         "run",
350         "--invoke",
351         "_initialize",
352         "-Ccache=n",
353         wasm.path().to_str().unwrap(),
354     ])?;
355     Ok(())
356 }
357 
358 // Run the greeter test, which runs a preloaded reactor and a command.
359 #[test]
360 fn greeter() -> Result<()> {
361     let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?;
362     let stdout = run_wasmtime(&[
363         "run",
364         "-Ccache=n",
365         "--preload",
366         "reactor=tests/all/cli_tests/greeter_reactor.wat",
367         wasm.path().to_str().unwrap(),
368     ])?;
369     assert_eq!(
370         stdout,
371         "Hello _initialize\nHello _start\nHello greet\nHello done\n"
372     );
373     Ok(())
374 }
375 
376 // Run the greeter test, but this time preload a command.
377 #[test]
378 fn greeter_preload_command() -> Result<()> {
379     let wasm = build_wasm("tests/all/cli_tests/greeter_reactor.wat")?;
380     let stdout = run_wasmtime(&[
381         "run",
382         "-Ccache=n",
383         "--preload",
384         "reactor=tests/all/cli_tests/hello_wasi_snapshot1.wat",
385         wasm.path().to_str().unwrap(),
386     ])?;
387     assert_eq!(stdout, "Hello _initialize\n");
388     Ok(())
389 }
390 
391 // Run the greeter test, which runs a preloaded reactor and a command.
392 #[test]
393 fn greeter_preload_callable_command() -> Result<()> {
394     let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?;
395     let stdout = run_wasmtime(&[
396         "run",
397         "-Ccache=n",
398         "--preload",
399         "reactor=tests/all/cli_tests/greeter_callable_command.wat",
400         wasm.path().to_str().unwrap(),
401     ])?;
402     assert_eq!(stdout, "Hello _start\nHello callable greet\nHello done\n");
403     Ok(())
404 }
405 
406 // Ensure successful WASI exit call with FPR saving frames on stack for Windows x64
407 // See https://github.com/bytecodealliance/wasmtime/issues/1967
408 #[test]
409 fn exit_with_saved_fprs() -> Result<()> {
410     let wasm = build_wasm("tests/all/cli_tests/exit_with_saved_fprs.wat")?;
411     let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
412     assert_eq!(output.status.code().unwrap(), 0);
413     assert!(output.stdout.is_empty());
414     Ok(())
415 }
416 
417 #[test]
418 fn run_cwasm() -> Result<()> {
419     let td = TempDir::new()?;
420     let cwasm = td.path().join("foo.cwasm");
421     let stdout = run_wasmtime(&[
422         "compile",
423         "tests/all/cli_tests/simple.wat",
424         "-o",
425         cwasm.to_str().unwrap(),
426     ])?;
427     assert_eq!(stdout, "");
428     let stdout = run_wasmtime(&["run", "--allow-precompiled", cwasm.to_str().unwrap()])?;
429     assert_eq!(stdout, "");
430     Ok(())
431 }
432 
433 #[cfg(unix)]
434 #[test]
435 fn hello_wasi_snapshot0_from_stdin() -> Result<()> {
436     // Run a simple WASI hello world, snapshot0 edition.
437     // The module is piped from standard input.
438     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?;
439     for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
440         let stdout = {
441             let path = wasm.path();
442             let args: &[&str] = &["-Ccache=n", preview2, "-"];
443             let output = run_wasmtime_for_output(args, Some(path))?;
444             if !output.status.success() {
445                 bail!(
446                     "Failed to execute wasmtime with: {:?}\n{}",
447                     args,
448                     String::from_utf8_lossy(&output.stderr)
449                 );
450             }
451             Ok::<_, anyhow::Error>(String::from_utf8(output.stdout).unwrap())
452         }?;
453         assert_eq!(stdout, "Hello, world!\n");
454     }
455     Ok(())
456 }
457 
458 #[test]
459 fn specify_env() -> Result<()> {
460     // By default no env is inherited
461     let output = get_wasmtime_command()?
462         .args(&["run", "tests/all/cli_tests/print_env.wat"])
463         .env("THIS_WILL_NOT", "show up in the output")
464         .output()?;
465     assert!(output.status.success());
466     assert_eq!(String::from_utf8_lossy(&output.stdout), "");
467 
468     // Specify a single env var
469     let output = get_wasmtime_command()?
470         .args(&[
471             "run",
472             "--env",
473             "FOO=bar",
474             "tests/all/cli_tests/print_env.wat",
475         ])
476         .output()?;
477     assert!(output.status.success());
478     assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n");
479 
480     // Inherit a single env var
481     let output = get_wasmtime_command()?
482         .args(&["run", "--env", "FOO", "tests/all/cli_tests/print_env.wat"])
483         .env("FOO", "bar")
484         .output()?;
485     assert!(output.status.success());
486     assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n");
487 
488     // Inherit a nonexistent env var
489     let output = get_wasmtime_command()?
490         .args(&[
491             "run",
492             "--env",
493             "SURELY_THIS_ENV_VAR_DOES_NOT_EXIST_ANYWHERE_RIGHT",
494             "tests/all/cli_tests/print_env.wat",
495         ])
496         .output()?;
497     assert!(output.status.success());
498 
499     // Inherit all env vars
500     let output = get_wasmtime_command()?
501         .args(&["run", "-Sinherit-env", "tests/all/cli_tests/print_env.wat"])
502         .env("FOO", "bar")
503         .output()?;
504     assert!(output.status.success());
505     let stdout = String::from_utf8_lossy(&output.stdout);
506     assert!(stdout.contains("FOO=bar"), "bad output: {stdout}");
507 
508     Ok(())
509 }
510 
511 #[cfg(unix)]
512 #[test]
513 fn run_cwasm_from_stdin() -> Result<()> {
514     use std::process::Stdio;
515 
516     let td = TempDir::new()?;
517     let cwasm = td.path().join("foo.cwasm");
518     let stdout = run_wasmtime(&[
519         "compile",
520         "tests/all/cli_tests/simple.wat",
521         "-o",
522         cwasm.to_str().unwrap(),
523     ])?;
524     assert_eq!(stdout, "");
525 
526     // If stdin is literally the file itself then that should work
527     let args: &[&str] = &["run", "--allow-precompiled", "-"];
528     let output = get_wasmtime_command()?
529         .args(args)
530         .stdin(File::open(&cwasm)?)
531         .output()?;
532     assert!(output.status.success(), "a file as stdin should work");
533 
534     // If stdin is a pipe, that should also work
535     let input = std::fs::read(&cwasm)?;
536     let mut child = get_wasmtime_command()?
537         .args(args)
538         .stdin(Stdio::piped())
539         .stdout(Stdio::piped())
540         .stderr(Stdio::piped())
541         .spawn()?;
542     let mut stdin = child.stdin.take().unwrap();
543     let t = std::thread::spawn(move || {
544         let _ = stdin.write_all(&input);
545     });
546     let output = child.wait_with_output()?;
547     assert!(output.status.success());
548     t.join().unwrap();
549     Ok(())
550 }
551 
552 #[cfg(feature = "wasi-threads")]
553 #[test]
554 fn run_threads() -> Result<()> {
555     // Only run threaded tests on platforms that support threads. Also skip
556     // these tests with ASAN as it, rightfully, complains about a memory leak.
557     // The memory leak at this time is that child threads aren't joined with the
558     // main thread, meaning that allocations done on child threads are indeed
559     // leaked.
560     if crate::threads::engine().is_none() || cfg!(asan) {
561         return Ok(());
562     }
563     let wasm = build_wasm("tests/all/cli_tests/threads.wat")?;
564     let stdout = run_wasmtime(&[
565         "run",
566         "-Wthreads,shared-memory",
567         "-Sthreads",
568         "-Ccache=n",
569         wasm.path().to_str().unwrap(),
570     ])?;
571 
572     assert!(
573         stdout
574             == "Called _start\n\
575     Running wasi_thread_start\n\
576     Running wasi_thread_start\n\
577     Running wasi_thread_start\n\
578     Done\n"
579     );
580     Ok(())
581 }
582 
583 #[cfg(feature = "wasi-threads")]
584 #[test]
585 fn run_simple_with_wasi_threads() -> Result<()> {
586     // Skip this test on platforms that don't support threads.
587     if crate::threads::engine().is_none() {
588         return Ok(());
589     }
590     // We expect to be able to run Wasm modules that do not have correct
591     // wasi-thread entry points or imported shared memory as long as no threads
592     // are spawned.
593     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
594     let stdout = run_wasmtime(&[
595         "run",
596         "-Wthreads",
597         "-Sthreads",
598         "-Ccache=n",
599         "--invoke",
600         "simple",
601         wasm.path().to_str().unwrap(),
602         "4",
603     ])?;
604     assert_eq!(stdout, "4\n");
605     Ok(())
606 }
607 
608 #[test]
609 fn wasm_flags() -> Result<()> {
610     // Any argument after the wasm module should be interpreted as for the
611     // command itself
612     let stdout = run_wasmtime(&[
613         "run",
614         "--",
615         "tests/all/cli_tests/print-arguments.wat",
616         "--argument",
617         "-for",
618         "the",
619         "command",
620     ])?;
621     assert_eq!(
622         stdout,
623         "\
624             print-arguments.wat\n\
625             --argument\n\
626             -for\n\
627             the\n\
628             command\n\
629         "
630     );
631     let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "-"])?;
632     assert_eq!(
633         stdout,
634         "\
635             print-arguments.wat\n\
636             -\n\
637         "
638     );
639     let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "--"])?;
640     assert_eq!(
641         stdout,
642         "\
643             print-arguments.wat\n\
644             --\n\
645         "
646     );
647     let stdout = run_wasmtime(&[
648         "run",
649         "--",
650         "tests/all/cli_tests/print-arguments.wat",
651         "--",
652         "--",
653         "-a",
654         "b",
655     ])?;
656     assert_eq!(
657         stdout,
658         "\
659             print-arguments.wat\n\
660             --\n\
661             --\n\
662             -a\n\
663             b\n\
664         "
665     );
666     Ok(())
667 }
668 
669 #[test]
670 fn name_same_as_builtin_command() -> Result<()> {
671     // a bare subcommand shouldn't run successfully
672     let output = get_wasmtime_command()?
673         .current_dir("tests/all/cli_tests")
674         .arg("run")
675         .output()?;
676     assert!(!output.status.success());
677 
678     // a `--` prefix should let everything else get interpreted as a wasm
679     // module and arguments, even if the module has a name like `run`
680     let output = get_wasmtime_command()?
681         .current_dir("tests/all/cli_tests")
682         .arg("--")
683         .arg("run")
684         .output()?;
685     assert!(output.status.success(), "expected success got {output:#?}");
686 
687     // Passing options before the subcommand should work and doesn't require
688     // `--` to disambiguate
689     let output = get_wasmtime_command()?
690         .current_dir("tests/all/cli_tests")
691         .arg("-Ccache=n")
692         .arg("run")
693         .output()?;
694     assert!(output.status.success(), "expected success got {output:#?}");
695     Ok(())
696 }
697 
698 #[test]
699 #[cfg(unix)]
700 fn run_just_stdin_argument() -> Result<()> {
701     let output = get_wasmtime_command()?
702         .arg("-")
703         .stdin(File::open("tests/all/cli_tests/simple.wat")?)
704         .output()?;
705     assert!(output.status.success());
706     Ok(())
707 }
708 
709 #[test]
710 fn wasm_flags_without_subcommand() -> Result<()> {
711     let output = get_wasmtime_command()?
712         .current_dir("tests/all/cli_tests/")
713         .arg("print-arguments.wat")
714         .arg("-foo")
715         .arg("bar")
716         .output()?;
717     assert!(output.status.success());
718     assert_eq!(
719         String::from_utf8_lossy(&output.stdout),
720         "\
721             print-arguments.wat\n\
722             -foo\n\
723             bar\n\
724         "
725     );
726     Ok(())
727 }
728 
729 #[test]
730 fn wasi_misaligned_pointer() -> Result<()> {
731     let output = get_wasmtime_command()?
732         .arg("./tests/all/cli_tests/wasi_misaligned_pointer.wat")
733         .output()?;
734     assert!(!output.status.success());
735     let stderr = String::from_utf8_lossy(&output.stderr);
736     assert!(
737         stderr.contains("Pointer not aligned"),
738         "bad stderr: {stderr}",
739     );
740     Ok(())
741 }
742 
743 #[test]
744 #[cfg_attr(not(feature = "component-model"), ignore)]
745 fn hello_with_preview2() -> Result<()> {
746     let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?;
747     let stdout = run_wasmtime(&["-Ccache=n", "-Spreview2", wasm.path().to_str().unwrap()])?;
748     assert_eq!(stdout, "Hello, world!\n");
749     Ok(())
750 }
751 
752 #[test]
753 #[cfg_attr(not(feature = "component-model"), ignore)]
754 fn component_missing_feature() -> Result<()> {
755     let path = "tests/all/cli_tests/empty-component.wat";
756     let wasm = build_wasm(path)?;
757     let output = get_wasmtime_command()?
758         .arg("-Ccache=n")
759         .arg("-Wcomponent-model=n")
760         .arg(wasm.path())
761         .output()?;
762     assert!(!output.status.success());
763     let stderr = String::from_utf8_lossy(&output.stderr);
764     assert!(
765         stderr.contains("cannot execute a component without `--wasm component-model`"),
766         "bad stderr: {stderr}"
767     );
768 
769     // also tests with raw *.wat input
770     let output = get_wasmtime_command()?
771         .arg("-Ccache=n")
772         .arg("-Wcomponent-model=n")
773         .arg(path)
774         .output()?;
775     assert!(!output.status.success());
776     let stderr = String::from_utf8_lossy(&output.stderr);
777     assert!(
778         stderr.contains("cannot execute a component without `--wasm component-model`"),
779         "bad stderr: {stderr}"
780     );
781 
782     Ok(())
783 }
784 
785 #[test]
786 #[cfg_attr(not(feature = "component-model"), ignore)]
787 fn component_enabled_by_default() -> Result<()> {
788     let path = "tests/all/cli_tests/component-basic.wat";
789     let wasm = build_wasm(path)?;
790     let output = get_wasmtime_command()?
791         .arg("-Ccache=n")
792         .arg(wasm.path())
793         .output()?;
794     assert!(output.status.success());
795 
796     // also tests with raw *.wat input
797     let output = get_wasmtime_command()?
798         .arg("-Ccache=n")
799         .arg(path)
800         .output()?;
801     assert!(output.status.success());
802 
803     Ok(())
804 }
805 
806 // If the text format is invalid then the filename should be mentioned in the
807 // error message.
808 #[test]
809 fn bad_text_syntax() -> Result<()> {
810     let output = get_wasmtime_command()?
811         .arg("-Ccache=n")
812         .arg("tests/all/cli_tests/bad-syntax.wat")
813         .output()?;
814     assert!(!output.status.success());
815     let stderr = String::from_utf8_lossy(&output.stderr);
816     assert!(
817         stderr.contains("--> tests/all/cli_tests/bad-syntax.wat"),
818         "bad stderr: {stderr}"
819     );
820     Ok(())
821 }
822 
823 #[test]
824 #[cfg_attr(not(feature = "component-model"), ignore)]
825 fn run_basic_component() -> Result<()> {
826     let path = "tests/all/cli_tests/component-basic.wat";
827     let wasm = build_wasm(path)?;
828 
829     // Run both the `*.wasm` binary and the text format
830     run_wasmtime(&[
831         "-Ccache=n",
832         "-Wcomponent-model",
833         wasm.path().to_str().unwrap(),
834     ])?;
835     run_wasmtime(&["-Ccache=n", "-Wcomponent-model", path])?;
836 
837     Ok(())
838 }
839 
840 #[test]
841 #[cfg_attr(not(feature = "component-model"), ignore)]
842 fn run_precompiled_component() -> Result<()> {
843     let td = TempDir::new()?;
844     let cwasm = td.path().join("component-basic.cwasm");
845     let stdout = run_wasmtime(&[
846         "compile",
847         "tests/all/cli_tests/component-basic.wat",
848         "-o",
849         cwasm.to_str().unwrap(),
850         "-Wcomponent-model",
851     ])?;
852     assert_eq!(stdout, "");
853     let stdout = run_wasmtime(&[
854         "run",
855         "-Wcomponent-model",
856         "--allow-precompiled",
857         cwasm.to_str().unwrap(),
858     ])?;
859     assert_eq!(stdout, "");
860 
861     Ok(())
862 }
863 
864 // Disable test on s390x because the large allocation may actually succeed;
865 // the whole 64-bit address space is available on this platform.
866 #[test]
867 #[cfg(not(target_arch = "s390x"))]
868 fn memory_growth_failure() -> Result<()> {
869     let output = get_wasmtime_command()?
870         .args(&[
871             "run",
872             "-Wmemory64",
873             "-Wtrap-on-grow-failure",
874             "tests/all/cli_tests/memory-grow-failure.wat",
875         ])
876         .output()?;
877     assert!(!output.status.success());
878     let stderr = String::from_utf8_lossy(&output.stderr);
879     assert!(
880         stderr.contains("forcing a memory growth failure to be a trap"),
881         "bad stderr: {stderr}"
882     );
883     Ok(())
884 }
885 
886 #[test]
887 fn table_growth_failure() -> Result<()> {
888     let output = get_wasmtime_command()?
889         .args(&[
890             "run",
891             "-Wtrap-on-grow-failure",
892             "tests/all/cli_tests/table-grow-failure.wat",
893         ])
894         .output()?;
895     assert!(!output.status.success());
896     let stderr = String::from_utf8_lossy(&output.stderr);
897     assert!(
898         stderr.contains("forcing trap when growing table"),
899         "bad stderr: {stderr}"
900     );
901     Ok(())
902 }
903 
904 #[test]
905 fn table_growth_failure2() -> Result<()> {
906     let output = get_wasmtime_command()?
907         .args(&[
908             "run",
909             "-Wtrap-on-grow-failure",
910             "tests/all/cli_tests/table-grow-failure2.wat",
911         ])
912         .output()?;
913     assert!(!output.status.success());
914     let stderr = String::from_utf8_lossy(&output.stderr);
915     let expected = if cfg!(target_pointer_width = "32") {
916         "overflow calculating new table size"
917     } else {
918         "forcing trap when growing table to 4294967296 elements"
919     };
920     assert!(stderr.contains(expected), "bad stderr: {stderr}");
921     Ok(())
922 }
923 
924 #[test]
925 fn option_group_help() -> Result<()> {
926     run_wasmtime(&["run", "-Whelp"])?;
927     run_wasmtime(&["run", "-O", "help"])?;
928     run_wasmtime(&["run", "--codegen", "help"])?;
929     run_wasmtime(&["run", "--debug=help"])?;
930     run_wasmtime(&["run", "-Shelp"])?;
931     run_wasmtime(&["run", "-Whelp-long"])?;
932     Ok(())
933 }
934 
935 #[test]
936 fn option_group_comma_separated() -> Result<()> {
937     run_wasmtime(&[
938         "run",
939         "-Wrelaxed-simd,simd",
940         "tests/all/cli_tests/simple.wat",
941     ])?;
942     Ok(())
943 }
944 
945 #[test]
946 fn option_group_boolean_parsing() -> Result<()> {
947     run_wasmtime(&["run", "-Wrelaxed-simd", "tests/all/cli_tests/simple.wat"])?;
948     run_wasmtime(&["run", "-Wrelaxed-simd=n", "tests/all/cli_tests/simple.wat"])?;
949     run_wasmtime(&["run", "-Wrelaxed-simd=y", "tests/all/cli_tests/simple.wat"])?;
950     run_wasmtime(&["run", "-Wrelaxed-simd=no", "tests/all/cli_tests/simple.wat"])?;
951     run_wasmtime(&[
952         "run",
953         "-Wrelaxed-simd=yes",
954         "tests/all/cli_tests/simple.wat",
955     ])?;
956     run_wasmtime(&[
957         "run",
958         "-Wrelaxed-simd=true",
959         "tests/all/cli_tests/simple.wat",
960     ])?;
961     run_wasmtime(&[
962         "run",
963         "-Wrelaxed-simd=false",
964         "tests/all/cli_tests/simple.wat",
965     ])?;
966     Ok(())
967 }
968 
969 #[test]
970 fn preview2_stdin() -> Result<()> {
971     let test = "tests/all/cli_tests/count-stdin.wat";
972     let cmd = || -> Result<_> {
973         let mut cmd = get_wasmtime_command()?;
974         cmd.arg("--invoke=count").arg("-Spreview2").arg(test);
975         Ok(cmd)
976     };
977 
978     // read empty pipe is ok
979     let output = cmd()?.output()?;
980     assert!(output.status.success());
981     assert_eq!(String::from_utf8_lossy(&output.stdout), "0\n");
982 
983     // read itself is ok
984     let file = File::open(test)?;
985     let size = file.metadata()?.len();
986     let output = cmd()?.stdin(File::open(test)?).output()?;
987     assert!(output.status.success());
988     assert_eq!(String::from_utf8_lossy(&output.stdout), format!("{size}\n"));
989 
990     // read piped input ok is ok
991     let mut child = cmd()?
992         .stdin(Stdio::piped())
993         .stdout(Stdio::piped())
994         .stderr(Stdio::piped())
995         .spawn()?;
996     let mut stdin = child.stdin.take().unwrap();
997     std::thread::spawn(move || {
998         stdin.write_all(b"hello").unwrap();
999     });
1000     let output = child.wait_with_output()?;
1001     assert!(output.status.success());
1002     assert_eq!(String::from_utf8_lossy(&output.stdout), "5\n");
1003 
1004     let count_up_to = |n: usize| -> Result<_> {
1005         let mut child = get_wasmtime_command()?
1006             .arg("--invoke=count-up-to")
1007             .arg("-Spreview2")
1008             .arg(test)
1009             .arg(n.to_string())
1010             .stdin(Stdio::piped())
1011             .stdout(Stdio::piped())
1012             .stderr(Stdio::piped())
1013             .spawn()?;
1014         let mut stdin = child.stdin.take().unwrap();
1015         let t = std::thread::spawn(move || {
1016             let mut written = 0;
1017             let bytes = [0; 64 * 1024];
1018             loop {
1019                 written += match stdin.write(&bytes) {
1020                     Ok(n) => n,
1021                     Err(_) => break written,
1022                 };
1023             }
1024         });
1025         let output = child.wait_with_output()?;
1026         assert!(output.status.success());
1027         let written = t.join().unwrap();
1028         let read = String::from_utf8_lossy(&output.stdout)
1029             .trim()
1030             .parse::<usize>()
1031             .unwrap();
1032         // The test reads in 1000 byte chunks so make sure that it doesn't read
1033         // more than 1000 bytes than requested.
1034         assert!(read < n + 1000, "test read too much {read}");
1035         Ok(written)
1036     };
1037 
1038     // wasmtime shouldn't eat information that the guest never actually tried to
1039     // read.
1040     //
1041     // NB: this may be a bit flaky. Exactly how much we wrote in the above
1042     // helper thread depends on how much the OS buffers for us. For now give
1043     // some some slop and assume that OSes are unlikely to buffer more than
1044     // that.
1045     let slop = 256 * 1024;
1046     for amt in [0, 100, 100_000] {
1047         let written = count_up_to(amt)?;
1048         assert!(written < slop + amt, "wrote too much {written}");
1049     }
1050     Ok(())
1051 }
1052 
1053 #[test]
1054 fn float_args() -> Result<()> {
1055     let result = run_wasmtime(&[
1056         "--invoke",
1057         "echo_f32",
1058         "tests/all/cli_tests/simple.wat",
1059         "1.0",
1060     ])?;
1061     assert_eq!(result, "1\n");
1062     let result = run_wasmtime(&[
1063         "--invoke",
1064         "echo_f64",
1065         "tests/all/cli_tests/simple.wat",
1066         "1.1",
1067     ])?;
1068     assert_eq!(result, "1.1\n");
1069     Ok(())
1070 }
1071 
1072 #[test]
1073 fn mpk_without_pooling() -> Result<()> {
1074     let output = get_wasmtime_command()?
1075         .args(&[
1076             "run",
1077             "-O",
1078             "memory-protection-keys=y",
1079             "--invoke",
1080             "echo_f32",
1081             "tests/all/cli_tests/simple.wat",
1082             "1.0",
1083         ])
1084         .env("WASMTIME_NEW_CLI", "1")
1085         .output()?;
1086     assert!(!output.status.success());
1087     Ok(())
1088 }
1089 
1090 // Very basic use case: compile binary wasm file and run specific function with arguments.
1091 #[test]
1092 fn increase_stack_size() -> Result<()> {
1093     run_wasmtime(&[
1094         "run",
1095         "--invoke",
1096         "simple",
1097         &format!("-Wmax-wasm-stack={}", 5 << 20),
1098         "-Ccache=n",
1099         "tests/all/cli_tests/simple.wat",
1100         "4",
1101     ])?;
1102     Ok(())
1103 }
1104 
1105 mod test_programs {
1106     use super::{get_wasmtime_command, run_wasmtime};
1107     use anyhow::{Context, Result, bail};
1108     use http_body_util::BodyExt;
1109     use hyper::header::HeaderValue;
1110     use std::io::{self, BufRead, BufReader, Read, Write};
1111     use std::iter;
1112     use std::net::SocketAddr;
1113     use std::process::{Child, Command, Stdio};
1114     use std::thread::{self, JoinHandle};
1115     use test_programs_artifacts::*;
1116     use tokio::net::TcpStream;
1117 
1118     macro_rules! assert_test_exists {
1119         ($name:ident) => {
1120             #[expect(unused_imports, reason = "just here to assert the test is here")]
1121             use self::$name as _;
1122         };
1123     }
1124     foreach_p2_cli!(assert_test_exists);
1125 
1126     #[test]
1127     fn p2_cli_hello_stdout() -> Result<()> {
1128         run_wasmtime(&["run", "-Wcomponent-model", P2_CLI_HELLO_STDOUT_COMPONENT])?;
1129         Ok(())
1130     }
1131 
1132     #[test]
1133     fn p2_cli_args() -> Result<()> {
1134         run_wasmtime(&[
1135             "run",
1136             "-Wcomponent-model",
1137             P2_CLI_ARGS_COMPONENT,
1138             "hello",
1139             "this",
1140             "",
1141             "is an argument",
1142             "with �� emoji",
1143         ])?;
1144         Ok(())
1145     }
1146 
1147     #[test]
1148     fn p2_cli_stdin_empty() -> Result<()> {
1149         let mut child = get_wasmtime_command()?
1150             .args(&["run", "-Wcomponent-model", P2_CLI_STDIN_EMPTY_COMPONENT])
1151             .stdout(Stdio::piped())
1152             .stderr(Stdio::piped())
1153             .stdin(Stdio::piped())
1154             .spawn()?;
1155         child
1156             .stdin
1157             .take()
1158             .unwrap()
1159             .write_all(b"not to be read")
1160             .unwrap();
1161         let output = child.wait_with_output()?;
1162         println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
1163         println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
1164         assert!(output.status.success());
1165         Ok(())
1166     }
1167 
1168     #[test]
1169     fn p2_cli_stdin() -> Result<()> {
1170         let mut child = get_wasmtime_command()?
1171             .args(&["run", "-Wcomponent-model", P2_CLI_STDIN_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"So rested he by the Tumtum tree")
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 p2_cli_splice_stdin() -> Result<()> {
1191         let mut child = get_wasmtime_command()?
1192             .args(&["run", "-Wcomponent-model", P2_CLI_SPLICE_STDIN_COMPONENT])
1193             .stdout(Stdio::piped())
1194             .stderr(Stdio::piped())
1195             .stdin(Stdio::piped())
1196             .spawn()?;
1197         let msg = "So rested he by the Tumtum tree";
1198         child
1199             .stdin
1200             .take()
1201             .unwrap()
1202             .write_all(msg.as_bytes())
1203             .unwrap();
1204         let output = child.wait_with_output()?;
1205         assert!(output.status.success());
1206         let stdout = String::from_utf8_lossy(&output.stdout);
1207         let stderr = String::from_utf8_lossy(&output.stderr);
1208         if !stderr.is_empty() {
1209             eprintln!("{stderr}");
1210         }
1211 
1212         assert_eq!(
1213             format!(
1214                 "before splice\n{msg}\ncompleted splicing {} bytes\n",
1215                 msg.as_bytes().len()
1216             ),
1217             stdout
1218         );
1219         Ok(())
1220     }
1221 
1222     #[test]
1223     fn p2_cli_env() -> Result<()> {
1224         run_wasmtime(&[
1225             "run",
1226             "-Wcomponent-model",
1227             "--env=frabjous=day",
1228             "--env=callooh=callay",
1229             P2_CLI_ENV_COMPONENT,
1230         ])?;
1231         Ok(())
1232     }
1233 
1234     #[test]
1235     fn p2_cli_file_read() -> Result<()> {
1236         let dir = tempfile::tempdir()?;
1237 
1238         std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?;
1239 
1240         run_wasmtime(&[
1241             "run",
1242             "-Wcomponent-model",
1243             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1244             P2_CLI_FILE_READ_COMPONENT,
1245         ])?;
1246         Ok(())
1247     }
1248 
1249     #[test]
1250     fn p2_cli_file_append() -> Result<()> {
1251         let dir = tempfile::tempdir()?;
1252 
1253         std::fs::File::create(dir.path().join("bar.txt"))?
1254             .write_all(b"'Twas brillig, and the slithy toves.\n")?;
1255 
1256         run_wasmtime(&[
1257             "run",
1258             "-Wcomponent-model",
1259             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1260             P2_CLI_FILE_APPEND_COMPONENT,
1261         ])?;
1262 
1263         let contents = std::fs::read(dir.path().join("bar.txt"))?;
1264         assert_eq!(
1265             std::str::from_utf8(&contents).unwrap(),
1266             "'Twas brillig, and the slithy toves.\n\
1267                    Did gyre and gimble in the wabe;\n\
1268                    All mimsy were the borogoves,\n\
1269                    And the mome raths outgrabe.\n"
1270         );
1271         Ok(())
1272     }
1273 
1274     #[test]
1275     fn p2_cli_file_dir_sync() -> Result<()> {
1276         let dir = tempfile::tempdir()?;
1277 
1278         std::fs::File::create(dir.path().join("bar.txt"))?
1279             .write_all(b"'Twas brillig, and the slithy toves.\n")?;
1280 
1281         run_wasmtime(&[
1282             "run",
1283             "-Wcomponent-model",
1284             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1285             P2_CLI_FILE_DIR_SYNC_COMPONENT,
1286         ])?;
1287 
1288         Ok(())
1289     }
1290 
1291     #[test]
1292     fn p2_cli_exit_success() -> Result<()> {
1293         run_wasmtime(&["run", "-Wcomponent-model", P2_CLI_EXIT_SUCCESS_COMPONENT])?;
1294         Ok(())
1295     }
1296 
1297     #[test]
1298     fn p2_cli_exit_default() -> Result<()> {
1299         run_wasmtime(&["run", "-Wcomponent-model", P2_CLI_EXIT_DEFAULT_COMPONENT])?;
1300         Ok(())
1301     }
1302 
1303     #[test]
1304     fn p2_cli_exit_failure() -> Result<()> {
1305         let output = get_wasmtime_command()?
1306             .args(&["run", "-Wcomponent-model", P2_CLI_EXIT_FAILURE_COMPONENT])
1307             .output()?;
1308         assert!(!output.status.success());
1309         assert_eq!(output.status.code(), Some(1));
1310         Ok(())
1311     }
1312 
1313     #[test]
1314     fn p2_cli_exit_with_code() -> Result<()> {
1315         let output = get_wasmtime_command()?
1316             .args(&[
1317                 "run",
1318                 "-Wcomponent-model",
1319                 "-Scli-exit-with-code",
1320                 P2_CLI_EXIT_WITH_CODE_COMPONENT,
1321             ])
1322             .output()?;
1323         assert!(!output.status.success());
1324         assert_eq!(output.status.code(), Some(42));
1325         Ok(())
1326     }
1327 
1328     #[test]
1329     fn p2_cli_exit_panic() -> Result<()> {
1330         let output = get_wasmtime_command()?
1331             .args(&["run", "-Wcomponent-model", P2_CLI_EXIT_PANIC_COMPONENT])
1332             .output()?;
1333         assert!(!output.status.success());
1334         let stderr = String::from_utf8_lossy(&output.stderr);
1335         assert!(stderr.contains("Curiouser and curiouser!"));
1336         Ok(())
1337     }
1338 
1339     #[test]
1340     fn p2_cli_directory_list() -> Result<()> {
1341         let dir = tempfile::tempdir()?;
1342 
1343         std::fs::File::create(dir.path().join("foo.txt"))?;
1344         std::fs::File::create(dir.path().join("bar.txt"))?;
1345         std::fs::File::create(dir.path().join("baz.txt"))?;
1346         std::fs::create_dir(dir.path().join("sub"))?;
1347         std::fs::File::create(dir.path().join("sub").join("wow.txt"))?;
1348         std::fs::File::create(dir.path().join("sub").join("yay.txt"))?;
1349 
1350         run_wasmtime(&[
1351             "run",
1352             "-Wcomponent-model",
1353             &format!("--dir={}::/", dir.path().to_str().unwrap()),
1354             P2_CLI_DIRECTORY_LIST_COMPONENT,
1355         ])?;
1356         Ok(())
1357     }
1358 
1359     #[test]
1360     fn p2_cli_default_clocks() -> Result<()> {
1361         run_wasmtime(&["run", "-Wcomponent-model", P2_CLI_DEFAULT_CLOCKS_COMPONENT])?;
1362         Ok(())
1363     }
1364 
1365     #[test]
1366     fn p2_cli_export_cabi_realloc() -> Result<()> {
1367         run_wasmtime(&[
1368             "run",
1369             "-Wcomponent-model",
1370             P2_CLI_EXPORT_CABI_REALLOC_COMPONENT,
1371         ])?;
1372         Ok(())
1373     }
1374 
1375     #[test]
1376     fn run_wasi_http_component() -> Result<()> {
1377         let output = super::run_wasmtime_for_output(
1378             &[
1379                 "-Ccache=no",
1380                 "-Wcomponent-model",
1381                 "-Scli,http,preview2",
1382                 P2_HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT,
1383             ],
1384             None,
1385         )?;
1386         println!("{}", String::from_utf8_lossy(&output.stderr));
1387         let stdout = String::from_utf8_lossy(&output.stdout);
1388         println!("{stdout}");
1389         assert!(stdout.starts_with("Called _start\n"));
1390         assert!(stdout.ends_with("Done\n"));
1391         assert!(output.status.success());
1392         Ok(())
1393     }
1394 
1395     // Test to ensure that prints in the guest aren't buffered on the host by
1396     // accident. The test here will print something without a newline and then
1397     // wait for input on stdin, and the test here is to ensure that the
1398     // character shows up here even as the guest is waiting on input via stdin.
1399     #[test]
1400     fn p2_cli_stdio_write_flushes() -> Result<()> {
1401         fn run(args: &[&str]) -> Result<()> {
1402             println!("running {args:?}");
1403             let mut child = get_wasmtime_command()?
1404                 .args(args)
1405                 .stdin(Stdio::piped())
1406                 .stdout(Stdio::piped())
1407                 .spawn()?;
1408             let mut stdout = child.stdout.take().unwrap();
1409             let mut buf = [0; 10];
1410             match stdout.read(&mut buf) {
1411                 Ok(2) => assert_eq!(&buf[..2], b"> "),
1412                 e => panic!("unexpected read result {e:?}"),
1413             }
1414             drop(stdout);
1415             drop(child.stdin.take().unwrap());
1416             let status = child.wait()?;
1417             assert!(status.success());
1418             Ok(())
1419         }
1420 
1421         run(&["run", "-Spreview2=n", P2_CLI_STDIO_WRITE_FLUSHES])?;
1422         run(&["run", "-Spreview2=y", P2_CLI_STDIO_WRITE_FLUSHES])?;
1423         run(&[
1424             "run",
1425             "-Wcomponent-model",
1426             P2_CLI_STDIO_WRITE_FLUSHES_COMPONENT,
1427         ])?;
1428         Ok(())
1429     }
1430 
1431     #[test]
1432     fn p2_cli_no_tcp() -> Result<()> {
1433         let output = super::run_wasmtime_for_output(
1434             &[
1435                 "-Wcomponent-model",
1436                 // Turn on network but turn off TCP
1437                 "-Sinherit-network,tcp=no",
1438                 P2_CLI_NO_TCP_COMPONENT,
1439             ],
1440             None,
1441         )?;
1442         println!("{}", String::from_utf8_lossy(&output.stderr));
1443         assert!(output.status.success());
1444         Ok(())
1445     }
1446 
1447     #[test]
1448     fn p2_cli_no_udp() -> Result<()> {
1449         let output = super::run_wasmtime_for_output(
1450             &[
1451                 "-Wcomponent-model",
1452                 // Turn on network but turn off UDP
1453                 "-Sinherit-network,udp=no",
1454                 P2_CLI_NO_UDP_COMPONENT,
1455             ],
1456             None,
1457         )?;
1458         println!("{}", String::from_utf8_lossy(&output.stderr));
1459         assert!(output.status.success());
1460         Ok(())
1461     }
1462 
1463     #[test]
1464     fn p2_cli_no_ip_name_lookup() -> Result<()> {
1465         let output = super::run_wasmtime_for_output(
1466             &[
1467                 "-Wcomponent-model",
1468                 // Turn on network but ensure name lookup is disabled
1469                 "-Sinherit-network,allow-ip-name-lookup=no",
1470                 P2_CLI_NO_IP_NAME_LOOKUP_COMPONENT,
1471             ],
1472             None,
1473         )?;
1474         println!("{}", String::from_utf8_lossy(&output.stderr));
1475         assert!(output.status.success());
1476         Ok(())
1477     }
1478 
1479     #[test]
1480     fn p2_cli_sleep() -> Result<()> {
1481         run_wasmtime(&["run", P2_CLI_SLEEP])?;
1482         run_wasmtime(&["run", P2_CLI_SLEEP_COMPONENT])?;
1483         Ok(())
1484     }
1485 
1486     #[test]
1487     fn p2_cli_sleep_forever() -> Result<()> {
1488         for timeout in [
1489             // Tests still pass when we race with going to sleep.
1490             "-Wtimeout=1ns",
1491             // Tests pass when we wait till the Wasm has (likely) gone to sleep.
1492             "-Wtimeout=250ms",
1493         ] {
1494             let e = run_wasmtime(&["run", timeout, P2_CLI_SLEEP_FOREVER]).unwrap_err();
1495             let e = e.to_string();
1496             println!("Got error: {e}");
1497             assert!(e.contains("interrupt"));
1498 
1499             let e = run_wasmtime(&["run", timeout, P2_CLI_SLEEP_FOREVER_COMPONENT]).unwrap_err();
1500             let e = e.to_string();
1501             println!("Got error: {e}");
1502             assert!(e.contains("interrupt"));
1503         }
1504 
1505         Ok(())
1506     }
1507 
1508     /// Helper structure to manage an invocation of `wasmtime serve`
1509     struct WasmtimeServe {
1510         child: Option<Child>,
1511         stdout: Option<JoinHandle<io::Result<Vec<u8>>>>,
1512         stderr: Option<JoinHandle<io::Result<Vec<u8>>>>,
1513         addr: SocketAddr,
1514         shutdown_addr: SocketAddr,
1515     }
1516 
1517     impl WasmtimeServe {
1518         /// Creates a new server which will serve the wasm component pointed to
1519         /// by `wasm`.
1520         ///
1521         /// A `configure` callback is provided to specify how `wasmtime serve`
1522         /// will be invoked and configure arguments such as headers.
1523         fn new(wasm: &str, configure: impl FnOnce(&mut Command)) -> Result<WasmtimeServe> {
1524             // Spawn `wasmtime serve` on port 0 which will randomly assign it a
1525             // port.
1526             let mut cmd = super::get_wasmtime_command()?;
1527             cmd.arg("serve").arg("--addr=127.0.0.1:0").arg(wasm);
1528             configure(&mut cmd);
1529             Self::spawn(&mut cmd)
1530         }
1531 
1532         fn spawn(cmd: &mut Command) -> Result<WasmtimeServe> {
1533             cmd.arg("--shutdown-addr=127.0.0.1:0");
1534             cmd.stdin(Stdio::null());
1535             cmd.stdout(Stdio::piped());
1536             cmd.stderr(Stdio::piped());
1537             let mut child = cmd.spawn()?;
1538 
1539             // Read the first few lines of stderr which will say which address
1540             // it's listening on. The first line is the shutdown line (with
1541             // `--shutdown-addr`) and the second is what `--addr` was bound to.
1542             // This is done to figure out what `:0` was bound to in the child
1543             // process.
1544             let mut line = String::new();
1545             let mut stderr = BufReader::new(child.stderr.take().unwrap());
1546             let mut read_addr_from_line = |prefix: &str| -> Result<SocketAddr> {
1547                 stderr.read_line(&mut line)?;
1548 
1549                 if !line.starts_with(prefix) {
1550                     bail!("input line `{line}` didn't start with `{prefix}`");
1551                 }
1552                 match line.find("127.0.0.1").and_then(|addr_start| {
1553                     let addr = &line[addr_start..];
1554                     let addr_end = addr.find("/")?;
1555                     addr[..addr_end].parse().ok()
1556                 }) {
1557                     Some(addr) => {
1558                         line.truncate(0);
1559                         Ok(addr)
1560                     }
1561                     None => bail!("failed to address from: {line}"),
1562                 }
1563             };
1564             let shutdown_addr = read_addr_from_line("Listening for shutdown");
1565             let addr = read_addr_from_line("Serving HTTP on");
1566             let (shutdown_addr, addr) = match (shutdown_addr, addr) {
1567                 (Ok(a), Ok(b)) => (a, b),
1568                 // If either failed kill the child and otherwise try to shepherd
1569                 // along any contextual information we have.
1570                 (Err(a), _) | (_, Err(a)) => {
1571                     child.kill()?;
1572                     child.wait()?;
1573                     stderr.read_to_string(&mut line)?;
1574                     return Err(a.context(line));
1575                 }
1576             };
1577             let mut stdout = child.stdout.take().unwrap();
1578             Ok(WasmtimeServe {
1579                 stdout: Some(thread::spawn(move || {
1580                     let mut dst = Vec::new();
1581                     stdout.read_to_end(&mut dst).map(|_| dst)
1582                 })),
1583 
1584                 stderr: Some(thread::spawn(move || {
1585                     let mut dst = Vec::new();
1586                     stderr.read_to_end(&mut dst).map(|_| dst)
1587                 })),
1588 
1589                 child: Some(child),
1590                 addr,
1591                 shutdown_addr,
1592             })
1593         }
1594 
1595         /// Completes this server gracefully by printing the output on failure.
1596         fn finish(mut self) -> Result<(String, String)> {
1597             self._finish()
1598         }
1599 
1600         fn _finish(&mut self) -> Result<(String, String)> {
1601             let mut child = self.child.take().unwrap();
1602 
1603             // If the child process has already exited, then great! Otherwise
1604             // the server is still running and it shouldn't be possible to exit
1605             // until a shutdown signal is sent, so do that here. Make a TCP
1606             // connection to the shutdown port which is used as a shutdown
1607             // signal.
1608             if child.try_wait()?.is_none() {
1609                 std::net::TcpStream::connect(&self.shutdown_addr)
1610                     .context("failed to initiate graceful shutdown")?;
1611             }
1612 
1613             // Regardless of whether we just shut the server down or whether it
1614             // was already shut down (e.g. panicked or similar), wait for the
1615             // result here. The result should succeed (e.g. 0 exit status), and
1616             // if it did then the stdout/stderr are the caller's problem.
1617             let mut output = child.wait_with_output()?;
1618             output.stdout = self.stdout.take().unwrap().join().unwrap()?;
1619             output.stderr = self.stderr.take().unwrap().join().unwrap()?;
1620             if !output.status.success() {
1621                 bail!("child failed {output:?}");
1622             }
1623 
1624             Ok((
1625                 String::from_utf8_lossy(&output.stdout).into_owned(),
1626                 String::from_utf8_lossy(&output.stderr).into_owned(),
1627             ))
1628         }
1629 
1630         /// Send a request to this server and wait for the response.
1631         async fn send_request(&self, req: http::Request<String>) -> Result<http::Response<String>> {
1632             let (mut send, conn_task) = self.start_requests().await?;
1633 
1634             let response = Self::send_request_with(&mut send, req).await?;
1635             drop(send);
1636 
1637             conn_task.await??;
1638 
1639             Ok(response)
1640         }
1641 
1642         async fn send_request_with(
1643             send: &mut hyper::client::conn::http1::SendRequest<String>,
1644             req: http::Request<String>,
1645         ) -> Result<http::Response<String>> {
1646             let response = send
1647                 .send_request(req)
1648                 .await
1649                 .context("error sending request")?;
1650 
1651             let (parts, body) = response.into_parts();
1652 
1653             let body = body.collect().await.context("failed to read body")?;
1654             assert!(body.trailers().is_none());
1655             let body = std::str::from_utf8(&body.to_bytes())?.to_string();
1656 
1657             Ok(http::Response::from_parts(parts, body))
1658         }
1659 
1660         async fn start_requests(
1661             &self,
1662         ) -> Result<(
1663             hyper::client::conn::http1::SendRequest<String>,
1664             tokio::task::JoinHandle<hyper::Result<()>>,
1665         )> {
1666             let tcp = TcpStream::connect(&self.addr)
1667                 .await
1668                 .context("failed to connect")?;
1669             let tcp = wasmtime_wasi_http::io::TokioIo::new(tcp);
1670             let (send, conn) = hyper::client::conn::http1::handshake(tcp)
1671                 .await
1672                 .context("failed http handshake")?;
1673             Ok((send, tokio::task::spawn(conn)))
1674         }
1675     }
1676 
1677     // Don't leave child processes running by accident so kill the child process
1678     // if our server goes away.
1679     impl Drop for WasmtimeServe {
1680         fn drop(&mut self) {
1681             match &mut self.child {
1682                 Some(child) => match child.kill() {
1683                     Ok(()) => {}
1684                     Err(e) => {
1685                         eprintln!("failed to kill child process {e}");
1686                         return;
1687                     }
1688                 },
1689                 None => return,
1690             }
1691             match self._finish() {
1692                 Ok((stdout, stderr)) => {
1693                     if !stdout.is_empty() {
1694                         println!("server stdout:\n{stdout}");
1695                     }
1696                     if !stderr.is_empty() {
1697                         println!("server stderr:\n{stderr}");
1698                     }
1699                 }
1700                 Err(e) => println!("failed to wait for child or read stdio: {e}"),
1701             }
1702         }
1703     }
1704 
1705     #[tokio::test]
1706     async fn p2_cli_serve_echo_env() -> Result<()> {
1707         let server = WasmtimeServe::new(P2_CLI_SERVE_ECHO_ENV_COMPONENT, |cmd| {
1708             cmd.arg("--env=FOO=bar");
1709             cmd.arg("--env=BAR");
1710             cmd.arg("-Scli");
1711             cmd.env_remove("BAR");
1712         })?;
1713 
1714         let foo_env = server
1715             .send_request(
1716                 hyper::Request::builder()
1717                     .uri("http://localhost/")
1718                     .header("env", "FOO")
1719                     .body(String::new())
1720                     .context("failed to make request")?,
1721             )
1722             .await?;
1723 
1724         assert!(foo_env.status().is_success());
1725         assert!(foo_env.body().is_empty());
1726         let headers = foo_env.headers();
1727         assert_eq!(headers.get("env"), Some(&HeaderValue::from_static("bar")));
1728 
1729         let bar_env = server
1730             .send_request(
1731                 hyper::Request::builder()
1732                     .uri("http://localhost/")
1733                     .header("env", "BAR")
1734                     .body(String::new())
1735                     .context("failed to make request")?,
1736             )
1737             .await?;
1738 
1739         assert!(bar_env.status().is_success());
1740         assert!(bar_env.body().is_empty());
1741         let headers = bar_env.headers();
1742         assert_eq!(headers.get("env"), None);
1743 
1744         server.finish()?;
1745         Ok(())
1746     }
1747 
1748     #[tokio::test]
1749     async fn p2_cli_serve_outgoing_body_config() -> Result<()> {
1750         let server = WasmtimeServe::new(P2_CLI_SERVE_ECHO_ENV_COMPONENT, |cmd| {
1751             cmd.arg("-Scli");
1752             cmd.arg("-Shttp-outgoing-body-buffer-chunks=2");
1753             cmd.arg("-Shttp-outgoing-body-chunk-size=1024");
1754         })?;
1755 
1756         let resp = server
1757             .send_request(
1758                 hyper::Request::builder()
1759                     .uri("http://localhost/")
1760                     .header("env", "FOO")
1761                     .body(String::new())
1762                     .context("failed to make request")?,
1763             )
1764             .await?;
1765 
1766         assert!(resp.status().is_success());
1767 
1768         server.finish()?;
1769         Ok(())
1770     }
1771 
1772     #[tokio::test]
1773     #[ignore] // TODO: printing stderr in the child and killing the child at the
1774     // end of this test race so the stderr may be present or not. Need
1775     // to implement a more graceful shutdown routine for `wasmtime
1776     // serve`.
1777     async fn p2_cli_serve_respect_pooling_options() -> Result<()> {
1778         let server = WasmtimeServe::new(P2_CLI_SERVE_ECHO_ENV_COMPONENT, |cmd| {
1779             cmd.arg("-Opooling-total-memories=0").arg("-Scli");
1780         })?;
1781 
1782         let result = server
1783             .send_request(
1784                 hyper::Request::builder()
1785                     .uri("http://localhost/")
1786                     .header("env", "FOO")
1787                     .body(String::new())
1788                     .context("failed to make request")?,
1789             )
1790             .await;
1791         assert!(result.is_err());
1792         let (_, stderr) = server.finish()?;
1793         assert!(
1794             stderr.contains("maximum concurrent memory limit of 0 reached"),
1795             "bad stderr: {stderr}",
1796         );
1797         Ok(())
1798     }
1799 
1800     #[test]
1801     fn p2_cli_large_env() -> Result<()> {
1802         for wasm in [P2_CLI_LARGE_ENV, P2_CLI_LARGE_ENV_COMPONENT] {
1803             println!("run {wasm:?}");
1804             let mut cmd = get_wasmtime_command()?;
1805             cmd.arg("run").arg("-Sinherit-env").arg(wasm);
1806 
1807             let debug_cmd = format!("{cmd:?}");
1808             for i in 0..512 {
1809                 let var = format!("KEY{i}");
1810                 let val = (0..1024).map(|_| 'x').collect::<String>();
1811                 cmd.env(&var, &val);
1812             }
1813             let output = cmd.output()?;
1814             if !output.status.success() {
1815                 bail!(
1816                     "Failed to execute wasmtime with: {debug_cmd}\n{}",
1817                     String::from_utf8_lossy(&output.stderr)
1818                 );
1819             }
1820         }
1821         Ok(())
1822     }
1823 
1824     #[tokio::test]
1825     async fn p2_cli_serve_only_one_process_allowed() -> Result<()> {
1826         let wasm = P2_CLI_SERVE_ECHO_ENV_COMPONENT;
1827         let server = WasmtimeServe::new(wasm, |cmd| {
1828             cmd.arg("-Scli");
1829         })?;
1830 
1831         let err = WasmtimeServe::spawn(
1832             super::get_wasmtime_command()?
1833                 .arg("serve")
1834                 .arg("-Scli")
1835                 .arg(format!("--addr={}", server.addr))
1836                 .arg(wasm),
1837         )
1838         .err()
1839         .expect("server spawn should have failed but it succeeded");
1840         drop(server);
1841 
1842         let err = format!("{err:?}");
1843         println!("{err}");
1844         assert!(err.contains("os error"));
1845         Ok(())
1846     }
1847 
1848     // Technically this test is a little racy. This binds port 0 to acquire a
1849     // random port, issues a single request to this port, but then kills this
1850     // server while the request is still processing. The port is then rebound
1851     // in the next process while it technically could be stolen by another
1852     // process.
1853     #[tokio::test]
1854     async fn p2_cli_serve_quick_rebind_allowed() -> Result<()> {
1855         let wasm = P2_CLI_SERVE_ECHO_ENV_COMPONENT;
1856         let server = WasmtimeServe::new(wasm, |cmd| {
1857             cmd.arg("-Scli");
1858         })?;
1859         let addr = server.addr;
1860 
1861         // Start up a `send` and `conn_task` which represents a connection to
1862         // this server.
1863         let (mut send, conn_task) = server.start_requests().await?;
1864         let _ = send
1865             .send_request(
1866                 hyper::Request::builder()
1867                     .uri("http://localhost/")
1868                     .header("env", "FOO")
1869                     .body(String::new())
1870                     .context("failed to make request")?,
1871             )
1872             .await;
1873 
1874         // ... once a response has been received (or at least the status
1875         // code/headers) then kill the server. THis is done while `conn_task`
1876         // and `send` are still alive so we're guaranteed that the other side
1877         // got a request (we got a response) and our connection is still open.
1878         //
1879         // This forces the address/port into the `TIME_WAIT` state. The rebind
1880         // below in the next process will fail if `SO_REUSEADDR` isn't set.
1881         drop(server);
1882         drop(send);
1883         let _ = conn_task.await;
1884 
1885         // If this is successfully bound then we'll create `WasmtimeServe`
1886         // which reads off the first line of output to know which address was
1887         // bound.
1888         let _server2 = WasmtimeServe::spawn(
1889             super::get_wasmtime_command()?
1890                 .arg("serve")
1891                 .arg("-Scli")
1892                 .arg(format!("--addr={addr}"))
1893                 .arg(wasm),
1894         )?;
1895 
1896         Ok(())
1897     }
1898 
1899     #[tokio::test]
1900     async fn p2_cli_serve_with_print() -> Result<()> {
1901         let server = WasmtimeServe::new(P2_CLI_SERVE_WITH_PRINT_COMPONENT, |cmd| {
1902             cmd.arg("-Scli");
1903         })?;
1904 
1905         for _ in 0..2 {
1906             let resp = server
1907                 .send_request(
1908                     hyper::Request::builder()
1909                         .uri("http://localhost/")
1910                         .body(String::new())
1911                         .context("failed to make request")?,
1912                 )
1913                 .await?;
1914             assert!(resp.status().is_success());
1915         }
1916 
1917         let (out, err) = server.finish()?;
1918         assert_eq!(
1919             out,
1920             "\
1921 stdout [0] :: this is half a print to stdout
1922 stdout [0] :: \n\
1923 stdout [0] :: after empty
1924 stdout [1] :: this is half a print to stdout
1925 stdout [1] :: \n\
1926 stdout [1] :: after empty
1927 "
1928         );
1929         assert!(
1930             err.contains(
1931                 "\
1932 stderr [0] :: this is half a print to stderr
1933 stderr [0] :: \n\
1934 stderr [0] :: after empty
1935 stderr [0] :: start a print 1234
1936 stderr [1] :: this is half a print to stderr
1937 stderr [1] :: \n\
1938 stderr [1] :: after empty
1939 stderr [1] :: start a print 1234
1940 "
1941             ),
1942             "bad stderr: {err}"
1943         );
1944 
1945         Ok(())
1946     }
1947 
1948     #[tokio::test]
1949     async fn p2_cli_serve_with_print_no_prefix() -> Result<()> {
1950         let server = WasmtimeServe::new(P2_CLI_SERVE_WITH_PRINT_COMPONENT, |cmd| {
1951             cmd.arg("-Scli");
1952             cmd.arg("--no-logging-prefix");
1953         })?;
1954 
1955         for _ in 0..2 {
1956             let resp = server
1957                 .send_request(
1958                     hyper::Request::builder()
1959                         .uri("http://localhost/")
1960                         .body(String::new())
1961                         .context("failed to make request")?,
1962                 )
1963                 .await?;
1964             assert!(resp.status().is_success());
1965         }
1966 
1967         let (out, err) = server.finish()?;
1968         assert_eq!(
1969             out,
1970             "\
1971 this is half a print to stdout
1972 \n\
1973 after empty
1974 this is half a print to stdout
1975 \n\
1976 after empty
1977 "
1978         );
1979         assert!(
1980             err.contains(
1981                 "\
1982 this is half a print to stderr
1983 \n\
1984 after empty
1985 start a print 1234
1986 this is half a print to stderr
1987 \n\
1988 after empty
1989 start a print 1234
1990 "
1991             ),
1992             "bad stderr {err}",
1993         );
1994 
1995         Ok(())
1996     }
1997 
1998     #[tokio::test]
1999     async fn p2_cli_serve_authority_and_scheme() -> Result<()> {
2000         let server = WasmtimeServe::new(P2_CLI_SERVE_AUTHORITY_AND_SCHEME_COMPONENT, |cmd| {
2001             cmd.arg("-Scli");
2002         })?;
2003 
2004         let resp = server
2005             .send_request(
2006                 hyper::Request::builder()
2007                     .uri("/")
2008                     .header("Host", "localhost")
2009                     .body(String::new())
2010                     .context("failed to make request")?,
2011             )
2012             .await?;
2013         assert!(resp.status().is_success());
2014 
2015         let resp = server
2016             .send_request(
2017                 hyper::Request::builder()
2018                     .method("CONNECT")
2019                     .uri("http://localhost/")
2020                     .body(String::new())
2021                     .context("failed to make request")?,
2022             )
2023             .await?;
2024         assert!(resp.status().is_success());
2025 
2026         Ok(())
2027     }
2028 
2029     #[test]
2030     fn p2_cli_argv0() -> Result<()> {
2031         run_wasmtime(&["run", "--argv0=a", P2_CLI_ARGV0, "a"])?;
2032         run_wasmtime(&["run", "--argv0=b", P2_CLI_ARGV0_COMPONENT, "b"])?;
2033         run_wasmtime(&["run", "--argv0=foo.wasm", P2_CLI_ARGV0, "foo.wasm"])?;
2034         Ok(())
2035     }
2036 
2037     #[tokio::test]
2038     async fn p2_cli_serve_config() -> Result<()> {
2039         let server = WasmtimeServe::new(P2_CLI_SERVE_CONFIG_COMPONENT, |cmd| {
2040             cmd.arg("-Scli");
2041             cmd.arg("-Sconfig");
2042             cmd.arg("-Sconfig-var=hello=world");
2043         })?;
2044 
2045         let resp = server
2046             .send_request(
2047                 hyper::Request::builder()
2048                     .uri("http://localhost/")
2049                     .body(String::new())
2050                     .context("failed to make request")?,
2051             )
2052             .await?;
2053 
2054         assert!(resp.status().is_success());
2055         assert_eq!(resp.body(), "world");
2056         Ok(())
2057     }
2058 
2059     #[test]
2060     fn p2_cli_config() -> Result<()> {
2061         run_wasmtime(&[
2062             "run",
2063             "-Sconfig",
2064             "-Sconfig-var=hello=world",
2065             CONFIG_GET_COMPONENT,
2066         ])?;
2067         Ok(())
2068     }
2069 
2070     #[tokio::test]
2071     async fn p2_cli_serve_keyvalue() -> Result<()> {
2072         let server = WasmtimeServe::new(P2_CLI_SERVE_KEYVALUE_COMPONENT, |cmd| {
2073             cmd.arg("-Scli");
2074             cmd.arg("-Skeyvalue");
2075             cmd.arg("-Skeyvalue-in-memory-data=hello=world");
2076         })?;
2077 
2078         let resp = server
2079             .send_request(
2080                 hyper::Request::builder()
2081                     .uri("http://localhost/")
2082                     .body(String::new())
2083                     .context("failed to make request")?,
2084             )
2085             .await?;
2086 
2087         assert!(resp.status().is_success());
2088         assert_eq!(resp.body(), "world");
2089         Ok(())
2090     }
2091 
2092     #[test]
2093     fn p2_cli_keyvalue() -> Result<()> {
2094         run_wasmtime(&[
2095             "run",
2096             "-Skeyvalue",
2097             "-Skeyvalue-in-memory-data=atomics_key=5",
2098             KEYVALUE_MAIN_COMPONENT,
2099         ])?;
2100         Ok(())
2101     }
2102 
2103     #[test]
2104     fn p2_cli_multiple_preopens() -> Result<()> {
2105         run_wasmtime(&[
2106             "run",
2107             "--dir=/::/a",
2108             "--dir=/::/b",
2109             "--dir=/::/c",
2110             P2_CLI_MULTIPLE_PREOPENS_COMPONENT,
2111         ])?;
2112         Ok(())
2113     }
2114 
2115     async fn p2_cli_serve_guest_never_invoked_set(wasm: &str) -> Result<()> {
2116         let server = WasmtimeServe::new(wasm, |cmd| {
2117             cmd.arg("-Scli");
2118         })?;
2119 
2120         for _ in 0..2 {
2121             let res = server
2122                 .send_request(
2123                     hyper::Request::builder()
2124                         .uri("http://localhost/")
2125                         .body(String::new())
2126                         .context("failed to make request")?,
2127                 )
2128                 .await
2129                 .expect("got response from wasmtime");
2130             assert_eq!(res.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
2131         }
2132 
2133         let (stdout, stderr) = server.finish()?;
2134         println!("stdout: {stdout}");
2135         println!("stderr: {stderr}");
2136         assert!(stderr.contains("guest never invoked `response-outparam::set` method"));
2137         assert!(!stderr.contains("panicked"));
2138         Ok(())
2139     }
2140 
2141     #[tokio::test]
2142     async fn p2_cli_serve_return_before_set() -> Result<()> {
2143         p2_cli_serve_guest_never_invoked_set(P2_CLI_SERVE_RETURN_BEFORE_SET_COMPONENT).await
2144     }
2145 
2146     #[tokio::test]
2147     async fn p2_cli_serve_trap_before_set() -> Result<()> {
2148         p2_cli_serve_guest_never_invoked_set(P2_CLI_SERVE_TRAP_BEFORE_SET_COMPONENT).await
2149     }
2150 
2151     #[test]
2152     fn p2_cli_p3_hello_stdout() -> Result<()> {
2153         let output = run_wasmtime(&[
2154             "run",
2155             "-Wcomponent-model-async",
2156             "-Sp3",
2157             P3_CLI_HELLO_STDOUT_COMPONENT,
2158         ]);
2159         if cfg!(feature = "component-model-async") {
2160             let output = output?;
2161             assert_eq!(output, "hello, world\n");
2162         } else {
2163             assert!(output.is_err());
2164         }
2165         Ok(())
2166     }
2167 
2168     #[test]
2169     fn p2_cli_p3_hello_stdout_post_return() -> Result<()> {
2170         let output = run_wasmtime(&[
2171             "run",
2172             "-Wcomponent-model-async",
2173             "-Sp3",
2174             P3_CLI_HELLO_STDOUT_POST_RETURN_COMPONENT,
2175         ]);
2176         if cfg!(feature = "component-model-async") {
2177             let output = output?;
2178             assert_eq!(output, "hello, world\nhello again, after return\n");
2179         } else {
2180             assert!(output.is_err());
2181         }
2182         Ok(())
2183     }
2184 
2185     mod invoke {
2186         use super::*;
2187 
2188         #[test]
2189         fn p2_cli_hello_stdout() -> Result<()> {
2190             println!("{P2_CLI_HELLO_STDOUT_COMPONENT}");
2191             let output = run_wasmtime(&[
2192                 "run",
2193                 "-Wcomponent-model",
2194                 "--invoke",
2195                 "run()",
2196                 P2_CLI_HELLO_STDOUT_COMPONENT,
2197             ])?;
2198             // First this component prints "hello, world", then the invoke
2199             // result is printed as "ok".
2200             assert_eq!(output, "hello, world\nok\n");
2201             Ok(())
2202         }
2203     }
2204 
2205     #[test]
2206     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2207     fn p2_cli_invoke_async() -> Result<()> {
2208         let output = run_wasmtime(&[
2209             "run",
2210             "-Wcomponent-model-async",
2211             "--invoke",
2212             "echo(\"hello?\")",
2213             P2_CLI_INVOKE_ASYNC_COMPONENT,
2214         ])?;
2215         assert_eq!(output, "\"hello?\"\n");
2216         Ok(())
2217     }
2218 
2219     fn run_much_stdout(component: &str, extra_flags: &[&str]) -> Result<()> {
2220         let total_write_size = 1 << 18;
2221         let expected = iter::repeat('a').take(total_write_size).collect::<String>();
2222 
2223         for i in 10..15 {
2224             let string = iter::repeat('a').take(1 << i).collect::<String>();
2225             let times = (total_write_size >> i).to_string();
2226             println!("writing {} bytes {times} times", string.len());
2227 
2228             let mut args = Vec::new();
2229             args.push("run");
2230             args.extend_from_slice(extra_flags);
2231             args.push(component);
2232             args.push(&string);
2233             args.push(&times);
2234             let output = run_wasmtime(&args)?;
2235             println!(
2236                 "expected {} bytes, got {} bytes",
2237                 expected.len(),
2238                 output.len()
2239             );
2240             assert!(output == expected);
2241         }
2242 
2243         Ok(())
2244     }
2245 
2246     #[test]
2247     fn p1_cli_much_stdout() -> Result<()> {
2248         run_much_stdout(P1_CLI_MUCH_STDOUT_COMPONENT, &[])
2249     }
2250 
2251     #[test]
2252     fn p2_cli_much_stdout() -> Result<()> {
2253         run_much_stdout(P2_CLI_MUCH_STDOUT_COMPONENT, &[])
2254     }
2255 
2256     #[test]
2257     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2258     fn p3_cli_much_stdout() -> Result<()> {
2259         run_much_stdout(
2260             P3_CLI_MUCH_STDOUT_COMPONENT,
2261             &["-Wcomponent-model-async", "-Sp3"],
2262         )
2263     }
2264 
2265     #[tokio::test]
2266     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2267     async fn p3_cli_serve_hello_world() -> Result<()> {
2268         cli_serve_hello_world(P3_CLI_SERVE_HELLO_WORLD_COMPONENT, 1, 1, |cmd| {
2269             cmd.arg("-Wcomponent-model-async");
2270             cmd.arg("-Sp3,cli");
2271         })
2272         .await
2273     }
2274 
2275     #[tokio::test]
2276     async fn p2_cli_serve_hello_world() -> Result<()> {
2277         cli_serve_hello_world(P2_CLI_SERVE_HELLO_WORLD_COMPONENT, 1, 1, |cmd| {
2278             cmd.arg("-Scli");
2279         })
2280         .await
2281     }
2282 
2283     const CONNECTION_COUNT_MANY: usize = 20;
2284     const REQUESTS_PER_CONNECTION_MANY: usize = 5;
2285 
2286     #[tokio::test]
2287     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2288     async fn p3_cli_serve_hello_world_many() -> Result<()> {
2289         cli_serve_hello_world(
2290             P3_CLI_SERVE_HELLO_WORLD_COMPONENT,
2291             CONNECTION_COUNT_MANY,
2292             REQUESTS_PER_CONNECTION_MANY,
2293             |cmd| {
2294                 cmd.arg("-Wcomponent-model-async");
2295                 cmd.arg("-Sp3,cli");
2296             },
2297         )
2298         .await
2299     }
2300 
2301     #[tokio::test]
2302     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2303     async fn p3_cli_serve_hello_world_many_no_reuse() -> Result<()> {
2304         cli_serve_hello_world(
2305             P3_CLI_SERVE_HELLO_WORLD_COMPONENT,
2306             CONNECTION_COUNT_MANY,
2307             REQUESTS_PER_CONNECTION_MANY,
2308             |cmd| {
2309                 cmd.arg("-Wcomponent-model-async");
2310                 cmd.arg("-Sp3,cli");
2311                 cmd.arg("--max-instance-reuse-count=1");
2312             },
2313         )
2314         .await
2315     }
2316 
2317     #[tokio::test]
2318     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2319     async fn p3_cli_serve_hello_world_many_no_concurrent_reuse() -> Result<()> {
2320         cli_serve_hello_world(
2321             P3_CLI_SERVE_HELLO_WORLD_COMPONENT,
2322             CONNECTION_COUNT_MANY,
2323             REQUESTS_PER_CONNECTION_MANY,
2324             |cmd| {
2325                 cmd.arg("-Wcomponent-model-async");
2326                 cmd.arg("-Sp3,cli");
2327                 cmd.arg("--max-instance-concurrent-reuse-count=1");
2328             },
2329         )
2330         .await
2331     }
2332 
2333     #[tokio::test]
2334     async fn p2_cli_serve_hello_world_many() -> Result<()> {
2335         cli_serve_hello_world(
2336             P2_CLI_SERVE_HELLO_WORLD_COMPONENT,
2337             CONNECTION_COUNT_MANY,
2338             REQUESTS_PER_CONNECTION_MANY,
2339             |cmd| {
2340                 cmd.arg("-Scli");
2341             },
2342         )
2343         .await
2344     }
2345 
2346     #[tokio::test]
2347     async fn p2_cli_serve_hello_world_many_with_reuse() -> Result<()> {
2348         cli_serve_hello_world(
2349             P2_CLI_SERVE_HELLO_WORLD_COMPONENT,
2350             CONNECTION_COUNT_MANY,
2351             REQUESTS_PER_CONNECTION_MANY,
2352             |cmd| {
2353                 cmd.arg("-Scli");
2354                 cmd.arg("--max-instance-reuse-count=128");
2355             },
2356         )
2357         .await
2358     }
2359 
2360     async fn cli_serve_hello_world(
2361         component: &str,
2362         connection_count: usize,
2363         requests_per_connection: usize,
2364         configure: impl FnOnce(&mut Command),
2365     ) -> Result<()> {
2366         let server = std::sync::Arc::new(WasmtimeServe::new(component, configure)?);
2367 
2368         let tasks = (0..connection_count).map({
2369             let server = server.clone();
2370             move |_| {
2371                 tokio::task::spawn({
2372                     let server = server.clone();
2373                     async move {
2374                         let (mut send, conn_task) = server.start_requests().await?;
2375 
2376                         for _ in 0..requests_per_connection {
2377                             let result = WasmtimeServe::send_request_with(
2378                                 &mut send,
2379                                 hyper::Request::builder()
2380                                     .uri("http://localhost/")
2381                                     .body(String::new())
2382                                     .context("failed to make request")?,
2383                             )
2384                             .await?;
2385 
2386                             assert!(result.status().is_success());
2387                             assert_eq!(result.body(), "Hello, WASI!");
2388                         }
2389 
2390                         drop(send);
2391 
2392                         conn_task.await??;
2393 
2394                         anyhow::Ok(())
2395                     }
2396                 })
2397             }
2398         });
2399 
2400         for task in tasks {
2401             task.await??;
2402         }
2403 
2404         std::sync::Arc::into_inner(server).unwrap().finish()?;
2405         Ok(())
2406     }
2407 
2408     #[tokio::test]
2409     async fn p2_cli_serve_sleep() -> Result<()> {
2410         cli_serve_sleep(P2_CLI_SERVE_SLEEP_COMPONENT, 1, 1, |cmd| {
2411             cmd.arg("-Scli");
2412         })
2413         .await
2414     }
2415 
2416     #[tokio::test]
2417     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2418     async fn p3_cli_serve_sleep() -> Result<()> {
2419         cli_serve_sleep(P3_CLI_SERVE_SLEEP_COMPONENT, 1, 1, |cmd| {
2420             cmd.arg("-Wcomponent-model-async");
2421             cmd.arg("-Sp3,cli");
2422         })
2423         .await
2424     }
2425 
2426     #[tokio::test]
2427     async fn p2_cli_serve_sleep_many() -> Result<()> {
2428         cli_serve_sleep(
2429             P2_CLI_SERVE_SLEEP_COMPONENT,
2430             CONNECTION_COUNT_MANY,
2431             REQUESTS_PER_CONNECTION_MANY,
2432             |cmd| {
2433                 cmd.arg("-Scli");
2434             },
2435         )
2436         .await
2437     }
2438 
2439     #[tokio::test]
2440     #[cfg_attr(not(feature = "component-model-async"), ignore)]
2441     async fn p3_cli_serve_sleep_many() -> Result<()> {
2442         cli_serve_sleep(
2443             P3_CLI_SERVE_SLEEP_COMPONENT,
2444             CONNECTION_COUNT_MANY,
2445             REQUESTS_PER_CONNECTION_MANY,
2446             |cmd| {
2447                 cmd.arg("-Wcomponent-model-async");
2448                 cmd.arg("-Sp3,cli");
2449             },
2450         )
2451         .await
2452     }
2453 
2454     async fn cli_serve_sleep(
2455         component: &str,
2456         connection_count: usize,
2457         requests_per_connection: usize,
2458         configure: impl FnOnce(&mut Command),
2459     ) -> Result<()> {
2460         let server = std::sync::Arc::new(WasmtimeServe::new(component, move |cmd| {
2461             configure(cmd);
2462             cmd.arg("-Wtimeout=100us");
2463         })?);
2464 
2465         let tasks = (0..connection_count).map({
2466             let server = server.clone();
2467             move |_| {
2468                 tokio::task::spawn({
2469                     let server = server.clone();
2470                     async move {
2471                         let (mut send, conn_task) = server.start_requests().await?;
2472 
2473                         for _ in 0..requests_per_connection {
2474                             let result = WasmtimeServe::send_request_with(
2475                                 &mut send,
2476                                 hyper::Request::builder()
2477                                     .uri("http://localhost/")
2478                                     .body(String::new())
2479                                     .context("failed to make request")?,
2480                             )
2481                             .await?;
2482 
2483                             assert!(result.status().is_server_error());
2484                         }
2485 
2486                         drop(send);
2487 
2488                         conn_task.await??;
2489 
2490                         anyhow::Ok(())
2491                     }
2492                 })
2493             }
2494         });
2495 
2496         for task in tasks {
2497             task.await??;
2498         }
2499 
2500         let (stdout, stderr) = std::sync::Arc::into_inner(server).unwrap().finish()?;
2501         assert_eq!(stdout, "");
2502         assert!(stderr.contains("guest timed out"), "bad stderr: {stderr}");
2503         Ok(())
2504     }
2505 }
2506 
2507 #[test]
2508 fn settings_command() -> Result<()> {
2509     // Skip this test on platforms that Cranelift doesn't support.
2510     if cranelift_native::builder().is_err() {
2511         return Ok(());
2512     }
2513     let output = run_wasmtime(&["settings"])?;
2514     assert!(output.contains("Cranelift settings for target"));
2515     Ok(())
2516 }
2517 
2518 #[cfg(target_arch = "x86_64")]
2519 #[test]
2520 fn profile_with_vtune() -> Result<()> {
2521     if !is_vtune_available() {
2522         println!("> `vtune` is not available on the system path; skipping test");
2523         return Ok(());
2524     }
2525 
2526     let mut bin = Command::new("vtune");
2527     bin.args(&[
2528         // Configure VTune...
2529         "-verbose",
2530         "-collect",
2531         "hotspots",
2532         "-user-data-dir",
2533         &std::env::temp_dir().to_string_lossy(),
2534         // ...then run Wasmtime with profiling enabled:
2535         get_wasmtime_path(),
2536         "--profile=vtune",
2537         "tests/all/cli_tests/simple.wat",
2538     ]);
2539 
2540     println!("> executing: {bin:?}");
2541     let output = bin.output()?;
2542 
2543     let stdout = String::from_utf8_lossy(&output.stdout);
2544     let stderr = String::from_utf8_lossy(&output.stderr);
2545     println!("> stdout:\n{stdout}");
2546     println!("> stderr:\n{stderr}");
2547 
2548     assert!(output.status.success());
2549     assert!(!stderr.contains("Error"));
2550     assert!(stdout.contains("CPU Time"));
2551     Ok(())
2552 }
2553 
2554 #[cfg(target_arch = "x86_64")]
2555 fn is_vtune_available() -> bool {
2556     Command::new("vtune").arg("-version").output().is_ok()
2557 }
2558 
2559 #[test]
2560 fn profile_guest() -> Result<()> {
2561     let tmpdir = std::env::temp_dir();
2562     let dir = tmpdir.to_string_lossy();
2563 
2564     let output = run_wasmtime_for_output(
2565         &[
2566             &format!("--profile=guest,{dir}/out.json"),
2567             "--env",
2568             "FOO=bar",
2569             "tests/all/cli_tests/print_env.wat",
2570         ],
2571         None,
2572     )?;
2573 
2574     assert!(output.status.success());
2575     let stdout = String::from_utf8_lossy(&output.stdout);
2576     let stderr = String::from_utf8_lossy(&output.stderr);
2577     println!("> stdout:\n{stdout}");
2578     println!("> stderr:\n{stderr}");
2579     assert!(!stderr.contains("Error"));
2580     let out_json = std::fs::read_to_string(format!("{dir}/out.json")).unwrap();
2581     println!("> out.json:\n{out_json}");
2582     Ok(())
2583 }
2584 
2585 #[test]
2586 fn unreachable_without_wasi() -> Result<()> {
2587     let output = run_wasmtime_for_output(
2588         &[
2589             "-Scli=n",
2590             "-Ccache=n",
2591             "tests/all/cli_tests/unreachable.wat",
2592         ],
2593         None,
2594     )?;
2595 
2596     assert_ne!(output.stderr, b"");
2597     assert_eq!(output.stdout, b"");
2598     assert_trap_code(&output.status);
2599     Ok(())
2600 }
2601 
2602 #[test]
2603 fn config_cli_flag() -> Result<()> {
2604     let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
2605 
2606     // Test some valid TOML values
2607     let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2608     cfg.write_all(
2609         br#"
2610         [optimize]
2611         opt-level = 2
2612         signals-based-traps = false
2613 
2614         [codegen]
2615         collector = "null"
2616 
2617         [debug]
2618         address-map = true
2619 
2620         [wasm]
2621         max-wasm-stack = 65536
2622 
2623         [wasi]
2624         cli = true
2625         "#,
2626     )?;
2627     let output = run_wasmtime(&[
2628         "run",
2629         "--config",
2630         cfg_path.to_str().unwrap(),
2631         "--invoke",
2632         "get_f64",
2633         wasm.path().to_str().unwrap(),
2634     ])?;
2635     assert_eq!(output, "100\n");
2636 
2637     // Make sure CLI flags overrides TOML values
2638     let output = run_wasmtime(&[
2639         "run",
2640         "--config",
2641         cfg_path.to_str().unwrap(),
2642         "--invoke",
2643         "get_f64",
2644         "-W",
2645         "max-wasm-stack=0", // should override TOML value 65536 specified above and execution should fail
2646         wasm.path().to_str().unwrap(),
2647     ]);
2648     assert!(
2649         output
2650             .as_ref()
2651             .unwrap_err()
2652             .to_string()
2653             .contains("max_wasm_stack size cannot be zero"),
2654         "'{output:?}' did not contain expected error message",
2655     );
2656 
2657     // Test invalid TOML key
2658     let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2659     cfg.write_all(
2660         br#"
2661         [optimize]
2662         this-key-does-not-exist = true
2663         "#,
2664     )?;
2665     let output = run_wasmtime(&[
2666         "run",
2667         "--config",
2668         cfg_path.to_str().unwrap(),
2669         wasm.path().to_str().unwrap(),
2670     ]);
2671     assert!(
2672         output
2673             .as_ref()
2674             .unwrap_err()
2675             .to_string()
2676             .contains("unknown field `this-key-does-not-exist`"),
2677         "'{output:?}' did not contain expected error message"
2678     );
2679 
2680     // Test invalid TOML table
2681     let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2682     cfg.write_all(
2683         br#"
2684         [invalid_table]
2685         "#,
2686     )?;
2687     let output = run_wasmtime(&[
2688         "run",
2689         "--config",
2690         cfg_path.to_str().unwrap(),
2691         wasm.path().to_str().unwrap(),
2692     ]);
2693     assert!(
2694         output
2695             .as_ref()
2696             .unwrap_err()
2697             .to_string()
2698             .contains("unknown field `invalid_table`, expected one of `optimize`, `codegen`, `debug`, `wasm`, `wasi`"),
2699         "'{output:?}' did not contain expected error message",
2700     );
2701 
2702     Ok(())
2703 }
2704 
2705 #[test]
2706 fn invalid_subcommand() -> Result<()> {
2707     let output = run_wasmtime_for_output(&["invalid-subcommand"], None)?;
2708     dbg!(&output);
2709     assert!(!output.status.success());
2710     assert!(String::from_utf8_lossy(&output.stderr).contains("invalid-subcommand"));
2711     Ok(())
2712 }
2713 
2714 #[test]
2715 fn numeric_args() -> Result<()> {
2716     let wasm = build_wasm("tests/all/cli_tests/numeric_args.wat")?;
2717     // Test decimal i32
2718     let output = run_wasmtime_for_output(
2719         &[
2720             "run",
2721             "--invoke",
2722             "i32_test",
2723             wasm.path().to_str().unwrap(),
2724             "42",
2725         ],
2726         None,
2727     )?;
2728     assert_eq!(output.status.success(), true);
2729     assert_eq!(output.stdout, b"42\n");
2730     // Test hexadecimal i32 with lowercase prefix
2731     let output = run_wasmtime_for_output(
2732         &[
2733             "run",
2734             "--invoke",
2735             "i32_test",
2736             wasm.path().to_str().unwrap(),
2737             "0x2A",
2738         ],
2739         None,
2740     )?;
2741     assert_eq!(output.status.success(), true);
2742     assert_eq!(output.stdout, b"42\n");
2743     // Test hexadecimal i32 with uppercase prefix
2744     let output = run_wasmtime_for_output(
2745         &[
2746             "run",
2747             "--invoke",
2748             "i32_test",
2749             wasm.path().to_str().unwrap(),
2750             "0X2a",
2751         ],
2752         None,
2753     )?;
2754     assert_eq!(output.status.success(), true);
2755     assert_eq!(output.stdout, b"42\n");
2756     // Test that non-prefixed hex strings are not interpreted as hex
2757     let output = run_wasmtime_for_output(
2758         &[
2759             "run",
2760             "--invoke",
2761             "i32_test",
2762             wasm.path().to_str().unwrap(),
2763             "ff",
2764         ],
2765         None,
2766     )?;
2767     assert!(!output.status.success()); // Should fail as "ff" is not a valid decimal number
2768 
2769     // Test decimal i64
2770     let output = run_wasmtime_for_output(
2771         &[
2772             "run",
2773             "--invoke",
2774             "i64_test",
2775             wasm.path().to_str().unwrap(),
2776             "42",
2777         ],
2778         None,
2779     )?;
2780     assert_eq!(output.status.success(), true);
2781     assert_eq!(output.stdout, b"42\n");
2782     // Test hexadecimal i64
2783     let output = run_wasmtime_for_output(
2784         &[
2785             "run",
2786             "--invoke",
2787             "i64_test",
2788             wasm.path().to_str().unwrap(),
2789             "0x2A",
2790         ],
2791         None,
2792     )?;
2793     assert_eq!(output.status.success(), true);
2794     assert_eq!(output.stdout, b"42\n");
2795     Ok(())
2796 }
2797 
2798 #[test]
2799 fn compilation_logs() -> Result<()> {
2800     let temp = tempfile::NamedTempFile::new()?;
2801     let output = get_wasmtime_command()?
2802         .args(&[
2803             "compile",
2804             "-Wgc",
2805             "tests/all/cli_tests/issue-10353.wat",
2806             "--output",
2807             &temp.path().display().to_string(),
2808         ])
2809         .env("WASMTIME_LOG", "trace")
2810         .env("RUST_BACKTRACE", "1")
2811         .output()?;
2812     if !output.status.success() {
2813         println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
2814         println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
2815         panic!("wasmtime compilation failed when logs requested");
2816     }
2817     Ok(())
2818 }
2819 
2820 #[test]
2821 fn big_table_in_pooling_allocator() -> Result<()> {
2822     // Works by default
2823     run_wasmtime(&["tests/all/cli_tests/big_table.wat"])?;
2824 
2825     // Does not work by default in the pooling allocator, and the error message
2826     // should mention something about the pooling allocator.
2827     let output = run_wasmtime_for_output(
2828         &["-Opooling-allocator", "tests/all/cli_tests/big_table.wat"],
2829         None,
2830     )?;
2831     assert!(!output.status.success());
2832     println!("{}", String::from_utf8_lossy(&output.stderr));
2833     assert!(String::from_utf8_lossy(&output.stderr).contains("pooling allocator"));
2834 
2835     // Does work with `-Wmax-table-elements`
2836     run_wasmtime(&[
2837         "-Opooling-allocator",
2838         "-Wmax-table-elements=25000",
2839         "tests/all/cli_tests/big_table.wat",
2840     ])?;
2841     // Also works with `-Opooling-table-elements`
2842     run_wasmtime(&[
2843         "-Opooling-allocator",
2844         "-Opooling-table-elements=25000",
2845         "tests/all/cli_tests/big_table.wat",
2846     ])?;
2847     Ok(())
2848 }
2849 
2850 fn wizen(args: &[&str], wat: &str) -> Result<Output> {
2851     let mut cmd = get_wasmtime_command()?;
2852     cmd.arg("wizer").args(args).arg("-");
2853     cmd.stdin(Stdio::piped())
2854         .stdout(Stdio::piped())
2855         .stderr(Stdio::piped());
2856     let mut child = cmd.spawn()?;
2857     let mut stdin = child.stdin.take().unwrap();
2858     stdin.write_all(wat.as_bytes())?;
2859     drop(stdin);
2860 
2861     let output = child.wait_with_output()?;
2862     if !output.status.success() {
2863         println!(
2864             "Failed to execute wasmtime wizer with: {cmd:?}\n{}",
2865             String::from_utf8_lossy(&output.stderr)
2866         );
2867     }
2868     Ok(output)
2869 }
2870 
2871 #[test]
2872 fn wizer_no_imports_by_default() -> Result<()> {
2873     let result = wizen(
2874         &[],
2875         r#"(module
2876             (func (export "wizer-initialize"))
2877         )"#,
2878     )?;
2879     assert!(result.status.success());
2880 
2881     let result = wizen(
2882         &[],
2883         r#"(module
2884             (import "foo" "bar" (func))
2885             (func (export "wizer-initialize"))
2886         )"#,
2887     )?;
2888     assert!(!result.status.success());
2889 
2890     let result = wizen(
2891         &[],
2892         r#"(module
2893             (import "wasi_snapshot_preview1" "fd_write" (func (param i32 i32 i32 i32) (result i32)))
2894             (func (export "wizer-initialize"))
2895         )"#,
2896     )?;
2897     assert!(!result.status.success());
2898 
2899     let result = wizen(
2900         &["-Scli"],
2901         r#"(module
2902             (import "wasi_snapshot_preview1" "fd_write" (func (param i32 i32 i32 i32) (result i32)))
2903             (func (export "wizer-initialize"))
2904         )"#,
2905     )?;
2906     assert!(result.status.success());
2907 
2908     Ok(())
2909 }
2910 
2911 #[test]
2912 fn wizer_components() -> Result<()> {
2913     let result = wizen(
2914         &[],
2915         r#"
2916 (component
2917   (core module $a
2918     (global (mut i32) (i32.const 0))
2919     (func (export "init")
2920         i32.const 100
2921         global.set 0)
2922   )
2923   (core instance $a (instantiate $a))
2924   (func (export "wizer-initialize") (canon lift (core func $a "init")))
2925 )
2926         "#,
2927     )?;
2928     assert!(result.status.success());
2929 
2930     let component_with_wasi = r#"
2931 (component
2932   (import "wasi:cli/[email protected]" (instance
2933     (export "get-arguments" (func (result (list string))))
2934   ))
2935   (core module $a
2936     (global (mut i32) (i32.const 0))
2937     (func (export "init")
2938         i32.const 100
2939         global.set 0)
2940   )
2941   (core instance $a (instantiate $a))
2942   (func (export "wizer-initialize") (canon lift (core func $a "init")))
2943 )
2944         "#;
2945 
2946     let result = wizen(&[], component_with_wasi)?;
2947     assert!(!result.status.success());
2948     let result = wizen(&["-Scli"], component_with_wasi)?;
2949     assert!(result.status.success());
2950 
2951     Ok(())
2952 }
2953