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) .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}