eval_stack/
case.rs

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}