wgpu_test/
lib.rs

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