wgpu_test/
run.rs

1use std::panic::AssertUnwindSafe;
2
3use futures_lite::FutureExt;
4use wgpu::{Adapter, Device, Instance, Queue};
5
6use crate::{
7    expectations::{expectations_match_failures, ExpectationMatchResult, FailureResult},
8    init::{init_logger, initialize_adapter, initialize_device},
9    isolation,
10    params::TestInfo,
11    report::AdapterReport,
12    GpuTestConfiguration,
13};
14
15/// Parameters and resources handed to the test function.
16#[derive(Hash)]
17pub struct TestingContext {
18    pub instance: Instance,
19    pub adapter: Adapter,
20    pub adapter_info: wgpu::AdapterInfo,
21    pub adapter_downlevel_capabilities: wgpu::DownlevelCapabilities,
22    pub device: Device,
23    pub device_features: wgpu::Features,
24    pub device_limits: wgpu::Limits,
25    pub queue: Queue,
26}
27
28/// Execute the given test configuration with the given adapter report.
29///
30/// If test_info is specified, will use the information whether to skip the test.
31/// If it is not, we'll create the test info from the adapter itself.
32pub async fn execute_test(
33    adapter_report: Option<&AdapterReport>,
34    config: GpuTestConfiguration,
35    test_info: Option<TestInfo>,
36) {
37    // If we get information externally, skip based on that information before we do anything.
38    if let Some(TestInfo { skip: true, .. }) = test_info {
39        return;
40    }
41
42    init_logger();
43
44    let _test_guard = isolation::OneTestPerProcessGuard::new();
45
46    let Some((instance, adapter, _surface_guard)) =
47        initialize_adapter(adapter_report, &config.params).await
48    else {
49        return;
50    };
51
52    let adapter_info = adapter.get_info();
53    let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities();
54
55    let test_info = test_info.unwrap_or_else(|| {
56        let adapter_report = AdapterReport::from_adapter(&adapter);
57        TestInfo::from_configuration(&config, &adapter_report)
58    });
59
60    // We are now guaranteed to have information about this test, so skip if we need to.
61    if test_info.skip {
62        log::info!("TEST RESULT: SKIPPED");
63        return;
64    }
65
66    // Print the name of the test.
67    log::info!("TEST: {}", config.name);
68
69    let (device, queue) = pollster::block_on(initialize_device(
70        &adapter,
71        config.params.required_features,
72        config.params.required_limits.clone(),
73    ));
74
75    let context = TestingContext {
76        instance,
77        adapter,
78        adapter_info,
79        adapter_downlevel_capabilities,
80        device,
81        device_features: config.params.required_features,
82        device_limits: config.params.required_limits.clone(),
83        queue,
84    };
85
86    let mut failures = Vec::new();
87
88    // Run the test, and catch panics (possibly due to failed assertions).
89    let panic_res = AssertUnwindSafe((config.test.as_ref().unwrap())(context))
90        .catch_unwind()
91        .await;
92
93    if let Err(panic) = panic_res {
94        let message = panic
95            .downcast_ref::<&str>()
96            .copied()
97            .or_else(|| panic.downcast_ref::<String>().map(String::as_str));
98
99        let result = FailureResult::panic();
100
101        let result = if let Some(panic_str) = message {
102            result.with_message(panic_str)
103        } else {
104            result
105        };
106
107        failures.push(result)
108    }
109
110    // Check whether any validation errors were reported during the test run.
111    cfg_if::cfg_if!(
112        if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] {
113            failures.extend(wgpu::hal::VALIDATION_CANARY.get_and_reset().into_iter().map(|msg| FailureResult::validation_error().with_message(msg)));
114        } else if #[cfg(all(target_arch = "wasm32", feature = "webgl"))] {
115            if _surface_guard.unwrap().check_for_unreported_errors() {
116                failures.push(FailureResult::validation_error());
117            }
118        } else {
119        }
120    );
121
122    // The call to matches_failure will log.
123    if expectations_match_failures(&test_info.failures, failures) == ExpectationMatchResult::Panic {
124        panic!(
125            "{}: test {:?} did not behave as expected",
126            config.location, config.name
127        );
128    }
129    // Print the name of the test.
130    log::info!("TEST FINISHED: {}", config.name);
131}