orionis 0.431.0__py3-none-any.whl → 0.434.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/console/commands/version.py +2 -2
- orionis/console/core/reactor.py +1 -1
- orionis/foundation/config/roots/paths.py +2 -2
- orionis/metadata/framework.py +38 -65
- orionis/metadata/package.py +280 -54
- 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 +162 -519
- 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.431.0.dist-info → orionis-0.434.0.dist-info}/METADATA +1 -1
- {orionis-0.431.0.dist-info → orionis-0.434.0.dist-info}/RECORD +82 -81
- tests/metadata/test_metadata_framework.py +64 -90
- tests/metadata/test_metadata_package.py +31 -31
- 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.431.0.dist-info → orionis-0.434.0.dist-info}/WHEEL +0 -0
- {orionis-0.431.0.dist-info → orionis-0.434.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.431.0.dist-info → orionis-0.434.0.dist-info}/top_level.txt +0 -0
- {orionis-0.431.0.dist-info → orionis-0.434.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,44 +168,39 @@ 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
|
-
|
|
222
204
|
# Validate and assign parameters using specialized validators
|
|
223
205
|
self.__verbosity = ValidVerbosity(verbosity)
|
|
224
206
|
self.__execution_mode = ValidExecutionMode(execution_mode)
|
|
@@ -249,48 +231,29 @@ class UnitTest(IUnitTest):
|
|
|
249
231
|
"""
|
|
250
232
|
Discover and add unit tests from a specified folder to the test suite.
|
|
251
233
|
|
|
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
234
|
Parameters
|
|
258
235
|
----------
|
|
259
236
|
base_path : str or Path
|
|
260
|
-
|
|
237
|
+
Base directory for resolving the folder path.
|
|
261
238
|
folder_path : str
|
|
262
|
-
|
|
239
|
+
Relative path to the folder containing test files.
|
|
263
240
|
pattern : str
|
|
264
|
-
|
|
241
|
+
File name pattern to match test files.
|
|
265
242
|
test_name_pattern : str, optional
|
|
266
|
-
|
|
267
|
-
this pattern will be included. If None, all test names are included.
|
|
243
|
+
Regular expression pattern to filter test names.
|
|
268
244
|
tags : list of str, optional
|
|
269
|
-
|
|
270
|
-
tags will be included. If None, no tag filtering is applied.
|
|
245
|
+
Tags to filter tests.
|
|
271
246
|
|
|
272
247
|
Returns
|
|
273
248
|
-------
|
|
274
249
|
UnitTest
|
|
275
|
-
The current instance with
|
|
250
|
+
The current instance with discovered tests added.
|
|
276
251
|
|
|
277
252
|
Raises
|
|
278
253
|
------
|
|
279
254
|
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.
|
|
255
|
+
If arguments are invalid, folder does not exist, no tests are found, or import/discovery errors occur.
|
|
292
256
|
"""
|
|
293
|
-
|
|
294
257
|
# Validate Parameters
|
|
295
258
|
self.__base_path = ValidBasePath(base_path)
|
|
296
259
|
self.__folder_path = ValidFolderPath(folder_path)
|
|
@@ -400,40 +363,23 @@ class UnitTest(IUnitTest):
|
|
|
400
363
|
"""
|
|
401
364
|
Discover and add unit tests from a specified Python module to the test suite.
|
|
402
365
|
|
|
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
366
|
Parameters
|
|
407
367
|
----------
|
|
408
368
|
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.
|
|
369
|
+
Fully qualified name of the module to discover tests from.
|
|
370
|
+
test_name_pattern : str, optional
|
|
371
|
+
Regular expression pattern to filter test names.
|
|
414
372
|
|
|
415
373
|
Returns
|
|
416
374
|
-------
|
|
417
375
|
UnitTest
|
|
418
|
-
The current UnitTest instance with
|
|
376
|
+
The current UnitTest instance with discovered tests added.
|
|
419
377
|
|
|
420
378
|
Raises
|
|
421
379
|
------
|
|
422
380
|
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.
|
|
381
|
+
If module_name is invalid, test_name_pattern is not a valid regex, the module cannot be imported, or no tests are found.
|
|
435
382
|
"""
|
|
436
|
-
|
|
437
383
|
# Validate input parameters
|
|
438
384
|
self.__module_name = ValidModuleName(module_name)
|
|
439
385
|
self.__test_name_pattern = ValidNamePattern(test_name_pattern)
|
|
@@ -503,28 +449,16 @@ class UnitTest(IUnitTest):
|
|
|
503
449
|
"""
|
|
504
450
|
Execute the test suite and return a summary of the results.
|
|
505
451
|
|
|
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
452
|
Returns
|
|
511
453
|
-------
|
|
512
|
-
|
|
513
|
-
|
|
454
|
+
dict
|
|
455
|
+
Dictionary summarizing the test results, including statistics and execution time.
|
|
514
456
|
|
|
515
457
|
Raises
|
|
516
458
|
------
|
|
517
459
|
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.
|
|
460
|
+
If the test suite execution fails and throw_exception is True.
|
|
526
461
|
"""
|
|
527
|
-
|
|
528
462
|
# Record the start time in nanoseconds
|
|
529
463
|
start_time = time.time_ns()
|
|
530
464
|
|
|
@@ -569,230 +503,146 @@ class UnitTest(IUnitTest):
|
|
|
569
503
|
suite: unittest.TestSuite
|
|
570
504
|
) -> List[unittest.TestCase]:
|
|
571
505
|
"""
|
|
572
|
-
Recursively
|
|
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.
|
|
506
|
+
Recursively flatten a unittest.TestSuite into a list of unique unittest.TestCase instances.
|
|
578
507
|
|
|
579
508
|
Parameters
|
|
580
509
|
----------
|
|
581
510
|
suite : unittest.TestSuite
|
|
582
|
-
The test suite to flatten.
|
|
511
|
+
The test suite to flatten.
|
|
583
512
|
|
|
584
513
|
Returns
|
|
585
514
|
-------
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
Notes
|
|
590
|
-
-----
|
|
591
|
-
- The uniqueness of test cases is determined by a "short id", which is composed of the last two segments
|
|
592
|
-
of the test's full id (typically "ClassName.methodName"). This helps avoid duplicate test cases in the result.
|
|
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.
|
|
515
|
+
list of unittest.TestCase
|
|
516
|
+
Flat list of unique unittest.TestCase instances.
|
|
595
517
|
"""
|
|
596
518
|
tests = []
|
|
597
519
|
seen_ids = set()
|
|
598
520
|
|
|
599
521
|
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
522
|
if isinstance(item, unittest.TestSuite):
|
|
607
|
-
# Recursively flatten all sub-items in the suite
|
|
608
523
|
for sub_item in item:
|
|
609
524
|
_flatten(sub_item)
|
|
610
525
|
elif hasattr(item, "id"):
|
|
611
|
-
# Generate a short id for uniqueness (e.g., "ClassName.methodName")
|
|
612
526
|
test_id = item.id()
|
|
613
527
|
parts = test_id.split('.')
|
|
614
528
|
if len(parts) >= 2:
|
|
615
529
|
short_id = '.'.join(parts[-2:])
|
|
616
530
|
else:
|
|
617
531
|
short_id = test_id
|
|
618
|
-
# Add the test case only if its short id has not been seen
|
|
619
532
|
if short_id not in seen_ids:
|
|
620
533
|
seen_ids.add(short_id)
|
|
621
534
|
tests.append(item)
|
|
622
535
|
|
|
623
|
-
# Start flattening from the root suite
|
|
624
536
|
_flatten(suite)
|
|
625
|
-
|
|
626
|
-
# Return a flat list of unique unittest.TestCase instances
|
|
627
537
|
return tests
|
|
628
538
|
|
|
629
539
|
def __runSuite(
|
|
630
540
|
self
|
|
631
541
|
) -> Tuple[unittest.TestResult, io.StringIO, io.StringIO]:
|
|
632
542
|
"""
|
|
633
|
-
|
|
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.
|
|
543
|
+
Execute the test suite using the configured execution mode, capturing output and error streams.
|
|
640
544
|
|
|
641
545
|
Returns
|
|
642
546
|
-------
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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.
|
|
547
|
+
tuple
|
|
548
|
+
(result, output_buffer, error_buffer)
|
|
549
|
+
result : unittest.TestResult
|
|
550
|
+
Test result object with test outcomes.
|
|
551
|
+
output_buffer : io.StringIO
|
|
552
|
+
Captured standard output during test execution.
|
|
553
|
+
error_buffer : io.StringIO
|
|
554
|
+
Captured standard error during test execution.
|
|
657
555
|
"""
|
|
658
|
-
|
|
659
|
-
# Create buffers to capture standard output and error during test execution
|
|
660
556
|
output_buffer = io.StringIO()
|
|
661
557
|
error_buffer = io.StringIO()
|
|
662
558
|
|
|
663
|
-
# Determine execution mode and run tests accordingly
|
|
664
559
|
if self.__execution_mode == ExecutionMode.PARALLEL.value:
|
|
665
|
-
# Run tests in parallel mode
|
|
666
560
|
result = self.__runTestsInParallel(
|
|
667
561
|
output_buffer,
|
|
668
562
|
error_buffer
|
|
669
563
|
)
|
|
670
564
|
else:
|
|
671
|
-
# Run tests sequentially (default)
|
|
672
565
|
result = self.__runTestsSequentially(
|
|
673
566
|
output_buffer,
|
|
674
567
|
error_buffer
|
|
675
568
|
)
|
|
676
569
|
|
|
677
|
-
# Return the test result along with the captured output and error buffers
|
|
678
570
|
return result, output_buffer, error_buffer
|
|
679
571
|
|
|
680
572
|
def __resolveFlattenedTestSuite(
|
|
681
573
|
self
|
|
682
574
|
) -> unittest.TestSuite:
|
|
683
575
|
"""
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
This method processes each test case in the internal suite, inspects the test method signatures,
|
|
687
|
-
and uses the application's dependency resolver to inject any required dependencies. It handles
|
|
688
|
-
decorated methods, methods without dependencies, and raises errors for unresolved dependencies.
|
|
689
|
-
The result is a new, flat unittest.TestSuite containing test cases with all dependencies resolved
|
|
690
|
-
and injected, ready for execution.
|
|
691
|
-
|
|
692
|
-
Parameters
|
|
693
|
-
----------
|
|
694
|
-
None
|
|
576
|
+
Resolve and inject dependencies for all test cases in the suite, returning a flattened TestSuite.
|
|
695
577
|
|
|
696
578
|
Returns
|
|
697
579
|
-------
|
|
698
580
|
unittest.TestSuite
|
|
699
|
-
|
|
700
|
-
where required. Test cases with unresolved dependencies will cause an exception to be raised.
|
|
581
|
+
New TestSuite with dependencies injected where required.
|
|
701
582
|
|
|
702
583
|
Raises
|
|
703
584
|
------
|
|
704
585
|
OrionisTestValueError
|
|
705
|
-
If any test method has dependencies
|
|
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.
|
|
586
|
+
If any test method has unresolved dependencies.
|
|
713
587
|
"""
|
|
714
|
-
|
|
715
|
-
# Create a new test suite to hold test cases with dependencies resolved
|
|
716
588
|
flattened_suite = unittest.TestSuite()
|
|
717
589
|
|
|
718
|
-
# Iterate through all test cases in the original (possibly nested) suite
|
|
719
590
|
for test_case in self.__flattenTestSuite(self.__suite):
|
|
720
591
|
|
|
721
|
-
# If it's a failed test, add it as-is to the flattened suite
|
|
722
592
|
if test_case.__class__.__name__ == "_FailedTest":
|
|
723
593
|
flattened_suite.addTest(test_case)
|
|
724
594
|
continue
|
|
725
595
|
|
|
726
|
-
# Get the test method name using reflection
|
|
727
596
|
rf_instance = ReflectionInstance(test_case)
|
|
728
597
|
method_name = rf_instance.getAttribute("_testMethodName")
|
|
729
598
|
|
|
730
|
-
# If no method name is found, add the test case as-is
|
|
731
599
|
if not method_name:
|
|
732
600
|
flattened_suite.addTest(test_case)
|
|
733
601
|
continue
|
|
734
602
|
|
|
735
|
-
# Retrieve the actual test method object from the class
|
|
736
603
|
test_method = getattr(test_case.__class__, method_name, None)
|
|
737
604
|
|
|
738
|
-
# Check if the test method is decorated by looking for __wrapped__ attributes
|
|
739
605
|
decorators = []
|
|
740
606
|
if hasattr(test_method, '__wrapped__'):
|
|
741
607
|
original = test_method
|
|
742
608
|
while hasattr(original, '__wrapped__'):
|
|
743
|
-
# Collect decorator names for informational purposes
|
|
744
609
|
if hasattr(original, '__qualname__'):
|
|
745
610
|
decorators.append(original.__qualname__)
|
|
746
611
|
elif hasattr(original, '__name__'):
|
|
747
612
|
decorators.append(original.__name__)
|
|
748
613
|
original = original.__wrapped__
|
|
749
614
|
|
|
750
|
-
# If decorators are present, add the test case as-is (do not inject dependencies)
|
|
751
615
|
if decorators:
|
|
752
616
|
flattened_suite.addTest(test_case)
|
|
753
617
|
continue
|
|
754
618
|
|
|
755
|
-
# Attempt to extract dependency information from the test method signature
|
|
756
619
|
signature = rf_instance.getMethodDependencies(method_name)
|
|
757
620
|
|
|
758
|
-
# If there are no dependencies to resolve, or unresolved dependencies exist, add as-is
|
|
759
621
|
if ((not signature.resolved and not signature.unresolved) or (not signature.resolved and len(signature.unresolved) > 0)):
|
|
760
622
|
flattened_suite.addTest(test_case)
|
|
761
623
|
continue
|
|
762
624
|
|
|
763
|
-
# If there are unresolved dependencies, raise an error
|
|
764
625
|
if (len(signature.unresolved) > 0):
|
|
765
626
|
raise OrionisTestValueError(
|
|
766
627
|
f"Test method '{method_name}' in class '{test_case.__class__.__name__}' has unresolved dependencies: {signature.unresolved}. "
|
|
767
628
|
"Please ensure all dependencies are correctly defined and available."
|
|
768
629
|
)
|
|
769
630
|
|
|
770
|
-
# All dependencies are resolved; prepare to inject them into the test method
|
|
771
631
|
test_class = ReflectionInstance(test_case).getClass()
|
|
772
632
|
original_method = getattr(test_class, method_name)
|
|
773
633
|
|
|
774
|
-
# Resolve dependencies using the application's resolver
|
|
775
634
|
params = Resolver(self.__app).resolveSignature(signature)
|
|
776
635
|
|
|
777
|
-
# Create a wrapper function that injects resolved dependencies into the test method
|
|
778
636
|
def create_test_wrapper(original_test, resolved_args: dict):
|
|
779
637
|
def wrapper(self_instance):
|
|
780
638
|
return original_test(self_instance, **resolved_args)
|
|
781
639
|
return wrapper
|
|
782
640
|
|
|
783
|
-
# Wrap the original test method with the dependency-injecting wrapper
|
|
784
641
|
wrapped_method = create_test_wrapper(original_method, params)
|
|
785
|
-
|
|
786
|
-
# Bind the wrapped method to the test case instance
|
|
787
642
|
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
643
|
setattr(test_case, method_name, bound_method)
|
|
791
|
-
|
|
792
|
-
# Add the modified test case to the flattened suite
|
|
793
644
|
flattened_suite.addTest(test_case)
|
|
794
645
|
|
|
795
|
-
# Return the new flattened suite with all dependencies resolved and injected
|
|
796
646
|
return flattened_suite
|
|
797
647
|
|
|
798
648
|
def __runTestsSequentially(
|
|
@@ -801,23 +651,20 @@ class UnitTest(IUnitTest):
|
|
|
801
651
|
error_buffer: io.StringIO
|
|
802
652
|
) -> unittest.TestResult:
|
|
803
653
|
"""
|
|
804
|
-
|
|
654
|
+
Execute the test suite sequentially, capturing output and error streams.
|
|
805
655
|
|
|
806
656
|
Parameters
|
|
807
657
|
----------
|
|
808
658
|
output_buffer : io.StringIO
|
|
809
|
-
|
|
659
|
+
Buffer to capture standard output.
|
|
810
660
|
error_buffer : io.StringIO
|
|
811
|
-
|
|
661
|
+
Buffer to capture standard error.
|
|
812
662
|
|
|
813
663
|
Returns
|
|
814
664
|
-------
|
|
815
665
|
unittest.TestResult
|
|
816
|
-
|
|
817
|
-
passed, failed, and skipped tests.
|
|
666
|
+
Result of the test suite execution.
|
|
818
667
|
"""
|
|
819
|
-
|
|
820
|
-
# Create a custom result class to capture detailed test results
|
|
821
668
|
result = None
|
|
822
669
|
for case in self.__resolveFlattenedTestSuite():
|
|
823
670
|
|
|
@@ -835,16 +682,13 @@ class UnitTest(IUnitTest):
|
|
|
835
682
|
)
|
|
836
683
|
single_result: IOrionisTestResult = runner.run(unittest.TestSuite([case]))
|
|
837
684
|
|
|
838
|
-
# Print a concise summary for each test.
|
|
839
685
|
self.__printer.unittestResult(single_result.test_results[0])
|
|
840
686
|
|
|
841
|
-
# Merge results
|
|
842
687
|
if result is None:
|
|
843
688
|
result = single_result
|
|
844
689
|
else:
|
|
845
690
|
self.__mergeTestResults(result, single_result)
|
|
846
691
|
|
|
847
|
-
# Return the result object containing test outcomes
|
|
848
692
|
return result
|
|
849
693
|
|
|
850
694
|
def __runTestsInParallel(
|
|
@@ -853,82 +697,47 @@ class UnitTest(IUnitTest):
|
|
|
853
697
|
error_buffer: io.StringIO
|
|
854
698
|
) -> unittest.TestResult:
|
|
855
699
|
"""
|
|
856
|
-
|
|
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.
|
|
700
|
+
Execute all test cases in the test suite concurrently using a thread pool, aggregating results.
|
|
865
701
|
|
|
866
702
|
Parameters
|
|
867
703
|
----------
|
|
868
704
|
output_buffer : io.StringIO
|
|
869
|
-
Buffer to capture standard output
|
|
705
|
+
Buffer to capture standard output.
|
|
870
706
|
error_buffer : io.StringIO
|
|
871
|
-
Buffer to capture standard error
|
|
707
|
+
Buffer to capture standard error.
|
|
872
708
|
|
|
873
709
|
Returns
|
|
874
710
|
-------
|
|
875
711
|
unittest.TestResult
|
|
876
|
-
|
|
877
|
-
the aggregated outcomes of all executed tests, including detailed information
|
|
878
|
-
about passed, failed, errored, and skipped tests.
|
|
879
|
-
|
|
880
|
-
Notes
|
|
881
|
-
-----
|
|
882
|
-
- Uses a custom result class to collect detailed test outcomes.
|
|
883
|
-
- If `fail_fast` is enabled and a test fails, remaining tests are canceled.
|
|
884
|
-
- Output and error streams are captured for the entire parallel execution.
|
|
712
|
+
Combined result object containing all test outcomes.
|
|
885
713
|
"""
|
|
886
|
-
|
|
887
|
-
# Flatten the test suite to get individual test cases
|
|
888
714
|
test_cases = list(self.__resolveFlattenedTestSuite())
|
|
889
|
-
|
|
890
|
-
# Create a custom result instance to collect all results
|
|
891
715
|
result_class = self.__customResultClass()
|
|
892
716
|
combined_result = result_class(io.StringIO(), descriptions=True, verbosity=self.__verbosity)
|
|
893
717
|
|
|
894
|
-
# Helper function to run a single test and return its result.
|
|
895
|
-
# Each test runs in its own thread with minimal output.
|
|
896
718
|
def run_single_test(test):
|
|
897
719
|
runner = unittest.TextTestRunner(
|
|
898
|
-
stream=io.StringIO(),
|
|
720
|
+
stream=io.StringIO(),
|
|
899
721
|
verbosity=0,
|
|
900
722
|
failfast=False,
|
|
901
723
|
resultclass=result_class
|
|
902
724
|
)
|
|
903
|
-
# Run the test and return its result object
|
|
904
725
|
return runner.run(unittest.TestSuite([test]))
|
|
905
726
|
|
|
906
|
-
# Redirect stdout and stderr to the provided buffers during parallel execution
|
|
907
727
|
with redirect_stdout(output_buffer), redirect_stderr(error_buffer):
|
|
908
|
-
|
|
909
|
-
# Create a ThreadPoolExecutor to run tests in parallel
|
|
910
728
|
with ThreadPoolExecutor(max_workers=self.__max_workers) as executor:
|
|
911
|
-
|
|
912
|
-
# Submit all test cases to the executor
|
|
913
729
|
futures = [executor.submit(run_single_test, test) for test in test_cases]
|
|
914
|
-
|
|
915
|
-
# Process the results as they complete
|
|
916
730
|
for future in as_completed(futures):
|
|
917
731
|
test_result = future.result()
|
|
918
|
-
# Merge each individual test result into the combined result
|
|
919
732
|
self.__mergeTestResults(combined_result, test_result)
|
|
920
|
-
|
|
921
|
-
# If fail_fast is enabled and a test failed, cancel remaining futures
|
|
922
733
|
if self.__fail_fast and not combined_result.wasSuccessful():
|
|
923
734
|
for f in futures:
|
|
924
735
|
f.cancel()
|
|
925
736
|
break
|
|
926
737
|
|
|
927
|
-
# Print a concise summary for each test in the combined result
|
|
928
738
|
for test_result in combined_result.test_results:
|
|
929
739
|
self.__printer.unittestResult(test_result)
|
|
930
740
|
|
|
931
|
-
# Return the combined result object containing all test outcomes
|
|
932
741
|
return combined_result
|
|
933
742
|
|
|
934
743
|
def __mergeTestResults(
|
|
@@ -939,42 +748,23 @@ class UnitTest(IUnitTest):
|
|
|
939
748
|
"""
|
|
940
749
|
Merge the results of two unittest.TestResult objects into a single result.
|
|
941
750
|
|
|
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
751
|
Parameters
|
|
949
752
|
----------
|
|
950
753
|
combined_result : unittest.TestResult
|
|
951
|
-
The TestResult object
|
|
754
|
+
The TestResult object to update.
|
|
952
755
|
individual_result : unittest.TestResult
|
|
953
|
-
The TestResult object
|
|
756
|
+
The TestResult object to merge in.
|
|
954
757
|
|
|
955
758
|
Returns
|
|
956
759
|
-------
|
|
957
760
|
None
|
|
958
|
-
This method does not return a value. It modifies `combined_result` in place.
|
|
959
|
-
|
|
960
|
-
Notes
|
|
961
|
-
-----
|
|
962
|
-
- The method aggregates all relevant test outcome lists and counters.
|
|
963
|
-
- If the `test_results` attribute exists (for custom result classes), it is also merged.
|
|
964
|
-
- This is useful for combining results from parallel or sequential test executions.
|
|
965
761
|
"""
|
|
966
|
-
|
|
967
|
-
# Aggregate the number of tests run
|
|
968
762
|
combined_result.testsRun += individual_result.testsRun
|
|
969
|
-
|
|
970
|
-
# Extend the lists of failures, errors, skipped, expected failures, and unexpected successes
|
|
971
763
|
combined_result.failures.extend(individual_result.failures)
|
|
972
764
|
combined_result.errors.extend(individual_result.errors)
|
|
973
765
|
combined_result.skipped.extend(individual_result.skipped)
|
|
974
766
|
combined_result.expectedFailures.extend(individual_result.expectedFailures)
|
|
975
767
|
combined_result.unexpectedSuccesses.extend(individual_result.unexpectedSuccesses)
|
|
976
|
-
|
|
977
|
-
# Merge custom test results if present (for enhanced result tracking)
|
|
978
768
|
if hasattr(individual_result, 'test_results'):
|
|
979
769
|
if not hasattr(combined_result, 'test_results'):
|
|
980
770
|
combined_result.test_results = []
|
|
@@ -984,30 +774,15 @@ class UnitTest(IUnitTest):
|
|
|
984
774
|
self
|
|
985
775
|
) -> type:
|
|
986
776
|
"""
|
|
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.
|
|
777
|
+
Create a custom test result class for enhanced test tracking.
|
|
991
778
|
|
|
992
779
|
Returns
|
|
993
780
|
-------
|
|
994
781
|
type
|
|
995
|
-
|
|
996
|
-
test results, including success, failure, error, and skipped tests. The class
|
|
997
|
-
collects detailed information about each test, such as execution time, error
|
|
998
|
-
messages, traceback, and file path.
|
|
999
|
-
|
|
1000
|
-
Notes
|
|
1001
|
-
-----
|
|
1002
|
-
The `OrionisTestResult` class includes the following method overrides:
|
|
1003
|
-
The method uses the `this` reference to access the outer class's methods, such as
|
|
1004
|
-
`_extractErrorInfo`, for extracting and formatting error information.
|
|
782
|
+
Dynamically created OrionisTestResult class extending unittest.TextTestResult.
|
|
1005
783
|
"""
|
|
1006
|
-
|
|
1007
|
-
# Use `this` to refer to the outer class instance
|
|
1008
784
|
this = self
|
|
1009
785
|
|
|
1010
|
-
# Define the custom test result class
|
|
1011
786
|
class OrionisTestResult(unittest.TextTestResult):
|
|
1012
787
|
def __init__(self, *args, **kwargs):
|
|
1013
788
|
super().__init__(*args, **kwargs)
|
|
@@ -1101,7 +876,6 @@ class UnitTest(IUnitTest):
|
|
|
1101
876
|
)
|
|
1102
877
|
)
|
|
1103
878
|
|
|
1104
|
-
# Return the dynamically created OrionisTestResult class
|
|
1105
879
|
return OrionisTestResult
|
|
1106
880
|
|
|
1107
881
|
def _extractErrorInfo(
|
|
@@ -1109,61 +883,34 @@ class UnitTest(IUnitTest):
|
|
|
1109
883
|
traceback_str: str
|
|
1110
884
|
) -> Tuple[Optional[str], Optional[str]]:
|
|
1111
885
|
"""
|
|
1112
|
-
|
|
1113
|
-
|
|
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.
|
|
886
|
+
Extract the file path and a cleaned traceback from a given traceback string.
|
|
1118
887
|
|
|
1119
888
|
Parameters
|
|
1120
889
|
----------
|
|
1121
890
|
traceback_str : str
|
|
1122
|
-
|
|
891
|
+
Full traceback string to process.
|
|
1123
892
|
|
|
1124
893
|
Returns
|
|
1125
894
|
-------
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
Notes
|
|
1132
|
-
-----
|
|
1133
|
-
The cleaned traceback starts from the first occurrence of the test file path and omits lines
|
|
1134
|
-
that are part of the Python standard library or third-party packages, focusing on user code.
|
|
895
|
+
tuple
|
|
896
|
+
file_path : str or None
|
|
897
|
+
Path to the Python file where the error occurred.
|
|
898
|
+
clean_tb : str or None
|
|
899
|
+
Cleaned traceback string with framework internals removed.
|
|
1135
900
|
"""
|
|
1136
|
-
|
|
1137
|
-
# Extract all Python file paths from the traceback string
|
|
1138
901
|
file_matches = re.findall(r'File ["\'](.*?.py)["\']', traceback_str)
|
|
1139
|
-
|
|
1140
|
-
# Use the last file in the traceback as the most relevant (where the error occurred)
|
|
1141
902
|
file_path = file_matches[-1] if file_matches else None
|
|
1142
|
-
|
|
1143
|
-
# Split the traceback into individual lines for processing
|
|
1144
903
|
tb_lines = traceback_str.split('\n')
|
|
1145
904
|
clean_lines = []
|
|
1146
905
|
relevant_lines_started = False
|
|
1147
|
-
|
|
1148
|
-
# Iterate through each line in the traceback
|
|
1149
906
|
for line in tb_lines:
|
|
1150
|
-
|
|
1151
|
-
# Skip lines that are part of framework internals or third-party libraries
|
|
1152
907
|
if any(s in line for s in ['unittest/', 'lib/python', 'site-packages']):
|
|
1153
908
|
continue
|
|
1154
|
-
|
|
1155
|
-
# Start including lines once the relevant file path is found
|
|
1156
909
|
if file_path and file_path in line and not relevant_lines_started:
|
|
1157
910
|
relevant_lines_started = True
|
|
1158
|
-
|
|
1159
|
-
# If we've started collecting relevant lines, add them to the cleaned traceback
|
|
1160
911
|
if relevant_lines_started:
|
|
1161
912
|
clean_lines.append(line)
|
|
1162
|
-
|
|
1163
|
-
# Join the cleaned lines into a single string; if none, return the original traceback
|
|
1164
913
|
clean_tb = str('\n').join(clean_lines) if clean_lines else traceback_str
|
|
1165
|
-
|
|
1166
|
-
# Return the file path and cleaned traceback
|
|
1167
914
|
return file_path, clean_tb
|
|
1168
915
|
|
|
1169
916
|
def __generateSummary(
|
|
@@ -1172,51 +919,26 @@ class UnitTest(IUnitTest):
|
|
|
1172
919
|
execution_time: float
|
|
1173
920
|
) -> Dict[str, Any]:
|
|
1174
921
|
"""
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
This method processes the provided unittest.TestResult object and aggregates
|
|
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.
|
|
922
|
+
Generate a summary of the test suite execution.
|
|
1187
923
|
|
|
1188
924
|
Parameters
|
|
1189
925
|
----------
|
|
1190
926
|
result : unittest.TestResult
|
|
1191
|
-
|
|
927
|
+
Result object containing details of the test execution.
|
|
1192
928
|
execution_time : float
|
|
1193
|
-
|
|
929
|
+
Total execution time in milliseconds.
|
|
1194
930
|
|
|
1195
931
|
Returns
|
|
1196
932
|
-------
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
- total_tests (int): Total number of tests executed.
|
|
1200
|
-
- passed (int): Number of tests that passed.
|
|
1201
|
-
- failed (int): Number of tests that failed.
|
|
1202
|
-
- errors (int): Number of tests that encountered errors.
|
|
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.
|
|
933
|
+
dict
|
|
934
|
+
Dictionary containing test statistics and details.
|
|
1210
935
|
|
|
1211
936
|
Side Effects
|
|
1212
937
|
------------
|
|
1213
|
-
|
|
1214
|
-
|
|
938
|
+
If persistence is enabled, the summary is persisted to storage.
|
|
939
|
+
If web reporting is enabled, a web report is generated.
|
|
1215
940
|
"""
|
|
1216
|
-
|
|
1217
941
|
test_details = []
|
|
1218
|
-
|
|
1219
|
-
# Collect detailed information for each test result
|
|
1220
942
|
for test_result in result.test_results:
|
|
1221
943
|
rst: TestResult = test_result
|
|
1222
944
|
test_details.append({
|
|
@@ -1230,13 +952,8 @@ class UnitTest(IUnitTest):
|
|
|
1230
952
|
'file_path': rst.file_path,
|
|
1231
953
|
'doc_string': rst.doc_string
|
|
1232
954
|
})
|
|
1233
|
-
|
|
1234
|
-
# Calculate the number of passed tests
|
|
1235
955
|
passed = result.testsRun - len(result.failures) - len(result.errors) - len(result.skipped)
|
|
1236
|
-
# Calculate the success rate as a percentage
|
|
1237
956
|
success_rate = (passed / result.testsRun * 100) if result.testsRun > 0 else 100.0
|
|
1238
|
-
|
|
1239
|
-
# Build the summary dictionary
|
|
1240
957
|
self.__result = {
|
|
1241
958
|
"total_tests": result.testsRun,
|
|
1242
959
|
"passed": passed,
|
|
@@ -1248,16 +965,10 @@ class UnitTest(IUnitTest):
|
|
|
1248
965
|
"test_details": test_details,
|
|
1249
966
|
"timestamp": datetime.now().isoformat()
|
|
1250
967
|
}
|
|
1251
|
-
|
|
1252
|
-
# Persist the summary if persistence is enabled
|
|
1253
968
|
if self.__persistent:
|
|
1254
969
|
self.__handlePersistResults(self.__result)
|
|
1255
|
-
|
|
1256
|
-
# Generate a web report if web reporting is enabled
|
|
1257
970
|
if self.__web_report:
|
|
1258
971
|
self.__handleWebReport(self.__result)
|
|
1259
|
-
|
|
1260
|
-
# Return the summary dictionary
|
|
1261
972
|
return self.__result
|
|
1262
973
|
|
|
1263
974
|
def __handleWebReport(
|
|
@@ -1267,38 +978,20 @@ class UnitTest(IUnitTest):
|
|
|
1267
978
|
"""
|
|
1268
979
|
Generate a web-based report for the provided test results summary.
|
|
1269
980
|
|
|
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
981
|
Parameters
|
|
1276
982
|
----------
|
|
1277
983
|
summary : dict
|
|
1278
|
-
|
|
984
|
+
Summary of test results for web report generation.
|
|
1279
985
|
|
|
1280
986
|
Returns
|
|
1281
987
|
-------
|
|
1282
988
|
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
|
-
|
|
1286
|
-
Notes
|
|
1287
|
-
-----
|
|
1288
|
-
- The storage path for the report is determined by `self.__base_path`.
|
|
1289
|
-
- If result persistence is enabled and the driver is set to 'sqlite', the report is marked as persistent.
|
|
1290
|
-
- The web report is generated using the `TestingResultRender` class.
|
|
1291
|
-
- The method prints the link to the generated web report using the printer.
|
|
1292
989
|
"""
|
|
1293
|
-
|
|
1294
|
-
# Create the TestingResultRender instance with the storage path and summary.
|
|
1295
990
|
render = TestingResultRender(
|
|
1296
991
|
storage_path=self.__storage,
|
|
1297
992
|
result=summary,
|
|
1298
993
|
persist=self.__persistent and self.__persistent_driver == 'sqlite'
|
|
1299
994
|
)
|
|
1300
|
-
|
|
1301
|
-
# Render the web report and print the link using the printer.
|
|
1302
995
|
self.__printer.linkWebReport(render.render())
|
|
1303
996
|
|
|
1304
997
|
def __handlePersistResults(
|
|
@@ -1308,71 +1001,35 @@ class UnitTest(IUnitTest):
|
|
|
1308
1001
|
"""
|
|
1309
1002
|
Persist the test results summary using the configured persistence driver.
|
|
1310
1003
|
|
|
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
1004
|
Parameters
|
|
1316
1005
|
----------
|
|
1317
1006
|
summary : dict
|
|
1318
|
-
|
|
1319
|
-
details, such as test counts, statuses, execution times, and individual test results.
|
|
1007
|
+
Test results summary to persist.
|
|
1320
1008
|
|
|
1321
1009
|
Returns
|
|
1322
1010
|
-------
|
|
1323
1011
|
None
|
|
1324
|
-
This method does not return any value. It performs persistence as a side effect.
|
|
1325
1012
|
|
|
1326
1013
|
Raises
|
|
1327
1014
|
------
|
|
1328
1015
|
OSError
|
|
1329
|
-
If there is an error creating directories or writing files
|
|
1016
|
+
If there is an error creating directories or writing files.
|
|
1330
1017
|
OrionisTestPersistenceError
|
|
1331
1018
|
If database operations fail or any other error occurs during persistence.
|
|
1332
|
-
|
|
1333
|
-
Notes
|
|
1334
|
-
-----
|
|
1335
|
-
- If `self.__persistent_driver` is set to 'sqlite', the summary is stored in an SQLite database
|
|
1336
|
-
using the TestLogs class.
|
|
1337
|
-
- If `self.__persistent_driver` is set to 'json', the summary is written to a timestamped JSON
|
|
1338
|
-
file in the specified base path.
|
|
1339
|
-
- The method ensures that the target directory exists before writing files.
|
|
1340
|
-
- Any errors encountered during persistence are raised as exceptions for the caller to handle.
|
|
1341
1019
|
"""
|
|
1342
|
-
|
|
1343
1020
|
try:
|
|
1344
|
-
|
|
1345
1021
|
if self.__persistent_driver == PersistentDrivers.SQLITE.value:
|
|
1346
|
-
|
|
1347
|
-
# Persist results to SQLite database using TestLogs
|
|
1348
1022
|
history = TestLogs(self.__storage)
|
|
1349
|
-
|
|
1350
|
-
# Insert the summary into the database
|
|
1351
1023
|
history.create(summary)
|
|
1352
|
-
|
|
1353
1024
|
elif self.__persistent_driver == PersistentDrivers.JSON.value:
|
|
1354
|
-
|
|
1355
|
-
# Generate a timestamp for the log file name
|
|
1356
1025
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
1357
|
-
|
|
1358
|
-
# Construct the log file path with the timestamp
|
|
1359
1026
|
log_path = Path(self.__storage) / f"{timestamp}_test_results.json"
|
|
1360
|
-
|
|
1361
|
-
# Ensure the directory exists
|
|
1362
1027
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1363
|
-
|
|
1364
|
-
# Write the summary dictionary to the JSON file
|
|
1365
1028
|
with open(log_path, 'w', encoding='utf-8') as log:
|
|
1366
1029
|
json.dump(summary, log, indent=4)
|
|
1367
|
-
|
|
1368
1030
|
except OSError as e:
|
|
1369
|
-
|
|
1370
|
-
# Raise an OSError if there is an issue with file or directory operations
|
|
1371
1031
|
raise OSError(f"Error creating directories or writing files: {str(e)}")
|
|
1372
|
-
|
|
1373
1032
|
except Exception as e:
|
|
1374
|
-
|
|
1375
|
-
# Raise a custom exception for any other issues during persistence
|
|
1376
1033
|
raise OrionisTestPersistenceError(f"Error persisting test results: {str(e)}")
|
|
1377
1034
|
|
|
1378
1035
|
def __filterTestsByName(
|
|
@@ -1381,29 +1038,26 @@ class UnitTest(IUnitTest):
|
|
|
1381
1038
|
pattern: str
|
|
1382
1039
|
) -> unittest.TestSuite:
|
|
1383
1040
|
"""
|
|
1384
|
-
|
|
1041
|
+
Filter tests in a test suite based on a specified name pattern.
|
|
1042
|
+
|
|
1385
1043
|
Parameters
|
|
1386
1044
|
----------
|
|
1387
1045
|
suite : unittest.TestSuite
|
|
1388
|
-
|
|
1046
|
+
Test suite containing the tests to filter.
|
|
1389
1047
|
pattern : str
|
|
1390
|
-
|
|
1048
|
+
Regular expression pattern to match test names.
|
|
1049
|
+
|
|
1391
1050
|
Returns
|
|
1392
1051
|
-------
|
|
1393
1052
|
unittest.TestSuite
|
|
1394
|
-
|
|
1053
|
+
New test suite containing only tests that match the pattern.
|
|
1054
|
+
|
|
1395
1055
|
Raises
|
|
1396
1056
|
------
|
|
1397
1057
|
OrionisTestValueError
|
|
1398
1058
|
If the provided pattern is not a valid regular expression.
|
|
1399
|
-
Notes
|
|
1400
|
-
-----
|
|
1401
1059
|
"""
|
|
1402
|
-
|
|
1403
|
-
# Initialize an empty TestSuite to hold the filtered tests
|
|
1404
1060
|
filtered_suite = unittest.TestSuite()
|
|
1405
|
-
|
|
1406
|
-
# Validate the pattern
|
|
1407
1061
|
try:
|
|
1408
1062
|
regex = re.compile(pattern)
|
|
1409
1063
|
except re.error as e:
|
|
@@ -1412,13 +1066,9 @@ class UnitTest(IUnitTest):
|
|
|
1412
1066
|
f"Regular expression compilation error: {str(e)}. "
|
|
1413
1067
|
"Please check the pattern syntax and try again."
|
|
1414
1068
|
)
|
|
1415
|
-
|
|
1416
|
-
# Iterate through all tests in the suite and filter by the regex pattern
|
|
1417
1069
|
for test in self.__flattenTestSuite(suite):
|
|
1418
1070
|
if regex.search(test.id()):
|
|
1419
1071
|
filtered_suite.addTest(test)
|
|
1420
|
-
|
|
1421
|
-
# Return the filtered suite containing only tests that match the pattern
|
|
1422
1072
|
return filtered_suite
|
|
1423
1073
|
|
|
1424
1074
|
def __filterTestsByTags(
|
|
@@ -1429,45 +1079,30 @@ class UnitTest(IUnitTest):
|
|
|
1429
1079
|
"""
|
|
1430
1080
|
Filter tests in a unittest TestSuite by specified tags.
|
|
1431
1081
|
|
|
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.
|
|
1435
|
-
|
|
1436
1082
|
Parameters
|
|
1437
1083
|
----------
|
|
1438
1084
|
suite : unittest.TestSuite
|
|
1439
|
-
|
|
1085
|
+
Original TestSuite containing all tests.
|
|
1440
1086
|
tags : list of str
|
|
1441
|
-
|
|
1087
|
+
Tags to filter the tests by.
|
|
1442
1088
|
|
|
1443
1089
|
Returns
|
|
1444
1090
|
-------
|
|
1445
1091
|
unittest.TestSuite
|
|
1446
|
-
|
|
1092
|
+
New TestSuite containing only tests with matching tags.
|
|
1447
1093
|
"""
|
|
1448
|
-
|
|
1449
|
-
# Initialize an empty TestSuite to hold the filtered tests
|
|
1450
1094
|
filtered_suite = unittest.TestSuite()
|
|
1451
1095
|
tag_set = set(tags)
|
|
1452
|
-
|
|
1453
1096
|
for test in self.__flattenTestSuite(suite):
|
|
1454
|
-
|
|
1455
|
-
# Get test method if this is a TestCase instance
|
|
1456
1097
|
test_method = getattr(test, test._testMethodName, None)
|
|
1457
|
-
|
|
1458
|
-
# Check for tags attribute on the test method
|
|
1459
1098
|
if hasattr(test_method, '__tags__'):
|
|
1460
1099
|
method_tags = set(getattr(test_method, '__tags__'))
|
|
1461
1100
|
if tag_set.intersection(method_tags):
|
|
1462
1101
|
filtered_suite.addTest(test)
|
|
1463
|
-
|
|
1464
|
-
# Also check on the test case class
|
|
1465
1102
|
elif hasattr(test, '__tags__'):
|
|
1466
1103
|
class_tags = set(getattr(test, '__tags__'))
|
|
1467
1104
|
if tag_set.intersection(class_tags):
|
|
1468
1105
|
filtered_suite.addTest(test)
|
|
1469
|
-
|
|
1470
|
-
# Return the filtered suite containing only tests with matching tags
|
|
1471
1106
|
return filtered_suite
|
|
1472
1107
|
|
|
1473
1108
|
def getTestNames(
|
|
@@ -1478,8 +1113,8 @@ class UnitTest(IUnitTest):
|
|
|
1478
1113
|
|
|
1479
1114
|
Returns
|
|
1480
1115
|
-------
|
|
1481
|
-
|
|
1482
|
-
List of test names
|
|
1116
|
+
list of str
|
|
1117
|
+
List of test names from the test suite.
|
|
1483
1118
|
"""
|
|
1484
1119
|
return [test.id() for test in self.__flattenTestSuite(self.__suite)]
|
|
1485
1120
|
|
|
@@ -1487,12 +1122,12 @@ class UnitTest(IUnitTest):
|
|
|
1487
1122
|
self
|
|
1488
1123
|
) -> int:
|
|
1489
1124
|
"""
|
|
1490
|
-
|
|
1125
|
+
Get the total number of test cases in the test suite.
|
|
1491
1126
|
|
|
1492
1127
|
Returns
|
|
1493
1128
|
-------
|
|
1494
1129
|
int
|
|
1495
|
-
|
|
1130
|
+
Total number of individual test cases in the suite.
|
|
1496
1131
|
"""
|
|
1497
1132
|
return len(list(self.__flattenTestSuite(self.__suite)))
|
|
1498
1133
|
|
|
@@ -1502,7 +1137,9 @@ class UnitTest(IUnitTest):
|
|
|
1502
1137
|
"""
|
|
1503
1138
|
Clear all tests from the current test suite.
|
|
1504
1139
|
|
|
1505
|
-
|
|
1140
|
+
Returns
|
|
1141
|
+
-------
|
|
1142
|
+
None
|
|
1506
1143
|
"""
|
|
1507
1144
|
self.__suite = unittest.TestSuite()
|
|
1508
1145
|
|
|
@@ -1510,12 +1147,12 @@ class UnitTest(IUnitTest):
|
|
|
1510
1147
|
self
|
|
1511
1148
|
) -> dict:
|
|
1512
1149
|
"""
|
|
1513
|
-
|
|
1150
|
+
Get the results of the executed test suite.
|
|
1514
1151
|
|
|
1515
1152
|
Returns
|
|
1516
1153
|
-------
|
|
1517
|
-
|
|
1518
|
-
|
|
1154
|
+
dict
|
|
1155
|
+
Result of the executed test suite.
|
|
1519
1156
|
"""
|
|
1520
1157
|
return self.__result
|
|
1521
1158
|
|
|
@@ -1523,12 +1160,12 @@ class UnitTest(IUnitTest):
|
|
|
1523
1160
|
self
|
|
1524
1161
|
) -> int:
|
|
1525
1162
|
"""
|
|
1526
|
-
|
|
1527
|
-
|
|
1163
|
+
Get the output buffer used for capturing test results.
|
|
1164
|
+
|
|
1528
1165
|
Returns
|
|
1529
1166
|
-------
|
|
1530
1167
|
int
|
|
1531
|
-
|
|
1168
|
+
Output buffer containing the results of the test execution.
|
|
1532
1169
|
"""
|
|
1533
1170
|
return self.__output_buffer
|
|
1534
1171
|
|
|
@@ -1536,8 +1173,11 @@ class UnitTest(IUnitTest):
|
|
|
1536
1173
|
self
|
|
1537
1174
|
) -> None:
|
|
1538
1175
|
"""
|
|
1539
|
-
|
|
1540
|
-
|
|
1176
|
+
Print the contents of the output buffer to the console.
|
|
1177
|
+
|
|
1178
|
+
Returns
|
|
1179
|
+
-------
|
|
1180
|
+
None
|
|
1541
1181
|
"""
|
|
1542
1182
|
self.__printer.print(self.__output_buffer)
|
|
1543
1183
|
|
|
@@ -1545,12 +1185,12 @@ class UnitTest(IUnitTest):
|
|
|
1545
1185
|
self
|
|
1546
1186
|
) -> int:
|
|
1547
1187
|
"""
|
|
1548
|
-
|
|
1549
|
-
|
|
1188
|
+
Get the error buffer used for capturing test errors.
|
|
1189
|
+
|
|
1550
1190
|
Returns
|
|
1551
1191
|
-------
|
|
1552
1192
|
int
|
|
1553
|
-
|
|
1193
|
+
Error buffer containing errors encountered during test execution.
|
|
1554
1194
|
"""
|
|
1555
1195
|
return self.__error_buffer
|
|
1556
1196
|
|
|
@@ -1558,7 +1198,10 @@ class UnitTest(IUnitTest):
|
|
|
1558
1198
|
self
|
|
1559
1199
|
) -> None:
|
|
1560
1200
|
"""
|
|
1561
|
-
|
|
1562
|
-
|
|
1201
|
+
Print the contents of the error buffer to the console.
|
|
1202
|
+
|
|
1203
|
+
Returns
|
|
1204
|
+
-------
|
|
1205
|
+
None
|
|
1563
1206
|
"""
|
|
1564
|
-
self.__printer.print(self.__error_buffer)
|
|
1207
|
+
self.__printer.print(self.__error_buffer)
|