Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ spirv = "0.3"
static_assertions = "1.1"
strum = { version = "0.27.1", default-features = false, features = ["derive"] }
syn = "2.0.98"
tempfile = "3"
toml = "0.9.0"
trybuild = "1"
tracy-client = "0.18"
Expand Down
3 changes: 3 additions & 0 deletions cts_runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ deno_webidl.workspace = true
deno_webgpu.workspace = true
tokio = { workspace = true, features = ["full"] }
termcolor.workspace = true

[dev-dependencies]
tempfile.workspace = true
17 changes: 17 additions & 0 deletions cts_runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# cts_runner

This crate contains infrastructure for running the WebGPU conformance tests on
Deno's `wgpu`-based implementation of WebGPU.

Instructions for running the tests via the CTS `xtask` are in the
[top-level README](https://github.com/gfx-rs/wgpu/blob/trunk/README.md#webgpu-conformance-test-suite).
The file [revision.txt](./revision.txt) specifies the version of the CTS that
will be used by default.

`cts_runner` is somewhat misnamed at this point, in that it is useful for
things other than just running the CTS:

- The [tests](./tests) directory contains a few directed tests for
Deno's bindings to `wgpu`.
- Standalone JavaScript snippets that use WebGPU can be run
with a command like: `cargo run -p cts_runner -- test.js`.
19 changes: 19 additions & 0 deletions cts_runner/src/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,25 @@ const windowOrWorkerGlobalScope = {

windowOrWorkerGlobalScope.console.enumerable = false;

// Print uncaptured WebGPU errors to stderr. This is useful when running
// standalone JavaScript test snippets. It isn't needed for the CTS, because the
// CTS uses error scopes. (The CTS also installs its own error handler with
// `addEventListener`, so having this here may result in printing duplicate
// errors from the CTS in some cases.) Printing uncaptured errors to stderr
// isn't desired as built-in behavior in Deno, because the console is reserved
// for the application.
//
// Note that catching an error here _does not_ result in a non-zero exit status.
const requestDevice = webgpu.GPUAdapter.prototype.requestDevice;
webgpu.GPUAdapter.prototype.requestDevice = function(desc) {
return requestDevice.call(this, desc).then((device) => {
device.onuncapturederror = (event) => {
core.print("cts_runner caught WebGPU error:" + event.error.message, true);
};
return device;
})
};

const mainRuntimeGlobalProperties = {
Window: globalInterfaces.windowConstructorDescriptor,
window: util.readOnly(globalThis),
Expand Down
5 changes: 0 additions & 5 deletions cts_runner/tests/features.js

This file was deleted.

84 changes: 60 additions & 24 deletions cts_runner/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
// As of June 2025, these tests are not run in CI.

use std::{
fmt::{self, Debug, Display},
ffi::OsStr,
io::Write,
path::PathBuf,
process::Command,
process::{Command, Output},
str,
};

use tempfile::NamedTempFile;

pub fn target_dir() -> PathBuf {
let current_exe = std::env::current_exe().unwrap();
let target_dir = current_exe.parent().unwrap().parent().unwrap();
Expand All @@ -24,38 +27,71 @@ pub fn cts_runner_exe_path() -> PathBuf {
p
}

pub struct JsError;
fn exec_cts_runner(script_file: impl AsRef<OsStr>) -> Output {
Command::new(cts_runner_exe_path())
.arg(script_file)
.output()
.unwrap()
}

impl Display for JsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "JavaScript test returned an error")
}
fn exec_js_file(script_file: impl AsRef<OsStr>) {
let output = exec_cts_runner(script_file);
println!("{}", str::from_utf8(&output.stdout).unwrap());
eprintln!("{}", str::from_utf8(&output.stderr).unwrap());
assert!(output.status.success());
}

impl Debug for JsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
fn check_js_stderr(script: &str, expected: &str) {
let mut tempfile = NamedTempFile::new().unwrap();
tempfile.write_all(script.as_bytes()).unwrap();
tempfile.flush().unwrap();
let output = exec_cts_runner(tempfile.path());
assert!(
output.stdout.is_empty(),
"unexpected output on stdout: {}",
str::from_utf8(&output.stdout).unwrap(),
);
assert_eq!(str::from_utf8(&output.stderr).unwrap(), expected);
assert!(output.status.success());
}

type JsResult = Result<(), JsError>;
fn exec_js(script: &str) {
check_js_stderr(script, "");
}

fn exec_js_test(script: &str) -> JsResult {
let output = Command::new(cts_runner_exe_path())
.arg(script)
.output()
.unwrap();
println!("{}", str::from_utf8(&output.stdout).unwrap());
eprintln!("{}", str::from_utf8(&output.stderr).unwrap());
output.status.success().then_some(()).ok_or(JsError)
#[test]
fn hello_compute_example() {
exec_js_file("examples/hello-compute.js");
}

#[test]
fn hello_compute_example() -> JsResult {
exec_js_test("examples/hello-compute.js")
fn features() {
exec_js(
r#"
const adapter = await navigator.gpu.requestAdapter();

if (adapter.features.has("mappable-primary-buffers")) {
throw new TypeError("Adapter should not report support for wgpu native-only features");
}
"#,
);
}

#[test]
fn features() -> JsResult {
exec_js_test("tests/features.js")
fn uncaptured_error() {
check_js_stderr(
r#"
const code = `const val: u32 = 1.1;`;

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
device.createShaderModule({ code })
"#,
"cts_runner caught WebGPU error:
Shader '' parsing error: the type of `val` is expected to be `u32`, but got `{AbstractFloat}`
┌─ wgsl:1:7
1 │ const val: u32 = 1.1;
│ ^^^ definition of `val`\n\n",
);
}
16 changes: 9 additions & 7 deletions deno_webgpu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ a
[wgpu trace](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications#tracing-infrastructure)
to the specified directory.

For testing this op crate will make use of the WebGPU conformance tests suite,
running through our WPT runner. This will be used to validate implementation
conformance.

GitHub CI doesn't run with GPUs, so testing relies on software like DX WARP &
Vulkan lavapipe. Currently, only using DX WARP works, so tests are only run on
Windows.
This op crate is tested primarily by running the
[WebGPU conformance test suite](https://github.com/gpuweb/cts) using `wgpu`'s
[`cts_runner`](https://github.com/gfx-rs/wgpu/blob/trunk/README.md#webgpu-conformance-test-suite).
`cts_runner` also has a few
[directed tests](https://github.com/gfx-rs/wgpu/tree/trunk/cts_runner/tests)
to fill in missing coverage.

GPU availability in GitHub CI is limited, so some configurations rely on
software like DX WARP & Vulkan lavapipe.

## Links

Expand Down
Loading