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 /// [skip]: super::TestParameters::skip
/// [expect_fail]: super::TestParameters::expect_fail /// [expect_fail]: super::TestParameters::expect_fail
/// [`AdapterInfo`]: wgpu::AdapterInfo /// [`AdapterInfo`]: wgpu::AdapterInfo
#[derive(Default, Clone)] #[derive(Default, Clone, PartialEq)]
pub struct FailureCase { pub struct FailureCase {
/// Backends expected to fail, or `None` for any backend. /// 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 { pub enum FailureBehavior {
/// Assert that the test fails for the given reason. /// 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() ..Default::default()
} }
.with_env(), .with_env(),
// TODO(https://github.com/gfx-rs/wgpu/issues/7119): Enable noop backend? // Allow the noop backend to be used in tests. This will not be used unless
noop: wgpu::NoopBackendOptions::default(), // 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,10 +81,20 @@ pub fn main() -> MainResult {
use crate::report::GpuReport; use crate::report::GpuReport;
// 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");
let report = if use_noop {
GpuReport::noop_only()
} else {
let config_text = { let config_text = {
profiling::scope!("Reading .gpuconfig"); profiling::scope!("Reading .gpuconfig");
&std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) &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`?")? .context(
"Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?",
)?
}; };
let mut report = let mut report =
GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?; GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?;
@ -95,6 +105,9 @@ pub fn main() -> MainResult {
.devices .devices
.retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend))); .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend)));
report
};
let mut test_guard = TEST_LIST.lock(); let mut test_guard = TEST_LIST.lock();
// Iterate through all the tests. Creating a test per adapter. // Iterate through all the tests. Creating a test per adapter.
execute_native(test_guard.drain(..).flat_map(|test| { execute_native(test_guard.drain(..).flat_map(|test| {

View File

@ -41,7 +41,9 @@ impl Default for TestParameters {
required_limits: Limits::downlevel_webgl2_defaults(), required_limits: Limits::downlevel_webgl2_defaults(),
required_instance_flags: InstanceFlags::empty(), required_instance_flags: InstanceFlags::empty(),
force_fxc: false, 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(), failures: Vec::new(),
} }
} }
@ -94,6 +96,16 @@ impl TestParameters {
self.skips.push(when); self.skips.push(when);
self 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. /// Information about a test, including if if it should be skipped.

View File

@ -15,6 +15,20 @@ pub(crate) struct GpuReport {
} }
impl 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))] #[cfg_attr(target_arch = "wasm32", allow(unused))]
pub(crate) fn from_json(file: &str) -> serde_json::Result<Self> { pub(crate) fn from_json(file: &str) -> serde_json::Result<Self> {
profiling::scope!("Parsing .gpuconfig"); profiling::scope!("Parsing .gpuconfig");

View File

@ -120,7 +120,19 @@ impl crate::Instance for Context {
) -> Vec<crate::ExposedAdapter<Api>> { ) -> Vec<crate::ExposedAdapter<Api>> {
vec![crate::ExposedAdapter { vec![crate::ExposedAdapter {
adapter: Context, adapter: Context,
info: wgt::AdapterInfo { info: adapter_info(),
features: wgt::Features::all(),
capabilities: 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"), name: String::from("noop wgpu backend"),
vendor: 0, vendor: 0,
device: 0, device: 0,
@ -128,14 +140,14 @@ impl crate::Instance for Context {
driver: String::from("wgpu"), driver: String::from("wgpu"),
driver_info: String::new(), driver_info: String::new(),
backend: wgt::Backend::Noop, backend: wgt::Backend::Noop,
},
features: wgt::Features::all(),
capabilities: CAPABILITIES,
}]
} }
} }
const CAPABILITIES: crate::Capabilities = { /// 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, /// 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. /// except on 16-bit platforms which we certainly dont fit in.
const ALLOC_MAX_U32: u32 = i32::MAX as u32; 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", "MIRIFLAGS",
"-Zmiri-disable-isolation -Zmiri-deterministic-floats", "-Zmiri-disable-isolation -Zmiri-deterministic-floats",
) )
.env("WGPU_GPU_TESTS_USE_NOOP_BACKEND", "1")
.quiet() .quiet()
.run()?; .run()?;