| Crates.io | wit-bindgen-test |
| lib.rs | wit-bindgen-test |
| version | 0.46.0 |
| created_at | 2025-03-13 02:06:31.261976+00 |
| updated_at | 2025-09-10 17:55:16.549839+00 |
| description | Backend of the `wit-bindgen test` subcommand |
| homepage | https://github.com/bytecodealliance/wit-bindgen |
| repository | https://github.com/bytecodealliance/wit-bindgen |
| max_upload_size | |
| id | 1590431 |
| size | 154,385 |
wit-bindgen-testThis folder contains the wit-bindgen-test crate which is used to power the
wit-bindgen test subcommand of the wit-bindgen CLI. The purpose of this
document is to document what this subcommand does and how it enables testing.
wit-bindgenThe goal of the test subcommand is to make it as easy as possible to test
bindings generators and their functionality. It's also intended to enable
testing various kinds of functionality of a bindings generator in terms of
code generator flags, language flags, etc. This is a pretty generic problem
though since this isn't just one bindings generator but instead a bindings
generator for many languages. There's additional overlap where component runtime
hosts may want similar tests as everything, if you squint hard enough, looks
like it's all testing in a similar fashion.
To help scope this problem down, the goals of this tool are:
*.wit file
as input and assert that both bindings can be generated and the generated
bindings are valid. Validity of the generated bindings is determined by the
guest language itself, and this step typically doesn't involve creating a
WebAssembly component binary.test.wit which contains a runner world and a test world.
There are then "runner" components defined with the file prefix runner, for
example runner.rs. To complement these there are "test" components, such as
test.rs. The runner is composed with the test to create a single component
binary which looks like a WASI CLI program. This program is then run to
completion.Built on these goals the wit-bindgen test subcommand has a number of sub-goals
such as parallelizing tests, making iteration fast, good error messages, etc.
This is all feeding towards the highest-level goal of making it easy to write
tests in any language that has a bindings generator and can compile to
components.
The wit-bindgen test subcommand can be explored with:
$ wit-bindgen test -h
The main arguments to the subcommand are directories that contain tests and a
--artifacts path which contains where to store temporary build artifacts, such
as compiled component binaries. For example:
$ wit-bindgen test ./tests --artifacts ./target/artifacts
This will look recursively in the ./tests directory for tests. Test discovery
is detailed below in runtime and codegen tests. During test execution any
intermediate artifacts are present in ./target/artifacts at a per-test stable
location to assist with debugging. For example if invalid code is generated it
can inspected within ./target/artifacts.
Some other basic flags are:
-f or --filter - a regex-based filter on which tests to run, used to run
only a single test if desired. Note that running a single test can also be
done by passing a narrower ./tests directory, such as
./tests/codegen/my-test.wit.
-i or --inherit-stderr - this is used to have subprocesses inherit stderr
from the calling process which can be useful when guest language compilers
produce colored error messages for example as otherwise stderr is captured
from subcommands by default meaning that colors won't show up.
-l or --languages - By default all languages that wit-bindgen test
supports are enabled. This means that if you don't have development toolchains
installed locally tests may fail. This flag can be used to filter languages to
test (e.g. --languages rust) or to disable specific languages (e.g.
--languages=-rust).
--runner - Runtime tests are executed within a WebAssembly component runtime
and this is the path to a custom runtime to use. By default wasmtime is used
but any other runtime can be supplied.
The first category of tests that wit-bindgen test supports are called "codegen
tests". These tests are a *.wit file which contains a single world within
it. These files are used as input to a language bindings generator and then the
output is compiled by the target language to ensure that valid bindings were
generated.
These tests do not produce a complete component. Instead the validity of the
generated bindings are up to the target language. For example in Rust the
bindings are compiled with --crate-type rlib, in C the bindings are compiled
to an object file, and interpreted languages might run various lints for
example.
Codegen tests are discovered inside of a directory called codegen. Internally
all codegen/*.wit files are then run as tests. By default all supported
languages of wit-bindgen test are run for each codegen/*.wit test file.
By default each language only tests the default settings of the bindings
generator. To have all tests also tested with more options you'll want to update
the LanguageMethods::codegen_test_variants method. If this is a non-empty
array then each entry will run each codegen test through those options as well,
effectively testing codegen tests in more than one configuration.
Ignoring classes of tests can be done in the CLI tool by updating a few locations:
WitConfig to contain a field for this class of test
that needs to be ignored (if it's not already present).//@ async = true which would indicate that this uses async features.LanguageMethods::should_fail_verify for your language to ignore this
class of tests by checking the WitConfig config option and returning
true for "should fail"This will still run the test but an error will be expected. If an error is
generated then the test will be considered to have passed. If the test instead
passes, however, then the test will be considered to have failed and the
should_fail_verify method will need to be updated.
If a single test is problematic and doesn't fall into a "class" of tests like
above then the LanguageMethods::should_fail_verify method should be updated
and the name field should be consulted. This is the name of the test itself
and that can be used to expect failure in individual tests.
The second class of tests supported by wit-bindgen test is what are called
"runtime tests". The goal of runtime tests is to not only test that generated
code is valid but it additionally produces a valid component that works at
runtime. These tests have the following structure:
my-test/
test.wit # WIT `test` and `runner` worlds
runner.rs # Implementation of `runner` in Rust
runner.c # Implementation of `runner` in C
test.go # Implementation of `test` in Go
test2.go # Another implementation of `test` in Go
Each folder must contain a test.wit file. This WIT file must contain at least
two worlds: runner and test. The runner world imports functionality and
the test world exports functionality.
Each runner* file is compiled, using the language-specific toolchain and
bindings, into a component. This is then additionally done for the test*
files. Bindings are automatically generated and provided to the compilation
phase and each language has its own conventions of how to assemble everything
into a component.
Once components are produced the matrix of runner x test is produced to
compose together. Each runner and test are composed to produce a single
component which is a test case. For example the above example would have four
test components produced:
runner.rs x test.gorunner.rs x test2.gorunner.c x test.gorunner.c x test2.goEach test component is then run with the --runner CLI option (or wasmtime by
default).
The runner component is expected to export a wasi:cli/run interface
according to language specific conventions (e.g. it has fn main() { .. } for
Rust). Both the runner and test component can access other WASI APIs such as
printing to standard out/err for debugging.
It's recommended to write both "runner" and "test" components in the language that you want to test. The "runner" component exercises the ability to import WIT interfaces and call them while the "test" component exercises the ability to export interfaces and have them called.
Each source language file can be annotated with arguments to pass to the bindings generation phase. This is done by having a comment at the top of the file such as:
//@ args = '--custom --arguments'
fn main() {
// ...
}
This //@ prefix indicates that test configuration present. The test
configuration deserializes via TOML to RuntimeTestConfig. The field used here
is args which are the CLI arguments to pass to wit-bindgen rust ... in this
case. This can be used to have specific source files test various options of a
bindings generator.
Note that multiple runners are supported, so for example in one test Rust might
have runner-std.rs and runner-nostd.rs to test with and without the
--std-feature flag to the Rust bindings generator. Note that regardless of
bindings generator flags it's expected that the original runner or test
worlds are still adhered to.
By default runner and test worlds are expected, but this can be configured
with:
//@ runner = "other-runner"
//@ dependencies = ["other-test"]
package foo:bar;
world other-runner {
// ...
}
world other-test {
// ...
}
This will then expect other-runner.rs for example as a test file, so test
files are still named after their worlds.
The wac tooling is available for composing components together. This can be
configured with dependencies and wac keys:
//@ dependencies = ["intermediate", "leaf"]
//@ wac = "./compose.wac"
package foo:bar;
world runner {
// ...
}
world intermediate {
// ...
}
world leaf {
// ...
}
This would then require a compose.wac file in the test directory. Components
named test:{world} are made available to the script to perform compositions
with:
package example:composition;
let leaf = new test:leaf { ... };
let intermediate = new test:intermediate { ...leaf, ... };
let runner = new test:runner { ...intermediate, ... };
export runner...;
Currently the wit-bindgen test CLI comes with built-in language support for a
few languages. Note that this does not include built-in toolchain support. For
example wit-bindgen test will still need access to a Rust toolchain to compile
Rust source files.
*.wat)Tests written in these languages can use wit-bindgen test natively and don't
need to otherwise provide anything else. Custom language support is
additionally supported at this time via the --custom CLI flag to
wit-bindgen test. For example if the CLI didn't natively have support for Rust
it could be specified as:
$ wit-bindgen test ./tests --artifacts-dir ./artifacts \
--custom rs=./wit-bindgen-rust-runner
This would recognize the rs file extension and use the
./wit-bindgen-rust-runner script or binary to execute tests. The exact
interface to the tests is documented as part of wit-bindgen test --help for
the --custom argument.
Rust configuration supports a few keys at the top of files in addition to the
default args option for bindings generator options
//@ [lang]
//@ rustflags = '-O'
//@ externs = ['./other.rs']
Here the crate will be compiled with -O and ./other.rs will be compiled as a
separate crate and passed as --extern
C/C++ configuration supports configuring compilation flags at this time:
//@ [lang]
//@ cflags = '-O'