use std::collections::VecDeque;
use std::path::Path;
use std::path::PathBuf;
use std::fs;
use std::env;
use std::time::Duration;
use std::io::Write;
use crate::runner::types::Language;
use crate::util::file::file_exists;
use crate::constants::CACHE_FOLDER;
use crate::constants::TARGET_BINARY_FILE;
use crate::constants::CHECKER_BINARY_FILE;
use crate::constants::GEN_BINARY_FILE;
use crate::constants::QTEST_INPUT_FILE;
use crate::constants::QTEST_OUTPUT_FILE;
use crate::constants::QTEST_ERROR_FILE;
use crate::constants::QTEST_CHECKER_FILE;
use crate::util::lang::{
get_language_by_ext_default,
get_language_by_ext_set_output
};
use failure::ResultExt;
use exitfailure::ExitFailure;
use colored::*;
use glob::glob;
pub fn run(target_file: PathBuf, checker_file: PathBuf,
gen_file: PathBuf, timeout: u32, test_cases: u32, wa_break: bool,
save_cases: bool) -> Result<(), ExitFailure> {
match fs::read_dir(CACHE_FOLDER) {
Ok(_) => (),
Err(_) => match fs::create_dir(CACHE_FOLDER) {
Ok(_) => (),
Err(_) => {
let error: Result<(), failure::Error> = Err(failure::err_msg(format!("Can't create internal cache files")));
return Ok(error.context("Error creating internal cache files".to_string())?);
}
},
}
let file_name = target_file.to_str().unwrap();
match file_exists(file_name) {
Ok(_) => (),
Err(_) => {
let error: Result<(), failure::Error> = Err(failure::err_msg(format!("Can't open the file '{}'", file_name)));
return Ok(error.context("<target-file> Not found".to_string())?);
}
}
let file_name = checker_file.to_str().unwrap();
match file_exists(file_name) {
Ok(_) => (),
Err(_) => {
let error: Result<(), failure::Error> = Err(failure::err_msg(format!("Can't open the file '{}'", file_name)));
return Ok(error.context("<checker-file> Not found".to_string())?);
}
}
let file_name = gen_file.to_str().unwrap();
match file_exists(file_name) {
Ok(_) => (),
Err(_) => {
let error: Result<(), failure::Error> = Err(failure::err_msg(format!("Can't open the file '{}'", file_name)));
return Ok(error.context("<gen-file> Not found".to_string())?);
}
}
let root: PathBuf = match env::current_dir() {
Ok(path) => path,
_ => unreachable!(),
};
let root: &str = match root.to_str() {
Some(root_path) => root_path,
_ => unreachable!(),
};
let any_gen: Option<Box<dyn Language>> = get_language_by_ext_set_output(
root,
gen_file,
&GEN_BINARY_FILE,
&QTEST_INPUT_FILE,
);
let any_gen: Box<dyn Language> = any_gen.unwrap();
let generator_file_lang: &dyn Language = any_gen.as_ref();
let any_target: Option<Box<dyn Language>> = get_language_by_ext_default(
root,
target_file,
&TARGET_BINARY_FILE,
&QTEST_INPUT_FILE,
&QTEST_OUTPUT_FILE,
&QTEST_ERROR_FILE
);
let any_target: Box<dyn Language> = any_target.unwrap();
let target_file_lang: &dyn Language = any_target.as_ref();
let any_checker: Option<Box<dyn Language>> = get_language_by_ext_default(
root,
checker_file,
&CHECKER_BINARY_FILE,
&QTEST_OUTPUT_FILE,
&QTEST_CHECKER_FILE,
&QTEST_ERROR_FILE
);
let any_checker: Box<dyn Language> = any_checker.unwrap();
let checker_file_lang_lang: &dyn Language = any_checker.as_ref();
checker_file_lang_lang.build();
target_file_lang.build();
generator_file_lang.build();
if save_cases {
let paths = glob("test_cases/testcase_wa*")?;
for entry in paths {
match entry {
Ok(path) => {
fs::remove_file(path.to_str().unwrap())?;
},
Err(_) => (),
}
}
}
let mut tle_count: u32 = 0;
let mut wa_count: u32 = 0;
for test_number in 1..=test_cases {
let time_gen: Duration = generator_file_lang.execute(timeout as u32);
let time_target: Duration = target_file_lang.execute(timeout as u32);
let time_checker: Duration = checker_file_lang_lang.execute(timeout as u32);
let mills_target: u128 = time_target.as_millis();
if time_gen >= Duration::from_millis(timeout as u64) {
println!(
" {} [{}] {} {}ms",
test_number,
"TLE".bold().red(),
"Generator Time Limit Exceeded :".bold().red(),
timeout
);
let error: Result<(), failure::Error> = Err(failure::err_msg("very slow generator"));
return Ok(error.context("Generator TLE".to_string())?);
}
if time_checker >= Duration::from_millis(timeout as u64) {
println!(
" {} [{}] {} {}ms",
test_number,
"TLE".bold().red(),
"Correct File give Time Limit Exceeded :".bold().red(),
timeout
);
let error: Result<(), failure::Error> = Err(failure::err_msg("Correct file very slow"));
return Ok(error.context("Correct File TLE".to_string())?);
}
if time_target >= Duration::from_millis(timeout as u64) {
tle_count += 1;
println!(
" {} [{}] {} {}ms",
test_number,
"TLE".bold().red(),
"Time Limit Exceeded :".bold().red(),
timeout
);
if save_cases && !Path::new("test_cases").exists() {
match fs::create_dir("test_cases") {
Err(_) => {
let error = Err(failure::err_msg("Could not create folder test_cases"));
return Ok(error.context("test_cases folder".to_string())?);
}
_ => (),
}
}
if save_cases {
let filename: String = format!("test_cases/testcase_tle_{}.txt", tle_count);
let mut file = fs::File::create(filename)
.expect("Error creating file test_cases/testcase(i).txt");
file.write_all(fs::read_to_string(QTEST_INPUT_FILE).unwrap().as_bytes()).unwrap();
}
if wa_break {
fs::remove_file(&QTEST_INPUT_FILE)?;
fs::remove_file(&QTEST_OUTPUT_FILE)?;
fs::remove_file(&QTEST_ERROR_FILE)?;
fs::remove_file(&QTEST_CHECKER_FILE)?;
match file_exists(&TARGET_BINARY_FILE) {
Ok(_) => fs::remove_file(&TARGET_BINARY_FILE)?,
_ => (),
}
match file_exists(&GEN_BINARY_FILE) {
Ok(_) => fs::remove_file(&GEN_BINARY_FILE)?,
_ => (),
}
match file_exists(&CHECKER_BINARY_FILE) {
Ok(_) => fs::remove_file(&CHECKER_BINARY_FILE)?,
_ => (),
};
return Ok(());
}
} else {
let file_checker = format!("{}/{}", root, QTEST_CHECKER_FILE);
if check_answer(&file_checker, true) {
println!(
" {} [{}] {} {}ms",
test_number.to_string().bold().white(),
"OK".bold().green(),
"Finished in".bold().green(), mills_target
);
} else {
wa_count += 1;
println!(
" {} [{}] {} {}ms",
test_number.to_string().bold().white(),
"WA".bold().red(),
"Finished in".bold().red(), mills_target
);
if save_cases && !Path::new("test_cases").exists() {
match fs::create_dir("test_cases") {
Err(_) => {
let error = Err(failure::err_msg("Could not create folder test_cases"));
return Ok(error.context("test_cases folder".to_string())?);
}
_ => (),
}
}
if save_cases {
let filename: String = format!("test_cases/testcase_wa_{}.txt", wa_count);
let mut file = fs::File::create(filename)
.expect("Error creating file test_cases/testcase(i).txt");
file.write_all(fs::read_to_string(QTEST_INPUT_FILE).unwrap().as_bytes()).unwrap();
}
if wa_break {
fs::remove_file(&QTEST_INPUT_FILE)?;
fs::remove_file(&QTEST_OUTPUT_FILE)?;
fs::remove_file(&QTEST_ERROR_FILE)?;
fs::remove_file(&QTEST_CHECKER_FILE)?;
match file_exists(&TARGET_BINARY_FILE) {
Ok(_) => fs::remove_file(&TARGET_BINARY_FILE)?,
_ => (),
}
match file_exists(&GEN_BINARY_FILE) {
Ok(_) => fs::remove_file(&GEN_BINARY_FILE)?,
_ => (),
}
match file_exists(&CHECKER_BINARY_FILE) {
Ok(_) => fs::remove_file(&CHECKER_BINARY_FILE)?,
_ => (),
};
let error = Err(failure::err_msg(format!("Wrong answer on test {}", test_number)));
return Ok(error.context("WA Status".to_string())?);
}
}
}
fs::remove_file(&QTEST_INPUT_FILE)?;
fs::remove_file(&QTEST_OUTPUT_FILE)?;
fs::remove_file(&QTEST_ERROR_FILE)?;
fs::remove_file(&QTEST_CHECKER_FILE)?;
}
match file_exists(&TARGET_BINARY_FILE) {
Ok(_) => fs::remove_file(&TARGET_BINARY_FILE)?,
_ => (),
}
match file_exists(&GEN_BINARY_FILE) {
Ok(_) => fs::remove_file(&GEN_BINARY_FILE)?,
_ => (),
}
match file_exists(&CHECKER_BINARY_FILE) {
Ok(_) => fs::remove_file(&CHECKER_BINARY_FILE)?,
_ => (),
}
Ok(())
}
pub fn check_answer(answer_file: &String, ignore_space: bool) -> bool {
let mut is_good = true;
let target_content = match std::fs::read_to_string(answer_file) {
Ok(content) => Some(content),
Err(_) => {
is_good = false;
None
},
};
if !is_good { return false; }
let mut target_content_vec: VecDeque<char> = VecDeque::new();
is_good = match target_content {
Some(s1) => {
target_content_vec = s1.to_lowercase().chars().collect::<VecDeque<char>>();
true
},
_ => false,
};
if !is_good { return false; }
if ignore_space {
while !target_content_vec.is_empty()
&& (*target_content_vec.back().unwrap()==' '||*target_content_vec.back().unwrap()=='\n') {
target_content_vec.pop_back();
}
while !target_content_vec.is_empty()
&& (*target_content_vec.front().unwrap()==' '||*target_content_vec.front().unwrap()=='\n') {
target_content_vec.pop_front();
}
}
target_content_vec == vec!['y', 'e', 's']
}