mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
完成基本的答案对比功能
This commit is contained in:
parent
ae1dca202a
commit
353236a154
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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<()> {
|
||||||
|
|||||||
131
src/result.rs
131
src/result.rs
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user