Skip to content
Closed
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
22 changes: 12 additions & 10 deletions codeflash/languages/java/maven_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,16 +647,18 @@ def get_text(xpath: str, default: str | None = None) -> str | None:
return None

def find_executable(self, build_root: Path) -> str | None:
mvnw_path = build_root / "mvnw"
if mvnw_path.exists():
return str(mvnw_path)
mvnw_cmd_path = build_root / "mvnw.cmd"
if mvnw_cmd_path.exists():
return str(mvnw_cmd_path)
if Path("mvnw").exists():
return "./mvnw"
if Path("mvnw.cmd").exists():
return "mvnw.cmd"
current = build_root.resolve()
while True:
mvnw_path = current / "mvnw"
if mvnw_path.exists():
return str(mvnw_path)
mvnw_cmd_path = current / "mvnw.cmd"
if mvnw_cmd_path.exists():
return str(mvnw_cmd_path)
parent = current.parent
if parent == current:
break
current = parent
return shutil.which("mvn")

def find_runtime_jar(self) -> Path | None:
Expand Down
62 changes: 56 additions & 6 deletions tests/test_languages/test_java/test_build_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,15 +388,65 @@ def test_find_wrapper_in_project_root(self, tmp_path):
assert result is not None
assert str(tmp_path / "mvnw") in result

def test_fallback_to_cwd(self, tmp_path):
def test_find_wrapper_in_parent_directory(self, tmp_path):
"""Multi-module project: mvnw at repo root, build_root is a submodule."""
repo_root = tmp_path / "repo"
repo_root.mkdir()
mvnw = repo_root / "mvnw"
mvnw.write_text("#!/bin/bash\necho Maven Wrapper")
mvnw.chmod(0o755)

submodule = repo_root / "submodule"
submodule.mkdir()
(submodule / "pom.xml").write_text("<project/>")

strategy = MavenStrategy()
result = strategy.find_executable(tmp_path)
# Should not crash even with a dir that has no wrapper
result = strategy.find_executable(submodule.resolve())
assert result is not None
assert Path(result).is_absolute(), f"Expected absolute path, got: {result}"
assert Path(result).exists(), f"Returned path does not exist: {result}"
assert result == str(mvnw.resolve())

def test_find_wrapper_in_grandparent_directory(self, tmp_path):
"""Deeply nested submodule: mvnw two levels up."""
repo_root = tmp_path / "repo"
repo_root.mkdir()
mvnw = repo_root / "mvnw"
mvnw.write_text("#!/bin/bash\necho Maven Wrapper")
mvnw.chmod(0o755)

nested = repo_root / "parent-module" / "child-module"
nested.mkdir(parents=True)

def test_with_nonexistent_wrapper(self, tmp_path):
strategy = MavenStrategy()
result = strategy.find_executable(tmp_path)
# Should not crash, may return system mvn or None
result = strategy.find_executable(nested.resolve())
assert result is not None
assert Path(result).is_absolute()
assert result == str(mvnw.resolve())

def test_no_wrapper_returns_system_mvn_or_none(self, tmp_path):
strategy = MavenStrategy()
result = strategy.find_executable(tmp_path.resolve())
# Should return system mvn (if available) or None — never crash

def test_cwd_does_not_affect_result(self, tmp_path, monkeypatch):
"""CWD should not influence find_executable — only build_root matters."""
repo_with_mvnw = tmp_path / "repo_a"
repo_with_mvnw.mkdir()
mvnw = repo_with_mvnw / "mvnw"
mvnw.write_text("#!/bin/bash\necho Maven Wrapper")
mvnw.chmod(0o755)

unrelated_dir = tmp_path / "repo_b"
unrelated_dir.mkdir()

monkeypatch.chdir(repo_with_mvnw)

strategy = MavenStrategy()
result = strategy.find_executable(unrelated_dir.resolve())
# Should NOT find mvnw from CWD — only from build_root and its parents
if result is not None:
assert "repo_a" not in result, f"Found mvnw from CWD instead of build_root: {result}"


class TestCustomSourceDirectoryDetection:
Expand Down
Loading