wasmtime_test_util/
wast.rs

1use anyhow::{Context, Result};
2use serde::de::DeserializeOwned;
3use serde_derive::Deserialize;
4use std::fmt;
5use std::fs;
6use std::path::Path;
7use std::path::PathBuf;
8
9/// Limits for running wast tests.
10///
11/// This is useful for sharing between `tests/wast.rs` and fuzzing, for
12/// example, and is used as the minimum threshold for configuration when
13/// fuzzing.
14///
15/// Note that it's ok to increase these numbers if a test comes along and needs
16/// it, they're just here as empirically found minimum thresholds so far and
17/// they're not too scientific.
18pub mod limits {
19    pub const MEMORY_SIZE: usize = 805 << 16;
20    pub const MEMORIES: u32 = 450;
21    pub const TABLES: u32 = 200;
22    pub const MEMORIES_PER_MODULE: u32 = 9;
23    pub const TABLES_PER_MODULE: u32 = 5;
24    pub const COMPONENT_INSTANCES: u32 = 50;
25    pub const CORE_INSTANCES: u32 = 900;
26    pub const TABLE_ELEMENTS: usize = 1000;
27    pub const CORE_INSTANCE_SIZE: usize = 64 * 1024;
28    pub const TOTAL_STACKS: u32 = 10;
29}
30
31/// Local all `*.wast` tests under `root` which should be the path to the root
32/// of the wasmtime repository.
33pub fn find_tests(root: &Path) -> Result<Vec<WastTest>> {
34    let mut tests = Vec::new();
35
36    let spec_tests = root.join("tests/spec_testsuite");
37    add_tests(
38        &mut tests,
39        &spec_tests,
40        &FindConfig::Infer(spec_test_config),
41    )
42    .with_context(|| format!("failed to add tests from `{}`", spec_tests.display()))?;
43
44    let misc_tests = root.join("tests/misc_testsuite");
45    add_tests(&mut tests, &misc_tests, &FindConfig::InTest)
46        .with_context(|| format!("failed to add tests from `{}`", misc_tests.display()))?;
47
48    let cm_tests = root.join("tests/component-model/test");
49    add_tests(
50        &mut tests,
51        &cm_tests,
52        &FindConfig::Infer(component_test_config),
53    )
54    .with_context(|| format!("failed to add tests from `{}`", cm_tests.display()))?;
55
56    // Temporarily work around upstream tests that loop forever.
57    //
58    // Now that `thread.yield` and `CALLBACK_CODE_YIELD` are both no-ops in
59    // non-blocking contexts, these tests need to be updated; meanwhile, we skip
60    // them.
61    //
62    // TODO: remove this once
63    // https://github.com/WebAssembly/component-model/pull/578 has been merged:
64    {
65        let skip_list = &["drop-subtask.wast", "async-calls-sync.wast"];
66        tests.retain(|test| {
67            test.path
68                .file_name()
69                .and_then(|name| name.to_str())
70                .map(|name| !skip_list.contains(&name))
71                .unwrap_or(true)
72        });
73    }
74
75    Ok(tests)
76}
77
78enum FindConfig {
79    InTest,
80    Infer(fn(&Path) -> TestConfig),
81}
82
83fn add_tests(tests: &mut Vec<WastTest>, path: &Path, config: &FindConfig) -> Result<()> {
84    for entry in path.read_dir().context("failed to read directory")? {
85        let entry = entry.context("failed to read directory entry")?;
86        let path = entry.path();
87        if entry
88            .file_type()
89            .context("failed to get file type")?
90            .is_dir()
91        {
92            add_tests(tests, &path, config).context("failed to read sub-directory")?;
93            continue;
94        }
95
96        if path.extension().and_then(|s| s.to_str()) != Some("wast") {
97            continue;
98        }
99
100        let contents =
101            fs::read_to_string(&path).with_context(|| format!("failed to read test: {path:?}"))?;
102        let config = match config {
103            FindConfig::InTest => parse_test_config(&contents, ";;!")
104                .with_context(|| format!("failed to parse test configuration: {path:?}"))?,
105            FindConfig::Infer(f) => f(&path),
106        };
107        tests.push(WastTest {
108            path,
109            contents,
110            config,
111        })
112    }
113    Ok(())
114}
115
116fn spec_test_config(test: &Path) -> TestConfig {
117    let mut ret = TestConfig::default();
118    ret.spec_test = Some(true);
119    match spec_proposal_from_path(test) {
120        Some("wide-arithmetic") => {
121            ret.wide_arithmetic = Some(true);
122        }
123        Some("threads") => {
124            ret.threads = Some(true);
125            ret.reference_types = Some(false);
126        }
127        Some("custom-page-sizes") => {
128            ret.custom_page_sizes = Some(true);
129            ret.multi_memory = Some(true);
130            ret.memory64 = Some(true);
131
132            // See commentary below in `wasm-3.0` case for why these "hog
133            // memory"
134            if test.ends_with("memory_max.wast") || test.ends_with("memory_max_i64.wast") {
135                ret.hogs_memory = Some(true);
136            }
137        }
138        Some("custom-descriptors") => {
139            ret.custom_descriptors = Some(true);
140        }
141        Some(proposal) => panic!("unsupported proposal {proposal:?}"),
142        None => {
143            ret.reference_types = Some(true);
144            ret.simd = Some(true);
145            ret.simd = Some(true);
146            ret.relaxed_simd = Some(true);
147            ret.multi_memory = Some(true);
148            ret.gc = Some(true);
149            ret.reference_types = Some(true);
150            ret.memory64 = Some(true);
151            ret.tail_call = Some(true);
152            ret.extended_const = Some(true);
153            ret.exceptions = Some(true);
154
155            if test.parent().unwrap().ends_with("legacy") {
156                ret.legacy_exceptions = Some(true);
157            }
158
159            // These tests technically don't actually hog any memory but they
160            // do have a module definition with a table/memory that is the
161            // maximum size. These modules fail to compile in the pooling
162            // allocator which has limits on the minimum size of
163            // memories/tables by default.
164            //
165            // Pretend that these hog memory to avoid running the tests in the
166            // pooling allocator.
167            if test.ends_with("memory.wast")
168                || test.ends_with("table.wast")
169                || test.ends_with("memory64.wast")
170                || test.ends_with("table64.wast")
171            {
172                ret.hogs_memory = Some(true);
173            }
174        }
175    }
176
177    ret
178}
179
180fn component_test_config(test: &Path) -> TestConfig {
181    let mut ret = TestConfig::default();
182    ret.spec_test = Some(true);
183    ret.reference_types = Some(true);
184    ret.multi_memory = Some(true);
185
186    if let Some(parent) = test.parent() {
187        if parent.ends_with("async") {
188            ret.component_model_async = Some(true);
189            ret.component_model_async_builtins = Some(true);
190        }
191        if parent.ends_with("wasm-tools") {
192            ret.memory64 = Some(true);
193            ret.threads = Some(true);
194            ret.exceptions = Some(true);
195            ret.gc = Some(true);
196        }
197        if parent.ends_with("wasmtime") {
198            ret.exceptions = Some(true);
199            ret.gc = Some(true);
200        }
201    }
202
203    ret
204}
205
206/// Parse test configuration from the specified test, comments starting with
207/// `;;!`.
208pub fn parse_test_config<T>(wat: &str, comment: &'static str) -> Result<T>
209where
210    T: DeserializeOwned,
211{
212    // The test config source is the leading lines of the WAT file that are
213    // prefixed with `;;!`.
214    let config_lines: Vec<_> = wat
215        .lines()
216        .take_while(|l| l.starts_with(comment))
217        .map(|l| &l[comment.len()..])
218        .collect();
219    let config_text = config_lines.join("\n");
220
221    toml::from_str(&config_text).context("failed to parse the test configuration")
222}
223
224/// A `*.wast` test with its path, contents, and configuration.
225#[derive(Clone)]
226pub struct WastTest {
227    pub path: PathBuf,
228    pub contents: String,
229    pub config: TestConfig,
230}
231
232impl fmt::Debug for WastTest {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        f.debug_struct("WastTest")
235            .field("path", &self.path)
236            .field("contents", &"...")
237            .field("config", &self.config)
238            .finish()
239    }
240}
241
242macro_rules! foreach_config_option {
243    ($m:ident) => {
244        $m! {
245            memory64
246            custom_page_sizes
247            multi_memory
248            threads
249            shared_everything_threads
250            gc
251            function_references
252            relaxed_simd
253            reference_types
254            tail_call
255            extended_const
256            wide_arithmetic
257            hogs_memory
258            nan_canonicalization
259            component_model_async
260            component_model_async_builtins
261            component_model_async_stackful
262            component_model_threading
263            component_model_error_context
264            component_model_gc
265            simd
266            gc_types
267            exceptions
268            legacy_exceptions
269            stack_switching
270            spec_test
271            custom_descriptors
272        }
273    };
274}
275
276macro_rules! define_test_config {
277    ($($option:ident)*) => {
278        /// Per-test configuration which is written down in the test file itself for
279        /// `misc_testsuite/**/*.wast` or in `spec_test_config` above for spec tests.
280        #[derive(Debug, PartialEq, Default, Deserialize, Clone)]
281        #[serde(deny_unknown_fields)]
282        pub struct TestConfig {
283            $(pub $option: Option<bool>,)*
284        }
285
286        impl TestConfig {
287            $(
288                pub fn $option(&self) -> bool {
289                    self.$option.unwrap_or(false)
290                }
291            )*
292        }
293    }
294}
295
296foreach_config_option!(define_test_config);
297
298impl TestConfig {
299    /// Returns an iterator over each option.
300    pub fn options_mut(&mut self) -> impl Iterator<Item = (&'static str, &mut Option<bool>)> {
301        macro_rules! mk {
302            ($($option:ident)*) => {
303                [
304                    $((stringify!($option), &mut self.$option),)*
305                ].into_iter()
306            }
307        }
308        foreach_config_option!(mk)
309    }
310}
311
312/// Configuration that spec tests can run under.
313#[derive(Debug)]
314pub struct WastConfig {
315    /// Compiler chosen to run this test.
316    pub compiler: Compiler,
317    /// Whether or not the pooling allocator is enabled.
318    pub pooling: bool,
319    /// What garbage collector is being used.
320    pub collector: Collector,
321}
322
323/// Different compilers that can be tested in Wasmtime.
324#[derive(PartialEq, Debug, Copy, Clone)]
325pub enum Compiler {
326    /// Cranelift backend.
327    ///
328    /// This tests the Cranelift code generator for native platforms. This
329    /// notably excludes Pulley since that's listed separately below even though
330    /// Pulley is a backend of Cranelift. This is only used for native code
331    /// generation such as x86_64.
332    CraneliftNative,
333
334    /// Winch backend.
335    ///
336    /// This tests the Winch backend for native platforms. Currently Winch
337    /// primarily supports x86_64.
338    Winch,
339
340    /// Pulley interpreter.
341    ///
342    /// This tests the Cranelift pulley backend plus the pulley execution
343    /// environment of the output bytecode. Note that this is separate from
344    /// `Cranelift` above to be able to test both on platforms where Cranelift
345    /// has native codegen support.
346    CraneliftPulley,
347}
348
349impl Compiler {
350    /// Returns whether this compiler is known to fail for the provided
351    /// `TestConfig`.
352    ///
353    /// This function will determine if the configuration of the test provided
354    /// is known to guarantee fail. This effectively tracks the proposal support
355    /// for each compiler backend/runtime and tests whether `config` enables or
356    /// disables features that aren't supported.
357    ///
358    /// Note that this is closely aligned with
359    /// `Config::compiler_panicking_wasm_features`.
360    pub fn should_fail(&self, config: &TestConfig) -> bool {
361        match self {
362            Compiler::CraneliftNative => config.legacy_exceptions(),
363
364            Compiler::Winch => {
365                if config.gc()
366                    || config.tail_call()
367                    || config.function_references()
368                    || config.gc()
369                    || config.relaxed_simd()
370                    || config.gc_types()
371                    || config.exceptions()
372                    || config.legacy_exceptions()
373                    || config.stack_switching()
374                    || config.legacy_exceptions()
375                    || config.component_model_async()
376                {
377                    return true;
378                }
379
380                if cfg!(target_arch = "aarch64") {
381                    return config.wide_arithmetic()
382                        || (config.simd() && !config.spec_test())
383                        || config.threads();
384                }
385
386                !cfg!(target_arch = "x86_64")
387            }
388
389            Compiler::CraneliftPulley => {
390                config.threads() || config.legacy_exceptions() || config.stack_switching()
391            }
392        }
393    }
394
395    /// Returns whether this compiler configuration supports the current host
396    /// architecture.
397    pub fn supports_host(&self) -> bool {
398        match self {
399            Compiler::CraneliftNative => {
400                cfg!(target_arch = "x86_64")
401                    || cfg!(target_arch = "aarch64")
402                    || cfg!(target_arch = "riscv64")
403                    || cfg!(target_arch = "s390x")
404            }
405            Compiler::Winch => cfg!(target_arch = "x86_64") || cfg!(target_arch = "aarch64"),
406            Compiler::CraneliftPulley => true,
407        }
408    }
409}
410
411#[derive(PartialEq, Debug, Copy, Clone)]
412pub enum Collector {
413    Auto,
414    Null,
415    DeferredReferenceCounting,
416}
417
418impl WastTest {
419    /// Returns whether this test exercises the GC types and might want to use
420    /// multiple different garbage collectors.
421    pub fn test_uses_gc_types(&self) -> bool {
422        self.config.gc() || self.config.function_references()
423    }
424
425    /// Returns the optional spec proposal that this test is associated with.
426    pub fn spec_proposal(&self) -> Option<&str> {
427        spec_proposal_from_path(&self.path)
428    }
429
430    /// Returns whether this test should fail under the specified extra
431    /// configuration.
432    pub fn should_fail(&self, config: &WastConfig) -> bool {
433        if !config.compiler.supports_host() {
434            return true;
435        }
436
437        // Some tests are known to fail with the pooling allocator
438        if config.pooling {
439            let unsupported = [
440                // allocates too much memory for the pooling configuration here
441                "misc_testsuite/memory64/more-than-4gb.wast",
442                // shared memories + pooling allocator aren't supported yet
443                "misc_testsuite/memory-combos.wast",
444                "misc_testsuite/threads/atomics-end-of-memory.wast",
445                "misc_testsuite/threads/LB.wast",
446                "misc_testsuite/threads/LB_atomic.wast",
447                "misc_testsuite/threads/MP.wast",
448                "misc_testsuite/threads/MP_atomic.wast",
449                "misc_testsuite/threads/MP_wait.wast",
450                "misc_testsuite/threads/SB.wast",
451                "misc_testsuite/threads/SB_atomic.wast",
452                "misc_testsuite/threads/atomics_notify.wast",
453                "misc_testsuite/threads/atomics_wait_address.wast",
454                "misc_testsuite/threads/wait_notify.wast",
455                "spec_testsuite/proposals/threads/atomic.wast",
456                "spec_testsuite/proposals/threads/exports.wast",
457                "spec_testsuite/proposals/threads/memory.wast",
458            ];
459
460            if unsupported.iter().any(|part| self.path.ends_with(part)) {
461                return true;
462            }
463        }
464
465        if config.compiler.should_fail(&self.config) {
466            return true;
467        }
468
469        // Disable spec tests per target for proposals that Winch does not implement yet.
470        if config.compiler == Compiler::Winch {
471            // Common list for tests that fail in all targets supported by Winch.
472            let unsupported = [
473                "extended-const/elem.wast",
474                "extended-const/global.wast",
475                "misc_testsuite/component-model/modules.wast",
476                "misc_testsuite/externref-id-function.wast",
477                "misc_testsuite/externref-segment.wast",
478                "misc_testsuite/externref-segments.wast",
479                "misc_testsuite/externref-table-dropped-segment-issue-8281.wast",
480                "misc_testsuite/linking-errors.wast",
481                "misc_testsuite/many_table_gets_lead_to_gc.wast",
482                "misc_testsuite/mutable_externref_globals.wast",
483                "misc_testsuite/no-mixup-stack-maps.wast",
484                "misc_testsuite/no-panic.wast",
485                "misc_testsuite/simple_ref_is_null.wast",
486                "misc_testsuite/table_grow_with_funcref.wast",
487                "spec_testsuite/br_table.wast",
488                "spec_testsuite/global.wast",
489                "spec_testsuite/ref_func.wast",
490                "spec_testsuite/ref_is_null.wast",
491                "spec_testsuite/ref_null.wast",
492                "spec_testsuite/select.wast",
493                "spec_testsuite/table_fill.wast",
494                "spec_testsuite/table_get.wast",
495                "spec_testsuite/table_grow.wast",
496                "spec_testsuite/table_set.wast",
497                "spec_testsuite/table_size.wast",
498                "spec_testsuite/elem.wast",
499                "spec_testsuite/linking.wast",
500            ];
501
502            if unsupported.iter().any(|part| self.path.ends_with(part)) {
503                return true;
504            }
505
506            #[cfg(target_arch = "aarch64")]
507            {
508                let unsupported = [
509                    "misc_testsuite/int-to-float-splat.wast",
510                    "misc_testsuite/issue6562.wast",
511                    "misc_testsuite/memory64/simd.wast",
512                    "misc_testsuite/simd/almost-extmul.wast",
513                    "misc_testsuite/simd/canonicalize-nan.wast",
514                    "misc_testsuite/simd/cvt-from-uint.wast",
515                    "misc_testsuite/simd/edge-of-memory.wast",
516                    "misc_testsuite/simd/interesting-float-splat.wast",
517                    "misc_testsuite/simd/issue4807.wast",
518                    "misc_testsuite/simd/issue6725-no-egraph-panic.wast",
519                    "misc_testsuite/simd/issue_3173_select_v128.wast",
520                    "misc_testsuite/simd/issue_3327_bnot_lowering.wast",
521                    "misc_testsuite/simd/load_splat_out_of_bounds.wast",
522                    "misc_testsuite/simd/replace-lane-preserve.wast",
523                    "misc_testsuite/simd/spillslot-size-fuzzbug.wast",
524                    "misc_testsuite/simd/sse-cannot-fold-unaligned-loads.wast",
525                    "misc_testsuite/simd/unaligned-load.wast",
526                    "misc_testsuite/simd/v128-select.wast",
527                    "misc_testsuite/winch/issue-10331.wast",
528                    "misc_testsuite/winch/issue-10357.wast",
529                    "misc_testsuite/winch/issue-10460.wast",
530                    "misc_testsuite/winch/replace_lane.wast",
531                    "misc_testsuite/winch/simd_multivalue.wast",
532                    "misc_testsuite/winch/v128_load_lane_invalid_address.wast",
533                    "spec_testsuite/proposals/annotations/simd_lane.wast",
534                    "spec_testsuite/proposals/multi-memory/simd_memory-multi.wast",
535                    "spec_testsuite/simd_address.wast",
536                    "spec_testsuite/simd_align.wast",
537                    "spec_testsuite/simd_bit_shift.wast",
538                    "spec_testsuite/simd_bitwise.wast",
539                    "spec_testsuite/simd_boolean.wast",
540                    "spec_testsuite/simd_const.wast",
541                    "spec_testsuite/simd_conversions.wast",
542                    "spec_testsuite/simd_f32x4.wast",
543                    "spec_testsuite/simd_f32x4_arith.wast",
544                    "spec_testsuite/simd_f32x4_cmp.wast",
545                    "spec_testsuite/simd_f32x4_pmin_pmax.wast",
546                    "spec_testsuite/simd_f32x4_rounding.wast",
547                    "spec_testsuite/simd_f64x2.wast",
548                    "spec_testsuite/simd_f64x2_arith.wast",
549                    "spec_testsuite/simd_f64x2_cmp.wast",
550                    "spec_testsuite/simd_f64x2_pmin_pmax.wast",
551                    "spec_testsuite/simd_f64x2_rounding.wast",
552                    "spec_testsuite/simd_i16x8_arith.wast",
553                    "spec_testsuite/simd_i16x8_arith2.wast",
554                    "spec_testsuite/simd_i16x8_cmp.wast",
555                    "spec_testsuite/simd_i16x8_extadd_pairwise_i8x16.wast",
556                    "spec_testsuite/simd_i16x8_extmul_i8x16.wast",
557                    "spec_testsuite/simd_i16x8_q15mulr_sat_s.wast",
558                    "spec_testsuite/simd_i16x8_sat_arith.wast",
559                    "spec_testsuite/simd_i32x4_arith.wast",
560                    "spec_testsuite/simd_i32x4_arith2.wast",
561                    "spec_testsuite/simd_i32x4_cmp.wast",
562                    "spec_testsuite/simd_i32x4_dot_i16x8.wast",
563                    "spec_testsuite/simd_i32x4_extadd_pairwise_i16x8.wast",
564                    "spec_testsuite/simd_i32x4_extmul_i16x8.wast",
565                    "spec_testsuite/simd_i32x4_trunc_sat_f32x4.wast",
566                    "spec_testsuite/simd_i32x4_trunc_sat_f64x2.wast",
567                    "spec_testsuite/simd_i64x2_arith.wast",
568                    "spec_testsuite/simd_i64x2_arith2.wast",
569                    "spec_testsuite/simd_i64x2_cmp.wast",
570                    "spec_testsuite/simd_i64x2_extmul_i32x4.wast",
571                    "spec_testsuite/simd_i8x16_arith.wast",
572                    "spec_testsuite/simd_i8x16_arith2.wast",
573                    "spec_testsuite/simd_i8x16_cmp.wast",
574                    "spec_testsuite/simd_i8x16_sat_arith.wast",
575                    "spec_testsuite/simd_int_to_int_extend.wast",
576                    "spec_testsuite/simd_lane.wast",
577                    "spec_testsuite/simd_load.wast",
578                    "spec_testsuite/simd_load16_lane.wast",
579                    "spec_testsuite/simd_load32_lane.wast",
580                    "spec_testsuite/simd_load64_lane.wast",
581                    "spec_testsuite/simd_load8_lane.wast",
582                    "spec_testsuite/simd_load_extend.wast",
583                    "spec_testsuite/simd_load_splat.wast",
584                    "spec_testsuite/simd_load_zero.wast",
585                    "spec_testsuite/simd_select.wast",
586                    "spec_testsuite/simd_splat.wast",
587                    "spec_testsuite/simd_store.wast",
588                    "spec_testsuite/simd_store16_lane.wast",
589                    "spec_testsuite/simd_store32_lane.wast",
590                    "spec_testsuite/simd_store64_lane.wast",
591                    "spec_testsuite/simd_store8_lane.wast",
592                ];
593
594                if unsupported.iter().any(|part| self.path.ends_with(part)) {
595                    return true;
596                }
597            }
598
599            #[cfg(target_arch = "x86_64")]
600            {
601                let unsupported = [
602                    // externref/reference-types related
603                    // simd-related failures
604                    "misc_testsuite/simd/canonicalize-nan.wast",
605                ];
606
607                if unsupported.iter().any(|part| self.path.ends_with(part)) {
608                    return true;
609                }
610
611                // SIMD on Winch requires AVX instructions.
612                #[cfg(target_arch = "x86_64")]
613                if !(std::is_x86_feature_detected!("avx") && std::is_x86_feature_detected!("avx2"))
614                {
615                    let unsupported = [
616                        "annotations/simd_lane.wast",
617                        "memory64/simd.wast",
618                        "misc_testsuite/int-to-float-splat.wast",
619                        "misc_testsuite/issue6562.wast",
620                        "misc_testsuite/simd/almost-extmul.wast",
621                        "misc_testsuite/simd/cvt-from-uint.wast",
622                        "misc_testsuite/simd/edge-of-memory.wast",
623                        "misc_testsuite/simd/issue_3327_bnot_lowering.wast",
624                        "misc_testsuite/simd/issue6725-no-egraph-panic.wast",
625                        "misc_testsuite/simd/replace-lane-preserve.wast",
626                        "misc_testsuite/simd/spillslot-size-fuzzbug.wast",
627                        "misc_testsuite/simd/sse-cannot-fold-unaligned-loads.wast",
628                        "misc_testsuite/winch/issue-10331.wast",
629                        "misc_testsuite/winch/replace_lane.wast",
630                        "spec_testsuite/simd_align.wast",
631                        "spec_testsuite/simd_boolean.wast",
632                        "spec_testsuite/simd_conversions.wast",
633                        "spec_testsuite/simd_f32x4.wast",
634                        "spec_testsuite/simd_f32x4_arith.wast",
635                        "spec_testsuite/simd_f32x4_cmp.wast",
636                        "spec_testsuite/simd_f32x4_pmin_pmax.wast",
637                        "spec_testsuite/simd_f32x4_rounding.wast",
638                        "spec_testsuite/simd_f64x2.wast",
639                        "spec_testsuite/simd_f64x2_arith.wast",
640                        "spec_testsuite/simd_f64x2_cmp.wast",
641                        "spec_testsuite/simd_f64x2_pmin_pmax.wast",
642                        "spec_testsuite/simd_f64x2_rounding.wast",
643                        "spec_testsuite/simd_i16x8_cmp.wast",
644                        "spec_testsuite/simd_i32x4_cmp.wast",
645                        "spec_testsuite/simd_i64x2_arith2.wast",
646                        "spec_testsuite/simd_i64x2_cmp.wast",
647                        "spec_testsuite/simd_i8x16_arith2.wast",
648                        "spec_testsuite/simd_i8x16_cmp.wast",
649                        "spec_testsuite/simd_int_to_int_extend.wast",
650                        "spec_testsuite/simd_load.wast",
651                        "spec_testsuite/simd_load_extend.wast",
652                        "spec_testsuite/simd_load_splat.wast",
653                        "spec_testsuite/simd_load_zero.wast",
654                        "spec_testsuite/simd_splat.wast",
655                        "spec_testsuite/simd_store16_lane.wast",
656                        "spec_testsuite/simd_store32_lane.wast",
657                        "spec_testsuite/simd_store64_lane.wast",
658                        "spec_testsuite/simd_store8_lane.wast",
659                        "spec_testsuite/simd_load16_lane.wast",
660                        "spec_testsuite/simd_load32_lane.wast",
661                        "spec_testsuite/simd_load64_lane.wast",
662                        "spec_testsuite/simd_load8_lane.wast",
663                        "spec_testsuite/simd_bitwise.wast",
664                        "misc_testsuite/simd/load_splat_out_of_bounds.wast",
665                        "misc_testsuite/simd/unaligned-load.wast",
666                        "multi-memory/simd_memory-multi.wast",
667                        "misc_testsuite/simd/issue4807.wast",
668                        "spec_testsuite/simd_const.wast",
669                        "spec_testsuite/simd_i8x16_sat_arith.wast",
670                        "spec_testsuite/simd_i64x2_arith.wast",
671                        "spec_testsuite/simd_i16x8_arith.wast",
672                        "spec_testsuite/simd_i16x8_arith2.wast",
673                        "spec_testsuite/simd_i16x8_q15mulr_sat_s.wast",
674                        "spec_testsuite/simd_i16x8_sat_arith.wast",
675                        "spec_testsuite/simd_i32x4_arith.wast",
676                        "spec_testsuite/simd_i32x4_dot_i16x8.wast",
677                        "spec_testsuite/simd_i32x4_trunc_sat_f32x4.wast",
678                        "spec_testsuite/simd_i32x4_trunc_sat_f64x2.wast",
679                        "spec_testsuite/simd_i8x16_arith.wast",
680                        "spec_testsuite/simd_bit_shift.wast",
681                        "spec_testsuite/simd_lane.wast",
682                        "spec_testsuite/simd_i16x8_extmul_i8x16.wast",
683                        "spec_testsuite/simd_i32x4_extmul_i16x8.wast",
684                        "spec_testsuite/simd_i64x2_extmul_i32x4.wast",
685                        "spec_testsuite/simd_i16x8_extadd_pairwise_i8x16.wast",
686                        "spec_testsuite/simd_i32x4_extadd_pairwise_i16x8.wast",
687                        "spec_testsuite/simd_i32x4_arith2.wast",
688                    ];
689
690                    if unsupported.iter().any(|part| self.path.ends_with(part)) {
691                        return true;
692                    }
693                }
694            }
695        }
696
697        let failing_component_model_tests = [
698            // FIXME(#11683)
699            "component-model/test/values/trap-in-post-return.wast",
700            "component-model/test/wasmtime/resources.wast",
701            "component-model/test/wasm-tools/naming.wast",
702            // TODO: remove these once
703            // https://github.com/WebAssembly/component-model/pull/578 has been
704            // merged:
705            "component-model/test/async/async-calls-sync.wast",
706            "component-model/test/async/backpressure-deadlock.wast",
707            "component-model/test/async/cancel-stream.wast",
708            "component-model/test/async/cancel-subtask.wast",
709            "component-model/test/async/deadlock.wast",
710            "component-model/test/async/drop-subtask.wast",
711            "component-model/test/async/drop-waitable-set.wast",
712            "component-model/test/async/empty-wait.wast",
713            "component-model/test/async/fused.wast",
714            "component-model/test/async/future-read.wast",
715            "component-model/test/async/partial-stream-copies.wast",
716            "component-model/test/async/passing-resources.wast",
717            "component-model/test/async/stackful.wast",
718            "component-model/test/async/trap-if-block-and-sync.wast",
719            "component-model/test/async/trap-if-done.wast",
720            "component-model/test/async/wait-during-callback.wast",
721            "component-model/test/async/zero-length.wast",
722        ];
723        if failing_component_model_tests
724            .iter()
725            .any(|part| self.path.ends_with(part))
726        {
727            return true;
728        }
729
730        // Not implemented in Wasmtime anywhere yet.
731        if self.config.custom_descriptors() {
732            let happens_to_work =
733                ["spec_testsuite/proposals/custom-descriptors/binary-leb128.wast"];
734
735            if happens_to_work.iter().any(|part| self.path.ends_with(part)) {
736                return false;
737            }
738            return true;
739        }
740
741        false
742    }
743}
744
745fn spec_proposal_from_path(path: &Path) -> Option<&str> {
746    let mut iter = path.iter();
747    loop {
748        match iter.next()?.to_str()? {
749            "proposals" => break,
750            _ => {}
751        }
752    }
753    Some(iter.next()?.to_str()?)
754}