testgenie-py 0.2.3__tar.gz → 0.2.5__tar.gz
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.
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/PKG-INFO +1 -1
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/pyproject.toml +1 -1
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/ast_analyzer.py +4 -1
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/fuzz_analyzer.py +2 -2
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/random_feedback_analyzer.py +3 -3
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/test_case_analyzer.py +2 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/controller/cli_controller.py +0 -1
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/controller/docker_controller.py +6 -11
- testgenie_py-0.2.5/testgen/generated_boolean.py +48 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/reinforcement/agent.py +30 -26
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/reinforcement/environment.py +7 -6
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/reinforcement/statement_coverage_state.py +5 -3
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/service/analysis_service.py +8 -6
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/service/cfg_service.py +2 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/service/generator_service.py +6 -9
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/service/logging_service.py +4 -2
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/service/service.py +11 -20
- testgenie_py-0.2.5/testgen/testgen.db +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/coverage_visualizer.py +5 -3
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/file_utils.py +1 -1
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/README.md +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/contracts/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/contracts/contract.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/contracts/no_exception_contract.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/contracts/nonnull_contract.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/reinforcement_analyzer.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/test_case_analyzer_context.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/controller/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/docker/Dockerfile +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generated_samplecodebin.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/code_generator.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/doctest_generator.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/generator.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/pytest_generator.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/test_generator.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/generator/unit_test_generator.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/inspector/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/inspector/inspector.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/main.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/models/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/models/analysis_context.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/models/function_metadata.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/models/generator_context.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/models/test_case.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/presentation/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/presentation/cli_view.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/reinforcement/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/reinforcement/abstract_state.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/service/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/sqlite/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/sqlite/db.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/sqlite/db_service.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/tree/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/tree/node.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/tree/tree_utils.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/coverage_utils.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/randomizer.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/utils.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/z3_utils/__init__.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/z3_utils/ast_to_z3.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/z3_utils/branch_condition.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/z3_utils/constraint_extractor.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/z3_utils/variable_finder.py +0 -0
- {testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/util/z3_utils/z3_test_case.py +0 -0
@@ -8,6 +8,9 @@ from abc import ABC
|
|
8
8
|
from testgen.models.function_metadata import FunctionMetadata
|
9
9
|
|
10
10
|
class ASTAnalyzer(TestCaseAnalyzerStrategy, ABC):
|
11
|
+
def __init__(self, analysis_context=None):
|
12
|
+
super().__init__(analysis_context)
|
13
|
+
|
11
14
|
def collect_test_cases(self, function_metadata: FunctionMetadata) -> List[TestCase]:
|
12
15
|
"""Collect test cases by analyzing AST conditions and return statements"""
|
13
16
|
|
@@ -34,7 +37,7 @@ class ASTAnalyzer(TestCaseAnalyzerStrategy, ABC):
|
|
34
37
|
for node in func_node_body:
|
35
38
|
if isinstance(node, ast.If):
|
36
39
|
condition_str = self.parse_condition(node.test)
|
37
|
-
|
40
|
+
self.logger.debug(f"Condition found in function: {condition_str}")
|
38
41
|
self.get_conditions_recursively(function_metadata, func_name, node.body, param_names, test_cases, conditions + [condition_str])
|
39
42
|
if node.orelse:
|
40
43
|
self.get_conditions_recursively(function_metadata, func_name, node.orelse, param_names, test_cases, conditions)
|
@@ -61,8 +61,8 @@ class FuzzAnalyzer(TestCaseAnalyzerStrategy, ABC):
|
|
61
61
|
covered_branches = self.get_branch_coverage(module)
|
62
62
|
covered_branches_tuple = tuple(covered_branches)
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
self.logger.debug(f"[COVERED_BRANCHES]: {covered_branches}")
|
65
|
+
self.logger.debug(f"[EXECUTED BRANCHES]: {self.executed_branches}")
|
66
66
|
|
67
67
|
for branch in covered_branches:
|
68
68
|
if branch[1] < 0:
|
@@ -74,7 +74,7 @@ class RandomFeedbackAnalyzer(TestCaseAnalyzerStrategy, ABC):
|
|
74
74
|
break
|
75
75
|
else:
|
76
76
|
# Optionally log duplicate detection
|
77
|
-
|
77
|
+
self.logger.debug(f"Skipping duplicate test case: {func_name}{test_case.inputs}")
|
78
78
|
|
79
79
|
except Exception as e:
|
80
80
|
print(f"Error testing {function_metadata.function_name}: {e}")
|
@@ -97,10 +97,10 @@ class RandomFeedbackAnalyzer(TestCaseAnalyzerStrategy, ABC):
|
|
97
97
|
func.function_name, test_case.inputs)
|
98
98
|
covered = coverage_utils.get_list_of_covered_statements(analysis)
|
99
99
|
self.covered_lines[func.function_name].update(covered)
|
100
|
-
|
100
|
+
self.logger.debug(f"Covered lines for {func.function_name}: {self.covered_lines[func.function_name]}")
|
101
101
|
|
102
102
|
executable_statements = set(self.get_all_executable_statements(func))
|
103
|
-
|
103
|
+
self.logger.debug(f"Executable statements for {func.function_name}: {executable_statements}")
|
104
104
|
|
105
105
|
return self.covered_lines[func.function_name] == executable_statements
|
106
106
|
|
@@ -5,10 +5,12 @@ from typing import List, Dict
|
|
5
5
|
from testgen.models.test_case import TestCase
|
6
6
|
from testgen.models.analysis_context import AnalysisContext
|
7
7
|
from testgen.models.function_metadata import FunctionMetadata
|
8
|
+
from testgen.service.logging_service import get_logger
|
8
9
|
|
9
10
|
class TestCaseAnalyzerStrategy(ABC):
|
10
11
|
def __init__(self, analysis_context: AnalysisContext = None):
|
11
12
|
self._analysis_context = analysis_context
|
13
|
+
self.logger = get_logger()
|
12
14
|
|
13
15
|
@abstractmethod
|
14
16
|
def collect_test_cases(self, function_metadata: FunctionMetadata) -> List[TestCase]:
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import importlib
|
1
2
|
import os
|
2
3
|
import sys
|
3
4
|
from argparse import Namespace
|
@@ -5,10 +6,7 @@ import docker
|
|
5
6
|
from docker import DockerClient, client
|
6
7
|
from docker import errors
|
7
8
|
from docker.models.containers import Container
|
8
|
-
import importlib.resources
|
9
|
-
import tempfile
|
10
|
-
import shutil
|
11
|
-
|
9
|
+
import importlib.resources
|
12
10
|
from testgen.service.logging_service import get_logger
|
13
11
|
from testgen.service.service import Service
|
14
12
|
|
@@ -201,20 +199,17 @@ class DockerController:
|
|
201
199
|
# First, try local development path
|
202
200
|
local_dockerfile = os.path.join(os.path.dirname(__file__), "docker", "Dockerfile")
|
203
201
|
if os.path.exists(local_dockerfile):
|
204
|
-
self.debug(f"Found local Dockerfile at: {local_dockerfile}")
|
205
202
|
return local_dockerfile
|
206
203
|
|
207
204
|
# If not found locally, try inside installed package
|
208
205
|
try:
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
return str(dockerfile_resource)
|
206
|
+
with importlib.resources.path('testgen.docker', 'Dockerfile') as dockerfile_path:
|
207
|
+
if dockerfile_path.exists():
|
208
|
+
return str(dockerfile_path)
|
213
209
|
except Exception as e:
|
214
210
|
print(f"Error locating Dockerfile in package resources: {e}")
|
215
211
|
|
216
|
-
|
217
|
-
sys.exit(1)
|
212
|
+
raise FileNotFoundError("Dockerfile not found in local project or package.")
|
218
213
|
|
219
214
|
@staticmethod
|
220
215
|
def is_inside_docker() -> bool:
|
@@ -0,0 +1,48 @@
|
|
1
|
+
def bin_and(a: bool, b: bool):
|
2
|
+
if a == True:
|
3
|
+
if b == True:
|
4
|
+
return True
|
5
|
+
else:
|
6
|
+
return False
|
7
|
+
else:
|
8
|
+
if b == True:
|
9
|
+
return False
|
10
|
+
else:
|
11
|
+
return False
|
12
|
+
|
13
|
+
def bin_xor(a: bool, b: bool):
|
14
|
+
if a == True:
|
15
|
+
if b == True:
|
16
|
+
return False
|
17
|
+
else:
|
18
|
+
return True
|
19
|
+
else:
|
20
|
+
if b == True:
|
21
|
+
return True
|
22
|
+
else:
|
23
|
+
return False
|
24
|
+
|
25
|
+
def status_flags(active: bool, verified: bool, admin: bool):
|
26
|
+
if active == True:
|
27
|
+
if verified == True:
|
28
|
+
if admin == True:
|
29
|
+
return 'admin-verified'
|
30
|
+
else:
|
31
|
+
return 'user-verified'
|
32
|
+
else:
|
33
|
+
if admin == True:
|
34
|
+
return 'admin-unverified'
|
35
|
+
else:
|
36
|
+
return 'user-unverified'
|
37
|
+
else:
|
38
|
+
if verified == True:
|
39
|
+
if admin == True:
|
40
|
+
return 'admin-verified'
|
41
|
+
else:
|
42
|
+
return 'inactive'
|
43
|
+
else:
|
44
|
+
if admin == True:
|
45
|
+
return 'admin-unverified'
|
46
|
+
else:
|
47
|
+
return 'inactive'
|
48
|
+
|
@@ -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}")
|
@@ -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"""
|
@@ -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
|
Binary file
|
@@ -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
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{testgenie_py-0.2.3 → testgenie_py-0.2.5}/testgen/analyzer/contracts/no_exception_contract.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|