ezcp/tests/
generic_tests.rs

1#[cfg(test)]
2#[allow(clippy::unwrap_used)]
3pub mod generic_tests {
4    use crate::{Error, Subtask, Task};
5    use log::LevelFilter;
6    use tempfile::TempDir;
7
8    pub struct Test {
9        pub task: Task,
10        task_path: TempDir,
11    }
12
13    impl Test {
14        pub fn new() -> Self {
15            let task_path = TempDir::new().expect("Failed to create temporary directory");
16            let mut task = Task::new("Test task", task_path.path());
17            task.debug_level = LevelFilter::Trace;
18            Test { task, task_path }
19        }
20
21        pub fn test(mut self) {
22            for _ in 0..10 {
23                self.task.create_tests().unwrap();
24            }
25            // Clean up the temporary directory
26            drop(self.task_path);
27        }
28    }
29
30    #[test]
31    fn create_empty() {
32        let mut task = Test::new();
33
34        // create solution file
35        let solution_contents = "int main() { return 0; }";
36        task.task.solution_source = solution_contents.to_owned();
37
38        task.test()
39    }
40
41    #[test]
42    fn create_with_subtasks() {
43        let mut task = Test::new();
44
45        // create solution file
46        let solution_contents = r#"
47        #include <iostream>
48        using namespace std;
49        
50        int main() {
51            cout<<"1\n";
52            return 0; 
53        }
54        
55        "#;
56
57        task.task.solution_source = solution_contents.to_owned();
58
59        let subtask1 = Subtask::new();
60        let subtask2 = Subtask::new();
61        let subtask3 = Subtask::new();
62
63        // create subtasks
64        task.task.add_subtask(subtask1);
65        task.task.add_subtask(subtask2);
66        task.task.add_subtask(subtask3);
67
68        task.test()
69    }
70
71    #[test]
72    fn create_with_tests() {
73        let mut task = Test::new();
74
75        // create solution file
76        let solution_contents = r#"
77        #include <iostream>
78        using namespace std;
79        
80        int main() {
81            cout<<"1\n";
82            return 0; 
83        }
84        
85        "#;
86
87        task.task.solution_source = solution_contents.to_owned();
88
89        let mut subtask1 = Subtask::new();
90        subtask1.add_test_str("1\n");
91        subtask1.add_test_str("2\n");
92        subtask1.add_test_str("3\n");
93        let mut subtask2 = Subtask::new();
94        subtask2.add_test_str("1\n");
95        subtask2.add_test_str("2\n");
96        subtask2.add_test_str("3\n");
97        let mut subtask3 = Subtask::new();
98        subtask3.add_test_str("1\n");
99        subtask3.add_test_str("2\n");
100
101        // create subtasks
102        task.task.add_subtask(subtask1);
103        task.task.add_subtask(subtask2);
104        task.task.add_subtask(subtask3);
105
106        task.test()
107    }
108
109    #[test]
110    fn test_fails_without_solution() {
111        let mut task = Test::new();
112
113        for _ in 0..10 {
114            assert!(matches!(task.task.create_tests(), Err(Error::MissingSolution { .. })));
115            assert!(matches!(task.task.create_tests(), Err(Error::MissingSolution { .. })));
116        }
117    }
118
119    #[test]
120    fn test_times_out() {
121        let mut task = Test::new();
122        task.task.time_limit = 0.1;
123
124        // create solution file
125        let solution_contents = r#"
126        #include<iostream>
127        using namespace std;
128
129        int fib(int a){
130                if(a<=2)
131                        return 1;
132                return fib(a-1)+fib(a-2);
133        }
134
135        int main() {
136            cout<<fib(100)<<"\n";
137            return 0;
138        }
139        "#;
140
141        task.task.solution_source = solution_contents.to_owned();
142
143        let mut subtask1 = Subtask::new();
144        subtask1.add_test_str("1\n");
145
146        // create subtasks
147        task.task.add_subtask(subtask1);
148
149        for _ in 0..3 {
150            assert!(matches!(task.task.create_tests(), Err(Error::SolutionTimedOut { .. })));
151        }
152    }
153
154    #[test]
155    fn test_compile_error() {
156        let mut task = Test::new();
157
158        // create solution file
159        let solution_contents = "
160        int main() {
161            this is a compile error
162            return 0;
163        }
164        ";
165
166        task.task.solution_source = solution_contents.to_owned();
167
168        let mut subtask1 = Subtask::new();
169        subtask1.add_test_str("1\n");
170
171        // create subtasks
172        task.task.add_subtask(subtask1);
173
174        for _ in 0..10 {
175            assert!(matches!(task.task.create_tests(), Err(Error::CompilerError { .. })));
176        }
177    }
178
179    #[test]
180    fn create_with_custom_names() {
181        let mut task = Test::new();
182
183        task.task.get_input_file_name = Box::new(|test_id: i32, subtask_id: i32, test_id_in_subtask: i32| format!("in_{subtask_id}_{test_id_in_subtask}_{test_id}.txt"));
184        task.task.get_output_file_name = Box::new(|test_id: i32, subtask_id: i32, test_id_in_subtask: i32| format!("out_{subtask_id}_{test_id_in_subtask}_{test_id}.txt"));
185
186        // create solution file
187        let solution_contents = r#"
188        #include <iostream>
189        using namespace std;
190        
191        int main() {
192            cout<<"1\n";
193            return 0; 
194        }
195        "#;
196
197        task.task.solution_source = solution_contents.to_owned();
198
199        let mut subtask1 = Subtask::new();
200        subtask1.add_test_str("1\n");
201        subtask1.add_test_str("2\n");
202        subtask1.add_test_str("3\n");
203        let mut subtask2 = Subtask::new();
204        subtask2.add_test_str("1\n");
205        subtask2.add_test_str("2\n");
206        subtask2.add_test_str("3\n");
207        let mut subtask3 = Subtask::new();
208        subtask3.add_test_str("1\n");
209        subtask3.add_test_str("2\n");
210
211        // create subtasks
212        task.task.add_subtask(subtask1);
213        task.task.add_subtask(subtask2);
214        task.task.add_subtask(subtask3);
215
216        task.test()
217    }
218
219    #[test]
220    fn create_with_custom_names2() {
221        let mut task = Test::new();
222
223        task.task.get_input_file_name = Box::new(|_test_id: i32, subtask_id: i32, test_id_in_subtask: i32| format!("in_{subtask_id}_{test_id_in_subtask}.txt"));
224        task.task.get_output_file_name = Box::new(|_test_id: i32, subtask_id: i32, test_id_in_subtask: i32| format!("out_{subtask_id}_{test_id_in_subtask}.txt"));
225
226        // create solution file
227        let solution_contents = r#"
228        #include <iostream>
229        using namespace std;
230        
231        int main() {
232            cout<<"1\n";
233            return 0; 
234        }
235        
236        "#;
237
238        task.task.solution_source = solution_contents.to_owned();
239
240        let mut subtask1 = Subtask::new();
241        subtask1.add_test_str("1\n");
242        subtask1.add_test_str("2\n");
243        subtask1.add_test_str("3\n");
244        let mut subtask2 = Subtask::new();
245        subtask2.add_test_str("1\n");
246        subtask2.add_test_str("2\n");
247        subtask2.add_test_str("3\n");
248        let mut subtask3 = Subtask::new();
249        subtask3.add_test_str("1\n");
250        subtask3.add_test_str("2\n");
251
252        // create subtasks
253        task.task.add_subtask(subtask1);
254        task.task.add_subtask(subtask2);
255        task.task.add_subtask(subtask3);
256
257        task.test()
258    }
259
260    #[test]
261    fn create_with_custom_names3() {
262        let mut task = Test::new();
263
264        task.task.get_input_file_name = Box::new(|test_id: i32, _subtask_id: i32, _test_id_in_subtask: i32| format!("in_{test_id}.txt"));
265        task.task.get_output_file_name = Box::new(|test_id: i32, _subtask_id: i32, _test_id_in_subtask: i32| format!("out_{test_id}.txt"));
266
267        // create solution file
268        let solution_contents = r#"
269        #include <iostream>
270        using namespace std;
271        
272        int main() {
273            cout<<"1\n";
274            return 0; 
275        }
276        
277        "#;
278
279        task.task.solution_source = solution_contents.to_owned();
280
281        let mut subtask1 = Subtask::new();
282        subtask1.add_test_str("1\n");
283        subtask1.add_test_str("2\n");
284        subtask1.add_test_str("3\n");
285        let mut subtask2 = Subtask::new();
286        subtask2.add_test_str("1\n");
287        subtask2.add_test_str("2\n");
288        subtask2.add_test_str("3\n");
289        let mut subtask3 = Subtask::new();
290        subtask3.add_test_str("1\n");
291        subtask3.add_test_str("2\n");
292
293        // create subtasks
294        task.task.add_subtask(subtask1);
295        task.task.add_subtask(subtask2);
296        task.task.add_subtask(subtask3);
297
298        task.test()
299    }
300
301}