1use crate::*;
7use alux_ext::ext;
8use anyhow::{Result, bail};
9use std::fmt::Debug;
10use std::time::Duration;
11
12pub trait SessionLawFixture: SessionSorts {
14 fn law_wanting(&mut self, says: usize) -> Self::Id;
17
18 fn law_unbeginnable(&mut self) -> Self::Id;
20
21 fn law_wants(&mut self, id: &Self::Id, wanted: Wanted);
23
24 fn law_passes(&mut self, elapsed: Duration);
26
27 fn law_told(&self, id: &Self::Id) -> Vec<Telling>;
29
30 fn law_abandonments(&self, id: &Self::Id) -> usize;
32}
33
34#[ext(name = SessionLaws)]
36pub impl<This> This
37where
38 This: UndertakingAlg + RunReadAlg + RunHoldAlg + AttentionAlg + ClockAlg + SessionLawFixture,
39 This::Id: Clone + Eq + Debug,
40{
41 fn session_laws(&mut self) -> Result<()> {
57 self.check_beginning_laws()?;
58 self.check_saying_laws()?;
59 self.check_unwanted_laws()?;
60 self.check_holding_laws()
61 }
62
63 fn check_beginning_laws(&mut self) -> Result<()> {
69 let mut running = Vec::new();
70 self.attend(&mut running);
71 if !running.is_empty() {
72 bail!("attending to nothing began something");
73 }
74
75 let wanting = [self.law_wanting(1), self.law_wanting(1)];
76 let refusing = self.law_unbeginnable();
77 self.attend(&mut running);
78 if running.len() != wanting.len() {
79 bail!(
80 "one pass began {} runs where {} requests wanted work",
81 running.len(),
82 wanting.len()
83 );
84 }
85 for id in &wanting {
86 if begun_count(&self.law_told(id)) != 1 {
87 bail!("{id:?} was not told once that its work began");
88 }
89 }
90 if self.law_told(&refusing) != vec![Telling::Refused] {
91 bail!("work refusing to begin was not stated once as a refusal");
92 }
93
94 self.attend(&mut running);
95 for id in &wanting {
96 if begun_count(&self.law_told(id)) != 1 {
97 bail!("{id:?} was begun again while its run was still happening");
98 }
99 }
100 if self.law_told(&refusing) != vec![Telling::Refused] {
101 bail!("work refusing to begin was begun again");
102 }
103 Ok(())
104 }
105
106 fn check_saying_laws(&mut self) -> Result<()> {
112 let mut running = Vec::new();
113 let said = 3;
114 let id = self.law_wanting(said);
115 self.attend(&mut running);
116 while !running.is_empty() {
117 self.attend(&mut running);
118 }
119
120 let told = self.law_told(&id);
121 let heard = told
122 .iter()
123 .filter(|telling| **telling == Telling::Heard)
124 .count();
125 if heard != said {
126 bail!("a run stating {said} things was heard {heard} times");
127 }
128 let Some(ending) = told.iter().position(|telling| *telling == Telling::Ended) else {
129 bail!("a run that finished never stated how it ended");
130 };
131 if ending + 1 != told.len() {
132 bail!("something was stated after the run had ended");
133 }
134 if told.first() != Some(&Telling::Begun) {
135 bail!("a run stated something before it began");
136 }
137 Ok(())
138 }
139
140 fn check_unwanted_laws(&mut self) -> Result<()> {
146 let mut running = Vec::new();
147 let id = self.law_wanting(4);
148 self.attend(&mut running);
149 self.law_wants(&id, Wanted::Unwanted);
150
151 let mut passes = 0;
152 while !running.is_empty() {
153 self.attend(&mut running);
154 passes += 1;
155 if passes > 8 {
156 bail!("a run nobody wanted never ended");
157 }
158 }
159 if self.law_abandonments(&id) != passes - 1 {
162 bail!(
163 "a run nobody wanted was told to end {} times over {passes} passes",
164 self.law_abandonments(&id)
165 );
166 }
167 let ended = self
168 .law_told(&id)
169 .iter()
170 .filter(|telling| matches!(telling, Telling::Ended | Telling::Refused))
171 .count();
172 if ended != 1 {
173 bail!("a run nobody wanted stated how it ended {ended} times");
174 }
175 Ok(())
176 }
177
178 fn check_holding_laws(&mut self) -> Result<()> {
184 let mut running = Vec::new();
185 let id = self.law_wanting(5);
186 let tick = Duration::from_secs(1);
187 self.attend(&mut running);
188
189 self.law_passes(tick);
190 self.law_wants(&id, Wanted::Held);
191 self.attend(&mut running);
192 let held_at = last_elapsed(&self.law_told(&id));
193
194 self.law_passes(tick * 10);
195 self.attend(&mut running);
196 if last_elapsed(&self.law_told(&id)) != held_at {
197 bail!("a run held still went on running");
198 }
199
200 self.law_wants(&id, Wanted::Running);
201 self.attend(&mut running);
202 self.law_passes(tick);
203 self.attend(&mut running);
204 if last_elapsed(&self.law_told(&id)) != held_at.map(|elapsed| elapsed + tick) {
205 bail!("a run let go again did not carry on from where it was held");
206 }
207 Ok(())
208 }
209}
210
211#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub enum Telling {
214 Begun,
216 Heard,
218 RanFor(Duration),
220 Ended,
222 Refused,
224}
225
226fn begun_count(told: &[Telling]) -> usize {
228 told.iter()
229 .filter(|telling| **telling == Telling::Begun)
230 .count()
231}
232
233fn last_elapsed(told: &[Telling]) -> Option<Duration> {
235 told.iter().rev().find_map(|telling| match telling {
236 Telling::RanFor(elapsed) => Some(*elapsed),
237 Telling::Begun | Telling::Heard | Telling::Ended | Telling::Refused => None,
238 })
239}