101 lines
4.1 KiB
Rust
101 lines
4.1 KiB
Rust
//! Commands that recompute the history off the window thread (`deferred`): the marker moved,
|
||
//! undo, each computed beside the window and run once in; a marker moved again while one move
|
||
//! computes replaces the move still waiting.
|
||
|
||
use std::time::Duration;
|
||
|
||
use serde_json::json;
|
||
|
||
use crate::scenario::Harness;
|
||
|
||
/// A 60 × 20 × 10 block, a 10 × 10 × 5 boss on it, and a 4 mm hole through the boss, the
|
||
/// marker at the end. Every command computes off the window thread (no wait at all).
|
||
fn block_boss_hole() -> Harness {
|
||
let mut h = Harness::new();
|
||
h.app.home.open = false;
|
||
for (id, p) in [
|
||
("solid.box", json!({"length": 60, "width": 20, "height": 10})),
|
||
("solid.box", json!({"corner": [5, 5, 10], "length": 10, "width": 10, "height": 5, "operation": "join"})),
|
||
("solid.cylinder", json!({"base": [10, 10, -1], "axis": [0, 0, 1], "radius": 2, "height": 20, "operation": "cut"})),
|
||
] {
|
||
h.app.run(id, p).unwrap();
|
||
}
|
||
h.app.apply_off_thread = Some(Duration::ZERO);
|
||
h
|
||
}
|
||
|
||
/// Frames until nothing computes or waits (a few seconds at most).
|
||
fn until_settled(h: &mut Harness) {
|
||
for _ in 0..500 {
|
||
if h.app.deferred.is_none() && h.app.deferred_queue.is_empty() {
|
||
break;
|
||
}
|
||
h.frames(1);
|
||
std::thread::sleep(Duration::from_millis(10));
|
||
}
|
||
h.frames(1);
|
||
}
|
||
|
||
fn marker(h: &Harness) -> Option<usize> {
|
||
h.app.session.doc.marker
|
||
}
|
||
|
||
#[test]
|
||
fn the_marker_moved_computes_off_the_window_thread_then_moves() {
|
||
let mut h = block_boss_hole();
|
||
h.app.run_heavy("timeline.roll_to", json!({"position": 1})).unwrap();
|
||
assert!(h.app.computing_since().is_some(), "computing");
|
||
assert_eq!(marker(&h), None, "the design is as it was while it computes");
|
||
until_settled(&mut h);
|
||
assert_eq!(marker(&h), Some(1));
|
||
h.step(&json!({"expect": {"volume": 12000}})).unwrap();
|
||
// And back to the end, the boss and the hole recomputed (or found in the cache).
|
||
h.app.run_heavy("timeline.roll_to", json!({})).unwrap();
|
||
until_settled(&mut h);
|
||
assert_eq!(marker(&h), None);
|
||
let hole = std::f64::consts::PI * 4.0 * 15.0;
|
||
let v = h.app.run("inspect.measure", json!({})).unwrap()["total"]["volume_mm3"].as_f64().unwrap();
|
||
assert!((v - (12500.0 - hole)).abs() < 1.0, "{v}");
|
||
}
|
||
|
||
#[test]
|
||
fn a_marker_moved_again_replaces_the_move_still_waiting() {
|
||
let mut h = block_boss_hole();
|
||
h.app.run_heavy("timeline.roll_to", json!({"position": 1})).unwrap();
|
||
assert!(h.app.computing_since().is_some());
|
||
// A drag: two more slots while the first computes. Only the last one waits.
|
||
h.app.run_heavy("timeline.roll_to", json!({"position": 2})).unwrap();
|
||
h.app.run_heavy("timeline.roll_to", json!({"position": 3})).unwrap();
|
||
assert_eq!(h.app.deferred_queue.len(), 1, "{:?}", h.app.deferred_queue);
|
||
until_settled(&mut h);
|
||
assert_eq!(marker(&h), None, "where the drag ended: the end of the history");
|
||
}
|
||
|
||
#[test]
|
||
fn undo_computes_off_the_window_thread() {
|
||
let mut h = block_boss_hole();
|
||
let features = h.app.session.doc.features.len();
|
||
h.app.start("edit.undo");
|
||
assert!(h.app.computing_since().is_some(), "undo computing");
|
||
assert_eq!(h.app.session.doc.features.len(), features, "nothing undone yet");
|
||
until_settled(&mut h);
|
||
assert_eq!(h.app.session.doc.features.len(), features - 1, "the hole undone");
|
||
h.step(&json!({"expect": {"volume": 12500}})).unwrap();
|
||
h.app.start("edit.redo");
|
||
until_settled(&mut h);
|
||
assert_eq!(h.app.session.doc.features.len(), features, "and redone");
|
||
}
|
||
|
||
#[test]
|
||
fn a_design_changed_while_a_move_computes_is_computed_again() {
|
||
let mut h = block_boss_hole();
|
||
h.app.run_heavy("timeline.roll_to", json!({"position": 2})).unwrap();
|
||
assert!(h.app.computing_since().is_some());
|
||
// Something else changes the design meanwhile (a command that just runs).
|
||
h.app.run("solid.box", json!({"corner": [40, 5, 10], "length": 5, "width": 5, "height": 5, "operation": "join"})).unwrap();
|
||
let revision = h.app.session.revision;
|
||
until_settled(&mut h);
|
||
assert!(h.app.session.revision > revision, "the move ran after the change");
|
||
assert_eq!(marker(&h), Some(2));
|
||
}
|