testgenie-py 0.3.7__py3-none-any.whl → 0.3.9__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.
Files changed (49) hide show
  1. testgen/analyzer/ast_analyzer.py +2 -11
  2. testgen/analyzer/fuzz_analyzer.py +1 -6
  3. testgen/analyzer/random_feedback_analyzer.py +20 -293
  4. testgen/analyzer/reinforcement_analyzer.py +59 -57
  5. testgen/analyzer/test_case_analyzer_context.py +0 -6
  6. testgen/controller/cli_controller.py +35 -29
  7. testgen/controller/docker_controller.py +1 -0
  8. testgen/db/dao.py +68 -0
  9. testgen/db/dao_impl.py +226 -0
  10. testgen/{sqlite → db}/db.py +15 -6
  11. testgen/generator/pytest_generator.py +2 -10
  12. testgen/generator/unit_test_generator.py +2 -11
  13. testgen/main.py +1 -3
  14. testgen/models/coverage_data.py +56 -0
  15. testgen/models/db_test_case.py +65 -0
  16. testgen/models/function.py +56 -0
  17. testgen/models/function_metadata.py +11 -1
  18. testgen/models/generator_context.py +30 -3
  19. testgen/models/source_file.py +29 -0
  20. testgen/models/test_result.py +38 -0
  21. testgen/models/test_suite.py +20 -0
  22. testgen/reinforcement/agent.py +1 -27
  23. testgen/reinforcement/environment.py +11 -93
  24. testgen/reinforcement/statement_coverage_state.py +5 -4
  25. testgen/service/analysis_service.py +31 -22
  26. testgen/service/cfg_service.py +3 -1
  27. testgen/service/coverage_service.py +115 -0
  28. testgen/service/db_service.py +140 -0
  29. testgen/service/generator_service.py +77 -20
  30. testgen/service/logging_service.py +2 -2
  31. testgen/service/service.py +62 -231
  32. testgen/service/test_executor_service.py +145 -0
  33. testgen/util/coverage_utils.py +38 -116
  34. testgen/util/coverage_visualizer.py +10 -9
  35. testgen/util/file_utils.py +10 -111
  36. testgen/util/randomizer.py +0 -26
  37. testgen/util/utils.py +197 -38
  38. {testgenie_py-0.3.7.dist-info → testgenie_py-0.3.9.dist-info}/METADATA +1 -1
  39. testgenie_py-0.3.9.dist-info/RECORD +72 -0
  40. testgen/inspector/inspector.py +0 -59
  41. testgen/presentation/__init__.py +0 -0
  42. testgen/presentation/cli_view.py +0 -12
  43. testgen/sqlite/__init__.py +0 -0
  44. testgen/sqlite/db_service.py +0 -239
  45. testgen/testgen.db +0 -0
  46. testgenie_py-0.3.7.dist-info/RECORD +0 -67
  47. /testgen/{inspector → db}/__init__.py +0 -0
  48. {testgenie_py-0.3.7.dist-info → testgenie_py-0.3.9.dist-info}/WHEEL +0 -0
  49. {testgenie_py-0.3.7.dist-info → testgenie_py-0.3.9.dist-info}/entry_points.txt +0 -0
testgen/util/utils.py CHANGED
@@ -1,5 +1,6 @@
1
1
 
2
2
  import ast
3
+ import os
3
4
  import string
4
5
  import sys
5
6
  import random
@@ -7,12 +8,11 @@ from typing import List
7
8
 
8
9
  from atheris import FuzzedDataProvider
9
10
 
11
+ import testgen.util.file_utils
12
+ from testgen.models.function import Function
13
+ from testgen.models.test_case import TestCase
10
14
  from testgen.util.file_utils import load_and_parse_file_for_tree
11
15
 
12
-
13
- def get_func(module, func_name:str):
14
- return getattr(module, func_name)
15
-
16
16
  def extract_parameter_types(func_node):
17
17
  """Extract parameter types from a function node."""
18
18
  param_types = {}
@@ -51,39 +51,6 @@ def generate_random_inputs(param_types):
51
51
  inputs[param] = None
52
52
  return inputs
53
53
 
54
- def generate_extreme_inputs(param_types):
55
- inputs = {}
56
- for param, param_type in param_types.items():
57
- if param_type == "int":
58
- # int is unbounded in Python, but sys.maxsize is the max value representable by a signed word
59
- inputs[param] = sys.maxsize
60
- if param_type == "bool":
61
- random_choice = random.choice([1, 0])
62
- inputs[param] = random_choice
63
- if param_type == "float":
64
- random_choice = random.choice([sys.float_info.min, sys.float_info.max])
65
- inputs[param] = random_choice
66
- if param_type == "str":
67
- inputs[param] = ''.join(random.choice([string.ascii_letters, string.digits, string.punctuation, string.whitespace]) for _ in range(100))
68
-
69
- def generate_inputs_from_fuzz_data(fdp: FuzzedDataProvider, param_types):
70
- """Generate fuzzed inputs based on parameter types."""
71
- inputs = []
72
- for param_type in param_types.values():
73
- if param_type == "int":
74
- inputs.append(fdp.ConsumeInt(4))
75
- elif param_type == "bool":
76
- inputs.append(fdp.ConsumeBool())
77
- elif param_type == "float":
78
- inputs.append(fdp.ConsumeFloat())
79
- elif param_type == "str":
80
- inputs.append(fdp.ConsumeString(10))
81
- elif param_type == "bytes":
82
- inputs.append(fdp.ConsumeBytes(10))
83
- else:
84
- inputs.append(None)
85
- return tuple(inputs)
86
-
87
54
  def get_functions(tree) -> List[ast.FunctionDef]:
88
55
  functions = []
89
56
  for node in tree.body:
@@ -95,6 +62,41 @@ def get_functions(tree) -> List[ast.FunctionDef]:
95
62
  functions.append(class_node)
96
63
  return functions
97
64
 
65
+ def get_list_of_functions(filepath: str) -> List[Function]:
66
+ tree = load_and_parse_file_for_tree(filepath)
67
+ functions = []
68
+ for node in tree.body:
69
+ if isinstance(node, ast.FunctionDef):
70
+ params = extract_parameter_types(node)
71
+ start_line = node.lineno
72
+ end_line = max(
73
+ [line.lineno for line in ast.walk(node) if hasattr(line, 'lineno') and line.lineno],
74
+ default=start_line
75
+ )
76
+ num_lines = end_line - start_line + 1
77
+ name = node.name
78
+ source_file_id = -1 # Placeholder
79
+ functions.append(Function(name, str(params), start_line, end_line, num_lines, source_file_id))
80
+ if isinstance(node, ast.ClassDef):
81
+ for method in node.body:
82
+ if isinstance(method, ast.FunctionDef):
83
+ params = extract_parameter_types(method)
84
+ start_line = method.lineno
85
+ end_line = max(
86
+ [line.lineno for line in ast.walk(method) if hasattr(line, 'lineno') and line.lineno],
87
+ default=start_line
88
+ )
89
+ num_lines = end_line - start_line + 1
90
+
91
+ # Method name (prefixed with class name for clarity)
92
+ name = f"{node.name}.{method.name}"
93
+
94
+ # Source file ID will be set when adding to database
95
+ source_file_id = -1 # Placeholder
96
+
97
+ functions.append(Function(name, str(params), start_line, end_line, num_lines, source_file_id))
98
+ return functions
99
+
98
100
  def get_function_boundaries(file_name: str, func_name: str) -> tuple:
99
101
  tree = load_and_parse_file_for_tree(file_name)
100
102
  for i, node in enumerate(tree.body):
@@ -139,5 +141,162 @@ def get_function_boundaries(file_name: str, func_name: str) -> tuple:
139
141
 
140
142
  raise ValueError(f"Function {func_name} not found in {file_name}")
141
143
 
142
-
144
+ def parse_test_case_from_result_name(name: str, format: int) -> TestCase:
145
+ test_case = TestCase(None, (), None)
146
+ if format == 3:
147
+ test_case = parse_test_case_from_result_name_doctest(name)
148
+ elif format == 2:
149
+ test_case = parse_test_case_from_result_name_pytest(name, format)
150
+ elif format == 1:
151
+ test_case = parse_test_case_from_result_name_unittest(name, format)
152
+ else:
153
+ raise ValueError(f"Unsupported format: {format}")
154
+ return test_case
155
+
156
+ def parse_test_case_from_result_name_pytest(name: str, format: int) -> TestCase:
157
+ parts_name = name.split("::")
158
+ test_filepath = parts_name[0]
159
+ print(f"Parse test case from result name pytest: {test_filepath}")
160
+ test_filepath = os.path.abspath(os.path.join(os.getcwd(), test_filepath))
161
+ test_function_name = parts_name[1]
162
+
163
+ if test_function_name.startswith("test_"):
164
+ function_name_with_int = test_function_name[5:]
165
+ else:
166
+ function_name_with_int = test_function_name
167
+
168
+ parts = function_name_with_int.rsplit('_', 1)
169
+ if len(parts) > 1 and parts[1].isdigit():
170
+ function_name = parts[0]
171
+ else:
172
+ function_name = function_name_with_int
173
+
174
+ args, expected = extract_test_case_data(test_filepath, test_function_name, format)
175
+
176
+ return TestCase(function_name, args, expected)
177
+
178
+ def parse_test_case_from_result_name_unittest(name: str, format: int) -> TestCase:
179
+ parts_name = name.split("::")
180
+ test_filepath = parts_name[0]
181
+ test_function_name_with_mod = parts_name[1]
182
+ test_function_name = test_function_name_with_mod.split('.')[1]
183
+
184
+ args, expected = extract_test_case_data(test_filepath, test_function_name, format)
185
+
186
+ parts = test_function_name.rsplit('_', 1)
187
+ if len(parts) > 1 and parts[1].isdigit():
188
+ function_name = parts[0]
189
+ else:
190
+ function_name = test_function_name
191
+
192
+ return TestCase(function_name, args, expected)
193
+
194
+ # TODO: Find a way to associate doctest results to the actual test case
195
+ def parse_test_case_from_result_name_doctest(name: str) -> TestCase:
196
+ function_name = name.split("::")[1]
197
+ return TestCase(function_name, (), None)
198
+
199
+ def extract_test_case_data(test_file_path: str, function_name: str, test_format: int):
200
+ if not os.path.exists(test_file_path):
201
+ print(f"Error: Test file not found: {test_file_path}")
202
+ return {}, None
203
+
204
+ try:
205
+ with open(test_file_path, 'r') as f:
206
+ file_content = f.read()
207
+
208
+ # For doctests, just return empty data since we can't easily extract it
209
+ if test_format == 3: # Doctest format
210
+ return {}, None
211
+
212
+ # Parse the file using ast
213
+ tree = ast.parse(file_content)
214
+
215
+ # Look for test function definition based on format
216
+ if test_format == 1: # Unittest
217
+ return _extract_unittest_data(tree, function_name)
218
+ elif test_format == 2: # Pytest
219
+ return _extract_pytest_data(tree, function_name)
220
+ else:
221
+ return None
222
+
223
+ except Exception as e:
224
+ print(f"Error extracting test case data: {e}")
225
+ return None
226
+
227
+ def _extract_unittest_data(tree: ast.AST, function_name: str):
228
+ for node in tree.body:
229
+ if isinstance(node, ast.ClassDef):
230
+ # Look for test method within the class
231
+ for class_node in node.body:
232
+ if isinstance(class_node, ast.FunctionDef) and class_node.name == f"test_{function_name}":
233
+ # Find 'args =' and 'expected =' assignments in the function body
234
+ args_value = None
235
+ expected_value = None
236
+
237
+ for stmt in class_node.body:
238
+ if isinstance(stmt, ast.Assign):
239
+ if isinstance(stmt.targets[0], ast.Name):
240
+ if stmt.targets[0].id == "args":
241
+ args_value = _extract_value(stmt.value)
242
+ elif stmt.targets[0].id == "expected":
243
+ expected_value = _extract_value(stmt.value)
244
+
245
+ if args_value is not None:
246
+ # Convert args to dict for consistency
247
+ if isinstance(args_value, tuple):
248
+ return args_value, expected_value
249
+ else:
250
+ return (args_value,), expected_value
251
+
252
+ return None
253
+
254
+ def _extract_pytest_data(tree: ast.AST, function_name: str):
255
+ """Extract args and expected output from pytest-style test file"""
256
+ for node in tree.body:
257
+ if isinstance(node, ast.FunctionDef) and node.name == f"test_{function_name}":
258
+ # Find 'args =' and 'expected =' assignments in the function body
259
+ args_value = None
260
+ expected_value = None
261
+
262
+ for stmt in node.body:
263
+ if isinstance(stmt, ast.Assign):
264
+ if isinstance(stmt.targets[0], ast.Name):
265
+ if stmt.targets[0].id == "args":
266
+ args_value = _extract_value(stmt.value)
267
+ elif stmt.targets[0].id == "expected":
268
+ expected_value = _extract_value(stmt.value)
269
+
270
+ if args_value is not None:
271
+ # Convert args to dict for consistency
272
+ if isinstance(args_value, tuple):
273
+ return args_value, expected_value
274
+ else:
275
+ return (args_value,), expected_value
276
+
277
+ return None
278
+
279
+ def _extract_value(node: ast.AST):
280
+ """Convert an AST node to its Python value"""
281
+ if isinstance(node, ast.Tuple):
282
+ return tuple(_extract_value(elt) for elt in node.elts)
283
+ elif isinstance(node, ast.List):
284
+ return [_extract_value(elt) for elt in node.elts]
285
+ elif isinstance(node, ast.Dict):
286
+ return {_extract_value(key): _extract_value(value) for key, value in zip(node.keys, node.values)}
287
+ elif isinstance(node, ast.Constant):
288
+ return node.value
289
+
290
+
291
+ # If we can't determine the value, try using ast.unparse (Python 3.9+)
292
+ try:
293
+ return eval(ast.unparse(node))
294
+ except:
295
+ try:
296
+ # Fallback for older Python versions
297
+ code = compile(ast.Expression(node), '<string>', 'eval')
298
+ return eval(code)
299
+ except:
300
+ return None
301
+
143
302
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: testgenie-py
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Automated unit test generation tool for Python.
5
5
  Author: cjseitz
6
6
  Author-email: charlesjseitz@gmail.com
@@ -0,0 +1,72 @@
1
+ testgen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ testgen/analyzer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ testgen/analyzer/ast_analyzer.py,sha256=yHpGJLVlyTngcBED50I6j_sRBCIdoT9l7eWIFCsfbwQ,6307
4
+ testgen/analyzer/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ testgen/analyzer/contracts/contract.py,sha256=6rNYJOy_2GrOhGtaXTDIOX6pTEOqo856FxG0yTc8amI,369
6
+ testgen/analyzer/contracts/no_exception_contract.py,sha256=rTWTuu5XKmvzBPD6yNAqiNehk9lbWn_Z8zFj-_djZ_w,512
7
+ testgen/analyzer/contracts/nonnull_contract.py,sha256=uurnrVptImYTVpSnu8ckdlU6c94AbiOWFoH1YnAQU0w,508
8
+ testgen/analyzer/fuzz_analyzer.py,sha256=dzdMoJmosSgOfXCY-EhSLIM6vsdGeKiDqXj0iLS9qqk,3988
9
+ testgen/analyzer/random_feedback_analyzer.py,sha256=1Hj2Zxhr98L35estFGFj8KHTEeo1dbccYiX_Tvz1nmw,11755
10
+ testgen/analyzer/reinforcement_analyzer.py,sha256=rz47udd9DplWmnDeYtwf8_udcXqyLIBMwiz51KdMtQ4,2900
11
+ testgen/analyzer/test_case_analyzer.py,sha256=0qLcRKOrQ4Uyh1ZMecVTkY9bWjNnx7OridigNyjfUk4,1620
12
+ testgen/analyzer/test_case_analyzer_context.py,sha256=akOsq3xnG6himbwb4vpxcYthySj9rhRwXjoKBdFuW5c,1911
13
+ testgen/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ testgen/controller/cli_controller.py,sha256=HXB8KvOkt8tWDFxP3NP5oVRlhRCCK6BpAn4BXyuZF8Q,8835
15
+ testgen/controller/docker_controller.py,sha256=1GqRstLLl93JRDfn0mTmWABYl2fYVxA6QN_1aqhe0w4,9236
16
+ testgen/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ testgen/db/dao.py,sha256=WXCXMrxHyqkB7oURPr0k_C5yDj8ifStZcn8nvpO5M5s,1957
18
+ testgen/db/dao_impl.py,sha256=nE3JmhL_stOQ0XiIjK0_E31z1bHpwmNBTOW4HQvQIUg,8678
19
+ testgen/db/db.py,sha256=ssQWNihmsyWMsmDWHNvWuYR63mtmUSxljMRJ3PwkUQU,2887
20
+ testgen/docker/Dockerfile,sha256=6mutzP0ZkVueuvMLCOy2bsyGxWjLHU-cq7RFkuC8r5Y,781
21
+ testgen/generator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ testgen/generator/code_generator.py,sha256=-Nh5Jt4my29YWA_LKWn98I4jvDA-fNi6AGuw-vJhRV0,3171
23
+ testgen/generator/doctest_generator.py,sha256=B9I-nw7G7ubcx51XLOl1kiOEL0YW0aDuCAKebqfkRRI,8751
24
+ testgen/generator/generator.py,sha256=9M9zg8DO0uU5W5s9hsz2oPCL7bz7sAD087M5TYottSs,2007
25
+ testgen/generator/pytest_generator.py,sha256=UH1olGSePOtJ9zO1r8FsSV24tfO5c1cFoD5m7v5yTI0,2888
26
+ testgen/generator/test_generator.py,sha256=D2Y3DaWH4fdIc5_9Xrznrkm0urFfhpYxuhL81M7RRaw,710
27
+ testgen/generator/unit_test_generator.py,sha256=RwWJ2Pg5ITQx9ZInOtLbwGbjeXXDA1cjLsa9EG__RUk,3114
28
+ testgen/main.py,sha256=sDaLZOKpw2PuExHYr10VDTrglbbWvBfKS6QCqUrHyrE,308
29
+ testgen/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ testgen/models/analysis_context.py,sha256=-mhw_hn26MgF6aJnmj6Hub5jfCi8gmvcf4QNM4l7G2s,1442
31
+ testgen/models/coverage_data.py,sha256=yW9ednji7iU9Dc_1xU5KIU6MbIzSloWVqlysy_1sLZ4,1661
32
+ testgen/models/db_test_case.py,sha256=yQdU-4mfeVV3eSe1dN9xtF83SJcm77dJH9QbbUvZbDY,1777
33
+ testgen/models/function.py,sha256=M-vAkvWnqxJjazT6n0xbirHNjzhrcg4pZewRvzX0PKc,1390
34
+ testgen/models/function_metadata.py,sha256=32udGhHN1-gDy--Np1Azli-OXNLbvspyytlrVBfaQFQ,1795
35
+ testgen/models/generator_context.py,sha256=TtPh5Td8jrEScvpwIuN2qfS31ZP8CAHL6a7vren5x4s,2462
36
+ testgen/models/source_file.py,sha256=-o6MKvvdrlDYZX7qSuAa6F02i25F3i4ckGRQa5M5x5o,738
37
+ testgen/models/test_case.py,sha256=jwodn-6fKHCh4SO2qycbrFyggCb9UlwTZ8k3RdTObDQ,216
38
+ testgen/models/test_result.py,sha256=YFH84ySbhxNotnlnZCWMSLrXrrw4zaABJjrLLJzlVxE,955
39
+ testgen/models/test_suite.py,sha256=hcKX4WSPZ7f2k_Qw24hPrkoO-_JQ49JX7E0BxatGOfg,475
40
+ testgen/reinforcement/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ testgen/reinforcement/abstract_state.py,sha256=M9Kp8fyJzsTzpyL3ca4HjjYpGXy9DRMw-IG7OgFVisY,154
42
+ testgen/reinforcement/agent.py,sha256=lM9ZgJKQZ2_SfBG0-U0RWYwU_nyBL5Upc8yC2JbU_o0,5270
43
+ testgen/reinforcement/environment.py,sha256=8rFSSFjnhkSvS_HSd-VC1J6iBOmoD2imsGFI-MN1fq8,6415
44
+ testgen/reinforcement/statement_coverage_state.py,sha256=N8blj5MBO5V0oxJZLnpoWnTqK3KAu7rTm1CsltsR93M,1743
45
+ testgen/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ testgen/service/analysis_service.py,sha256=Vl9DgxX_JcIOsIMMjEajyT_uys3DPvmlgzdwFQKvRRk,11508
47
+ testgen/service/cfg_service.py,sha256=qjBaUFXPVMAdysx5OcWjIH0d4qOeaSijwPHYVPNglPY,2351
48
+ testgen/service/coverage_service.py,sha256=zpbEJMU23WmfjdPfekkYdDeUKa0WZvJILfk6OYAbG_A,4573
49
+ testgen/service/db_service.py,sha256=BiDUZni647CrePFOS4Ia-wf62j0gYKq3lsEOZphn0Pc,7081
50
+ testgen/service/generator_service.py,sha256=CyzYIM2yp-7h05uTCuhAlPkxL6ZP3Q4N9NdZFn-kSDI,10764
51
+ testgen/service/logging_service.py,sha256=X0HhvSFPXj08634cOfFgq_o685JTlzoNPriUZLpBpac,3195
52
+ testgen/service/service.py,sha256=v5bfQ-mxlyBv1iQQYD06JKgU0xAWgnAVrpKKEyTizBM,14984
53
+ testgen/service/test_executor_service.py,sha256=-37-DSQUmD4cSPC098raV8eYhHlpU11yoqNxBwSS34U,5685
54
+ testgen/tree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
+ testgen/tree/node.py,sha256=ONJtbACShN4yNj1X-UslFRgLyBP6mrbg7qZr3c6dWyA,165
56
+ testgen/tree/tree_utils.py,sha256=gT7jucky6_GWVOlDI6jpv6RMeWCkntGOHIYLvHxD85k,2122
57
+ testgen/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
+ testgen/util/coverage_utils.py,sha256=2jY7I3QFbIWNvpkZDl5jVCd9NJh2_fxiptbsg__MPf4,5666
59
+ testgen/util/coverage_visualizer.py,sha256=arGU7gl5Gm0uByZrc4sGSrJ_2DdY71UG2mOU8-C_Ue0,5974
60
+ testgen/util/file_utils.py,sha256=bfGZ8zZ6QHmnC9VkuYkDFgose586v8GokGGkidApypA,3004
61
+ testgen/util/randomizer.py,sha256=8UqqVDk13N7SnC-8HrBi6wjl7RhAgfh2TMVN5u-ZQxk,4661
62
+ testgen/util/utils.py,sha256=KqJsd54iD4LS0KbsBM3hS0XAdQhdCgkJmGs-bypKb6c,12212
63
+ testgen/util/z3_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ testgen/util/z3_utils/ast_to_z3.py,sha256=V87cvlH2OlO7_owuVTqygymMmK_FyesBovi6qe-BUcg,3180
65
+ testgen/util/z3_utils/branch_condition.py,sha256=N9FNR-iJmxIC62NpDQNVZ1OP14rXXqYzdA_NODPDUm4,2453
66
+ testgen/util/z3_utils/constraint_extractor.py,sha256=RXJLpmk6dAvHZ27839VXKXNtdy9St1F-17-pSEFu4bM,1285
67
+ testgen/util/z3_utils/variable_finder.py,sha256=dUh3F9_L_BDMz1ybiGss09LLcM_egbitgj0FT5Nh9u4,245
68
+ testgen/util/z3_utils/z3_test_case.py,sha256=yF4oJOrXMLzOwDUqXdoeg83MOTl3pvc_lYaZcS01CuQ,4983
69
+ testgenie_py-0.3.9.dist-info/METADATA,sha256=B9Hz4soDJ1VUOEPrWy_gWCwaUjpZuU95lgCbRTNTKss,4350
70
+ testgenie_py-0.3.9.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
71
+ testgenie_py-0.3.9.dist-info/entry_points.txt,sha256=OUN4GqB4zHlHWwWGjwIPbur4E_ZqQgkeeqaCLhzRZgg,47
72
+ testgenie_py-0.3.9.dist-info/RECORD,,
@@ -1,59 +0,0 @@
1
- from ast import FunctionType
2
- import inspect
3
-
4
- class Inspector:
5
-
6
- @staticmethod
7
- def get_functions(file):
8
- return inspect.getmembers(file, inspect.isfunction)
9
-
10
- @staticmethod
11
- def get_signature(func: FunctionType):
12
- return inspect.signature(func)
13
-
14
- @staticmethod
15
- def get_code(func: FunctionType):
16
- return inspect.getsource(func)
17
-
18
- @staticmethod
19
- def get_params(sig: inspect.Signature):
20
- return sig.parameters
21
-
22
- @staticmethod
23
- def get_params_not_self(sig: inspect.Signature):
24
- params = sig.parameters
25
- return [param for param, value in params.items() if param != 'self']
26
-
27
- def func_inspect(self, function_name) -> list[tuple]:
28
- test_cases: list[tuple] = []
29
-
30
- functions = inspect.getmembers(function_name, inspect.isfunction)
31
-
32
- for name, func in functions:
33
- print(f"Function Name: {name}")
34
- signature = inspect.signature(func)
35
- print(f"Signature: {signature}")
36
- for param in signature.parameters:
37
- print(f"Param: {param}")
38
- print(f"Function Code: {inspect.getsource(func)}")
39
-
40
- docstring: str = inspect.getdoc(func)
41
- print(f"Docstring: {docstring}")
42
-
43
- cases: list[str] = docstring.split(",")
44
-
45
- for case in cases:
46
- io: list[str] = case.split("-")
47
- input: str = io[0][7:].strip()
48
- output: str = io[1][8:].strip()
49
- print(f"Input: {input}")
50
- print(f"Output: {output}")
51
- test_cases.append((name, (input, output)))
52
-
53
- return test_cases
54
-
55
-
56
- if __name__ == "__main__":
57
- inspector = Inspector()
58
- test_cases = inspector.func_inspect()
59
- print(f"Collected Test Cases: {test_cases}")
File without changes
@@ -1,12 +0,0 @@
1
- class CLIView:
2
- def __init__(self):
3
- "pass"
4
-
5
- def display_message(self, message: str):
6
- print(f"[INFO] {message}")
7
-
8
- def display_error(self, error: str):
9
- print(f"[ERROR] {error}")
10
-
11
- def prompt_input(self, prompt: str) -> str:
12
- return input(f"{prompt}:> ")
File without changes
@@ -1,239 +0,0 @@
1
- import os
2
- import sqlite3
3
- import json
4
- import time
5
- import ast
6
- from datetime import datetime
7
- from typing import List, Tuple
8
-
9
- from testgen.models.test_case import TestCase
10
- from testgen.sqlite.db import create_database
11
-
12
- class DBService:
13
- def __init__(self, db_name="testgen.db"):
14
- self.db_name = db_name
15
- self.conn = None
16
- self.cursor = None
17
- self._connect()
18
-
19
- def _connect(self):
20
- """Establish connection to the database."""
21
- if not os.path.exists(self.db_name):
22
- create_database(self.db_name)
23
-
24
- self.conn = sqlite3.connect(self.db_name)
25
- self.conn.row_factory = sqlite3.Row
26
- self.cursor = self.conn.cursor()
27
- # Enable foreign keys
28
- self.cursor.execute("PRAGMA foreign_keys = ON;")
29
-
30
- def close(self):
31
- """Close the database connection."""
32
- if self.conn:
33
- self.conn.close()
34
- self.conn = None
35
- self.cursor = None
36
-
37
- def insert_test_suite(self, name: str) -> int:
38
- """Insert a test suite and return its ID."""
39
- self.cursor.execute(
40
- "INSERT INTO TestSuite (name, creation_date) VALUES (?, ?)",
41
- (name, datetime.now())
42
- )
43
- self.conn.commit()
44
- return self.cursor.lastrowid
45
-
46
- def insert_source_file(self, path: str, lines_of_code: int) -> int:
47
- """Insert a source file and return its ID."""
48
- # Check if file already exists
49
- self.cursor.execute("SELECT id FROM SourceFile WHERE path = ?", (path,))
50
- existing = self.cursor.fetchone()
51
- if existing:
52
- return existing[0]
53
-
54
- self.cursor.execute(
55
- "INSERT INTO SourceFile (path, lines_of_code, last_modified) VALUES (?, ?, ?)",
56
- (path, lines_of_code, datetime.now())
57
- )
58
- self.conn.commit()
59
- return self.cursor.lastrowid
60
-
61
- def insert_function(self, name: str, start_line: int, end_line: int, source_file_id: int) -> int:
62
- """Insert a function and return its ID."""
63
- num_lines = end_line - start_line + 1
64
-
65
- # Check if function already exists for this source file
66
- self.cursor.execute(
67
- "SELECT id FROM Function WHERE name = ? AND source_file_id = ?",
68
- (name, source_file_id)
69
- )
70
- existing = self.cursor.fetchone()
71
- if existing:
72
- return existing[0]
73
-
74
- self.cursor.execute(
75
- "INSERT INTO Function (name, start_line, end_line, num_lines, source_file_id) VALUES (?, ?, ?, ?, ?)",
76
- (name, start_line, end_line, num_lines, source_file_id)
77
- )
78
- self.conn.commit()
79
- return self.cursor.lastrowid
80
-
81
- def insert_test_case(self, test_case: TestCase, test_suite_id: int, function_id: int, test_method_type: int) -> int:
82
- """Insert a test case and return its ID."""
83
- # Convert inputs and expected output to JSON strings
84
- inputs_json = json.dumps(test_case.inputs)
85
- expected_json = json.dumps(test_case.expected)
86
-
87
- self.cursor.execute(
88
- "INSERT INTO TestCase (name, expected_output, input, test_function, last_run_time, test_method_type, test_suite_id, function_id) "
89
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
90
- (
91
- f"test_{test_case.func_name}",
92
- expected_json,
93
- inputs_json,
94
- test_case.func_name,
95
- datetime.now(),
96
- test_method_type,
97
- test_suite_id,
98
- function_id
99
- )
100
- )
101
- self.conn.commit()
102
- return self.cursor.lastrowid
103
-
104
- def insert_test_result(self, test_case_id: int, status: bool, error: str = None) -> int:
105
- """Insert a test result and return its ID."""
106
- self.cursor.execute(
107
- "INSERT INTO TestResult (test_case_id, status, error, execution_time) VALUES (?, ?, ?, ?)",
108
- (test_case_id, status, error, datetime.now())
109
- )
110
- self.conn.commit()
111
- return self.cursor.lastrowid
112
-
113
- def insert_coverage_data(self, file_name: str, executed_lines: str, missed_lines: str,
114
- branch_coverage: float, source_file_id: int) -> int:
115
- """Insert coverage data and return its ID."""
116
- self.cursor.execute(
117
- "INSERT INTO CoverageData (file_name, executed_lines, missed_lines, branch_coverage, source_file_id) "
118
- "VALUES (?, ?, ?, ?, ?)",
119
- (file_name, executed_lines, missed_lines, branch_coverage, source_file_id)
120
- )
121
- self.conn.commit()
122
- return self.cursor.lastrowid
123
-
124
- def save_test_generation_data(self, file_path: str, test_cases: List[TestCase],
125
- test_method_type: int, class_name: str = None) -> Tuple[int, List[int]]:
126
- """
127
- Save all data related to a test generation run.
128
- Returns the test suite ID and a list of test case IDs.
129
- """
130
- # Count lines in the source file
131
- with open(file_path, 'r') as f:
132
- lines_of_code = len(f.readlines())
133
-
134
- # Create test suite
135
- strategy_names = {1: "AST", 2: "Fuzz", 3: "Random", 4: "Reinforcement"}
136
- suite_name = f"{strategy_names.get(test_method_type, 'Unknown')}_Suite_{int(time.time())}"
137
- test_suite_id = self.insert_test_suite(suite_name)
138
-
139
- # Insert source file
140
- source_file_id = self.insert_source_file(file_path, lines_of_code)
141
-
142
- # Process functions and test cases
143
- test_case_ids = []
144
- function_ids = {} # Cache function IDs to avoid redundant queries
145
-
146
- for test_case in test_cases:
147
- # Extract function name from test case
148
- func_name = test_case.func_name
149
-
150
- # If function not already processed
151
- if func_name not in function_ids:
152
- # Get function line numbers
153
- start_line, end_line = self._get_function_line_numbers(file_path, func_name)
154
- function_id = self.insert_function(func_name, start_line, end_line, source_file_id)
155
- function_ids[func_name] = function_id
156
-
157
- # Insert test case
158
- test_case_id = self.insert_test_case(
159
- test_case,
160
- test_suite_id,
161
- function_ids[func_name],
162
- test_method_type
163
- )
164
- test_case_ids.append(test_case_id)
165
-
166
- return test_suite_id, test_case_ids
167
-
168
- def _get_function_line_numbers(self, file_path: str, function_name: str) -> Tuple[int, int]:
169
- """
170
- Extract the start and end line numbers for a function in a file.
171
- Returns a tuple of (start_line, end_line).
172
- """
173
- try:
174
- # Load the file and parse it
175
- with open(file_path, 'r') as f:
176
- file_content = f.read()
177
-
178
- tree = ast.parse(file_content)
179
-
180
- # Find the function definition
181
- for node in ast.walk(tree):
182
- if isinstance(node, ast.FunctionDef) and node.name == function_name:
183
- end_line = node.end_lineno if hasattr(node, 'end_lineno') else node.lineno + 5 # Estimate if end_lineno not available
184
- return node.lineno, end_line
185
-
186
- # Also look for class methods
187
- for node in ast.walk(tree):
188
- if isinstance(node, ast.ClassDef):
189
- for class_node in node.body:
190
- if isinstance(class_node, ast.FunctionDef) and class_node.name == function_name:
191
- end_line = class_node.end_lineno if hasattr(class_node, 'end_lineno') else class_node.lineno + 5
192
- return class_node.lineno, end_line
193
- except Exception as e:
194
- print(f"Error getting function line numbers: {e}")
195
-
196
- # If we reach here, the function wasn't found or there was an error
197
- return 0, 0
198
-
199
- def get_test_suites(self):
200
- """Get all test suites from the database."""
201
- self.cursor.execute("SELECT * FROM TestSuite ORDER BY creation_date DESC")
202
- return self.cursor.fetchall()
203
-
204
- def get_test_cases_by_function(self, function_name):
205
- """Get all test cases for a specific function."""
206
- self.cursor.execute(
207
- "SELECT tc.* FROM TestCase tc JOIN Function f ON tc.function_id = f.id WHERE f.name = ?",
208
- (function_name,)
209
- )
210
- return self.cursor.fetchall()
211
-
212
- def get_coverage_by_file(self, file_path):
213
- """Get coverage data for a specific file."""
214
- self.cursor.execute(
215
- "SELECT cd.* FROM CoverageData cd JOIN SourceFile sf ON cd.source_file_id = sf.id WHERE sf.path = ?",
216
- (file_path,)
217
- )
218
- return self.cursor.fetchall()
219
-
220
- def get_test_file_data(self, file_path: str):
221
- """
222
- Retrieve all test cases, coverage data, and test results for a specific file.
223
- """
224
- query = """
225
- SELECT
226
- tc.id AS test_case_id,
227
- tc.name AS test_case_name,
228
- tc.test_function AS test_case_test_function,
229
- tc.test_method_type AS test_case_method_type,
230
- COALESCE(cd.missed_lines, 'None') AS coverage_data_missed_lines
231
- FROM SourceFile sf
232
- LEFT JOIN Function f ON sf.id = f.source_file_id
233
- LEFT JOIN TestCase tc ON f.id = tc.function_id
234
- LEFT JOIN TestResult tr ON tc.id = tr.test_case_id
235
- LEFT JOIN CoverageData cd ON sf.id = cd.source_file_id
236
- WHERE sf.path = ?;
237
- """
238
- self.cursor.execute(query, (file_path,))
239
- return self.cursor.fetchall()
testgen/testgen.db DELETED
Binary file