mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 21:44:57 +00:00
Tests: raster effects draw the same twice while another test assigns a document profile
tests::newdoc::a_profile_brings_the_color_profile_its_file_carries and tests::saveoptions::an_embedded_profile_is_restored give their front document a non-sRGB RGB profile. That profile becomes the process-wide active color settings and stays active until a document without one comes to the front. Every color the engine test process renders goes through those settings, so the test tests::rasterfilters::each_effect_changes_the_canvas_and_draws_the_same_twice could draw the same art in two colors and fail with "<effect> is deterministic". The two tests now run again in a child process of the test binary through a shared helper, tests::in_own_process. The SVG Drop Shadow test for a non-sRGB working space uses the helper in place of its own copy, and the GLOBAL doc comment in tests/colormgmt.rs names this route. The rasterfilters test now checks that the color settings are the defaults before it draws and that nothing replaced them while it drew. A test that changes them in the same process makes it fail with "another test changed the color settings while this one drew". With the three tests run together (--test-threads=3) on Linux, the rasterfilters test failed 10 of 10 runs without the child processes, each time with that message, and passed 10 of 10 with them.
This commit is contained in:
@@ -352,20 +352,8 @@ fn svg_darkness_preserves_source_model_and_gradient_appearance() {
|
||||
|
||||
#[test]
|
||||
fn svg_darkness_uses_rendered_shadow_in_non_srgb_working_space() {
|
||||
const CHILD: &str = "VECTORCRAFT_TEST_SHADOW_RGB_PROFILE";
|
||||
if std::env::var_os(CHILD).is_none() {
|
||||
// Color settings are process-wide: exercise this profile in a separate test process.
|
||||
let output = std::process::Command::new(std::env::current_exe().unwrap())
|
||||
.args(["--exact", "cmd::fileio::tests_svg::svg_darkness_uses_rendered_shadow_in_non_srgb_working_space", "--nocapture"])
|
||||
.env(CHILD, "1")
|
||||
.output()
|
||||
.unwrap();
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(
|
||||
output.status.success() && stdout.contains("test result: ok. 1 passed; 0 failed;"),
|
||||
"isolated color profile test failed:\n{stdout}\n{}",
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
// Color settings are process-wide: exercise this profile in a separate test process.
|
||||
if !crate::tests::in_own_process("cmd::fileio::tests_svg::svg_darkness_uses_rendered_shadow_in_non_srgb_working_space") {
|
||||
return;
|
||||
}
|
||||
use vectorcraft_color::{Color, Paint, cms};
|
||||
|
||||
@@ -241,6 +241,25 @@ pub(crate) fn key_guide(s: &Session) -> Option<usize> {
|
||||
st.selection.key_guide.and_then(|k| crate::cmd::guide_indexes(&st.doc, &[k]).first().copied())
|
||||
}
|
||||
|
||||
/// Runs the unit test `path` (`tests::newdoc::...`) again in a child process of the test binary
|
||||
/// → whether this is that child. For a test that changes process-wide state the tests running
|
||||
/// beside it read, such as the active color settings.
|
||||
pub(crate) fn in_own_process(path: &str) -> bool {
|
||||
const CHILD: &str = "VECTORCRAFT_TEST_OWN_PROCESS";
|
||||
if std::env::var_os(CHILD).is_some() {
|
||||
return true;
|
||||
}
|
||||
let output =
|
||||
std::process::Command::new(std::env::current_exe().unwrap()).args(["--exact", path, "--nocapture"]).env(CHILD, "1").output().unwrap();
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(
|
||||
output.status.success() && stdout.contains("test result: ok. 1 passed; 0 failed;"),
|
||||
"{path} failed in its own process:\n{stdout}\n{}",
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
false
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_and_undo_redo() {
|
||||
let mut s = session();
|
||||
|
||||
@@ -16,7 +16,10 @@ use crate::*;
|
||||
/// Every test here reads or changes the process-wide proof view or colour settings: one at a time.
|
||||
/// Tests that switch the Color Settings away from the defaults live in `tests/color_settings.rs`
|
||||
/// (their own process): every colour shown or rendered goes through them, so no lock could cover
|
||||
/// all the tests that would see the change.
|
||||
/// all the tests that would see the change. A unit test that assigns its front document a profile
|
||||
/// other than the working space changes the active settings the same way until another document
|
||||
/// comes to the front: it starts with `crate::tests::in_own_process`, which runs it again in a
|
||||
/// child process.
|
||||
pub(crate) static GLOBAL: Mutex<()> = Mutex::new(());
|
||||
|
||||
fn session() -> Session {
|
||||
|
||||
@@ -683,6 +683,9 @@ fn the_new_document_links_from_the_profile_file() {
|
||||
/// is a warning.
|
||||
#[test]
|
||||
fn a_profile_brings_the_color_profile_its_file_carries() {
|
||||
if !super::in_own_process("tests::newdoc::a_profile_brings_the_color_profile_its_file_carries") {
|
||||
return;
|
||||
}
|
||||
use vectorcraft_color::cms;
|
||||
let (mut s, dir) = with_profiles("icc");
|
||||
let builtin = cms::profiles().into_iter().find(|p| p.kind == cms::ProfileKind::Rgb && p.builtin && p.name != cms::SRGB).unwrap();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
//! as images of the effected object.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_color::cms;
|
||||
use vectorcraft_doc::NodeKind;
|
||||
use vectorcraft_geom::Rect;
|
||||
use vectorcraft_render::{Rendered, Renderer};
|
||||
@@ -173,16 +174,22 @@ fn each_effect_changes_the_canvas_and_draws_the_same_twice() {
|
||||
EFFECTS.into_iter().chain(ARTISTIC).chain(BRUSH_STROKES).chain(DISTORT).chain(PIXELATE).chain(SKETCH).chain(TEXTURE).chain(VIDEO)
|
||||
{
|
||||
let (mut s, g) = striped();
|
||||
// Every color drawn goes through the process-wide color settings. A test that changes them
|
||||
// runs in a process of its own (`crate::tests::in_own_process`).
|
||||
let colors = cms::active();
|
||||
assert_eq!(cms::active_settings(), cms::ColorSettings::default(), "{id}: another test left the color settings changed");
|
||||
let plain = render(&s);
|
||||
let params: Value = serde_json::from_str(params).unwrap();
|
||||
s.execute("effect.apply", &json!({"effect": id, "ids": [g], "params": params})).unwrap();
|
||||
let a = render(&s);
|
||||
assert!(mean_diff(&plain, &a) > 0.0005, "{id} shows: {}", mean_diff(&plain, &a));
|
||||
assert_eq!(a.pixels, render(&s).pixels, "{id} is deterministic");
|
||||
let again = render(&s);
|
||||
// The multithreaded pipeline draws the same.
|
||||
let mut mt = Renderer::new();
|
||||
mt.threads = 4;
|
||||
let b = mt.render_region(&s.doc().unwrap().doc, Rect::new(0.0, 0.0, 200.0, 200.0), 1.0, true);
|
||||
assert!(std::sync::Arc::ptr_eq(&colors, &cms::active()), "{id}: another test changed the color settings while this one drew");
|
||||
assert!(mean_diff(&plain, &a) > 0.0005, "{id} shows: {}", mean_diff(&plain, &a));
|
||||
assert_eq!(a.pixels, again.pixels, "{id} is deterministic");
|
||||
assert!(mean_diff(&a, &b) < 0.002, "{id}: multithreaded differs by {}", mean_diff(&a, &b));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,9 @@ fn a_linked_pngs_bytes_are_embedded() {
|
||||
|
||||
#[test]
|
||||
fn an_embedded_profile_is_restored() {
|
||||
if !super::in_own_process("tests::saveoptions::an_embedded_profile_is_restored") {
|
||||
return;
|
||||
}
|
||||
let builtin = cms::profiles().into_iter().find(|p| p.kind == cms::ProfileKind::Rgb && p.builtin && p.name != cms::SRGB).unwrap();
|
||||
let icc = cms::icc_bytes(&builtin.name).unwrap();
|
||||
let name = format!("Studio RGB {}", std::process::id());
|
||||
|
||||
Reference in New Issue
Block a user