File: interfaces.rs

package info (click to toggle)
rustc 1.85.0%2Bdfsg3-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, sid, trixie
  • size: 893,396 kB
  • sloc: xml: 158,127; python: 35,830; javascript: 19,497; cpp: 19,002; sh: 17,245; ansic: 13,127; asm: 4,376; makefile: 1,051; perl: 29; lisp: 29; ruby: 19; sql: 11
file content (120 lines) | stat: -rw-r--r-- 4,316 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use anyhow::{Context, Result};
use libtest_mimic::{Arguments, Trial};
use pretty_assertions::assert_eq;
use std::fs;
use std::path::Path;
use wit_component::WitPrinter;
use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup};

/// Tests the encoding of a WIT package as a WebAssembly binary.
///
/// This test looks in the `interfaces/` directory for test cases. Each test
/// case is a `*.wit` file or a folder which contains `*.wit` files as part of a
/// multi-file package. Each tests `foo.wit` is accompanied with a `foo.wat` for
/// the WebAssembly text format encoding of the package. Additionally each test
/// has a `foo.print.wit` which is the machine-printed version of the WIT
/// document as decoded from the binary encoded interface.
///
/// Run the test with the environment variable `BLESS` set to update
/// the baseline files.
fn main() -> Result<()> {
    env_logger::init();

    let mut trials = Vec::new();
    for entry in fs::read_dir("tests/interfaces")? {
        let path = entry?.path();
        let name = match path.file_name().and_then(|s| s.to_str()) {
            Some(s) => s,
            None => continue,
        };
        let is_dir = path.is_dir();
        let is_test = is_dir || name.ends_with(".wit");
        if is_test {
            trials.push(Trial::test(name.to_string(), move || {
                run_test(&path, is_dir)
                    .context(format!("failed test `{}`", path.display()))
                    .map_err(|e| format!("{e:?}").into())
            }));
        }
    }

    let mut args = Arguments::from_args();
    if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
        args.test_threads = Some(1);
    }
    libtest_mimic::run(&args, trials).exit();
}

fn run_test(path: &Path, is_dir: bool) -> Result<()> {
    let mut resolve = Resolve::new();
    let package = if is_dir {
        resolve.push_dir(path)?.0
    } else {
        resolve.push_file(path)?
    };

    assert_print(&resolve, package, path, is_dir)?;

    // First convert the WIT package to a binary WebAssembly output, then
    // convert that binary wasm to textual wasm, then assert it matches the
    // expectation.
    let wasm = wit_component::encode(&resolve, package)?;
    let wat = wasmprinter::print_bytes(&wasm)?;
    assert_output(&path.with_extension("wat"), &wat)?;
    wasmparser::Validator::new()
        .validate_all(&wasm)
        .context("failed to validate wasm output")?;

    // Next decode a fresh WIT package from the WebAssembly generated. Print
    // this package's documents and assert they all match the expectations.
    let decoded = wit_component::decode(&wasm)?;

    let decoded_package = decoded.package();
    let resolve = decoded.resolve();

    assert_print(resolve, decoded.package(), path, is_dir)?;

    // Finally convert the decoded package to wasm again and make sure it
    // matches the prior wasm.
    let wasm2 = wit_component::encode(resolve, decoded_package)?;
    if wasm != wasm2 {
        let wat2 = wasmprinter::print_bytes(&wasm)?;
        assert_eq!(wat, wat2, "document did not roundtrip correctly");
    }

    Ok(())
}

fn assert_print(resolve: &Resolve, pkg_id: PackageId, path: &Path, is_dir: bool) -> Result<()> {
    let output = WitPrinter::default().print(resolve, pkg_id, &[])?;
    let pkg = &resolve.packages[pkg_id];
    let expected = if is_dir {
        path.join(format!("{}.wit.print", &pkg.name.name))
    } else {
        path.with_extension("wit.print")
    };
    assert_output(&expected, &output)?;

    UnresolvedPackageGroup::parse("foo.wit", &output).context("failed to parse printed output")?;
    Ok(())
}

fn assert_output(expected: &Path, actual: &str) -> Result<()> {
    let actual = actual.replace(
        concat!("\"", env!("CARGO_PKG_VERSION"), "\""),
        "\"$CARGO_PKG_VERSION\"",
    );
    if std::env::var_os("BLESS").is_some() {
        fs::write(expected, actual).with_context(|| format!("failed to write {expected:?}"))?;
    } else {
        assert_eq!(
            fs::read_to_string(expected)
                .with_context(|| format!("failed to read {expected:?}"))?
                .replace("\r\n", "\n"),
            actual,
            "expectation `{}` did not match actual",
            expected.display(),
        );
    }
    Ok(())
}