orionis 0.432.0__py3-none-any.whl → 0.435.0__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.
- orionis/app.py +17 -0
- orionis/metadata/framework.py +1 -1
- orionis/support/entities/base.py +18 -37
- orionis/support/facades/console.py +3 -9
- orionis/support/facades/dumper.py +3 -9
- orionis/support/facades/logger.py +3 -9
- orionis/support/facades/path_resolver.py +3 -10
- orionis/support/facades/progress_bar.py +3 -10
- orionis/support/facades/testing.py +4 -10
- orionis/support/facades/workers.py +4 -9
- orionis/support/formatter/exceptions/contracts/parser.py +10 -7
- orionis/support/formatter/exceptions/parser.py +28 -26
- orionis/support/formatter/serializer.py +12 -5
- orionis/support/patterns/singleton/meta.py +17 -21
- orionis/support/standard/contracts/std.py +25 -24
- orionis/support/standard/exceptions/value.py +2 -2
- orionis/support/standard/std.py +26 -24
- orionis/support/wrapper/dot_dict.py +16 -51
- orionis/test/cases/asynchronous.py +17 -81
- orionis/test/cases/synchronous.py +17 -73
- orionis/test/contracts/dumper.py +17 -21
- orionis/test/contracts/kernel.py +5 -12
- orionis/test/contracts/logs.py +16 -21
- orionis/test/contracts/printer.py +70 -8
- orionis/test/contracts/render.py +7 -13
- orionis/test/contracts/test_result.py +58 -27
- orionis/test/contracts/unit_test.py +18 -18
- orionis/test/core/unit_test.py +325 -445
- orionis/test/entities/result.py +49 -21
- orionis/test/enums/status.py +11 -17
- orionis/test/exceptions/config.py +4 -8
- orionis/test/exceptions/failure.py +2 -18
- orionis/test/exceptions/persistence.py +4 -8
- orionis/test/exceptions/runtime.py +4 -8
- orionis/test/exceptions/value.py +5 -13
- orionis/test/kernel.py +14 -42
- orionis/test/output/dumper.py +21 -43
- orionis/test/output/printer.py +6 -146
- orionis/test/records/logs.py +57 -121
- orionis/test/validators/base_path.py +8 -6
- orionis/test/validators/execution_mode.py +2 -3
- orionis/test/validators/fail_fast.py +4 -8
- orionis/test/validators/folder_path.py +5 -7
- orionis/test/validators/module_name.py +3 -3
- orionis/test/validators/name_pattern.py +4 -9
- orionis/test/validators/pattern.py +4 -9
- orionis/test/validators/persistent.py +4 -14
- orionis/test/validators/persistent_driver.py +7 -12
- orionis/test/validators/print_result.py +4 -9
- orionis/test/validators/tags.py +6 -7
- orionis/test/validators/throw_exception.py +7 -14
- orionis/test/validators/verbosity.py +15 -5
- orionis/test/validators/web_report.py +6 -10
- orionis/test/validators/workers.py +9 -4
- orionis/test/view/render.py +9 -26
- {orionis-0.432.0.dist-info → orionis-0.435.0.dist-info}/METADATA +1 -1
- {orionis-0.432.0.dist-info → orionis-0.435.0.dist-info}/RECORD +76 -75
- tests/support/entities/mock_dataclass.py +16 -10
- tests/support/entities/test_base.py +6 -14
- tests/support/patterns/singleton/test_patterns_singleton.py +7 -8
- tests/support/standard/test_services_std.py +113 -37
- tests/support/wrapper/test_services_wrapper_docdict.py +25 -40
- tests/testing/cases/test_testing_asynchronous.py +14 -14
- tests/testing/cases/test_testing_synchronous.py +12 -14
- tests/testing/entities/test_testing_result.py +12 -51
- tests/testing/enums/test_testing_status.py +8 -13
- tests/testing/output/test_testing_dumper.py +3 -6
- tests/testing/output/test_testing_printer.py +5 -5
- tests/testing/records/test_testing_records.py +16 -26
- tests/testing/test_testing_unit.py +8 -94
- tests/testing/validators/test_testing_validators.py +55 -112
- tests/testing/view/test_render.py +4 -5
- {orionis-0.432.0.dist-info → orionis-0.435.0.dist-info}/WHEEL +0 -0
- {orionis-0.432.0.dist-info → orionis-0.435.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.432.0.dist-info → orionis-0.435.0.dist-info}/top_level.txt +0 -0
- {orionis-0.432.0.dist-info → orionis-0.435.0.dist-info}/zip-safe +0 -0
orionis/test/core/unit_test.py
CHANGED
|
@@ -51,81 +51,68 @@ class UnitTest(IUnitTest):
|
|
|
51
51
|
|
|
52
52
|
Advanced unit testing manager for the Orionis framework.
|
|
53
53
|
|
|
54
|
-
This class
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
This class provides mechanisms for discovering, executing, and reporting unit tests with extensive configurability. It supports sequential and parallel execution, test filtering by name or tags, and detailed result tracking including execution times, error messages, and tracebacks.
|
|
55
|
+
|
|
56
|
+
Attributes
|
|
57
|
+
----------
|
|
58
|
+
__app : Optional[IApplication]
|
|
59
|
+
Application instance for dependency injection.
|
|
60
|
+
__verbosity : Optional[int]
|
|
61
|
+
Verbosity level for test output.
|
|
62
|
+
__execution_mode : Optional[str]
|
|
63
|
+
Execution mode for tests ('SEQUENTIAL' or 'PARALLEL').
|
|
64
|
+
__max_workers : Optional[int]
|
|
65
|
+
Maximum number of workers for parallel execution.
|
|
66
|
+
__fail_fast : Optional[bool]
|
|
67
|
+
Whether to stop on first failure.
|
|
68
|
+
__throw_exception : Optional[bool]
|
|
69
|
+
Whether to raise exceptions on test failures.
|
|
70
|
+
__persistent : Optional[bool]
|
|
71
|
+
Whether to persist test results.
|
|
72
|
+
__persistent_driver : Optional[str]
|
|
73
|
+
Persistence driver ('sqlite' or 'json').
|
|
74
|
+
__web_report : Optional[bool]
|
|
75
|
+
Whether to generate a web report.
|
|
76
|
+
__folder_path : Optional[str]
|
|
77
|
+
Folder path for test discovery.
|
|
78
|
+
__base_path : Optional[str]
|
|
79
|
+
Base directory for test discovery.
|
|
80
|
+
__pattern : Optional[str]
|
|
81
|
+
File name pattern for test discovery.
|
|
82
|
+
__test_name_pattern : Optional[str]
|
|
83
|
+
Pattern for filtering test names.
|
|
84
|
+
__tags : Optional[List[str]]
|
|
85
|
+
Tags for filtering tests.
|
|
86
|
+
__module_name : Optional[str]
|
|
87
|
+
Module name for test discovery.
|
|
88
|
+
__loader : unittest.TestLoader
|
|
89
|
+
Loader for discovering tests.
|
|
90
|
+
__suite : unittest.TestSuite
|
|
91
|
+
Test suite containing discovered tests.
|
|
92
|
+
__discovered_tests : List
|
|
93
|
+
List of discovered test metadata.
|
|
94
|
+
__printer : Optional[TestPrinter]
|
|
95
|
+
Utility for printing test results.
|
|
96
|
+
__output_buffer : Optional[str]
|
|
97
|
+
Buffer for capturing standard output.
|
|
98
|
+
__error_buffer : Optional[str]
|
|
99
|
+
Buffer for capturing error output.
|
|
100
|
+
__result : Optional[dict]
|
|
101
|
+
Result summary of the test execution.
|
|
64
102
|
"""
|
|
65
103
|
|
|
66
104
|
def __init__(
|
|
67
105
|
self
|
|
68
106
|
) -> None:
|
|
69
107
|
"""
|
|
70
|
-
Initialize a
|
|
108
|
+
Initialize a UnitTest instance with default configuration and internal state.
|
|
71
109
|
|
|
72
|
-
|
|
73
|
-
result reporting, and configuration management. It prepares the instance for further
|
|
74
|
-
configuration and use, but does not perform any test discovery or execution itself.
|
|
75
|
-
|
|
76
|
-
Attributes
|
|
77
|
-
----------
|
|
78
|
-
__app : Optional[IApplication]
|
|
79
|
-
The application instance used for dependency injection in test cases.
|
|
80
|
-
__verbosity : Optional[int]
|
|
81
|
-
Verbosity level for test output (None until configured).
|
|
82
|
-
__execution_mode : Optional[str]
|
|
83
|
-
Test execution mode, e.g., 'SEQUENTIAL' or 'PARALLEL' (None until configured).
|
|
84
|
-
__max_workers : Optional[int]
|
|
85
|
-
Maximum number of worker threads/processes for parallel execution (None until configured).
|
|
86
|
-
__fail_fast : Optional[bool]
|
|
87
|
-
If True, stops execution upon the first test failure (None until configured).
|
|
88
|
-
__throw_exception : Optional[bool]
|
|
89
|
-
If True, raises exceptions on test failures (None until configured).
|
|
90
|
-
__persistent : Optional[bool]
|
|
91
|
-
If True, enables persistent storage for test results (None until configured).
|
|
92
|
-
__persistent_driver : Optional[str]
|
|
93
|
-
The driver to use for persistence, e.g., 'sqlite' or 'json' (None until configured).
|
|
94
|
-
__web_report : Optional[bool]
|
|
95
|
-
If True, enables web-based reporting of test results (None until configured).
|
|
96
|
-
__folder_path : Optional[str]
|
|
97
|
-
Relative folder path for test discovery (None until set).
|
|
98
|
-
__base_path : Optional[str]
|
|
99
|
-
Base directory for test discovery (None until set).
|
|
100
|
-
__pattern : Optional[str]
|
|
101
|
-
File name pattern to match test files (None until set).
|
|
102
|
-
__test_name_pattern : Optional[str]
|
|
103
|
-
Pattern to filter test names (None until set).
|
|
104
|
-
__tags : Optional[List[str]]
|
|
105
|
-
List of tags to filter tests (None until set).
|
|
106
|
-
__module_name : Optional[str]
|
|
107
|
-
Name of the module for test discovery (None until set).
|
|
108
|
-
__loader : unittest.TestLoader
|
|
109
|
-
Loader for discovering tests.
|
|
110
|
-
__suite : unittest.TestSuite
|
|
111
|
-
Test suite containing discovered tests.
|
|
112
|
-
__discovered_tests : List
|
|
113
|
-
List of discovered test metadata.
|
|
114
|
-
__printer : Optional[TestPrinter]
|
|
115
|
-
Utility for printing test results to the console.
|
|
116
|
-
__output_buffer : Optional[str]
|
|
117
|
-
Buffer for capturing standard output during tests.
|
|
118
|
-
__error_buffer : Optional[str]
|
|
119
|
-
Buffer for capturing error output during tests.
|
|
120
|
-
__result : Optional[dict]
|
|
121
|
-
Result summary of the test execution.
|
|
110
|
+
Sets up all internal attributes required for test discovery, execution, result reporting, and configuration management. Does not perform test discovery or execution.
|
|
122
111
|
|
|
123
112
|
Returns
|
|
124
113
|
-------
|
|
125
114
|
None
|
|
126
|
-
This constructor does not return a value.
|
|
127
115
|
"""
|
|
128
|
-
|
|
129
116
|
# Application instance for dependency injection (set via __setApp)
|
|
130
117
|
self.__app: Optional[IApplication] = None
|
|
131
118
|
|
|
@@ -181,42 +168,38 @@ class UnitTest(IUnitTest):
|
|
|
181
168
|
web_report: bool
|
|
182
169
|
) -> 'UnitTest':
|
|
183
170
|
"""
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
This method sets all relevant options for running unit tests in Orionis, including execution mode
|
|
187
|
-
(sequential or parallel), verbosity level, maximum number of workers, result persistence, exception
|
|
188
|
-
handling, and web report generation.
|
|
171
|
+
Configure the UnitTest instance with execution and reporting parameters.
|
|
189
172
|
|
|
190
173
|
Parameters
|
|
191
174
|
----------
|
|
192
|
-
verbosity : int
|
|
193
|
-
Verbosity level for test output.
|
|
194
|
-
execution_mode : str
|
|
195
|
-
|
|
175
|
+
verbosity : int or VerbosityMode
|
|
176
|
+
Verbosity level for test output.
|
|
177
|
+
execution_mode : str or ExecutionMode
|
|
178
|
+
Execution mode ('SEQUENTIAL' or 'PARALLEL').
|
|
196
179
|
max_workers : int
|
|
197
|
-
Maximum number of
|
|
180
|
+
Maximum number of workers for parallel execution.
|
|
198
181
|
fail_fast : bool
|
|
199
|
-
|
|
182
|
+
Whether to stop on the first failure.
|
|
200
183
|
print_result : bool
|
|
201
|
-
|
|
184
|
+
Whether to print results to the console.
|
|
202
185
|
throw_exception : bool
|
|
203
|
-
|
|
186
|
+
Whether to raise exceptions on test failures.
|
|
204
187
|
persistent : bool
|
|
205
|
-
|
|
188
|
+
Whether to enable result persistence.
|
|
206
189
|
persistent_driver : str or PersistentDrivers
|
|
207
|
-
Persistence driver
|
|
190
|
+
Persistence driver ('sqlite' or 'json').
|
|
208
191
|
web_report : bool
|
|
209
|
-
|
|
192
|
+
Whether to enable web report generation.
|
|
210
193
|
|
|
211
194
|
Returns
|
|
212
195
|
-------
|
|
213
196
|
UnitTest
|
|
214
|
-
The configured UnitTest instance
|
|
197
|
+
The configured UnitTest instance.
|
|
215
198
|
|
|
216
199
|
Raises
|
|
217
200
|
------
|
|
218
201
|
OrionisTestValueError
|
|
219
|
-
If any parameter is invalid
|
|
202
|
+
If any parameter is invalid.
|
|
220
203
|
"""
|
|
221
204
|
|
|
222
205
|
# Validate and assign parameters using specialized validators
|
|
@@ -249,48 +232,29 @@ class UnitTest(IUnitTest):
|
|
|
249
232
|
"""
|
|
250
233
|
Discover and add unit tests from a specified folder to the test suite.
|
|
251
234
|
|
|
252
|
-
This method searches for test files within a given folder, using a file name pattern,
|
|
253
|
-
and optionally filters discovered tests by test name pattern and tags. All matching
|
|
254
|
-
tests are added to the internal test suite. The method also records metadata about
|
|
255
|
-
the discovery process, such as the folder path and the number of tests found.
|
|
256
|
-
|
|
257
235
|
Parameters
|
|
258
236
|
----------
|
|
259
237
|
base_path : str or Path
|
|
260
|
-
|
|
238
|
+
Base directory for resolving the folder path.
|
|
261
239
|
folder_path : str
|
|
262
|
-
|
|
240
|
+
Relative path to the folder containing test files.
|
|
263
241
|
pattern : str
|
|
264
|
-
|
|
242
|
+
File name pattern to match test files.
|
|
265
243
|
test_name_pattern : str, optional
|
|
266
|
-
|
|
267
|
-
this pattern will be included. If None, all test names are included.
|
|
244
|
+
Regular expression pattern to filter test names.
|
|
268
245
|
tags : list of str, optional
|
|
269
|
-
|
|
270
|
-
tags will be included. If None, no tag filtering is applied.
|
|
246
|
+
Tags to filter tests.
|
|
271
247
|
|
|
272
248
|
Returns
|
|
273
249
|
-------
|
|
274
250
|
UnitTest
|
|
275
|
-
The current instance with
|
|
251
|
+
The current instance with discovered tests added.
|
|
276
252
|
|
|
277
253
|
Raises
|
|
278
254
|
------
|
|
279
255
|
OrionisTestValueError
|
|
280
|
-
If
|
|
281
|
-
or if there are import or discovery errors.
|
|
282
|
-
|
|
283
|
-
Notes
|
|
284
|
-
-----
|
|
285
|
-
- The method validates all input parameters using Orionis validators.
|
|
286
|
-
- The folder path is resolved relative to the provided base path.
|
|
287
|
-
- Test discovery uses Python's unittest loader.
|
|
288
|
-
- If `test_name_pattern` is provided, only tests whose names match the pattern are included.
|
|
289
|
-
- If `tags` are provided, only tests with matching tags are included.
|
|
290
|
-
- If no tests are found after filtering, an exception is raised.
|
|
291
|
-
- Metadata about the discovery (folder and test count) is appended to the internal record.
|
|
256
|
+
If arguments are invalid, folder does not exist, no tests are found, or import/discovery errors occur.
|
|
292
257
|
"""
|
|
293
|
-
|
|
294
258
|
# Validate Parameters
|
|
295
259
|
self.__base_path = ValidBasePath(base_path)
|
|
296
260
|
self.__folder_path = ValidFolderPath(folder_path)
|
|
@@ -321,15 +285,17 @@ class UnitTest(IUnitTest):
|
|
|
321
285
|
# Check for failed test imports (unittest.loader._FailedTest)
|
|
322
286
|
for test in self.__flattenTestSuite(tests):
|
|
323
287
|
if test.__class__.__name__ == "_FailedTest":
|
|
288
|
+
|
|
324
289
|
# Extract the error message from the test's traceback
|
|
325
290
|
error_message = ""
|
|
326
291
|
if hasattr(test, "_exception"):
|
|
327
292
|
error_message = str(test._exception)
|
|
328
293
|
elif hasattr(test, "_outcome") and hasattr(test._outcome, "errors"):
|
|
329
294
|
error_message = str(test._outcome.errors)
|
|
295
|
+
# Try to get error from test id or str(test)
|
|
330
296
|
else:
|
|
331
|
-
# Try to get error from test id or str(test)
|
|
332
297
|
error_message = str(test)
|
|
298
|
+
|
|
333
299
|
raise OrionisTestValueError(
|
|
334
300
|
f"Failed to import test module: {test.id()}.\n"
|
|
335
301
|
f"Error details: {error_message}\n"
|
|
@@ -400,38 +366,22 @@ class UnitTest(IUnitTest):
|
|
|
400
366
|
"""
|
|
401
367
|
Discover and add unit tests from a specified Python module to the test suite.
|
|
402
368
|
|
|
403
|
-
This method loads all unit tests defined within the given module and adds them to the internal test suite.
|
|
404
|
-
Optionally, it can filter discovered tests by a regular expression pattern applied to test names.
|
|
405
|
-
|
|
406
369
|
Parameters
|
|
407
370
|
----------
|
|
408
371
|
module_name : str
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
A regular expression pattern to filter test names. Only tests whose names match this pattern
|
|
413
|
-
will be included in the suite. If None, all discovered tests are included.
|
|
372
|
+
Fully qualified name of the module to discover tests from.
|
|
373
|
+
test_name_pattern : str, optional
|
|
374
|
+
Regular expression pattern to filter test names.
|
|
414
375
|
|
|
415
376
|
Returns
|
|
416
377
|
-------
|
|
417
378
|
UnitTest
|
|
418
|
-
The current UnitTest instance with
|
|
379
|
+
The current UnitTest instance with discovered tests added.
|
|
419
380
|
|
|
420
381
|
Raises
|
|
421
382
|
------
|
|
422
383
|
OrionisTestValueError
|
|
423
|
-
If
|
|
424
|
-
or if no tests are found after filtering.
|
|
425
|
-
OrionisTestValueError
|
|
426
|
-
For any unexpected error during test discovery, with details about the failure.
|
|
427
|
-
|
|
428
|
-
Notes
|
|
429
|
-
-----
|
|
430
|
-
- Input parameters are validated using Orionis validators before discovery.
|
|
431
|
-
- If `test_name_pattern` is provided, only tests matching the pattern are included.
|
|
432
|
-
- Metadata about the discovery (module name and test count) is appended to the internal `__discovered_tests` list.
|
|
433
|
-
- This method is useful for dynamically loading tests from specific modules, such as in plugin architectures or
|
|
434
|
-
when tests are not organized in standard file patterns.
|
|
384
|
+
If module_name is invalid, test_name_pattern is not a valid regex, the module cannot be imported, or no tests are found.
|
|
435
385
|
"""
|
|
436
386
|
|
|
437
387
|
# Validate input parameters
|
|
@@ -503,28 +453,16 @@ class UnitTest(IUnitTest):
|
|
|
503
453
|
"""
|
|
504
454
|
Execute the test suite and return a summary of the results.
|
|
505
455
|
|
|
506
|
-
This method manages the full test execution lifecycle: it prints start and finish messages,
|
|
507
|
-
executes the test suite, captures output and error buffers, processes the results, and
|
|
508
|
-
optionally raises an exception if failures occur and exception throwing is enabled.
|
|
509
|
-
|
|
510
456
|
Returns
|
|
511
457
|
-------
|
|
512
|
-
|
|
513
|
-
|
|
458
|
+
dict
|
|
459
|
+
Dictionary summarizing the test results, including statistics and execution time.
|
|
514
460
|
|
|
515
461
|
Raises
|
|
516
462
|
------
|
|
517
463
|
OrionisTestFailureException
|
|
518
|
-
If the test suite execution fails and
|
|
519
|
-
|
|
520
|
-
Notes
|
|
521
|
-
-----
|
|
522
|
-
- Measures total execution time in milliseconds.
|
|
523
|
-
- Uses the configured printer to display start, result, and finish messages.
|
|
524
|
-
- Captures and stores output and error buffers.
|
|
525
|
-
- Raises an exception if tests fail and exception throwing is enabled.
|
|
464
|
+
If the test suite execution fails and throw_exception is True.
|
|
526
465
|
"""
|
|
527
|
-
|
|
528
466
|
# Record the start time in nanoseconds
|
|
529
467
|
start_time = time.time_ns()
|
|
530
468
|
|
|
@@ -569,161 +507,122 @@ class UnitTest(IUnitTest):
|
|
|
569
507
|
suite: unittest.TestSuite
|
|
570
508
|
) -> List[unittest.TestCase]:
|
|
571
509
|
"""
|
|
572
|
-
Recursively flattens a
|
|
573
|
-
|
|
574
|
-
This method traverses the provided test suite, which may contain nested suites or individual test cases,
|
|
575
|
-
and collects all unique TestCase instances into a flat list. It ensures that each test case appears only once
|
|
576
|
-
in the resulting list, based on a short identifier derived from the test's id. This is particularly useful
|
|
577
|
-
for operations that require direct access to all test cases, such as filtering, counting, or custom execution.
|
|
510
|
+
Recursively flattens a unittest.TestSuite into a list of unique unittest.TestCase instances.
|
|
578
511
|
|
|
579
512
|
Parameters
|
|
580
513
|
----------
|
|
581
514
|
suite : unittest.TestSuite
|
|
582
|
-
The test suite to
|
|
515
|
+
The test suite to be flattened.
|
|
583
516
|
|
|
584
517
|
Returns
|
|
585
518
|
-------
|
|
586
519
|
List[unittest.TestCase]
|
|
587
|
-
A flat list containing
|
|
520
|
+
A flat list containing unique unittest.TestCase instances extracted from the suite.
|
|
588
521
|
|
|
589
522
|
Notes
|
|
590
523
|
-----
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
- The method uses recursion to traverse all levels of nested suites.
|
|
594
|
-
- Only objects with an 'id' attribute (i.e., test cases) are included in the result.
|
|
524
|
+
Test uniqueness is determined by a shortened test identifier (the last two components of the test id).
|
|
525
|
+
This helps avoid duplicate test cases in the returned list.
|
|
595
526
|
"""
|
|
527
|
+
|
|
528
|
+
# Initialize an empty list to hold unique test cases and a set to track seen test IDs
|
|
596
529
|
tests = []
|
|
597
530
|
seen_ids = set()
|
|
598
531
|
|
|
532
|
+
# Recursive function to flatten the test suite
|
|
599
533
|
def _flatten(item):
|
|
600
|
-
"""
|
|
601
|
-
Recursively process a TestSuite or test case, collecting unique test cases.
|
|
602
|
-
|
|
603
|
-
- If the item is a TestSuite, recursively process its children.
|
|
604
|
-
- If the item is a test case (has 'id'), generate a short id and add it if not already seen.
|
|
605
|
-
"""
|
|
606
534
|
if isinstance(item, unittest.TestSuite):
|
|
607
|
-
# Recursively flatten all sub-items in the suite
|
|
608
535
|
for sub_item in item:
|
|
609
536
|
_flatten(sub_item)
|
|
610
537
|
elif hasattr(item, "id"):
|
|
611
|
-
# Generate a short id for uniqueness (e.g., "ClassName.methodName")
|
|
612
538
|
test_id = item.id()
|
|
539
|
+
|
|
540
|
+
# Use the last two components of the test id for uniqueness
|
|
613
541
|
parts = test_id.split('.')
|
|
614
542
|
if len(parts) >= 2:
|
|
615
543
|
short_id = '.'.join(parts[-2:])
|
|
616
544
|
else:
|
|
617
545
|
short_id = test_id
|
|
618
|
-
# Add the test case only if its short id has not been seen
|
|
619
546
|
if short_id not in seen_ids:
|
|
620
547
|
seen_ids.add(short_id)
|
|
621
548
|
tests.append(item)
|
|
622
549
|
|
|
623
|
-
# Start
|
|
550
|
+
# Start the flattening process
|
|
624
551
|
_flatten(suite)
|
|
625
|
-
|
|
626
|
-
# Return a flat list of unique unittest.TestCase instances
|
|
627
552
|
return tests
|
|
628
553
|
|
|
629
554
|
def __runSuite(
|
|
630
555
|
self
|
|
631
556
|
) -> Tuple[unittest.TestResult, io.StringIO, io.StringIO]:
|
|
632
557
|
"""
|
|
633
|
-
Executes the test suite
|
|
634
|
-
while capturing both standard output and error streams during the test run.
|
|
635
|
-
|
|
636
|
-
This method determines the execution mode (sequential or parallel) based on the current
|
|
637
|
-
configuration and delegates the actual test execution to the appropriate internal method.
|
|
638
|
-
It ensures that all output and error messages generated during the test run are captured
|
|
639
|
-
in dedicated buffers for later inspection or reporting.
|
|
558
|
+
Executes the test suite according to the configured execution mode, capturing both standard output and error streams.
|
|
640
559
|
|
|
641
560
|
Returns
|
|
642
561
|
-------
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
produced during the test execution.
|
|
651
|
-
|
|
652
|
-
Notes
|
|
653
|
-
-----
|
|
654
|
-
- The execution mode is determined by the value of self.__execution_mode.
|
|
655
|
-
- Output and error streams are always captured, regardless of execution mode.
|
|
656
|
-
- The returned buffers can be used for further processing, logging, or displaying test output.
|
|
562
|
+
tuple
|
|
563
|
+
result : unittest.TestResult
|
|
564
|
+
The result object containing the outcomes of the executed tests.
|
|
565
|
+
output_buffer : io.StringIO
|
|
566
|
+
Buffer capturing the standard output generated during test execution.
|
|
567
|
+
error_buffer : io.StringIO
|
|
568
|
+
Buffer capturing the standard error generated during test execution.
|
|
657
569
|
"""
|
|
658
570
|
|
|
659
|
-
#
|
|
571
|
+
# Initialize output and error buffers to capture test execution output
|
|
660
572
|
output_buffer = io.StringIO()
|
|
661
573
|
error_buffer = io.StringIO()
|
|
662
574
|
|
|
663
|
-
#
|
|
575
|
+
# Run tests in parallel mode using multiple workers
|
|
664
576
|
if self.__execution_mode == ExecutionMode.PARALLEL.value:
|
|
665
|
-
# Run tests in parallel mode
|
|
666
577
|
result = self.__runTestsInParallel(
|
|
667
578
|
output_buffer,
|
|
668
579
|
error_buffer
|
|
669
580
|
)
|
|
581
|
+
|
|
582
|
+
# Run tests sequentially
|
|
670
583
|
else:
|
|
671
|
-
# Run tests sequentially (default)
|
|
672
584
|
result = self.__runTestsSequentially(
|
|
673
585
|
output_buffer,
|
|
674
586
|
error_buffer
|
|
675
587
|
)
|
|
676
588
|
|
|
677
|
-
# Return the
|
|
589
|
+
# Return the result, output, and error buffers
|
|
678
590
|
return result, output_buffer, error_buffer
|
|
679
591
|
|
|
680
592
|
def __resolveFlattenedTestSuite(
|
|
681
593
|
self
|
|
682
594
|
) -> unittest.TestSuite:
|
|
683
595
|
"""
|
|
684
|
-
Resolves and injects dependencies for all test cases in the suite, returning a flattened TestSuite.
|
|
596
|
+
Resolves and injects dependencies for all test cases in the current suite, returning a flattened TestSuite.
|
|
685
597
|
|
|
686
|
-
This method
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
and injected, ready for execution.
|
|
691
|
-
|
|
692
|
-
Parameters
|
|
693
|
-
----------
|
|
694
|
-
None
|
|
598
|
+
This method iterates through all test cases in the suite, checks for failed imports, decorated methods, and unresolved dependencies.
|
|
599
|
+
For each test case, it uses reflection to determine the test method and its dependencies. If dependencies are required and can be resolved,
|
|
600
|
+
it injects them using the application's resolver. If a test method has unresolved dependencies, an exception is raised.
|
|
601
|
+
Decorated methods and failed imports are added as-is. The resulting TestSuite contains all test cases with dependencies injected where needed.
|
|
695
602
|
|
|
696
603
|
Returns
|
|
697
604
|
-------
|
|
698
605
|
unittest.TestSuite
|
|
699
|
-
A new TestSuite containing all test cases
|
|
700
|
-
where required. Test cases with unresolved dependencies will cause an exception to be raised.
|
|
606
|
+
A new TestSuite containing all test cases with dependencies injected as required.
|
|
701
607
|
|
|
702
608
|
Raises
|
|
703
609
|
------
|
|
704
610
|
OrionisTestValueError
|
|
705
|
-
If any test method has dependencies that cannot be resolved.
|
|
706
|
-
|
|
707
|
-
Notes
|
|
708
|
-
-----
|
|
709
|
-
- Decorated test methods are left unchanged and added as-is.
|
|
710
|
-
- Test methods without dependencies are added directly.
|
|
711
|
-
- Test methods with unresolved dependencies will trigger an error.
|
|
712
|
-
- The returned TestSuite is flat and contains all processed test cases.
|
|
611
|
+
If any test method has unresolved dependencies that cannot be resolved by the resolver.
|
|
713
612
|
"""
|
|
714
613
|
|
|
715
|
-
# Create a new
|
|
614
|
+
# Create a new TestSuite to hold the resolved test cases
|
|
716
615
|
flattened_suite = unittest.TestSuite()
|
|
717
616
|
|
|
718
|
-
# Iterate through all test cases in the
|
|
617
|
+
# Iterate through all test cases in the flattened suite
|
|
719
618
|
for test_case in self.__flattenTestSuite(self.__suite):
|
|
720
619
|
|
|
721
|
-
# If
|
|
620
|
+
# If the test case is a failed import, add it directly
|
|
722
621
|
if test_case.__class__.__name__ == "_FailedTest":
|
|
723
622
|
flattened_suite.addTest(test_case)
|
|
724
623
|
continue
|
|
725
624
|
|
|
726
|
-
#
|
|
625
|
+
# Use reflection to get the test method name
|
|
727
626
|
rf_instance = ReflectionInstance(test_case)
|
|
728
627
|
method_name = rf_instance.getAttribute("_testMethodName")
|
|
729
628
|
|
|
@@ -732,30 +631,29 @@ class UnitTest(IUnitTest):
|
|
|
732
631
|
flattened_suite.addTest(test_case)
|
|
733
632
|
continue
|
|
734
633
|
|
|
735
|
-
# Retrieve the
|
|
634
|
+
# Retrieve the test method from the class
|
|
736
635
|
test_method = getattr(test_case.__class__, method_name, None)
|
|
737
636
|
|
|
738
|
-
# Check if the
|
|
637
|
+
# Check if the method is decorated (wrapped)
|
|
739
638
|
decorators = []
|
|
740
639
|
if hasattr(test_method, '__wrapped__'):
|
|
741
640
|
original = test_method
|
|
742
641
|
while hasattr(original, '__wrapped__'):
|
|
743
|
-
# Collect decorator names for informational purposes
|
|
744
642
|
if hasattr(original, '__qualname__'):
|
|
745
643
|
decorators.append(original.__qualname__)
|
|
746
644
|
elif hasattr(original, '__name__'):
|
|
747
645
|
decorators.append(original.__name__)
|
|
748
646
|
original = original.__wrapped__
|
|
749
647
|
|
|
750
|
-
# If decorators are present, add the test case as-is
|
|
648
|
+
# If decorators are present, add the test case as-is
|
|
751
649
|
if decorators:
|
|
752
650
|
flattened_suite.addTest(test_case)
|
|
753
651
|
continue
|
|
754
652
|
|
|
755
|
-
#
|
|
653
|
+
# Get the method's dependency signature
|
|
756
654
|
signature = rf_instance.getMethodDependencies(method_name)
|
|
757
655
|
|
|
758
|
-
# If
|
|
656
|
+
# If no dependencies are required or unresolved, add the test case as-is
|
|
759
657
|
if ((not signature.resolved and not signature.unresolved) or (not signature.resolved and len(signature.unresolved) > 0)):
|
|
760
658
|
flattened_suite.addTest(test_case)
|
|
761
659
|
continue
|
|
@@ -767,32 +665,25 @@ class UnitTest(IUnitTest):
|
|
|
767
665
|
"Please ensure all dependencies are correctly defined and available."
|
|
768
666
|
)
|
|
769
667
|
|
|
770
|
-
#
|
|
668
|
+
# Get the original test class and method
|
|
771
669
|
test_class = ReflectionInstance(test_case).getClass()
|
|
772
670
|
original_method = getattr(test_class, method_name)
|
|
773
671
|
|
|
774
672
|
# Resolve dependencies using the application's resolver
|
|
775
673
|
params = Resolver(self.__app).resolveSignature(signature)
|
|
776
674
|
|
|
777
|
-
# Create a wrapper
|
|
675
|
+
# Create a wrapper to inject resolved dependencies into the test method
|
|
778
676
|
def create_test_wrapper(original_test, resolved_args: dict):
|
|
779
677
|
def wrapper(self_instance):
|
|
780
678
|
return original_test(self_instance, **resolved_args)
|
|
781
679
|
return wrapper
|
|
782
680
|
|
|
783
|
-
# Wrap the original test method with the dependency-injecting wrapper
|
|
784
|
-
wrapped_method = create_test_wrapper(original_method, params)
|
|
785
|
-
|
|
786
681
|
# Bind the wrapped method to the test case instance
|
|
682
|
+
wrapped_method = create_test_wrapper(original_method, params)
|
|
787
683
|
bound_method = wrapped_method.__get__(test_case, test_case.__class__)
|
|
788
|
-
|
|
789
|
-
# Replace the original test method on the test case with the wrapped version
|
|
790
684
|
setattr(test_case, method_name, bound_method)
|
|
791
|
-
|
|
792
|
-
# Add the modified test case to the flattened suite
|
|
793
685
|
flattened_suite.addTest(test_case)
|
|
794
686
|
|
|
795
|
-
# Return the new flattened suite with all dependencies resolved and injected
|
|
796
687
|
return flattened_suite
|
|
797
688
|
|
|
798
689
|
def __runTestsSequentially(
|
|
@@ -801,31 +692,45 @@ class UnitTest(IUnitTest):
|
|
|
801
692
|
error_buffer: io.StringIO
|
|
802
693
|
) -> unittest.TestResult:
|
|
803
694
|
"""
|
|
804
|
-
Executes the test suite sequentially, capturing
|
|
695
|
+
Executes all test cases in the test suite sequentially, capturing standard output and error streams.
|
|
805
696
|
|
|
806
697
|
Parameters
|
|
807
698
|
----------
|
|
808
699
|
output_buffer : io.StringIO
|
|
809
|
-
|
|
700
|
+
Buffer to capture the standard output generated during test execution.
|
|
810
701
|
error_buffer : io.StringIO
|
|
811
|
-
|
|
702
|
+
Buffer to capture the standard error generated during test execution.
|
|
812
703
|
|
|
813
704
|
Returns
|
|
814
705
|
-------
|
|
815
706
|
unittest.TestResult
|
|
816
|
-
The result
|
|
817
|
-
|
|
707
|
+
The aggregated result object containing the outcomes of all executed test cases.
|
|
708
|
+
|
|
709
|
+
Raises
|
|
710
|
+
------
|
|
711
|
+
OrionisTestValueError
|
|
712
|
+
If an item in the suite is not a valid unittest.TestCase instance.
|
|
713
|
+
|
|
714
|
+
Notes
|
|
715
|
+
-----
|
|
716
|
+
Each test case is executed individually, and results are merged into a single result object.
|
|
717
|
+
Output and error streams are redirected for each test case to ensure complete capture.
|
|
718
|
+
The printer is used to display the result of each test immediately after execution.
|
|
818
719
|
"""
|
|
819
720
|
|
|
820
|
-
#
|
|
721
|
+
# Initialize output and error buffers to capture test execution output
|
|
821
722
|
result = None
|
|
723
|
+
|
|
724
|
+
# Iterate through all resolved test cases in the suite
|
|
822
725
|
for case in self.__resolveFlattenedTestSuite():
|
|
823
726
|
|
|
727
|
+
# Ensure the test case is a valid unittest.TestCase instance
|
|
824
728
|
if not isinstance(case, unittest.TestCase):
|
|
825
729
|
raise OrionisTestValueError(
|
|
826
730
|
f"Invalid test case type: Expected unittest.TestCase, got {type(case).__name__}."
|
|
827
731
|
)
|
|
828
732
|
|
|
733
|
+
# Redirect output and error streams for the current test case
|
|
829
734
|
with redirect_stdout(output_buffer), redirect_stderr(error_buffer):
|
|
830
735
|
runner = unittest.TextTestRunner(
|
|
831
736
|
stream=output_buffer,
|
|
@@ -833,18 +738,19 @@ class UnitTest(IUnitTest):
|
|
|
833
738
|
failfast=self.__fail_fast,
|
|
834
739
|
resultclass=self.__customResultClass()
|
|
835
740
|
)
|
|
741
|
+
# Run the current test case and obtain the result
|
|
836
742
|
single_result: IOrionisTestResult = runner.run(unittest.TestSuite([case]))
|
|
837
743
|
|
|
838
|
-
# Print
|
|
744
|
+
# Print the result of the current test case using the printer
|
|
839
745
|
self.__printer.unittestResult(single_result.test_results[0])
|
|
840
746
|
|
|
841
|
-
# Merge
|
|
747
|
+
# Merge the result of the current test case into the aggregated result
|
|
842
748
|
if result is None:
|
|
843
749
|
result = single_result
|
|
844
750
|
else:
|
|
845
751
|
self.__mergeTestResults(result, single_result)
|
|
846
752
|
|
|
847
|
-
# Return the result
|
|
753
|
+
# Return the aggregated result containing all test outcomes
|
|
848
754
|
return result
|
|
849
755
|
|
|
850
756
|
def __runTestsInParallel(
|
|
@@ -853,82 +759,72 @@ class UnitTest(IUnitTest):
|
|
|
853
759
|
error_buffer: io.StringIO
|
|
854
760
|
) -> unittest.TestResult:
|
|
855
761
|
"""
|
|
856
|
-
Executes all test cases in the test suite concurrently using a thread pool
|
|
857
|
-
aggregating their results into a single result object. Standard output and error
|
|
858
|
-
streams are redirected to the provided buffers during execution.
|
|
859
|
-
|
|
860
|
-
This method is designed to speed up test execution by running multiple test cases
|
|
861
|
-
in parallel threads, making use of the configured maximum number of workers. Each
|
|
862
|
-
test case is executed in isolation, and their results are merged into a combined
|
|
863
|
-
result object. If the `fail_fast` option is enabled and a test fails, remaining
|
|
864
|
-
tests are canceled as soon as possible.
|
|
762
|
+
Executes all test cases in the test suite concurrently using a thread pool and aggregates their results.
|
|
865
763
|
|
|
866
764
|
Parameters
|
|
867
765
|
----------
|
|
868
766
|
output_buffer : io.StringIO
|
|
869
|
-
Buffer to capture standard output
|
|
767
|
+
Buffer to capture the standard output generated during test execution.
|
|
870
768
|
error_buffer : io.StringIO
|
|
871
|
-
Buffer to capture standard error
|
|
769
|
+
Buffer to capture the standard error generated during test execution.
|
|
872
770
|
|
|
873
771
|
Returns
|
|
874
772
|
-------
|
|
875
773
|
unittest.TestResult
|
|
876
|
-
|
|
877
|
-
the aggregated outcomes of all executed tests, including detailed information
|
|
878
|
-
about passed, failed, errored, and skipped tests.
|
|
774
|
+
Combined result object containing the outcomes of all executed test cases.
|
|
879
775
|
|
|
880
776
|
Notes
|
|
881
777
|
-----
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
778
|
+
Each test case is executed in a separate thread using a ThreadPoolExecutor.
|
|
779
|
+
Results from all threads are merged into a single result object.
|
|
780
|
+
Output and error streams are redirected for the entire parallel execution.
|
|
781
|
+
If fail-fast is enabled, execution stops as soon as a failure is detected.
|
|
885
782
|
"""
|
|
886
783
|
|
|
887
|
-
#
|
|
784
|
+
# Resolve and flatten all test cases in the suite, injecting dependencies if needed
|
|
888
785
|
test_cases = list(self.__resolveFlattenedTestSuite())
|
|
889
786
|
|
|
890
|
-
#
|
|
787
|
+
# Get the custom result class for enhanced test tracking
|
|
891
788
|
result_class = self.__customResultClass()
|
|
789
|
+
|
|
790
|
+
# Create a combined result object to aggregate all individual test results
|
|
892
791
|
combined_result = result_class(io.StringIO(), descriptions=True, verbosity=self.__verbosity)
|
|
893
792
|
|
|
894
|
-
#
|
|
895
|
-
# Each test runs in its own thread with minimal output.
|
|
793
|
+
# Define a function to run a single test case and return its result
|
|
896
794
|
def run_single_test(test):
|
|
897
795
|
runner = unittest.TextTestRunner(
|
|
898
|
-
stream=io.StringIO(), # Use a
|
|
796
|
+
stream=io.StringIO(), # Use a separate buffer for each test
|
|
899
797
|
verbosity=0,
|
|
900
798
|
failfast=False,
|
|
901
799
|
resultclass=result_class
|
|
902
800
|
)
|
|
903
|
-
# Run the test and return its result object
|
|
904
801
|
return runner.run(unittest.TestSuite([test]))
|
|
905
802
|
|
|
906
|
-
# Redirect
|
|
803
|
+
# Redirect output and error streams for the entire parallel execution
|
|
907
804
|
with redirect_stdout(output_buffer), redirect_stderr(error_buffer):
|
|
908
805
|
|
|
909
|
-
# Create a
|
|
806
|
+
# Create a thread pool with the configured number of workers
|
|
910
807
|
with ThreadPoolExecutor(max_workers=self.__max_workers) as executor:
|
|
911
808
|
|
|
912
|
-
# Submit all test cases to the
|
|
809
|
+
# Submit all test cases to the thread pool for execution
|
|
913
810
|
futures = [executor.submit(run_single_test, test) for test in test_cases]
|
|
914
811
|
|
|
915
|
-
#
|
|
812
|
+
# As each test completes, merge its result into the combined result
|
|
916
813
|
for future in as_completed(futures):
|
|
917
814
|
test_result = future.result()
|
|
918
|
-
# Merge each individual test result into the combined result
|
|
919
815
|
self.__mergeTestResults(combined_result, test_result)
|
|
920
816
|
|
|
921
|
-
# If
|
|
817
|
+
# If fail-fast is enabled and a failure occurs, cancel remaining tests
|
|
922
818
|
if self.__fail_fast and not combined_result.wasSuccessful():
|
|
923
819
|
for f in futures:
|
|
924
820
|
f.cancel()
|
|
925
821
|
break
|
|
926
822
|
|
|
927
|
-
# Print
|
|
823
|
+
# Print the result of each individual test using the printer
|
|
928
824
|
for test_result in combined_result.test_results:
|
|
929
825
|
self.__printer.unittestResult(test_result)
|
|
930
826
|
|
|
931
|
-
# Return the
|
|
827
|
+
# Return the aggregated result containing all test outcomes
|
|
932
828
|
return combined_result
|
|
933
829
|
|
|
934
830
|
def __mergeTestResults(
|
|
@@ -939,42 +835,45 @@ class UnitTest(IUnitTest):
|
|
|
939
835
|
"""
|
|
940
836
|
Merge the results of two unittest.TestResult objects into a single result.
|
|
941
837
|
|
|
942
|
-
This method updates the `combined_result` object by aggregating the test run counts,
|
|
943
|
-
failures, errors, skipped tests, expected failures, and unexpected successes from the
|
|
944
|
-
`individual_result` object. It also merges any custom test results stored in the
|
|
945
|
-
`test_results` attribute, if present, ensuring that all detailed test outcomes are
|
|
946
|
-
included in the combined result.
|
|
947
|
-
|
|
948
838
|
Parameters
|
|
949
839
|
----------
|
|
950
840
|
combined_result : unittest.TestResult
|
|
951
|
-
The TestResult object that will be updated
|
|
841
|
+
The TestResult object that will be updated with the merged results.
|
|
952
842
|
individual_result : unittest.TestResult
|
|
953
|
-
The TestResult object whose results will be merged into
|
|
843
|
+
The TestResult object whose results will be merged into the combined_result.
|
|
954
844
|
|
|
955
845
|
Returns
|
|
956
846
|
-------
|
|
957
847
|
None
|
|
958
|
-
This method does not return a value. It
|
|
848
|
+
This method does not return a value. It updates combined_result in place.
|
|
959
849
|
|
|
960
850
|
Notes
|
|
961
851
|
-----
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
852
|
+
This method aggregates the test statistics and detailed results from individual_result into combined_result.
|
|
853
|
+
It updates the total number of tests run, and extends the lists of failures, errors, skipped tests,
|
|
854
|
+
expected failures, and unexpected successes. If the result objects contain a 'test_results' attribute,
|
|
855
|
+
this method also merges the detailed test result entries.
|
|
965
856
|
"""
|
|
966
857
|
|
|
967
|
-
#
|
|
858
|
+
# Increment the total number of tests run
|
|
968
859
|
combined_result.testsRun += individual_result.testsRun
|
|
969
860
|
|
|
970
|
-
# Extend the
|
|
861
|
+
# Extend the list of failures with those from the individual result
|
|
971
862
|
combined_result.failures.extend(individual_result.failures)
|
|
863
|
+
|
|
864
|
+
# Extend the list of errors with those from the individual result
|
|
972
865
|
combined_result.errors.extend(individual_result.errors)
|
|
866
|
+
|
|
867
|
+
# Extend the list of skipped tests with those from the individual result
|
|
973
868
|
combined_result.skipped.extend(individual_result.skipped)
|
|
869
|
+
|
|
870
|
+
# Extend the list of expected failures with those from the individual result
|
|
974
871
|
combined_result.expectedFailures.extend(individual_result.expectedFailures)
|
|
872
|
+
|
|
873
|
+
# Extend the list of unexpected successes with those from the individual result
|
|
975
874
|
combined_result.unexpectedSuccesses.extend(individual_result.unexpectedSuccesses)
|
|
976
875
|
|
|
977
|
-
#
|
|
876
|
+
# If the individual result contains detailed test results, merge them as well
|
|
978
877
|
if hasattr(individual_result, 'test_results'):
|
|
979
878
|
if not hasattr(combined_result, 'test_results'):
|
|
980
879
|
combined_result.test_results = []
|
|
@@ -984,46 +883,45 @@ class UnitTest(IUnitTest):
|
|
|
984
883
|
self
|
|
985
884
|
) -> type:
|
|
986
885
|
"""
|
|
987
|
-
|
|
988
|
-
This method dynamically generates an `OrionisTestResult` class that extends
|
|
989
|
-
`unittest.TextTestResult`. The custom class provides advanced functionality for
|
|
990
|
-
tracking test execution details, including timings, statuses, and error information.
|
|
886
|
+
Create and return a custom test result class for enhanced test tracking.
|
|
991
887
|
|
|
992
888
|
Returns
|
|
993
889
|
-------
|
|
994
890
|
type
|
|
995
|
-
A dynamically created
|
|
996
|
-
test
|
|
997
|
-
|
|
998
|
-
messages, traceback, and file path.
|
|
891
|
+
A dynamically created subclass of unittest.TextTestResult that collects
|
|
892
|
+
detailed information about each test execution, including timing, status,
|
|
893
|
+
error messages, tracebacks, and metadata.
|
|
999
894
|
|
|
1000
895
|
Notes
|
|
1001
896
|
-----
|
|
1002
|
-
The
|
|
1003
|
-
|
|
1004
|
-
|
|
897
|
+
The returned class, OrionisTestResult, extends unittest.TextTestResult and
|
|
898
|
+
overrides key methods to capture additional data for each test case. This
|
|
899
|
+
includes execution time, error details, and test metadata, which are stored
|
|
900
|
+
in a list of TestResult objects for later reporting and analysis.
|
|
1005
901
|
"""
|
|
1006
|
-
|
|
1007
|
-
# Use `this` to refer to the outer class instance
|
|
1008
902
|
this = self
|
|
1009
903
|
|
|
1010
|
-
# Define the custom test result class
|
|
1011
904
|
class OrionisTestResult(unittest.TextTestResult):
|
|
905
|
+
|
|
906
|
+
# Initialize the parent class and custom attributes for tracking results and timings
|
|
1012
907
|
def __init__(self, *args, **kwargs):
|
|
1013
908
|
super().__init__(*args, **kwargs)
|
|
1014
|
-
self.test_results = []
|
|
1015
|
-
self._test_timings = {}
|
|
1016
|
-
self._current_test_start = None
|
|
909
|
+
self.test_results = [] # Stores detailed results for each test
|
|
910
|
+
self._test_timings = {} # Maps test instances to their execution time
|
|
911
|
+
self._current_test_start = None # Tracks the start time of the current test
|
|
1017
912
|
|
|
913
|
+
# Record the start time of the test
|
|
1018
914
|
def startTest(self, test):
|
|
1019
915
|
self._current_test_start = time.time()
|
|
1020
916
|
super().startTest(test)
|
|
1021
917
|
|
|
918
|
+
# Calculate and store the elapsed time for the test
|
|
1022
919
|
def stopTest(self, test):
|
|
1023
920
|
elapsed = time.time() - self._current_test_start
|
|
1024
921
|
self._test_timings[test] = elapsed
|
|
1025
922
|
super().stopTest(test)
|
|
1026
923
|
|
|
924
|
+
# Handle a successful test case and record its result
|
|
1027
925
|
def addSuccess(self, test):
|
|
1028
926
|
super().addSuccess(test)
|
|
1029
927
|
elapsed = self._test_timings.get(test, 0.0)
|
|
@@ -1041,6 +939,7 @@ class UnitTest(IUnitTest):
|
|
|
1041
939
|
)
|
|
1042
940
|
)
|
|
1043
941
|
|
|
942
|
+
# Handle a failed test case, extract error info, and record its result
|
|
1044
943
|
def addFailure(self, test, err):
|
|
1045
944
|
super().addFailure(test, err)
|
|
1046
945
|
elapsed = self._test_timings.get(test, 0.0)
|
|
@@ -1062,6 +961,7 @@ class UnitTest(IUnitTest):
|
|
|
1062
961
|
)
|
|
1063
962
|
)
|
|
1064
963
|
|
|
964
|
+
# Handle a test case that raised an error, extract error info, and record its result
|
|
1065
965
|
def addError(self, test, err):
|
|
1066
966
|
super().addError(test, err)
|
|
1067
967
|
elapsed = self._test_timings.get(test, 0.0)
|
|
@@ -1083,6 +983,7 @@ class UnitTest(IUnitTest):
|
|
|
1083
983
|
)
|
|
1084
984
|
)
|
|
1085
985
|
|
|
986
|
+
# Handle a skipped test case and record its result
|
|
1086
987
|
def addSkip(self, test, reason):
|
|
1087
988
|
super().addSkip(test, reason)
|
|
1088
989
|
elapsed = self._test_timings.get(test, 0.0)
|
|
@@ -1111,11 +1012,6 @@ class UnitTest(IUnitTest):
|
|
|
1111
1012
|
"""
|
|
1112
1013
|
Extracts the file path and a cleaned traceback from a given traceback string.
|
|
1113
1014
|
|
|
1114
|
-
This method analyzes a Python traceback string to determine the file path of the Python file
|
|
1115
|
-
where the error occurred (typically the last file in the traceback). It also removes lines
|
|
1116
|
-
related to framework internals and irrelevant noise, such as those containing 'unittest/',
|
|
1117
|
-
'lib/python', or 'site-packages', to produce a more concise and relevant traceback for reporting.
|
|
1118
|
-
|
|
1119
1015
|
Parameters
|
|
1120
1016
|
----------
|
|
1121
1017
|
traceback_str : str
|
|
@@ -1123,21 +1019,23 @@ class UnitTest(IUnitTest):
|
|
|
1123
1019
|
|
|
1124
1020
|
Returns
|
|
1125
1021
|
-------
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1022
|
+
tuple
|
|
1023
|
+
file_path : str or None
|
|
1024
|
+
The path to the Python file where the error occurred, or None if not found.
|
|
1025
|
+
clean_tb : str or None
|
|
1026
|
+
The cleaned traceback string with framework internals removed, or the original traceback if no cleaning was possible.
|
|
1130
1027
|
|
|
1131
1028
|
Notes
|
|
1132
1029
|
-----
|
|
1133
|
-
|
|
1134
|
-
|
|
1030
|
+
This method parses the traceback string to identify the most relevant file path (typically the last Python file in the traceback).
|
|
1031
|
+
It then filters out lines related to framework internals (such as 'unittest/', 'lib/python', or 'site-packages') to produce a more concise and relevant traceback.
|
|
1032
|
+
The cleaned traceback starts from the first occurrence of the relevant file path.
|
|
1135
1033
|
"""
|
|
1136
1034
|
|
|
1137
|
-
#
|
|
1035
|
+
# Find all Python file paths in the traceback
|
|
1138
1036
|
file_matches = re.findall(r'File ["\'](.*?.py)["\']', traceback_str)
|
|
1139
1037
|
|
|
1140
|
-
#
|
|
1038
|
+
# Select the last file path as the most relevant one
|
|
1141
1039
|
file_path = file_matches[-1] if file_matches else None
|
|
1142
1040
|
|
|
1143
1041
|
# Split the traceback into individual lines for processing
|
|
@@ -1145,25 +1043,21 @@ class UnitTest(IUnitTest):
|
|
|
1145
1043
|
clean_lines = []
|
|
1146
1044
|
relevant_lines_started = False
|
|
1147
1045
|
|
|
1148
|
-
# Iterate through each line
|
|
1046
|
+
# Iterate through each line to filter out framework internals
|
|
1149
1047
|
for line in tb_lines:
|
|
1150
1048
|
|
|
1151
|
-
# Skip lines that are part of
|
|
1049
|
+
# Skip lines that are part of unittest, Python standard library, or site-packages
|
|
1152
1050
|
if any(s in line for s in ['unittest/', 'lib/python', 'site-packages']):
|
|
1153
1051
|
continue
|
|
1154
1052
|
|
|
1155
|
-
# Start
|
|
1053
|
+
# Start collecting lines from the first occurrence of the relevant file path
|
|
1156
1054
|
if file_path and file_path in line and not relevant_lines_started:
|
|
1157
1055
|
relevant_lines_started = True
|
|
1158
|
-
|
|
1159
|
-
# If we've started collecting relevant lines, add them to the cleaned traceback
|
|
1160
1056
|
if relevant_lines_started:
|
|
1161
1057
|
clean_lines.append(line)
|
|
1162
1058
|
|
|
1163
|
-
# Join the
|
|
1059
|
+
# Join the filtered lines to form the cleaned traceback
|
|
1164
1060
|
clean_tb = str('\n').join(clean_lines) if clean_lines else traceback_str
|
|
1165
|
-
|
|
1166
|
-
# Return the file path and cleaned traceback
|
|
1167
1061
|
return file_path, clean_tb
|
|
1168
1062
|
|
|
1169
1063
|
def __generateSummary(
|
|
@@ -1172,51 +1066,31 @@ class UnitTest(IUnitTest):
|
|
|
1172
1066
|
execution_time: float
|
|
1173
1067
|
) -> Dict[str, Any]:
|
|
1174
1068
|
"""
|
|
1175
|
-
Generates a
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
statistics such as the total number of tests, counts of passed, failed, errored,
|
|
1179
|
-
and skipped tests, as well as the overall execution time and success rate.
|
|
1180
|
-
It also collects detailed information for each individual test, including
|
|
1181
|
-
identifiers, class and method names, status, execution time, error messages,
|
|
1182
|
-
tracebacks, file paths, and docstrings.
|
|
1183
|
-
|
|
1184
|
-
If result persistence is enabled, the summary is saved using the configured
|
|
1185
|
-
persistence driver (e.g., SQLite or JSON). If web reporting is enabled, a
|
|
1186
|
-
web report is generated and linked.
|
|
1069
|
+
Generates a summary dictionary of the test suite execution, including statistics,
|
|
1070
|
+
timing, and detailed results for each test. Optionally persists the summary and/or
|
|
1071
|
+
generates a web report if configured.
|
|
1187
1072
|
|
|
1188
1073
|
Parameters
|
|
1189
1074
|
----------
|
|
1190
1075
|
result : unittest.TestResult
|
|
1191
1076
|
The result object containing details of the test execution.
|
|
1192
1077
|
execution_time : float
|
|
1193
|
-
The total execution time of the test suite in
|
|
1078
|
+
The total execution time of the test suite in seconds.
|
|
1194
1079
|
|
|
1195
1080
|
Returns
|
|
1196
1081
|
-------
|
|
1197
|
-
|
|
1198
|
-
A dictionary containing
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
- skipped (int): Number of tests that were skipped.
|
|
1204
|
-
- total_time (float): Total execution time in milliseconds.
|
|
1205
|
-
- success_rate (float): Percentage of tests that passed.
|
|
1206
|
-
- test_details (List[Dict[str, Any]]): List of dictionaries with details for each test,
|
|
1207
|
-
including id, class, method, status, execution_time, error_message, traceback,
|
|
1208
|
-
file_path, and doc_string.
|
|
1209
|
-
- timestamp (str): ISO-formatted timestamp of when the summary was generated.
|
|
1210
|
-
|
|
1211
|
-
Side Effects
|
|
1212
|
-
------------
|
|
1213
|
-
- If persistence is enabled, the summary is persisted to storage.
|
|
1082
|
+
dict
|
|
1083
|
+
A dictionary containing test statistics, details, and metadata.
|
|
1084
|
+
|
|
1085
|
+
Notes
|
|
1086
|
+
-----
|
|
1087
|
+
- If persistence is enabled, the summary is saved to storage.
|
|
1214
1088
|
- If web reporting is enabled, a web report is generated.
|
|
1089
|
+
- The summary includes per-test details, overall statistics, and a timestamp.
|
|
1215
1090
|
"""
|
|
1216
1091
|
|
|
1217
|
-
test_details = []
|
|
1218
|
-
|
|
1219
1092
|
# Collect detailed information for each test result
|
|
1093
|
+
test_details = []
|
|
1220
1094
|
for test_result in result.test_results:
|
|
1221
1095
|
rst: TestResult = test_result
|
|
1222
1096
|
test_details.append({
|
|
@@ -1233,10 +1107,11 @@ class UnitTest(IUnitTest):
|
|
|
1233
1107
|
|
|
1234
1108
|
# Calculate the number of passed tests
|
|
1235
1109
|
passed = result.testsRun - len(result.failures) - len(result.errors) - len(result.skipped)
|
|
1110
|
+
|
|
1236
1111
|
# Calculate the success rate as a percentage
|
|
1237
1112
|
success_rate = (passed / result.testsRun * 100) if result.testsRun > 0 else 100.0
|
|
1238
1113
|
|
|
1239
|
-
# Build the summary dictionary
|
|
1114
|
+
# Build the summary dictionary with all relevant statistics and details
|
|
1240
1115
|
self.__result = {
|
|
1241
1116
|
"total_tests": result.testsRun,
|
|
1242
1117
|
"passed": passed,
|
|
@@ -1267,38 +1142,33 @@ class UnitTest(IUnitTest):
|
|
|
1267
1142
|
"""
|
|
1268
1143
|
Generate a web-based report for the provided test results summary.
|
|
1269
1144
|
|
|
1270
|
-
This method creates a web report for the test execution summary using the `TestingResultRender` class.
|
|
1271
|
-
It determines the appropriate storage path for the report, configures persistence options based on the
|
|
1272
|
-
current settings, and invokes the rendering process. After generating the report, it prints a link to
|
|
1273
|
-
the web report using the configured printer.
|
|
1274
|
-
|
|
1275
1145
|
Parameters
|
|
1276
1146
|
----------
|
|
1277
1147
|
summary : dict
|
|
1278
|
-
|
|
1148
|
+
Summary of test results for web report generation.
|
|
1279
1149
|
|
|
1280
1150
|
Returns
|
|
1281
1151
|
-------
|
|
1282
1152
|
None
|
|
1283
|
-
This method does not return any value. The generated web report is rendered and a link to it is printed
|
|
1284
|
-
to the console via the printer.
|
|
1285
1153
|
|
|
1286
1154
|
Notes
|
|
1287
1155
|
-----
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1156
|
+
This method creates a web-based report for the given test results summary.
|
|
1157
|
+
It uses the TestingResultRender class to generate the report, passing the storage path,
|
|
1158
|
+
the summary result, and a flag indicating whether to persist the report based on the
|
|
1159
|
+
persistence configuration and driver. After rendering, it prints a link to the generated
|
|
1160
|
+
web report using the printer.
|
|
1292
1161
|
"""
|
|
1293
1162
|
|
|
1294
|
-
# Create
|
|
1163
|
+
# Create a TestingResultRender instance with the storage path, result summary,
|
|
1164
|
+
# and persistence flag (True if persistent and using sqlite driver)
|
|
1295
1165
|
render = TestingResultRender(
|
|
1296
1166
|
storage_path=self.__storage,
|
|
1297
1167
|
result=summary,
|
|
1298
1168
|
persist=self.__persistent and self.__persistent_driver == 'sqlite'
|
|
1299
1169
|
)
|
|
1300
1170
|
|
|
1301
|
-
#
|
|
1171
|
+
# Print the link to the generated web report
|
|
1302
1172
|
self.__printer.linkWebReport(render.render())
|
|
1303
1173
|
|
|
1304
1174
|
def __handlePersistResults(
|
|
@@ -1308,71 +1178,51 @@ class UnitTest(IUnitTest):
|
|
|
1308
1178
|
"""
|
|
1309
1179
|
Persist the test results summary using the configured persistence driver.
|
|
1310
1180
|
|
|
1311
|
-
This method saves the provided test results summary to persistent storage, based on the
|
|
1312
|
-
current configuration. Supported drivers include SQLite (using the TestLogs class) and
|
|
1313
|
-
JSON file output. The storage location is determined by the configured base path.
|
|
1314
|
-
|
|
1315
1181
|
Parameters
|
|
1316
1182
|
----------
|
|
1317
1183
|
summary : dict
|
|
1318
|
-
The summary
|
|
1319
|
-
details, such as test counts, statuses, execution times, and individual test results.
|
|
1320
|
-
|
|
1321
|
-
Returns
|
|
1322
|
-
-------
|
|
1323
|
-
None
|
|
1324
|
-
This method does not return any value. It performs persistence as a side effect.
|
|
1184
|
+
The summary dictionary containing test results and metadata to be persisted.
|
|
1325
1185
|
|
|
1326
1186
|
Raises
|
|
1327
1187
|
------
|
|
1328
1188
|
OSError
|
|
1329
|
-
If there is an error creating directories or writing files
|
|
1189
|
+
If there is an error creating directories or writing files.
|
|
1330
1190
|
OrionisTestPersistenceError
|
|
1331
1191
|
If database operations fail or any other error occurs during persistence.
|
|
1332
1192
|
|
|
1333
1193
|
Notes
|
|
1334
1194
|
-----
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
- Any errors encountered during persistence are raised as exceptions for the caller to handle.
|
|
1195
|
+
This method persists the test results summary according to the configured persistence driver.
|
|
1196
|
+
If the driver is set to 'sqlite', the summary is stored in a SQLite database using the TestLogs class.
|
|
1197
|
+
If the driver is set to 'json', the summary is saved as a JSON file in the specified storage directory,
|
|
1198
|
+
with a filename based on the current timestamp. The method ensures that the target directory exists,
|
|
1199
|
+
and handles any errors that may occur during file or database operations.
|
|
1341
1200
|
"""
|
|
1342
|
-
|
|
1343
1201
|
try:
|
|
1344
1202
|
|
|
1203
|
+
# If the persistence driver is SQLite, store the summary in the database
|
|
1345
1204
|
if self.__persistent_driver == PersistentDrivers.SQLITE.value:
|
|
1346
|
-
|
|
1347
|
-
# Persist results to SQLite database using TestLogs
|
|
1348
1205
|
history = TestLogs(self.__storage)
|
|
1349
|
-
|
|
1350
|
-
# Insert the summary into the database
|
|
1351
1206
|
history.create(summary)
|
|
1352
1207
|
|
|
1208
|
+
# If the persistence driver is JSON, write the summary to a JSON file
|
|
1353
1209
|
elif self.__persistent_driver == PersistentDrivers.JSON.value:
|
|
1354
|
-
|
|
1355
|
-
# Generate a timestamp for the log file name
|
|
1356
1210
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
1357
|
-
|
|
1358
|
-
# Construct the log file path with the timestamp
|
|
1359
1211
|
log_path = Path(self.__storage) / f"{timestamp}_test_results.json"
|
|
1360
1212
|
|
|
1361
|
-
# Ensure the directory exists
|
|
1213
|
+
# Ensure the parent directory exists
|
|
1362
1214
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1363
1215
|
|
|
1364
|
-
# Write the summary
|
|
1216
|
+
# Write the summary to the JSON file
|
|
1365
1217
|
with open(log_path, 'w', encoding='utf-8') as log:
|
|
1366
1218
|
json.dump(summary, log, indent=4)
|
|
1367
|
-
|
|
1368
1219
|
except OSError as e:
|
|
1369
1220
|
|
|
1370
|
-
# Raise an
|
|
1221
|
+
# Raise an error if directory creation or file writing fails
|
|
1371
1222
|
raise OSError(f"Error creating directories or writing files: {str(e)}")
|
|
1372
|
-
|
|
1373
1223
|
except Exception as e:
|
|
1374
1224
|
|
|
1375
|
-
# Raise a
|
|
1225
|
+
# Raise a persistence error for any other exceptions
|
|
1376
1226
|
raise OrionisTestPersistenceError(f"Error persisting test results: {str(e)}")
|
|
1377
1227
|
|
|
1378
1228
|
def __filterTestsByName(
|
|
@@ -1381,44 +1231,57 @@ class UnitTest(IUnitTest):
|
|
|
1381
1231
|
pattern: str
|
|
1382
1232
|
) -> unittest.TestSuite:
|
|
1383
1233
|
"""
|
|
1384
|
-
|
|
1234
|
+
Filter tests in a test suite by a regular expression pattern applied to test names.
|
|
1235
|
+
|
|
1385
1236
|
Parameters
|
|
1386
1237
|
----------
|
|
1387
1238
|
suite : unittest.TestSuite
|
|
1388
|
-
The test suite containing the tests to
|
|
1239
|
+
The test suite containing the tests to be filtered.
|
|
1389
1240
|
pattern : str
|
|
1390
|
-
|
|
1241
|
+
Regular expression pattern to match against test names (test IDs).
|
|
1242
|
+
|
|
1391
1243
|
Returns
|
|
1392
1244
|
-------
|
|
1393
1245
|
unittest.TestSuite
|
|
1394
|
-
A new
|
|
1246
|
+
A new TestSuite containing only the tests whose names match the given pattern.
|
|
1247
|
+
|
|
1395
1248
|
Raises
|
|
1396
1249
|
------
|
|
1397
1250
|
OrionisTestValueError
|
|
1398
1251
|
If the provided pattern is not a valid regular expression.
|
|
1252
|
+
|
|
1399
1253
|
Notes
|
|
1400
1254
|
-----
|
|
1255
|
+
This method compiles the provided regular expression and applies it to the IDs of all test cases
|
|
1256
|
+
in the flattened suite. Only tests whose IDs match the pattern are included in the returned suite.
|
|
1257
|
+
If the pattern is invalid, an OrionisTestValueError is raised with details about the regex error.
|
|
1401
1258
|
"""
|
|
1402
1259
|
|
|
1403
|
-
#
|
|
1260
|
+
# Create a new TestSuite to hold the filtered tests
|
|
1404
1261
|
filtered_suite = unittest.TestSuite()
|
|
1405
1262
|
|
|
1406
|
-
# Validate the pattern
|
|
1407
1263
|
try:
|
|
1264
|
+
|
|
1265
|
+
# Compile the provided regular expression pattern
|
|
1408
1266
|
regex = re.compile(pattern)
|
|
1267
|
+
|
|
1409
1268
|
except re.error as e:
|
|
1269
|
+
|
|
1270
|
+
# Raise a value error if the regex is invalid
|
|
1410
1271
|
raise OrionisTestValueError(
|
|
1411
1272
|
f"The provided test name pattern is invalid: '{pattern}'. "
|
|
1412
1273
|
f"Regular expression compilation error: {str(e)}. "
|
|
1413
1274
|
"Please check the pattern syntax and try again."
|
|
1414
1275
|
)
|
|
1415
1276
|
|
|
1416
|
-
# Iterate through all
|
|
1277
|
+
# Iterate through all test cases in the flattened suite
|
|
1417
1278
|
for test in self.__flattenTestSuite(suite):
|
|
1279
|
+
|
|
1280
|
+
# Add the test to the filtered suite if its ID matches the regex
|
|
1418
1281
|
if regex.search(test.id()):
|
|
1419
1282
|
filtered_suite.addTest(test)
|
|
1420
1283
|
|
|
1421
|
-
# Return the
|
|
1284
|
+
# Return the suite containing only the filtered tests
|
|
1422
1285
|
return filtered_suite
|
|
1423
1286
|
|
|
1424
1287
|
def __filterTestsByTags(
|
|
@@ -1427,47 +1290,56 @@ class UnitTest(IUnitTest):
|
|
|
1427
1290
|
tags: List[str]
|
|
1428
1291
|
) -> unittest.TestSuite:
|
|
1429
1292
|
"""
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
Iterates through all tests in the provided TestSuite and checks for a `__tags__`
|
|
1433
|
-
attribute either on the test method or the test case class. If any of the specified
|
|
1434
|
-
tags match the tags associated with the test, the test is included in the filtered suite.
|
|
1293
|
+
Filters tests in a unittest TestSuite by matching specified tags.
|
|
1435
1294
|
|
|
1436
1295
|
Parameters
|
|
1437
1296
|
----------
|
|
1438
1297
|
suite : unittest.TestSuite
|
|
1439
|
-
The original TestSuite containing all
|
|
1298
|
+
The original TestSuite containing all test cases to be filtered.
|
|
1440
1299
|
tags : list of str
|
|
1441
1300
|
List of tags to filter the tests by.
|
|
1442
1301
|
|
|
1443
1302
|
Returns
|
|
1444
1303
|
-------
|
|
1445
1304
|
unittest.TestSuite
|
|
1446
|
-
A new TestSuite containing only the tests that
|
|
1305
|
+
A new TestSuite containing only the tests that have at least one matching tag.
|
|
1306
|
+
|
|
1307
|
+
Notes
|
|
1308
|
+
-----
|
|
1309
|
+
This method inspects each test case in the provided suite and checks for the presence of tags
|
|
1310
|
+
either on the test method (via a `__tags__` attribute) or on the test class instance itself.
|
|
1311
|
+
If any of the specified tags are found in the test's tags, the test is included in the returned suite.
|
|
1447
1312
|
"""
|
|
1448
1313
|
|
|
1449
|
-
#
|
|
1314
|
+
# Create a new TestSuite to hold the filtered tests
|
|
1450
1315
|
filtered_suite = unittest.TestSuite()
|
|
1316
|
+
|
|
1317
|
+
# Convert the list of tags to a set for efficient intersection checks
|
|
1451
1318
|
tag_set = set(tags)
|
|
1452
1319
|
|
|
1320
|
+
# Iterate through all test cases in the flattened suite
|
|
1453
1321
|
for test in self.__flattenTestSuite(suite):
|
|
1454
1322
|
|
|
1455
|
-
#
|
|
1323
|
+
# Attempt to retrieve the test method from the test case
|
|
1456
1324
|
test_method = getattr(test, test._testMethodName, None)
|
|
1457
1325
|
|
|
1458
|
-
# Check
|
|
1326
|
+
# Check if the test method has a __tags__ attribute
|
|
1459
1327
|
if hasattr(test_method, '__tags__'):
|
|
1460
1328
|
method_tags = set(getattr(test_method, '__tags__'))
|
|
1329
|
+
|
|
1330
|
+
# If there is any intersection between the method's tags and the filter tags, add the test
|
|
1461
1331
|
if tag_set.intersection(method_tags):
|
|
1462
1332
|
filtered_suite.addTest(test)
|
|
1463
1333
|
|
|
1464
|
-
#
|
|
1334
|
+
# If the method does not have tags, check if the test case itself has a __tags__ attribute
|
|
1465
1335
|
elif hasattr(test, '__tags__'):
|
|
1466
1336
|
class_tags = set(getattr(test, '__tags__'))
|
|
1337
|
+
|
|
1338
|
+
# If there is any intersection between the class's tags and the filter tags, add the test
|
|
1467
1339
|
if tag_set.intersection(class_tags):
|
|
1468
1340
|
filtered_suite.addTest(test)
|
|
1469
1341
|
|
|
1470
|
-
# Return the
|
|
1342
|
+
# Return the suite containing only the filtered tests
|
|
1471
1343
|
return filtered_suite
|
|
1472
1344
|
|
|
1473
1345
|
def getTestNames(
|
|
@@ -1478,8 +1350,8 @@ class UnitTest(IUnitTest):
|
|
|
1478
1350
|
|
|
1479
1351
|
Returns
|
|
1480
1352
|
-------
|
|
1481
|
-
|
|
1482
|
-
List of test names
|
|
1353
|
+
list of str
|
|
1354
|
+
List of test names from the test suite.
|
|
1483
1355
|
"""
|
|
1484
1356
|
return [test.id() for test in self.__flattenTestSuite(self.__suite)]
|
|
1485
1357
|
|
|
@@ -1487,12 +1359,12 @@ class UnitTest(IUnitTest):
|
|
|
1487
1359
|
self
|
|
1488
1360
|
) -> int:
|
|
1489
1361
|
"""
|
|
1490
|
-
|
|
1362
|
+
Get the total number of test cases in the test suite.
|
|
1491
1363
|
|
|
1492
1364
|
Returns
|
|
1493
1365
|
-------
|
|
1494
1366
|
int
|
|
1495
|
-
|
|
1367
|
+
Total number of individual test cases in the suite.
|
|
1496
1368
|
"""
|
|
1497
1369
|
return len(list(self.__flattenTestSuite(self.__suite)))
|
|
1498
1370
|
|
|
@@ -1502,7 +1374,9 @@ class UnitTest(IUnitTest):
|
|
|
1502
1374
|
"""
|
|
1503
1375
|
Clear all tests from the current test suite.
|
|
1504
1376
|
|
|
1505
|
-
|
|
1377
|
+
Returns
|
|
1378
|
+
-------
|
|
1379
|
+
None
|
|
1506
1380
|
"""
|
|
1507
1381
|
self.__suite = unittest.TestSuite()
|
|
1508
1382
|
|
|
@@ -1510,12 +1384,12 @@ class UnitTest(IUnitTest):
|
|
|
1510
1384
|
self
|
|
1511
1385
|
) -> dict:
|
|
1512
1386
|
"""
|
|
1513
|
-
|
|
1387
|
+
Get the results of the executed test suite.
|
|
1514
1388
|
|
|
1515
1389
|
Returns
|
|
1516
1390
|
-------
|
|
1517
|
-
|
|
1518
|
-
|
|
1391
|
+
dict
|
|
1392
|
+
Result of the executed test suite.
|
|
1519
1393
|
"""
|
|
1520
1394
|
return self.__result
|
|
1521
1395
|
|
|
@@ -1523,12 +1397,12 @@ class UnitTest(IUnitTest):
|
|
|
1523
1397
|
self
|
|
1524
1398
|
) -> int:
|
|
1525
1399
|
"""
|
|
1526
|
-
|
|
1527
|
-
|
|
1400
|
+
Get the output buffer used for capturing test results.
|
|
1401
|
+
|
|
1528
1402
|
Returns
|
|
1529
1403
|
-------
|
|
1530
1404
|
int
|
|
1531
|
-
|
|
1405
|
+
Output buffer containing the results of the test execution.
|
|
1532
1406
|
"""
|
|
1533
1407
|
return self.__output_buffer
|
|
1534
1408
|
|
|
@@ -1536,8 +1410,11 @@ class UnitTest(IUnitTest):
|
|
|
1536
1410
|
self
|
|
1537
1411
|
) -> None:
|
|
1538
1412
|
"""
|
|
1539
|
-
|
|
1540
|
-
|
|
1413
|
+
Print the contents of the output buffer to the console.
|
|
1414
|
+
|
|
1415
|
+
Returns
|
|
1416
|
+
-------
|
|
1417
|
+
None
|
|
1541
1418
|
"""
|
|
1542
1419
|
self.__printer.print(self.__output_buffer)
|
|
1543
1420
|
|
|
@@ -1545,12 +1422,12 @@ class UnitTest(IUnitTest):
|
|
|
1545
1422
|
self
|
|
1546
1423
|
) -> int:
|
|
1547
1424
|
"""
|
|
1548
|
-
|
|
1549
|
-
|
|
1425
|
+
Get the error buffer used for capturing test errors.
|
|
1426
|
+
|
|
1550
1427
|
Returns
|
|
1551
1428
|
-------
|
|
1552
1429
|
int
|
|
1553
|
-
|
|
1430
|
+
Error buffer containing errors encountered during test execution.
|
|
1554
1431
|
"""
|
|
1555
1432
|
return self.__error_buffer
|
|
1556
1433
|
|
|
@@ -1558,7 +1435,10 @@ class UnitTest(IUnitTest):
|
|
|
1558
1435
|
self
|
|
1559
1436
|
) -> None:
|
|
1560
1437
|
"""
|
|
1561
|
-
|
|
1562
|
-
|
|
1438
|
+
Print the contents of the error buffer to the console.
|
|
1439
|
+
|
|
1440
|
+
Returns
|
|
1441
|
+
-------
|
|
1442
|
+
None
|
|
1563
1443
|
"""
|
|
1564
1444
|
self.__printer.print(self.__error_buffer)
|