mirror of
https://github.com/karma-riuk/crab.git
synced 2025-07-05 05:28:13 +02:00
generating and checking the coverage with jacoco
This commit is contained in:
64
handlers.py
64
handlers.py
@ -99,10 +99,31 @@ class BuildHandler(ABC):
|
|||||||
finally:
|
finally:
|
||||||
signal.alarm(0) # Cancel the alarm
|
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:
|
def clean_repo(self) -> None:
|
||||||
self.container.exec_run(self.clean_cmd())
|
self.container.exec_run(self.clean_cmd())
|
||||||
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def compile_cmd(self) -> str:
|
def compile_cmd(self) -> str:
|
||||||
pass
|
pass
|
||||||
@ -119,6 +140,14 @@ class BuildHandler(ABC):
|
|||||||
def clean_cmd(self) -> str:
|
def clean_cmd(self) -> str:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def generate_coverage_report_cmd(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_jacoco_report_path(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def container_name(self) -> str:
|
def container_name(self) -> str:
|
||||||
pass
|
pass
|
||||||
@ -140,6 +169,9 @@ class MavenHandler(BuildHandler):
|
|||||||
def clean_cmd(self) -> str:
|
def clean_cmd(self) -> str:
|
||||||
return f"{self.base_cmd} clean"
|
return f"{self.base_cmd} clean"
|
||||||
|
|
||||||
|
def generate_coverage_report_cmd(self):
|
||||||
|
return f"{self.base_cmd} jacoco:report"
|
||||||
|
|
||||||
def container_name(self) -> str:
|
def container_name(self) -> str:
|
||||||
return "crab-maven"
|
return "crab-maven"
|
||||||
|
|
||||||
@ -165,6 +197,9 @@ class MavenHandler(BuildHandler):
|
|||||||
self.updates["n_tests_skipped"] += skipped
|
self.updates["n_tests_skipped"] += skipped
|
||||||
self.updates["n_tests_passed"] += (tests_run - (failures + errors)) # Calculate passed tests
|
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):
|
class GradleHandler(BuildHandler):
|
||||||
def __init__(self, repo_path: str, build_file: str, updates: dict) -> None:
|
def __init__(self, repo_path: str, build_file: str, updates: dict) -> None:
|
||||||
super().__init__(repo_path, build_file, updates)
|
super().__init__(repo_path, build_file, updates)
|
||||||
@ -179,6 +214,9 @@ class GradleHandler(BuildHandler):
|
|||||||
def clean_cmd(self) -> str:
|
def clean_cmd(self) -> str:
|
||||||
return f"{self.base_cmd} clean"
|
return f"{self.base_cmd} clean"
|
||||||
|
|
||||||
|
def generate_coverage_report_cmd(self) -> str:
|
||||||
|
return f"{self.base_cmd}jacocoTestReport"
|
||||||
|
|
||||||
def container_name(self) -> str:
|
def container_name(self) -> str:
|
||||||
return "crab-gradle"
|
return "crab-gradle"
|
||||||
|
|
||||||
@ -197,21 +235,25 @@ class GradleHandler(BuildHandler):
|
|||||||
with open(test_results_path, "r") as file:
|
with open(test_results_path, "r") as file:
|
||||||
soup = BeautifulSoup(file, "html.parser")
|
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:
|
if test_div is None:
|
||||||
raise NoTestResultsToExtractError("No test results found (no div.infoBox#tests)")
|
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:
|
if counter_div is None:
|
||||||
raise NoTestResultsToExtractError("No test results found (not div.counter for tests)")
|
raise NoTestResultsToExtractError("No test results found (not div.counter for tests)")
|
||||||
|
|
||||||
self.updates["n_tests"] = int(counter_div.text.strip())
|
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:
|
if failures_div is None:
|
||||||
raise NoTestResultsToExtractError("No test results found (no div.infoBox#failures)")
|
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:
|
if counter_div is None:
|
||||||
raise NoTestResultsToExtractError("No test results found (not div.counter for failures)")
|
raise NoTestResultsToExtractError("No test results found (not div.counter for failures)")
|
||||||
|
|
||||||
@ -220,6 +262,9 @@ class GradleHandler(BuildHandler):
|
|||||||
# Calculate passed tests
|
# Calculate passed tests
|
||||||
self.updates["n_tests_passed"] = self.updates["n_tests"] - self.updates["n_tests_failed"]
|
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):
|
class NoTestsFoundError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -232,6 +277,15 @@ class FailedToTestError(Exception):
|
|||||||
class NoTestResultsToExtractError(Exception):
|
class NoTestResultsToExtractError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class CantExecJacoco(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NoCoverageReportFound(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FileNotCovered(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def merge_download_lines(lines: list) -> list:
|
def merge_download_lines(lines: list) -> list:
|
||||||
"""
|
"""
|
||||||
Merges lines that are part of the same download block in Maven output.
|
Merges lines that are part of the same download block in Maven output.
|
||||||
|
@ -8,7 +8,7 @@ from tqdm import tqdm
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from dataset import Dataset, DatasetEntry, FileData, Metadata, Diff
|
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
|
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
|
return
|
||||||
build_handler.set_client(docker_client)
|
build_handler.set_client(docker_client)
|
||||||
|
|
||||||
|
def _check_coverage(files: list[str]):
|
||||||
|
for file in files:
|
||||||
|
build_handler.check_coverage(file)
|
||||||
|
|
||||||
steps = [
|
steps = [
|
||||||
("Checking for tests...", build_handler.check_for_tests),
|
("Checking for tests...", build_handler.check_for_tests),
|
||||||
("Compiling...", build_handler.compile_repo),
|
("Compiling...", build_handler.compile_repo),
|
||||||
("Running tests...", build_handler.test_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 = {
|
error_map = {
|
||||||
@ -134,6 +140,9 @@ def process_pull(repo: Repository, pr: PullRequest, dataset: Dataset, repos_dir:
|
|||||||
FailedToCompileError: "Failed to compile",
|
FailedToCompileError: "Failed to compile",
|
||||||
FailedToTestError: "Failed to test",
|
FailedToTestError: "Failed to test",
|
||||||
NoTestResultsToExtractError: "Failed to extract test results",
|
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:
|
with build_handler, tqdm(total=len(steps), desc="Processing PR", leave=False) as pbar:
|
||||||
|
Reference in New Issue
Block a user