]> git.lizzy.rs Git - mt_net.git/blob - tests/random.rs
Exclude ToSrvPkt::Disco from tests
[mt_net.git] / tests / random.rs
1 use libtest_mimic::{Arguments, Trial};
2
3 use mt_net::{generate_random::GenerateRandomVariant, rand, ToCltPkt, ToSrvPkt};
4 use mt_ser::{DefCfg, MtDeserialize, MtSerialize};
5 use std::{
6     error::Error,
7     fmt::Debug,
8     io::{Cursor, Write},
9     path::Path,
10     process::{Command, Stdio},
11 };
12
13 fn test_reserialize<'a, T>(type_name: &'static str, reserialize: &Path) -> Vec<Trial>
14 where
15     T: MtSerialize + MtDeserialize + GenerateRandomVariant + PartialEq + Debug,
16 {
17     (0..T::num_variants())
18         .flat_map(move |i| {
19             let pkt_name = format!("{type_name}::{}", T::variant_name(i));
20             let reserialize = reserialize.as_os_str().to_os_string();
21
22             if pkt_name == "ToSrvPkt::Disco" {
23                 return None;
24             }
25
26             Some(
27                 Trial::test(pkt_name.clone(), move || {
28                     let mut rng = rand::thread_rng();
29                     let mut printed_stderr = false;
30
31                     for _ in 0..100 {
32                         // use buffered IO instead of directly reading from the process
33                         // this enables printing out payloads for debugging
34
35                         let input = T::generate_random_variant(&mut rng, i);
36
37                         let mut input_payload = Vec::new();
38                         input
39                             .mt_serialize::<DefCfg>(&mut input_payload)
40                             .map_err(|e| format!("serialize error: {e}\ninput: {input:?}"))?;
41
42                         let mut child = Command::new(&reserialize)
43                             .arg(type_name)
44                             .stdin(Stdio::piped())
45                             .stdout(Stdio::piped())
46                             .stderr(Stdio::piped())
47                             .spawn()
48                             .expect("failed to spawn reserialize");
49
50                         let mut stdin = child.stdin.take().unwrap();
51                         let stdin_payload = input_payload.clone();
52                         std::thread::spawn(move || {
53                             stdin.write_all(&stdin_payload).unwrap();
54                         });
55
56                         let command_out = child.wait_with_output().unwrap();
57
58                         let stderr = String::from_utf8_lossy(&command_out.stderr);
59                         if command_out.status.success() {
60                             if stderr.len() > 0 && !printed_stderr {
61                                 printed_stderr = true;
62                                 eprintln!("stderr for {pkt_name}: {stderr}");
63                             }
64                         } else {
65                             return Err(format!(
66                                 "reserialize returned failure\n\
67                                                                 input: {input:?}\n\
68                                                                 input payload: {input_payload:?}\n\
69                                                                 stderr: {stderr}"
70                             )
71                             .into());
72                         }
73
74                         let mut reader = Cursor::new(command_out.stdout);
75                         let output = T::mt_deserialize::<DefCfg>(&mut reader).map_err(|e| {
76                             format!(
77                                 "deserialize error: {e}\n\
78                                                                 input: {input:?}\n\
79                                                                 input payload: {input_payload:?}\n\
80                                                                 output payload: {:?}\n\
81                                                                 stderr: {stderr}",
82                                 reader.get_ref()
83                             )
84                         })?;
85
86                         if input != output {
87                             return Err(format!(
88                                 "output does not match input\n\
89                                                                 input: {input:?}\n\
90                                                                 output: {output:?}\n\
91                                                                 input payload: {input_payload:?}\n\
92                                                                 output payload: {:?}\n\
93                                                                 stderr: {stderr}",
94                                 reader.get_ref(),
95                             )
96                             .into());
97                         }
98                     }
99
100                     Ok(())
101                 })
102                 .with_kind("random"),
103             )
104         })
105         .collect()
106 }
107
108 fn main() -> Result<(), Box<dyn Error>> {
109     let reserialize = Path::new(file!()).with_file_name("reserialize/reserialize");
110
111     if !reserialize.exists() {
112         if !Command::new("go")
113             .arg("build")
114             .current_dir(reserialize.parent().unwrap())
115             .spawn()
116             .expect("go is required for random tests")
117             .wait()
118             .expect("go build didn't run")
119             .success()
120         {
121             panic!("go build failed");
122         }
123     }
124
125     let args = Arguments::from_args();
126
127     let mut tests = Vec::new();
128     tests.extend(test_reserialize::<ToSrvPkt>("ToSrvPkt", &reserialize));
129     tests.extend(test_reserialize::<ToCltPkt>("ToCltPkt", &reserialize));
130
131     libtest_mimic::run(&args, tests).exit();
132 }