ezcp/tests/
generic_tests.rs1#[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 drop(self.task_path);
27 }
28 }
29
30 #[test]
31 fn create_empty() {
32 let mut task = Test::new();
33
34 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 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 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 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 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 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 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 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 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 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 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 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 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 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 task.task.add_subtask(subtask1);
295 task.task.add_subtask(subtask2);
296 task.task.add_subtask(subtask3);
297
298 task.test()
299 }
300
301}