157 lines
4.9 KiB
Python
157 lines
4.9 KiB
Python
"""Test-file generation for migrated C++ code.
|
|
|
|
Strategy (B + C combined, no pre-existing test infrastructure):
|
|
- QTest for Qt-heavy translation units (QObject / signals / slots)
|
|
- gtest for pure-logic / non-UI code
|
|
- Smoke CTest for every file (build + link + default-construction check)
|
|
|
|
Generated files are written to ``{worktree_root}/tests_generated/`` and
|
|
registered in that directory's CMakeLists.txt (idempotent via a guard
|
|
comment).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from pathlib import Path
|
|
|
|
import httpx
|
|
|
|
from migration_pipeline.api_client import call_api
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# System prompts
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_QTEST_SYSTEM = (
|
|
"Du bist ein QA-Ingenieur für Qt6/C++20. "
|
|
"Schreibe einen eigenständigen QTest Unit-Test für den folgenden migrierten Qt6-Code. "
|
|
"Verwende QCOMPARE, QVERIFY und QTEST_MAIN. "
|
|
"Gib NUR den C++ Quellcode zurück, ohne Markdown oder Erklärungen."
|
|
)
|
|
|
|
_GTEST_SYSTEM = (
|
|
"Du bist ein QA-Ingenieur für C++20. "
|
|
"Schreibe einen eigenständigen Google Test (gtest) Unit-Test für den folgenden C++ Code. "
|
|
"Gib NUR den C++ Quellcode zurück, ohne Markdown oder Erklärungen."
|
|
)
|
|
|
|
_SMOKE_SYSTEM = (
|
|
"Du bist ein QA-Ingenieur. "
|
|
"Schreibe einen minimalen Smoke-Test mit Google Test, der sicherstellt, dass die Datei "
|
|
"kompiliert, linkt und die Default-Konstruktion der Hauptklasse nicht crasht. "
|
|
"Gib NUR den C++ Quellcode zurück, ohne Markdown oder Erklärungen."
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Qt-content heuristic
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_QT_MARKERS = (
|
|
"QObject",
|
|
"QWidget",
|
|
"QApplication",
|
|
"QMainWindow",
|
|
"emit ",
|
|
"signals:",
|
|
"slots:",
|
|
"Q_OBJECT",
|
|
"Q_EMIT",
|
|
)
|
|
|
|
|
|
def _is_qt_heavy(code: str) -> bool:
|
|
return any(marker in code for marker in _QT_MARKERS)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Public API
|
|
# ---------------------------------------------------------------------------
|
|
|
|
async def generate_tests(
|
|
client: httpx.AsyncClient,
|
|
model: str,
|
|
source_file_path: str,
|
|
migrated_code: str,
|
|
worktree_root: Path,
|
|
track_id: str,
|
|
) -> str:
|
|
"""Generate unit + smoke tests for *migrated_code*.
|
|
|
|
Returns the CTest test-name prefix (e.g. ``test_track_a_conservative_MyClass``).
|
|
Both the unit test and smoke test are registered under that prefix.
|
|
"""
|
|
base_name = Path(source_file_path).stem
|
|
test_prefix = f"test_{track_id}_{base_name}"
|
|
tests_dir = worktree_root / "tests_generated"
|
|
tests_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
use_qtest = _is_qt_heavy(migrated_code)
|
|
unit_system = _QTEST_SYSTEM if use_qtest else _GTEST_SYSTEM
|
|
framework = "Qt6::Core Qt6::Test" if use_qtest else "GTest::gtest_main"
|
|
|
|
# Generate unit test and smoke test in parallel.
|
|
unit_code, smoke_code = await asyncio.gather(
|
|
call_api(
|
|
client, model, unit_system,
|
|
f"Erstelle einen Unit-Test für folgenden migrierten Qt6-Code:\n\n{migrated_code}",
|
|
),
|
|
call_api(
|
|
client, model, _SMOKE_SYSTEM,
|
|
f"Erstelle einen Smoke-Test für:\n\n{migrated_code}",
|
|
),
|
|
)
|
|
|
|
unit_file = tests_dir / f"{test_prefix}_unit.cpp"
|
|
smoke_file = tests_dir / f"{test_prefix}_smoke.cpp"
|
|
unit_file.write_text(unit_code)
|
|
smoke_file.write_text(smoke_code)
|
|
|
|
_register_tests_in_cmake(
|
|
tests_dir,
|
|
test_prefix,
|
|
unit_file.name,
|
|
smoke_file.name,
|
|
framework,
|
|
)
|
|
|
|
return test_prefix
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CMakeLists.txt registration (idempotent)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_GUARD_PREFIX = "# MIGRATION_PIPELINE:"
|
|
|
|
|
|
def _register_tests_in_cmake(
|
|
tests_dir: Path,
|
|
test_prefix: str,
|
|
unit_filename: str,
|
|
smoke_filename: str,
|
|
framework_libs: str,
|
|
) -> None:
|
|
cmake_path = tests_dir / "CMakeLists.txt"
|
|
guard = f"{_GUARD_PREFIX}{test_prefix}"
|
|
|
|
# Read existing content to avoid duplicate registrations.
|
|
existing = cmake_path.read_text() if cmake_path.exists() else ""
|
|
if guard in existing:
|
|
return # already registered
|
|
|
|
entry = (
|
|
f"\n{guard}\n"
|
|
f"add_executable({test_prefix}_unit {unit_filename})\n"
|
|
f"target_link_libraries({test_prefix}_unit PRIVATE {framework_libs})\n"
|
|
f"add_test(NAME {test_prefix}_unit COMMAND {test_prefix}_unit)\n"
|
|
f"\nadd_executable({test_prefix}_smoke {smoke_filename})\n"
|
|
f"target_link_libraries({test_prefix}_smoke PRIVATE GTest::gtest_main)\n"
|
|
f"add_test(NAME {test_prefix}_smoke COMMAND {test_prefix}_smoke)\n"
|
|
)
|
|
|
|
with cmake_path.open("a") as fh:
|
|
fh.write(entry)
|