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