Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/cli_tests.rs
3052 views
1
#![cfg(not(miri))]
2
3
use std::fs::File;
4
use std::io::Write;
5
use std::path::Path;
6
use std::process::{Command, ExitStatus, Output, Stdio};
7
use tempfile::{NamedTempFile, TempDir};
8
use wasmtime::{Result, bail};
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::<_, wasmtime::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 http_body_util::BodyExt;
1108
use hyper::header::HeaderValue;
1109
use std::io::{self, BufRead, BufReader, Read, Write};
1110
use std::iter;
1111
use std::net::SocketAddr;
1112
use std::process::{Child, Command, Stdio};
1113
use std::thread::{self, JoinHandle};
1114
use test_programs_artifacts::*;
1115
use tokio::net::TcpStream;
1116
use wasmtime::{Result, bail, error::Context as _};
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
#[test]
2186
fn p2_cli_p3_hello_stdout_post_return_invoke() -> Result<()> {
2187
let output = run_wasmtime(&[
2188
"run",
2189
"-Wcomponent-model-async",
2190
"-Sp3",
2191
"--invoke",
2192
"run()",
2193
P3_CLI_HELLO_STDOUT_POST_RETURN_COMPONENT,
2194
]);
2195
if cfg!(feature = "component-model-async") {
2196
let output = output?;
2197
assert_eq!(output, "hello, world\nhello again, after return\nok\n");
2198
} else {
2199
assert!(output.is_err());
2200
}
2201
Ok(())
2202
}
2203
2204
mod invoke {
2205
use super::*;
2206
2207
#[test]
2208
fn p2_cli_hello_stdout() -> Result<()> {
2209
println!("{P2_CLI_HELLO_STDOUT_COMPONENT}");
2210
let output = run_wasmtime(&[
2211
"run",
2212
"-Wcomponent-model",
2213
"--invoke",
2214
"run()",
2215
P2_CLI_HELLO_STDOUT_COMPONENT,
2216
])?;
2217
// First this component prints "hello, world", then the invoke
2218
// result is printed as "ok".
2219
assert_eq!(output, "hello, world\nok\n");
2220
Ok(())
2221
}
2222
}
2223
2224
#[test]
2225
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2226
fn p2_cli_invoke_async() -> Result<()> {
2227
let output = run_wasmtime(&[
2228
"run",
2229
"-Wcomponent-model-async",
2230
"--invoke",
2231
"echo(\"hello?\")",
2232
P2_CLI_INVOKE_ASYNC_COMPONENT,
2233
])?;
2234
assert_eq!(output, "\"hello?\"\n");
2235
Ok(())
2236
}
2237
2238
fn run_much_stdout(component: &str, extra_flags: &[&str]) -> Result<()> {
2239
let total_write_size = 1 << 18;
2240
let expected = iter::repeat('a').take(total_write_size).collect::<String>();
2241
2242
for i in 10..15 {
2243
let string = iter::repeat('a').take(1 << i).collect::<String>();
2244
let times = (total_write_size >> i).to_string();
2245
println!("writing {} bytes {times} times", string.len());
2246
2247
let mut args = Vec::new();
2248
args.push("run");
2249
args.extend_from_slice(extra_flags);
2250
args.push(component);
2251
args.push(&string);
2252
args.push(&times);
2253
let output = run_wasmtime(&args)?;
2254
println!(
2255
"expected {} bytes, got {} bytes",
2256
expected.len(),
2257
output.len()
2258
);
2259
assert!(output == expected);
2260
}
2261
2262
Ok(())
2263
}
2264
2265
#[test]
2266
fn p1_cli_much_stdout() -> Result<()> {
2267
run_much_stdout(P1_CLI_MUCH_STDOUT_COMPONENT, &[])
2268
}
2269
2270
#[test]
2271
fn p2_cli_much_stdout() -> Result<()> {
2272
run_much_stdout(P2_CLI_MUCH_STDOUT_COMPONENT, &[])
2273
}
2274
2275
#[test]
2276
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2277
fn p3_cli_much_stdout() -> Result<()> {
2278
run_much_stdout(
2279
P3_CLI_MUCH_STDOUT_COMPONENT,
2280
&["-Wcomponent-model-async", "-Sp3"],
2281
)
2282
}
2283
2284
#[tokio::test]
2285
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2286
async fn p3_cli_serve_hello_world() -> Result<()> {
2287
cli_serve_hello_world(P3_CLI_SERVE_HELLO_WORLD_COMPONENT, 1, 1, |cmd| {
2288
cmd.arg("-Wcomponent-model-async");
2289
cmd.arg("-Sp3,cli");
2290
})
2291
.await
2292
}
2293
2294
#[tokio::test]
2295
async fn p2_cli_serve_hello_world() -> Result<()> {
2296
cli_serve_hello_world(P2_CLI_SERVE_HELLO_WORLD_COMPONENT, 1, 1, |cmd| {
2297
cmd.arg("-Scli");
2298
})
2299
.await
2300
}
2301
2302
const CONNECTION_COUNT_MANY: usize = 20;
2303
const REQUESTS_PER_CONNECTION_MANY: usize = 5;
2304
2305
#[tokio::test]
2306
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2307
async fn p3_cli_serve_hello_world_many() -> Result<()> {
2308
cli_serve_hello_world(
2309
P3_CLI_SERVE_HELLO_WORLD_COMPONENT,
2310
CONNECTION_COUNT_MANY,
2311
REQUESTS_PER_CONNECTION_MANY,
2312
|cmd| {
2313
cmd.arg("-Wcomponent-model-async");
2314
cmd.arg("-Sp3,cli");
2315
},
2316
)
2317
.await
2318
}
2319
2320
#[tokio::test]
2321
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2322
async fn p3_cli_serve_hello_world_many_no_reuse() -> Result<()> {
2323
cli_serve_hello_world(
2324
P3_CLI_SERVE_HELLO_WORLD_COMPONENT,
2325
CONNECTION_COUNT_MANY,
2326
REQUESTS_PER_CONNECTION_MANY,
2327
|cmd| {
2328
cmd.arg("-Wcomponent-model-async");
2329
cmd.arg("-Sp3,cli");
2330
cmd.arg("--max-instance-reuse-count=1");
2331
},
2332
)
2333
.await
2334
}
2335
2336
#[tokio::test]
2337
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2338
async fn p3_cli_serve_hello_world_many_no_concurrent_reuse() -> Result<()> {
2339
cli_serve_hello_world(
2340
P3_CLI_SERVE_HELLO_WORLD_COMPONENT,
2341
CONNECTION_COUNT_MANY,
2342
REQUESTS_PER_CONNECTION_MANY,
2343
|cmd| {
2344
cmd.arg("-Wcomponent-model-async");
2345
cmd.arg("-Sp3,cli");
2346
cmd.arg("--max-instance-concurrent-reuse-count=1");
2347
},
2348
)
2349
.await
2350
}
2351
2352
#[tokio::test]
2353
async fn p2_cli_serve_hello_world_many() -> Result<()> {
2354
cli_serve_hello_world(
2355
P2_CLI_SERVE_HELLO_WORLD_COMPONENT,
2356
CONNECTION_COUNT_MANY,
2357
REQUESTS_PER_CONNECTION_MANY,
2358
|cmd| {
2359
cmd.arg("-Scli");
2360
},
2361
)
2362
.await
2363
}
2364
2365
#[tokio::test]
2366
async fn p2_cli_serve_hello_world_many_with_reuse() -> Result<()> {
2367
cli_serve_hello_world(
2368
P2_CLI_SERVE_HELLO_WORLD_COMPONENT,
2369
CONNECTION_COUNT_MANY,
2370
REQUESTS_PER_CONNECTION_MANY,
2371
|cmd| {
2372
cmd.arg("-Scli");
2373
cmd.arg("--max-instance-reuse-count=128");
2374
},
2375
)
2376
.await
2377
}
2378
2379
async fn cli_serve_hello_world(
2380
component: &str,
2381
connection_count: usize,
2382
requests_per_connection: usize,
2383
configure: impl FnOnce(&mut Command),
2384
) -> Result<()> {
2385
let server = std::sync::Arc::new(WasmtimeServe::new(component, configure)?);
2386
2387
let tasks = (0..connection_count).map({
2388
let server = server.clone();
2389
move |_| {
2390
tokio::task::spawn({
2391
let server = server.clone();
2392
async move {
2393
let (mut send, conn_task) = server.start_requests().await?;
2394
2395
for _ in 0..requests_per_connection {
2396
let result = WasmtimeServe::send_request_with(
2397
&mut send,
2398
hyper::Request::builder()
2399
.uri("http://localhost/")
2400
.body(String::new())
2401
.context("failed to make request")?,
2402
)
2403
.await?;
2404
2405
assert!(result.status().is_success());
2406
assert_eq!(result.body(), "Hello, WASI!");
2407
}
2408
2409
drop(send);
2410
2411
conn_task.await??;
2412
2413
wasmtime::error::Ok(())
2414
}
2415
})
2416
}
2417
});
2418
2419
for task in tasks {
2420
task.await??;
2421
}
2422
2423
std::sync::Arc::into_inner(server).unwrap().finish()?;
2424
Ok(())
2425
}
2426
2427
#[tokio::test]
2428
async fn p2_cli_serve_sleep() -> Result<()> {
2429
cli_serve_sleep(P2_CLI_SERVE_SLEEP_COMPONENT, 1, 1, |cmd| {
2430
cmd.arg("-Scli");
2431
})
2432
.await
2433
}
2434
2435
#[tokio::test]
2436
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2437
async fn p3_cli_serve_sleep() -> Result<()> {
2438
cli_serve_sleep(P3_CLI_SERVE_SLEEP_COMPONENT, 1, 1, |cmd| {
2439
cmd.arg("-Wcomponent-model-async");
2440
cmd.arg("-Sp3,cli");
2441
})
2442
.await
2443
}
2444
2445
#[tokio::test]
2446
async fn p2_cli_serve_sleep_many() -> Result<()> {
2447
cli_serve_sleep(
2448
P2_CLI_SERVE_SLEEP_COMPONENT,
2449
CONNECTION_COUNT_MANY,
2450
REQUESTS_PER_CONNECTION_MANY,
2451
|cmd| {
2452
cmd.arg("-Scli");
2453
},
2454
)
2455
.await
2456
}
2457
2458
#[tokio::test]
2459
#[cfg_attr(not(feature = "component-model-async"), ignore)]
2460
async fn p3_cli_serve_sleep_many() -> Result<()> {
2461
cli_serve_sleep(
2462
P3_CLI_SERVE_SLEEP_COMPONENT,
2463
CONNECTION_COUNT_MANY,
2464
REQUESTS_PER_CONNECTION_MANY,
2465
|cmd| {
2466
cmd.arg("-Wcomponent-model-async");
2467
cmd.arg("-Sp3,cli");
2468
},
2469
)
2470
.await
2471
}
2472
2473
async fn cli_serve_sleep(
2474
component: &str,
2475
connection_count: usize,
2476
requests_per_connection: usize,
2477
configure: impl FnOnce(&mut Command),
2478
) -> Result<()> {
2479
let server = std::sync::Arc::new(WasmtimeServe::new(component, move |cmd| {
2480
configure(cmd);
2481
cmd.arg("-Wtimeout=100us");
2482
})?);
2483
2484
let tasks = (0..connection_count).map({
2485
let server = server.clone();
2486
move |_| {
2487
tokio::task::spawn({
2488
let server = server.clone();
2489
async move {
2490
let (mut send, conn_task) = server.start_requests().await?;
2491
2492
for _ in 0..requests_per_connection {
2493
let result = WasmtimeServe::send_request_with(
2494
&mut send,
2495
hyper::Request::builder()
2496
.uri("http://localhost/")
2497
.body(String::new())
2498
.context("failed to make request")?,
2499
)
2500
.await?;
2501
2502
assert!(result.status().is_server_error());
2503
}
2504
2505
drop(send);
2506
2507
conn_task.await??;
2508
2509
wasmtime::error::Ok(())
2510
}
2511
})
2512
}
2513
});
2514
2515
for task in tasks {
2516
task.await??;
2517
}
2518
2519
let (stdout, stderr) = std::sync::Arc::into_inner(server).unwrap().finish()?;
2520
assert_eq!(stdout, "");
2521
assert!(stderr.contains("guest timed out"), "bad stderr: {stderr}");
2522
Ok(())
2523
}
2524
}
2525
2526
#[test]
2527
fn settings_command() -> Result<()> {
2528
// Skip this test on platforms that Cranelift doesn't support.
2529
if cranelift_native::builder().is_err() {
2530
return Ok(());
2531
}
2532
let output = run_wasmtime(&["settings"])?;
2533
assert!(output.contains("Cranelift settings for target"));
2534
Ok(())
2535
}
2536
2537
#[cfg(target_arch = "x86_64")]
2538
#[test]
2539
fn profile_with_vtune() -> Result<()> {
2540
if !is_vtune_available() {
2541
println!("> `vtune` is not available on the system path; skipping test");
2542
return Ok(());
2543
}
2544
2545
let mut bin = Command::new("vtune");
2546
bin.args(&[
2547
// Configure VTune...
2548
"-verbose",
2549
"-collect",
2550
"hotspots",
2551
"-user-data-dir",
2552
&std::env::temp_dir().to_string_lossy(),
2553
// ...then run Wasmtime with profiling enabled:
2554
get_wasmtime_path(),
2555
"--profile=vtune",
2556
"tests/all/cli_tests/simple.wat",
2557
]);
2558
2559
println!("> executing: {bin:?}");
2560
let output = bin.output()?;
2561
2562
let stdout = String::from_utf8_lossy(&output.stdout);
2563
let stderr = String::from_utf8_lossy(&output.stderr);
2564
println!("> stdout:\n{stdout}");
2565
println!("> stderr:\n{stderr}");
2566
2567
assert!(output.status.success());
2568
assert!(!stderr.contains("Error"));
2569
assert!(stdout.contains("CPU Time"));
2570
Ok(())
2571
}
2572
2573
#[cfg(target_arch = "x86_64")]
2574
fn is_vtune_available() -> bool {
2575
Command::new("vtune").arg("-version").output().is_ok()
2576
}
2577
2578
#[test]
2579
fn profile_guest() -> Result<()> {
2580
let tmpdir = std::env::temp_dir();
2581
let dir = tmpdir.to_string_lossy();
2582
2583
let output = run_wasmtime_for_output(
2584
&[
2585
&format!("--profile=guest,{dir}/out.json"),
2586
"--env",
2587
"FOO=bar",
2588
"tests/all/cli_tests/print_env.wat",
2589
],
2590
None,
2591
)?;
2592
2593
assert!(output.status.success());
2594
let stdout = String::from_utf8_lossy(&output.stdout);
2595
let stderr = String::from_utf8_lossy(&output.stderr);
2596
println!("> stdout:\n{stdout}");
2597
println!("> stderr:\n{stderr}");
2598
assert!(!stderr.contains("Error"));
2599
let out_json = std::fs::read_to_string(format!("{dir}/out.json")).unwrap();
2600
println!("> out.json:\n{out_json}");
2601
Ok(())
2602
}
2603
2604
#[test]
2605
fn unreachable_without_wasi() -> Result<()> {
2606
let output = run_wasmtime_for_output(
2607
&[
2608
"-Scli=n",
2609
"-Ccache=n",
2610
"tests/all/cli_tests/unreachable.wat",
2611
],
2612
None,
2613
)?;
2614
2615
assert_ne!(output.stderr, b"");
2616
assert_eq!(output.stdout, b"");
2617
assert_trap_code(&output.status);
2618
Ok(())
2619
}
2620
2621
#[test]
2622
fn config_cli_flag() -> Result<()> {
2623
let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
2624
2625
// Test some valid TOML values
2626
let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2627
cfg.write_all(
2628
br#"
2629
[optimize]
2630
opt-level = 2
2631
signals-based-traps = false
2632
2633
[codegen]
2634
collector = "null"
2635
2636
[debug]
2637
address-map = true
2638
2639
[wasm]
2640
max-wasm-stack = 65536
2641
2642
[wasi]
2643
cli = true
2644
"#,
2645
)?;
2646
let output = run_wasmtime(&[
2647
"run",
2648
"--config",
2649
cfg_path.to_str().unwrap(),
2650
"--invoke",
2651
"get_f64",
2652
wasm.path().to_str().unwrap(),
2653
])?;
2654
assert_eq!(output, "100\n");
2655
2656
// Make sure CLI flags overrides TOML values
2657
let output = run_wasmtime(&[
2658
"run",
2659
"--config",
2660
cfg_path.to_str().unwrap(),
2661
"--invoke",
2662
"get_f64",
2663
"-W",
2664
"max-wasm-stack=0", // should override TOML value 65536 specified above and execution should fail
2665
wasm.path().to_str().unwrap(),
2666
]);
2667
assert!(
2668
output
2669
.as_ref()
2670
.unwrap_err()
2671
.to_string()
2672
.contains("max_wasm_stack size cannot be zero"),
2673
"'{output:?}' did not contain expected error message",
2674
);
2675
2676
// Test invalid TOML key
2677
let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2678
cfg.write_all(
2679
br#"
2680
[optimize]
2681
this-key-does-not-exist = true
2682
"#,
2683
)?;
2684
let output = run_wasmtime(&[
2685
"run",
2686
"--config",
2687
cfg_path.to_str().unwrap(),
2688
wasm.path().to_str().unwrap(),
2689
]);
2690
assert!(
2691
output
2692
.as_ref()
2693
.unwrap_err()
2694
.to_string()
2695
.contains("unknown field `this-key-does-not-exist`"),
2696
"'{output:?}' did not contain expected error message"
2697
);
2698
2699
// Test invalid TOML table
2700
let (mut cfg, cfg_path) = tempfile::NamedTempFile::new()?.into_parts();
2701
cfg.write_all(
2702
br#"
2703
[invalid_table]
2704
"#,
2705
)?;
2706
let output = run_wasmtime(&[
2707
"run",
2708
"--config",
2709
cfg_path.to_str().unwrap(),
2710
wasm.path().to_str().unwrap(),
2711
]);
2712
assert!(
2713
output
2714
.as_ref()
2715
.unwrap_err()
2716
.to_string()
2717
.contains("unknown field `invalid_table`, expected one of `optimize`, `codegen`, `debug`, `wasm`, `wasi`"),
2718
"'{output:?}' did not contain expected error message",
2719
);
2720
2721
Ok(())
2722
}
2723
2724
#[test]
2725
fn invalid_subcommand() -> Result<()> {
2726
let output = run_wasmtime_for_output(&["invalid-subcommand"], None)?;
2727
dbg!(&output);
2728
assert!(!output.status.success());
2729
assert!(String::from_utf8_lossy(&output.stderr).contains("invalid-subcommand"));
2730
Ok(())
2731
}
2732
2733
#[test]
2734
fn numeric_args() -> Result<()> {
2735
let wasm = build_wasm("tests/all/cli_tests/numeric_args.wat")?;
2736
// Test decimal i32
2737
let output = run_wasmtime_for_output(
2738
&[
2739
"run",
2740
"--invoke",
2741
"i32_test",
2742
wasm.path().to_str().unwrap(),
2743
"42",
2744
],
2745
None,
2746
)?;
2747
assert_eq!(output.status.success(), true);
2748
assert_eq!(output.stdout, b"42\n");
2749
// Test hexadecimal i32 with lowercase prefix
2750
let output = run_wasmtime_for_output(
2751
&[
2752
"run",
2753
"--invoke",
2754
"i32_test",
2755
wasm.path().to_str().unwrap(),
2756
"0x2A",
2757
],
2758
None,
2759
)?;
2760
assert_eq!(output.status.success(), true);
2761
assert_eq!(output.stdout, b"42\n");
2762
// Test hexadecimal i32 with uppercase prefix
2763
let output = run_wasmtime_for_output(
2764
&[
2765
"run",
2766
"--invoke",
2767
"i32_test",
2768
wasm.path().to_str().unwrap(),
2769
"0X2a",
2770
],
2771
None,
2772
)?;
2773
assert_eq!(output.status.success(), true);
2774
assert_eq!(output.stdout, b"42\n");
2775
// Test that non-prefixed hex strings are not interpreted as hex
2776
let output = run_wasmtime_for_output(
2777
&[
2778
"run",
2779
"--invoke",
2780
"i32_test",
2781
wasm.path().to_str().unwrap(),
2782
"ff",
2783
],
2784
None,
2785
)?;
2786
assert!(!output.status.success()); // Should fail as "ff" is not a valid decimal number
2787
2788
// Test decimal i64
2789
let output = run_wasmtime_for_output(
2790
&[
2791
"run",
2792
"--invoke",
2793
"i64_test",
2794
wasm.path().to_str().unwrap(),
2795
"42",
2796
],
2797
None,
2798
)?;
2799
assert_eq!(output.status.success(), true);
2800
assert_eq!(output.stdout, b"42\n");
2801
// Test hexadecimal i64
2802
let output = run_wasmtime_for_output(
2803
&[
2804
"run",
2805
"--invoke",
2806
"i64_test",
2807
wasm.path().to_str().unwrap(),
2808
"0x2A",
2809
],
2810
None,
2811
)?;
2812
assert_eq!(output.status.success(), true);
2813
assert_eq!(output.stdout, b"42\n");
2814
Ok(())
2815
}
2816
2817
#[test]
2818
fn compilation_logs() -> Result<()> {
2819
let temp = tempfile::NamedTempFile::new()?;
2820
let output = get_wasmtime_command()?
2821
.args(&[
2822
"compile",
2823
"-Wgc",
2824
"tests/all/cli_tests/issue-10353.wat",
2825
"--output",
2826
&temp.path().display().to_string(),
2827
])
2828
.env("WASMTIME_LOG", "trace")
2829
.env("RUST_BACKTRACE", "1")
2830
.output()?;
2831
if !output.status.success() {
2832
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
2833
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
2834
panic!("wasmtime compilation failed when logs requested");
2835
}
2836
Ok(())
2837
}
2838
2839
#[test]
2840
fn big_table_in_pooling_allocator() -> Result<()> {
2841
// Works by default
2842
run_wasmtime(&["tests/all/cli_tests/big_table.wat"])?;
2843
2844
// Does not work by default in the pooling allocator, and the error message
2845
// should mention something about the pooling allocator.
2846
let output = run_wasmtime_for_output(
2847
&["-Opooling-allocator", "tests/all/cli_tests/big_table.wat"],
2848
None,
2849
)?;
2850
assert!(!output.status.success());
2851
println!("{}", String::from_utf8_lossy(&output.stderr));
2852
assert!(String::from_utf8_lossy(&output.stderr).contains("pooling allocator"));
2853
2854
// Does work with `-Wmax-table-elements`
2855
run_wasmtime(&[
2856
"-Opooling-allocator",
2857
"-Wmax-table-elements=25000",
2858
"tests/all/cli_tests/big_table.wat",
2859
])?;
2860
// Also works with `-Opooling-table-elements`
2861
run_wasmtime(&[
2862
"-Opooling-allocator",
2863
"-Opooling-table-elements=25000",
2864
"tests/all/cli_tests/big_table.wat",
2865
])?;
2866
Ok(())
2867
}
2868
2869
fn wizen(args: &[&str], wat: &str) -> Result<Output> {
2870
let mut cmd = get_wasmtime_command()?;
2871
cmd.arg("wizer").args(args).arg("-");
2872
cmd.stdin(Stdio::piped())
2873
.stdout(Stdio::piped())
2874
.stderr(Stdio::piped());
2875
let mut child = cmd.spawn()?;
2876
let mut stdin = child.stdin.take().unwrap();
2877
stdin.write_all(wat.as_bytes())?;
2878
drop(stdin);
2879
2880
let output = child.wait_with_output()?;
2881
if !output.status.success() {
2882
println!(
2883
"Failed to execute wasmtime wizer with: {cmd:?}\n{}",
2884
String::from_utf8_lossy(&output.stderr)
2885
);
2886
}
2887
Ok(output)
2888
}
2889
2890
#[test]
2891
fn wizer_no_imports_by_default() -> Result<()> {
2892
let result = wizen(
2893
&[],
2894
r#"(module
2895
(func (export "wizer-initialize"))
2896
)"#,
2897
)?;
2898
assert!(result.status.success());
2899
2900
let result = wizen(
2901
&[],
2902
r#"(module
2903
(import "foo" "bar" (func))
2904
(func (export "wizer-initialize"))
2905
)"#,
2906
)?;
2907
assert!(!result.status.success());
2908
2909
let result = wizen(
2910
&[],
2911
r#"(module
2912
(import "wasi_snapshot_preview1" "fd_write" (func (param i32 i32 i32 i32) (result i32)))
2913
(func (export "wizer-initialize"))
2914
)"#,
2915
)?;
2916
assert!(!result.status.success());
2917
2918
let result = wizen(
2919
&["-Scli"],
2920
r#"(module
2921
(import "wasi_snapshot_preview1" "fd_write" (func (param i32 i32 i32 i32) (result i32)))
2922
(func (export "wizer-initialize"))
2923
)"#,
2924
)?;
2925
assert!(result.status.success());
2926
2927
Ok(())
2928
}
2929
2930
#[test]
2931
fn wizer_components() -> Result<()> {
2932
let result = wizen(
2933
&[],
2934
r#"
2935
(component
2936
(core module $a
2937
(global (mut i32) (i32.const 0))
2938
(func (export "init")
2939
i32.const 100
2940
global.set 0)
2941
)
2942
(core instance $a (instantiate $a))
2943
(func (export "wizer-initialize") (canon lift (core func $a "init")))
2944
)
2945
"#,
2946
)?;
2947
assert!(result.status.success());
2948
2949
let component_with_wasi = r#"
2950
(component
2951
(import "wasi:cli/[email protected]" (instance
2952
(export "get-arguments" (func (result (list string))))
2953
))
2954
(core module $a
2955
(global (mut i32) (i32.const 0))
2956
(func (export "init")
2957
i32.const 100
2958
global.set 0)
2959
)
2960
(core instance $a (instantiate $a))
2961
(func (export "wizer-initialize") (canon lift (core func $a "init")))
2962
)
2963
"#;
2964
2965
let result = wizen(&[], component_with_wasi)?;
2966
assert!(!result.status.success());
2967
let result = wizen(&["-Scli"], component_with_wasi)?;
2968
assert!(result.status.success());
2969
2970
Ok(())
2971
}
2972
2973