testgenie-py 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testgen/.coverage +0 -0
- testgen/analyzer/ast_analyzer.py +4 -1
- testgen/analyzer/fuzz_analyzer.py +2 -2
- testgen/analyzer/random_feedback_analyzer.py +3 -3
- testgen/analyzer/test_case_analyzer.py +2 -0
- testgen/code_to_test/__init__.py +0 -0
- testgen/code_to_test/boolean.py +146 -0
- testgen/code_to_test/calculator.py +29 -0
- testgen/code_to_test/code_to_fuzz.py +234 -0
- testgen/code_to_test/code_to_fuzz_lite.py +397 -0
- testgen/code_to_test/decisions.py +57 -0
- testgen/code_to_test/math_utils.py +47 -0
- testgen/code_to_test/sample_code_bin.py +141 -0
- testgen/controller/cli_controller.py +0 -1
- testgen/generated_boolean.py +48 -0
- testgen/reinforcement/agent.py +30 -26
- testgen/reinforcement/environment.py +7 -6
- testgen/reinforcement/statement_coverage_state.py +5 -3
- testgen/service/analysis_service.py +8 -6
- testgen/service/cfg_service.py +2 -0
- testgen/service/generator_service.py +6 -9
- testgen/service/logging_service.py +4 -2
- testgen/service/service.py +11 -20
- testgen/testgen.db +0 -0
- testgen/tests/__init__.py +0 -0
- testgen/tests/test_boolean.py +81 -0
- testgen/tests/test_generated_boolean.py +83 -0
- testgen/util/coverage_visualizer.py +5 -3
- testgen/util/file_utils.py +1 -1
- testgen/visualize/boolean_bin_and_coverage.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v1.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v2.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v3.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v4.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v5.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v6.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v7.png +0 -0
- testgen/visualize/boolean_bin_and_coverage_v8.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v1.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v2.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v3.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v4.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v5.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v6.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v7.png +0 -0
- testgen/visualize/boolean_bin_xor_coverage_v8.png +0 -0
- testgen/visualize/boolean_status_flags_coverage.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v1.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v2.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v3.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v4.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v5.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v6.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v7.png +0 -0
- testgen/visualize/boolean_status_flags_coverage_v8.png +0 -0
- testgenie_py-0.2.4.dist-info/METADATA +139 -0
- testgenie_py-0.2.4.dist-info/RECORD +108 -0
- testgenie_py-0.2.2.dist-info/METADATA +0 -27
- testgenie_py-0.2.2.dist-info/RECORD +0 -67
- {testgenie_py-0.2.2.dist-info → testgenie_py-0.2.4.dist-info}/WHEEL +0 -0
- {testgenie_py-0.2.2.dist-info → testgenie_py-0.2.4.dist-info}/entry_points.txt +0 -0
testgen/reinforcement/agent.py
CHANGED
@@ -4,6 +4,7 @@ from typing import List
|
|
4
4
|
|
5
5
|
from testgen.models.test_case import TestCase
|
6
6
|
from testgen.reinforcement.environment import ReinforcementEnvironment
|
7
|
+
from testgen.service.logging_service import get_logger
|
7
8
|
|
8
9
|
|
9
10
|
class ReinforcementAgent:
|
@@ -13,6 +14,7 @@ class ReinforcementAgent:
|
|
13
14
|
self.env = environment
|
14
15
|
self.q_table = q_table if q_table else {}
|
15
16
|
self.actions = ["add", "merge", "remove", "z3"]
|
17
|
+
self.logger = get_logger()
|
16
18
|
|
17
19
|
def collect_test_cases(self) -> List[TestCase]:
|
18
20
|
max_time = 30
|
@@ -50,7 +52,7 @@ class ReinforcementAgent:
|
|
50
52
|
self.env.reset()
|
51
53
|
|
52
54
|
current_state = self.env.get_state()
|
53
|
-
|
55
|
+
self.logger.debug(f"Current state after reset: {current_state}")
|
54
56
|
|
55
57
|
goal_state: float = 100.0
|
56
58
|
steps_in_episode = 1
|
@@ -59,7 +61,7 @@ class ReinforcementAgent:
|
|
59
61
|
start_time = time.time()
|
60
62
|
|
61
63
|
while current_state[0] != goal_state and steps_in_episode < max_steps_per_episode and time.time() - start_time < max_time:
|
62
|
-
print(f"
|
64
|
+
print(f"\nStep {steps_in_episode} in episode {episode}")
|
63
65
|
|
64
66
|
action = self.choose_action(current_state)
|
65
67
|
next_state, reward = self.env.step(action)
|
@@ -77,10 +79,10 @@ class ReinforcementAgent:
|
|
77
79
|
if current_state[0] > best_coverage:
|
78
80
|
best_coverage = current_state[0]
|
79
81
|
best_test_cases = self.env.test_cases.copy()
|
80
|
-
|
82
|
+
self.logger.debug(f"New best coverage: {best_coverage}% with {len(best_test_cases)} test cases")
|
81
83
|
elif current_state[0] == best_coverage and len(best_test_cases) > len(self.env.test_cases):
|
82
84
|
best_test_cases = self.env.test_cases.copy()
|
83
|
-
|
85
|
+
self.logger.debug(f"New best coverage: {best_coverage}% with {len(best_test_cases)} test cases")
|
84
86
|
|
85
87
|
return best_test_cases
|
86
88
|
|
@@ -106,7 +108,29 @@ class ReinforcementAgent:
|
|
106
108
|
print(f"CHOSEN EXPLOITATION ACTION: {chosen_action}")
|
107
109
|
return chosen_action
|
108
110
|
|
109
|
-
|
111
|
+
|
112
|
+
|
113
|
+
@staticmethod
|
114
|
+
def get_action_list(test_case_length: int) -> List[str]:
|
115
|
+
action_list = ["add", "z3"]
|
116
|
+
if test_case_length >= 2:
|
117
|
+
action_list.extend(["merge", "remove"])
|
118
|
+
return action_list
|
119
|
+
|
120
|
+
def update_q_table(self, state: tuple, action: str, new_state:tuple, reward:float):
|
121
|
+
current_q = self.q_table.get((state, action), 0)
|
122
|
+
self.logger.debug(f"CURRENT Q: {current_q}")
|
123
|
+
valid_actions = self.get_action_list(new_state[1])
|
124
|
+
|
125
|
+
max_next_q = max(self.q_table.get((new_state, a), 0) for a in valid_actions)
|
126
|
+
self.logger.debug(f"MAX NEXT Q: {max_next_q}")
|
127
|
+
|
128
|
+
print(f"UPDATING Q TABLE FOR STATE: {state}, ACTION: {action} WITH REWARD: {reward}")
|
129
|
+
new_q = (1 - self.learning_rate) * current_q + self.learning_rate * (reward + max_next_q)
|
130
|
+
|
131
|
+
self.q_table[(state, action)] = new_q
|
132
|
+
|
133
|
+
"""def optimize_test_suit(self, current_state, executable_statements):
|
110
134
|
# Try to optimize test cases by repeatedly performing remove actions if reached full coverage
|
111
135
|
test_case_count = current_state[1]
|
112
136
|
optimization_attempts = min(10, test_case_count - 1)
|
@@ -130,24 +154,4 @@ class ReinforcementAgent:
|
|
130
154
|
self.env.step("add")
|
131
155
|
break
|
132
156
|
|
133
|
-
return current_state
|
134
|
-
|
135
|
-
@staticmethod
|
136
|
-
def get_action_list(test_case_length: int) -> List[str]:
|
137
|
-
action_list = ["add", "z3"]
|
138
|
-
if test_case_length >= 2:
|
139
|
-
action_list.extend(["merge", "remove"])
|
140
|
-
return action_list
|
141
|
-
|
142
|
-
def update_q_table(self, state: tuple, action: str, new_state:tuple, reward:float):
|
143
|
-
current_q = self.q_table.get((state, action), 0)
|
144
|
-
print(f"CURRENT Q: {current_q}")
|
145
|
-
valid_actions = self.get_action_list(new_state[1])
|
146
|
-
|
147
|
-
max_next_q = max(self.q_table.get((new_state, a), 0) for a in valid_actions)
|
148
|
-
print(f"MAX NEXT Q: {max_next_q}")
|
149
|
-
|
150
|
-
print(f"UPDATING Q TABLE FOR STATE: {state}, ACTION: {action} WITH REWARD: {reward}")
|
151
|
-
new_q = (1 - self.learning_rate) * current_q + self.learning_rate * (reward + max_next_q)
|
152
|
-
|
153
|
-
self.q_table[(state, action)] = new_q
|
157
|
+
return current_state"""
|
@@ -4,6 +4,7 @@ from typing import List, Tuple
|
|
4
4
|
|
5
5
|
import coverage
|
6
6
|
|
7
|
+
from testgen.service.logging_service import get_logger
|
7
8
|
import testgen.util.coverage_utils
|
8
9
|
import testgen.util.file_utils
|
9
10
|
from testgen.reinforcement.abstract_state import AbstractState
|
@@ -21,6 +22,7 @@ class ReinforcementEnvironment:
|
|
21
22
|
self.test_cases = initial_test_cases.copy()
|
22
23
|
self.state = state
|
23
24
|
self.cov = coverage.Coverage()
|
25
|
+
self.logger = get_logger()
|
24
26
|
|
25
27
|
# State represented by covered_statements, test_count
|
26
28
|
def get_state(self) -> Tuple:
|
@@ -29,7 +31,7 @@ class ReinforcementEnvironment:
|
|
29
31
|
def step(self, action) -> Tuple[Tuple, float]:
|
30
32
|
prev_coverage = self.state.get_state()[0] # Get actual coverage before action
|
31
33
|
prev_test_cases = self.state.get_state()[1]
|
32
|
-
|
34
|
+
self.logger.debug(f"STEP: Previous coverage: {prev_coverage} before action: {action}")
|
33
35
|
|
34
36
|
# Execute action
|
35
37
|
if action == "add":
|
@@ -62,8 +64,7 @@ class ReinforcementEnvironment:
|
|
62
64
|
def render(self):
|
63
65
|
pass
|
64
66
|
|
65
|
-
|
66
|
-
def get_reward(coverage_delta, num_test_cases_delta) -> float:
|
67
|
+
def get_reward(self, coverage_delta, num_test_cases_delta) -> float:
|
67
68
|
reward: float
|
68
69
|
"""
|
69
70
|
Reward of 1.0 for increasing coverage
|
@@ -77,7 +78,7 @@ class ReinforcementEnvironment:
|
|
77
78
|
else:
|
78
79
|
reward = -1.0
|
79
80
|
|
80
|
-
|
81
|
+
self.logger.debug(f"Coverage delta reward: {reward}")
|
81
82
|
|
82
83
|
"""
|
83
84
|
If new test cases are added, subtract a small penalty
|
@@ -86,7 +87,7 @@ class ReinforcementEnvironment:
|
|
86
87
|
"""
|
87
88
|
test_cases_factor = (num_test_cases_delta * -0.1)
|
88
89
|
reward = reward + test_cases_factor
|
89
|
-
|
90
|
+
self.logger.debug(f"Reward or penalty added to coverage delta reward: {test_cases_factor}")
|
90
91
|
|
91
92
|
print(f"Final reward {reward}")
|
92
93
|
return reward
|
@@ -100,7 +101,7 @@ class ReinforcementEnvironment:
|
|
100
101
|
|
101
102
|
executable_lines = set()
|
102
103
|
if not test_cases:
|
103
|
-
|
104
|
+
self.logger.debug("Warning: No test cases available to determine executable statements")
|
104
105
|
from testgen.util.randomizer import new_random_test_case
|
105
106
|
temp_case = new_random_test_case(self.file_name, self.class_name, self.fut)
|
106
107
|
analysis = testgen.util.coverage_utils.get_coverage_analysis(self.file_name, self.class_name, self.fut.name, temp_case.inputs)
|
@@ -1,11 +1,13 @@
|
|
1
1
|
from typing import Tuple
|
2
2
|
|
3
|
+
from testgen.service.logging_service import get_logger
|
3
4
|
import testgen.util.coverage_utils
|
4
5
|
from testgen.reinforcement.abstract_state import AbstractState
|
5
6
|
from testgen.util import utils
|
6
7
|
|
7
8
|
class StatementCoverageState(AbstractState):
|
8
9
|
def __init__(self, environment):
|
10
|
+
self.logger = get_logger()
|
9
11
|
self.environment = environment
|
10
12
|
|
11
13
|
def get_state(self) -> Tuple[float, int]:
|
@@ -23,9 +25,9 @@ class StatementCoverageState(AbstractState):
|
|
23
25
|
else:
|
24
26
|
calc_coverage: float = (len(all_covered_statements) / len(executable_statements)) * 100
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
self.logger.debug(f"GET STATE ALL COVERED STATEMENTS: {all_covered_statements}")
|
29
|
+
self.logger.debug(f"GET STATE ALL EXECUTABLE STATEMENTS: {self.environment.get_all_executable_statements()}")
|
30
|
+
self.logger.debug(f"GET STATE FLOAT COVERAGE: {calc_coverage}")
|
29
31
|
|
30
32
|
if calc_coverage >= 100:
|
31
33
|
print(f"!!!!!!!!FULLY COVERED FUNCTION: {self.environment.fut.name}!!!!!!!!")
|
@@ -5,6 +5,7 @@ from types import ModuleType
|
|
5
5
|
from typing import Dict, List
|
6
6
|
|
7
7
|
import testgen
|
8
|
+
from testgen.service.logging_service import get_logger
|
8
9
|
import testgen.util.file_utils
|
9
10
|
import testgen.util.file_utils as file_utils
|
10
11
|
import testgen.util.utils
|
@@ -32,6 +33,7 @@ class AnalysisService:
|
|
32
33
|
self.test_case_analyzer_context = TestCaseAnalyzerContext(None, None)
|
33
34
|
self.test_strategy = 0
|
34
35
|
self.reinforcement_mode = "train"
|
36
|
+
self.logger = get_logger()
|
35
37
|
|
36
38
|
def generate_test_cases(self) -> List[TestCase]:
|
37
39
|
"""Generate test cases using the current strategy."""
|
@@ -43,15 +45,15 @@ class AnalysisService:
|
|
43
45
|
|
44
46
|
def create_analysis_context(self, filepath: str) -> AnalysisContext:
|
45
47
|
"""Create an analysis context for the given file."""
|
46
|
-
|
48
|
+
self.logger.debug(f"Creating analysis context for {filepath}")
|
47
49
|
filename = file_utils.get_filename(filepath)
|
48
|
-
|
50
|
+
self.logger.debug(f"Filename: {filename}")
|
49
51
|
module = file_utils.load_module(filepath)
|
50
|
-
|
52
|
+
self.logger.debug(f"Module: {module}")
|
51
53
|
class_name = self.get_class_name(module)
|
52
|
-
|
54
|
+
self.logger.debug(f"Class name: {class_name}")
|
53
55
|
function_data = self.get_function_data(filename, module, class_name)
|
54
|
-
|
56
|
+
self.logger.debug(f"Function data: {function_data}")
|
55
57
|
return AnalysisContext(filepath, filename, class_name, module, function_data)
|
56
58
|
|
57
59
|
def get_function_data(self, filename: str, module: ModuleType, class_name: str | None) -> List[FunctionMetadata]:
|
@@ -113,7 +115,7 @@ class AnalysisService:
|
|
113
115
|
new_test_cases = agent.collect_test_cases()
|
114
116
|
function_test_cases.extend(new_test_cases)
|
115
117
|
|
116
|
-
print(f"\
|
118
|
+
print(f"\nNumber of test cases for {function.name}: {len(function_test_cases)}")
|
117
119
|
|
118
120
|
current_coverage: float = environment.run_tests()
|
119
121
|
print(f"Current coverage: {function.name}: {current_coverage}")
|
testgen/service/cfg_service.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
from typing import List
|
3
3
|
from testgen.models.test_case import TestCase
|
4
|
+
from testgen.service.logging_service import get_logger
|
4
5
|
from testgen.util.coverage_visualizer import CoverageVisualizer
|
5
6
|
from testgen.service.analysis_service import AnalysisService
|
6
7
|
|
@@ -10,6 +11,7 @@ class CFGService:
|
|
10
11
|
def __init__(self):
|
11
12
|
self.analysis_service = AnalysisService()
|
12
13
|
self.visualizer = None
|
14
|
+
self.logger = get_logger()
|
13
15
|
|
14
16
|
def initialize_visualizer(self, service):
|
15
17
|
self.visualizer = CoverageVisualizer()
|
@@ -9,6 +9,7 @@ from testgen.generator.doctest_generator import DocTestGenerator
|
|
9
9
|
from testgen.generator.pytest_generator import PyTestGenerator
|
10
10
|
from testgen.generator.unit_test_generator import UnitTestGenerator
|
11
11
|
from testgen.inspector.inspector import Inspector
|
12
|
+
from testgen.service.logging_service import get_logger
|
12
13
|
from testgen.tree.node import Node
|
13
14
|
from testgen.tree.tree_utils import build_binary_tree
|
14
15
|
from testgen.models.generator_context import GeneratorContext
|
@@ -26,18 +27,19 @@ class GeneratorService:
|
|
26
27
|
self.code_generator = CodeGenerator()
|
27
28
|
self.test_generator = UnitTestGenerator(generator_context=None)
|
28
29
|
self.generated_file_path = None
|
30
|
+
self.logger = get_logger()
|
29
31
|
|
30
32
|
def set_test_format(self, test_format: int):
|
31
33
|
"""Set the test generator format."""
|
32
34
|
self.test_format = test_format
|
33
35
|
if test_format == UNITTEST_FORMAT:
|
34
|
-
|
36
|
+
self.logger.debug("SETTING TEST FORMAT TO UNITTEST")
|
35
37
|
self.test_generator = UnitTestGenerator(generator_context=None)
|
36
38
|
elif test_format == PYTEST_FORMAT:
|
37
|
-
|
39
|
+
self.logger.debug("SETTING TEST FORMAT TO PYTEST")
|
38
40
|
self.test_generator = PyTestGenerator(generator_context=None)
|
39
41
|
elif test_format == DOCTEST_FORMAT:
|
40
|
-
|
42
|
+
self.logger.debug("SETTING TEST FORMAT TO DOCTEST")
|
41
43
|
self.test_generator = DocTestGenerator(generator_context=None)
|
42
44
|
else:
|
43
45
|
raise NotImplementedError(f"Test format {test_format} not implemented")
|
@@ -69,16 +71,11 @@ class GeneratorService:
|
|
69
71
|
|
70
72
|
self.test_generator.generate_test_header()
|
71
73
|
|
72
|
-
print("GENERATE TEST FILE: Generated test header")
|
73
|
-
|
74
74
|
self.generate_function_tests(test_cases)
|
75
75
|
|
76
|
-
print("GENERATE TEST FILE: Generate function tests")
|
77
|
-
|
78
|
-
print()
|
79
76
|
|
80
77
|
if self.test_format == DOCTEST_FORMAT:
|
81
|
-
|
78
|
+
self.logger.debug("SAVING DOCT TEST FILE")
|
82
79
|
self.test_generator.save_file()
|
83
80
|
return self.filepath
|
84
81
|
else:
|
@@ -15,7 +15,6 @@ class LogLevel(Enum):
|
|
15
15
|
|
16
16
|
class LoggingService:
|
17
17
|
"""Centralized logging service for testgen framework"""
|
18
|
-
|
19
18
|
_instance = None
|
20
19
|
_initialized = False
|
21
20
|
|
@@ -42,6 +41,8 @@ class LoggingService:
|
|
42
41
|
"""Initialize the logging service"""
|
43
42
|
if LoggingService._initialized:
|
44
43
|
return
|
44
|
+
|
45
|
+
self.debug_mode = debug_mode
|
45
46
|
|
46
47
|
# Set the base logging level
|
47
48
|
level = LogLevel.DEBUG.value if debug_mode else LogLevel.INFO.value
|
@@ -69,7 +70,8 @@ class LoggingService:
|
|
69
70
|
|
70
71
|
def debug(self, message: str):
|
71
72
|
"""Log debug message"""
|
72
|
-
self.
|
73
|
+
if self.debug_mode:
|
74
|
+
self.logger.debug(message)
|
73
75
|
|
74
76
|
def info(self, message: str):
|
75
77
|
"""Log info message"""
|
testgen/service/service.py
CHANGED
@@ -36,7 +36,6 @@ DOCTEST_FORMAT = 3
|
|
36
36
|
|
37
37
|
class Service:
|
38
38
|
def __init__(self):
|
39
|
-
self.debug_mode: bool = False
|
40
39
|
self.test_strategy: int = 0
|
41
40
|
self.test_format: int = 0
|
42
41
|
self.file_path = None
|
@@ -78,7 +77,7 @@ class Service:
|
|
78
77
|
test_cases = self.analysis_service.generate_test_cases()
|
79
78
|
|
80
79
|
if os.environ.get("RUNNING_IN_DOCKER") is not None:
|
81
|
-
self.debug(f"Serializing test cases {test_cases}")
|
80
|
+
self.logger.debug(f"Serializing test cases {test_cases}")
|
82
81
|
self.serialize_test_cases(test_cases)
|
83
82
|
return None # Exit early in analysis-only mode
|
84
83
|
|
@@ -151,7 +150,7 @@ class Service:
|
|
151
150
|
"""Run coverage analysis on the generated tests."""
|
152
151
|
Service.wait_for_file(test_file)
|
153
152
|
file_path_to_use = self.generated_file_path if self.test_strategy == AST_STRAT else self.file_path
|
154
|
-
|
153
|
+
self.logger.debug(f"File path to use for coverage: {file_path_to_use}")
|
155
154
|
coverage_output = ""
|
156
155
|
|
157
156
|
try:
|
@@ -179,7 +178,7 @@ class Service:
|
|
179
178
|
"""Parse coverage output and save to database."""
|
180
179
|
# Skip if running in Docker or DB service is None
|
181
180
|
if os.environ.get("RUNNING_IN_DOCKER") is not None or self.db_service is None:
|
182
|
-
self.debug("Skipping database operations in Docker container")
|
181
|
+
self.logger.debug("Skipping database operations in Docker container")
|
183
182
|
return
|
184
183
|
|
185
184
|
try:
|
@@ -370,27 +369,26 @@ class Service:
|
|
370
369
|
functions = inspect.getmembers(cls, inspect.isfunction)
|
371
370
|
return functions
|
372
371
|
|
373
|
-
|
374
|
-
def resolve_module_path(module_name):
|
372
|
+
def resolve_module_path(self, module_name):
|
375
373
|
"""Resolve a module name to its file path by checking multiple locations."""
|
376
374
|
direct_path = f"/controller/{module_name}.py"
|
377
375
|
if os.path.exists(direct_path):
|
378
|
-
|
376
|
+
self.logger.debug(f"Found module at {direct_path}")
|
379
377
|
return direct_path
|
380
378
|
|
381
379
|
testgen_path = f"/controller/testgen/{module_name}.py"
|
382
380
|
if os.path.exists(testgen_path):
|
383
|
-
|
381
|
+
self.logger.debug(f"Found module at {testgen_path}")
|
384
382
|
return testgen_path
|
385
383
|
|
386
384
|
if '.' in module_name:
|
387
385
|
parts = module_name.split('.')
|
388
386
|
potential_path = os.path.join('/controller', *parts) + '.py'
|
389
387
|
if os.path.exists(potential_path):
|
390
|
-
|
388
|
+
self.logger.debug(f"Found module at {potential_path}")
|
391
389
|
return potential_path
|
392
390
|
|
393
|
-
|
391
|
+
self.logger.debug(f"Could not find module: {module_name}")
|
394
392
|
return None
|
395
393
|
|
396
394
|
def visualize_test_coverage(self):
|
@@ -408,13 +406,6 @@ class Service:
|
|
408
406
|
if hasattr(self ,'analysis_service'):
|
409
407
|
self.analysis_service.set_reinforcement_mode(mode)
|
410
408
|
|
411
|
-
def set_debug_mode(self, debug: bool):
|
412
|
-
self.debug_mode = debug
|
413
|
-
|
414
|
-
def debug(self, message: str):
|
415
|
-
if self.debug_mode:
|
416
|
-
self.logger.debug(message)
|
417
|
-
|
418
409
|
def _get_test_case_id(self, test_case_name: str) -> int:
|
419
410
|
"""
|
420
411
|
Retrieve the test case ID from the database based on the test case name.
|
@@ -464,13 +455,13 @@ class Service:
|
|
464
455
|
def execute_and_store_unittest(self, file_path_to_use, test_file):
|
465
456
|
import unittest
|
466
457
|
loader = unittest.TestLoader()
|
467
|
-
|
458
|
+
self.logger.debug(f"Discovering tests in: {os.path.dirname(file_path_to_use)} with pattern: {os.path.basename(test_file)}")
|
468
459
|
test_module = os.path.relpath(test_file,
|
469
460
|
start=os.getcwd()) # Get relative path from the current working directory
|
470
461
|
test_module = test_module.replace("/", ".").replace("\\", ".").rstrip(".py") # Convert to module name
|
471
462
|
if test_module.startswith("."):
|
472
463
|
test_module = test_module[1:] # Remove leading dot if present
|
473
|
-
|
464
|
+
self.logger.debug(f"Test module: {test_module}")
|
474
465
|
suite = loader.loadTestsFromName(test_module)
|
475
466
|
runner = unittest.TextTestRunner()
|
476
467
|
result = runner.run(suite)
|
@@ -506,7 +497,7 @@ class Service:
|
|
506
497
|
|
507
498
|
results = self.db_service.get_test_file_data(test_file_name)
|
508
499
|
if not results:
|
509
|
-
|
500
|
+
self.logger.debug(f"No data found for file: {test_file_name}")
|
510
501
|
return
|
511
502
|
|
512
503
|
from tabulate import tabulate
|
testgen/testgen.db
ADDED
Binary file
|
File without changes
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
import testgen.code_to_test.boolean as boolean
|
4
|
+
|
5
|
+
def test_bin_and_0():
|
6
|
+
args = (False, False)
|
7
|
+
expected = False
|
8
|
+
result = boolean.bin_and(*args)
|
9
|
+
assert result == expected
|
10
|
+
|
11
|
+
def test_bin_and_1():
|
12
|
+
args = (True, True)
|
13
|
+
expected = True
|
14
|
+
result = boolean.bin_and(*args)
|
15
|
+
assert result == expected
|
16
|
+
|
17
|
+
def test_bin_and_2():
|
18
|
+
args = (True, False)
|
19
|
+
expected = False
|
20
|
+
result = boolean.bin_and(*args)
|
21
|
+
assert result == expected
|
22
|
+
|
23
|
+
def test_bin_xor_3():
|
24
|
+
args = (False, True)
|
25
|
+
expected = True
|
26
|
+
result = boolean.bin_xor(*args)
|
27
|
+
assert result == expected
|
28
|
+
|
29
|
+
def test_bin_xor_4():
|
30
|
+
args = (False, False)
|
31
|
+
expected = False
|
32
|
+
result = boolean.bin_xor(*args)
|
33
|
+
assert result == expected
|
34
|
+
|
35
|
+
def test_bin_xor_5():
|
36
|
+
args = (True, True)
|
37
|
+
expected = False
|
38
|
+
result = boolean.bin_xor(*args)
|
39
|
+
assert result == expected
|
40
|
+
|
41
|
+
def test_bin_xor_6():
|
42
|
+
args = (True, False)
|
43
|
+
expected = True
|
44
|
+
result = boolean.bin_xor(*args)
|
45
|
+
assert result == expected
|
46
|
+
|
47
|
+
def test_status_flags_7():
|
48
|
+
args = (True, False, True)
|
49
|
+
expected = 'admin-unverified'
|
50
|
+
result = boolean.status_flags(*args)
|
51
|
+
assert result == expected
|
52
|
+
|
53
|
+
def test_status_flags_8():
|
54
|
+
args = (True, True, False)
|
55
|
+
expected = 'user-verified'
|
56
|
+
result = boolean.status_flags(*args)
|
57
|
+
assert result == expected
|
58
|
+
|
59
|
+
def test_status_flags_9():
|
60
|
+
args = (False, True, True)
|
61
|
+
expected = 'admin-verified'
|
62
|
+
result = boolean.status_flags(*args)
|
63
|
+
assert result == expected
|
64
|
+
|
65
|
+
def test_status_flags_10():
|
66
|
+
args = (True, True, True)
|
67
|
+
expected = 'admin-verified'
|
68
|
+
result = boolean.status_flags(*args)
|
69
|
+
assert result == expected
|
70
|
+
|
71
|
+
def test_status_flags_11():
|
72
|
+
args = (True, False, False)
|
73
|
+
expected = 'user-unverified'
|
74
|
+
result = boolean.status_flags(*args)
|
75
|
+
assert result == expected
|
76
|
+
|
77
|
+
def test_status_flags_12():
|
78
|
+
args = (False, True, False)
|
79
|
+
expected = 'inactive'
|
80
|
+
result = boolean.status_flags(*args)
|
81
|
+
assert result == expected
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import unittest
|
2
|
+
|
3
|
+
import testgen.generated_boolean as generated_boolean
|
4
|
+
|
5
|
+
class TestNone(unittest.TestCase):
|
6
|
+
|
7
|
+
def test_bin_and_0(self):
|
8
|
+
args = (True, True)
|
9
|
+
expected = True
|
10
|
+
result = generated_boolean.bin_and(*args)
|
11
|
+
self.assertEqual(result, expected)
|
12
|
+
|
13
|
+
def test_bin_and_1(self):
|
14
|
+
args = (True, False)
|
15
|
+
expected = False
|
16
|
+
result = generated_boolean.bin_and(*args)
|
17
|
+
self.assertEqual(result, expected)
|
18
|
+
|
19
|
+
def test_bin_and_2(self):
|
20
|
+
args = (False, True)
|
21
|
+
expected = False
|
22
|
+
result = generated_boolean.bin_and(*args)
|
23
|
+
self.assertEqual(result, expected)
|
24
|
+
|
25
|
+
def test_bin_xor_3(self):
|
26
|
+
args = (True, True)
|
27
|
+
expected = False
|
28
|
+
result = generated_boolean.bin_xor(*args)
|
29
|
+
self.assertEqual(result, expected)
|
30
|
+
|
31
|
+
def test_bin_xor_4(self):
|
32
|
+
args = (True, False)
|
33
|
+
expected = True
|
34
|
+
result = generated_boolean.bin_xor(*args)
|
35
|
+
self.assertEqual(result, expected)
|
36
|
+
|
37
|
+
def test_bin_xor_5(self):
|
38
|
+
args = (False, True)
|
39
|
+
expected = True
|
40
|
+
result = generated_boolean.bin_xor(*args)
|
41
|
+
self.assertEqual(result, expected)
|
42
|
+
|
43
|
+
def test_status_flags_6(self):
|
44
|
+
args = (True, True, True)
|
45
|
+
expected = 'admin-verified'
|
46
|
+
result = generated_boolean.status_flags(*args)
|
47
|
+
self.assertEqual(result, expected)
|
48
|
+
|
49
|
+
def test_status_flags_7(self):
|
50
|
+
args = (True, True, False)
|
51
|
+
expected = 'user-verified'
|
52
|
+
result = generated_boolean.status_flags(*args)
|
53
|
+
self.assertEqual(result, expected)
|
54
|
+
|
55
|
+
def test_status_flags_8(self):
|
56
|
+
args = (True, False, True)
|
57
|
+
expected = 'admin-unverified'
|
58
|
+
result = generated_boolean.status_flags(*args)
|
59
|
+
self.assertEqual(result, expected)
|
60
|
+
|
61
|
+
def test_status_flags_9(self):
|
62
|
+
args = (True, False, False)
|
63
|
+
expected = 'user-unverified'
|
64
|
+
result = generated_boolean.status_flags(*args)
|
65
|
+
self.assertEqual(result, expected)
|
66
|
+
|
67
|
+
def test_status_flags_10(self):
|
68
|
+
args = (False, True, True)
|
69
|
+
expected = 'admin-verified'
|
70
|
+
result = generated_boolean.status_flags(*args)
|
71
|
+
self.assertEqual(result, expected)
|
72
|
+
|
73
|
+
def test_status_flags_11(self):
|
74
|
+
args = (False, True, False)
|
75
|
+
expected = 'inactive'
|
76
|
+
result = generated_boolean.status_flags(*args)
|
77
|
+
self.assertEqual(result, expected)
|
78
|
+
|
79
|
+
def test_status_flags_12(self):
|
80
|
+
args = (False, False, True)
|
81
|
+
expected = 'admin-unverified'
|
82
|
+
result = generated_boolean.status_flags(*args)
|
83
|
+
self.assertEqual(result, expected)
|
@@ -3,6 +3,7 @@ import os
|
|
3
3
|
from typing import Dict, List, Set
|
4
4
|
import coverage
|
5
5
|
|
6
|
+
from testgen.service.logging_service import get_logger
|
6
7
|
import testgen.util.coverage_utils
|
7
8
|
from testgen.models.test_case import TestCase
|
8
9
|
import pygraphviz as pgv
|
@@ -12,6 +13,7 @@ class CoverageVisualizer:
|
|
12
13
|
self.service = None
|
13
14
|
self.cov = coverage.Coverage(branch=True)
|
14
15
|
self.covered_lines: Dict[str, Set[int]] = {}
|
16
|
+
self.logger = get_logger()
|
15
17
|
|
16
18
|
def set_service(self, service):
|
17
19
|
self.service = service
|
@@ -27,9 +29,9 @@ class CoverageVisualizer:
|
|
27
29
|
self.covered_lines[func_def.name].update(covered)
|
28
30
|
|
29
31
|
if func_def.name in self.covered_lines:
|
30
|
-
|
32
|
+
self.logger.debug(f"Covered lines for {func_def.name}: {self.covered_lines[func_def.name]}")
|
31
33
|
else:
|
32
|
-
|
34
|
+
self.logger.debug(f"No coverage data found for {func_def.name}")
|
33
35
|
|
34
36
|
def generate_colored_cfg(self, function_name, output_path):
|
35
37
|
"""Generate colored CFG for a function showing test coverage"""
|
@@ -54,7 +56,7 @@ class CoverageVisualizer:
|
|
54
56
|
try:
|
55
57
|
tree = ast.parse(source_code)
|
56
58
|
ast_functions = [node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)]
|
57
|
-
|
59
|
+
self.logger.debug(f"Functions found by AST: {ast_functions}")
|
58
60
|
|
59
61
|
return self._create_basic_cfg(source_code, function_name, output_path)
|
60
62
|
|
testgen/util/file_utils.py
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|