judge_core/judge/
common.rs

1use crate::judge::result::{
2    check_checker_result, check_user_result, get_max_mem, get_run_time, JudgeResultInfo,
3};
4use crate::run::sandbox::SCRIPT_LIMIT_CONFIG;
5use crate::utils::{compare_files, get_pathbuf_str};
6use crate::{error::JudgeCoreError, run::sandbox::Sandbox};
7
8use super::result::JudgeVerdict;
9use super::JudgeConfig;
10
11use std::fs::File;
12use std::os::unix::io::{AsRawFd, RawFd};
13use std::path::PathBuf;
14use std::time::Duration;
15
16fn run_user(
17    config: &JudgeConfig,
18) -> Result<(Option<JudgeVerdict>, Duration, i64, i32), JudgeCoreError> {
19    let input_file = File::open(&config.test_data.input_file_path)?;
20
21    if !config.program.output_file_path.exists() {
22        File::create(&config.program.output_file_path)?;
23    }
24    let program_output_file = File::options()
25        .write(true)
26        .truncate(true) // Overwrite the whole content of this file
27        .open(&config.program.output_file_path)?;
28
29    let input_raw_fd: RawFd = input_file.as_raw_fd();
30    let program_output_raw_fd: RawFd = program_output_file.as_raw_fd();
31
32    let user_executor = config.program.executor.clone();
33    let mut user_sandbox = Sandbox::new(
34        user_executor,
35        config.runtime.rlimit_configs.clone(),
36        Some(input_raw_fd),
37        Some(program_output_raw_fd),
38        true,
39    )?;
40
41    log::debug!("Spawning user process");
42    let _user_spawn = user_sandbox.spawn()?;
43    log::debug!("Waiting for user process");
44    let user_result = user_sandbox.wait()?;
45    let user_time = get_run_time(&user_result);
46    let max_mem = get_max_mem(&user_result);
47    Ok((
48        check_user_result(&user_result),
49        user_time,
50        max_mem,
51        user_result.exit_status,
52    ))
53}
54
55pub fn run_checker(config: &JudgeConfig) -> Result<(JudgeVerdict, i32), JudgeCoreError> {
56    if let Some(mut checker_executor) = config.checker.executor.clone() {
57        let first_args = String::from("");
58        let checker_args = vec![
59            first_args,
60            get_pathbuf_str(&config.test_data.input_file_path)?,
61            get_pathbuf_str(&config.program.output_file_path)?,
62            get_pathbuf_str(&config.test_data.answer_file_path)?,
63            get_pathbuf_str(&config.checker.output_file_path)?,
64        ];
65        checker_executor.set_additional_args(checker_args);
66
67        let mut checker_process =
68            Sandbox::new(checker_executor, SCRIPT_LIMIT_CONFIG, None, None, false)?;
69
70        log::debug!("Spawning checker process");
71        let _checker_spawn = checker_process.spawn()?;
72        log::debug!("Waiting for checker process");
73        let checker_result = checker_process.wait()?;
74        Ok((
75            check_checker_result(&checker_result),
76            checker_result.exit_status,
77        ))
78    } else {
79        Err(JudgeCoreError::AnyhowError(anyhow::anyhow!(
80            "Checker executor is not set"
81        )))
82    }
83}
84
85pub fn run_judge(config: &JudgeConfig) -> Result<Option<JudgeResultInfo>, JudgeCoreError> {
86    let (user_verdict, user_time, max_mem, user_exit_status) = run_user(config)?;
87    if let Some(verdict) = user_verdict {
88        return Ok(Some(JudgeResultInfo {
89            verdict,
90            time_usage: user_time,
91            memory_usage_bytes: max_mem,
92            exit_status: user_exit_status,
93            checker_exit_status: 0,
94        }));
95    }
96
97    log::debug!("Creating sandbox for checker process");
98    if let Some(_checker_executor) = config.checker.executor.clone() {
99        let (verdict, checker_exit_status) = run_checker(config)?;
100        Ok(Some(JudgeResultInfo {
101            verdict,
102            time_usage: user_time,
103            memory_usage_bytes: max_mem,
104            exit_status: user_exit_status,
105            checker_exit_status,
106        }))
107    } else if compare_files(
108        &PathBuf::from(&config.program.output_file_path),
109        &PathBuf::from(&config.test_data.answer_file_path),
110    ) {
111        Ok(Some(JudgeResultInfo {
112            verdict: JudgeVerdict::Accepted,
113            time_usage: user_time,
114            memory_usage_bytes: max_mem,
115            exit_status: user_exit_status,
116            checker_exit_status: 0,
117        }))
118    } else {
119        Ok(Some(JudgeResultInfo {
120            verdict: JudgeVerdict::WrongAnswer,
121            time_usage: user_time,
122            memory_usage_bytes: max_mem,
123            exit_status: user_exit_status,
124            checker_exit_status: 0,
125        }))
126    }
127}
128
129#[cfg(test)]
130pub mod common_judge_tests {
131    use std::path::PathBuf;
132
133    use crate::{
134        compiler::Language,
135        judge::{
136            result::JudgeVerdict, CheckerConfig, JudgeConfig, ProgramConfig, RuntimeConfig,
137            TestDataConfig,
138        },
139        run::{executor::Executor, sandbox::RlimitConfigs},
140    };
141
142    use super::run_judge;
143
144    const TEST_CONFIG: RlimitConfigs = RlimitConfigs {
145        stack_limit: Some((64 * 1024 * 1024, 64 * 1024 * 1024)),
146        as_limit: Some((64 * 1024 * 1024, 64 * 1024 * 1024)),
147        cpu_limit: Some((1, 2)),
148        nproc_limit: Some((1, 1)),
149        fsize_limit: Some((1024, 1024)),
150    };
151
152    fn init() {
153        let _ = env_logger::builder()
154            .filter_level(log::LevelFilter::Debug)
155            .try_init();
156    }
157
158    fn build_test_config(program_executor: Executor) -> JudgeConfig {
159        JudgeConfig {
160            runtime: RuntimeConfig {
161                rlimit_configs: TEST_CONFIG,
162            },
163            test_data: TestDataConfig {
164                input_file_path: PathBuf::from("../tmp/in"),
165                answer_file_path: PathBuf::from("../tmp/ans"),
166            },
167            checker: CheckerConfig {
168                executor: None,
169                output_file_path: PathBuf::from("../tmp/check"),
170            },
171            program: ProgramConfig {
172                executor: program_executor,
173                output_file_path: PathBuf::from("../tmp/out"),
174            },
175        }
176    }
177
178    #[test]
179    fn test_run_judge() {
180        init();
181        let program_path = PathBuf::from("./../test-collection/dist/programs/read_and_write");
182        let program_executor = Executor::new(Language::Cpp, program_path).unwrap();
183
184        let runner_config = build_test_config(program_executor);
185        let result = run_judge(&runner_config);
186        if let Ok(Some(result)) = result {
187            log::debug!("{:?}", result);
188            assert_eq!(result.verdict, JudgeVerdict::Accepted);
189        } else {
190            log::debug!("{:?}", result);
191            assert!(false)
192        }
193    }
194
195    #[test]
196    fn test_run_tle() {
197        init();
198        let program_path = PathBuf::from("./../test-collection/dist/programs/infinite_loop");
199        let program_executor = Executor::new(Language::Cpp, program_path).unwrap();
200
201        let runner_config = build_test_config(program_executor);
202        let result = run_judge(&runner_config);
203        assert!(result.is_ok());
204        if let Ok(Some(result)) = result {
205            log::debug!("{:?}", result);
206            assert_eq!(result.verdict, JudgeVerdict::TimeLimitExceeded);
207        }
208    }
209
210    #[test]
211    fn test_run_mle() {
212        init();
213        let program_path = PathBuf::from("./../test-collection/dist/programs/memory_limit");
214        let program_executor = Executor::new(Language::Cpp, program_path).unwrap();
215
216        let runner_config = build_test_config(program_executor);
217        let result = run_judge(&runner_config);
218        assert!(result.is_ok());
219        if let Ok(Some(result)) = result {
220            log::debug!("{:?}", result);
221            assert_eq!(result.verdict, JudgeVerdict::RuntimeError);
222        }
223    }
224}