1#![cfg(not(target_arch = "wasm32"))]
2//! Infrastructure for the native, `cargo-nextest` based harness.
3//!
4//! This is largly used by [`gpu_test_main`](crate::gpu_test_main) and [`gpu_test`](crate::gpu_test).
56use std::{future::Future, pin::Pin};
78use parking_lot::Mutex;
910use crate::{
11 config::GpuTestConfiguration, params::TestInfo, report::AdapterReport, run::execute_test,
12 GpuTestInitializer,
13};
1415type NativeTestFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
1617struct NativeTest {
18 name: String,
19 future: NativeTestFuture,
20}
2122impl NativeTest {
23/// Adapter index is only used for naming the test, the adapters are matched based on the adapter info.
24fn from_configuration(
25 config: GpuTestConfiguration,
26 adapter_report: AdapterReport,
27 adapter_index: usize,
28 ) -> Self {
29let backend = adapter_report.info.backend;
30let device_name = &adapter_report.info.name;
3132let test_info = TestInfo::from_configuration(&config, &adapter_report);
3334let full_name = format!(
35"[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}",
36 running_msg = test_info.running_msg,
37 base_name = config.name,
38 );
39Self {
40 name: full_name,
41 future: Box::pin(async move {
42// Enable metal validation layers if we're running on metal.
43 //
44 // This is a process-wide setting as it's via environment variable, but all
45 // tests are run in separate processes.
46 //
47 // We don't do this in the instance initializer as we don't want to enable
48 // validation layers for the entire process, or other instances.
49 //
50 // We do not enable metal validation when running on moltenvk.
51let metal_validation = backend == wgpu::Backend::Metal;
5253let env_value = if metal_validation { "1" } else { "0" };
54 std::env::set_var("MTL_DEBUG_LAYER", env_value);
55if std::env::var("GITHUB_ACTIONS").as_deref() != Ok("true") {
56// Metal Shader Validation is entirely broken in the paravirtualized CI environment.
57std::env::set_var("MTL_SHADER_VALIDATION", env_value);
58 }
5960 execute_test(Some(&adapter_report), config, Some(test_info)).await;
61 }),
62 }
63 }
6465pub fn into_trial(self) -> libtest_mimic::Trial {
66 libtest_mimic::Trial::test(self.name, || {
67 pollster::block_on(self.future);
68Ok(())
69 })
70 }
71}
7273#[doc(hidden)]
74pub static TEST_LIST: Mutex<Vec<crate::GpuTestConfiguration>> = Mutex::new(Vec::new());
7576/// Return value for the main function.
77pub type MainResult = anyhow::Result<()>;
7879/// Main function that runs every gpu function once for every adapter on the system.
80pub fn main(tests: Vec<GpuTestInitializer>) -> MainResult {
81use anyhow::Context;
8283use crate::report::GpuReport;
8485// If this environment variable is set, we will only enumerate the noop backend. The
86 // main use case is running tests with miri, where we can't even enumerate adapters,
87 // as we cannot load DLLs or make any external calls.
88let use_noop = std::env::var("WGPU_GPU_TESTS_USE_NOOP_BACKEND").as_deref() == Ok("1");
8990let report = if use_noop {
91 GpuReport::noop_only()
92 } else {
93let config_text = {
94profiling::scope!("Reading .gpuconfig");
95&std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR")))
96 .context(
97"Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?",
98 )?
99};
100let mut report =
101 GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?;
102103// Filter out the adapters that are not part of WGPU_BACKEND.
104let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default();
105 report
106 .devices
107 .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend)));
108109 report
110 };
111112// Iterate through all the tests. Creating a test per adapter.
113execute_native(tests.into_iter().flat_map(|initializer| {
114let test = initializer();
115 report
116 .devices
117 .iter()
118 .enumerate()
119 .map(move |(adapter_index, adapter_report)| {
120 NativeTest::from_configuration(test.clone(), adapter_report.clone(), adapter_index)
121 })
122 }));
123124Ok(())
125}
126127fn execute_native(tests: impl IntoIterator<Item = NativeTest>) {
128let args = libtest_mimic::Arguments::from_args();
129let trials = {
130profiling::scope!("collecting tests");
131 tests.into_iter().map(NativeTest::into_trial).collect()
132 };
133134 libtest_mimic::run(&args, trials).exit_if_failed();
135}