trellis_testing/
harness_step.rs1use std::fmt::Debug;
2
3use trellis_core::{
4 GraphResult, InputNode, OutputFrameTrace, ResourceCommandTrace, Transaction, TransactionResult,
5};
6
7use crate::{ScenarioError, ScenarioTarget, StageOperation, TrellisHarness};
8
9pub(crate) type InvariantCheck<G, C> = dyn Fn(&G, &TransactionResult<C>) -> bool + 'static;
10
11pub(crate) struct NamedInvariantCheck<G, C> {
12 pub(crate) name: String,
13 pub(crate) check: Box<InvariantCheck<G, C>>,
14}
15
16pub struct HarnessStep<'harness, G, C> {
18 harness: &'harness mut TrellisHarness<G, C>,
19 name: String,
20 operations: Vec<Box<StageOperation<C>>>,
21 expected_resource_commands: Option<Vec<ResourceCommandTrace>>,
22 expected_output_frames: Option<Vec<OutputFrameTrace>>,
23 invariant_checks: Vec<NamedInvariantCheck<G, C>>,
24}
25
26impl<'harness, G, C> HarnessStep<'harness, G, C> {
27 pub(crate) fn new(harness: &'harness mut TrellisHarness<G, C>, name: String) -> Self {
28 Self {
29 harness,
30 name,
31 operations: Vec::new(),
32 expected_resource_commands: None,
33 expected_output_frames: None,
34 invariant_checks: Vec::new(),
35 }
36 }
37}
38
39impl<'harness, G, C> HarnessStep<'harness, G, C>
40where
41 G: ScenarioTarget<C>,
42 C: Clone + Debug + PartialEq,
43{
44 pub fn input<T>(mut self, input: InputNode<T>, value: T) -> Self
46 where
47 T: Clone + PartialEq + Send + Sync + 'static,
48 {
49 self.operations
50 .push(Box::new(move |tx| tx.set_input(input, value.clone())));
51 self
52 }
53
54 pub fn operation(
56 mut self,
57 operation: impl for<'tx> Fn(&mut Transaction<'tx, C>) -> GraphResult<()> + 'static,
58 ) -> Self {
59 self.operations.push(Box::new(operation));
60 self
61 }
62
63 pub fn expect_plan(mut self, command: ResourceCommandTrace) -> Self {
65 self.expected_resource_commands
66 .get_or_insert_with(Vec::new)
67 .push(command);
68 self
69 }
70
71 pub fn expect_plans(
73 mut self,
74 commands: impl IntoIterator<Item = ResourceCommandTrace>,
75 ) -> Self {
76 self.expected_resource_commands = Some(commands.into_iter().collect());
77 self
78 }
79
80 pub fn expect_output(mut self, frame: OutputFrameTrace) -> Self {
82 self.expected_output_frames
83 .get_or_insert_with(Vec::new)
84 .push(frame);
85 self
86 }
87
88 pub fn expect_outputs(mut self, frames: impl IntoIterator<Item = OutputFrameTrace>) -> Self {
90 self.expected_output_frames = Some(frames.into_iter().collect());
91 self
92 }
93
94 pub fn check(
96 mut self,
97 name: impl Into<String>,
98 check: impl Fn(&G, &TransactionResult<C>) -> bool + 'static,
99 ) -> Self {
100 self.invariant_checks.push(NamedInvariantCheck {
101 name: name.into(),
102 check: Box::new(check),
103 });
104 self
105 }
106
107 pub fn commit(self) -> Result<&'harness mut TrellisHarness<G, C>, ScenarioError> {
109 self.harness.commit_operations(
110 &self.name,
111 &self.operations,
112 &self.invariant_checks,
113 self.expected_resource_commands.as_deref(),
114 self.expected_output_frames.as_deref(),
115 )?;
116 Ok(self.harness)
117 }
118}