testgenie-py 0.2.8__tar.gz → 0.3.0__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.
Files changed (109) hide show
  1. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/PKG-INFO +1 -1
  2. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/pyproject.toml +1 -1
  3. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/.coverage +0 -0
  4. testgenie_py-0.3.0/testgen/code_to_test/code_to_fuzz.py +329 -0
  5. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/controller/docker_controller.py +12 -11
  6. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/service/service.py +10 -3
  7. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/testgen.db +0 -0
  8. testgenie_py-0.2.8/testgen/tests/test_code_to_fuzz_lite.py → testgenie_py-0.3.0/testgen/tests/test_code_to_fuzz.py +127 -115
  9. testgenie_py-0.2.8/testgen/code_to_test/code_to_fuzz.py +0 -234
  10. testgenie_py-0.2.8/testgen/generated_boolean.py +0 -48
  11. testgenie_py-0.2.8/testgen/generated_samplecodebin.py +0 -545
  12. testgenie_py-0.2.8/testgen/tests/test_boolean.py +0 -81
  13. testgenie_py-0.2.8/testgen/tests/test_generated_boolean.py +0 -83
  14. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage.png +0 -0
  15. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v1.png +0 -0
  16. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v2.png +0 -0
  17. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v3.png +0 -0
  18. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v4.png +0 -0
  19. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v5.png +0 -0
  20. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v6.png +0 -0
  21. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v7.png +0 -0
  22. testgenie_py-0.2.8/testgen/visualize/boolean_bin_and_coverage_v8.png +0 -0
  23. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage.png +0 -0
  24. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v1.png +0 -0
  25. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v2.png +0 -0
  26. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v3.png +0 -0
  27. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v4.png +0 -0
  28. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v5.png +0 -0
  29. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v6.png +0 -0
  30. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v7.png +0 -0
  31. testgenie_py-0.2.8/testgen/visualize/boolean_bin_xor_coverage_v8.png +0 -0
  32. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage.png +0 -0
  33. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v1.png +0 -0
  34. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v2.png +0 -0
  35. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v3.png +0 -0
  36. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v4.png +0 -0
  37. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v5.png +0 -0
  38. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v6.png +0 -0
  39. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v7.png +0 -0
  40. testgenie_py-0.2.8/testgen/visualize/boolean_status_flags_coverage_v8.png +0 -0
  41. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/README.md +0 -0
  42. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/__init__.py +0 -0
  43. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/__init__.py +0 -0
  44. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/ast_analyzer.py +0 -0
  45. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/contracts/__init__.py +0 -0
  46. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/contracts/contract.py +0 -0
  47. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/contracts/no_exception_contract.py +0 -0
  48. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/contracts/nonnull_contract.py +0 -0
  49. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/fuzz_analyzer.py +0 -0
  50. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/random_feedback_analyzer.py +0 -0
  51. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/reinforcement_analyzer.py +0 -0
  52. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/test_case_analyzer.py +0 -0
  53. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/analyzer/test_case_analyzer_context.py +0 -0
  54. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/__init__.py +0 -0
  55. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/boolean.py +0 -0
  56. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/calculator.py +0 -0
  57. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/code_to_fuzz_lite.py +0 -0
  58. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/decisions.py +0 -0
  59. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/math_utils.py +0 -0
  60. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/code_to_test/sample_code_bin.py +0 -0
  61. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/controller/__init__.py +0 -0
  62. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/controller/cli_controller.py +0 -0
  63. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/docker/Dockerfile +0 -0
  64. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/__init__.py +0 -0
  65. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/code_generator.py +0 -0
  66. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/doctest_generator.py +0 -0
  67. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/generator.py +0 -0
  68. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/pytest_generator.py +0 -0
  69. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/test_generator.py +0 -0
  70. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/generator/unit_test_generator.py +0 -0
  71. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/inspector/__init__.py +0 -0
  72. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/inspector/inspector.py +0 -0
  73. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/main.py +0 -0
  74. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/models/__init__.py +0 -0
  75. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/models/analysis_context.py +0 -0
  76. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/models/function_metadata.py +0 -0
  77. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/models/generator_context.py +0 -0
  78. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/models/test_case.py +0 -0
  79. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/presentation/__init__.py +0 -0
  80. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/presentation/cli_view.py +0 -0
  81. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/reinforcement/__init__.py +0 -0
  82. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/reinforcement/abstract_state.py +0 -0
  83. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/reinforcement/agent.py +0 -0
  84. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/reinforcement/environment.py +0 -0
  85. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/reinforcement/statement_coverage_state.py +0 -0
  86. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/service/__init__.py +0 -0
  87. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/service/analysis_service.py +0 -0
  88. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/service/cfg_service.py +0 -0
  89. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/service/generator_service.py +0 -0
  90. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/service/logging_service.py +0 -0
  91. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/sqlite/__init__.py +0 -0
  92. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/sqlite/db.py +0 -0
  93. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/sqlite/db_service.py +0 -0
  94. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/tests/__init__.py +0 -0
  95. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/tree/__init__.py +0 -0
  96. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/tree/node.py +0 -0
  97. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/tree/tree_utils.py +0 -0
  98. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/__init__.py +0 -0
  99. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/coverage_utils.py +0 -0
  100. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/coverage_visualizer.py +0 -0
  101. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/file_utils.py +0 -0
  102. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/randomizer.py +0 -0
  103. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/utils.py +0 -0
  104. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/z3_utils/__init__.py +0 -0
  105. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/z3_utils/ast_to_z3.py +0 -0
  106. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/z3_utils/branch_condition.py +0 -0
  107. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/z3_utils/constraint_extractor.py +0 -0
  108. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/z3_utils/variable_finder.py +0 -0
  109. {testgenie_py-0.2.8 → testgenie_py-0.3.0}/testgen/util/z3_utils/z3_test_case.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: testgenie-py
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: Automated unit test generation tool for Python.
5
5
  Author: cjseitz
6
6
  Author-email: charlesjseitz@gmail.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "testgenie-py"
3
- version = "0.2.8"
3
+ version = "0.3.0"
4
4
  description = "Automated unit test generation tool for Python."
5
5
  authors = ["cjseitz <charlesjseitz@gmail.com>"]
6
6
  readme = "README.md"
@@ -0,0 +1,329 @@
1
+ def random_fuzz_code(x: int, y: int) -> int:
2
+ result = x
3
+ if x < y:
4
+ result += y
5
+ else:
6
+ result -= y
7
+ return result
8
+
9
+
10
+ def bin_and(a: bool, b: bool) -> bool:
11
+ if a == True:
12
+ if b == True:
13
+ return True
14
+ else:
15
+ return False
16
+ else:
17
+ return False
18
+
19
+
20
+ def pos_or_neg(i: int):
21
+ if i > 0:
22
+ sgn = 1
23
+ else:
24
+ sgn = -1
25
+ if sgn > 0:
26
+ return 1
27
+ elif sgn < 0:
28
+ return -1
29
+ # else sgn == 0
30
+ # no return here
31
+
32
+
33
+ def int_even(x: int):
34
+ if x % 2 == 0:
35
+ return True
36
+ else:
37
+ return False
38
+
39
+ def http_code(code: int) ->str:
40
+ if code < 100 or code > 599:
41
+ return 'invalid'
42
+ elif 100 <= code < 200:
43
+ return 'informational'
44
+ elif 200 <= code < 300:
45
+ return 'success'
46
+ elif 300 <= code < 400:
47
+ return 'redirection'
48
+ elif 400 <= code < 500:
49
+ return 'client error'
50
+ else:
51
+ return 'server error'
52
+
53
+ def add_or_subtract(x: int, y: int) ->int:
54
+ result = x
55
+ if x < y:
56
+ result += y
57
+ else:
58
+ result -= y
59
+ return result
60
+
61
+ def bin_and_bad_generated_function(a: bool, b: bool):
62
+ if a == True:
63
+ if b == True:
64
+ return True
65
+ else:
66
+ return True
67
+ else:
68
+ if b == True:
69
+ return True
70
+ else:
71
+ return False
72
+
73
+
74
+ def bin_nand_generated_function(a: bool, b: bool):
75
+ if a == True:
76
+ if b == True:
77
+ return False
78
+ else:
79
+ return True
80
+ else:
81
+ if b == True:
82
+ return True
83
+ else:
84
+ return True
85
+
86
+
87
+ def bin_nor_generated_function(a: bool, b: bool):
88
+ if a == True:
89
+ if b == True:
90
+ return False
91
+ else:
92
+ return False
93
+ else:
94
+ if b == True:
95
+ return False
96
+ else:
97
+ return True
98
+
99
+
100
+ def bin_or_generated_function(a: bool, b: bool):
101
+ if a == True:
102
+ if b == True:
103
+ return True
104
+ else:
105
+ return True
106
+ else:
107
+ if b == True:
108
+ return True
109
+ else:
110
+ return False
111
+
112
+
113
+ def bin_or_bad_generated_function(a: bool, b: bool):
114
+ if a == True:
115
+ if b == True:
116
+ return False
117
+ else:
118
+ return False
119
+ else:
120
+ if b == True:
121
+ return False
122
+ else:
123
+ return True
124
+
125
+
126
+ def bin_xor_generated_function(a: bool, b: bool):
127
+ if a == True:
128
+ if b == True:
129
+ return False
130
+ else:
131
+ return True
132
+ else:
133
+ if b == True:
134
+ return True
135
+ else:
136
+ return False
137
+
138
+
139
+ def mux_generated_function(c1: bool, c2: bool, x0: bool, x1: bool, x2: bool, x3: bool):
140
+ if c1 == True:
141
+ if c2 == True:
142
+ if x0 == True:
143
+ if x1 == True:
144
+ if x2 == True:
145
+ if x3 == True:
146
+ return True
147
+ else:
148
+ return True
149
+ else:
150
+ if x3 == True:
151
+ return True
152
+ else:
153
+ return True
154
+ else:
155
+ if x2 == True:
156
+ if x3 == True:
157
+ return True
158
+ else:
159
+ return True
160
+ else:
161
+ if x3 == True:
162
+ return True
163
+ else:
164
+ return True
165
+ else:
166
+ if x1 == True:
167
+ if x2 == True:
168
+ if x3 == True:
169
+ return False
170
+ else:
171
+ return False
172
+ else:
173
+ if x3 == True:
174
+ return False
175
+ else:
176
+ return False
177
+ else:
178
+ if x2 == True:
179
+ if x3 == True:
180
+ return False
181
+ else:
182
+ return False
183
+ else:
184
+ if x3 == True:
185
+ return False
186
+ else:
187
+ return False
188
+ else:
189
+ if x0 == True:
190
+ if x1 == True:
191
+ if x2 == True:
192
+ if x3 == True:
193
+ return True
194
+ else:
195
+ return True
196
+ else:
197
+ if x3 == True:
198
+ return True
199
+ else:
200
+ return True
201
+ else:
202
+ if x2 == True:
203
+ if x3 == True:
204
+ return False
205
+ else:
206
+ return False
207
+ else:
208
+ if x3 == True:
209
+ return False
210
+ else:
211
+ return False
212
+ else:
213
+ if x1 == True:
214
+ if x2 == True:
215
+ if x3 == True:
216
+ return True
217
+ else:
218
+ return True
219
+ else:
220
+ if x3 == True:
221
+ return True
222
+ else:
223
+ return True
224
+ else:
225
+ if x2 == True:
226
+ if x3 == True:
227
+ return False
228
+ else:
229
+ return False
230
+ else:
231
+ if x3 == True:
232
+ return False
233
+ else:
234
+ return False
235
+ else:
236
+ if c2 == True:
237
+ if x0 == True:
238
+ if x1 == True:
239
+ if x2 == True:
240
+ if x3 == True:
241
+ return True
242
+ else:
243
+ return True
244
+ else:
245
+ if x3 == True:
246
+ return False
247
+ else:
248
+ return False
249
+ else:
250
+ if x2 == True:
251
+ if x3 == True:
252
+ return True
253
+ else:
254
+ return True
255
+ else:
256
+ if x3 == True:
257
+ return False
258
+ else:
259
+ return False
260
+ else:
261
+ if x1 == True:
262
+ if x2 == True:
263
+ if x3 == True:
264
+ return True
265
+ else:
266
+ return True
267
+ else:
268
+ if x3 == True:
269
+ return False
270
+ else:
271
+ return False
272
+ else:
273
+ if x2 == True:
274
+ if x3 == True:
275
+ return True
276
+ else:
277
+ return True
278
+ else:
279
+ if x3 == True:
280
+ return False
281
+ else:
282
+ return False
283
+ else:
284
+ if x0 == True:
285
+ if x1 == True:
286
+ if x2 == True:
287
+ if x3 == True:
288
+ return True
289
+ else:
290
+ return False
291
+ else:
292
+ if x3 == True:
293
+ return True
294
+ else:
295
+ return False
296
+ else:
297
+ if x2 == True:
298
+ if x3 == True:
299
+ return True
300
+ else:
301
+ return False
302
+ else:
303
+ if x3 == True:
304
+ return True
305
+ else:
306
+ return False
307
+ else:
308
+ if x1 == True:
309
+ if x2 == True:
310
+ if x3 == True:
311
+ return True
312
+ else:
313
+ return False
314
+ else:
315
+ if x3 == True:
316
+ return True
317
+ else:
318
+ return False
319
+ else:
320
+ if x2 == True:
321
+ if x3 == True:
322
+ return True
323
+ else:
324
+ return False
325
+ else:
326
+ if x3 == True:
327
+ return True
328
+ else:
329
+ return False
@@ -1,4 +1,3 @@
1
- import importlib
2
1
  import os
3
2
  import sys
4
3
  from argparse import Namespace
@@ -6,7 +5,10 @@ import docker
6
5
  from docker import DockerClient, client
7
6
  from docker import errors
8
7
  from docker.models.containers import Container
9
- import importlib.resources
8
+ import importlib.resources as pkg_resources
9
+ import tempfile
10
+ import shutil
11
+
10
12
  from testgen.service.logging_service import get_logger
11
13
  from testgen.service.service import Service
12
14
 
@@ -134,7 +136,6 @@ class DockerController:
134
136
  print(f"Docker args: {docker_args}")
135
137
  print(f"Project root: {project_root}")
136
138
 
137
-
138
139
  return docker_client.containers.run(
139
140
  image=image_name,
140
141
  command=["testgenie"] + docker_args,
@@ -192,23 +193,23 @@ class DockerController:
192
193
  return False
193
194
 
194
195
  def get_dockerfile_path(self) -> str:
195
- print("Get dockerfile path")
196
+ # First, try local development path
196
197
  local_dockerfile = os.path.join(os.path.dirname(__file__), "docker", "Dockerfile")
197
- print(f"Checking local path: {local_dockerfile}")
198
198
  if os.path.exists(local_dockerfile):
199
+ self.debug(f"Found local Dockerfile at: {local_dockerfile}")
199
200
  return local_dockerfile
200
201
 
201
202
  # If not found locally, try inside installed package
202
203
  try:
203
- print("With importlib.resources")
204
- with importlib.resources.path('testgen.docker', 'Dockerfile') as dockerfile_path:
205
- print(f"Checking package path: {dockerfile_path}")
206
- if dockerfile_path.exists():
207
- return str(dockerfile_path)
204
+ dockerfile_resource = pkg_resources.files('testgen').joinpath('docker/Dockerfile')
205
+ if dockerfile_resource.is_file():
206
+ self.debug(f"Found package-installed Dockerfile at: {dockerfile_resource}")
207
+ return str(dockerfile_resource)
208
208
  except Exception as e:
209
209
  print(f"Error locating Dockerfile in package resources: {e}")
210
210
 
211
- raise FileNotFoundError("Dockerfile not found in local project or package.")
211
+ print("Dockerfile not found in local project or package.")
212
+ sys.exit(1)
212
213
 
213
214
  @staticmethod
214
215
  def is_inside_docker() -> bool:
@@ -155,7 +155,14 @@ class Service:
155
155
 
156
156
  try:
157
157
  if self.test_format == UNITTEST_FORMAT:
158
- self.execute_and_store_unittest(file_path_to_use, test_file)
158
+ subprocess.run(["python", "-m", "coverage", "run", "--source=.", "-m", "unittest", test_file], check=True)
159
+ result = subprocess.run(
160
+ ["python", "-m", "coverage", "report", file_path_to_use],
161
+ check=True,
162
+ capture_output=True,
163
+ text=True
164
+ )
165
+ coverage_output = result.stdout
159
166
  elif self.test_format == PYTEST_FORMAT:
160
167
  self.execute_and_store_pytest(test_file)
161
168
  elif self.test_format == DOCTEST_FORMAT:
@@ -457,8 +464,8 @@ class Service:
457
464
  loader = unittest.TestLoader()
458
465
  self.logger.debug(f"Discovering tests in: {os.path.dirname(file_path_to_use)} with pattern: {os.path.basename(test_file)}")
459
466
  test_module = os.path.relpath(test_file,
460
- start=os.getcwd())
461
- test_module = test_module.replace("/", ".").replace("\\", ".").rstrip(".py")
467
+ start=os.getcwd()) # Get relative path from the current working directory
468
+ test_module = test_module.replace("/", ".").replace("\\", ".").rstrip(".py") # Convert to module name
462
469
  if test_module.startswith("."):
463
470
  test_module = test_module[1:] # Remove leading dot if present
464
471
  self.logger.debug(f"Test module: {test_module}")