Skip to main content

rs_teststand_bridge/
error.rs

1//! What can go wrong when the engine lives on someone else's thread.
2
3/// A failure from the host, as distinct from a failure from the engine.
4///
5/// The two are worth telling apart. An [`Engine`](Self::Engine) error means the
6/// engine refused the work and the host is still serving; the other variants
7/// mean the host itself is unavailable, and retrying the same request will not
8/// help.
9#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum Error {
12    /// The engine refused the work. The host is unaffected.
13    #[error(transparent)]
14    Engine(#[from] rs_teststand::Error),
15
16    /// The engine thread could not be started.
17    #[error("the engine thread could not be started: {reason}")]
18    ThreadNotStarted {
19        /// What the operating system reported.
20        reason: String,
21    },
22
23    /// The engine thread has stopped, so no further work can be run.
24    ///
25    /// Terminal for this host: build a new one.
26    #[error("the engine thread has stopped")]
27    HostStopped,
28
29    /// The work was submitted but the thread ended before answering.
30    #[error("the engine thread ended before returning a result")]
31    ResultLost,
32
33    /// A message's object payload could not be turned into data.
34    ///
35    /// Separate from [`Engine`](Self::Engine) because the engine answered
36    /// perfectly well: what failed was rendering the tree it handed over, and
37    /// the rest of the message is still worth delivering.
38    #[error("a message payload could not be serialized: {0}")]
39    Payload(#[from] serde_json::Error),
40
41    /// The socket carrying events failed.
42    ///
43    /// Separate again from [`Engine`](Self::Engine): the engine is fine and the
44    /// run is unaffected, only the listener is unreachable. A host may well
45    /// choose to keep testing and stop reporting.
46    #[error("the event transport failed: {0}")]
47    Transport(#[from] std::io::Error),
48
49    /// A control frame payload exceeded what the protocol allows.
50    ///
51    /// RFC 6455 section 5.5 caps every control frame at 125 bytes. A peer that
52    /// receives a larger one fails the connection, and the send that caused it
53    /// reports success, so this is raised before the frame goes out.
54    #[error("control frame payload of {bytes} bytes exceeds the {limit}-byte limit")]
55    ControlFrameTooLarge {
56        /// What was offered.
57        bytes: usize,
58        /// What the protocol allows.
59        limit: usize,
60    },
61}
62
63#[cfg(test)]
64mod tests {
65    use super::Error;
66
67    #[test]
68    fn an_engine_failure_keeps_its_own_message() {
69        // The host must not bury what the engine said behind wording of its
70        // own; a caller debugging a refused call needs the engine's text.
71        let engine = rs_teststand::Error::UnexpectedType {
72            expected: "a number",
73            actual: "a string",
74        };
75        let expected = engine.to_string();
76        let wrapped = Error::from(engine);
77        assert_eq!(wrapped.to_string(), expected);
78    }
79
80    #[test]
81    fn a_payload_failure_is_not_reported_as_an_engine_failure() {
82        // The distinction matters to a host deciding whether to retry: the
83        // engine is fine, only this message's object could not be rendered.
84        let parsed = serde_json::from_str::<serde_json::Value>("{");
85        assert!(parsed.is_err(), "an unterminated object is not valid JSON");
86        if let Err(broken) = parsed {
87            let wrapped = Error::from(broken);
88            assert!(matches!(wrapped, Error::Payload(_)), "got {wrapped:?}");
89            assert!(wrapped.to_string().starts_with("a message payload"));
90        }
91    }
92
93    #[test]
94    fn a_stopped_host_says_so_without_blaming_the_engine() {
95        assert_eq!(
96            Error::HostStopped.to_string(),
97            "the engine thread has stopped"
98        );
99    }
100}