Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions languages/java/tasks.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[
{
"label": "Run $ZED_CUSTOM_java_class_name",
"command": "pkg=\"${ZED_CUSTOM_java_package_name:}\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; if [ -f pom.xml ]; then [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; $CMD clean compile exec:java -Dexec.mainClass=\"$c\"; elif [ -f build.gradle ] || [ -f build.gradle.kts ]; then [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;",
"command": "pkg=\"$ZED_CUSTOM_java_package_name\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test-compile exec:java -Dexec.mainClass=\"$c\" -Dexec.classpathScope=test; else $CMD clean test-compile -pl \"$m\" -am && $CMD exec:java -pl \"$m\" -Dexec.mainClass=\"$c\" -Dexec.classpathScope=test; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using test-compile for running the main class alongside providing the tests' scope?

"use_new_terminal": false,
"reveal": "always",
"tags": ["java-main"],
Expand All @@ -14,7 +14,7 @@
},
{
"label": "$ZED_CUSTOM_java_class_name.${ZED_CUSTOM_java_outer_class_name:}.$ZED_CUSTOM_java_method_name",
"command": "package=\"$ZED_CUSTOM_java_package_name\"; outer=\"${ZED_CUSTOM_java_outer_class_name:}\"; inner=\"$ZED_CUSTOM_java_class_name\"; method=\"$ZED_CUSTOM_java_method_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; if [ -f pom.xml ]; then [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; $CMD clean test -Dtest=\"$package.$c#$method\"; elif [ -f build.gradle ] || [ -f build.gradle.kts ]; then [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD test --tests \"$package.$c.$method\"; else >&2 echo 'No build tool found'; exit 1; fi;",
"command": "package=\"$ZED_CUSTOM_java_package_name\"; outer=\"${ZED_CUSTOM_java_outer_class_name:}\"; inner=\"$ZED_CUSTOM_java_class_name\"; method=\"$ZED_CUSTOM_java_method_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test -Dtest=\"$package.$c#$method\"; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\" -Dtest=\"$package.$c#$method\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test --tests \"$package.$c.$method\"; else >&2 echo 'No build tool found'; exit 1; fi;",
"use_new_terminal": false,
"reveal": "always",
"tags": ["java-test-method", "java-test-method-nested"],
Expand All @@ -27,7 +27,7 @@
},
{
"label": "Test class $ZED_CUSTOM_java_class_name",
"command": "package=\"$ZED_CUSTOM_java_package_name\"; outer=\"${ZED_CUSTOM_java_outer_class_name:}\"; inner=\"$ZED_CUSTOM_java_class_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; if [ -f pom.xml ]; then [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; $CMD clean test -Dtest=\"$package.$c\"; elif [ -f build.gradle ] || [ -f build.gradle.kts ]; then [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD test --tests \"$package.$c\"; else >&2 echo 'No build tool found'; exit 1; fi;",
"command": "package=\"$ZED_CUSTOM_java_package_name\"; outer=\"${ZED_CUSTOM_java_outer_class_name:}\"; inner=\"$ZED_CUSTOM_java_class_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test -Dtest=\"$package.$c\"; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\" -Dtest=\"$package.$c\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test --tests \"$package.$c\"; else >&2 echo 'No build tool found'; exit 1; fi;",
"use_new_terminal": false,
"reveal": "always",
"tags": ["java-test-class", "java-test-class-nested"],
Expand All @@ -40,7 +40,7 @@
},
{
"label": "Run tests",
"command": "if [ -f pom.xml ]; then [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; $CMD clean test; elif [ -f build.gradle ] || [ -f build.gradle.kts ]; then [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD test; else >&2 echo 'No build tool found'; exit 1; fi;",
"command": "f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test; else >&2 echo 'No build tool found'; exit 1; fi;",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we first compiling the test to the run it for maven? Isn't that redundant?

"use_new_terminal": false,
"reveal": "always",
"shell": {
Expand Down
222 changes: 222 additions & 0 deletions tests/task_verification_test.rs
Copy link
Copy Markdown
Collaborator

@tartarughina tartarughina May 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never thought of creating a temp project like you are doing here. This is a really nice idea to make testing more robust. I would although expand the temp projects to cover all instances covered by our tasks, for example nesting and single module project

Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
use serde_json::Value;
use std::fs;
use std::path::{Path, PathBuf};

fn setup_mock_project(
temp_dir: &Path,
project_type: &str,
is_multi: bool,
) -> (PathBuf, PathBuf) {
let module_dir = if is_multi {
temp_dir.join("module-a")
} else {
temp_dir.to_path_buf()
};

let package_dir = if project_type == "maven" && is_multi {
module_dir.join("src/test/java/com/example")
} else {
module_dir.join("src/main/java/com/example")
};

fs::create_dir_all(&package_dir).unwrap();

let bin_dir = temp_dir.join("bin");
fs::create_dir_all(&bin_dir).unwrap();

if project_type == "maven" {
fs::File::create(temp_dir.join("pom.xml")).unwrap();
if is_multi {
fs::File::create(module_dir.join("pom.xml")).unwrap();
}
let mvn_mock = bin_dir.join("mvn");
fs::write(&mvn_mock, "#!/bin/sh\necho \"MVN_CALLED: $@\"").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&mvn_mock, fs::Permissions::from_mode(0o755)).unwrap();
}
} else {
fs::File::create(temp_dir.join("settings.gradle")).unwrap();
if is_multi {
fs::File::create(module_dir.join("build.gradle")).unwrap();
}
let gradle_mock = bin_dir.join("gradle");
fs::write(&gradle_mock, "#!/bin/sh\necho \"GRADLE_CALLED: $@\"").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&gradle_mock, fs::Permissions::from_mode(0o755)).unwrap();
}
}

let zed_file = package_dir.join("Main.java");
fs::File::create(&zed_file).unwrap();

(zed_file, bin_dir)
}

#[test]
fn test_maven_multi_module_command_logic() {
use std::process::Command as StdCommand;

let tasks_json = fs::read_to_string("languages/java/tasks.json").expect("Failed to read tasks.json");
let tasks: Value = serde_json::from_str(&tasks_json).expect("Failed to parse tasks.json");
let mut run_command = tasks[0]["command"].as_str().expect("Command is not a string").to_string();

run_command = run_command.replace("${ZED_CUSTOM_java_outer_class_name:}", "${ZED_CUSTOM_java_outer_class_name:-}");

let temp_dir = std::env::temp_dir().join("zed_java_test_maven_logic_integration");
if temp_dir.exists() {
fs::remove_dir_all(&temp_dir).unwrap();
}
fs::create_dir_all(&temp_dir).unwrap();

let (zed_file, bin_dir) = setup_mock_project(&temp_dir, "maven", true);

let old_path = std::env::var("PATH").unwrap_or_default();
let new_path = format!("{}:{}", bin_dir.to_string_lossy(), old_path);

let output = StdCommand::new("sh")
.arg("-c")
.arg(&run_command)
.env("ZED_FILE", zed_file.to_string_lossy().to_string())
.env("PWD", temp_dir.to_string_lossy().to_string())
.env("ZED_CUSTOM_java_package_name", "com.example")
.env("ZED_CUSTOM_java_class_name", "Main")
.env("PATH", new_path)
.current_dir(&temp_dir)
.output()
.expect("Failed to execute shell command");

let stdout = String::from_utf8_lossy(&output.stdout);

assert!(stdout.contains("MVN_CALLED: clean test-compile -pl module-a -am"), "Should build submodule with dependencies. Got: {}", stdout);
assert!(stdout.contains("MVN_CALLED: exec:java -pl module-a"), "Should run only the submodule. Got: {}", stdout);
assert!(stdout.contains("-Dexec.classpathScope=test"), "Should use test classpath scope. Got: {}", stdout);

fs::remove_dir_all(&temp_dir).unwrap();
}

#[test]
fn test_maven_multi_module_test_method_logic() {
use std::process::Command as StdCommand;

let tasks_json = fs::read_to_string("languages/java/tasks.json").expect("Failed to read tasks.json");
let tasks: Value = serde_json::from_str(&tasks_json).expect("Failed to parse tasks.json");
let mut test_command = tasks[1]["command"].as_str().expect("Command is not a string").to_string();

test_command = test_command.replace("${ZED_CUSTOM_java_outer_class_name:}", "${ZED_CUSTOM_java_outer_class_name:-}");

let temp_dir = std::env::temp_dir().join("zed_java_test_maven_method_logic_integration");
if temp_dir.exists() {
fs::remove_dir_all(&temp_dir).unwrap();
}
fs::create_dir_all(&temp_dir).unwrap();

let (zed_file, bin_dir) = setup_mock_project(&temp_dir, "maven", true);

let old_path = std::env::var("PATH").unwrap_or_default();
let new_path = format!("{}:{}", bin_dir.to_string_lossy(), old_path);

let output = StdCommand::new("sh")
.arg("-c")
.arg(&test_command)
.env("ZED_FILE", zed_file.to_string_lossy().to_string())
.env("PWD", temp_dir.to_string_lossy().to_string())
.env("ZED_CUSTOM_java_package_name", "com.example")
.env("ZED_CUSTOM_java_class_name", "Main")
.env("ZED_CUSTOM_java_method_name", "shouldPersist")
.env("PATH", new_path)
.current_dir(&temp_dir)
.output()
.expect("Failed to execute shell command");

let stdout = String::from_utf8_lossy(&output.stdout);

assert!(stdout.contains("MVN_CALLED: clean test-compile -pl module-a -am"), "Should build submodule with dependencies. Got: {}", stdout);
assert!(stdout.contains("MVN_CALLED: test -pl module-a -Dtest=com.example.Main#shouldPersist"), "Should run only the submodule test. Got: {}", stdout);

fs::remove_dir_all(&temp_dir).unwrap();
}

#[test]
fn test_gradle_multi_module_command_logic() {
use std::process::Command as StdCommand;

let tasks_json = fs::read_to_string("languages/java/tasks.json").expect("Failed to read tasks.json");
let tasks: Value = serde_json::from_str(&tasks_json).expect("Failed to parse tasks.json");
let mut run_command = tasks[0]["command"].as_str().expect("Command is not a string").to_string();

run_command = run_command.replace("${ZED_CUSTOM_java_outer_class_name:}", "${ZED_CUSTOM_java_outer_class_name:-}");

let temp_dir = std::env::temp_dir().join("zed_java_test_gradle_logic_integration");
if temp_dir.exists() {
fs::remove_dir_all(&temp_dir).unwrap();
}
fs::create_dir_all(&temp_dir).unwrap();

let (zed_file, bin_dir) = setup_mock_project(&temp_dir, "gradle", true);

let old_path = std::env::var("PATH").unwrap_or_default();
let new_path = format!("{}:{}", bin_dir.to_string_lossy(), old_path);

let output = StdCommand::new("sh")
.arg("-c")
.arg(&run_command)
.env("ZED_FILE", zed_file.to_string_lossy().to_string())
.env("PWD", temp_dir.to_string_lossy().to_string())
.env("ZED_CUSTOM_java_package_name", "com.example")
.env("ZED_CUSTOM_java_class_name", "Main")
.env("PATH", new_path)
.current_dir(&temp_dir)
.output()
.expect("Failed to execute shell command");

let stdout = String::from_utf8_lossy(&output.stdout);

assert!(stdout.contains("GRADLE_CALLED: :module-a:run"), "Should run with correct module path. Got: {}", stdout);

fs::remove_dir_all(&temp_dir).unwrap();
}

#[test]
fn test_gradle_multi_module_test_method_logic() {
use std::process::Command as StdCommand;

let tasks_json = fs::read_to_string("languages/java/tasks.json").expect("Failed to read tasks.json");
let tasks: Value = serde_json::from_str(&tasks_json).expect("Failed to parse tasks.json");
let mut test_command = tasks[1]["command"].as_str().expect("Command is not a string").to_string();
Copy link
Copy Markdown
Collaborator

@tartarughina tartarughina May 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are testing only tasks at indices 0 and 1 on multi module projects, missing the remaining tasks and not checking normal projects. Also let's consider the possibility of tasks being reordered, tests would start to fail. Maybe we could retrieve the command by tag to make them more robust.


test_command = test_command.replace("${ZED_CUSTOM_java_outer_class_name:}", "${ZED_CUSTOM_java_outer_class_name:-}");

let temp_dir = std::env::temp_dir().join("zed_java_test_gradle_method_logic_integration");
if temp_dir.exists() {
fs::remove_dir_all(&temp_dir).unwrap();
}
fs::create_dir_all(&temp_dir).unwrap();

let (zed_file, bin_dir) = setup_mock_project(&temp_dir, "gradle", true);

let old_path = std::env::var("PATH").unwrap_or_default();
let new_path = format!("{}:{}", bin_dir.to_string_lossy(), old_path);

let output = StdCommand::new("sh")
.arg("-c")
.arg(&test_command)
.env("ZED_FILE", zed_file.to_string_lossy().to_string())
.env("PWD", temp_dir.to_string_lossy().to_string())
.env("ZED_CUSTOM_java_package_name", "com.example")
.env("ZED_CUSTOM_java_class_name", "Main")
.env("ZED_CUSTOM_java_method_name", "shouldPersist")
.env("PATH", new_path)
.current_dir(&temp_dir)
.output()
.expect("Failed to execute shell command");

let stdout = String::from_utf8_lossy(&output.stdout);

assert!(stdout.contains("GRADLE_CALLED: :module-a:test --tests com.example.Main.shouldPersist"), "Should run submodule test with correct path. Got: {}", stdout);

fs::remove_dir_all(&temp_dir).unwrap();
}