test: add expected msg. arg. to wgpu_test::fail{,_if}

This commit is contained in:
Erich Gubler 2024-02-22 14:21:35 -05:00
parent c7a16b36b1
commit 94dba0b026
16 changed files with 652 additions and 432 deletions

View File

@ -27,10 +27,28 @@ pub use run::{execute_test, TestingContext};
pub use wgpu_macros::gpu_test;
/// Run some code in an error scope and assert that validation fails.
pub fn fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
pub fn fail<T>(
device: &wgpu::Device,
callback: impl FnOnce() -> T,
expected_msg_substring: Option<&'static str>,
) -> T {
device.push_error_scope(wgpu::ErrorFilter::Validation);
let result = callback();
assert!(pollster::block_on(device.pop_error_scope()).is_some());
let validation_error = pollster::block_on(device.pop_error_scope())
.expect("expected validation error in callback, but no validation error was emitted");
if let Some(expected_msg_substring) = expected_msg_substring {
let lowered_expected = expected_msg_substring.to_lowercase();
let lowered_actual = validation_error.to_string().to_lowercase();
assert!(
lowered_actual.contains(&lowered_expected),
concat!(
"expected validation error case-insensitively containing {:?}, ",
"but it was not present in actual error message:\n{:?}"
),
expected_msg_substring,
validation_error
);
}
result
}
@ -46,9 +64,14 @@ pub fn valid<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
/// Run some code in an error scope and assert that validation succeeds or fails depending on the
/// provided `should_fail` boolean.
pub fn fail_if<T>(device: &wgpu::Device, should_fail: bool, callback: impl FnOnce() -> T) -> T {
pub fn fail_if<T>(
device: &wgpu::Device,
should_fail: bool,
callback: impl FnOnce() -> T,
expected_msg_substring: Option<&'static str>,
) -> T {
if should_fail {
fail(device, callback)
fail(device, callback, expected_msg_substring)
} else {
valid(device, callback)
}

View File

@ -368,9 +368,13 @@ fn separate_programs_have_incompatible_derived_bgls(ctx: TestingContext) {
pass.set_bind_group(0, &bg2, &[]);
pass.dispatch_workgroups(1, 1, 1);
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
drop(pass);
});
},
None,
);
}
#[gpu_test]
@ -436,7 +440,11 @@ fn derived_bgls_incompatible_with_regular_bgls(ctx: TestingContext) {
pass.set_bind_group(0, &bg, &[]);
pass.dispatch_workgroups(1, 1, 1);
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
drop(pass);
})
},
None,
)
}

View File

@ -217,7 +217,9 @@ static MINIMUM_BUFFER_BINDING_SIZE_LAYOUT: GpuTestConfiguration = GpuTestConfigu
push_constant_ranges: &[],
});
wgpu_test::fail(&ctx.device, || {
wgpu_test::fail(
&ctx.device,
|| {
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
@ -227,7 +229,9 @@ static MINIMUM_BUFFER_BINDING_SIZE_LAYOUT: GpuTestConfiguration = GpuTestConfigu
compilation_options: Default::default(),
cache: None,
});
});
},
None,
);
});
/// The WebGPU algorithm [validating shader binding][vsb] requires
@ -314,7 +318,9 @@ static MINIMUM_BUFFER_BINDING_SIZE_DISPATCH: GpuTestConfiguration = GpuTestConfi
}],
});
wgpu_test::fail(&ctx.device, || {
wgpu_test::fail(
&ctx.device,
|| {
let mut encoder = ctx.device.create_command_encoder(&Default::default());
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
@ -328,7 +334,9 @@ static MINIMUM_BUFFER_BINDING_SIZE_DISPATCH: GpuTestConfiguration = GpuTestConfi
drop(pass);
let _ = encoder.finish();
});
},
None,
);
});
#[gpu_test]

View File

@ -12,9 +12,12 @@ fn try_copy(
) {
let buffer = ctx.device.create_buffer(&BUFFER_DESCRIPTOR);
let data = vec![255; size as usize];
fail_if(&ctx.device, should_fail, || {
ctx.queue.write_buffer(&buffer, offset, &data)
});
fail_if(
&ctx.device,
should_fail,
|| ctx.queue.write_buffer(&buffer, offset, &data),
None,
);
}
#[gpu_test]

View File

@ -31,14 +31,19 @@ fn try_create(ctx: TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) {
.iter()
.flat_map(|&(expect_error, usages)| usages.iter().copied().map(move |u| (expect_error, u)))
{
fail_if(&ctx.device, expect_validation_error, || {
fail_if(
&ctx.device,
expect_validation_error,
|| {
let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: BUFFER_SIZE,
usage,
mapped_at_creation: false,
});
});
},
None,
);
}
}
@ -89,14 +94,19 @@ async fn map_test(
let mut buffer = None;
fail_if(&ctx.device, buffer_creation_validation_error, || {
fail_if(
&ctx.device,
buffer_creation_validation_error,
|| {
buffer = Some(ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size,
usage,
mapped_at_creation: false,
}));
});
},
None,
);
if buffer_creation_validation_error {
return;
}
@ -107,9 +117,14 @@ async fn map_test(
|| (map_mode_type == Ma::Read && !usage.contains(Bu::MAP_READ))
|| (map_mode_type == Ma::Write && !usage.contains(Bu::MAP_WRITE));
fail_if(&ctx.device, map_async_validation_error, || {
fail_if(
&ctx.device,
map_async_validation_error,
|| {
buffer.slice(0..size).map_async(map_mode_type, |_| {});
});
},
None,
);
if map_async_validation_error {
return;

View File

@ -298,23 +298,33 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
// the device is not valid.
// Creating a command encoder should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
});
},
None,
);
// Creating a buffer should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
});
},
None,
);
// Creating a texture should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
@ -329,10 +339,14 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
usage: wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
});
},
None,
);
// Texture clear should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_clear.clear_texture(
&texture_for_write,
&wgpu::ImageSubresourceRange {
@ -343,18 +357,26 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
array_layer_count: None,
},
);
});
},
None,
);
// Creating a compute pass should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_compute_pass.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: None,
timestamp_writes: None,
});
});
},
None,
);
// Creating a render pass should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_render_pass.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
@ -366,10 +388,14 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
timestamp_writes: None,
occlusion_query_set: None,
});
});
},
None,
);
// Copying a buffer to a buffer should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_buffer_buffer_copy.copy_buffer_to_buffer(
&buffer_source,
0,
@ -377,10 +403,14 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
0,
256,
);
});
},
None,
);
// Copying a buffer to a texture should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_buffer_texture_copy.copy_buffer_to_texture(
wgpu::ImageCopyBuffer {
buffer: &buffer_source,
@ -393,10 +423,14 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
texture_for_write.as_image_copy(),
texture_extent,
);
});
},
None,
);
// Copying a texture to a buffer should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_texture_buffer_copy.copy_texture_to_buffer(
texture_for_read.as_image_copy(),
wgpu::ImageCopyBuffer {
@ -409,28 +443,40 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
},
texture_extent,
);
});
},
None,
);
// Copying a texture to a texture should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
encoder_for_texture_texture_copy.copy_texture_to_texture(
texture_for_read.as_image_copy(),
texture_for_write.as_image_copy(),
texture_extent,
);
});
},
None,
);
// Creating a bind group layout should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[],
});
});
},
None,
);
// Creating a bind group should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bind_group_layout,
@ -441,38 +487,54 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
),
}],
});
});
},
None,
);
// Creating a pipeline layout should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
});
},
None,
);
// Creating a shader module should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
});
});
},
None,
);
// Creating a shader module spirv should fail.
fail(&ctx.device, || unsafe {
fail(
&ctx.device,
|| unsafe {
ctx.device
.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
label: None,
source: std::borrow::Cow::Borrowed(&[]),
});
});
},
None,
);
// Creating a render pipeline should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
@ -490,10 +552,14 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
multiview: None,
cache: None,
});
});
},
None,
);
// Creating a compute pipeline should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
@ -503,19 +569,46 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
compilation_options: Default::default(),
cache: None,
});
},
None,
);
// Creating a compute pipeline should fail.
fail(
&ctx.device,
|| {
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
layout: None,
module: &shader_module,
entry_point: "",
compilation_options: Default::default(),
cache: None,
});
},
None,
);
// Buffer map should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
buffer_for_map
.slice(..)
.map_async(wgpu::MapMode::Write, |_| ());
});
},
None,
);
// Buffer unmap should fail.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
buffer_for_unmap.unmap();
});
},
None,
);
});
#[gpu_test]

View File

@ -58,10 +58,14 @@ static DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::ne
});
// Set a bad viewport on renderpass, triggering an error.
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
renderpass.set_viewport(0.0, 0.0, -1.0, -1.0, 0.0, 1.0);
drop(renderpass);
});
},
None,
);
// This is the actual interesting error condition. We've created
// a CommandEncoder which errored out when processing a command.

View File

@ -264,7 +264,10 @@ static IMAGE_BITMAP_IMPORT: GpuTestConfiguration =
view_formats: &[],
});
fail_if(&ctx.device, !valid, || {
fail_if(
&ctx.device,
!valid,
|| {
ctx.queue.copy_external_image_to_texture(
&wgpu::ImageCopyExternalImage {
source: source.clone(),
@ -281,7 +284,9 @@ static IMAGE_BITMAP_IMPORT: GpuTestConfiguration =
},
copy_size,
);
});
},
None,
);
let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("readback buffer"),

View File

@ -58,9 +58,13 @@ static FLOAT32_FILTERABLE_WITHOUT_FEATURE: GpuTestConfiguration = GpuTestConfigu
// Float 32 textures can be used as non-filterable only
create_texture_binding(device, wgpu::TextureFormat::R32Float, false);
// This is supposed to fail, since we have not activated the feature
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
create_texture_binding(device, wgpu::TextureFormat::R32Float, true);
});
},
None,
);
});
#[gpu_test]

View File

@ -18,11 +18,15 @@ static BUFFER_DESTROY: GpuTestConfiguration =
.await
.panic_on_timeout();
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
buffer
.slice(..)
.map_async(wgpu::MapMode::Write, move |_| {});
});
},
None,
);
buffer.destroy();

View File

@ -141,12 +141,16 @@ static NV12_TEXTURE_VIEW_PLANE_ON_NON_PLANAR_FORMAT: GpuTestConfiguration =
sample_count: 1,
view_formats: &[],
});
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
let _ = tex.create_view(&wgpu::TextureViewDescriptor {
aspect: wgpu::TextureAspect::Plane0,
..Default::default()
});
});
},
None,
);
});
#[gpu_test]
@ -168,13 +172,17 @@ static NV12_TEXTURE_VIEW_PLANE_OUT_OF_BOUNDS: GpuTestConfiguration = GpuTestConf
sample_count: 1,
view_formats: &[],
});
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
let _ = tex.create_view(&wgpu::TextureViewDescriptor {
format: Some(wgpu::TextureFormat::R8Unorm),
aspect: wgpu::TextureAspect::Plane2,
..Default::default()
});
});
},
None,
);
});
#[gpu_test]
@ -196,13 +204,17 @@ static NV12_TEXTURE_BAD_FORMAT_VIEW_PLANE: GpuTestConfiguration = GpuTestConfigu
sample_count: 1,
view_formats: &[],
});
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
let _ = tex.create_view(&wgpu::TextureViewDescriptor {
format: Some(wgpu::TextureFormat::Rg8Unorm),
aspect: wgpu::TextureAspect::Plane0,
..Default::default()
});
});
},
None,
);
});
#[gpu_test]
@ -215,7 +227,9 @@ static NV12_TEXTURE_BAD_SIZE: GpuTestConfiguration = GpuTestConfiguration::new()
depth_or_array_layers: 1,
};
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
@ -226,5 +240,7 @@ static NV12_TEXTURE_BAD_SIZE: GpuTestConfiguration = GpuTestConfiguration::new()
sample_count: 1,
view_formats: &[],
});
});
},
None,
);
});

View File

@ -13,7 +13,9 @@ static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfigu
.run_sync(|ctx| {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
let module = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
@ -21,8 +23,8 @@ static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfigu
source: wgpu::ShaderSource::Wgsl("not valid wgsl".into()),
});
let pipeline = ctx
.device
let pipeline =
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("mandelbrot compute pipeline"),
layout: None,
@ -33,7 +35,9 @@ static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfigu
});
pipeline.get_bind_group_layout(0);
});
},
None,
);
});
const TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {
@ -47,7 +51,9 @@ const TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderMod
static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_sync(|ctx| {
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
// Testing multisampling is important, because some backends don't behave well if one
// tries to compile code in an unsupported multisample count. Failing to validate here
// has historically resulted in requesting the back end to compile code.
@ -57,7 +63,9 @@ static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new()
label: None,
layout: None,
vertex: wgpu::VertexState {
module: &ctx.device.create_shader_module(TRIVIAL_VERTEX_SHADER_DESC),
module: &ctx
.device
.create_shader_module(TRIVIAL_VERTEX_SHADER_DESC),
entry_point: "main",
compilation_options: Default::default(),
buffers: &[],
@ -73,7 +81,9 @@ static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new()
cache: None,
});
}
})
},
None,
)
// TODO: concrete error message:
// At least one color attachment or depth-stencil attachment was expected, but no
// render target for the pipeline was specified.

View File

@ -22,7 +22,9 @@ static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration =
let data = vec![255; 128];
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
ctx.queue.write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
@ -43,5 +45,7 @@ static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration =
depth_or_array_layers: 4294967295,
},
);
});
},
None,
);
});

View File

@ -4,26 +4,34 @@ use wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration};
static BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
// Create a buffer with bad parameters and call a few methods.
// Validation should fail but there should be not panic.
let buffer = fail(&ctx.device, || {
let buffer = fail(
&ctx.device,
|| {
ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 99999999,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE,
mapped_at_creation: false,
})
});
},
None,
);
fail(&ctx.device, || {
buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {})
});
fail(&ctx.device, || buffer.unmap());
fail(
&ctx.device,
|| buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {}),
None,
);
fail(&ctx.device, || buffer.unmap(), None);
valid(&ctx.device, || buffer.destroy());
valid(&ctx.device, || buffer.destroy());
});
#[gpu_test]
static BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
let texture = fail(&ctx.device, || {
let texture = fail(
&ctx.device,
|| {
ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
@ -38,11 +46,17 @@ static BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(
usage: wgpu::TextureUsages::all(),
view_formats: &[],
})
});
},
None,
);
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
let _ = texture.create_view(&wgpu::TextureViewDescriptor::default());
});
},
None,
);
valid(&ctx.device, || texture.destroy());
valid(&ctx.device, || texture.destroy());
});

View File

@ -8,7 +8,10 @@ static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new().
let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR);
let data = vec![255; BUFFER_SIZE as usize];
fail_if(&ctx.device, should_panic, || {
fail_if(
&ctx.device,
should_panic,
|| {
ctx.queue.write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
@ -20,7 +23,9 @@ static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new().
BUFFER_COPY_LAYOUT,
size,
)
});
},
None,
);
};
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false);

View File

@ -35,7 +35,9 @@ static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new().run_s
view_formats: &[],
});
fail(&ctx.device, || {
fail(
&ctx.device,
|| {
// Validation should catch the silly selected z layer range without panicking.
encoder.copy_texture_to_texture(
wgpu::ImageCopyTexture {
@ -61,5 +63,7 @@ static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new().run_s
},
);
ctx.queue.submit(Some(encoder.finish()));
});
},
None,
);
});