diff --git a/codeflash/languages/java/support.py b/codeflash/languages/java/support.py index 7115d2225..500308391 100644 --- a/codeflash/languages/java/support.py +++ b/codeflash/languages/java/support.py @@ -546,6 +546,21 @@ def ensure_runtime_environment(self, project_root: Path) -> bool: if self._language_version is None: self._detect_java_version() + if self._language_version is not None: + try: + major = int(self._language_version) + if major < 11: + logger.error( + "Java %s detected, but codeflash requires JDK 11 or later. " + "The codeflash-runtime JAR and --add-opens flags are incompatible with JDK %s. " + "Please install JDK 11+ and ensure it is on your PATH.", + self._language_version, + self._language_version, + ) + return False + except ValueError: + pass + self._test_framework = config.test_framework return True @@ -568,9 +583,9 @@ def _detect_java_version(self) -> None: end = line.find('"', start + 1) if start != -1 and end != -1: full_version = line[start + 1 : end] - # Use major version only: "17.0.2" -> "17", "1.8.0_292" -> "8" - major = full_version.split(".")[0] - self._language_version = "8" if major == "1" else major + # Use major version only: "17.0.2" -> "17". JDK 8 and earlier (reported as + # "1.x.y") are unsupported — the downstream minimum-version check rejects them. + self._language_version = full_version.split(".")[0] return except Exception: pass diff --git a/tests/test_languages/test_java/test_support.py b/tests/test_languages/test_java/test_support.py index b5cba5ab4..3f991f7a4 100644 --- a/tests/test_languages/test_java/test_support.py +++ b/tests/test_languages/test_java/test_support.py @@ -134,3 +134,74 @@ def test_discover_functions_from_fixture(self, support, java_fixture_path: Path) source = calculator_file.read_text(encoding="utf-8") functions = support.discover_functions(source, calculator_file) assert len(functions) > 0 + + +class TestJdkVersionCheck: + def test_jdk8_rejected(self, tmp_path: Path) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + + mock_config = MagicMock() + mock_config.java_version = "8" + mock_config.test_framework = "junit5" + + with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): + result = support.ensure_runtime_environment(tmp_path) + assert result is False + + def test_jdk11_accepted(self, tmp_path: Path) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + + mock_config = MagicMock() + mock_config.java_version = "11" + mock_config.test_framework = "junit5" + + with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): + result = support.ensure_runtime_environment(tmp_path) + assert result is True + + def test_jdk21_accepted(self, tmp_path: Path) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + + mock_config = MagicMock() + mock_config.java_version = "21" + mock_config.test_framework = "junit5" + + with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): + result = support.ensure_runtime_environment(tmp_path) + assert result is True + + def test_detect_java_version_legacy_jdk8_format(self) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + support._language_version = None + + mock_result = MagicMock() + mock_result.stderr = 'openjdk version "1.8.0_292"\n' + mock_result.stdout = "" + + with patch("subprocess.run", return_value=mock_result): + support._detect_java_version() + + assert support._language_version == "1" + + def test_detect_java_version_modern_format(self) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + support._language_version = None + + mock_result = MagicMock() + mock_result.stderr = 'openjdk version "17.0.2"\n' + mock_result.stdout = "" + + with patch("subprocess.run", return_value=mock_result): + support._detect_java_version() + + assert support._language_version == "17"