Oregami
Repositories/oxedyne/ore

oxedyne/ore/oracle/tests/cycle_rule.rs

5.4 KiB, 1 run

created by r2848102244:101, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Assertions over stage five, the cross-file cycle rule. Panics are the point.
2
3use ore_oracle::csweep;
4use ore_oracle::cyc;
5use ore_oracle::repo::CycleRule;
6
7/// Every cycle case meets the prediction written before it was run, under every
8/// rule.
9#[test]
10fn stage5_cases_meet_predictions() {
11 for rule in cyc::RULES {
12 let outs = match cyc::all(rule) {
13 Ok(o) => o,
14 Err(e) => panic!("{} failed to run: {}", rule.name(), e),
15 };
16 for o in &outs {
17 assert!(o.met(), "{} / {}: predicted {}, got {}",
18 rule.name(), o.name, o.expect, o.got);
19 }
20 }
21}
22
23/// No rule loses a byte or renders one twice, whatever it does with a cycle.
24#[test]
25fn stage5_conservation_holds_under_every_rule() {
26 for rule in cyc::RULES {
27 let outs = match cyc::all(rule) {
28 Ok(o) => o,
29 Err(e) => panic!("{} failed to run: {}", rule.name(), e),
30 };
31 for o in &outs {
32 assert!(o.conserved(), "{} / {}: {} rendered twice, {} lost",
33 rule.name(), o.name, o.duplicated, o.lost);
34 }
35 }
36}
37
38/// A cycle inside one file is none of a confinement rule's business: the two
39/// in-file cases render exactly what the status quo renders, and are demoted
40/// rather than confined.
41#[test]
42fn stage5_in_file_cycles_are_untouched() {
43 for rule in cyc::RULES {
44 for (name, out) in [
45 ("in-file", cyc::c7_in_file_cycle(rule)),
46 ("self", cyc::c8_self_anchoring_move(rule)),
47 ] {
48 let o = match out {
49 Ok(o) => o,
50 Err(e) => panic!("{} / {}: {}", rule.name(), name, e),
51 };
52 assert!(o.met(), "{} / {}: {} became {}",
53 rule.name(), name, o.expect, o.got);
54 assert!(o.flags.confined.is_empty(),
55 "{} / {}: an in-file cycle was confined", rule.name(), name);
56 assert!(!o.flags.demoted.is_empty(),
57 "{} / {}: an in-file cycle was not demoted", rule.name(), name);
58 }
59 }
60}
61
62/// The two-file cycle, which is the case the design shipped with. Every
63/// candidate leaves both files with content or leaves one empty by its author's
64/// own instruction; only the status quo empties a file into another.
65#[test]
66fn stage5_the_shipped_case_stops_collapsing() {
67 let quo = match cyc::c1_two_file_cycle(CycleRule::Demote) {
68 Ok(o) => o,
69 Err(e) => panic!("{}", e),
70 };
71 assert_eq!(quo.got, "a.txt=\"\" b.txt=\"axyz\\nbc\\n\"");
72 let confined = match cyc::c1_two_file_cycle(CycleRule::ConfineCycle) {
73 Ok(o) => o,
74 Err(e) => panic!("{}", e),
75 };
76 assert_eq!(confined.got, "a.txt=\"abc\\n\" b.txt=\"xyz\\n\"");
77 assert_eq!(confined.emptied, 0, "confinement left a file empty");
78 assert_eq!(confined.flags.confined.len(), 2, "both moves should be told");
79}
80
81/// An author who re-moves their own block, having seen the move that raced it,
82/// is not voided for it. Under every candidate the re-move wins; only the status
83/// quo puts the block in the file its author twice refused.
84#[test]
85fn stage5_an_informed_re_move_is_never_voided() {
86 for rule in cyc::RULES {
87 let o = match cyc::c6_supersession_cycle(rule) {
88 Ok(o) => o,
89 Err(e) => panic!("{}: {}", rule.name(), e),
90 };
91 assert!(o.met(), "{}: predicted {}, got {}", rule.name(), o.expect, o.got);
92 if rule == CycleRule::Demote {
93 continue;
94 }
95 assert_eq!(o.got, "a.txt=\"\" b.txt=\"xyz\\nabc\\n\"",
96 "{} did not honour the re-move", rule.name());
97 }
98}
99
100/// The planted sweep: whatever a rule does, it converges, conserves, keeps the
101/// concurrent edit, and does nothing at all where there is no cycle.
102#[test]
103fn stage5_planted_sweep_is_sound() {
104 let rows = match csweep::sweep(1000, &cyc::RULES) {
105 Ok(r) => r,
106 Err(e) => panic!("the planted sweep failed: {}", e),
107 };
108 for (rule, row) in cyc::RULES.iter().zip(rows.iter()) {
109 assert_eq!(row.duplicated, 0, "{}: bytes rendered twice", rule.name());
110 assert_eq!(row.lost, 0, "{}: bytes rendered nowhere", rule.name());
111 assert_eq!(row.diverged, 0, "{}: delivery order changed the render",
112 rule.name());
113 assert_eq!(row.edits_lost, 0, "{}: an edit inside a cycling block vanished",
114 rule.name());
115 assert_eq!(row.meddled, 0, "{}: a rule acted where there was no cycle",
116 rule.name());
117 }
118}
119
120/// The comparative claim, over a thousand planted trials: confining the whole
121/// cycle empties no file that nobody emptied, whole-cycle arbitration nearly
122/// none, and confining one edge is *worse than the status quo* at it.
123#[test]
124fn stage5_the_sweep_ranks_the_rules() {
125 let rows = match csweep::sweep(1000, &cyc::RULES) {
126 Ok(r) => r,
127 Err(e) => panic!("the planted sweep failed: {}", e),
128 };
129 let by = |r: CycleRule| {
130 let i = match cyc::RULES.iter().position(|x| *x == r) {
131 Some(i) => i,
132 None => panic!("no such rule"),
133 };
134 rows[i].clone()
135 };
136 let quo = by(CycleRule::Demote);
137 let all = by(CycleRule::ConfineCycle);
138 let one = by(CycleRule::LowestEdge);
139 let whole = by(CycleRule::WholeCycle);
140 assert_eq!(all.unasked, 0, "confining the whole cycle emptied a file");
141 assert_eq!(all.drained, 0, "confining the whole cycle emptied a file at all");
142 assert!(whole.unasked < quo.unasked / 4,
143 "whole-cycle arbitration: {} unasked against {}", whole.unasked, quo.unasked);
144 assert!(one.unasked >= quo.unasked,
145 "confining one edge was expected to be no better than the status quo, \
146 got {} against {}", one.unasked, quo.unasked);
147 assert!(whole.misplaced * 4 < one.misplaced,
148 "whole-cycle arbitration should misplace far less than one-edge: {} vs {}",
149 whole.misplaced, one.misplaced);
150 assert!(whole.landed > all.landed * 4,
151 "whole-cycle arbitration should complete far more moves than confining \
152 all: {} vs {}", whole.landed, all.landed);
153}