Source code

Revision control

Copy as Markdown

Other Tools

# Introduction
These documents contains guidelines for contributors to the WebGPU CTS (Conformance Test Suite)
on how to write effective tests, and on the testing philosophy to adopt.
The WebGPU CTS is arguably more important than the WebGPU specification itself, because
it is what forces implementation to be interoperable by checking they conform to the specification.
However writing a CTS is hard and requires a lot of effort to reach good coverage.
More than a collection of tests like regular end2end and unit tests for software artifacts, a CTS
needs to be exhaustive. Contrast for example the WebGL2 CTS with the ANGLE end2end tests: they
cover the same functionality (WebGL 2 / OpenGL ES 3) but are structured very differently:
- ANGLE's test suite has one or two tests per functionality to check it works correctly, plus
regression tests and special tests to cover implementation details.
- WebGL2's CTS can have thousands of tests per API aspect to cover every combination of
parameters (and global state) used by an operation.
Below are guidelines based on our collective experience with graphics API CTSes like WebGL's.
They are expected to evolve over time and have exceptions, but should give a general idea of what
to do.
## Contributing
Testing tasks are tracked in the [CTS project tracker](https://github.com/orgs/gpuweb/projects/3).
Go here if you're looking for tasks, or if you have a test idea that isn't already covered.
If contributing conformance tests, the directory you'll work in is [`src/webgpu/`](../src/webgpu/).
This directory is organized according to the goal of the test (API validation behavior vs
actual results) and its target (API entry points and spec areas, e.g. texture sampling).
The contents of a test file (`src/webgpu/**/*.spec.ts`) are twofold:
- Documentation ("test plans") on what tests do, how they do it, and what cases they cover.
Some test plans are fully or partially unimplemented:
they either contain "TODO" in a description or are `.unimplemented()`.
- Actual tests.
**Please read the following short documents before contributing.**
### 0. [Developing](developing.md)
- Reviewers should also read [Review Requirements](../reviews.md).
### 1. [Life of a Test Change](life_of.md)
### 2. [Adding or Editing Test Plans](plans.md)
### 3. [Implementing Tests](tests.md)
## [Additional Documentation](../)
## Examples
### Operation testing of vertex input id generation
This section provides an example of the planning process for a test.
It has not been refined into a set of final test plan descriptions.
(Note: this predates the actual implementation of these tests, so doesn't match the actual tests.)
Somewhere under the `api/operation` node are tests checking that running `GPURenderPipelines` on
the device using the `GPURenderEncoderBase.draw` family of functions works correctly. Render
pipelines are composed of several stages that are mostly independent so they can be split in
several parts such as `vertex_input`, `rasterization`, `blending`.
Vertex input itself has several parts that are mostly separate in hardware:
- generation of the vertex and instance indices to run for this draw
- fetching of vertex data from vertex buffers based on these indices
- conversion from the vertex attribute `GPUVertexFormat` to the datatype for the input variable
in the shader
Each of these are tested separately and have cases for each combination of the variables that may
affect them. This means that `api/operation/render/vertex_input/id_generation` checks that the
correct operation is performed for the cartesian product of all the following dimensions:
- for encoding in a `GPURenderPassEncoder` or a `GPURenderBundleEncoder`
- whether the draw is direct or indirect
- whether the draw is indexed or not
- for various values of the `firstInstance` argument
- for various values of the `instanceCount` argument
- if the draw is not indexed:
- for various values of the `firstVertex` argument
- for various values of the `vertexCount` argument
- if the draw is indexed:
- for each `GPUIndexFormat`
- for various values of the indices in the index buffer including the primitive restart values
- for various values for the `offset` argument to `setIndexBuffer`
- for various values of the `firstIndex` argument
- for various values of the `indexCount` argument
- for various values of the `baseVertex` argument
"Various values" above mean several small values, including `0` and the second smallest valid
value to check for corner cases, as well as some large value.
An instance of the test sets up a `draw*` call based on the parameters, using point rendering and
a fragment shader that outputs to a storage buffer. After the draw the test checks the content of
the storage buffer to make sure all expected vertex shader invocation, and only these ones have
been generated.