From 45b085a64f35773b18dec6ad2c02c1fb87e783b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E5=90=91=E5=A4=9C?= <46275354+fu050409@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:36:44 +0800 Subject: [PATCH] feat(lang): support java and golang (#11) --- .changes/lang.md | 5 +++ .cspell.json | 1 + Cargo.lock | 2 +- package.json | 3 +- src/case.rs | 3 ++ src/compile.rs | 36 ++++++++++++--- tests/test.c | 12 +++++ tests/test.go | 17 +++++++ tests/test.java | 17 +++++++ tests/test_fs.rs | 2 +- tests/test_judge.rs | 106 +++++++++++++++++++++++++++++++++++++++++--- 11 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 .changes/lang.md create mode 100644 tests/test.c create mode 100644 tests/test.go create mode 100644 tests/test.java diff --git a/.changes/lang.md b/.changes/lang.md new file mode 100644 index 0000000..37e9e42 --- /dev/null +++ b/.changes/lang.md @@ -0,0 +1,5 @@ +--- +"eval-stack": patch:feat +--- + +Support for Java and Golang. diff --git a/.cspell.json b/.cspell.json index 998ea55..1965964 100644 --- a/.cspell.json +++ b/.cspell.json @@ -6,6 +6,7 @@ "covector", "fmax", "getuid", + "javac", "libc", "NEWNS", "prctl", diff --git a/Cargo.lock b/Cargo.lock index 3aa1f8a..aeebed6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,7 +80,7 @@ dependencies = [ [[package]] name = "eval-stack" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anyhow", "libc", diff --git a/package.json b/package.json index 83bf83c..23784e3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Online Judge core runtime for ACMers", "main": "index.js", "scripts": { - "covector": "covector" + "covector": "covector", + "test": "cargo test" }, "keywords": [ "oj", diff --git a/src/case.rs b/src/case.rs index baaaf22..77a9c8b 100644 --- a/src/case.rs +++ b/src/case.rs @@ -34,6 +34,7 @@ where let exec_path = match &language { Language::Python => which("python")?, Language::NodeJs => which("deno")?, + Language::Java => which("java")?, _ => workspace.join("out"), }; @@ -65,9 +66,11 @@ where "--deny-ffi=*", source_file_path.as_str(), ]; + let java_args = vec!["Main"]; let args = match language { Language::Python => Some(&py_args), Language::NodeJs => Some(&deno_args), + Language::Java => Some(&java_args), _ => None, } .map(|v| &**v); diff --git a/src/compile.rs b/src/compile.rs index f2fee82..a14ac18 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -5,27 +5,30 @@ use std::{ }; use anyhow::Result; -use tokio::process::Command; +use tokio::{fs::File, io, process::Command}; -#[derive(Clone, Copy)] +#[derive(Default, Debug, Clone, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum Language { + #[default] + Rust, C, CPP, - Rust, Python, NodeJs, + Golang, + Java, } -pub async fn compile, E: AsRef, O: AsRef>( +pub async fn compile, S: Into, O: AsRef>( language: Language, base: B, - source_file: E, + source_file_path: S, output_file: O, ) -> Result<()> { let base_path = Into::::into(base); - let source_path = base_path.join(source_file.as_ref()); + let source_path = Into::::into(source_file_path); let source_path_str = source_path.to_string_lossy(); let output_path = base_path.join(output_file.as_ref()); let output_path_str = output_path.to_string_lossy(); @@ -84,6 +87,27 @@ pub async fn compile, E: AsRef, O: AsRef>( Some(command) } Language::NodeJs => None, + Language::Golang => { + let mut command = Command::new("go"); + command.args([ + "build", + "-o", + output_path_str.as_ref(), + source_path_str.as_ref(), + ]); + Some(command) + } + Language::Java => { + let java_path = base_path.join("Main.java"); + let mut command = Command::new("javac"); + io::copy( + &mut File::open(source_path_str.as_ref()).await?, + &mut File::create(&java_path).await?, + ) + .await?; + command.arg(java_path.to_string_lossy().as_ref()); + Some(command) + } }; if let Some(mut command) = command { diff --git a/tests/test.c b/tests/test.c new file mode 100644 index 0000000..eeceadc --- /dev/null +++ b/tests/test.c @@ -0,0 +1,12 @@ +#include + +typedef long long int i64; + +int main() +{ + i64 a, b; + scanf("%lld %lld", &a, &b); + printf("%lld\n", a + b); + printf("%lld\n", a + b); + return 0; +} \ No newline at end of file diff --git a/tests/test.go b/tests/test.go new file mode 100644 index 0000000..ab58440 --- /dev/null +++ b/tests/test.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" +) + +func main() { + var num1, num2 int32 + + fmt.Scan(&num1) + + fmt.Scan(&num2) + + sum := num1 + num2 + fmt.Println(sum) + fmt.Println(sum) +} diff --git a/tests/test.java b/tests/test.java new file mode 100644 index 0000000..3d9024e --- /dev/null +++ b/tests/test.java @@ -0,0 +1,17 @@ +import java.util.Scanner; + +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + int num1 = scanner.nextInt(); + + int num2 = scanner.nextInt(); + + int sum = num1 + num2; + System.out.printf("%d\n", sum); + System.out.printf("%d", sum); + + scanner.close(); + } +} diff --git a/tests/test_fs.rs b/tests/test_fs.rs index aa7f437..02a49a5 100644 --- a/tests/test_fs.rs +++ b/tests/test_fs.rs @@ -6,7 +6,7 @@ use eval_stack::{case::run_test_cases, compile::Language, config::JudgeOptions}; #[tokio::test] async fn test_fs() -> Result<()> { let current_dir = std::env::current_dir()?; - let workspace_path = current_dir.join("workspace"); + let workspace_path = current_dir.join("fs_workspace"); let tests_path = current_dir.join("tests"); let results = run_test_cases( diff --git a/tests/test_judge.rs b/tests/test_judge.rs index 654372b..58a8ee0 100644 --- a/tests/test_judge.rs +++ b/tests/test_judge.rs @@ -6,7 +6,7 @@ use eval_stack::{case::run_test_cases, compile::Language, config::JudgeOptions}; #[tokio::test] async fn test_rust_judge() -> Result<()> { let current_dir = std::env::current_dir()?; - let workspace_path = current_dir.join("workspace"); + let workspace_path = current_dir.join("rust_workspace"); let tests_path = current_dir.join("tests"); let results = run_test_cases( @@ -61,7 +61,7 @@ async fn test_rust_judge() -> Result<()> { #[tokio::test] async fn test_cpp_judge() -> Result<()> { let current_dir = std::env::current_dir()?; - let workspace_path = current_dir.join("workspace"); + let workspace_path = current_dir.join("cpp_workspace"); let tests_path = current_dir.join("tests"); let results = run_test_cases( @@ -90,10 +90,42 @@ async fn test_cpp_judge() -> Result<()> { Ok(()) } +#[tokio::test] +async fn test_c_judge() -> Result<()> { + let current_dir = std::env::current_dir()?; + let workspace_path = current_dir.join("c_workspace"); + let tests_path = current_dir.join("tests"); + + let results = run_test_cases( + Language::C, + &workspace_path, + &tests_path.join("test.c"), + JudgeOptions { + time_limit: Duration::from_secs(1), + memory_limit: 128 * 1024 * 1024, + fail_fast: true, + no_startup_limits: false, + }, + vec![ + (tests_path.join("1.in"), tests_path.join("1.out")), + (tests_path.join("2.in"), tests_path.join("2.out")), + ], + true, + ) + .await?; + + for result in results { + println!("{:?}", result); + assert!(result.is_accepted()) + } + + Ok(()) +} + #[tokio::test] async fn test_python_judge() -> Result<()> { let current_dir = std::env::current_dir()?; - let workspace_path = current_dir.join("workspace"); + let workspace_path = current_dir.join("python_workspace"); let tests_path = current_dir.join("tests"); let results = run_test_cases( @@ -125,7 +157,7 @@ async fn test_python_judge() -> Result<()> { #[tokio::test] async fn test_nodejs_judge() -> Result<()> { let current_dir = std::env::current_dir()?; - let workspace_path = current_dir.join("workspace"); + let workspace_path = current_dir.join("nodejs_workspace"); let tests_path = current_dir.join("tests"); let results = run_test_cases( @@ -142,7 +174,71 @@ async fn test_nodejs_judge() -> Result<()> { (tests_path.join("1.in"), tests_path.join("1.out")), (tests_path.join("2.in"), tests_path.join("2.out")), ], - false, + true, + ) + .await?; + + for result in results { + println!("{:?}", result); + assert!(result.is_accepted()) + } + + Ok(()) +} + +#[tokio::test] +async fn test_golang_judge() -> Result<()> { + let current_dir = std::env::current_dir()?; + let workspace_path = current_dir.join("golang_workspace"); + let tests_path = current_dir.join("tests"); + + let results = run_test_cases( + Language::Golang, + &workspace_path, + &tests_path.join("test.go"), + JudgeOptions { + time_limit: Duration::from_secs(1), + memory_limit: 128 * 1024 * 1024, + fail_fast: true, + no_startup_limits: true, + }, + vec![ + (tests_path.join("1.in"), tests_path.join("1.out")), + (tests_path.join("2.in"), tests_path.join("2.out")), + ], + true, + ) + .await?; + + for result in results { + println!("{:?}", result); + assert!(result.is_accepted()) + } + + Ok(()) +} + +#[tokio::test] +async fn test_java_judge() -> Result<()> { + let current_dir = std::env::current_dir()?; + let workspace_path = current_dir.join("java_workspace"); + let tests_path = current_dir.join("tests"); + + let results = run_test_cases( + Language::Java, + &workspace_path, + &tests_path.join("test.java"), + JudgeOptions { + time_limit: Duration::from_secs(1), + memory_limit: 128 * 1024 * 1024, + fail_fast: true, + no_startup_limits: true, + }, + vec![ + (tests_path.join("1.in"), tests_path.join("1.out")), + (tests_path.join("2.in"), tests_path.join("2.out")), + ], + true, ) .await?;