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!("one pass began {} runs where {} requests wanted work", running.len(), wanting.len());
80 }
81 for id in &wanting {
82 if begun_count(&self.law_told(id)) != 1 {
83 bail!("{id:?} was not told once that its work began");
84 }
85 }
86 if self.law_told(&refusing) != vec![Telling::Refused] {
87 bail!("work refusing to begin was not stated once as a refusal");
88 }
89
90 self.attend(&mut running);
91 for id in &wanting {
92 if begun_count(&self.law_told(id)) != 1 {
93 bail!("{id:?} was begun again while its run was still happening");
94 }
95 }
96 if self.law_told(&refusing) != vec![Telling::Refused] {
97 bail!("work refusing to begin was begun again");
98 }
99 Ok(())
100 }
101
102 fn check_saying_laws(&mut self) -> Result<()> {
108 let mut running = Vec::new();
109 let said = 3;
110 let id = self.law_wanting(said);
111 self.attend(&mut running);
112 while !running.is_empty() {
113 self.attend(&mut running);
114 }
115
116 let told = self.law_told(&id);
117 let heard = told.iter().filter(|telling| **telling == Telling::Heard).count();
118 if heard != said {
119 bail!("a run stating {said} things was heard {heard} times");
120 }
121 let Some(ending) = told.iter().position(|telling| *telling == Telling::Ended) else {
122 bail!("a run that finished never stated how it ended");
123 };
124 if ending + 1 != told.len() {
125 bail!("something was stated after the run had ended");
126 }
127 if told.first() != Some(&Telling::Begun) {
128 bail!("a run stated something before it began");
129 }
130 Ok(())
131 }
132
133 fn check_unwanted_laws(&mut self) -> Result<()> {
139 let mut running = Vec::new();
140 let id = self.law_wanting(4);
141 self.attend(&mut running);
142 self.law_wants(&id, Wanted::Unwanted);
143
144 let mut passes = 0;
145 while !running.is_empty() {
146 self.attend(&mut running);
147 passes += 1;
148 if passes > 8 {
149 bail!("a run nobody wanted never ended");
150 }
151 }
152 if self.law_abandonments(&id) != passes - 1 {
155 bail!("a run nobody wanted was told to end {} times over {passes} passes", self.law_abandonments(&id));
156 }
157 let ended =
158 self.law_told(&id).iter().filter(|telling| matches!(telling, Telling::Ended | Telling::Refused)).count();
159 if ended != 1 {
160 bail!("a run nobody wanted stated how it ended {ended} times");
161 }
162 Ok(())
163 }
164
165 fn check_holding_laws(&mut self) -> Result<()> {
171 let mut running = Vec::new();
172 let id = self.law_wanting(5);
173 let tick = Duration::from_secs(1);
174 self.attend(&mut running);
175
176 self.law_passes(tick);
177 self.law_wants(&id, Wanted::Held);
178 self.attend(&mut running);
179 let held_at = last_elapsed(&self.law_told(&id));
180
181 self.law_passes(tick * 10);
182 self.attend(&mut running);
183 if last_elapsed(&self.law_told(&id)) != held_at {
184 bail!("a run held still went on running");
185 }
186
187 self.law_wants(&id, Wanted::Running);
188 self.attend(&mut running);
189 self.law_passes(tick);
190 self.attend(&mut running);
191 if last_elapsed(&self.law_told(&id)) != held_at.map(|elapsed| elapsed + tick) {
192 bail!("a run let go again did not carry on from where it was held");
193 }
194 Ok(())
195 }
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200pub enum Telling {
201 Begun,
203 Heard,
205 RanFor(Duration),
207 Ended,
209 Refused,
211}
212
213fn begun_count(told: &[Telling]) -> usize {
215 told.iter().filter(|telling| **telling == Telling::Begun).count()
216}
217
218fn last_elapsed(told: &[Telling]) -> Option<Duration> {
220 told.iter().rev().find_map(|telling| match telling {
221 Telling::RanFor(elapsed) => Some(*elapsed),
222 Telling::Begun | Telling::Heard | Telling::Ended | Telling::Refused => None,
223 })
224}