From 16510112749d0b9fdcdf5e3cde017135e32e91f7 Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 16:20:13 +0700 Subject: [PATCH 1/7] feat(unit-test): util function for testing --- src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8c09304..7d69959 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,3 +22,37 @@ pub struct Language<'a> { pub compiler: Compiler<'a>, pub runner_args: CommandArgs<'a>, } + +#[cfg(test)] +mod test { + use std::{fs, path::Path}; + + use crate::Language; + + pub fn read_test_cases(problem_path: &Path) -> Vec<(Vec, Vec)> { + let test_cases: Vec<_> = fs::read_dir(problem_path) + .unwrap() + .filter_map(|entry| { + let entry = entry.unwrap(); + let path = entry.path(); + if !path.is_dir() { + return None; + } + + let input_path = path.join("in.txt"); + let input = fs::read(input_path).unwrap(); + + let output_path = path.join("out.txt"); + let output = fs::read(output_path).unwrap(); + + Some((input, output)) + }) + .collect(); + + test_cases + } + + pub fn read_code(language: Language, problem_path: &Path) -> Vec { + fs::read(problem_path.join(language.compiler.main_file)).unwrap() + } +} From d66cc2c3b180c8fe94983db355372b613727ce3a Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 16:25:31 +0700 Subject: [PATCH 2/7] feat(test): add correct case for code run --- src/runner.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/runner.rs b/src/runner.rs index 851dea5..3538192 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -126,3 +126,43 @@ impl<'a> Runner<'a> { }) } } + +#[cfg(test)] +mod test { + use std::{path::PathBuf, time::Duration}; + + use bstr::ByteSlice; + use rstest::rstest; + + use crate::{ + CPP, JAVA, Language, PYTHON, RUST, Runner, + test::{read_code, read_test_cases}, + }; + + #[rstest] + #[tokio::test] + async fn test_code_run_should_output_correct( + #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, + #[files("tests/problem/*")] problem_path: PathBuf, + ) { + let test_cases = read_test_cases(&problem_path); + + let code = read_code(language, &problem_path); + let project_path = language.compiler.compile(&code).await.unwrap(); + + let runner = Runner::new( + language.runner_args, + &project_path, + Duration::from_secs(2), + i64::MAX, + 512, + ) + .unwrap(); + for (input, output) in test_cases { + let metrics = runner.run(&input).await.unwrap(); + let metrics_out = metrics.stdout.trim(); + let test_case_out = output.trim(); + assert_eq!(metrics_out, test_case_out); + } + } +} From 66952bd6edebe6c77cd48f69836e92cb8a8da76e Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 16:28:52 +0700 Subject: [PATCH 3/7] feat(test): add timeout test case --- src/runner.rs | 63 ++++++++++++++++++++++++++++++++++++++++- tests/should_timeout.rs | 2 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 3538192..8f4d8b1 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -141,7 +141,7 @@ mod test { #[rstest] #[tokio::test] - async fn test_code_run_should_output_correct( + async fn should_output_correct( #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, #[files("tests/problem/*")] problem_path: PathBuf, ) { @@ -165,4 +165,65 @@ mod test { assert_eq!(metrics_out, test_case_out); } } + + fn get_timeout_code(language: Language<'static>) -> &'static str { + match language { + CPP => { + r#" +#include + +using namespace std; + +int main() { + while(true) {} +} + "# + } + RUST => { + r#" +fn main() { + loop {} +} + "# + } + JAVA => { + r#" +class Main { + public static void main(String[] args) { + while(true) {} + } +} + "# + } + PYTHON => { + r#" +while True: + continue + "# + } + _ => unreachable!(), + } + } + + #[rstest] + #[tokio::test] + async fn should_timeout(#[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>) { + use crate::ExitStatus; + + let code = get_timeout_code(language); + let project_path = language.compiler.compile(code.as_bytes()).await.unwrap(); + + let runner = Runner::new( + language.runner_args, + &project_path, + Duration::from_secs(2), + i64::MAX, + 512, + ) + .unwrap(); + + let metrics = runner.run(b"").await.unwrap(); + + assert_eq!(metrics.exit_status, ExitStatus::Timeout) + } } diff --git a/tests/should_timeout.rs b/tests/should_timeout.rs index 9bfcbcc..814dd97 100644 --- a/tests/should_timeout.rs +++ b/tests/should_timeout.rs @@ -44,7 +44,7 @@ while True: #[rstest] #[tokio::test] -async fn should_output_correct(#[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>) { +async fn should_timeout(#[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>) { let code = get_timeout_code(language); let project_path = language.compiler.compile(code.as_bytes()).await.unwrap(); From c8b5c050dbc5c64815c8f9000a7188355c18abe3 Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 16:44:28 +0700 Subject: [PATCH 4/7] refactor(test): re use read_code function --- src/runner.rs | 52 +++---------------- .../problem/maximum-sum-of-digits/1/in.txt | 0 .../problem/maximum-sum-of-digits/1/out.txt | 0 .../problem/maximum-sum-of-digits/2/in.txt | 0 .../problem/maximum-sum-of-digits/2/out.txt | 0 .../problem/maximum-sum-of-digits/3/in.txt | 0 .../problem/maximum-sum-of-digits/3/out.txt | 0 .../problem/maximum-sum-of-digits/4/in.txt | 0 .../problem/maximum-sum-of-digits/4/out.txt | 0 .../problem/maximum-sum-of-digits/5/in.txt | 0 .../problem/maximum-sum-of-digits/5/out.txt | 0 .../problem/maximum-sum-of-digits/Main.java | 0 .../problem/maximum-sum-of-digits/main.cpp | 0 .../problem/maximum-sum-of-digits/main.py | 0 .../problem/maximum-sum-of-digits/main.rs | 0 tests/data/timeout/Main.java | 5 ++ tests/data/timeout/main.cpp | 7 +++ tests/data/timeout/main.py | 2 + tests/data/timeout/main.rs | 3 ++ tests/should_output_correct.rs | 2 +- tests/should_timeout.rs | 46 +++------------- tests/util/mod.rs | 3 ++ 22 files changed, 34 insertions(+), 86 deletions(-) rename tests/{ => data}/problem/maximum-sum-of-digits/1/in.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/1/out.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/2/in.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/2/out.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/3/in.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/3/out.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/4/in.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/4/out.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/5/in.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/5/out.txt (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/Main.java (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/main.cpp (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/main.py (100%) rename tests/{ => data}/problem/maximum-sum-of-digits/main.rs (100%) create mode 100644 tests/data/timeout/Main.java create mode 100644 tests/data/timeout/main.cpp create mode 100644 tests/data/timeout/main.py create mode 100644 tests/data/timeout/main.rs diff --git a/src/runner.rs b/src/runner.rs index 8f4d8b1..f13b6bc 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -129,13 +129,16 @@ impl<'a> Runner<'a> { #[cfg(test)] mod test { - use std::{path::PathBuf, time::Duration}; + use std::{ + path::{Path, PathBuf}, + time::Duration, + }; use bstr::ByteSlice; use rstest::rstest; use crate::{ - CPP, JAVA, Language, PYTHON, RUST, Runner, + CPP, ExitStatus, JAVA, Language, PYTHON, RUST, Runner, test::{read_code, read_test_cases}, }; @@ -143,7 +146,7 @@ mod test { #[tokio::test] async fn should_output_correct( #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, - #[files("tests/problem/*")] problem_path: PathBuf, + #[files("tests/data/problem/*")] problem_path: PathBuf, ) { let test_cases = read_test_cases(&problem_path); @@ -166,51 +169,10 @@ mod test { } } - fn get_timeout_code(language: Language<'static>) -> &'static str { - match language { - CPP => { - r#" -#include - -using namespace std; - -int main() { - while(true) {} -} - "# - } - RUST => { - r#" -fn main() { - loop {} -} - "# - } - JAVA => { - r#" -class Main { - public static void main(String[] args) { - while(true) {} - } -} - "# - } - PYTHON => { - r#" -while True: - continue - "# - } - _ => unreachable!(), - } - } - #[rstest] #[tokio::test] async fn should_timeout(#[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>) { - use crate::ExitStatus; - - let code = get_timeout_code(language); + let code = read_code(language, Path::new("tests/data/timeout")); let project_path = language.compiler.compile(code.as_bytes()).await.unwrap(); let runner = Runner::new( diff --git a/tests/problem/maximum-sum-of-digits/1/in.txt b/tests/data/problem/maximum-sum-of-digits/1/in.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/1/in.txt rename to tests/data/problem/maximum-sum-of-digits/1/in.txt diff --git a/tests/problem/maximum-sum-of-digits/1/out.txt b/tests/data/problem/maximum-sum-of-digits/1/out.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/1/out.txt rename to tests/data/problem/maximum-sum-of-digits/1/out.txt diff --git a/tests/problem/maximum-sum-of-digits/2/in.txt b/tests/data/problem/maximum-sum-of-digits/2/in.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/2/in.txt rename to tests/data/problem/maximum-sum-of-digits/2/in.txt diff --git a/tests/problem/maximum-sum-of-digits/2/out.txt b/tests/data/problem/maximum-sum-of-digits/2/out.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/2/out.txt rename to tests/data/problem/maximum-sum-of-digits/2/out.txt diff --git a/tests/problem/maximum-sum-of-digits/3/in.txt b/tests/data/problem/maximum-sum-of-digits/3/in.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/3/in.txt rename to tests/data/problem/maximum-sum-of-digits/3/in.txt diff --git a/tests/problem/maximum-sum-of-digits/3/out.txt b/tests/data/problem/maximum-sum-of-digits/3/out.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/3/out.txt rename to tests/data/problem/maximum-sum-of-digits/3/out.txt diff --git a/tests/problem/maximum-sum-of-digits/4/in.txt b/tests/data/problem/maximum-sum-of-digits/4/in.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/4/in.txt rename to tests/data/problem/maximum-sum-of-digits/4/in.txt diff --git a/tests/problem/maximum-sum-of-digits/4/out.txt b/tests/data/problem/maximum-sum-of-digits/4/out.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/4/out.txt rename to tests/data/problem/maximum-sum-of-digits/4/out.txt diff --git a/tests/problem/maximum-sum-of-digits/5/in.txt b/tests/data/problem/maximum-sum-of-digits/5/in.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/5/in.txt rename to tests/data/problem/maximum-sum-of-digits/5/in.txt diff --git a/tests/problem/maximum-sum-of-digits/5/out.txt b/tests/data/problem/maximum-sum-of-digits/5/out.txt similarity index 100% rename from tests/problem/maximum-sum-of-digits/5/out.txt rename to tests/data/problem/maximum-sum-of-digits/5/out.txt diff --git a/tests/problem/maximum-sum-of-digits/Main.java b/tests/data/problem/maximum-sum-of-digits/Main.java similarity index 100% rename from tests/problem/maximum-sum-of-digits/Main.java rename to tests/data/problem/maximum-sum-of-digits/Main.java diff --git a/tests/problem/maximum-sum-of-digits/main.cpp b/tests/data/problem/maximum-sum-of-digits/main.cpp similarity index 100% rename from tests/problem/maximum-sum-of-digits/main.cpp rename to tests/data/problem/maximum-sum-of-digits/main.cpp diff --git a/tests/problem/maximum-sum-of-digits/main.py b/tests/data/problem/maximum-sum-of-digits/main.py similarity index 100% rename from tests/problem/maximum-sum-of-digits/main.py rename to tests/data/problem/maximum-sum-of-digits/main.py diff --git a/tests/problem/maximum-sum-of-digits/main.rs b/tests/data/problem/maximum-sum-of-digits/main.rs similarity index 100% rename from tests/problem/maximum-sum-of-digits/main.rs rename to tests/data/problem/maximum-sum-of-digits/main.rs diff --git a/tests/data/timeout/Main.java b/tests/data/timeout/Main.java new file mode 100644 index 0000000..f918b4a --- /dev/null +++ b/tests/data/timeout/Main.java @@ -0,0 +1,5 @@ +class Main { + public static void main(String[] args) { + while(true) {} + } +} diff --git a/tests/data/timeout/main.cpp b/tests/data/timeout/main.cpp new file mode 100644 index 0000000..cedc29e --- /dev/null +++ b/tests/data/timeout/main.cpp @@ -0,0 +1,7 @@ +#include + +using namespace std; + +int main() { + while(true) {} +} diff --git a/tests/data/timeout/main.py b/tests/data/timeout/main.py new file mode 100644 index 0000000..c20082d --- /dev/null +++ b/tests/data/timeout/main.py @@ -0,0 +1,2 @@ +while True: + continue diff --git a/tests/data/timeout/main.rs b/tests/data/timeout/main.rs new file mode 100644 index 0000000..b181381 --- /dev/null +++ b/tests/data/timeout/main.rs @@ -0,0 +1,3 @@ +fn main() { + loop {} +} diff --git a/tests/should_output_correct.rs b/tests/should_output_correct.rs index 70d4660..5232e3f 100644 --- a/tests/should_output_correct.rs +++ b/tests/should_output_correct.rs @@ -11,7 +11,7 @@ use util::{Problem, read_code}; #[tokio::test] async fn should_output_correct( #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, - #[files("tests/problem/*")] problem_path: PathBuf, + #[files("tests/data/problem/*")] problem_path: PathBuf, ) { let problem: Problem = problem_path.as_path().into(); diff --git a/tests/should_timeout.rs b/tests/should_timeout.rs index 814dd97..ab07059 100644 --- a/tests/should_timeout.rs +++ b/tests/should_timeout.rs @@ -1,51 +1,17 @@ -use std::time::Duration; +mod util; +use std::{path::Path, time::Duration}; + +use bstr::ByteSlice; use code_executor::*; use rstest::rstest; -fn get_timeout_code(language: Language<'static>) -> &'static str { - match language { - CPP => { - r#" -#include - -using namespace std; - -int main() { - while(true) {} -} - "# - } - RUST => { - r#" -fn main() { - loop {} -} - "# - } - JAVA => { - r#" -class Main { - public static void main(String[] args) { - while(true) {} - } -} - "# - } - PYTHON => { - r#" -while True: - continue - "# - } - _ => unreachable!(), - } -} +use util::read_code; #[rstest] #[tokio::test] async fn should_timeout(#[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>) { - let code = get_timeout_code(language); + let code = read_code(language, Path::new("tests/data/timeout")); let project_path = language.compiler.compile(code.as_bytes()).await.unwrap(); let runner = Runner::new( diff --git a/tests/util/mod.rs b/tests/util/mod.rs index f85f74c..6b22080 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -2,11 +2,13 @@ use std::{fs, path::Path}; use code_executor::Language; +#[allow(unused)] pub struct TestCase { pub input: Vec, pub output: Vec, } +#[allow(unused)] pub struct Problem { pub test_cases: Vec, } @@ -36,6 +38,7 @@ impl From<&Path> for Problem { } } +#[allow(unused)] pub fn read_code(language: Language, problem_path: &Path) -> Vec { fs::read(problem_path.join(language.compiler.main_file)).unwrap() } From 8d364f372022a7a9b6e5ba6cb27ab8b4da3aa79b Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 16:54:02 +0700 Subject: [PATCH 5/7] feat(test): add compilation test --- src/compiler.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/compiler.rs b/src/compiler.rs index 61179c4..e9b8e9d 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -60,3 +60,38 @@ impl Compiler<'_> { Ok(project_path) } } + +#[cfg(test)] +mod test { + use std:: + path::Path + ; + + use bstr::ByteSlice; + use rstest::rstest; + + use crate::{CPP, JAVA, Language, PYTHON, RUST, test::read_code}; + + const EXAMPLE_CODE_DIR: &str = "tests/data/timeout"; + + #[rstest] + #[tokio::test] + async fn should_create_valid_project_directory( + #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, + ) { + let code = read_code(language, Path::new(EXAMPLE_CODE_DIR)); + let project_path = language.compiler.create_project(code.as_bytes()).await.unwrap(); + let main_path = project_path.join(language.compiler.main_file); + + assert!(main_path.exists()) + } + + #[rstest] + #[tokio::test] + async fn should_compile_successfully( + #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, + ) { + let code = read_code(language, Path::new(EXAMPLE_CODE_DIR)); + assert!(language.compiler.compile(code.as_bytes()).await.ok()); + } +} From ec4d5be8cc742caa4330cd78de8ec53f0afc0c5d Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 16:59:26 +0700 Subject: [PATCH 6/7] fix: use is_ok instead --- src/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler.rs b/src/compiler.rs index e9b8e9d..6623707 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -92,6 +92,6 @@ mod test { #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, ) { let code = read_code(language, Path::new(EXAMPLE_CODE_DIR)); - assert!(language.compiler.compile(code.as_bytes()).await.ok()); + assert!(language.compiler.compile(code.as_bytes()).await.is_ok()); } } From 8024c2830d812e6628841900fd3bed8c2383a82f Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Mon, 2 Jun 2025 17:02:10 +0700 Subject: [PATCH 7/7] fix: reformat --- src/compiler.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 6623707..8e246d9 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -63,9 +63,7 @@ impl Compiler<'_> { #[cfg(test)] mod test { - use std:: - path::Path - ; + use std::path::Path; use bstr::ByteSlice; use rstest::rstest; @@ -80,7 +78,11 @@ mod test { #[values(CPP, RUST, JAVA, PYTHON)] language: Language<'static>, ) { let code = read_code(language, Path::new(EXAMPLE_CODE_DIR)); - let project_path = language.compiler.create_project(code.as_bytes()).await.unwrap(); + let project_path = language + .compiler + .create_project(code.as_bytes()) + .await + .unwrap(); let main_path = project_path.join(language.compiler.main_file); assert!(main_path.exists())