eval_stack/
case.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::{fs::create_dir_all, path::PathBuf, time::Duration};

use anyhow::Result;
use which::which;

use crate::{
    compile::{compile, Language},
    config::{JudgeOptions, TestCase},
    exec::execute,
    judge::{JudgeResult, JudgeStatus},
};

pub async fn run_test_cases<B, C>(
    language: Language,
    workspace: B,
    source_file_path: B,
    options: JudgeOptions,
    test_cases: Vec<(C, C)>,
    clean: bool,
) -> Result<Vec<JudgeResult>>
where
    B: Into<PathBuf>,
    C: Into<PathBuf>,
{
    let workspace: PathBuf = workspace.into();

    if !workspace.exists() {
        create_dir_all(&workspace)?;
    }

    let source_file_path = Into::<PathBuf>::into(source_file_path)
        .to_string_lossy()
        .to_string();
    let exec_path = match &language {
        Language::Python => which("python")?,
        Language::NodeJs => which("deno")?,
        Language::Java => which("java")?,
        _ => workspace.join("out"),
    };

    if let Err(e) = compile(
        language,
        workspace.clone(),
        &source_file_path,
        exec_path.to_string_lossy(),
    )
    .await
    {
        return Ok(vec![JudgeResult {
            status: JudgeStatus::CompileError {
                message: e.to_string(),
            },
            time_used: Duration::default(),
            memory_used: 0,
        }]);
    };

    let py_args = vec![source_file_path.as_str()];
    let deno_args = vec![
        "run",
        format!("--v8-flags=--max-old-space-size={}", options.memory_limit).leak(),
        "--deny-read=*",
        "--deny-write=*",
        "--deny-env=*",
        "--deny-run=*",
        "--deny-ffi=*",
        source_file_path.as_str(),
    ];
    let java_args = vec!["Main"];
    let args = match language {
        Language::Python => Some(&py_args),
        Language::NodeJs => Some(&deno_args),
        Language::Java => Some(&java_args),
        _ => None,
    }
    .map(|v| &**v);

    let mut results = vec![];
    for (input_file, expected_output_file) in test_cases {
        let result = execute(
            &workspace,
            exec_path.to_string_lossy(),
            args,
            &options,
            TestCase {
                input_file: input_file.into(),
                expected_output_file: expected_output_file.into(),
            },
            workspace.join("test.out"),
        )
        .await?;
        if options.fail_fast && !matches!(result.status, JudgeStatus::Accepted) {
            results.push(result);
            break;
        }
        results.push(result);
    }

    if clean {
        if let Err(e) = std::fs::remove_dir_all(workspace) {
            anyhow::bail!("Failed to remove workspace: {}", e);
        }
    }
    Ok(results)
}