Skip to content

Commit 05ca2a9

Browse files
feature/298-flag-disable-file-generation (#324)
* Feature-298: Flag to Disable File Generation * Feature-298: Flag to Disable File Generation * Rename flag to suppress-logs * Fix bug in CoverAgent * Add files creation suppression logic * Improve logger usage in classes * Refactor CustomLogger * Fix failing test test_main_test_file_not_found * Extend tests to check if suppress-logs passed or not * Add file logger test for CustomLogger * Add log level handling and tests * Remove get log_level from env var * Rename suppress-logs to suppress-log-files * Apply review suggestion for log suppression description * Improve tests integration scripts * Remove .coveragerc file due to side effects --------- Co-authored-by: akashjainwork <[email protected]>
1 parent c8e215c commit 05ca2a9

21 files changed

+250
-74
lines changed

.coveragerc

Lines changed: 0 additions & 12 deletions
This file was deleted.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ A few debug files will be outputted locally within the repository (that are part
215215
* `stdout`
216216
* Generated test
217217

218+
You can suppress logs using the `--suppress-log-files` flag. This prevents the creation of the `run.log`, `test_results.html`, and the test results `db` files.
219+
218220
### Additional logging
219221
If you set an environment variable `WANDB_API_KEY`, the prompts, responses, and additional information will be logged to [Weights and Biases](https://wandb.ai/).
220222

cover_agent/CoverAgent.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ def __init__(self, args, agent_completion: AgentCompletionABC=None, logger: Opti
4242
FileNotFoundError: If required source files or directories are not found.
4343
"""
4444
self.args = args
45-
self.logger = logger or CustomLogger.get_logger(__name__)
45+
self.generate_log_files = not args.suppress_log_files
46+
# Initialize logger with file generation flag
47+
self.logger = logger or CustomLogger.get_logger(__name__, generate_log_files=self.generate_log_files)
48+
if args.suppress_log_files:
49+
self.logger.info("Suppressed all generated log files.")
4650

4751
self._validate_paths()
4852
self._duplicate_test_file()
@@ -52,7 +56,9 @@ def __init__(self, args, agent_completion: AgentCompletionABC=None, logger: Opti
5256
self.agent_completion = agent_completion
5357
else:
5458
self.ai_caller = self._initialize_ai_caller()
55-
self.agent_completion = DefaultAgentCompletion(caller=self.ai_caller)
59+
self.agent_completion = DefaultAgentCompletion(
60+
caller=self.ai_caller, generate_log_files=self.generate_log_files
61+
)
5662

5763
# Modify test command for a single test execution if needed
5864
test_command = args.test_command
@@ -103,6 +109,7 @@ def __init__(self, args, agent_completion: AgentCompletionABC=None, logger: Opti
103109
llm_model=args.model,
104110
use_report_coverage_feature_flag=args.use_report_coverage_feature_flag,
105111
agent_completion=self.agent_completion,
112+
generate_log_files=self.generate_log_files,
106113
)
107114

108115
# Initialize test validator with configuration
@@ -124,6 +131,7 @@ def __init__(self, args, agent_completion: AgentCompletionABC=None, logger: Opti
124131
num_attempts=args.run_tests_multiple_times,
125132
agent_completion=self.agent_completion,
126133
max_run_time=args.max_run_time,
134+
generate_log_files=self.generate_log_files,
127135
)
128136

129137
def _initialize_ai_caller(self):
@@ -196,9 +204,9 @@ def _validate_paths(self):
196204
if not self.args.log_db_path:
197205
self.args.log_db_path = "cover_agent_unit_test_runs.db"
198206
# Connect to the test DB
199-
self.test_db = UnitTestDB(
200-
db_connection_string=f"sqlite:///{self.args.log_db_path}"
201-
)
207+
208+
if self.generate_log_files:
209+
self.test_db = UnitTestDB(db_connection_string=f"sqlite:///{self.args.log_db_path}")
202210

203211
def _duplicate_test_file(self):
204212
"""
@@ -263,13 +271,23 @@ def generate_and_validate_tests(self, failed_test_runs, language, test_framework
263271
]
264272

265273
# Insert results into database
266-
for result in test_results:
267-
result["prompt"] = self.test_gen.prompt
268-
self.test_db.insert_attempt(result)
274+
if self.has_test_db():
275+
for result in test_results:
276+
result["prompt"] = self.test_gen.prompt
277+
self.test_db.insert_attempt(result)
269278

270279
except AttributeError as e:
271280
self.logger.error(f"Failed to validate the tests within {generated_tests_dict}. Error: {e}")
272281

282+
def has_test_db(self) -> bool:
283+
"""
284+
Check if the test database is initialized.
285+
286+
Returns:
287+
bool: True if the test database is initialized, False otherwise.
288+
"""
289+
return hasattr(self, "test_db") and self.test_db is not None
290+
273291
def check_iteration_progress(self):
274292
"""
275293
Evaluate current progress towards coverage goals.
@@ -320,8 +338,11 @@ def finalize_test_generation(self, iteration_count):
320338
f"Total number of output tokens used for LLM model {self.args.model}: {self.test_gen.total_output_token_count + self.test_validator.total_output_token_count}"
321339
)
322340

323-
# Generate report and cleanup
324-
self.test_db.dump_to_report(self.args.report_filepath)
341+
# Only generate report if file generation is enabled
342+
if self.generate_log_files:
343+
# Generate report and cleanup
344+
self.test_db.dump_to_report(self.args.report_filepath)
345+
325346
if "WANDB_API_KEY" in os.environ:
326347
wandb.finish()
327348

cover_agent/CoverageProcessor.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from cover_agent.CustomLogger import CustomLogger
2-
from typing import Literal, Tuple, Union, List
2+
from typing import Literal, Tuple, Union, List, Optional
33
import csv
44
import json
55
import os
@@ -15,6 +15,8 @@ def __init__(
1515
coverage_type: Literal["cobertura", "lcov", "jacoco"],
1616
use_report_coverage_feature_flag: bool = False,
1717
diff_coverage_report_path: str = None,
18+
logger: Optional[CustomLogger] = None,
19+
generate_log_files: bool = True,
1820
):
1921
"""
2022
Initializes a CoverageProcessor object.
@@ -23,6 +25,8 @@ def __init__(
2325
file_path (str): The path to the coverage report file.
2426
src_file_path (str): The fully qualified path of the file for which coverage data is being processed.
2527
coverage_type (Literal["cobertura", "lcov"]): The type of coverage report being processed.
28+
logger (CustomLogger): The logger object for logging messages.
29+
generate_log_files (bool): Whether or not to generate logs.
2630
2731
Attributes:
2832
file_path (str): The path to the coverage report file.
@@ -36,7 +40,7 @@ def __init__(
3640
self.file_path = file_path
3741
self.src_file_path = src_file_path
3842
self.coverage_type = coverage_type
39-
self.logger = CustomLogger.get_logger(__name__)
43+
self.logger = logger or CustomLogger.get_logger(__name__, generate_log_files=generate_log_files)
4044
self.use_report_coverage_feature_flag = use_report_coverage_feature_flag
4145
self.diff_coverage_report_path = diff_coverage_report_path
4246

cover_agent/CustomLogger.py

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
import logging
2-
import os
32

43

54
class CustomLogger:
5+
66
@classmethod
7-
def get_logger(cls, name):
7+
def get_logger(cls, name, generate_log_files=True, file_level=logging.INFO, console_level=logging.INFO):
88
"""
99
Return a logger object with specified name.
1010
1111
Parameters:
1212
name (str): The name of the logger.
13+
generate_log_files (bool): Whether to generate log files.
14+
file_level (int): The log level to use.
15+
console_level (int): The log level to use.
1316
1417
Returns:
1518
logging.Logger: The logger object.
1619
1720
Note:
18-
This method sets up the logger to handle all messages of DEBUG level and above. It adds a file handler to write log messages to a file specified by 'log_file_path' and a stream handler to output log messages to the console. The log file is overwritten on each run.
21+
This method sets up the logger to handle all messages of DEBUG level and above.
22+
It adds a file handler to write log messages to a file specified by 'log_file_path' and a stream handler
23+
to output log messages to the console. The log file is overwritten on each run.
1924
2025
Example:
2126
logger = CustomLogger.get_logger('my_logger')
@@ -26,35 +31,27 @@ def get_logger(cls, name):
2631
logger.critical('This is a critical message')
2732
"""
2833
logger = logging.getLogger(name)
29-
logger.setLevel(
30-
logging.DEBUG
31-
) # Set the logger to handle all messages of DEBUG level and above
34+
logger.setLevel(logging.DEBUG)
3235

3336
# Specify the log file path
3437
log_file_path = "run.log"
3538

3639
# Check if handlers are already set up to avoid adding them multiple times
3740
if not logger.handlers:
38-
# File handler for writing to a file
39-
file_handler = logging.FileHandler(
40-
log_file_path, mode="w"
41-
) # Use 'w' to overwrite the log file on each run
42-
file_handler.setLevel(logging.INFO)
43-
file_formatter = logging.Formatter(
44-
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
45-
)
46-
file_handler.setFormatter(file_formatter)
47-
logger.addHandler(file_handler)
41+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
42+
43+
# Only add file handler if file generation is enabled
44+
if generate_log_files:
45+
# File handler for writing to a file. Use 'w' to overwrite the log file on each run
46+
file_handler = logging.FileHandler(log_file_path, mode="w")
47+
file_handler.setLevel(file_level)
48+
file_handler.setFormatter(formatter)
49+
logger.addHandler(file_handler)
4850

4951
# Stream handler for output to the console
5052
stream_handler = logging.StreamHandler()
51-
stream_handler.setLevel(
52-
logging.INFO
53-
) # Set this to DEBUG if you want to see DEBUG messages in the console
54-
stream_formatter = logging.Formatter(
55-
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
56-
)
57-
stream_handler.setFormatter(stream_formatter)
53+
stream_handler.setLevel(console_level)
54+
stream_handler.setFormatter(formatter)
5855
logger.addHandler(stream_handler)
5956

6057
# Prevent log messages from being propagated to the root logger

cover_agent/DefaultAgentCompletion.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from cover_agent.utils import load_yaml
66

77
from jinja2 import Environment, StrictUndefined
8-
from typing import Tuple
8+
from typing import Optional, Tuple
99

1010

1111
class DefaultAgentCompletion(AgentCompletionABC):
@@ -16,15 +16,17 @@ class DefaultAgentCompletion(AgentCompletionABC):
1616
to get the response.
1717
"""
1818

19-
def __init__(self, caller: AICaller):
19+
def __init__(self, caller: AICaller, logger: Optional[CustomLogger]=None, generate_log_files: bool=True):
2020
"""
2121
Initializes the DefaultAgentCompletion.
2222
2323
Args:
2424
caller (AICaller): A class responsible for sending the prompt to an AI model and returning the response.
25+
logger (CustomLogger, optional): The logger object for logging messages.
26+
generate_log_files (bool, optional): Whether or not to generate logs.
2527
"""
2628
self.caller = caller
27-
self.logger = CustomLogger.get_logger(__name__)
29+
self.logger = logger or CustomLogger.get_logger(__name__, generate_log_files=generate_log_files)
2830

2931
def _build_prompt(self, file: str, **kwargs) -> dict:
3032
"""

cover_agent/UnitTestGenerator.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import json
22
import os
33

4+
from typing import Optional
5+
46
from cover_agent.CustomLogger import CustomLogger
57
from cover_agent.FilePreprocessor import FilePreprocessor
68
from cover_agent.AgentCompletionABC import AgentCompletionABC
79
from cover_agent.settings.config_loader import get_settings
810
from cover_agent.utils import load_yaml
911

12+
1013
MAX_TESTS_PER_RUN = 4
1114

1215

@@ -25,6 +28,8 @@ def __init__(
2528
additional_instructions: str = "",
2629
use_report_coverage_feature_flag: bool = False,
2730
project_root: str = "",
31+
logger: Optional[CustomLogger]=None,
32+
generate_log_files: bool=True,
2833
):
2934
"""
3035
Initialize the UnitTestGenerator class with the provided parameters.
@@ -45,6 +50,8 @@ def __init__(
4550
use_report_coverage_feature_flag (bool, optional): Setting this to True considers the coverage of all the files in the coverage report.
4651
This means we consider a test as good if it increases coverage for a different
4752
file other than the source file. Defaults to False.
53+
logger (CustomLogger, optional): The logger object for logging messages.
54+
generate_log_files (bool): Whether or not to generate logs.
4855
4956
Returns:
5057
None
@@ -64,9 +71,10 @@ def __init__(
6471
self.last_coverage_percentages = {}
6572
self.llm_model = llm_model
6673
self.agent_completion = agent_completion
74+
self.generate_log_files = generate_log_files
6775

6876
# Get the logger instance from CustomLogger
69-
self.logger = CustomLogger.get_logger(__name__)
77+
self.logger = logger or CustomLogger.get_logger(__name__, generate_log_files=self.generate_log_files)
7078

7179
# States to maintain within this class
7280
self.preprocessor = FilePreprocessor(self.test_file_path)

cover_agent/UnitTestValidator.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from wandb.sdk.data_types.trace_tree import Trace
21
import datetime
32
import json
43
import logging
54
import os
6-
import re
5+
6+
from typing import Optional
77

88
from diff_cover.diff_cover_tool import main as diff_cover_main
9+
from wandb.sdk.data_types.trace_tree import Trace
910

1011
from cover_agent.AgentCompletionABC import AgentCompletionABC
1112
from cover_agent.CoverageProcessor import CoverageProcessor
@@ -36,6 +37,8 @@ def __init__(
3637
diff_coverage: bool = False,
3738
comparison_branch: str = "main",
3839
num_attempts: int = 1,
40+
logger: Optional[CustomLogger]=None,
41+
generate_log_files: bool=True,
3942
):
4043
"""
4144
Initialize the UnitTestValidator class with the provided parameters.
@@ -57,6 +60,8 @@ def __init__(
5760
use_report_coverage_feature_flag (bool, optional): Setting this to True considers the coverage of all the files in the coverage report.
5861
This means we consider a test as good if it increases coverage for a different
5962
file other than the source file. Defaults to False.
63+
logger (CustomLogger, optional): The logger object for logging messages.
64+
generate_log_files (bool): Whether or not to generate logs.
6065
6166
Returns:
6267
None
@@ -84,9 +89,10 @@ def __init__(
8489
self.num_attempts = num_attempts
8590
self.agent_completion = agent_completion
8691
self.max_run_time = max_run_time
92+
self.generate_log_files = generate_log_files
8793

8894
# Get the logger instance from CustomLogger
89-
self.logger = CustomLogger.get_logger(__name__)
95+
self.logger = logger or CustomLogger.get_logger(__name__, generate_log_files=self.generate_log_files)
9096

9197
# Override covertype to be 'diff' if diff_coverage is enabled
9298
if self.diff_coverage:
@@ -120,6 +126,7 @@ def __init__(
120126
coverage_type=self.coverage_type,
121127
use_report_coverage_feature_flag=self.use_report_coverage_feature_flag,
122128
diff_coverage_report_path=self.diff_cover_report_path,
129+
generate_log_files=self.generate_log_files,
123130
)
124131

125132
def get_coverage(self):

cover_agent/main.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ def parse_args():
116116
default="main",
117117
help="The branch to compare against when using --diff-coverage. Default: %(default)s.",
118118
)
119+
parser.add_argument(
120+
"--suppress-log-files",
121+
action="store_true",
122+
default=False,
123+
help="Suppress all generated log files (HTML, logs, DB files).",
124+
)
119125

120126
# Create mutually exclusive group
121127
group = parser.add_mutually_exclusive_group()

cover_agent/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ def parse_args_full_repo():
337337
action="store_true",
338338
help="Enable record mode for LLM responses. Default: False.",
339339
)
340+
parser.add_argument(
341+
"--suppress-log-files",
342+
action="store_true",
343+
default=False,
344+
help="Suppress all generated log files (HTML, logs, DB files).",
345+
)
340346
return parser.parse_args()
341347

342348

0 commit comments

Comments
 (0)