generating and checking the coverage with jacoco

This commit is contained in:
Karma Riuk
2025-03-17 11:29:07 +01:00
parent 47763f5803
commit d7b3d62c0c
2 changed files with 69 additions and 6 deletions

View File

@ -99,10 +99,31 @@ class BuildHandler(ABC):
finally:
signal.alarm(0) # Cancel the alarm
def generate_coverage_report(self):
result = self.container.exec_run(self.generate_coverage_report_cmd())
if result.exit_code != 0:
raise CantExecJacoco(clean_output(result.output))
def check_coverage(self, filename: str) -> None:
"""
Check if the given filename is covered by JaCoCo.
"""
coverage_report_path = self.get_jacoco_report_path()
if not os.path.exists(coverage_report_path):
raise NoCoverageReportFound(f"Coverage report file '{coverage_report_path}' does not exist")
with open(coverage_report_path, "r") as file:
soup = BeautifulSoup(file, "html.parser")
# Extract all files listed in the JaCoCo coverage report
covered_files = [a.text.strip() for a in soup.select("table tbody tr td a")]
if filename not in covered_files:
raise FileNotCovered(f"The file '{filename}' was not present in the report '{coverage_report_path}'")
def clean_repo(self) -> None:
self.container.exec_run(self.clean_cmd())
@abstractmethod
def compile_cmd(self) -> str:
pass
@ -119,6 +140,14 @@ class BuildHandler(ABC):
def clean_cmd(self) -> str:
pass
@abstractmethod
def generate_coverage_report_cmd(self) -> str:
pass
@abstractmethod
def get_jacoco_report_path(self) -> str:
pass
@abstractmethod
def container_name(self) -> str:
pass
@ -140,6 +169,9 @@ class MavenHandler(BuildHandler):
def clean_cmd(self) -> str:
return f"{self.base_cmd} clean"
def generate_coverage_report_cmd(self):
return f"{self.base_cmd} jacoco:report"
def container_name(self) -> str:
return "crab-maven"
@ -165,6 +197,9 @@ class MavenHandler(BuildHandler):
self.updates["n_tests_skipped"] += skipped
self.updates["n_tests_passed"] += (tests_run - (failures + errors)) # Calculate passed tests
def get_jacoco_report_path(self) -> str:
return os.path.join(self.path, "target/site/jacoco/index.html")
class GradleHandler(BuildHandler):
def __init__(self, repo_path: str, build_file: str, updates: dict) -> None:
super().__init__(repo_path, build_file, updates)
@ -179,6 +214,9 @@ class GradleHandler(BuildHandler):
def clean_cmd(self) -> str:
return f"{self.base_cmd} clean"
def generate_coverage_report_cmd(self) -> str:
return f"{self.base_cmd}jacocoTestReport"
def container_name(self) -> str:
return "crab-gradle"
@ -197,21 +235,25 @@ class GradleHandler(BuildHandler):
with open(test_results_path, "r") as file:
soup = BeautifulSoup(file, "html.parser")
test_div = soup.find("div", class_="infoBox", id="tests")
# test_div = soup.select_one("div", class_="infoBox", id="tests")
test_div = soup.select_one("div.infoBox#tests")
if test_div is None:
raise NoTestResultsToExtractError("No test results found (no div.infoBox#tests)")
counter_div = test_div.find("div", class_="counter")
# counter_div = test_div.find("div", class_="counter")
counter_div = test_div.select_one("div.counter")
if counter_div is None:
raise NoTestResultsToExtractError("No test results found (not div.counter for tests)")
self.updates["n_tests"] = int(counter_div.text.strip())
failures_div = soup.find("div", class_="infoBox", id="failures")
# failures_div = soup.find("div", class_="infoBox", id="failures")
failures_div = soup.select_one("div.infoBox#failures")
if failures_div is None:
raise NoTestResultsToExtractError("No test results found (no div.infoBox#failures)")
counter_div = failures_div.find("div", class_="counter")
# counter_div = failures_div.find("div", class_="counter")
counter_div = failures_div.select_one("div.counter")
if counter_div is None:
raise NoTestResultsToExtractError("No test results found (not div.counter for failures)")
@ -220,6 +262,9 @@ class GradleHandler(BuildHandler):
# Calculate passed tests
self.updates["n_tests_passed"] = self.updates["n_tests"] - self.updates["n_tests_failed"]
def get_jacoco_report_path(self) -> str:
return os.path.join(self.path, "build/reports/jacoco/test/html/index.html")
class NoTestsFoundError(Exception):
pass
@ -232,6 +277,15 @@ class FailedToTestError(Exception):
class NoTestResultsToExtractError(Exception):
pass
class CantExecJacoco(Exception):
pass
class NoCoverageReportFound(Exception):
pass
class FileNotCovered(Exception):
pass
def merge_download_lines(lines: list) -> list:
"""
Merges lines that are part of the same download block in Maven output.

View File

@ -8,7 +8,7 @@ from tqdm import tqdm
from datetime import datetime
from dataset import Dataset, DatasetEntry, FileData, Metadata, Diff
from handlers import FailedToCompileError, FailedToTestError, NoTestsFoundError, NoTestResultsToExtractError, get_build_handler
from handlers import CantExecJacoco, FailedToCompileError, FailedToTestError, FileNotCovered, NoCoverageReportFound, NoTestsFoundError, NoTestResultsToExtractError, get_build_handler
from utils import has_only_1_comment, move_github_logging_to_file, clone
@ -123,10 +123,16 @@ def process_pull(repo: Repository, pr: PullRequest, dataset: Dataset, repos_dir:
return
build_handler.set_client(docker_client)
def _check_coverage(files: list[str]):
for file in files:
build_handler.check_coverage(file)
steps = [
("Checking for tests...", build_handler.check_for_tests),
("Compiling...", build_handler.compile_repo),
("Running tests...", build_handler.test_repo),
("Generating coverage...", build_handler.generate_coverage_report),
("Checking coverage...", lambda: _check_coverage([file.filename for file in pr.get_files()])),
]
error_map = {
@ -134,6 +140,9 @@ def process_pull(repo: Repository, pr: PullRequest, dataset: Dataset, repos_dir:
FailedToCompileError: "Failed to compile",
FailedToTestError: "Failed to test",
NoTestResultsToExtractError: "Failed to extract test results",
CantExecJacoco: "Coudln't execute jacoco",
NoCoverageReportFound: "No coverage report was found",
FileNotCovered: "A file from the PR was not coverege",
}
with build_handler, tqdm(total=len(steps), desc="Processing PR", leave=False) as pbar: