wgpu_test/
lib.rs

1//! Test utilities for the wgpu repository.
2
3#![allow(clippy::arc_with_non_send_sync, reason = "False positive on wasm")]
4
5mod config;
6mod expectations;
7pub mod image;
8mod init;
9mod isolation;
10pub mod native;
11mod params;
12mod poll;
13mod report;
14mod run;
15
16#[cfg(target_arch = "wasm32")]
17pub use init::initialize_html_canvas;
18
19pub use self::image::ComparisonType;
20pub use config::{GpuTestConfiguration, GpuTestInitializer};
21pub use expectations::{FailureApplicationReasons, FailureBehavior, FailureCase, FailureReason};
22pub use init::{initialize_adapter, initialize_device, initialize_instance};
23pub use params::TestParameters;
24pub use run::{execute_test, TestingContext};
25pub use wgpu_macros::gpu_test;
26
27/// Run some code in an error scope and assert that validation fails.
28///
29/// Note that errors related to commands for the GPU (i.e. raised by methods on
30/// GPUCommandEncoder, GPURenderPassEncoder, GPUComputePassEncoder,
31/// GPURenderBundleEncoder) are usually not raised immediately. They are raised
32/// only when `finish()` is called on the command encoder. Tests of such error
33/// cases should call `fail` with a closure that calls `finish()`, not with a
34/// closure that encodes the actual command.
35pub fn fail<T>(
36    device: &wgpu::Device,
37    callback: impl FnOnce() -> T,
38    expected_msg_substring: Option<&str>,
39) -> T {
40    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
41    let result = callback();
42    let validation_error = pollster::block_on(scope.pop())
43        .expect("expected validation error in callback, but no validation error was emitted");
44    if let Some(expected_msg_substring) = expected_msg_substring {
45        let lowered_expected = expected_msg_substring.to_lowercase();
46        let lowered_actual = validation_error.to_string().to_lowercase();
47        assert!(
48            lowered_actual.contains(&lowered_expected),
49            concat!(
50                "expected validation error case-insensitively containing {}, ",
51                "but it was not present in actual error message:\n{}"
52            ),
53            expected_msg_substring,
54            validation_error
55        );
56    }
57
58    result
59}
60
61/// Run some code in an error scope and assert that validation succeeds.
62#[track_caller]
63pub fn valid<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
64    let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
65    let result = callback();
66    if let Some(error) = pollster::block_on(scope.pop()) {
67        panic!(
68            "`valid` block at {} encountered wgpu error:\n{error}",
69            std::panic::Location::caller()
70        );
71    }
72
73    result
74}
75
76/// Run some code in an error scope and assert that validation succeeds or fails depending on the
77/// provided `should_fail` boolean.
78pub fn fail_if<T>(
79    device: &wgpu::Device,
80    should_fail: bool,
81    callback: impl FnOnce() -> T,
82    expected_msg_substring: Option<&'static str>,
83) -> T {
84    if should_fail {
85        fail(device, callback, expected_msg_substring)
86    } else {
87        valid(device, callback)
88    }
89}
90
91fn did_fill_error_scope<T>(
92    device: &wgpu::Device,
93    callback: impl FnOnce() -> T,
94    filter: wgpu::ErrorFilter,
95) -> (bool, T) {
96    let scope = device.push_error_scope(filter);
97    let result = callback();
98    let validation_error = pollster::block_on(scope.pop());
99    let failed = validation_error.is_some();
100
101    (failed, result)
102}
103
104/// Returns true if the provided callback fails validation.
105pub fn did_fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> (bool, T) {
106    did_fill_error_scope(device, callback, wgpu::ErrorFilter::Validation)
107}
108
109/// Returns true if the provided callback encounters an out-of-memory error.
110pub fn did_oom<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> (bool, T) {
111    did_fill_error_scope(device, callback, wgpu::ErrorFilter::OutOfMemory)
112}
113
114/// Adds the necessary main function for our gpu test harness.
115///
116/// Takes a single argument which is an expression that evaluates to `Vec<wgpu_test::GpuTestInitializer>`.
117#[macro_export]
118macro_rules! gpu_test_main {
119    ($tests: expr) => {
120        #[cfg(target_arch = "wasm32")]
121        wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
122        #[cfg(target_arch = "wasm32")]
123        fn main() {
124            // Ensure that value is used so that warnings don't happen.
125            let _ = $tests;
126        }
127
128        #[cfg(not(target_arch = "wasm32"))]
129        fn main() -> $crate::native::MainResult {
130            $crate::native::main($tests)
131        }
132    };
133}