Laws selected by capability.

An adapter's harness says what it can make by implementing the fixture trait of each capability it has (MemoryFixture, JournalFixture, ...); [Suite] then offers exactly the law sets of those capabilities. A law set for a capability the harness has no fixture for is not skipped, it cannot be selected: the method that would select it does not apply to the harness, and the compiler says which fixture is missing.

The fixture traits sit beside the Has… capability traits of whiskers-ports and mirror them: a capability the service can be given (HasJournal) has a fixture the suite can be given (JournalFixture). A harness for a whole adapter implements [Fixtures], which [Suite::complete] runs in full.

Capabilities with no fixture here have no law yet: the embedder and the thinker are held to nothing a conformance run can show without calling out (what a real model server says back is the adapter's own test), and Secrets is not served by a route; its laws are suite::secrets.

16use whiskers_ports::{Allowance, ChatHub, Clock, HouseholdHub, Journal, Judge, MemoryHub, PictureShelf, Voice};
18use super::hubs::WriteSwitch;
19use super::{allowance, hubs, journal, outbound, pictures};
20
21macro_rules! stateful_fixture {
22    ($(#[$doc:meta])* $name:ident, $port:ident) => {
23        $(#[$doc])*
24        pub trait $name {
25            type Port: $port;

A new, empty port of this capability.

27            fn fresh(&self) -> Self::Port;

The same port after its process restarted: it must find what it kept.

29            fn restart(&self, port: Self::Port) -> Self::Port;
30        }
31    };
32}
34stateful_fixture!(

How to make, and restart, the memory hub (capability HasMemory).

36    MemoryFixture, MemoryHub
37);
38stateful_fixture!(

How to make, and restart, the household hub (capability HasHousehold).

40    HouseholdFixture, HouseholdHub
41);
42stateful_fixture!(

How to make, and restart, the chat hub (capability HasChat).

44    ChatFixture, ChatHub
45);
46stateful_fixture!(

How to make, and restart, the journal (capability HasJournal).

48    JournalFixture, Journal
49);
50stateful_fixture!(

How to make, and restart, the picture shelf (capability HasPictures).

52    PicturesFixture, PictureShelf
53);
54stateful_fixture!(

How to make, and restart, the allowance (capability HasAllowance).

56    AllowanceFixture, Allowance
57);

How to make a chat hub whose storage can be told to refuse writes, as a full disk would. Not every harness can (a black box over HTTP cannot), so the law that needs it is selected apart from the chat hub's others.

61pub trait ChatFaultFixture {
62    type Port: ChatHub;

A new chat hub and the switch that makes its storage refuse writes (true) or take them again.

64    fn fresh_refusing(&self) -> (Self::Port, WriteSwitch);

The same hub after a restart, its storage working again.

66    fn restart_refusing(&self, port: Self::Port) -> Self::Port;
67}

A judge with nothing to call with (capability HasJudge).

70pub trait JudgeFixture {
71    type Port: Judge;
72    fn without_credentials(&self) -> Self::Port;
73}

A voice with nothing to call with (capability HasVoice). The law that has it speak, which a paid voice runs only when its operator agrees to the spend, is suite::timing::speaks_timed, called on purpose.

77pub trait VoiceFixture {
78    type Port: Voice;
79    fn without_credentials(&self) -> Self::Port;
80}

A clock (capability HasClock).

83pub trait ClockFixture {
84    type Port: Clock;
85    fn clock(&self) -> Self::Port;
86}

A harness for a whole adapter: a fixture for every capability there is a law for.

89pub trait Fixtures:
90    MemoryFixture + HouseholdFixture + ChatFixture + ChatFaultFixture + JournalFixture + PicturesFixture + AllowanceFixture + JudgeFixture + VoiceFixture + ClockFixture
91{
92}
94impl<F> Fixtures for F where
95    F: MemoryFixture
96        + HouseholdFixture
97        + ChatFixture
98        + ChatFaultFixture
99        + JournalFixture
100        + PicturesFixture
101        + AllowanceFixture
102        + JudgeFixture
103        + VoiceFixture
104        + ClockFixture
105{
106}

The capabilities whose laws a [Suite] has run, by name, in the order run.

109pub type Ran = Vec<&'static str>;

A harness, and the laws chosen for it so far. Every method runs the laws of one capability, and applies only to a harness with that capability's fixture. A harness with the memory hub and nothing else can run the memory hub's laws:

use whiskers_conformance::memory::MemMemoryHub;
use whiskers_conformance::suite::fixture::{MemoryFixture, Suite};

struct OnlyMemory;
impl MemoryFixture for OnlyMemory {
    type Port = MemMemoryHub;
    fn fresh(&self) -> MemMemoryHub { MemMemoryHub::default() }
    fn restart(&self, port: MemMemoryHub) -> MemMemoryHub { port }
}

let suite = Suite::new(OnlyMemory).memory();
assert_eq!(suite.ran(), ["memory"]);

It cannot select the journal's laws. They are not skipped, there is no way to ask for them:

use whiskers_conformance::memory::MemMemoryHub;
use whiskers_conformance::suite::fixture::{MemoryFixture, Suite};

struct OnlyMemory;
impl MemoryFixture for OnlyMemory {
    type Port = MemMemoryHub;
    fn fresh(&self) -> MemMemoryHub { MemMemoryHub::default() }
    fn restart(&self, port: MemMemoryHub) -> MemMemoryHub { port }
}

// error[E0277]: the trait bound `OnlyMemory: JournalFixture` is not satisfied
let _ = Suite::new(OnlyMemory).memory().journal();
146pub struct Suite<F> {
147    fixture: F,
148    ran: Ran,
149}
151impl<F> Suite<F> {

A suite that has run nothing.

153    pub fn new(fixture: F) -> Self {
154        Self { fixture, ran: Vec::new() }
155    }

Which capabilities' laws have run.

158    pub fn ran(&self) -> &[&'static str] {
159        &self.ran
160    }

The memory hub's laws.

163    pub fn memory(mut self) -> Self
164    where
165        F: MemoryFixture,
166    {
167        hubs::memory_hub(&|| MemoryFixture::fresh(&self.fixture), &|p| MemoryFixture::restart(&self.fixture, p));
168        self.ran.push("memory");
169        self
170    }

The household hub's laws.

173    pub fn household(mut self) -> Self
174    where
175        F: HouseholdFixture,
176    {
177        hubs::household_hub(&|| HouseholdFixture::fresh(&self.fixture), &|p| HouseholdFixture::restart(&self.fixture, p));
178        self.ran.push("household");
179        self
180    }

The chat hub's laws, all but the one that needs its storage to fail ([Suite::chat_write_faults]).

183    pub fn chat(mut self) -> Self
184    where
185        F: ChatFixture,
186    {
187        hubs::chat_hub_whose_storage_works(&|| ChatFixture::fresh(&self.fixture), &|p| ChatFixture::restart(&self.fixture, p));
188        self.ran.push("chat");
189        self
190    }

The chat-hub law that a copy that cannot be kept is an error and changes nothing.

193    pub fn chat_write_faults(mut self) -> Self
194    where
195        F: ChatFaultFixture,
196    {
197        hubs::chat_a_copy_that_cannot_be_kept_is_an_error_and_changes_nothing(
198            &|| ChatFaultFixture::fresh_refusing(&self.fixture),
199            &|p| ChatFaultFixture::restart_refusing(&self.fixture, p),
200        );
201        self.ran.push("chat write faults");
202        self
203    }

The journal's laws.

206    pub fn journal(mut self) -> Self
207    where
208        F: JournalFixture,
209    {
210        journal::journal(&|| JournalFixture::fresh(&self.fixture), &|p| JournalFixture::restart(&self.fixture, p));
211        self.ran.push("journal");
212        self
213    }

The picture shelf's laws.

216    pub fn pictures(mut self) -> Self
217    where
218        F: PicturesFixture,
219    {
220        pictures::pictures(&|| PicturesFixture::fresh(&self.fixture), &|p| PicturesFixture::restart(&self.fixture, p));
221        self.ran.push("pictures");
222        self
223    }

The allowance's laws.

226    pub fn allowance(mut self) -> Self
227    where
228        F: AllowanceFixture,
229    {
230        allowance::allowance(&|| AllowanceFixture::fresh(&self.fixture), &|p| AllowanceFixture::restart(&self.fixture, p));
231        self.ran.push("allowance");
232        self
233    }

What a judge with no credentials does.

236    pub fn judge(mut self) -> Self
237    where
238        F: JudgeFixture,
239    {
240        outbound::judge_without_credentials(&JudgeFixture::without_credentials(&self.fixture));
241        self.ran.push("judge");
242        self
243    }

What a voice with no credentials does.

246    pub fn voice(mut self) -> Self
247    where
248        F: VoiceFixture,
249    {
250        outbound::voice_without_credentials(&VoiceFixture::without_credentials(&self.fixture));
251        self.ran.push("voice");
252        self
253    }

The clock's laws.

256    pub fn clock(mut self) -> Self
257    where
258        F: ClockFixture,
259    {
260        outbound::clock(&ClockFixture::clock(&self.fixture));
261        self.ran.push("clock");
262        self
263    }

Every law there is for every capability, for a harness of a whole adapter.

266    pub fn complete(fixture: F) -> Self
267    where
268        F: Fixtures,
269    {
270        Self::new(fixture).memory().household().chat().chat_write_faults().journal().pictures().allowance().judge().voice().clock()
271    }
272}