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