use std::path::{Path, PathBuf};
fn workspace_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(Path::parent)
.expect("crates/frust-gpu has a grandparent (the workspace root)")
.to_path_buf()
}
#[test]
fn shader_directory_is_scanned_when_present_and_tolerated_when_absent() {
let engine_crate = workspace_root().join("crates/frust-engine");
let engine_shaders = engine_crate.join("shaders");
if !engine_crate.exists() {
return;
}
assert!(
engine_shaders.is_dir(),
"crates/frust-engine exists but {} is missing — this test's shader-dir tripwire has \
decayed",
engine_shaders.display()
);
let violations = frust_gpu::lint::lint_wgsl_dir(&engine_shaders)
.unwrap_or_else(|err| panic!("shader directory could not be scanned: {err}"));
assert!(
violations.is_empty(),
"Shader violations found: {:?}",
violations
);
}
#[test]
fn shader_directory_scan_flags_a_real_violation() {
let tmpdir = tempfile::tempdir().expect("failed to create temp dir");
std::fs::write(
tmpdir.path().join("bad.wgsl"),
"@compute @workgroup_size(1, 1, 1) fn main() {}",
)
.expect("write fixture shader");
let violations = frust_gpu::lint::lint_wgsl_dir(tmpdir.path())
.expect("scan of a readable temp dir must not fail");
assert!(
!violations.is_empty(),
"a directory containing a design-rule-violating shader must not scan clean"
);
}
#[test]
fn frust_gpu_requests_minimal_features() {
let allowed_features = if cfg!(feature = "perf-trace") {
wgpu::Features::TIMESTAMP_QUERY
} else {
wgpu::Features::empty()
};
if cfg!(feature = "perf-trace") {
assert_eq!(
allowed_features,
wgpu::Features::TIMESTAMP_QUERY,
"perf-trace feature must enable only TIMESTAMP_QUERY"
);
} else {
assert_eq!(
allowed_features,
wgpu::Features::empty(),
"Default feature set must be empty"
);
}
}
#[test]
fn caps_fake_webgl2_has_no_storage_buffers() {
let caps = frust_gpu::caps::TierCaps::fake(frust_gpu::caps::DownlevelProfile::WebGl2);
assert!(
!caps.has_storage_buffers,
"WebGL2 profile must report has_storage_buffers=false (rule E2)"
);
}
#[test]
fn caps_fake_desktop_has_storage_buffers() {
let caps = frust_gpu::caps::TierCaps::fake(frust_gpu::caps::DownlevelProfile::Full);
assert!(
caps.has_storage_buffers,
"Full profile must report has_storage_buffers=true"
);
}
#[test]
fn caps_webgl2_max_uniform_size_16_kib() {
let limits = wgpu::Limits::downlevel_webgl2_defaults();
assert_eq!(
limits.max_uniform_buffer_binding_size,
16 << 10,
"WebGL2 uniform buffer max size must be 16 KiB (rule E6)"
);
}
#[test]
fn caps_webgl2_max_bind_groups_4() {
let limits = wgpu::Limits::downlevel_webgl2_defaults();
assert!(
limits.max_bind_groups <= 4,
"WebGL2 max bind groups must be ≤4 (rule E8)"
);
}
#[test]
fn caps_webgl2_sample_count_1() {
let caps = frust_gpu::caps::TierCaps::fake(frust_gpu::caps::DownlevelProfile::WebGl2);
assert!(caps.downlevel_profile == frust_gpu::caps::DownlevelProfile::WebGl2);
}
#[test]
fn lint_pipeline_layout_accepts_valid_spec() {
let desc = frust_gpu::lint::PipelineLayoutDesc {
bind_group_count: 2,
max_vertex_buffers: 4,
total_vertex_attributes: 8,
max_vertex_buffer_stride: 128,
sample_count: 1,
uniform_buffer_sizes: vec![256, 512], };
let violations = frust_gpu::lint::lint_pipeline_layout(&desc);
assert!(
violations.is_empty(),
"Valid layout should have no violations"
);
}