From a7371b55ca7c48d086edf37c65fb39fac8427e15 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 10 Apr 2026 21:46:42 +0000 Subject: [PATCH] fix: add --configure-on-demand to all Gradle commands Gradle evaluates all project configurations during the configuration phase, even when only one module is targeted. Multi-module projects with diverse toolchain requirements (e.g., OpenRewrite's rewrite-gradle needs JDK 8) fail when an unrelated module's toolchain isn't available. Adds --configure-on-demand to all 8 Gradle command construction sites so Gradle only configures projects needed for the requested task. Co-Authored-By: Claude Opus 4.6 --- codeflash/languages/java/gradle_strategy.py | 24 ++++-- .../test_java/test_gradle_strategy.py | 76 +++++++++++++++++++ 2 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 tests/test_languages/test_java/test_gradle_strategy.py diff --git a/codeflash/languages/java/gradle_strategy.py b/codeflash/languages/java/gradle_strategy.py index c4174ffe4..bcca1b984 100644 --- a/codeflash/languages/java/gradle_strategy.py +++ b/codeflash/languages/java/gradle_strategy.py @@ -533,7 +533,15 @@ def install_multi_module_deps(self, build_root: Path, test_module: str | None, e logger.error("Gradle not found — cannot pre-install multi-module dependencies") return False - cmd = [gradle, f":{test_module}:testClasses", "-x", "test", "--build-cache", "--no-daemon"] + cmd = [ + gradle, + f":{test_module}:testClasses", + "-x", + "test", + "--build-cache", + "--no-daemon", + "--configure-on-demand", + ] cmd.extend(["--init-script", _get_skip_validation_init_script()]) logger.info("Pre-installing multi-module dependencies: %s (module: %s)", build_root, test_module) @@ -568,9 +576,9 @@ def compile_tests( return subprocess.CompletedProcess(args=["gradle"], returncode=-1, stdout="", stderr="Gradle not found") if test_module: - cmd = [gradle, f":{test_module}:testClasses", "--no-daemon"] + cmd = [gradle, f":{test_module}:testClasses", "--no-daemon", "--configure-on-demand"] else: - cmd = [gradle, "testClasses", "--no-daemon"] + cmd = [gradle, "testClasses", "--no-daemon", "--configure-on-demand"] cmd.extend(["--init-script", _get_skip_validation_init_script()]) logger.debug("Compiling tests: %s in %s", " ".join(cmd), build_root) @@ -592,9 +600,9 @@ def compile_source_only( return subprocess.CompletedProcess(args=["gradle"], returncode=-1, stdout="", stderr="Gradle not found") if test_module: - cmd = [gradle, f":{test_module}:classes", "--no-daemon"] + cmd = [gradle, f":{test_module}:classes", "--no-daemon", "--configure-on-demand"] else: - cmd = [gradle, "classes", "--no-daemon"] + cmd = [gradle, "classes", "--no-daemon", "--configure-on-demand"] cmd.extend(["--init-script", _get_skip_validation_init_script()]) logger.debug("Compiling source only: %s in %s", " ".join(cmd), build_root) @@ -638,7 +646,7 @@ def _get_classpath_uncached( else: task = "codeflashPrintClasspath" - cmd = [gradle, "--init-script", init_script_path, task, "-q", "--no-daemon"] + cmd = [gradle, "--init-script", init_script_path, task, "-q", "--no-daemon", "--configure-on-demand"] logger.debug("Getting classpath: %s", " ".join(cmd)) @@ -789,7 +797,7 @@ def run_tests_via_build_tool( with os.fdopen(init_fd, "w", encoding="utf-8") as f: f.write(init_script_content) - cmd = [gradle, task, "--no-daemon", "--rerun", "--init-script", init_path] + cmd = [gradle, task, "--no-daemon", "--rerun", "--configure-on-demand", "--init-script", init_path] cmd.extend(["--init-script", _get_skip_validation_init_script()]) for class_filter in test_filter.split(","): @@ -1044,7 +1052,7 @@ def get_test_run_command(self, project_root: Path, test_classes: list[str] | Non raise ValueError(msg) gradle = self.find_executable(project_root) or "gradle" - cmd = [gradle, "test", "--no-daemon"] + cmd = [gradle, "test", "--no-daemon", "--configure-on-demand"] if test_classes: for cls in test_classes: cmd.extend(["--tests", cls]) diff --git a/tests/test_languages/test_java/test_gradle_strategy.py b/tests/test_languages/test_java/test_gradle_strategy.py new file mode 100644 index 000000000..08c62580a --- /dev/null +++ b/tests/test_languages/test_java/test_gradle_strategy.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import MagicMock, patch + +from codeflash.languages.java.gradle_strategy import GradleStrategy + +if TYPE_CHECKING: + from pathlib import Path + +COD_FLAG = "--configure-on-demand" +MOCK_TARGET = "codeflash.languages.java.test_runner._run_cmd_kill_pg_on_timeout" + + +class TestConfigureOnDemand: + def test_compile_tests_includes_configure_on_demand(self, tmp_path: Path) -> None: + strategy = GradleStrategy() + with patch.object(strategy, "find_executable", return_value="gradlew"), patch(MOCK_TARGET) as mock_run: + mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") + strategy.compile_tests(tmp_path, {}, test_module=None) + cmd = mock_run.call_args[0][0] + assert COD_FLAG in cmd + + def test_compile_tests_multimodule_includes_configure_on_demand(self, tmp_path: Path) -> None: + strategy = GradleStrategy() + with patch.object(strategy, "find_executable", return_value="gradlew"), patch(MOCK_TARGET) as mock_run: + mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") + strategy.compile_tests(tmp_path, {}, test_module="core") + cmd = mock_run.call_args[0][0] + assert COD_FLAG in cmd + assert ":core:testClasses" in cmd + + def test_compile_source_only_includes_configure_on_demand(self, tmp_path: Path) -> None: + strategy = GradleStrategy() + with patch.object(strategy, "find_executable", return_value="gradlew"), patch(MOCK_TARGET) as mock_run: + mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") + strategy.compile_source_only(tmp_path, {}, test_module=None) + cmd = mock_run.call_args[0][0] + assert COD_FLAG in cmd + + def test_get_test_run_command_includes_configure_on_demand(self, tmp_path: Path) -> None: + strategy = GradleStrategy() + with patch.object(strategy, "find_executable", return_value="gradlew"): + cmd = strategy.get_test_run_command(tmp_path) + assert COD_FLAG in cmd + + def test_install_multi_module_deps_includes_configure_on_demand(self, tmp_path: Path) -> None: + strategy = GradleStrategy() + with ( + patch.object(strategy, "find_executable", return_value="gradlew"), + patch(MOCK_TARGET) as mock_run, + patch("codeflash.languages.java.gradle_strategy._multimodule_deps_installed", set()), + ): + mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") + strategy.install_multi_module_deps(tmp_path, test_module="core", env={}) + cmd = mock_run.call_args[0][0] + assert COD_FLAG in cmd + assert ":core:testClasses" in cmd + + def test_run_tests_via_build_tool_includes_configure_on_demand(self, tmp_path: Path) -> None: + strategy = GradleStrategy() + reports_dir = tmp_path / "build" / "test-results" / "test" + reports_dir.mkdir(parents=True, exist_ok=True) + + with patch.object(strategy, "find_executable", return_value="gradlew"), patch(MOCK_TARGET) as mock_run: + mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") + strategy.run_tests_via_build_tool( + build_root=tmp_path, + test_paths=["com.example.TestFoo"], + env={}, + timeout=60, + mode="behavior", + test_module=None, + ) + cmd = mock_run.call_args[0][0] + assert COD_FLAG in cmd