mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
基本流程就绪
This commit is contained in:
parent
33f93913d6
commit
04fe473175
1
runtime/data/1000/1.in
Normal file
1
runtime/data/1000/1.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 2
|
||||||
1
runtime/data/1000/1.out
Normal file
1
runtime/data/1000/1.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
3
|
||||||
0
runtime/data/1001/1.in
Normal file
0
runtime/data/1001/1.in
Normal file
1
runtime/data/1001/1.out
Normal file
1
runtime/data/1001/1.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello World!
|
||||||
@ -4,9 +4,6 @@ use std::fs::{read_to_string, remove_dir};
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tempfile::tempdir_in;
|
use tempfile::tempdir_in;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use std::println as debug;
|
|
||||||
|
|
||||||
pub struct CGroupOptions {
|
pub struct CGroupOptions {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
@ -14,7 +11,6 @@ pub struct CGroupOptions {
|
|||||||
impl CGroupOptions {
|
impl CGroupOptions {
|
||||||
pub fn new(base_path: &str) -> Result<CGroupOptions> {
|
pub fn new(base_path: &str) -> Result<CGroupOptions> {
|
||||||
let pwd = try_io!(tempdir_in(base_path));
|
let pwd = try_io!(tempdir_in(base_path));
|
||||||
debug!("register cgroup {:?}", pwd);
|
|
||||||
Ok(CGroupOptions {
|
Ok(CGroupOptions {
|
||||||
path: pwd.path().to_path_buf(),
|
path: pwd.path().to_path_buf(),
|
||||||
})
|
})
|
||||||
@ -22,13 +18,11 @@ impl CGroupOptions {
|
|||||||
|
|
||||||
/// 将指定进程加入该 cgroup 组
|
/// 将指定进程加入该 cgroup 组
|
||||||
pub fn apply(&self, pid: i32) -> Result<()> {
|
pub fn apply(&self, pid: i32) -> Result<()> {
|
||||||
debug!("add {} to cgroup {:?}", pid, self.path);
|
|
||||||
self.set("cgroup.procs", &format!("{}", pid))
|
self.set("cgroup.procs", &format!("{}", pid))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// e.g `set("memory.limit_in_bytes", "67108864")`
|
/// e.g `set("memory.limit_in_bytes", "67108864")`
|
||||||
pub fn set(&self, key: &str, value: &str) -> Result<()> {
|
pub fn set(&self, key: &str, value: &str) -> Result<()> {
|
||||||
debug!("set {} values {}", key, value);
|
|
||||||
try_io!(fs::write(self.path.join(key), value));
|
try_io!(fs::write(self.path.join(key), value));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -41,7 +35,6 @@ impl CGroupOptions {
|
|||||||
|
|
||||||
impl Drop for CGroupOptions {
|
impl Drop for CGroupOptions {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
debug!("remove cgroup {:?}", self.path);
|
|
||||||
remove_dir(&self.path).unwrap();
|
remove_dir(&self.path).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ pub enum Error {
|
|||||||
CreateTempDirError(io::Error),
|
CreateTempDirError(io::Error),
|
||||||
CustomError(String),
|
CustomError(String),
|
||||||
LanguageNotFound(String),
|
LanguageNotFound(String),
|
||||||
|
SystemError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@ -37,6 +38,7 @@ impl fmt::Display for Error {
|
|||||||
Error::IOError(ref e) => write!(f, "IOError: `{}`", errno_str(e.raw_os_error())),
|
Error::IOError(ref e) => write!(f, "IOError: `{}`", errno_str(e.raw_os_error())),
|
||||||
Error::CustomError(ref e) => write!(f, "Internal Server Error: `{}`", e),
|
Error::CustomError(ref e) => write!(f, "Internal Server Error: `{}`", e),
|
||||||
Error::LanguageNotFound(ref e) => write!(f, "Language Not Fount: `{}`", e),
|
Error::LanguageNotFound(ref e) => write!(f, "Language Not Fount: `{}`", e),
|
||||||
|
Error::SystemError(ref e) => write!(f, "System Error: `{}`", e),
|
||||||
_ => write!(f, "{:?}", self),
|
_ => write!(f, "{:?}", self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,28 +1,22 @@
|
|||||||
use crate::config::{CONFIG, STDERR_FILENAME, STDOUT_FILENAME};
|
use crate::config::{CONFIG, STDERR_FILENAME, STDIN_FILENAME, STDOUT_FILENAME};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::process::{Process, Runner};
|
use crate::process::{Process, Runner};
|
||||||
use crate::result::{compile_error, compile_success};
|
use crate::result::{
|
||||||
use crate::river;
|
accepted, compile_error, compile_success, memory_limit_exceeded, runtime_error,
|
||||||
use crate::river::JudgeResponse;
|
standard_result, time_limit_exceeded, wrong_answer,
|
||||||
use std::fs;
|
};
|
||||||
use std::fs::read_to_string;
|
use crate::river::{JudgeResponse, JudgeResultEnum, JudgeType};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use tokio::fs;
|
||||||
#[cfg(test)]
|
use tokio::fs::read_to_string;
|
||||||
use std::println as debug;
|
|
||||||
|
|
||||||
pub async fn compile(language: &str, code: &str, path: &Path) -> Result<JudgeResponse> {
|
pub async fn compile(language: &str, code: &str, path: &Path) -> Result<JudgeResponse> {
|
||||||
debug!("language: {}", language);
|
|
||||||
debug!("code: {}", code);
|
|
||||||
debug!("path: {:?}", path);
|
|
||||||
let lang = match CONFIG.languages.get(language) {
|
let lang = match CONFIG.languages.get(language) {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Err(Error::LanguageNotFound(String::from(language))),
|
None => return Err(Error::LanguageNotFound(String::from(language))),
|
||||||
};
|
};
|
||||||
debug!("write file to {:?}", path.join(&lang.code_file));
|
try_io!(fs::write(path.join(&lang.code_file), &code).await);
|
||||||
try_io!(fs::write(path.join(&lang.code_file), &code));
|
|
||||||
|
|
||||||
debug!("build command: {}", lang.compile_cmd);
|
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
String::from(&lang.compile_cmd),
|
String::from(&lang.compile_cmd),
|
||||||
10000,
|
10000,
|
||||||
@ -41,10 +35,9 @@ pub async fn compile(language: &str, code: &str, path: &Path) -> Result<JudgeRes
|
|||||||
// 因为不同的语言、不同的编译器,错误信息输出到了不同的地方
|
// 因为不同的语言、不同的编译器,错误信息输出到了不同的地方
|
||||||
let errmsg = format!(
|
let errmsg = format!(
|
||||||
"{}\n{}",
|
"{}\n{}",
|
||||||
try_io!(read_to_string(path.join(STDOUT_FILENAME))),
|
try_io!(read_to_string(path.join(STDOUT_FILENAME)).await),
|
||||||
try_io!(read_to_string(path.join(STDERR_FILENAME))),
|
try_io!(read_to_string(path.join(STDERR_FILENAME)).await),
|
||||||
);
|
);
|
||||||
debug!("{}", errmsg);
|
|
||||||
return Ok(compile_error(status.time_used, mem_used, &errmsg));
|
return Ok(compile_error(status.time_used, mem_used, &errmsg));
|
||||||
} else {
|
} else {
|
||||||
return Ok(compile_success(status.time_used, mem_used));
|
return Ok(compile_success(status.time_used, mem_used));
|
||||||
@ -60,16 +53,62 @@ pub async fn judge(
|
|||||||
judge_type: i32,
|
judge_type: i32,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<JudgeResponse> {
|
) -> Result<JudgeResponse> {
|
||||||
debug!("language: {}", language);
|
let data_dir = Path::new(&CONFIG.data_dir);
|
||||||
debug!("in_file: {}", in_file);
|
// 复制输入文件
|
||||||
debug!("out_file: {}", out_file);
|
try_io!(fs::copy(data_dir.join(&in_file), path.join(STDIN_FILENAME)).await);
|
||||||
debug!("time_limit: {}", time_limit);
|
|
||||||
debug!("memory_limit: {}", memory_limit);
|
let lang = match CONFIG.languages.get(language) {
|
||||||
debug!("judge_type: {}", judge_type);
|
Some(val) => val,
|
||||||
debug!("path: {:?}", path);
|
None => return Err(Error::LanguageNotFound(String::from(language))),
|
||||||
Ok(JudgeResponse {
|
};
|
||||||
state: Some(river::judge_response::State::Status(
|
let process = Process::new(
|
||||||
river::JudgeStatus::Pending as i32,
|
String::from(&lang.run_cmd),
|
||||||
)),
|
time_limit,
|
||||||
})
|
memory_limit,
|
||||||
|
path.to_path_buf(),
|
||||||
|
);
|
||||||
|
let result = Runner::from(process)?;
|
||||||
|
let status = result.await?;
|
||||||
|
// 为了更好的体验,此处内存用量取 getrusage 报告与 cgroup 报告中较小的一个
|
||||||
|
let mem_used = if status.memory_used < status.cgroup_memory_used {
|
||||||
|
status.memory_used
|
||||||
|
} else {
|
||||||
|
status.cgroup_memory_used
|
||||||
|
};
|
||||||
|
if status.time_used > time_limit.into() {
|
||||||
|
// TLE
|
||||||
|
return Ok(time_limit_exceeded(status.time_used, mem_used));
|
||||||
|
} else if mem_used > memory_limit.into() {
|
||||||
|
// MLE
|
||||||
|
return Ok(memory_limit_exceeded(status.time_used, mem_used));
|
||||||
|
} else if status.signal != 0 {
|
||||||
|
// RE
|
||||||
|
return Ok(runtime_error(
|
||||||
|
status.time_used,
|
||||||
|
mem_used,
|
||||||
|
&format!("Program was interrupted by signal: `{}`", status.signal),
|
||||||
|
));
|
||||||
|
} else if status.exit_code != 0 {
|
||||||
|
// RE
|
||||||
|
// 就算是用户自己返回的非零,也算 RE
|
||||||
|
return Ok(runtime_error(
|
||||||
|
status.time_used,
|
||||||
|
mem_used,
|
||||||
|
&format!("Exceptional program return code: `{}`", status.exit_code),
|
||||||
|
));
|
||||||
|
} else if judge_type == JudgeType::Standard as i32 {
|
||||||
|
// 答案对比
|
||||||
|
let out = try_io!(fs::read(path.join(STDOUT_FILENAME)).await);
|
||||||
|
let ans = try_io!(fs::read(data_dir.join(&out_file)).await);
|
||||||
|
let res = standard_result(&out, &ans)?;
|
||||||
|
if res == JudgeResultEnum::Accepted {
|
||||||
|
return Ok(accepted(status.time_used, mem_used));
|
||||||
|
} else {
|
||||||
|
return Ok(wrong_answer(status.time_used, mem_used));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::CustomError(String::from(format!(
|
||||||
|
"Unknown JudgeType: {}",
|
||||||
|
judge_type
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,15 +51,14 @@ impl River for RiverService {
|
|||||||
};
|
};
|
||||||
// 是否通过编译
|
// 是否通过编译
|
||||||
let mut compile_success = false;
|
let mut compile_success = false;
|
||||||
|
let mut language = String::from("");
|
||||||
while let Some(req) = stream.next().await {
|
while let Some(req) = stream.next().await {
|
||||||
yield result::pending();
|
yield result::pending();
|
||||||
// TODO: 限制并发数量
|
// TODO: 限制并发数量
|
||||||
yield result::running();
|
yield result::running();
|
||||||
let req = req?;
|
let req = req?;
|
||||||
let mut language = String::from("");
|
|
||||||
let result = match &req.data {
|
let result = match &req.data {
|
||||||
Some(Data::CompileData(data)) => {
|
Some(Data::CompileData(data)) => {
|
||||||
debug!("compile request");
|
|
||||||
// 因为评测时还需要 language 的信息,因此此处进行复制保存
|
// 因为评测时还需要 language 的信息,因此此处进行复制保存
|
||||||
language = String::from(&data.language);
|
language = String::from(&data.language);
|
||||||
let res = judger::compile(&language, &data.code, &pwd.path()).await;
|
let res = judger::compile(&language, &data.code, &pwd.path()).await;
|
||||||
@ -75,7 +74,6 @@ impl River for RiverService {
|
|||||||
res
|
res
|
||||||
},
|
},
|
||||||
Some(Data::JudgeData(data)) => {
|
Some(Data::JudgeData(data)) => {
|
||||||
debug!("judge request");
|
|
||||||
// 必须通过编译才能运行
|
// 必须通过编译才能运行
|
||||||
if language == "" || !compile_success {
|
if language == "" || !compile_success {
|
||||||
Err(error::Error::CustomError(String::from("not compiled")))
|
Err(error::Error::CustomError(String::from("not compiled")))
|
||||||
|
|||||||
@ -149,7 +149,7 @@ impl Future for Runner {
|
|||||||
runner.cgroup_set.apply(pid).unwrap();
|
runner.cgroup_set.apply(pid).unwrap();
|
||||||
runner.cgroup_set.memory.set(
|
runner.cgroup_set.memory.set(
|
||||||
"memory.limit_in_bytes",
|
"memory.limit_in_bytes",
|
||||||
&format!("{}", runner.process.memory_limit as i64 * 1024),
|
&format!("{}", runner.process.memory_limit as i64 * 1536), // 本来应该乘以 1024,此处略微放宽限制,从而让用户体验更好
|
||||||
)?;
|
)?;
|
||||||
let tx = runner.tx.clone();
|
let tx = runner.tx.clone();
|
||||||
|
|
||||||
@ -182,6 +182,9 @@ impl Future for Runner {
|
|||||||
debug!("real time used: {} ms", status.real_time_used);
|
debug!("real time used: {} ms", status.real_time_used);
|
||||||
debug!("cgroup memory used: {} KiB", status.cgroup_memory_used);
|
debug!("cgroup memory used: {} KiB", status.cgroup_memory_used);
|
||||||
debug!("rusage memory used: {} KiB", status.memory_used);
|
debug!("rusage memory used: {} KiB", status.memory_used);
|
||||||
|
if status.signal < 0 {
|
||||||
|
return Poll::Ready(Err(Error::SystemError(status.errmsg)));
|
||||||
|
}
|
||||||
return Poll::Ready(Ok(status));
|
return Poll::Ready(Ok(status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
233
src/result.rs
233
src/result.rs
@ -1,4 +1,5 @@
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::error::Result;
|
||||||
use crate::river::judge_response::State;
|
use crate::river::judge_response::State;
|
||||||
use crate::river::{JudgeResponse, JudgeResult, JudgeResultEnum, JudgeStatus};
|
use crate::river::{JudgeResponse, JudgeResult, JudgeResultEnum, JudgeStatus};
|
||||||
|
|
||||||
@ -38,12 +39,242 @@ pub fn compile_error(time_used: i64, memory_used: i64, errmsg: &str) -> JudgeRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_success(time_used: i64, memory_used: i64) -> JudgeResponse {
|
pub fn compile_success(time_used: i64, memory_used: i64) -> JudgeResponse {
|
||||||
|
judge_result(time_used, memory_used, JudgeResultEnum::CompileSuccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accepted(time_used: i64, memory_used: i64) -> JudgeResponse {
|
||||||
|
judge_result(time_used, memory_used, JudgeResultEnum::Accepted)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrong_answer(time_used: i64, memory_used: i64) -> JudgeResponse {
|
||||||
|
judge_result(time_used, memory_used, JudgeResultEnum::WrongAnswer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time_limit_exceeded(time_used: i64, memory_used: i64) -> JudgeResponse {
|
||||||
|
judge_result(time_used, memory_used, JudgeResultEnum::TimeLimitExceeded)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_limit_exceeded(time_used: i64, memory_used: i64) -> JudgeResponse {
|
||||||
|
judge_result(time_used, memory_used, JudgeResultEnum::MemoryLimitExceeded)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime_error(time_used: i64, memory_used: i64, errmsg: &str) -> JudgeResponse {
|
||||||
JudgeResponse {
|
JudgeResponse {
|
||||||
state: Some(State::Result(JudgeResult {
|
state: Some(State::Result(JudgeResult {
|
||||||
time_used: time_used,
|
time_used: time_used,
|
||||||
memory_used: memory_used,
|
memory_used: memory_used,
|
||||||
result: JudgeResultEnum::CompileSuccess as i32,
|
result: JudgeResultEnum::RuntimeError as i32,
|
||||||
|
errmsg: String::from(errmsg),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn judge_result(time_used: i64, memory_used: i64, result: JudgeResultEnum) -> JudgeResponse {
|
||||||
|
JudgeResponse {
|
||||||
|
state: Some(State::Result(JudgeResult {
|
||||||
|
time_used: time_used,
|
||||||
|
memory_used: memory_used,
|
||||||
|
result: result as i32,
|
||||||
errmsg: String::from(""),
|
errmsg: String::from(""),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn standard_result(out: &[u8], ans: &[u8]) -> Result<JudgeResultEnum> {
|
||||||
|
let out_len = out.len();
|
||||||
|
let ans_len = ans.len();
|
||||||
|
let mut out_offset = 0;
|
||||||
|
let mut ans_offset = 0;
|
||||||
|
// 没有 PE,PE 直接 WA
|
||||||
|
let mut r = JudgeResultEnum::Accepted;
|
||||||
|
while out_offset <= out_len && ans_offset <= ans_len {
|
||||||
|
let (out_start, out_end, out_exists) = next_line(&out, out_offset, out_len);
|
||||||
|
let (ans_start, ans_end, ans_exists) = next_line(&ans, ans_offset, ans_len);
|
||||||
|
if !out_exists || !ans_exists {
|
||||||
|
// 如果一个已经读取完但另一个还有数据,则结果为 WA
|
||||||
|
if out_exists != ans_exists {
|
||||||
|
r = JudgeResultEnum::WrongAnswer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 如果两个数据当前行长度不同,则结果为 WA(这个长度已经排除了末尾空白符号)
|
||||||
|
if out_end - out_start != ans_end - ans_start {
|
||||||
|
r = JudgeResultEnum::WrongAnswer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let line_len = out_end - out_start;
|
||||||
|
for i in 0..line_len {
|
||||||
|
// 逐个对比
|
||||||
|
if out[out_start + i] != ans[ans_start + i] {
|
||||||
|
r = JudgeResultEnum::WrongAnswer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果结果出来了,则退出循环
|
||||||
|
if r != JudgeResultEnum::Accepted {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out_offset = out_end;
|
||||||
|
ans_offset = ans_end;
|
||||||
|
}
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略空行与每行末尾的空格与制表符
|
||||||
|
* 如果某行只有空白字符,则忽略此行
|
||||||
|
* "Hello ; "
|
||||||
|
* " "
|
||||||
|
* " World"
|
||||||
|
* -----------------
|
||||||
|
* "Hello ;"
|
||||||
|
* " World"
|
||||||
|
*/
|
||||||
|
fn next_line(v: &[u8], offset: usize, len: usize) -> (usize, usize, bool) {
|
||||||
|
let mut line_offset = offset;
|
||||||
|
let mut left = 0;
|
||||||
|
let mut right = len;
|
||||||
|
let mut has_line = false;
|
||||||
|
while line_offset < len {
|
||||||
|
let ch = v[line_offset] as char;
|
||||||
|
// 当读取到某行结束时
|
||||||
|
if ch == '\n' {
|
||||||
|
if has_line {
|
||||||
|
// 如果已经有新行的数据,则在这个位置结束
|
||||||
|
right = line_offset;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// 如果还没有数据,说明整行为空,忽略当前行,将下一行设为起点重复过程
|
||||||
|
left = line_offset + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
|
||||||
|
// 空白字符
|
||||||
|
} else {
|
||||||
|
// 非空白字符
|
||||||
|
has_line = true;
|
||||||
|
}
|
||||||
|
line_offset += 1;
|
||||||
|
}
|
||||||
|
// 排除该行末尾的空白字符
|
||||||
|
while left < right {
|
||||||
|
let ch = v[right - 1] as char;
|
||||||
|
if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
|
||||||
|
// 空白字符
|
||||||
|
} else {
|
||||||
|
// 非空白字符
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
right -= 1;
|
||||||
|
}
|
||||||
|
(left, right, has_line)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test1() {
|
||||||
|
let v: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let (l, r, e) = next_line(v, 0, v.len());
|
||||||
|
assert_eq!(l, 0);
|
||||||
|
assert_eq!(r, 12);
|
||||||
|
assert!(e);
|
||||||
|
let (_l, _r, e) = next_line(v, r, v.len());
|
||||||
|
assert!(!e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test2() {
|
||||||
|
let v: &[u8] = "Hello World! ".as_bytes();
|
||||||
|
let (l, r, e) = next_line(v, 0, v.len());
|
||||||
|
assert_eq!(l, 0);
|
||||||
|
assert_eq!(r, 12);
|
||||||
|
assert!(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test3() {
|
||||||
|
let v: &[u8] = " Hello World!".as_bytes();
|
||||||
|
let (l, r, e) = next_line(v, 0, v.len());
|
||||||
|
assert_eq!(l, 0);
|
||||||
|
assert_eq!(r, 15);
|
||||||
|
assert!(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test4() {
|
||||||
|
let v: &[u8] =
|
||||||
|
" Hello World!\n Hello World!\n\n\n\n \n\n\n\n\n\n \t\t\t \t\n\n\n\n"
|
||||||
|
.as_bytes();
|
||||||
|
let (l, r, e) = next_line(v, 0, v.len());
|
||||||
|
assert_eq!(l, 0);
|
||||||
|
assert_eq!(r, 15);
|
||||||
|
assert!(e);
|
||||||
|
let (l, r, e) = next_line(v, r, v.len());
|
||||||
|
assert_eq!(l, 16);
|
||||||
|
assert_eq!(r, 31);
|
||||||
|
assert!(e);
|
||||||
|
let (_l, _r, e) = next_line(v, r, v.len());
|
||||||
|
assert!(!e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test5() {
|
||||||
|
let ans: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let out: &[u8] = "Hello World!".as_bytes();
|
||||||
|
assert_eq!(
|
||||||
|
standard_result(out, ans).unwrap(),
|
||||||
|
JudgeResultEnum::Accepted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test6() {
|
||||||
|
let ans: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let out: &[u8] = "Hello World! ".as_bytes();
|
||||||
|
assert_eq!(
|
||||||
|
standard_result(out, ans).unwrap(),
|
||||||
|
JudgeResultEnum::Accepted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test7() {
|
||||||
|
let ans: &[u8] = "Hello World! \n\n\n\n \n\n\n\n".as_bytes();
|
||||||
|
let out: &[u8] = "Hello World!\t\t\t\t\n\n\n\n \n\n\n\n\t\t\t\t".as_bytes();
|
||||||
|
assert_eq!(
|
||||||
|
standard_result(out, ans).unwrap(),
|
||||||
|
JudgeResultEnum::Accepted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test8() {
|
||||||
|
let ans: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let out: &[u8] = "".as_bytes();
|
||||||
|
assert_eq!(
|
||||||
|
standard_result(out, ans).unwrap(),
|
||||||
|
JudgeResultEnum::WrongAnswer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test9() {
|
||||||
|
let ans: &[u8] = "".as_bytes();
|
||||||
|
let out: &[u8] = "".as_bytes();
|
||||||
|
assert_eq!(
|
||||||
|
standard_result(out, ans).unwrap(),
|
||||||
|
JudgeResultEnum::Accepted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test10() {
|
||||||
|
let ans: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let out: &[u8] = "Hello World!\n".as_bytes();
|
||||||
|
assert_eq!(
|
||||||
|
standard_result(out, ans).unwrap(),
|
||||||
|
JudgeResultEnum::Accepted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user