nix_compat/wire/ser/
mock.rs

1use std::collections::VecDeque;
2use std::fmt;
3use std::io;
4use std::thread;
5
6#[cfg(test)]
7use ::proptest::prelude::TestCaseError;
8use thiserror::Error;
9
10use crate::wire::ProtocolVersion;
11
12use super::NixWrite;
13
14#[derive(Debug, Error, PartialEq, Eq, Clone)]
15pub enum Error {
16    #[error("custom error '{0}'")]
17    Custom(String),
18    #[error("unsupported data error '{0}'")]
19    UnsupportedData(String),
20    #[error("Invalid enum: {0}")]
21    InvalidEnum(String),
22    #[error("IO error {0} '{1}'")]
23    IO(io::ErrorKind, String),
24    #[error("wrong write: expected {0} got {1}")]
25    WrongWrite(OperationType, OperationType),
26    #[error("unexpected write: got an extra {0}")]
27    ExtraWrite(OperationType),
28    #[error("got an unexpected number {0} in write_number")]
29    UnexpectedNumber(u64),
30    #[error("got an unexpected slice '{0:?}' in write_slice")]
31    UnexpectedSlice(Vec<u8>),
32    #[error("got an unexpected display '{0:?}' in write_slice")]
33    UnexpectedDisplay(String),
34}
35
36impl Error {
37    pub fn unexpected_write_number(expected: OperationType) -> Error {
38        Error::WrongWrite(expected, OperationType::WriteNumber)
39    }
40
41    pub fn extra_write_number() -> Error {
42        Error::ExtraWrite(OperationType::WriteNumber)
43    }
44
45    pub fn unexpected_write_slice(expected: OperationType) -> Error {
46        Error::WrongWrite(expected, OperationType::WriteSlice)
47    }
48
49    pub fn unexpected_write_display(expected: OperationType) -> Error {
50        Error::WrongWrite(expected, OperationType::WriteDisplay)
51    }
52}
53
54impl super::Error for Error {
55    fn custom<T: fmt::Display>(msg: T) -> Self {
56        Self::Custom(msg.to_string())
57    }
58
59    fn io_error(err: std::io::Error) -> Self {
60        Self::IO(err.kind(), err.to_string())
61    }
62
63    fn unsupported_data<T: fmt::Display>(msg: T) -> Self {
64        Self::UnsupportedData(msg.to_string())
65    }
66
67    fn invalid_enum<T: fmt::Display>(msg: T) -> Self {
68        Self::InvalidEnum(msg.to_string())
69    }
70}
71
72#[allow(clippy::enum_variant_names)]
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74pub enum OperationType {
75    WriteNumber,
76    WriteSlice,
77    WriteDisplay,
78}
79
80impl fmt::Display for OperationType {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        match self {
83            Self::WriteNumber => write!(f, "write_number"),
84            Self::WriteSlice => write!(f, "write_slice"),
85            Self::WriteDisplay => write!(f, "write_display"),
86        }
87    }
88}
89
90#[allow(clippy::enum_variant_names)]
91#[derive(Debug, Clone, PartialEq, Eq)]
92enum Operation {
93    WriteNumber(u64, Result<(), Error>),
94    WriteSlice(Vec<u8>, Result<(), Error>),
95    WriteDisplay(String, Result<(), Error>),
96}
97
98impl From<Operation> for OperationType {
99    fn from(value: Operation) -> Self {
100        match value {
101            Operation::WriteNumber(_, _) => OperationType::WriteNumber,
102            Operation::WriteSlice(_, _) => OperationType::WriteSlice,
103            Operation::WriteDisplay(_, _) => OperationType::WriteDisplay,
104        }
105    }
106}
107
108pub struct Builder {
109    version: ProtocolVersion,
110    ops: VecDeque<Operation>,
111}
112
113impl Builder {
114    pub fn new() -> Builder {
115        Builder {
116            version: Default::default(),
117            ops: VecDeque::new(),
118        }
119    }
120
121    pub fn version<V: Into<ProtocolVersion>>(&mut self, version: V) -> &mut Self {
122        self.version = version.into();
123        self
124    }
125
126    pub fn write_number(&mut self, value: u64) -> &mut Self {
127        self.ops.push_back(Operation::WriteNumber(value, Ok(())));
128        self
129    }
130
131    pub fn write_number_error(&mut self, value: u64, err: Error) -> &mut Self {
132        self.ops.push_back(Operation::WriteNumber(value, Err(err)));
133        self
134    }
135
136    pub fn write_slice(&mut self, value: &[u8]) -> &mut Self {
137        self.ops
138            .push_back(Operation::WriteSlice(value.to_vec(), Ok(())));
139        self
140    }
141
142    pub fn write_slice_error(&mut self, value: &[u8], err: Error) -> &mut Self {
143        self.ops
144            .push_back(Operation::WriteSlice(value.to_vec(), Err(err)));
145        self
146    }
147
148    pub fn write_display<D>(&mut self, value: D) -> &mut Self
149    where
150        D: fmt::Display,
151    {
152        let msg = value.to_string();
153        self.ops.push_back(Operation::WriteDisplay(msg, Ok(())));
154        self
155    }
156
157    pub fn write_display_error<D>(&mut self, value: D, err: Error) -> &mut Self
158    where
159        D: fmt::Display,
160    {
161        let msg = value.to_string();
162        self.ops.push_back(Operation::WriteDisplay(msg, Err(err)));
163        self
164    }
165
166    #[cfg(test)]
167    fn write_operation_type(&mut self, op: OperationType) -> &mut Self {
168        match op {
169            OperationType::WriteNumber => self.write_number(10),
170            OperationType::WriteSlice => self.write_slice(b"testing"),
171            OperationType::WriteDisplay => self.write_display("testing"),
172        }
173    }
174
175    #[cfg(test)]
176    fn write_operation(&mut self, op: &Operation) -> &mut Self {
177        match op {
178            Operation::WriteNumber(value, Ok(_)) => self.write_number(*value),
179            Operation::WriteNumber(value, Err(Error::UnexpectedNumber(_))) => {
180                self.write_number(*value)
181            }
182            Operation::WriteNumber(_, Err(Error::ExtraWrite(OperationType::WriteNumber))) => self,
183            Operation::WriteNumber(_, Err(Error::WrongWrite(op, OperationType::WriteNumber))) => {
184                self.write_operation_type(*op)
185            }
186            Operation::WriteNumber(value, Err(Error::Custom(msg))) => {
187                self.write_number_error(*value, Error::Custom(msg.clone()))
188            }
189            Operation::WriteNumber(value, Err(Error::IO(kind, msg))) => {
190                self.write_number_error(*value, Error::IO(*kind, msg.clone()))
191            }
192            Operation::WriteSlice(value, Ok(_)) => self.write_slice(value),
193            Operation::WriteSlice(value, Err(Error::UnexpectedSlice(_))) => self.write_slice(value),
194            Operation::WriteSlice(_, Err(Error::ExtraWrite(OperationType::WriteSlice))) => self,
195            Operation::WriteSlice(_, Err(Error::WrongWrite(op, OperationType::WriteSlice))) => {
196                self.write_operation_type(*op)
197            }
198            Operation::WriteSlice(value, Err(Error::Custom(msg))) => {
199                self.write_slice_error(value, Error::Custom(msg.clone()))
200            }
201            Operation::WriteSlice(value, Err(Error::IO(kind, msg))) => {
202                self.write_slice_error(value, Error::IO(*kind, msg.clone()))
203            }
204            Operation::WriteDisplay(value, Ok(_)) => self.write_display(value),
205            Operation::WriteDisplay(value, Err(Error::Custom(msg))) => {
206                self.write_display_error(value, Error::Custom(msg.clone()))
207            }
208            Operation::WriteDisplay(value, Err(Error::IO(kind, msg))) => {
209                self.write_display_error(value, Error::IO(*kind, msg.clone()))
210            }
211            Operation::WriteDisplay(value, Err(Error::UnexpectedDisplay(_))) => {
212                self.write_display(value)
213            }
214            Operation::WriteDisplay(_, Err(Error::ExtraWrite(OperationType::WriteDisplay))) => self,
215            Operation::WriteDisplay(_, Err(Error::WrongWrite(op, OperationType::WriteDisplay))) => {
216                self.write_operation_type(*op)
217            }
218            s => panic!("Invalid operation {s:?}"),
219        }
220    }
221
222    pub fn build(&mut self) -> Mock {
223        Mock {
224            version: self.version,
225            ops: self.ops.clone(),
226        }
227    }
228}
229
230impl Default for Builder {
231    fn default() -> Self {
232        Self::new()
233    }
234}
235
236pub struct Mock {
237    version: ProtocolVersion,
238    ops: VecDeque<Operation>,
239}
240
241impl Mock {
242    #[cfg(test)]
243    #[allow(dead_code)]
244    async fn assert_operation(&mut self, op: Operation) {
245        match op {
246            Operation::WriteNumber(_, Err(Error::UnexpectedNumber(value))) => {
247                assert_eq!(
248                    self.write_number(value).await,
249                    Err(Error::UnexpectedNumber(value))
250                );
251            }
252            Operation::WriteNumber(value, res) => {
253                assert_eq!(self.write_number(value).await, res);
254            }
255            Operation::WriteSlice(_, ref res @ Err(Error::UnexpectedSlice(ref value))) => {
256                assert_eq!(self.write_slice(value).await, res.clone());
257            }
258            Operation::WriteSlice(value, res) => {
259                assert_eq!(self.write_slice(&value).await, res);
260            }
261            Operation::WriteDisplay(_, ref res @ Err(Error::UnexpectedDisplay(ref value))) => {
262                assert_eq!(self.write_display(value).await, res.clone());
263            }
264            Operation::WriteDisplay(value, res) => {
265                assert_eq!(self.write_display(value).await, res);
266            }
267        }
268    }
269
270    #[cfg(test)]
271    async fn prop_assert_operation(&mut self, op: Operation) -> Result<(), TestCaseError> {
272        use ::proptest::prop_assert_eq;
273
274        match op {
275            Operation::WriteNumber(_, Err(Error::UnexpectedNumber(value))) => {
276                prop_assert_eq!(
277                    self.write_number(value).await,
278                    Err(Error::UnexpectedNumber(value))
279                );
280            }
281            Operation::WriteNumber(value, res) => {
282                prop_assert_eq!(self.write_number(value).await, res);
283            }
284            Operation::WriteSlice(_, ref res @ Err(Error::UnexpectedSlice(ref value))) => {
285                prop_assert_eq!(self.write_slice(value).await, res.clone());
286            }
287            Operation::WriteSlice(value, res) => {
288                prop_assert_eq!(self.write_slice(&value).await, res);
289            }
290            Operation::WriteDisplay(_, ref res @ Err(Error::UnexpectedDisplay(ref value))) => {
291                prop_assert_eq!(self.write_display(&value).await, res.clone());
292            }
293            Operation::WriteDisplay(value, res) => {
294                prop_assert_eq!(self.write_display(&value).await, res);
295            }
296        }
297        Ok(())
298    }
299}
300
301impl NixWrite for Mock {
302    type Error = Error;
303
304    fn version(&self) -> ProtocolVersion {
305        self.version
306    }
307
308    async fn write_number(&mut self, value: u64) -> Result<(), Self::Error> {
309        match self.ops.pop_front() {
310            Some(Operation::WriteNumber(expected, ret)) => {
311                if value != expected {
312                    return Err(Error::UnexpectedNumber(value));
313                }
314                ret
315            }
316            Some(op) => Err(Error::unexpected_write_number(op.into())),
317            _ => Err(Error::ExtraWrite(OperationType::WriteNumber)),
318        }
319    }
320
321    async fn write_slice(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
322        match self.ops.pop_front() {
323            Some(Operation::WriteSlice(expected, ret)) => {
324                if buf != expected {
325                    return Err(Error::UnexpectedSlice(buf.to_vec()));
326                }
327                ret
328            }
329            Some(op) => Err(Error::unexpected_write_slice(op.into())),
330            _ => Err(Error::ExtraWrite(OperationType::WriteSlice)),
331        }
332    }
333
334    async fn write_display<D>(&mut self, msg: D) -> Result<(), Self::Error>
335    where
336        D: fmt::Display + Send,
337        Self: Sized,
338    {
339        let value = msg.to_string();
340        match self.ops.pop_front() {
341            Some(Operation::WriteDisplay(expected, ret)) => {
342                if value != expected {
343                    return Err(Error::UnexpectedDisplay(value));
344                }
345                ret
346            }
347            Some(op) => Err(Error::unexpected_write_display(op.into())),
348            _ => Err(Error::ExtraWrite(OperationType::WriteDisplay)),
349        }
350    }
351}
352
353impl Drop for Mock {
354    fn drop(&mut self) {
355        // No need to panic again
356        if thread::panicking() {
357            return;
358        }
359        if let Some(op) = self.ops.front() {
360            panic!("reader dropped with {op:?} operation still unread")
361        }
362    }
363}
364
365#[cfg(test)]
366mod proptest {
367    use std::io;
368
369    use proptest::{
370        prelude::{Arbitrary, BoxedStrategy, Just, Strategy, any},
371        prop_oneof,
372    };
373
374    use super::{Error, Operation, OperationType};
375
376    pub fn arb_write_number_operation() -> impl Strategy<Value = Operation> {
377        (
378            any::<u64>(),
379            prop_oneof![
380                Just(Ok(())),
381                any::<u64>().prop_map(|v| Err(Error::UnexpectedNumber(v))),
382                Just(Err(Error::WrongWrite(
383                    OperationType::WriteSlice,
384                    OperationType::WriteNumber
385                ))),
386                Just(Err(Error::WrongWrite(
387                    OperationType::WriteDisplay,
388                    OperationType::WriteNumber
389                ))),
390                any::<String>().prop_map(|s| Err(Error::Custom(s))),
391                (any::<io::ErrorKind>(), any::<String>())
392                    .prop_map(|(kind, msg)| Err(Error::IO(kind, msg))),
393            ],
394        )
395            .prop_filter("same number", |(v, res)| match res {
396                Err(Error::UnexpectedNumber(exp_v)) => v != exp_v,
397                _ => true,
398            })
399            .prop_map(|(v, res)| Operation::WriteNumber(v, res))
400    }
401
402    pub fn arb_write_slice_operation() -> impl Strategy<Value = Operation> {
403        (
404            any::<Vec<u8>>(),
405            prop_oneof![
406                Just(Ok(())),
407                any::<Vec<u8>>().prop_map(|v| Err(Error::UnexpectedSlice(v))),
408                Just(Err(Error::WrongWrite(
409                    OperationType::WriteNumber,
410                    OperationType::WriteSlice
411                ))),
412                Just(Err(Error::WrongWrite(
413                    OperationType::WriteDisplay,
414                    OperationType::WriteSlice
415                ))),
416                any::<String>().prop_map(|s| Err(Error::Custom(s))),
417                (any::<io::ErrorKind>(), any::<String>())
418                    .prop_map(|(kind, msg)| Err(Error::IO(kind, msg))),
419            ],
420        )
421            .prop_filter("same slice", |(v, res)| match res {
422                Err(Error::UnexpectedSlice(exp_v)) => v != exp_v,
423                _ => true,
424            })
425            .prop_map(|(v, res)| Operation::WriteSlice(v, res))
426    }
427
428    #[allow(dead_code)]
429    pub fn arb_extra_write() -> impl Strategy<Value = Operation> {
430        prop_oneof![
431            any::<u64>().prop_map(|msg| {
432                Operation::WriteNumber(msg, Err(Error::ExtraWrite(OperationType::WriteNumber)))
433            }),
434            any::<Vec<u8>>().prop_map(|msg| {
435                Operation::WriteSlice(msg, Err(Error::ExtraWrite(OperationType::WriteSlice)))
436            }),
437            any::<String>().prop_map(|msg| {
438                Operation::WriteDisplay(msg, Err(Error::ExtraWrite(OperationType::WriteDisplay)))
439            }),
440        ]
441    }
442
443    pub fn arb_write_display_operation() -> impl Strategy<Value = Operation> {
444        (
445            any::<String>(),
446            prop_oneof![
447                Just(Ok(())),
448                any::<String>().prop_map(|v| Err(Error::UnexpectedDisplay(v))),
449                Just(Err(Error::WrongWrite(
450                    OperationType::WriteNumber,
451                    OperationType::WriteDisplay
452                ))),
453                Just(Err(Error::WrongWrite(
454                    OperationType::WriteSlice,
455                    OperationType::WriteDisplay
456                ))),
457                any::<String>().prop_map(|s| Err(Error::Custom(s))),
458                (any::<io::ErrorKind>(), any::<String>())
459                    .prop_map(|(kind, msg)| Err(Error::IO(kind, msg))),
460            ],
461        )
462            .prop_filter("same string", |(v, res)| match res {
463                Err(Error::UnexpectedDisplay(exp_v)) => v != exp_v,
464                _ => true,
465            })
466            .prop_map(|(v, res)| Operation::WriteDisplay(v, res))
467    }
468
469    pub fn arb_operation() -> impl Strategy<Value = Operation> {
470        prop_oneof![
471            arb_write_number_operation(),
472            arb_write_slice_operation(),
473            arb_write_display_operation(),
474        ]
475    }
476
477    impl Arbitrary for Operation {
478        type Parameters = ();
479        type Strategy = BoxedStrategy<Operation>;
480
481        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
482            arb_operation().boxed()
483        }
484    }
485}
486
487#[cfg(test)]
488mod test {
489    use hex_literal::hex;
490    use proptest::prelude::TestCaseError;
491    use proptest::prelude::any;
492    use proptest::proptest;
493
494    use crate::wire::ser::Error as _;
495    use crate::wire::ser::NixWrite;
496    use crate::wire::ser::mock::Operation;
497    use crate::wire::ser::mock::OperationType;
498    use crate::wire::ser::mock::proptest::arb_extra_write;
499
500    use super::{Builder, Error};
501
502    #[tokio::test]
503    async fn write_number() {
504        let mut mock = Builder::new().write_number(10).build();
505        mock.write_number(10).await.unwrap();
506    }
507
508    #[tokio::test]
509    async fn write_number_error() {
510        let mut mock = Builder::new()
511            .write_number_error(10, Error::custom("bad number"))
512            .build();
513        assert_eq!(
514            Err(Error::custom("bad number")),
515            mock.write_number(10).await
516        );
517    }
518
519    #[tokio::test]
520    async fn write_number_unexpected() {
521        let mut mock = Builder::new().write_slice(b"").build();
522        assert_eq!(
523            Err(Error::unexpected_write_number(OperationType::WriteSlice)),
524            mock.write_number(11).await
525        );
526    }
527
528    #[tokio::test]
529    async fn write_number_unexpected_number() {
530        let mut mock = Builder::new().write_number(10).build();
531        assert_eq!(
532            Err(Error::UnexpectedNumber(11)),
533            mock.write_number(11).await
534        );
535    }
536
537    #[tokio::test]
538    async fn extra_write_number() {
539        let mut mock = Builder::new().build();
540        assert_eq!(
541            Err(Error::ExtraWrite(OperationType::WriteNumber)),
542            mock.write_number(11).await
543        );
544    }
545
546    #[tokio::test]
547    async fn write_slice() {
548        let mut mock = Builder::new()
549            .write_slice(&[])
550            .write_slice(&hex!("0000 1234 5678 9ABC DEFF"))
551            .build();
552        mock.write_slice(&[]).await.expect("write_slice empty");
553        mock.write_slice(&hex!("0000 1234 5678 9ABC DEFF"))
554            .await
555            .expect("write_slice");
556    }
557
558    #[tokio::test]
559    async fn write_slice_error() {
560        let mut mock = Builder::new()
561            .write_slice_error(&[], Error::custom("bad slice"))
562            .build();
563        assert_eq!(Err(Error::custom("bad slice")), mock.write_slice(&[]).await);
564    }
565
566    #[tokio::test]
567    async fn write_slice_unexpected() {
568        let mut mock = Builder::new().write_number(10).build();
569        assert_eq!(
570            Err(Error::unexpected_write_slice(OperationType::WriteNumber)),
571            mock.write_slice(b"").await
572        );
573    }
574
575    #[tokio::test]
576    async fn write_slice_unexpected_slice() {
577        let mut mock = Builder::new().write_slice(b"").build();
578        assert_eq!(
579            Err(Error::UnexpectedSlice(b"bad slice".to_vec())),
580            mock.write_slice(b"bad slice").await
581        );
582    }
583
584    #[tokio::test]
585    async fn extra_write_slice() {
586        let mut mock = Builder::new().build();
587        assert_eq!(
588            Err(Error::ExtraWrite(OperationType::WriteSlice)),
589            mock.write_slice(b"extra slice").await
590        );
591    }
592
593    #[tokio::test]
594    async fn write_display() {
595        let mut mock = Builder::new().write_display("testing").build();
596        mock.write_display("testing").await.unwrap();
597    }
598
599    #[tokio::test]
600    async fn write_display_error() {
601        let mut mock = Builder::new()
602            .write_display_error("testing", Error::custom("bad number"))
603            .build();
604        assert_eq!(
605            Err(Error::custom("bad number")),
606            mock.write_display("testing").await
607        );
608    }
609
610    #[tokio::test]
611    async fn write_display_unexpected() {
612        let mut mock = Builder::new().write_number(10).build();
613        assert_eq!(
614            Err(Error::unexpected_write_display(OperationType::WriteNumber)),
615            mock.write_display("").await
616        );
617    }
618
619    #[tokio::test]
620    async fn write_display_unexpected_display() {
621        let mut mock = Builder::new().write_display("").build();
622        assert_eq!(
623            Err(Error::UnexpectedDisplay("bad display".to_string())),
624            mock.write_display("bad display").await
625        );
626    }
627
628    #[tokio::test]
629    async fn extra_write_display() {
630        let mut mock = Builder::new().build();
631        assert_eq!(
632            Err(Error::ExtraWrite(OperationType::WriteDisplay)),
633            mock.write_display("extra slice").await
634        );
635    }
636
637    #[test]
638    #[should_panic]
639    fn operations_left() {
640        let _ = Builder::new().write_number(10).build();
641    }
642
643    #[test]
644    fn proptest_mock() {
645        let rt = tokio::runtime::Builder::new_current_thread()
646            .enable_all()
647            .build()
648            .unwrap();
649        proptest!(|(
650            operations in any::<Vec<Operation>>(),
651            extra_operations in proptest::collection::vec(arb_extra_write(), 0..3)
652            )| {
653            rt.block_on(async {
654                let mut builder = Builder::new();
655                for op in operations.iter() {
656                    builder.write_operation(op);
657                }
658                for op in extra_operations.iter() {
659                    builder.write_operation(op);
660                }
661                let mut mock = builder.build();
662                for op in operations {
663                    mock.prop_assert_operation(op).await?;
664                }
665                for op in extra_operations {
666                    mock.prop_assert_operation(op).await?;
667                }
668                Ok(()) as Result<(), TestCaseError>
669            })?;
670        });
671    }
672}