Skip to content

Commit e6c790c

Browse files
Refactor revert (#267)
* Revert "Fix regression on Jacoco coverage & Corbertura (#264)" This reverts commit f46980f. * Revert "Removing bad reference to self.last_coverage_percentages causing necessary failure logs (#262)" This reverts commit 1b1a9ad. * Revert "Refactored coverage processor in to class hierarchy (#230)" This reverts commit 3496069. * Incrementing version.
1 parent f46980f commit e6c790c

File tree

12 files changed

+1070
-1098
lines changed

12 files changed

+1070
-1098
lines changed

cover_agent/CoverAgent.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,19 +199,19 @@ def run_test_gen(self, failed_test_runs: List, language: str, test_framework: st
199199

200200
# Check if the desired coverage has been reached
201201
failed_test_runs, language, test_framework, coverage_report = self.test_validator.get_coverage()
202-
if self.test_validator.get_current_coverage() >= (self.test_validator.desired_coverage / 100):
202+
if self.test_validator.current_coverage >= (self.test_validator.desired_coverage / 100):
203203
break
204204

205205
# Log the final coverage
206-
if self.test_validator.get_current_coverage() >= (self.test_validator.desired_coverage / 100):
206+
if self.test_validator.current_coverage >= (self.test_validator.desired_coverage / 100):
207207
self.logger.info(
208-
f"Reached above target coverage of {self.test_validator.desired_coverage}% (Current Coverage: {round(self.test_validator.get_current_coverage() * 100, 2)}%) in {iteration_count} iterations."
208+
f"Reached above target coverage of {self.test_validator.desired_coverage}% (Current Coverage: {round(self.test_validator.current_coverage * 100, 2)}%) in {iteration_count} iterations."
209209
)
210210
elif iteration_count == self.args.max_iterations:
211211
if self.args.diff_coverage:
212-
failure_message = f"Reached maximum iteration limit without achieving desired diff coverage. Current Coverage: {round(self.test_validator.get_current_coverage() * 100, 2)}%"
212+
failure_message = f"Reached maximum iteration limit without achieving desired diff coverage. Current Coverage: {round(self.test_validator.current_coverage * 100, 2)}%"
213213
else:
214-
failure_message = f"Reached maximum iteration limit without achieving desired coverage. Current Coverage: {round(self.test_validator.get_current_coverage() * 100, 2)}%"
214+
failure_message = f"Reached maximum iteration limit without achieving desired coverage. Current Coverage: {round(self.test_validator.current_coverage * 100, 2)}%"
215215
if self.args.strict_coverage:
216216
# User requested strict coverage (similar to "--cov-fail-under in pytest-cov"). Fail with exist code 2.
217217
self.logger.error(failure_message)
@@ -237,11 +237,11 @@ def run_test_gen(self, failed_test_runs: List, language: str, test_framework: st
237237
def log_coverage(self):
238238
if self.args.diff_coverage:
239239
self.logger.info(
240-
f"Current Diff Coverage: {round(self.test_validator.get_current_coverage() * 100, 2)}%"
240+
f"Current Diff Coverage: {round(self.test_validator.current_coverage * 100, 2)}%"
241241
)
242242
else:
243243
self.logger.info(
244-
f"Current Coverage: {round(self.test_validator.get_current_coverage() * 100, 2)}%"
244+
f"Current Coverage: {round(self.test_validator.current_coverage * 100, 2)}%"
245245
)
246246
self.logger.info(f"Desired Coverage: {self.test_validator.desired_coverage}%")
247247

cover_agent/CoverageProcessor.py

Lines changed: 413 additions & 0 deletions
Large diffs are not rendered by default.

cover_agent/UnitTestGenerator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import re
77

88
from cover_agent.AICaller import AICaller
9+
from cover_agent.CoverageProcessor import CoverageProcessor
910
from cover_agent.CustomLogger import CustomLogger
1011
from cover_agent.FilePreprocessor import FilePreprocessor
1112
from cover_agent.PromptBuilder import PromptBuilder

cover_agent/UnitTestValidator.py

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
import json
44
import logging
55
import os
6+
import re
67

78
from diff_cover.diff_cover_tool import main as diff_cover_main
89

910
from cover_agent.AICaller import AICaller
11+
from cover_agent.CoverageProcessor import CoverageProcessor
1012
from cover_agent.CustomLogger import CustomLogger
1113
from cover_agent.FilePreprocessor import FilePreprocessor
1214
from cover_agent.PromptBuilder import PromptBuilder
1315
from cover_agent.Runner import Runner
1416
from cover_agent.settings.config_loader import get_settings
1517
from cover_agent.utils import load_yaml
16-
from cover_agent.coverage.processor import process_coverage, CoverageReport, CoverageData
18+
1719

1820
class UnitTestValidator:
1921
def __init__(
@@ -106,6 +108,15 @@ def __init__(
106108
with open(self.source_file_path, "r") as f:
107109
self.source_code = f.read()
108110

111+
# initialize the coverage processor
112+
self.coverage_processor = CoverageProcessor(
113+
file_path=self.code_coverage_report_path,
114+
src_file_path=self.source_file_path,
115+
coverage_type=self.coverage_type,
116+
use_report_coverage_feature_flag=self.use_report_coverage_feature_flag,
117+
diff_coverage_report_path=self.diff_cover_report_path,
118+
)
119+
109120
def get_coverage(self):
110121
"""
111122
Run code coverage and build the prompt to be used for generating tests.
@@ -115,9 +126,6 @@ def get_coverage(self):
115126
"""
116127
# Run coverage and build the prompt
117128
self.run_coverage()
118-
# Run diff coverage if enabled
119-
if self.diff_coverage:
120-
self.generate_diff_coverage_report()
121129
return self.failed_test_runs, self.language, self.testing_framework, self.code_coverage_report
122130

123131
def get_code_language(self, source_file_path: str) -> str:
@@ -288,9 +296,14 @@ def run_coverage(self):
288296
), f'Fatal: Error running test command. Are you sure the command is correct? "{self.test_command}"\nExit code {exit_code}. \nStdout: \n{stdout} \nStderr: \n{stderr}'
289297

290298
try:
291-
self.current_coverage_report = self.post_process_coverage_report(time_of_test_command)
299+
# Process the extracted coverage metrics
300+
coverage, coverage_percentages = self.post_process_coverage_report(
301+
time_of_test_command
302+
)
303+
self.current_coverage = coverage
304+
self.last_coverage_percentages = coverage_percentages.copy()
292305
self.logger.info(
293-
f"Initial coverage: {round(self.current_coverage_report.total_coverage * 100, 2)}%"
306+
f"Initial coverage: {round(self.current_coverage * 100, 2)}%"
294307
)
295308

296309
except AssertionError as error:
@@ -449,6 +462,7 @@ def validate_test(self, generated_test: dict):
449462
if exit_code != 0:
450463
break
451464

465+
452466
# Step 3: Check for pass/fail from the Runner object
453467
if exit_code != 0:
454468
# Test failed, roll back the test file to its original content
@@ -491,9 +505,11 @@ def validate_test(self, generated_test: dict):
491505

492506
# If test passed, check for coverage increase
493507
try:
494-
new_coverage_report = self.post_process_coverage_report(time_of_test_command)
508+
new_percentage_covered, new_coverage_percentages = self.post_process_coverage_report(
509+
time_of_test_command
510+
)
495511

496-
if self.current_coverage_report is not None and new_coverage_report.total_coverage <= self.current_coverage_report.total_coverage:
512+
if new_percentage_covered <= self.current_coverage:
497513
# Coverage has not increased, rollback the test by removing it from the test file
498514
with open(self.test_file_path, "w") as test_file:
499515
test_file.write(original_content)
@@ -565,20 +581,20 @@ def validate_test(self, generated_test: dict):
565581
additional_imports_lines
566582
) # this is important, otherwise the next test will be inserted at the wrong line
567583

568-
for key in new_coverage_report.file_coverage:
569-
new_v: CoverageData = new_coverage_report.file_coverage[key]
570-
old_v: CoverageData = self.current_coverage_report.file_coverage[key]
571-
if new_v.coverage > old_v.coverage and key == self.source_file_path.split("/")[-1]:
584+
for key in new_coverage_percentages:
585+
if new_coverage_percentages[key] > self.last_coverage_percentages[key] and key == self.source_file_path.split("/")[-1]:
572586
self.logger.info(
573-
f"Coverage for provided source file: {key} increased to {round(new_v.coverage * 100, 2)}"
587+
f"Coverage for provided source file: {key} increased from {round(self.last_coverage_percentages[key] * 100, 2)} to {round(new_coverage_percentages[key] * 100, 2)}"
574588
)
575-
elif new_v.coverage > old_v.coverage:
589+
elif new_coverage_percentages[key] > self.last_coverage_percentages[key]:
576590
self.logger.info(
577-
f"Coverage for non-source file: {key} increased to {round(new_v.coverage * 100, 2)}"
591+
f"Coverage for non-source file: {key} increased from {round(self.last_coverage_percentages[key] * 100, 2)} to {round(new_coverage_percentages[key] * 100, 2)}"
578592
)
579-
593+
self.current_coverage = new_percentage_covered
594+
self.last_coverage_percentages = new_coverage_percentages.copy()
595+
580596
self.logger.info(
581-
f"Test passed and coverage increased. Current coverage: {round(new_coverage_report.total_coverage * 100, 2)}%"
597+
f"Test passed and coverage increased. Current coverage: {round(new_percentage_covered * 100, 2)}%"
582598
)
583599
return {
584600
"status": "PASS",
@@ -675,20 +691,59 @@ def extract_error_message(self, fail_details):
675691
logging.error(f"Error extracting error message: {e}")
676692
return ""
677693

678-
def post_process_coverage_report(self, time_of_test_command: int):
679-
report: CoverageReport = process_coverage(
680-
tool_type=self.coverage_type,
681-
time_of_test_command=time_of_test_command,
682-
report_path=self.code_coverage_report_path,
683-
src_file_path=self.source_file_path,
684-
is_global_coverage_enabled=self.use_report_coverage_feature_flag,
685-
file_pattern=None,
686-
diff_coverage_report_path=self.diff_cover_report_path,
687-
)
688-
self.logger.info(
689-
f"coverage: Percentage {round(report.total_coverage * 100, 2)}%"
690-
)
691-
return report
694+
def post_process_coverage_report(self, time_of_test_command):
695+
coverage_percentages = {}
696+
if self.use_report_coverage_feature_flag:
697+
self.logger.info(
698+
"Using the report coverage feature flag to process the coverage report"
699+
)
700+
file_coverage_dict = self.coverage_processor.process_coverage_report(
701+
time_of_test_command=time_of_test_command
702+
)
703+
total_lines_covered = 0
704+
total_lines_missed = 0
705+
total_lines = 0
706+
for key in file_coverage_dict:
707+
lines_covered, lines_missed, percentage_covered = (
708+
file_coverage_dict[key]
709+
)
710+
total_lines_covered += len(lines_covered)
711+
total_lines_missed += len(lines_missed)
712+
total_lines += len(lines_covered) + len(lines_missed)
713+
if key == self.source_file_path:
714+
self.last_source_file_coverage = percentage_covered
715+
if key not in coverage_percentages:
716+
coverage_percentages[key] = 0
717+
coverage_percentages[key] = percentage_covered
718+
try:
719+
percentage_covered = total_lines_covered / total_lines
720+
except ZeroDivisionError:
721+
self.logger.error(f"ZeroDivisionError: Attempting to perform total_lines_covered / total_lines: {total_lines_covered} / {total_lines}.")
722+
percentage_covered = 0
723+
724+
self.logger.info(
725+
f"Total lines covered: {total_lines_covered}, Total lines missed: {total_lines_missed}, Total lines: {total_lines}"
726+
)
727+
self.logger.info(
728+
f"coverage: Percentage {round(percentage_covered * 100, 2)}%"
729+
)
730+
elif self.diff_coverage:
731+
self.generate_diff_coverage_report()
732+
lines_covered, lines_missed, percentage_covered = (
733+
self.coverage_processor.process_coverage_report(
734+
time_of_test_command=time_of_test_command
735+
)
736+
)
737+
self.code_coverage_report = f"Lines covered: {lines_covered}\nLines missed: {lines_missed}\nPercentage covered: {round(percentage_covered * 100, 2)}%"
738+
else:
739+
lines_covered, lines_missed, percentage_covered = (
740+
self.coverage_processor.process_coverage_report(
741+
time_of_test_command=time_of_test_command
742+
)
743+
)
744+
self.code_coverage_report = f"Lines covered: {lines_covered}\nLines missed: {lines_missed}\nPercentage covered: {round(percentage_covered * 100, 2)}%"
745+
return percentage_covered, coverage_percentages
746+
692747

693748
def generate_diff_coverage_report(self):
694749
"""
@@ -719,4 +774,4 @@ def generate_diff_coverage_report(self):
719774
self.logger.error(f"Error running diff-cover: {e}")
720775

721776
def get_current_coverage(self):
722-
return self.current_coverage_report.total_coverage
777+
return self.current_coverage_report.total_coverage

0 commit comments

Comments
 (0)