Add support for running tests on the noop backend

This commit is contained in:
Connor Fitzgerald 2025-07-13 03:48:52 -04:00
parent fdc4d421e7
commit 86d5211f84
7 changed files with 86 additions and 27 deletions

View File

@ -36,7 +36,7 @@ use core::fmt;
/// [skip]: super::TestParameters::skip
/// [expect_fail]: super::TestParameters::expect_fail
/// [`AdapterInfo`]: wgpu::AdapterInfo
#[derive(Default, Clone)]
#[derive(Default, Clone, PartialEq)]
pub struct FailureCase {
/// Backends expected to fail, or `None` for any backend.
///
@ -334,7 +334,7 @@ impl FailureReason {
}
}
#[derive(Default, Clone)]
#[derive(Default, Clone, PartialEq)]
pub enum FailureBehavior {
/// Assert that the test fails for the given reason.
///

View File

@ -65,8 +65,15 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) ->
..Default::default()
}
.with_env(),
// TODO(https://github.com/gfx-rs/wgpu/issues/7119): Enable noop backend?
noop: wgpu::NoopBackendOptions::default(),
// Allow the noop backend to be used in tests. This will not be used unless
// WGPU_GPU_TESTS_USE_NOOP_BACKEND env var is set, because wgpu-info will not
// enumerate the noop backend.
//
// However, we use wasm_bindgen_test to run tests on wasm, and wgpu
// will chose the noop on wasm32 for some reason.
noop: wgpu::NoopBackendOptions {
enable: !cfg!(target_arch = "wasm32"),
},
},
})
}

View File

@ -81,19 +81,32 @@ pub fn main() -> MainResult {
use crate::report::GpuReport;
let config_text = {
profiling::scope!("Reading .gpuconfig");
&std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR")))
.context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?
};
let mut report =
GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?;
// If this environment variable is set, we will only enumerate the noop backend. The
// main use case is running tests with miri, where we can't even enumerate adapters,
// as we cannot load DLLs or make any external calls.
let use_noop = std::env::var("WGPU_GPU_TESTS_USE_NOOP_BACKEND").as_deref() == Ok("1");
// Filter out the adapters that are not part of WGPU_BACKEND.
let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default();
report
.devices
.retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend)));
let report = if use_noop {
GpuReport::noop_only()
} else {
let config_text = {
profiling::scope!("Reading .gpuconfig");
&std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR")))
.context(
"Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?",
)?
};
let mut report =
GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?;
// Filter out the adapters that are not part of WGPU_BACKEND.
let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default();
report
.devices
.retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend)));
report
};
let mut test_guard = TEST_LIST.lock();
// Iterate through all the tests. Creating a test per adapter.

View File

@ -41,7 +41,9 @@ impl Default for TestParameters {
required_limits: Limits::downlevel_webgl2_defaults(),
required_instance_flags: InstanceFlags::empty(),
force_fxc: false,
skips: Vec::new(),
// By default we skip the noop backend, and enable it if the test
// parameters ask us to remove it.
skips: vec![FailureCase::backend(wgpu::Backends::NOOP)],
failures: Vec::new(),
}
}
@ -94,6 +96,16 @@ impl TestParameters {
self.skips.push(when);
self
}
/// Enable testing against the noop backend and miri.
///
/// The noop backend does not execute any operations, but allows us to test
/// validation and memory safety.
pub fn enable_noop(mut self) -> Self {
self.skips
.retain(|case| *case != FailureCase::backend(wgpu::Backends::NOOP));
self
}
}
/// Information about a test, including if if it should be skipped.

View File

@ -15,6 +15,20 @@ pub(crate) struct GpuReport {
}
impl GpuReport {
#[cfg(not(target_arch = "wasm32"))]
/// Creates a new GpuReport with a single noop adapter.
pub(crate) fn noop_only() -> Self {
GpuReport {
devices: vec![AdapterReport {
info: wgpu::hal::noop::adapter_info(),
features: Features::all(),
limits: wgpu::hal::noop::CAPABILITIES.limits,
downlevel_caps: wgpu::hal::noop::CAPABILITIES.downlevel,
texture_format_features: HashMap::new(), // todo
}],
}
}
#[cfg_attr(target_arch = "wasm32", allow(unused))]
pub(crate) fn from_json(file: &str) -> serde_json::Result<Self> {
profiling::scope!("Parsing .gpuconfig");

View File

@ -120,22 +120,34 @@ impl crate::Instance for Context {
) -> Vec<crate::ExposedAdapter<Api>> {
vec![crate::ExposedAdapter {
adapter: Context,
info: wgt::AdapterInfo {
name: String::from("noop wgpu backend"),
vendor: 0,
device: 0,
device_type: wgt::DeviceType::Cpu,
driver: String::from("wgpu"),
driver_info: String::new(),
backend: wgt::Backend::Noop,
},
info: adapter_info(),
features: wgt::Features::all(),
capabilities: CAPABILITIES,
}]
}
}
const CAPABILITIES: crate::Capabilities = {
/// Returns the adapter info for the noop backend.
///
/// This is used in the test harness to construct info about
/// the noop backend adapter without actually initializing wgpu.
pub fn adapter_info() -> wgt::AdapterInfo {
wgt::AdapterInfo {
name: String::from("noop wgpu backend"),
vendor: 0,
device: 0,
device_type: wgt::DeviceType::Cpu,
driver: String::from("wgpu"),
driver_info: String::new(),
backend: wgt::Backend::Noop,
}
}
/// The capabilities of the noop backend.
///
/// This is used in the test harness to construct capabilities
/// of the noop backend without actually initializing wgpu.
pub const CAPABILITIES: crate::Capabilities = {
/// Guaranteed to be no bigger than isize::MAX which is the maximum size of an allocation,
/// except on 16-bit platforms which we certainly dont fit in.
const ALLOC_MAX_U32: u32 = i32::MAX as u32;

View File

@ -22,6 +22,7 @@ pub fn run_miri(shell: Shell, mut args: Arguments) -> anyhow::Result<()> {
"MIRIFLAGS",
"-Zmiri-disable-isolation -Zmiri-deterministic-floats",
)
.env("WGPU_GPU_TESTS_USE_NOOP_BACKEND", "1")
.quiet()
.run()?;