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 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}