Path: blob/main/docs/contributing-reducing-test-cases.md
1685 views
Reducing Test Cases
When reporting a bug, or investing a bug report, in Wasmtime it is easier for everyone involved when there is a test case that reproduces the bug. It is even better when that test case is as small as possible, so that developers don't need to wade through megabytes of unrelated Wasm that isn't necessary to showcase the bug. The process of taking a large test case and stripping out the unnecessary bits is called test case reduction.
The wasm-tools shrink
tool can automatically reduce Wasm test cases when given
the original, unreduced test case, and
a predicate script to determine whether the bug reproduces on a given reduced test case candidate.
If the test case causes Wasmtime to segfault, the script can run Wasmtime and check its exit code. If the test case produces a different result in Wasmtime vs another Wasm engine, the script can run both engines and compare their results. It is also often useful to grep
through the candidate's WAT disassembly to make sure that relevant features and instructions are present.
Note that there are also a few other test-case reducers that can operate on Wasm. All of them, including wasm-shrink
, work fairly similarly at a high level, but often if one reducer gets stuck in a local minimum, another reducer can pick up from there and reduce the test case further due to differences in the details of their implementations. Therefore, if you find that wasm-shrink
isn't very effective on a particular test case, you can try continuing reduction with one of the following:
Case Study: Issue #7779
A bug was reported involving the memory.init
instruction. The attached test case was larger than it needed to be and contained a bunch of functions and other things that were irrelevant. A perfect use case for wasm-tools shrink
!
First, we needed a predicate script to identify the reported buggy behavior. The script is given the candidate test case as its first argument and must exit zero if the candidate exhibits the bug and non-zero otherwise.
Note that this script is a little fuzzy! It just checks for memory.init
and a particular trap. That trap can correctly occur according to Wasm semantics when memory.init
is given certain inputs! This means we need to double check that the reduced test case actually exhibits a real bug and its inputs haven't been transformed into something that Wasm semantics specify should indeed trap. Sometimes writing very precise predicate scripts is difficult, but we do the best we can and usually it works out fine.
With the predicate script in hand, we can automatically reduce the original test case:
In this case, the arguments to the original memory.init
instruction haven't changed, and neither has the relevant data segment, so the reduced test case should exhibit the same behavior as the original.
In the end, it was determined that Wasmtime was behaving as expected, but the presence of the reduced test case makes it much easier to make that determination.