Skip to main content

telltale_runtime/testing/
observer.rs

1//! Protocol observer trait for monitoring execution
2//!
3//! Observers receive callbacks during protocol execution, enabling
4//! logging, metrics collection, and simulation control.
5
6use crate::effects::ChoreographyError;
7
8/// Trait for observing protocol execution events.
9///
10/// Implementations can log events, collect metrics, or control
11/// simulation behavior based on observed events.
12pub trait ProtocolObserver: Send {
13    /// Called when a protocol phase starts.
14    fn on_phase_start(&mut self, protocol: &str, role: &str, phase: &str);
15
16    /// Called when a protocol phase ends.
17    fn on_phase_end(&mut self, protocol: &str, role: &str, phase: &str);
18
19    /// Called when a message is sent.
20    fn on_send(&mut self, from: &str, to: &str, msg_type: &str, size: usize);
21
22    /// Called when a message is received.
23    fn on_recv(&mut self, from: &str, to: &str, msg_type: &str, size: usize);
24
25    /// Called when a choice is made (internal choice).
26    fn on_choice(&mut self, decider: &str, branch: &str);
27
28    /// Called when a choice is received (external choice).
29    fn on_offer(&mut self, from: &str, branch: &str);
30
31    /// Called when an error occurs.
32    fn on_error(&mut self, protocol: &str, role: &str, error: &ChoreographyError);
33
34    /// Called when the protocol completes successfully.
35    fn on_complete(&mut self, protocol: &str, role: &str);
36}
37
38/// A null observer that does nothing.
39///
40/// Use this when you don't need to observe protocol execution.
41#[derive(Debug, Default, Clone, Copy)]
42pub struct NullObserver;
43
44impl ProtocolObserver for NullObserver {
45    fn on_phase_start(&mut self, _protocol: &str, _role: &str, _phase: &str) {}
46    fn on_phase_end(&mut self, _protocol: &str, _role: &str, _phase: &str) {}
47    fn on_send(&mut self, _from: &str, _to: &str, _msg_type: &str, _size: usize) {}
48    fn on_recv(&mut self, _from: &str, _to: &str, _msg_type: &str, _size: usize) {}
49    fn on_choice(&mut self, _decider: &str, _branch: &str) {}
50    fn on_offer(&mut self, _from: &str, _branch: &str) {}
51    fn on_error(&mut self, _protocol: &str, _role: &str, _error: &ChoreographyError) {}
52    fn on_complete(&mut self, _protocol: &str, _role: &str) {}
53}
54
55/// An event that occurred during protocol execution.
56#[derive(Debug, Clone)]
57pub enum ProtocolEvent {
58    /// A phase started.
59    PhaseStart {
60        protocol: String,
61        role: String,
62        phase: String,
63    },
64    /// A phase ended.
65    PhaseEnd {
66        protocol: String,
67        role: String,
68        phase: String,
69    },
70    /// A message was sent.
71    Send {
72        from: String,
73        to: String,
74        msg_type: String,
75        size: usize,
76    },
77    /// A message was received.
78    Recv {
79        from: String,
80        to: String,
81        msg_type: String,
82        size: usize,
83    },
84    /// A choice was made.
85    Choice { decider: String, branch: String },
86    /// A choice was received.
87    Offer { from: String, branch: String },
88    /// An error occurred.
89    Error {
90        protocol: String,
91        role: String,
92        error: String,
93    },
94    /// Protocol completed.
95    Complete { protocol: String, role: String },
96}
97
98/// A recording observer that stores all events.
99///
100/// Useful for testing and debugging protocol execution.
101#[derive(Debug, Default)]
102pub struct RecordingObserver {
103    events: Vec<ProtocolEvent>,
104}
105
106impl RecordingObserver {
107    /// Create a new recording observer.
108    #[must_use]
109    pub fn new() -> Self {
110        Self { events: Vec::new() }
111    }
112
113    /// Get all recorded events.
114    #[must_use]
115    pub fn events(&self) -> &[ProtocolEvent] {
116        &self.events
117    }
118
119    /// Take all recorded events, clearing the observer.
120    pub fn take_events(&mut self) -> Vec<ProtocolEvent> {
121        std::mem::take(&mut self.events)
122    }
123
124    /// Clear all recorded events.
125    pub fn clear(&mut self) {
126        self.events.clear();
127    }
128
129    /// Get the number of recorded events.
130    #[must_use]
131    pub fn len(&self) -> usize {
132        self.events.len()
133    }
134
135    /// Check if no events have been recorded.
136    #[must_use]
137    pub fn is_empty(&self) -> bool {
138        self.events.is_empty()
139    }
140
141    /// Get send events only.
142    pub fn sends(&self) -> impl Iterator<Item = &ProtocolEvent> {
143        self.events
144            .iter()
145            .filter(|e| matches!(e, ProtocolEvent::Send { .. }))
146    }
147
148    /// Get recv events only.
149    pub fn recvs(&self) -> impl Iterator<Item = &ProtocolEvent> {
150        self.events
151            .iter()
152            .filter(|e| matches!(e, ProtocolEvent::Recv { .. }))
153    }
154
155    /// Get error events only.
156    pub fn errors(&self) -> impl Iterator<Item = &ProtocolEvent> {
157        self.events
158            .iter()
159            .filter(|e| matches!(e, ProtocolEvent::Error { .. }))
160    }
161}
162
163impl ProtocolObserver for RecordingObserver {
164    fn on_phase_start(&mut self, protocol: &str, role: &str, phase: &str) {
165        self.events.push(ProtocolEvent::PhaseStart {
166            protocol: protocol.to_string(),
167            role: role.to_string(),
168            phase: phase.to_string(),
169        });
170    }
171
172    fn on_phase_end(&mut self, protocol: &str, role: &str, phase: &str) {
173        self.events.push(ProtocolEvent::PhaseEnd {
174            protocol: protocol.to_string(),
175            role: role.to_string(),
176            phase: phase.to_string(),
177        });
178    }
179
180    fn on_send(&mut self, from: &str, to: &str, msg_type: &str, size: usize) {
181        self.events.push(ProtocolEvent::Send {
182            from: from.to_string(),
183            to: to.to_string(),
184            msg_type: msg_type.to_string(),
185            size,
186        });
187    }
188
189    fn on_recv(&mut self, from: &str, to: &str, msg_type: &str, size: usize) {
190        self.events.push(ProtocolEvent::Recv {
191            from: from.to_string(),
192            to: to.to_string(),
193            msg_type: msg_type.to_string(),
194            size,
195        });
196    }
197
198    fn on_choice(&mut self, decider: &str, branch: &str) {
199        self.events.push(ProtocolEvent::Choice {
200            decider: decider.to_string(),
201            branch: branch.to_string(),
202        });
203    }
204
205    fn on_offer(&mut self, from: &str, branch: &str) {
206        self.events.push(ProtocolEvent::Offer {
207            from: from.to_string(),
208            branch: branch.to_string(),
209        });
210    }
211
212    fn on_error(&mut self, protocol: &str, role: &str, error: &ChoreographyError) {
213        self.events.push(ProtocolEvent::Error {
214            protocol: protocol.to_string(),
215            role: role.to_string(),
216            error: error.to_string(),
217        });
218    }
219
220    fn on_complete(&mut self, protocol: &str, role: &str) {
221        self.events.push(ProtocolEvent::Complete {
222            protocol: protocol.to_string(),
223            role: role.to_string(),
224        });
225    }
226}
227
228/// A multiplexing observer that forwards events to multiple observers.
229pub struct MultiObserver<'a> {
230    observers: Vec<&'a mut dyn ProtocolObserver>,
231}
232
233impl<'a> MultiObserver<'a> {
234    /// Create a new multi-observer.
235    pub fn new(observers: Vec<&'a mut dyn ProtocolObserver>) -> Self {
236        Self { observers }
237    }
238}
239
240impl ProtocolObserver for MultiObserver<'_> {
241    fn on_phase_start(&mut self, protocol: &str, role: &str, phase: &str) {
242        for obs in &mut self.observers {
243            obs.on_phase_start(protocol, role, phase);
244        }
245    }
246
247    fn on_phase_end(&mut self, protocol: &str, role: &str, phase: &str) {
248        for obs in &mut self.observers {
249            obs.on_phase_end(protocol, role, phase);
250        }
251    }
252
253    fn on_send(&mut self, from: &str, to: &str, msg_type: &str, size: usize) {
254        for obs in &mut self.observers {
255            obs.on_send(from, to, msg_type, size);
256        }
257    }
258
259    fn on_recv(&mut self, from: &str, to: &str, msg_type: &str, size: usize) {
260        for obs in &mut self.observers {
261            obs.on_recv(from, to, msg_type, size);
262        }
263    }
264
265    fn on_choice(&mut self, decider: &str, branch: &str) {
266        for obs in &mut self.observers {
267            obs.on_choice(decider, branch);
268        }
269    }
270
271    fn on_offer(&mut self, from: &str, branch: &str) {
272        for obs in &mut self.observers {
273            obs.on_offer(from, branch);
274        }
275    }
276
277    fn on_error(&mut self, protocol: &str, role: &str, error: &ChoreographyError) {
278        for obs in &mut self.observers {
279            obs.on_error(protocol, role, error);
280        }
281    }
282
283    fn on_complete(&mut self, protocol: &str, role: &str) {
284        for obs in &mut self.observers {
285            obs.on_complete(protocol, role);
286        }
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn test_null_observer() {
296        let mut obs = NullObserver;
297        // Should not panic
298        obs.on_phase_start("Proto", "Role", "Phase");
299        obs.on_send("A", "B", "Msg", 100);
300    }
301
302    #[test]
303    fn test_recording_observer() {
304        let mut obs = RecordingObserver::new();
305
306        obs.on_phase_start("Proto", "Client", "handshake");
307        obs.on_send("Client", "Server", "Request", 50);
308        obs.on_recv("Server", "Client", "Response", 100);
309        obs.on_phase_end("Proto", "Client", "handshake");
310
311        assert_eq!(obs.len(), 4);
312        assert_eq!(obs.sends().count(), 1);
313        assert_eq!(obs.recvs().count(), 1);
314    }
315
316    #[test]
317    fn test_recording_observer_take() {
318        let mut obs = RecordingObserver::new();
319        obs.on_send("A", "B", "Msg", 10);
320
321        let events = obs.take_events();
322        assert_eq!(events.len(), 1);
323        assert!(obs.is_empty());
324    }
325}