Download backend/app/sandbox/java_runner.py from SevdanurGenc/LLM-Automated-Test-Generation-Agent: direct link, hf CLI and curl.
- Browser
- Download file 9.26 kB
-
https://huggingface.co/SevdanurGenc/LLM-Automated-Test-Generation-Agent/resolve/main/backend/app/sandbox/java_runner.py
- Command line
-
hf download hf://SevdanurGenc/LLM-Automated-Test-Generation-Agent/backend/app/sandbox/java_runner.py
-
curl -L -o java_runner.py https://huggingface.co/SevdanurGenc/LLM-Automated-Test-Generation-Agent/resolve/main/backend/app/sandbox/java_runner.py
9.26 kB
| """Java: javac + JUnit 5 (console launcher) + JaCoCo. | |
| Docker imajında JUnit 5 ve JaCoCo hazırdır. Yerel modda `JAVA_LIB_DIR` içinde | |
| `junit-platform-console-standalone.jar` (+ isteğe bağlı `jacocoagent.jar`, `jacococli.jar`) | |
| aranır; yoksa JUnit 4 (`junit.jar` + `hamcrest-core.jar`) ile kapsama olmadan çalışılır. | |
| """ | |
| from __future__ import annotations | |
| import re | |
| import xml.etree.ElementTree as ET | |
| from pathlib import Path | |
| from typing import Optional | |
| from ..config import settings | |
| from ..models import RunResult, TestCaseResult, TestKind | |
| from .base import COMPILE_FAIL_RC, LanguageRunner, fill_counts, make_coverage, parse_junit_xml, truncate | |
| from .executor import ExecResult | |
| PKG_RE = re.compile(r"^\s*package\s+([\w.]+)\s*;", re.M) | |
| CLASS_RE = re.compile(r"^\s*public\s+(?:final\s+|abstract\s+)*(?:class|interface|enum|record)\s+(\w+)", re.M) | |
| ANY_CLASS_RE = re.compile(r"^\s*(?:public\s+)?(?:final\s+|abstract\s+)*(?:class|interface|enum|record)\s+(\w+)", re.M) | |
| def java_package(code: str) -> str: | |
| m = PKG_RE.search(code) | |
| return m.group(1) if m else "" | |
| def java_class(code: str, default: str = "Solution") -> str: | |
| m = CLASS_RE.search(code) or ANY_CLASS_RE.search(code) | |
| return m.group(1) if m else default | |
| class JavaRunner(LanguageRunner): | |
| language = "java" | |
| def image(self) -> str: | |
| return settings.docker_image_java | |
| # --- Framework tespiti --- | |
| def framework(self) -> str: | |
| if self.ex.mode != "local": | |
| return "junit5" | |
| lib = Path(self.ex.java_lib) | |
| if (lib / "junit-platform-console-standalone.jar").exists(): | |
| return "junit5" | |
| if (lib / "junit.jar").exists(): | |
| return "junit4" | |
| return "missing" | |
| def has_jacoco(self) -> bool: | |
| if self.ex.mode != "local": | |
| return True | |
| lib = Path(self.ex.java_lib) | |
| return (lib / "jacocoagent.jar").exists() and (lib / "jacococli.jar").exists() | |
| def supports_coverage(self) -> bool: # type: ignore[override] | |
| return self.has_jacoco() | |
| def source_filename(self, code: str) -> str: | |
| return f"{java_class(code)}.java" | |
| def test_filename(self, code: str, kind: TestKind) -> str: | |
| suffix = "InterfaceTest" if kind == "interface" else "Test" | |
| return f"{java_class(code)}{suffix}.java" | |
| def framework_hint(self, code: str, kind: TestKind) -> str: | |
| cls, pkg = java_class(code), java_package(code) | |
| test_cls = Path(self.test_filename(code, kind)).stem | |
| pkg_line = f"Put `package {pkg};` at the top of the test (same package as the code). " if pkg else \ | |
| "The code is in the default package; do NOT add a package declaration. " | |
| if self.framework() == "junit4": | |
| fw = ("Framework: JUnit 4.13 (`import org.junit.Test; import static org.junit.Assert.*;`). " | |
| "Use `@Test(expected = X.class)` or `assertThrows` for exceptions.") | |
| else: | |
| fw = ("Framework: JUnit 5 Jupiter (`import org.junit.jupiter.api.*; import static " | |
| "org.junit.jupiter.api.Assertions.*;`, `@ParameterizedTest` with `@CsvSource` is available " | |
| "via `org.junit.jupiter.params`). Use `assertThrows` for exceptions and `assertEquals(expected, " | |
| "actual, delta)` for doubles.") | |
| hint = (f"{fw} Java 21. The class under test is `{cls}`. {pkg_line}The test class MUST be named " | |
| f"`{test_cls}` (file `{test_cls}.java`). Only the JDK standard library and JUnit are available; " | |
| "no Mockito.") | |
| if kind == "interface": | |
| hint += (" These are INTERFACE / CONTRACT tests: check the public API via reflection where useful " | |
| "(public methods, their parameter/return types, implemented interfaces), the exceptions " | |
| "declared/thrown by public methods, and end-to-end collaboration between public methods.") | |
| return hint | |
| def extra_path(self, wd: Path, rel: Path, content: str) -> Path: | |
| if rel.suffix != ".java": | |
| return wd / "src" / "main" / "resources" / rel | |
| pkg = java_package(content) | |
| # Java'da public sınıf kendi adını taşıyan dosyada olmalı: Test02.java içindeki "public class InventoryManager" | |
| # InventoryManager.java olarak yazılır (yoksa tüm derleme başarısız olur). | |
| name = f"{java_class(content, rel.stem)}.java" | |
| return wd / "src" / "main" / (Path(*pkg.split(".")) if pkg else Path()) / name | |
| def write_files(self, wd: Path, code: str, test_code: str, kind: TestKind, html: Optional[str]) -> None: | |
| pkg = java_package(code) | |
| rel = Path(*pkg.split(".")) if pkg else Path() | |
| main = wd / "src" / "main" / rel | |
| test = wd / "src" / "test" / rel | |
| main.mkdir(parents=True, exist_ok=True) | |
| test.mkdir(parents=True, exist_ok=True) | |
| (main / self.source_filename(code)).write_text(code, encoding="utf-8") | |
| test_name = f"{java_class(test_code, Path(self.test_filename(code, kind)).stem)}.java" | |
| (test / test_name).write_text(test_code, encoding="utf-8") | |
| def command(self, wd: Path, code: str, test_code: str, kind: TestKind, with_coverage: bool) -> str: | |
| lib = self.ex.java_lib | |
| fw = self.framework() | |
| if fw == "missing": | |
| return (f"echo '__LTA_COMPILE_FAILED__ JUnit bulunamadı: {lib} dizinine " | |
| f"junit-platform-console-standalone.jar koyun (README).'; exit {COMPILE_FAIL_RC}") | |
| test_cp = f"{lib}/junit-platform-console-standalone.jar" if fw == "junit5" else \ | |
| f"{lib}/junit.jar:{lib}/hamcrest-core.jar" | |
| compile_ = ( | |
| "unset JAVA_TOOL_OPTIONS; mkdir -p .lta out/main out/test && " | |
| "( { javac -nowarn -encoding UTF-8 -d out/main $(find src/main -name '*.java') && " | |
| f"javac -nowarn -encoding UTF-8 -d out/test -cp out/main:{test_cp} $(find src/test -name '*.java'); }} " | |
| f"> .lta/compile.log 2>&1 || {{ echo __LTA_COMPILE_FAILED__; cat .lta/compile.log; exit {COMPILE_FAIL_RC}; }} )" | |
| ) | |
| if fw == "junit4": | |
| pkg = java_package(code) | |
| test_cls = java_class(test_code, Path(self.test_filename(code, kind)).stem) | |
| fq = f"{pkg}.{test_cls}" if pkg else test_cls | |
| return f"{compile_} && java -cp out/main:out/test:{test_cp} org.junit.runner.JUnitCore {fq}" | |
| agent = "" | |
| if with_coverage and self.has_jacoco(): | |
| agent = f"-javaagent:{lib}/jacocoagent.jar=destfile=.lta/jacoco.exec" | |
| run = (f"java {agent} -jar {lib}/junit-platform-console-standalone.jar execute " | |
| "--class-path out/main:out/test --scan-class-path --disable-banner --disable-ansi-colors " | |
| "--details=none --reports-dir=.lta/reports; rc=$?") | |
| report = "" | |
| if agent: | |
| report = (f"; [ -f .lta/jacoco.exec ] && java -jar {lib}/jacococli.jar report .lta/jacoco.exec " | |
| "--classfiles out/main --sourcefiles src/main --xml .lta/jacoco.xml > /dev/null 2>&1") | |
| return f"{compile_} && {{ {run}{report}; exit $rc; }}" | |
| def parse(self, wd: Path, res: ExecResult, code: str, kind: TestKind) -> RunResult: | |
| if self.framework() == "junit4": | |
| return self._parse_junit4(res) | |
| reports = list((wd / ".lta" / "reports").glob("*.xml")) | |
| cases = parse_junit_xml(reports) | |
| for c in cases: # "pkg.FooTest::addsNumbers()" -> "FooTest.addsNumbers" | |
| cls, _, name = c.name.partition("::") | |
| c.name = f"{cls.split('.')[-1]}.{name.rstrip('()')}" if name else c.name | |
| result = fill_counts(RunResult(cases=cases, output=truncate(res.combined))) | |
| jx = wd / ".lta" / "jacoco.xml" | |
| if jx.exists(): | |
| result.coverage = _jacoco_lines(jx, self.source_filename(code)) | |
| return result | |
| def _parse_junit4(self, res: ExecResult) -> RunResult: | |
| out = res.combined | |
| cases: list[TestCaseResult] = [] | |
| failed_names = re.findall(r"^\d+\) (\w+)\(([\w.$]+)\)", out, re.M) | |
| m_ok = re.search(r"OK \((\d+) tests?\)", out) | |
| m_fail = re.search(r"Tests run: (\d+),\s+Failures: (\d+)", out) | |
| total = int(m_ok.group(1)) if m_ok else int(m_fail.group(1)) if m_fail else 0 | |
| for name, cls in failed_names: | |
| block = out.split(f") {name}({cls})", 1)[-1][:800] | |
| cases.append(TestCaseResult(name=f"{cls.split('.')[-1]}.{name}", status="failed", message=block.strip())) | |
| for i in range(total - len(failed_names)): | |
| cases.append(TestCaseResult(name=f"passed_{i + 1}", status="passed")) | |
| return fill_counts(RunResult(cases=cases, output=truncate(out))) | |
| def _jacoco_lines(path: Path, filename: str): | |
| covered: set[int] = set() | |
| missed: set[int] = set() | |
| root = ET.parse(path).getroot() | |
| for sf in root.iter("sourcefile"): | |
| if sf.get("name") != filename: | |
| continue | |
| for ln in sf.iter("line"): | |
| nr, ci, mi = int(ln.get("nr")), int(ln.get("ci", 0)), int(ln.get("mi", 0)) | |
| if ci > 0: | |
| covered.add(nr) | |
| elif mi > 0: | |
| missed.add(nr) | |
| return make_coverage(covered, missed) | |