use colci::Color;
use crate::model::ExecutionErrorResponse;
use crate::printer::{decorator::bold_text, Printer, NEW_LINE};
use crate::{icon::Icon, model::SubmissionResponse, Either};
#[derive(Debug)]
pub struct TestExecutionResult {
test_data: Either,
submission_response: SubmissionResponse,
}
impl Printer for TestExecutionResult {
fn is_error(&self) -> bool {
self.submission_response.is_error()
}
fn buffer(&self) -> String {
if self.is_error() {
self.error_buffer()
} else {
self.success_buffer()
}
}
}
impl TestExecutionResult {
pub fn new(test_data: Either, submission_result: SubmissionResponse) -> Self {
Self {
test_data,
submission_response: submission_result,
}
}
fn error_buffer(&self) -> String {
let error_buffer = self.runtime_error_buffer()
+ NEW_LINE
+ NEW_LINE
+ self.compile_error_buffer().as_str();
if error_buffer.trim().is_empty() {
self.wrong_answer_buffer()
} else {
error_buffer
}
}
fn runtime_error_buffer(&self) -> String {
if !self.submission_response.has_runtime_error() {
return NEW_LINE.to_owned();
}
self.submission_response.status_msg.to_owned()
}
fn compile_error_buffer(&self) -> String {
if !self.submission_response.has_compile_error() {
return NEW_LINE.to_owned();
}
self.submission_response
.full_compile_error
.to_owned()
.unwrap_or_default()
}
fn wrong_answer_buffer(&self) -> String {
let mut buffer = String::new();
buffer.push_str(&bold_text(
&Color::Red(&format!(
"\n{} Wrong Answer: ({})\n\n",
Icon::_No.to_string(),
self.total_cases_ratio_buffer(&self.submission_response)
))
.make(),
));
buffer.push_str(&self.test_cases_buffer());
buffer.push_str(&Color::Red(&self.get_metas()).make());
buffer
}
fn test_cases_buffer(&self) -> String {
let mut buffer = String::new();
// combine test_data, code_answer & expected_code_answer
match (
&self.test_data,
&self.submission_response.code_answer,
&self.submission_response.expected_code_answer,
) {
(
Either::Sequence(input_seq),
Some(Either::Sequence(ans_seq)),
Some(Either::Sequence(exp_ans_seq)),
) => {
let chunk_size = input_seq.len() / ans_seq.len();
let input_chunks: Vec<Vec<String>> = input_seq
.chunks(chunk_size)
.map(|chunk| chunk.to_vec())
.collect();
for (i, ((input, ans), exp_ans)) in input_chunks
.iter()
.zip(ans_seq)
.zip(exp_ans_seq)
.enumerate()
{
let mut test_case = String::new();
let is_correct = ans.eq(exp_ans);
let colored_case = if is_correct {
Color::Green(&format!("{} Case {}:\n", Icon::Yes.to_string(), i + 1)).make()
} else {
Color::Red(&format!("{} Case {}:\n", Icon::_No.to_string(), i + 1)).make()
};
test_case.push_str(&colored_case);
test_case.push_str(&format!("\tInput: \n\t\t{}\n", input.join("\n\t\t")));
test_case.push_str(&format!("\n\tOutput: {}\n", ans));
test_case.push_str(&format!("\tExpected: {}\n\n", exp_ans));
buffer.push_str(test_case.as_str());
}
}
_ => {}
}
buffer
}
fn success_buffer(&self) -> String {
let mut buffer = String::new();
buffer.push_str(&bold_text(
&Color::Green(&format!(
"{} Accepted: ({})\n\n",
Icon::Yes.to_string(),
self.total_cases_ratio_buffer(&self.submission_response)
))
.make(),
));
buffer.push_str(&self.test_cases_buffer());
buffer.push_str(&Color::Green(&self.get_metas()).make());
buffer
}
fn get_metas(&self) -> String {
let memory_percentile = self
.submission_response
.memory_percentile
.unwrap_or(0.0)
.to_string();
let runtime_percentile = self
.submission_response
.runtime_percentile
.unwrap_or(0.0)
.to_string();
let testcases = format!(
"{}/{}",
self.submission_response.total_correct.unwrap_or(0),
self.submission_response.total_testcases.unwrap_or(0)
);
let accepted_meta = format!(
"{}: ({})",
self.submission_response.status_msg.as_str(),
testcases.as_str()
);
let metas = vec![
accepted_meta,
"Memory: ".to_string() + self.submission_response.status_memory.as_str(),
"Memory %ile: ".to_string() + memory_percentile.as_str(),
"Runtime: ".to_string() + self.submission_response.status_runtime.as_str(),
"Runtime %ile: ".to_string() + runtime_percentile.as_str(),
"\n\n".to_string(),
];
metas.join("\n")
}
}
#[cfg(test)]
mod tests {
use super::{Printer, TestExecutionResult};
use crate::{model::SubmissionResponse, Either};
use serde_json::from_value;
#[test]
fn print_sequence_success() {
let test_data = Either::Sequence(vec![
"[1,2,3]".to_owned(),
"[1,0,-1,-1,-1,1,0,1]".to_owned(),
"[1,0,-1]".to_owned(),
"[-1,0,1,2,-1,-4]".to_owned(),
"[0,1,1]".to_owned(),
"[0,0,0]".to_owned(),
]);
let json_value = serde_json::from_str(
r#"{
"status_code": 10,
"lang": "java",
"run_success": true,
"status_runtime": "3 ms",
"memory": 40404000,
"display_runtime": "3",
"code_answer": [
"[]",
"[[-1,0,1]]",
"[[-1,0,1]]",
"[[-1,-1,2],[-1,0,1]]",
"[]",
"[[0,0,0]]"
],
"code_output": [],
"std_output_list": [
"",
"",
"",
"",
"",
"",
""
],
"elapsed_time": 217,
"task_finish_time": 1693886584999,
"task_name": "judger.runcodetask.RunCode",
"expected_status_code": 10,
"expected_lang": "cpp",
"expected_run_success": true,
"expected_status_runtime": "0",
"expected_memory": 6304000,
"expected_code_answer": [
"[]",
"[[-1,0,1]]",
"[[-1,0,1]]",
"[[-1,-1,2],[-1,0,1]]",
"[]",
"[[0,0,0]]"
],
"expected_code_output": [],
"expected_std_output_list": [
"",
"",
"",
"",
"",
"",
""
],
"expected_elapsed_time": 21,
"expected_task_finish_time": 1693885714905,
"expected_task_name": "judger.interprettask.Interpret",
"correct_answer": true,
"compare_result": "111111",
"total_correct": 6,
"total_testcases": 6,
"runtime_percentile": null,
"status_memory": "40.4 MB",
"memory_percentile": null,
"pretty_lang": "Java",
"submission_id": "runcode_1693886582.3348386_GUkqbCdnmN",
"status_msg": "Accepted",
"state": "SUCCESS"
}"#,
)
.unwrap();
let response = from_value::<SubmissionResponse>(json_value).unwrap().into();
let result = TestExecutionResult::new(test_data, response);
result.print();
// TODO implement snapshot testing
assert!(1 == 1);
}
#[test]
fn print_sequence_wrong_answer() {
let test_data = Either::Sequence(vec![
"[1,2,3]".to_owned(),
"[1,0,-1,-1,-1,1,0,1]".to_owned(),
"[1,0,-1]".to_owned(),
"[-1,0,1,2,-1,-4]".to_owned(),
"[0,1,1]".to_owned(),
"[0,0,0]".to_owned(),
]);
let json_value = serde_json::from_str(
r#"{
"status_code": 10,
"lang": "java",
"run_success": true,
"status_runtime": "2 ms",
"memory": 40560000,
"display_runtime": "2",
"code_answer": [
"[]",
"[[-1,0,1]]",
"[[-1,0,1]]",
"[[-1,-1,2]]",
"[]",
"[[0,0,0]]"
],
"code_output": [],
"std_output_list": [
"",
"",
"",
"",
"",
"",
""
],
"elapsed_time": 229,
"task_finish_time": 1693885714946,
"task_name": "judger.runcodetask.RunCode",
"expected_status_code": 10,
"expected_lang": "cpp",
"expected_run_success": true,
"expected_status_runtime": "0",
"expected_memory": 6304000,
"expected_code_answer": [
"[]",
"[[-1,0,1]]",
"[[-1,0,1]]",
"[[-1,-1,2],[-1,0,1]]",
"[]",
"[[0,0,0]]"
],
"expected_code_output": [],
"expected_std_output_list": [
"",
"",
"",
"",
"",
"",
""
],
"expected_elapsed_time": 21,
"expected_task_finish_time": 1693885714905,
"expected_task_name": "judger.interprettask.Interpret",
"correct_answer": false,
"compare_result": "111011",
"total_correct": 5,
"total_testcases": 6,
"runtime_percentile": null,
"status_memory": "40.6 MB",
"memory_percentile": null,
"pretty_lang": "Java",
"submission_id": "runcode_1693885710.9158015_9uDhRiWjFV",
"status_msg": "Accepted",
"state": "SUCCESS"
}"#,
)
.unwrap();
let response = from_value::<SubmissionResponse>(json_value).unwrap().into();
let result = TestExecutionResult::new(test_data, response);
result.print();
// TODO implement snapshot testing
assert!(1 == 1);
}
#[test]
fn print_multi_input_accepted() {
let test_data = Either::Sequence(vec![
"aa".to_owned(),
"a".to_owned(),
"aa".to_owned(),
"a*".to_owned(),
"ab".to_owned(),
".*".to_owned(),
]);
let json_value = serde_json::from_str(
r#"{"status_code": 10, "lang": "rust", "run_success": true, "status_runtime": "0 ms", "memory": 2096000, "code_answer": ["false", "true", "true"], "code_output": [], "std_output_list": ["", "", "", ""], "elapsed_time": 39, "task_finish_time": 1694281117287, "task_name": "judger.runcodetask.RunCode", "expected_status_code": 10, "expected_lang": "cpp", "expected_run_success": true, "expected_status_runtime": "0", "expected_memory": 5936000, "expected_code_answer": ["false", "true", "true"], "expected_code_output": [], "expected_std_output_list": ["", "", "", ""], "expected_elapsed_time": 37, "expected_task_finish_time": 1694281041897, "expected_task_name": "judger.interprettask.Interpret", "correct_answer": true, "compare_result": "111", "total_correct": 3, "total_testcases": 3, "runtime_percentile": null, "status_memory": "2.1 MB", "memory_percentile": null, "pretty_lang": "Rust", "submission_id": "runcode_1694281112.034828_DfKA6OnxO1", "status_msg": "Accepted", "state": "SUCCESS"}"#
)
.unwrap();
let response = from_value::<SubmissionResponse>(json_value).unwrap().into();
let result = TestExecutionResult::new(test_data, response);
result.print();
// TODO implement snapshot testing
assert!(1 == 1);
}
#[test]
fn print_multi_input_wrong_answer() {
let test_data = Either::Sequence(vec![
"aa".to_owned(),
"a".to_owned(),
"aa".to_owned(),
"a*".to_owned(),
"ab".to_owned(),
".*".to_owned(),
]);
let json_value = serde_json::from_str(
r#"{"status_code": 10, "lang": "rust", "run_success": true, "status_runtime": "1 ms", "memory": 2000000, "code_answer": ["false", "false", "false"], "code_output": [], "std_output_list": ["", "", "", ""], "elapsed_time": 16, "task_finish_time": 1694281884439, "task_name": "judger.runcodetask.RunCode", "expected_status_code": 10, "expected_lang": "cpp", "expected_run_success": true, "expected_status_runtime": "0", "expected_memory": 5936000, "expected_code_answer": ["false", "true", "true"], "expected_code_output": [], "expected_std_output_list": ["", "", "", ""], "expected_elapsed_time": 37, "expected_task_finish_time": 1694281041897, "expected_task_name": "judger.interprettask.Interpret", "correct_answer": false, "compare_result": "100", "total_correct": 1, "total_testcases": 3, "runtime_percentile": null, "status_memory": "2 MB", "memory_percentile": null, "pretty_lang": "Rust", "submission_id": "runcode_1694281880.8477898_KECPBvM8Vm", "status_msg": "Accepted", "state": "SUCCESS"}"#
)
.unwrap();
let response = from_value::<SubmissionResponse>(json_value).unwrap().into();
let result = TestExecutionResult::new(test_data, response);
result.print();
// TODO implement snapshot testing
assert!(1 == 1);
}
}