1use std::{path::PathBuf, time::Duration};
2
3use anyhow::Result;
4use tokio::fs::{create_dir_all, remove_dir_all};
5use which::which;
6
7use crate::{
8 compile::{compile, Language},
9 config::{JudgeOptions, TestCase},
10 exec::execute,
11 judge::{JudgeResult, JudgeStatus},
12};
13
14pub async fn run_test_cases<B, C>(
15 language: Language,
16 workspace: B,
17 source_file_path: B,
18 options: JudgeOptions,
19 test_cases: Vec<(C, C)>,
20 clean: bool,
21) -> Result<Vec<JudgeResult>>
22where
23 B: Into<PathBuf>,
24 C: Into<PathBuf>,
25{
26 let workspace: PathBuf = workspace.into();
27
28 if !workspace.exists() {
29 create_dir_all(&workspace).await?;
30 }
31
32 let source_file_path = Into::<PathBuf>::into(source_file_path)
33 .to_string_lossy()
34 .to_string();
35 let exec_path = match &language {
36 Language::Python => which("python")?,
37 Language::NodeJs => which("deno")?,
38 Language::Java => which("java")?,
39 _ => workspace.join("out"),
40 };
41
42 if let Err(e) = compile(
43 language,
44 workspace.clone(),
45 &source_file_path,
46 exec_path.to_string_lossy(),
47 )
48 .await
49 {
50 if clean {
51 if let Err(e) = remove_dir_all(workspace).await {
52 anyhow::bail!("Failed to remove workspace: {}", e);
53 }
54 }
55 return Ok(vec![JudgeResult {
56 status: JudgeStatus::CompileError {
57 message: e.to_string(),
58 },
59 time_used: Duration::default(),
60 memory_used: 0,
61 }]);
62 };
63
64 let py_args = vec![source_file_path.as_str()];
65 let deno_args = vec![
66 "run",
67 format!("--v8-flags=--max-old-space-size={}", options.memory_limit).leak(),
68 "--deny-read=*",
69 "--deny-write=*",
70 "--deny-env=*",
71 "--deny-run=*",
72 "--deny-ffi=*",
73 source_file_path.as_str(),
74 ];
75 let java_args = vec!["Main"];
76 let args = match language {
77 Language::Python => Some(&py_args),
78 Language::NodeJs => Some(&deno_args),
79 Language::Java => Some(&java_args),
80 _ => None,
81 }
82 .map(|v| &**v);
83
84 let mut results = vec![];
85 for (input_file, expected_output_file) in test_cases {
86 let result = execute(
87 &workspace,
88 exec_path.to_string_lossy(),
89 args,
90 &options,
91 TestCase {
92 input_file: input_file.into(),
93 expected_output_file: expected_output_file.into(),
94 },
95 workspace.join("test.out"),
96 )
97 .await?;
98 if options.fail_fast && !matches!(result.status, JudgeStatus::Accepted) {
99 results.push(result);
100 break;
101 }
102 results.push(result);
103 }
104
105 if clean {
106 if let Err(e) = remove_dir_all(workspace).await {
107 anyhow::bail!("Failed to remove workspace: {}", e);
108 }
109 }
110 Ok(results)
111}