mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 20:36:02 +00:00
Merge pull request #7541 from dotCooCoo/test/actions-tool-exec-recording-limit
Actions: test that a tool's command is checked against the recording limit
This commit is contained in:
@@ -6216,10 +6216,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// A drag with the Selection tool (a committed preview) or the Rectangle tool (a command the
|
||||
/// tool runs) that takes a recording past 10,000 steps keeps its edit and the journal, the
|
||||
/// status bar names the limit as the tool ends, and Stop Playing/Recording keeps the 10,000
|
||||
/// steps that fit. One that fills the action exactly names no limit.
|
||||
/// A drag with the Selection tool or the Rectangle tool (each commits its preview) that takes a
|
||||
/// recording past 10,000 steps keeps its edit and the journal, the status bar names the limit
|
||||
/// as the tool ends, and Stop Playing/Recording keeps the 10,000 steps that fit. One that fills
|
||||
/// the action exactly names no limit.
|
||||
#[test]
|
||||
fn tool_commit_checks_recording_step_limit() {
|
||||
let steps = "the action already has 10,000 steps";
|
||||
@@ -6272,6 +6272,115 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Drags the Enclosing Lasso away from the selected rectangle, which runs `select.set` with no
|
||||
/// objects, and checks that the tool journaled that one step and cleared the selection.
|
||||
fn lasso_nothing(app: &mut VectorcraftApp) {
|
||||
app.select_tool(vectorcraft_tools::addons::enclosing_lasso::ID);
|
||||
assert_eq!(app.session.tool_id(), vectorcraft_tools::addons::enclosing_lasso::ID);
|
||||
assert!(!app.session.active().unwrap().selection.objects.is_empty());
|
||||
let journal = app.session.journal.len();
|
||||
tool_drag(app, (100.0, 80.0), (120.0, 80.0));
|
||||
assert_eq!(app.session.journal.len(), journal + 1, "the tool journals one step");
|
||||
assert_eq!(app.session.journal.last(), Some(&("select.set".to_string(), json!({"ids": []}))));
|
||||
assert!(app.session.active().unwrap().selection.objects.is_empty(), "the edit stays");
|
||||
}
|
||||
|
||||
/// A command a tool runs (the Enclosing Lasso's `select.set`) that takes a recording past
|
||||
/// 10,000 steps keeps its edit and the journal, the status bar names the limit as the tool
|
||||
/// ends, the next recorded command does not run, and Stop Playing/Recording keeps the 10,000
|
||||
/// steps that fit. One that fills the action exactly names no limit.
|
||||
#[test]
|
||||
fn tool_exec_checks_recording_step_limit() {
|
||||
let steps = "the action already has 10,000 steps";
|
||||
for filler in [files::MAX_ACTION_STEPS, files::MAX_ACTION_STEPS - 1] {
|
||||
let (mut app, _) = app_with_three_moves();
|
||||
recording_with(&mut app, filler);
|
||||
app.ui.status.clear();
|
||||
lasso_nothing(&mut app);
|
||||
if filler < files::MAX_ACTION_STEPS {
|
||||
assert_eq!(app.ui.status, "", "10,000 steps fit");
|
||||
continue;
|
||||
}
|
||||
assert_eq!(app.ui.status, steps, "the status bar names the limit now");
|
||||
assert!(app.ui.recording.is_some(), "the recording goes on");
|
||||
let (journal, art) = (app.session.journal.len(), app.session.active().unwrap().doc.art_bounds());
|
||||
let r = app.run("shape.rectangle", json!({"x": 50, "y": 50, "width": 20, "height": 20}));
|
||||
assert_eq!(r.unwrap_err(), steps, "no later command runs");
|
||||
assert_eq!(app.session.journal.len(), journal);
|
||||
assert_eq!(app.session.active().unwrap().doc.art_bounds(), art, "no rectangle is drawn");
|
||||
assert_eq!(app.session.journal.since(recording_start(&app).unwrap()).len(), files::MAX_ACTION_STEPS + 1, "the journal keeps the step");
|
||||
app.run(STOP_RECORDING, json!({})).unwrap();
|
||||
assert_eq!(app.ui.action_sets[0].actions.last().unwrap().steps.len(), files::MAX_ACTION_STEPS);
|
||||
}
|
||||
}
|
||||
|
||||
/// A command a tool runs (the Enclosing Lasso's `select.set`) whose step gives the recording's
|
||||
/// set a file larger than 8 MiB keeps its edit, the status bar names the limit as the tool
|
||||
/// ends, and Stop Playing/Recording leaves the step out of a set that Load Actions… reads.
|
||||
#[test]
|
||||
fn tool_exec_checks_recording_byte_limit() {
|
||||
let size = "the action set would be larger than 8 MiB";
|
||||
let (mut app, _) = app_with_three_moves();
|
||||
app.run(START_RECORDING, json!({})).unwrap();
|
||||
let pad = |n: usize| ("object.move".to_string(), json!({"dx": 1, "dy": 0, "pad": "x".repeat(n)}));
|
||||
app.ui.action_sets[0].actions[0].steps = vec![pad(0)];
|
||||
let room = files::MAX_SET_FILE - files::write_set(&app.ui.action_sets[0]).len();
|
||||
app.ui.action_sets[0].actions[0].steps = vec![pad(room)];
|
||||
app.ui.recording_into = Some(app.ui.action_sets[0].actions[0].id);
|
||||
app.ui.status.clear();
|
||||
lasso_nothing(&mut app);
|
||||
assert_eq!(app.ui.status, size, "the status bar names the limit now");
|
||||
assert_eq!(app.session.journal.since(recording_start(&app).unwrap()).len(), 1, "the journal keeps the step");
|
||||
app.run(STOP_RECORDING, json!({})).unwrap();
|
||||
assert_eq!(app.ui.action_sets[0].actions[0].steps.len(), 1, "the step is left out");
|
||||
let saved = files::write_set(&app.ui.action_sets[0]);
|
||||
assert!(saved.len() <= files::MAX_SET_FILE);
|
||||
assert!(files::read_set(&saved, "Test").is_ok());
|
||||
}
|
||||
|
||||
/// The place cursor's `file.place` that takes a recording past 10,000 steps keeps the placed
|
||||
/// file and the journal, and the status bar names the limit once the file is placed: on the
|
||||
/// release when the file is read at once, and on the frame that places it when it is read off
|
||||
/// the UI thread. Stop Playing/Recording keeps the 10,000 steps that fit.
|
||||
#[test]
|
||||
fn place_cursor_exec_checks_recording_step_limit_on_completion() {
|
||||
let steps = "the action already has 10,000 steps";
|
||||
let mut png = vec![];
|
||||
image::RgbImage::new(8, 8).write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap();
|
||||
let placed = |app: &VectorcraftApp| {
|
||||
let st = app.session.active().unwrap();
|
||||
st.doc.layers.iter().flat_map(|l| l.children().into_iter().flatten()).filter(|n| n.name.as_deref() == Some("a.png")).count()
|
||||
};
|
||||
for background in [false, true] {
|
||||
let (mut app, _) = app_with_three_moves();
|
||||
app.placing.off_the_ui_thread = background;
|
||||
app.run("file.place.queue", json!({"files": [{"name": "a.png", "dataBase64": vectorcraft_format::base64_encode(&png)}]})).unwrap();
|
||||
assert_eq!(app.session.tool_id(), "place");
|
||||
recording_with(&mut app, files::MAX_ACTION_STEPS);
|
||||
app.ui.status.clear();
|
||||
let journal = app.session.journal.len();
|
||||
tool_drag(&mut app, (40.0, 40.0), (80.0, 80.0));
|
||||
if background {
|
||||
assert_eq!(app.session.journal.len(), journal, "the release places nothing yet");
|
||||
assert_eq!(placed(&app), 0);
|
||||
assert_ne!(app.ui.status, steps, "nothing is checked before the file is placed");
|
||||
app.placing.wait_for_reads();
|
||||
let ctx = egui::Context::default();
|
||||
app.begin_host_iteration();
|
||||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| app.tick(ui.ctx()));
|
||||
out.textures_delta.clear();
|
||||
assert!(app.placing.pending.is_empty(), "the frame after the read places the file");
|
||||
}
|
||||
assert_eq!(app.session.journal.len(), journal + 1, "background {background}: the place journals one step");
|
||||
assert_eq!(app.session.journal.last().map(|(c, _)| c.as_str()), Some("file.place"));
|
||||
assert_eq!(placed(&app), 1, "background {background}: the placed file stays");
|
||||
assert_eq!(app.ui.status, steps, "background {background}: the status bar names the limit once the file is placed");
|
||||
assert!(app.ui.recording.is_some(), "the recording goes on");
|
||||
app.run(STOP_RECORDING, json!({})).unwrap();
|
||||
assert_eq!(app.ui.action_sets[0].actions.last().unwrap().steps.len(), files::MAX_ACTION_STEPS, "background {background}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A step that reaches the journal outside a command, as a panel's or a dialog's commit does, is
|
||||
/// checked on the app's next frame. A journal rewrite that makes a checked step larger is
|
||||
/// checked again, and a frame with no new step does not name the limit again.
|
||||
|
||||
@@ -178,6 +178,23 @@ pub struct Placing {
|
||||
pub(crate) reading: std::sync::Arc<[std::sync::atomic::AtomicUsize; 2]>,
|
||||
}
|
||||
|
||||
impl Placing {
|
||||
/// Unit tests: wait until the reader thread has finished every read started so far. The thread
|
||||
/// runs its jobs in order, so a job sent after them runs once they have handed back their files.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn wait_for_reads(&self) {
|
||||
if let Some(reader) = &self.reader {
|
||||
let (tx, rx) = channel();
|
||||
let marker: Job = Box::new(move || {
|
||||
let _ = tx.send(());
|
||||
});
|
||||
if reader.send(marker).is_ok() {
|
||||
let _ = rx.recv();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PendingSwatch {
|
||||
clipboard: Clipboard,
|
||||
insertion: Insertion,
|
||||
|
||||
Reference in New Issue
Block a user