

March 14, 2023
Testing - WAI
Testing WAI packages using wasmer-pack-testing
Rudra
In my previous article, I explained how to port a Rust package for use in the JavaScript ecosystem. However, these published packages need to be tested within their own ecosystems to verify their functionality and identify any potential bugs.
Testing in WAI
Testing for WAI has been made easier using an automated testing framework wasmer-pack-testing. This crate automatically discovers the tests for available language configurations for WAI and runs the tests correspondingly.
wasmer-pack-testing uses jest for JavaScript/Typescript and pytest for Python.
This testing framework allows for both logical checks through unit tests and code generation using snapshot testing.
Tutorial Outline
- Introduction
- Environment Setup
- Bindings generation
- Testing for JavaScript
Introduction
In this tutorial, we will test the recently published rustfft package, which demonstrates the portability and logical computation power of WebAssembly. WAI can be used to harness that power!
💡 RustFFT is a high-performance FFT library written in pure Rust.
Environment Setup
The default testing using cargo test only runs the tests that are written using the test macro in the library files.To run integration tests, we can create a new file in a separate tests directory.
- 
Create an integration tests file $ mkdir tests && touch tests/rustfft-integration-tests.rs
- 
Let’s update our cargo.tomlto indicate that we are providing a custom test harness.# Cargo.toml [dev-dependencies] anyhow = "1.0.68" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } wasmer-pack-testing = "0.7.0" [[test]] name = "rustfft-integration-tests" harness = false
- 
Finally, let's write an integration test to generate bindings and test them against the appropriate target. // tests/rustfft-integration-tests.rs use anyhow::Error; use tracing_subscriber::EnvFilter; fn main() -> Result<(), Error> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .init(); wasmer_pack_testing::autodiscover(env!("CARGO_MANIFEST_DIR"))?; Ok(()) }
Bindings Generation
To enable our testing suite to discover which targets to generate bindings for, let's create a blank JavaScript test file in the crate directory (the one with Cargo.toml) that autodiscover() can use.
Now let’s create a blank rustfft.test.js in the crate directory.
$ touch rustfft.test.js
Running the tests will generate a testing package (note the package.json) and bindings for your test to use.
$ cargo test
$ tree .
.
├── Cargo.lock
├── Cargo.toml
├── LICENSE_APACHE.md
├── LICENSE_MIT.md
├── generated_bindings
├── jest.config.js
├── node_modules
├── package.json
├── rustfft.test.js
├── rustfft.wai
├── snapshots
├── src
├── target
├── tests
└── yarn.lock
Our JavaScript generated bindings live inside generate_bindings.
🐍 Same would work for python if you place a
test_rustfft.pyfile in your root directory
Testing with JavaScript
Now let’s write a test to see if it works, I have extracted a test case for the dft algorithm from the original rustfft crate.
const { bindings } = require('@dynamite-bud/rustfft');
const {
  Complex,
  Algorithm,
} = require('@dynamite-bud/rustfft/src/bindings/rustfft/rustfft.js');
const { test, expect } = require('@jest/globals');
test('dft test len 3', async () => {
  const wasm = await bindings.rustfft();
  let signal = [
    { re: 1.0, im: 1.0 },
    { re: 2.0, im: -3.0 },
    { re: -1.0, im: 4.0 },
  ];
  let spectrum = [
    { re: 2.0, im: 2.0 },
    { re: -5.562177, im: -2.098076 },
    { re: 6.562178, im: 3.09807 },
  ];
  let dft = Algorithm.newDft(wasm, signal.length, 'forward');
  let output = dft.compute(signal);
  output.forEach((element, index) => {
    expect(element.re.toPrecision(5)).toBe(spectrum[index].re.toPrecision(5));
  });
});
test('dft test len 4', async () => {
  const wasm = await bindings.rustfft();
  let signal = [
    { re: 0.0, im: 1.0 },
    { re: 2.5, im: -3.0 },
    { re: -1.0, im: -1.0 },
    { re: 4.0, im: 0.0 },
  ];
  let spectrum = [
    { re: 5.5, im: -3.0 },
    { re: -2.0, im: 3.5 },
    { re: -7.5, im: 3.0 },
    { re: 4.0, im: 0.5 },
  ];
  let dft = Algorithm.newDft(wasm, signal.length, 'forward');
  let output = dft.compute(signal);
  output.forEach((element, index) => {
    expect(element.re.toPrecision(2)).toBe(spectrum[index].re.toPrecision(2));
  });
});
Running cargo test now will automatically run the jest test from above for us.
Finished test [unoptimized + debuginfo] target(s) in 0.46s
     Running unittests src/lib.rs (target/debug/deps/rustfft-e1e8db73703fde9b)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running tests/rustfft-integration-tests.rs (target/debug/deps/rustfft_integration_tests-44dff04ad3dd20ad)
yarn install v1.22.19
warning package.json: No license field
info No lockfile found.
warning @dynamite-bud/rustfft@0.1.0: No license field
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done in 0.07s.
yarn install v1.22.19
[1/4] 🔍  Resolving packages...
success Already up-to-date.
✨  Done in 0.08s.
yarn run v1.22.19
$ (...)/rustfft/node_modules/.bin/jest
 PASS  ./rustfft.test.js
  ✓ dft test len 3 (11 ms)
  ✓ dft test len 4 (1 ms)
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.169 s
Ran all test suites.
✨  Done in 0.76s.
We see here that our tests run using the jest testing suite and both test cases pass successfully.
Conclusion
In this tutorial, we got to know how to testing your published packages with WAI. The testing was done using JavaScript. This testing also concludes that our logic internally is working correctly and correct bindings are being generated for our package in JavaScript.
Appendix
Sobre el autor
Rudra
Testing in WAI
Tutorial Outline
Introduction
Environment Setup
Bindings Generation
Testing with JavaScript
Conclusion
Appendix
Despliega tu app web en segundos con nuestra solución de nube gestionada.
Leer más
wapmwebassemblyregistry
WebAssembly as a Universal Binary Format (Part II: WAPM)
Syrus AkbaryAugust 19, 2022
bindingsCEngineering
Novel way to Develop, Test and Document C libraries from Rust
Syrus AkbaryJuly 6, 2021
wapmwasmer registryengineeringregistry
WAPM: A Newly Renovated Home For WebAssembly
Syrus AkbaryMarch 2, 2022
wapmengineeringregistry
Introducing Markdown Playgrounds - Powered by WebAssembly
Syrus AkbaryApril 22, 2022