完成基本的答案对比功能

This commit is contained in:
MeiK 2020-05-20 19:02:17 +08:00
parent ae1dca202a
commit 353236a154
4 changed files with 144 additions and 29 deletions

View File

@ -28,6 +28,8 @@ pub enum Error {
WaitError(Option<i32>), WaitError(Option<i32>),
SystemTimeError(time::SystemTimeError), SystemTimeError(time::SystemTimeError),
RemoveFileError(String, Option<i32>), RemoveFileError(String, Option<i32>),
OpenFileError(String, Option<i32>),
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -56,6 +58,10 @@ impl fmt::Display for Error {
let reason = errno_str(errno); let reason = errno_str(errno);
write!(f, "ReadFileError: `{}` {}", filename, reason) write!(f, "ReadFileError: `{}` {}", filename, reason)
} }
Error::OpenFileError(ref filename, errno) => {
let reason = errno_str(errno);
write!(f, "OpenFileError: `{}` {}", filename, reason)
}
Error::UnknownJudgeType(ref judge_type) => { Error::UnknownJudgeType(ref judge_type) => {
write!(f, "UnknownJudgeType: {}", judge_type) write!(f, "UnknownJudgeType: {}", judge_type)
} }

View File

@ -32,6 +32,7 @@ fn main() {
}; };
println!("{}", judge_config.code.language); println!("{}", judge_config.code.language);
// TODO: 处理 SE处理交互问题
match process::run(&judge_config) { match process::run(&judge_config) {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {

View File

@ -2,6 +2,8 @@ extern crate libc;
use super::error::{errno_str, Error, Result}; use super::error::{errno_str, Error, Result};
use super::judger::{JudgeConfig, TestCase}; use super::judger::{JudgeConfig, TestCase};
use super::result::{ExitStatus, TestCaseResult};
use handlebars::Handlebars; use handlebars::Handlebars;
use libc::{c_long, suseconds_t, time_t}; use libc::{c_long, suseconds_t, time_t};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -131,11 +133,14 @@ pub fn run(judge_config: &JudgeConfig) -> Result<()> {
Err(err) => return Err(Error::CopyFileError(err)), Err(err) => return Err(Error::CopyFileError(err)),
}; };
} }
// TODO: 处理 CE 与 RE编译期间程序运行出错为 CE运行期间出错为 RE
let _ = compile(judge_config, to_dir)?; let _ = compile(judge_config, to_dir)?;
for test_case in &judge_config.tests { for test_case in &judge_config.tests {
println!("\n----------------------------------\n"); let exit_status = run_one(judge_config, test_case, to_dir)?;
let _ = run_one(judge_config, test_case, to_dir)?;
let output_file_path = to_dir.join(&test_case.output_file); let output_file_path = to_dir.join(&test_case.output_file);
let answer_file_path = from_dir.join(&test_case.answer_file);
let res = TestCaseResult::standard(&output_file_path, &answer_file_path, exit_status);
println!("{:?}", res);
match fs::remove_file(output_file_path) { match fs::remove_file(output_file_path) {
Ok(_) => {} Ok(_) => {}
Err(_) => { Err(_) => {
@ -153,14 +158,6 @@ pub fn run(judge_config: &JudgeConfig) -> Result<()> {
Ok(()) Ok(())
} }
pub struct ExitStatus {
pub rusage: libc::rusage,
pub status: i32,
pub time_used: i64,
pub real_time_used: u128,
pub memory_used: i64,
}
pub fn wait(pid: i32) -> Result<ExitStatus> { pub fn wait(pid: i32) -> Result<ExitStatus> {
let start = SystemTime::now(); let start = SystemTime::now();
let mut status = 0; let mut status = 0;
@ -203,9 +200,6 @@ pub fn wait(pid: i32) -> Result<ExitStatus> {
Ok(elapsed) => elapsed.as_millis(), Ok(elapsed) => elapsed.as_millis(),
Err(err) => return Err(Error::SystemTimeError(err)), Err(err) => return Err(Error::SystemTimeError(err)),
}; };
println!("time used:\t{}", time_used);
println!("real time used:\t{}", real_time_used);
println!("memory used:\t{}", rusage.ru_maxrss);
Ok(ExitStatus { Ok(ExitStatus {
rusage, rusage,
status, status,
@ -231,8 +225,11 @@ pub unsafe fn dup(filename: &str, to: libc::c_int, flag: libc::c_int, mode: libc
} }
} }
pub fn run_one(judge_config: &JudgeConfig, test_case: &TestCase, workdir: &Path) -> Result<()> { pub fn run_one(
println!("running test case: {}", test_case.index); judge_config: &JudgeConfig,
test_case: &TestCase,
workdir: &Path,
) -> Result<ExitStatus> {
let pid; let pid;
// 这个操作就需要 1-2ms 左右,此处预先完成,不占用子进程运行时间 // 这个操作就需要 1-2ms 左右,此处预先完成,不占用子进程运行时间
@ -255,13 +252,13 @@ pub fn run_one(judge_config: &JudgeConfig, test_case: &TestCase, workdir: &Path)
&test_case.input_file, &test_case.input_file,
libc::STDIN_FILENO, libc::STDIN_FILENO,
libc::O_RDONLY, libc::O_RDONLY,
0644, 0o644,
); );
dup( dup(
&test_case.output_file, &test_case.output_file,
libc::STDOUT_FILENO, libc::STDOUT_FILENO,
libc::O_CREAT | libc::O_RDWR, libc::O_CREAT | libc::O_RDWR,
0644, 0o644,
); );
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp); libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
} }
@ -270,12 +267,12 @@ pub fn run_one(judge_config: &JudgeConfig, test_case: &TestCase, workdir: &Path)
panic!("How dare you!"); panic!("How dare you!");
} else if pid > 0 { } else if pid > 0 {
// 父进程 // 父进程
let _ = wait(pid)?; let exit_status = wait(pid)?;
return Ok(exit_status);
} else { } else {
// 异常 // 异常
return Err(Error::ForkError(io::Error::last_os_error().raw_os_error())); return Err(Error::ForkError(io::Error::last_os_error().raw_os_error()));
} }
Ok(())
} }
pub fn compile(judge_config: &JudgeConfig, workdir: &Path) -> Result<()> { pub fn compile(judge_config: &JudgeConfig, workdir: &Path) -> Result<()> {

View File

@ -1,11 +1,23 @@
use super::error::Result; extern crate libc;
use super::error::{Error, Result};
use std::fmt; use std::fmt;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::path::Path; use std::path::Path;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ResourceUsed { pub struct ResourceUsed {
pub time_used: u32, pub time_used: i64,
pub memory_used: u32, pub memory_used: i64,
}
pub struct ExitStatus {
pub rusage: libc::rusage,
pub status: i32,
pub time_used: i64,
pub real_time_used: u128,
pub memory_used: i64,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -24,12 +36,111 @@ impl fmt::Display for TestCaseResult {
} }
impl TestCaseResult { impl TestCaseResult {
pub fn standard<P: AsRef<Path>>(output_file: P, answer_file: P) -> Result<TestCaseResult> { pub fn standard<P: AsRef<Path>>(
let result = TestCaseResult::Accepted(ResourceUsed { output_file: P,
time_used: 0, answer_file: P,
memory_used: 0, exit_status: ExitStatus,
}); ) -> Result<TestCaseResult> {
// TODO: 对比答案文件,返回结果 println!("\n----------------------------------\n");
Ok(result) println!("time used:\t{}", exit_status.time_used);
println!("real time used:\t{}", exit_status.real_time_used);
println!("memory used:\t{}", exit_status.rusage.ru_maxrss);
let used = ResourceUsed {
time_used: exit_status.time_used,
memory_used: exit_status.rusage.ru_maxrss,
};
let output_filename = match output_file.as_ref().to_str() {
Some(val) => val,
None => "",
};
let answer_filename = match answer_file.as_ref().to_str() {
Some(val) => val,
None => "",
};
let output_file = match File::open(output_filename) {
Ok(val) => val,
Err(_) => {
return Err(Error::OpenFileError(
output_filename.to_string(),
io::Error::last_os_error().raw_os_error(),
))
}
};
let mut output_reader = BufReader::new(output_file);
let mut output_buffer = String::new();
let mut output_bytes;
let answer_file = match File::open(answer_filename) {
Ok(val) => val,
Err(_) => {
return Err(Error::OpenFileError(
answer_filename.to_string(),
io::Error::last_os_error().raw_os_error(),
))
}
};
let mut answer_reader = BufReader::new(answer_file);
let mut answer_buffer = String::new();
let mut answer_bytes;
loop {
output_bytes = match output_reader.read_line(&mut output_buffer) {
Ok(val) => val,
Err(_) => {
return Err(Error::ReadFileError(
output_filename.to_string(),
io::Error::last_os_error().raw_os_error(),
))
}
};
answer_bytes = match answer_reader.read_line(&mut answer_buffer) {
Ok(val) => val,
Err(_) => {
return Err(Error::ReadFileError(
answer_filename.to_string(),
io::Error::last_os_error().raw_os_error(),
))
}
};
if output_bytes == 0 || answer_bytes == 0 {
break;
}
if output_buffer.trim_end() != answer_buffer.trim_end() {
return Ok(TestCaseResult::WrongAnswer(used));
}
}
// 末尾的空白字符不影响结果
while output_bytes != 0 {
if output_buffer.trim() != "" {
return Ok(TestCaseResult::WrongAnswer(used));
}
output_bytes = match output_reader.read_line(&mut output_buffer) {
Ok(val) => val,
Err(_) => {
return Err(Error::ReadFileError(
output_filename.to_string(),
io::Error::last_os_error().raw_os_error(),
))
}
};
}
while answer_bytes != 0 {
if answer_buffer.trim() != "" {
return Ok(TestCaseResult::WrongAnswer(used));
}
answer_bytes = match answer_reader.read_line(&mut answer_buffer) {
Ok(val) => val,
Err(_) => {
return Err(Error::ReadFileError(
answer_filename.to_string(),
io::Error::last_os_error().raw_os_error(),
))
}
};
}
Ok(TestCaseResult::Accepted(used))
} }
} }