132 lines
4.1 KiB
Rust
132 lines
4.1 KiB
Rust
//! Runs the `resolved` binary on the example scenes.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
fn repo() -> PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..")
|
|
}
|
|
|
|
fn out_dir() -> PathBuf {
|
|
let dir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("cli");
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
dir
|
|
}
|
|
|
|
/// Runs the binary from the repository root with `ASTROKIT_KERNELS` cleared, so the
|
|
/// kernel search falls back the way it does for someone who has not set it.
|
|
fn resolved(args: &[&str]) -> std::process::Output {
|
|
Command::new(env!("CARGO_BIN_EXE_resolved"))
|
|
.args(args)
|
|
.current_dir(repo())
|
|
.env_remove("ASTROKIT_KERNELS")
|
|
.output()
|
|
.unwrap()
|
|
}
|
|
|
|
/// Renders `scene` on `adapter` to a 16-bit PNG and returns its pixels as DN (the
|
|
/// file holds 12-bit DN shifted up by 4).
|
|
fn render(scene: &str, adapter: &str) -> image::ImageBuffer<image::Rgb<u16>, Vec<u16>> {
|
|
let out = out_dir().join(format!(
|
|
"{}-{adapter}.png",
|
|
Path::new(scene).file_stem().unwrap().to_str().unwrap()
|
|
));
|
|
let preview = out.with_extension("preview.png");
|
|
let result = resolved(&[
|
|
"render",
|
|
"--scene",
|
|
scene,
|
|
"--adapter",
|
|
adapter,
|
|
"--out",
|
|
out.to_str().unwrap(),
|
|
"--preview",
|
|
preview.to_str().unwrap(),
|
|
]);
|
|
assert!(
|
|
result.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&result.stderr)
|
|
);
|
|
assert!(preview.is_file());
|
|
let mut pixels = image::open(&out).unwrap().into_rgb16();
|
|
for p in pixels.pixels_mut() {
|
|
assert!(p.0.iter().all(|v| v & 0xf == 0), "low 4 bits are padding");
|
|
p.0 = p.0.map(|v| v >> 4);
|
|
}
|
|
pixels
|
|
}
|
|
|
|
#[test]
|
|
fn example_scene_on_gpu_and_cpu() {
|
|
let gpu = render("examples/scenes/boxwing.toml", "gpu");
|
|
let cpu = render("examples/scenes/boxwing.toml", "cpu");
|
|
assert_eq!(gpu.dimensions(), (1920, 1080));
|
|
// The image center is on the sunlit gold bus: brighter in red than in blue.
|
|
let [r, _, b] = gpu.get_pixel(960, 540).0;
|
|
assert!(r > 500 && r > 2 * b, "{r} {b}");
|
|
// Above the limb is empty space, which reads the 64 DN bias; below it, Earth's
|
|
// dayside, lit and blue.
|
|
assert_eq!(gpu.get_pixel(0, 0).0, [64; 3]);
|
|
let [r, g, b] = gpu.get_pixel(0, 1079).0;
|
|
assert!(r > 64 && g > r && b > g, "{r} {g} {b}");
|
|
let differing = gpu
|
|
.pixels()
|
|
.zip(cpu.pixels())
|
|
.filter(|(a, b)| a.0.iter().zip(b.0).any(|(x, y)| x.abs_diff(y) > 1))
|
|
.count();
|
|
// Only pixels on silhouette edges may land differently.
|
|
assert!(
|
|
differing < 500,
|
|
"{differing} pixels differ between GPU and CPU"
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "astrokit")]
|
|
#[test]
|
|
fn epoch_scene_renders() {
|
|
let frame = render("examples/scenes/boxwing-epoch.toml", "gpu");
|
|
// The same view as boxwing.toml, rotated to the real Sun: the sunlit bus at the
|
|
// center, space above the limb.
|
|
assert!(frame.get_pixel(960, 540).0[0] > 500);
|
|
assert_eq!(frame.get_pixel(0, 0).0, [64; 3]);
|
|
}
|
|
|
|
#[test]
|
|
fn errors_are_reported() {
|
|
let missing = resolved(&["render", "--scene", "no/such.toml", "--preview", "x.png"]);
|
|
assert!(!missing.status.success());
|
|
assert!(String::from_utf8_lossy(&missing.stderr).contains("no/such.toml"));
|
|
|
|
let idle = resolved(&["render", "--scene", "examples/scenes/boxwing.toml"]);
|
|
assert!(!idle.status.success());
|
|
assert!(String::from_utf8_lossy(&idle.stderr).contains("nothing to do"));
|
|
}
|
|
|
|
#[cfg(feature = "astrokit")]
|
|
#[test]
|
|
fn missing_kernels_say_where_they_looked() {
|
|
let result = resolved(&[
|
|
"render",
|
|
"--scene",
|
|
"examples/scenes/boxwing-epoch.toml",
|
|
"--kernels",
|
|
"no/such/dir",
|
|
"--preview",
|
|
"x.png",
|
|
]);
|
|
assert!(!result.status.success());
|
|
let stderr = String::from_utf8_lossy(&result.stderr);
|
|
assert!(
|
|
stderr.contains("--kernels") && stderr.contains("no/such/dir"),
|
|
"{stderr}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn adapters_lists_something() {
|
|
let result = resolved(&["adapters"]);
|
|
assert!(result.status.success());
|
|
assert!(!result.stdout.is_empty());
|
|
}
|