Skip to content

Commit

Permalink
[test] Split snapshots into separate files + unify update behavior (#850
Browse files Browse the repository at this point in the history
)
  • Loading branch information
wkozyra95 authored Nov 8, 2024
1 parent c7534b6 commit c816a68
Show file tree
Hide file tree
Showing 22 changed files with 1,920 additions and 2,277 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ resolver = "2"

[features]
default = ["web_renderer"]
update_snapshots = []
decklink = ["compositor_api/decklink"]
web_renderer = ["dep:compositor_chromium", "compositor_api/web_renderer"]

Expand Down
1 change: 0 additions & 1 deletion build_tools/nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ rustPlatform.buildRustPackage {
''
rm -f $out/bin/live_compositor
rm -f $out/bin/package_for_release
rm -f $out/bin/update_snapshots
mv $out/bin/main_process $out/bin/live_compositor
'' + (
Expand Down
2 changes: 1 addition & 1 deletion snapshot_tests/snapshots
Submodule snapshots updated 25 files
+ render_snapshots/simple/simple_input_pass_through_0_output_1.png
+ render_snapshots/transition/change_rescaler_absolute_and_send_next_update_2500_output_1.png
+ render_snapshots/transition/change_rescaler_absolute_and_send_next_update_7500_output_1.png
+ render_snapshots/transition/change_view_absolute_2500_output_1.png
+ render_snapshots/transition/change_view_absolute_7500_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_10000_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_5000_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_7500_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_9000_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_linear_like_10000_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_linear_like_5000_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_linear_like_7500_output_1.png
+ render_snapshots/transition/change_view_absolute_cubic_bezier_linear_like_9000_output_1.png
+ render_snapshots/transition/change_view_height_2500_output_1.png
+ render_snapshots/transition/change_view_height_7500_output_1.png
+ render_snapshots/transition/change_view_height_9000_output_1.png
+ render_snapshots/transition/change_view_width_2500_output_1.png
+ render_snapshots/transition/change_view_width_7500_output_1.png
+ render_snapshots/transition/change_view_width_9000_output_1.png
+ render_snapshots/transition/change_view_width_and_send_abort_transition_2500_output_1.png
+ render_snapshots/transition/change_view_width_and_send_abort_transition_7500_output_1.png
+ render_snapshots/transition/change_view_width_and_send_abort_transition_9000_output_1.png
+ render_snapshots/transition/change_view_width_and_send_next_update_2500_output_1.png
+ render_snapshots/transition/change_view_width_and_send_next_update_7500_output_1.png
+ render_snapshots/transition/change_view_width_and_send_next_update_9000_output_1.png
85 changes: 0 additions & 85 deletions src/bin/update_snapshots/main.rs

This file was deleted.

3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ pub mod middleware;
pub mod routes;
pub mod server;
pub mod state;

#[cfg(test)]
mod snapshot_tests;
3 changes: 0 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ mod routes;
mod server;
mod state;

#[cfg(test)]
mod snapshot_tests;

fn main() {
#[cfg(feature = "web_renderer")]
{
Expand Down
173 changes: 109 additions & 64 deletions src/snapshot_tests.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,134 @@
use std::{collections::HashSet, env, fs, path::PathBuf};

use self::{
test_case::{TestCaseError, TestCaseInstance},
tests::snapshot_tests,
utils::{find_unused_snapshots, snapshots_path},
use std::{
collections::HashSet,
fs,
path::{Path, PathBuf},
time::Duration,
};

use compositor_api::types::UpdateOutputRequest;
use compositor_render::{scene::Component, Resolution};
use test_case::{TestCase, TestResult, OUTPUT_ID};
use utils::SNAPSHOTS_DIR_NAME;

mod input;
mod snapshot;
mod test_case;
mod tests;
mod utils;

#[test]
fn test_snapshots() {
let tests: Vec<TestCaseInstance> = snapshot_tests()
.into_iter()
.map(TestCaseInstance::new)
.collect();
mod image_tests;
mod rescaler_tests;
mod shader_tests;
mod simple_tests;
mod text_tests;
mod tiles_tests;
mod tiles_transitions_tests;
mod transition_tests;
mod view_tests;

check_test_names_uniqueness(&tests);
const DEFAULT_RESOLUTION: Resolution = Resolution {
width: 640,
height: 360,
};

for test in tests.iter() {
println!("Test \"{}\"", test.case.name);
if let Err(err) = test.run() {
handle_error(err);
struct TestRunner {
cases: Vec<TestCase>,
snapshot_dir: PathBuf,
}

impl TestRunner {
fn new(snapshot_dir: PathBuf) -> Self {
Self {
cases: Vec::new(),
snapshot_dir,
}
}

// Check for unused snapshots
let snapshot_paths = tests
.iter()
.flat_map(TestCaseInstance::snapshot_paths)
.collect::<HashSet<_>>();
let unused_snapshots = find_unused_snapshots(&snapshot_paths, snapshots_path());
if !unused_snapshots.is_empty() {
panic!("Some snapshots were not used: {unused_snapshots:#?}")
fn add(&mut self, case: TestCase) {
self.cases.push(case)
}
}

fn handle_error(err: TestCaseError) {
let TestCaseError::Mismatch {
ref snapshot_from_disk,
ref produced_snapshot,
..
} = err
else {
panic!("{err}");
};

let failed_snapshot_path = failed_snapshot_path();
if !failed_snapshot_path.exists() {
fs::create_dir_all(&failed_snapshot_path).unwrap();
fn run(self) {
check_test_names_uniqueness(&self.cases);
check_unused_snapshots(&self.cases, &self.snapshot_dir);
let has_only = self.cases.iter().any(|test| test.only);

let mut failed = false;
for test in self.cases.iter() {
if has_only && !test.only {
continue;
}
println!("Test \"{}\"", test.name);
if let TestResult::Failure = test.run() {
failed = true;
}
}
if failed {
panic!("Test failed")
}
}
let snapshot_save_path = produced_snapshot.save_path();
let snapshot_name = snapshot_save_path.file_name().unwrap().to_string_lossy();

let width = produced_snapshot.resolution.width - (produced_snapshot.resolution.width % 2);
let height = produced_snapshot.resolution.height - (produced_snapshot.resolution.height % 2);
image::save_buffer(
failed_snapshot_path.join(format!("mismatched_{snapshot_name}")),
&produced_snapshot.data,
width as u32,
height as u32,
image::ColorType::Rgba8,
)
.unwrap();

snapshot_from_disk
.save(failed_snapshot_path.join(format!("original_{snapshot_name}")))
.unwrap();

panic!("{err}");
}

fn failed_snapshot_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("failed_snapshot_tests")
fn scene_from_json(scene: &'static str) -> Vec<Component> {
let scene: UpdateOutputRequest = serde_json::from_str(scene).unwrap();
vec![scene.video.unwrap().try_into().unwrap()]
}

fn scenes_from_json(scenes: &[&'static str]) -> Vec<Component> {
scenes
.iter()
.map(|scene| {
let scene: UpdateOutputRequest = serde_json::from_str(scene).unwrap();
scene.video.unwrap().try_into().unwrap()
})
.collect()
}

fn check_test_names_uniqueness(tests: &[TestCaseInstance]) {
fn check_test_names_uniqueness(tests: &[TestCase]) {
let mut test_names = HashSet::new();
for test in tests.iter() {
if !test_names.insert(test.case.name) {
if !test_names.insert(test.name) {
panic!(
"Multiple snapshots tests with the same name: \"{}\".",
test.case.name
test.name
);
}
}
}

fn snapshots_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SNAPSHOTS_DIR_NAME)
}

fn snapshot_save_path(test_name: &str, pts: &Duration) -> PathBuf {
let out_file_name = format!("{}_{}_{}.png", test_name, pts.as_millis(), OUTPUT_ID);
snapshots_path().join(out_file_name)
}

fn check_unused_snapshots(tests: &[TestCase], snapshot_dir: &Path) {
let existing_snapshots = tests
.iter()
.flat_map(TestCase::snapshot_paths)
.collect::<HashSet<_>>();
let mut unused_snapshots = Vec::new();
for entry in fs::read_dir(snapshot_dir).unwrap() {
let entry = entry.unwrap();
if !entry.file_name().to_string_lossy().ends_with(".png") {
continue;
}

if !existing_snapshots.contains(&entry.path()) {
unused_snapshots.push(entry.path())
}
}

if !unused_snapshots.is_empty() {
if cfg!(feature = "update_snapshots") {
for snapshot_path in unused_snapshots {
println!("DELETE: Unused snapshot {snapshot_path:?}");
fs::remove_file(snapshot_path).unwrap();
}
} else {
panic!("Some snapshots were not used: {unused_snapshots:#?}")
}
}
}
76 changes: 76 additions & 0 deletions src/snapshot_tests/image_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use compositor_render::{
image::{ImageSource, ImageSpec, ImageType},
RendererId, RendererSpec,
};

use super::{
input::TestInput, scene_from_json, scenes_from_json, snapshots_path, test_case::TestCase,
TestRunner,
};

#[test]
fn image_tests() {
let mut runner = TestRunner::new(snapshots_path().join("image"));

let image_renderer = (
RendererId("image_jpeg".into()),
RendererSpec::Image(ImageSpec {
src: ImageSource::Url {
url: "https://www.rust-lang.org/static/images/rust-social.jpg".to_string(),
},
image_type: ImageType::Jpeg,
}),
);

runner.add(TestCase {
name: "image/jpeg_as_root",
scene_updates: scene_from_json(include_str!(
"../../snapshot_tests/image/jpeg_as_root.scene.json"
)),
renderers: vec![image_renderer.clone()],
inputs: vec![TestInput::new(1)],
..Default::default()
});
runner.add(TestCase {
name: "image/jpeg_in_view",
scene_updates: scene_from_json(include_str!(
"../../snapshot_tests/image/jpeg_in_view.scene.json"
)),
renderers: vec![image_renderer.clone()],
inputs: vec![TestInput::new(1)],
..Default::default()
});
runner.add(TestCase {
name: "image/jpeg_in_view_overflow_fit",
scene_updates: scene_from_json(include_str!(
"../../snapshot_tests/image/jpeg_in_view_overflow_fit.scene.json"
)),
renderers: vec![image_renderer.clone()],
inputs: vec![TestInput::new(1)],
..Default::default()
});
runner.add(TestCase {
// Test if removing image from scene works
name: "image/remove_jpeg_as_root",
scene_updates: scenes_from_json(&[
include_str!("../../snapshot_tests/image/jpeg_as_root.scene.json"),
include_str!("../../snapshot_tests/view/empty_view.scene.json"),
]),
renderers: vec![image_renderer.clone()],
inputs: vec![TestInput::new(1)],
..Default::default()
});
runner.add(TestCase {
// Test if removing image from scene works
name: "image/remove_jpeg_in_view",
scene_updates: scenes_from_json(&[
include_str!("../../snapshot_tests/image/jpeg_in_view.scene.json"),
include_str!("../../snapshot_tests/view/empty_view.scene.json"),
]),
renderers: vec![image_renderer.clone()],
inputs: vec![TestInput::new(1)],
..Default::default()
});

runner.run()
}
Loading

0 comments on commit c816a68

Please sign in to comment.