orionis 0.405.0__py3-none-any.whl → 0.406.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/console/base/command.py +57 -50
- orionis/console/base/contracts/command.py +68 -0
- orionis/console/dynamic/contracts/progress_bar.py +3 -3
- orionis/console/dynamic/progress_bar.py +8 -8
- orionis/console/output/console.py +8 -2
- orionis/console/output/contracts/console.py +1 -1
- orionis/container/container.py +2 -2
- orionis/container/context/scope.py +4 -1
- orionis/container/contracts/service_provider.py +2 -2
- orionis/container/entities/binding.py +31 -44
- orionis/container/enums/lifetimes.py +22 -1
- orionis/container/facades/facade.py +1 -2
- orionis/container/providers/service_provider.py +2 -2
- orionis/foundation/application.py +542 -248
- orionis/foundation/config/app/entities/app.py +107 -90
- orionis/foundation/config/auth/entities/auth.py +4 -33
- orionis/foundation/config/cache/entities/cache.py +18 -41
- orionis/foundation/config/cache/entities/file.py +8 -35
- orionis/foundation/config/cache/entities/stores.py +17 -38
- orionis/foundation/config/cors/entities/cors.py +41 -54
- orionis/foundation/config/database/entities/connections.py +40 -56
- orionis/foundation/config/database/entities/database.py +11 -38
- orionis/foundation/config/database/entities/mysql.py +48 -76
- orionis/foundation/config/database/entities/oracle.py +30 -57
- orionis/foundation/config/database/entities/pgsql.py +45 -61
- orionis/foundation/config/database/entities/sqlite.py +26 -53
- orionis/foundation/config/filesystems/entitites/aws.py +28 -49
- orionis/foundation/config/filesystems/entitites/disks.py +27 -47
- orionis/foundation/config/filesystems/entitites/filesystems.py +15 -37
- orionis/foundation/config/filesystems/entitites/local.py +9 -35
- orionis/foundation/config/filesystems/entitites/public.py +14 -41
- orionis/foundation/config/logging/entities/channels.py +56 -86
- orionis/foundation/config/logging/entities/chunked.py +9 -9
- orionis/foundation/config/logging/entities/daily.py +8 -8
- orionis/foundation/config/logging/entities/hourly.py +6 -6
- orionis/foundation/config/logging/entities/logging.py +12 -18
- orionis/foundation/config/logging/entities/monthly.py +7 -7
- orionis/foundation/config/logging/entities/stack.py +5 -5
- orionis/foundation/config/logging/entities/weekly.py +6 -6
- orionis/foundation/config/mail/entities/file.py +9 -36
- orionis/foundation/config/mail/entities/mail.py +22 -40
- orionis/foundation/config/mail/entities/mailers.py +29 -44
- orionis/foundation/config/mail/entities/smtp.py +47 -48
- orionis/foundation/config/queue/entities/brokers.py +19 -41
- orionis/foundation/config/queue/entities/database.py +24 -46
- orionis/foundation/config/queue/entities/queue.py +28 -40
- orionis/foundation/config/roots/paths.py +272 -468
- orionis/foundation/config/session/entities/session.py +23 -53
- orionis/foundation/config/startup.py +165 -135
- orionis/foundation/config/testing/entities/testing.py +137 -122
- orionis/foundation/config/testing/enums/__init__.py +6 -2
- orionis/foundation/config/testing/enums/drivers.py +16 -0
- orionis/foundation/config/testing/enums/verbosity.py +18 -0
- orionis/foundation/contracts/application.py +152 -362
- orionis/foundation/providers/console_provider.py +24 -2
- orionis/foundation/providers/dumper_provider.py +24 -2
- orionis/foundation/providers/logger_provider.py +24 -2
- orionis/foundation/providers/path_resolver_provider.py +25 -2
- orionis/foundation/providers/progress_bar_provider.py +24 -2
- orionis/foundation/providers/testing_provider.py +39 -0
- orionis/foundation/providers/workers_provider.py +24 -2
- orionis/metadata/framework.py +1 -1
- orionis/services/environment/helpers/functions.py +1 -2
- orionis/services/environment/key/__init__.py +0 -0
- orionis/services/environment/key/key_generator.py +37 -0
- orionis/support/entities/__init__.py +0 -0
- orionis/support/entities/base.py +104 -0
- orionis/support/facades/testing.py +15 -0
- orionis/support/facades/workers.py +1 -1
- orionis/test/cases/asynchronous.py +0 -11
- orionis/test/cases/synchronous.py +0 -9
- orionis/test/contracts/dumper.py +11 -4
- orionis/test/contracts/kernel.py +5 -110
- orionis/test/contracts/logs.py +27 -65
- orionis/test/contracts/printer.py +16 -128
- orionis/test/contracts/test_result.py +100 -0
- orionis/test/contracts/unit_test.py +87 -150
- orionis/test/core/unit_test.py +608 -554
- orionis/test/entities/result.py +22 -2
- orionis/test/enums/__init__.py +0 -2
- orionis/test/enums/status.py +14 -9
- orionis/test/exceptions/config.py +9 -1
- orionis/test/exceptions/failure.py +34 -11
- orionis/test/exceptions/persistence.py +10 -2
- orionis/test/exceptions/runtime.py +9 -1
- orionis/test/exceptions/value.py +13 -1
- orionis/test/kernel.py +87 -289
- orionis/test/output/dumper.py +82 -18
- orionis/test/output/printer.py +399 -156
- orionis/test/records/logs.py +203 -82
- orionis/test/validators/__init__.py +33 -0
- orionis/test/validators/base_path.py +45 -0
- orionis/test/validators/execution_mode.py +45 -0
- orionis/test/validators/fail_fast.py +37 -0
- orionis/test/validators/folder_path.py +34 -0
- orionis/test/validators/module_name.py +31 -0
- orionis/test/validators/name_pattern.py +40 -0
- orionis/test/validators/pattern.py +36 -0
- orionis/test/validators/persistent.py +42 -0
- orionis/test/validators/persistent_driver.py +43 -0
- orionis/test/validators/print_result.py +37 -0
- orionis/test/validators/tags.py +37 -0
- orionis/test/validators/throw_exception.py +39 -0
- orionis/test/validators/verbosity.py +37 -0
- orionis/test/validators/web_report.py +35 -0
- orionis/test/validators/workers.py +31 -0
- orionis/test/view/render.py +48 -54
- {orionis-0.405.0.dist-info → orionis-0.406.0.dist-info}/METADATA +1 -1
- {orionis-0.405.0.dist-info → orionis-0.406.0.dist-info}/RECORD +149 -98
- tests/container/__init__.py +0 -0
- tests/container/context/__init__.py +0 -0
- tests/container/context/test_manager.py +27 -0
- tests/container/context/test_scope.py +23 -0
- tests/container/entities/__init__.py +0 -0
- tests/container/entities/test_binding.py +133 -0
- tests/container/enums/__init__.py +0 -0
- tests/container/enums/test_lifetimes.py +63 -0
- tests/container/facades/__init__.py +0 -0
- tests/container/facades/test_facade.py +61 -0
- tests/container/mocks/__init__.py +0 -0
- tests/container/mocks/mock_complex_classes.py +482 -0
- tests/container/mocks/mock_simple_classes.py +32 -0
- tests/container/providers/__init__.py +0 -0
- tests/container/providers/test_providers.py +48 -0
- tests/container/resolver/__init__.py +0 -0
- tests/container/resolver/test_resolver.py +55 -0
- tests/container/test_container.py +254 -0
- tests/container/test_singleton.py +98 -0
- tests/container/test_thread_safety.py +217 -0
- tests/container/validators/__init__.py +0 -0
- tests/container/validators/test_implements.py +140 -0
- tests/container/validators/test_is_abstract_class.py +99 -0
- tests/container/validators/test_is_callable.py +73 -0
- tests/container/validators/test_is_concrete_class.py +97 -0
- tests/container/validators/test_is_instance.py +105 -0
- tests/container/validators/test_is_not_subclass.py +117 -0
- tests/container/validators/test_is_subclass.py +115 -0
- tests/container/validators/test_is_valid_alias.py +113 -0
- tests/container/validators/test_lifetime.py +75 -0
- tests/foundation/config/testing/test_foundation_config_testing.py +1 -1
- tests/metadata/test_metadata_framework.py +18 -18
- tests/testing/test_testing_result.py +117 -117
- tests/testing/test_testing_unit.py +209 -209
- orionis/foundation/config/base.py +0 -112
- orionis/test/arguments/parser.py +0 -187
- orionis/test/contracts/parser.py +0 -43
- orionis/test/entities/arguments.py +0 -38
- orionis/test/enums/execution_mode.py +0 -16
- /orionis/{test/arguments → console/base/contracts}/__init__.py +0 -0
- /orionis/foundation/config/testing/enums/{test_mode.py → mode.py} +0 -0
- {orionis-0.405.0.dist-info → orionis-0.406.0.dist-info}/WHEEL +0 -0
- {orionis-0.405.0.dist-info → orionis-0.406.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.405.0.dist-info → orionis-0.406.0.dist-info}/top_level.txt +0 -0
- {orionis-0.405.0.dist-info → orionis-0.406.0.dist-info}/zip-safe +0 -0
orionis/test/kernel.py
CHANGED
|
@@ -1,347 +1,145 @@
|
|
|
1
|
-
import
|
|
2
|
-
import os
|
|
1
|
+
from pathlib import Path
|
|
3
2
|
import re
|
|
3
|
+
from typing import List
|
|
4
4
|
from os import walk
|
|
5
|
-
import
|
|
6
|
-
from orionis.foundation.config.testing.entities.testing import Testing as Configuration
|
|
5
|
+
from orionis.foundation.config.testing.entities.testing import Testing
|
|
7
6
|
from orionis.foundation.contracts.application import IApplication
|
|
8
7
|
from orionis.test.contracts.kernel import ITestKernel
|
|
9
|
-
from orionis.test.
|
|
10
|
-
from orionis.test.core.unit_test import UnitTest
|
|
11
|
-
from orionis.test.entities.arguments import TestArguments
|
|
12
|
-
from orionis.test.enums.execution_mode import ExecutionMode
|
|
8
|
+
from orionis.test.contracts.unit_test import IUnitTest
|
|
13
9
|
from orionis.test.exceptions import OrionisTestConfigException
|
|
14
10
|
|
|
15
11
|
class TestKernel(ITestKernel):
|
|
16
|
-
"""
|
|
17
|
-
Core test kernel for the Orionis testing framework.
|
|
18
|
-
|
|
19
|
-
This class provides the main interface for discovering, configuring, and executing
|
|
20
|
-
test suites within the Orionis framework. It handles test configuration validation,
|
|
21
|
-
test discovery across multiple directories, and orchestrates the execution of
|
|
22
|
-
discovered tests.
|
|
23
|
-
|
|
24
|
-
Parameters
|
|
25
|
-
----------
|
|
26
|
-
app : IApplication
|
|
27
|
-
The Orionis application instance that provides the testing context.
|
|
28
|
-
|
|
29
|
-
Attributes
|
|
30
|
-
----------
|
|
31
|
-
__app : IApplication
|
|
32
|
-
Private reference to the application instance.
|
|
33
|
-
__config : Configuration
|
|
34
|
-
Private reference to the testing configuration.
|
|
35
|
-
"""
|
|
36
12
|
|
|
37
13
|
def __init__(
|
|
38
14
|
self,
|
|
39
15
|
app: IApplication
|
|
40
16
|
) -> None:
|
|
41
17
|
"""
|
|
42
|
-
Initialize the
|
|
18
|
+
Initialize the TestKernel with the provided application instance.
|
|
43
19
|
|
|
44
20
|
Parameters
|
|
45
21
|
----------
|
|
46
22
|
app : IApplication
|
|
47
|
-
The application instance
|
|
48
|
-
This provides the context and services needed for test execution.
|
|
23
|
+
The application instance implementing the IApplication contract.
|
|
49
24
|
|
|
50
25
|
Raises
|
|
51
26
|
------
|
|
52
|
-
|
|
53
|
-
If the provided app is
|
|
27
|
+
OrionisTestConfigException
|
|
28
|
+
If the provided app is not an instance of IApplication.
|
|
54
29
|
"""
|
|
55
|
-
# Validate application instance
|
|
56
|
-
if app is None or not isinstance(app, IApplication):
|
|
57
|
-
raise ValueError("The provided application is not a valid instance of IApplication.")
|
|
58
30
|
|
|
59
|
-
#
|
|
60
|
-
|
|
31
|
+
# Validate that the app is an instance of IApplication
|
|
32
|
+
if not isinstance(app, IApplication):
|
|
33
|
+
raise OrionisTestConfigException(
|
|
34
|
+
f"Failed to initialize TestKernel: expected IApplication, got {type(app).__module__}.{type(app).__name__}."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Load testing configuration from the application
|
|
38
|
+
self.__config = Testing(**app.config('testing'))
|
|
61
39
|
|
|
62
|
-
|
|
40
|
+
# Create and configure the unit test instance
|
|
41
|
+
self.__unit_test: IUnitTest = app.make('core.orionis.testing')
|
|
42
|
+
self.__unit_test._UnitTest__app = app
|
|
43
|
+
self.__unit_test._UnitTest__storage = app.path('storage_testing')
|
|
44
|
+
|
|
45
|
+
def __listMatchingFolders(
|
|
63
46
|
self,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
base_path: Path,
|
|
48
|
+
custom_path: Path,
|
|
49
|
+
pattern: str
|
|
50
|
+
) -> List[str]:
|
|
67
51
|
"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
This method validates the provided configuration or creates a new one from
|
|
71
|
-
keyword arguments. It ensures that the configuration is properly set up
|
|
72
|
-
before test execution begins.
|
|
52
|
+
List folders within a custom path whose files match a given pattern.
|
|
73
53
|
|
|
74
54
|
Parameters
|
|
75
55
|
----------
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
56
|
+
base_path : Path
|
|
57
|
+
The base directory path for relative calculation.
|
|
58
|
+
custom_path : Path
|
|
59
|
+
The custom directory path to search for matching files.
|
|
60
|
+
pattern : str
|
|
61
|
+
The filename pattern to match (supports '*' and '?').
|
|
82
62
|
|
|
83
63
|
Returns
|
|
84
64
|
-------
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Raises
|
|
89
|
-
------
|
|
90
|
-
OrionisTestConfigException
|
|
91
|
-
If the configuration is invalid or required fields are missing.
|
|
92
|
-
The exception message includes details about required fields and their types.
|
|
65
|
+
List[str]
|
|
66
|
+
List of relative folder paths containing files matching the pattern.
|
|
93
67
|
"""
|
|
94
|
-
# Check if config is None and kwargs are provided
|
|
95
|
-
if config is None:
|
|
96
68
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if not kwargs:
|
|
101
|
-
config = Configuration(**self.__app.config('testing'))
|
|
69
|
+
# Compile the pattern into a regex for matching file names
|
|
70
|
+
regex = re.compile('^' + pattern.replace('*', '.*').replace('?', '.') + '$')
|
|
71
|
+
matched_folders = set()
|
|
102
72
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
config = Configuration(**kwargs)
|
|
73
|
+
# Walk through the directory tree starting at custom_path
|
|
74
|
+
for root, _, files in walk(str(custom_path)):
|
|
106
75
|
|
|
107
|
-
|
|
76
|
+
# Check if any file in the current folder matches the pattern
|
|
77
|
+
if any(regex.fullmatch(file) for file in files):
|
|
108
78
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
required_fields.append(f"{field.get('name')} = (Type: {field.get('type')}, Default: {field.get('default')})")
|
|
79
|
+
# Calculate the relative path from base_path and add to results
|
|
80
|
+
rel_path = Path(root).relative_to(base_path).as_posix()
|
|
81
|
+
matched_folders.add(rel_path)
|
|
113
82
|
|
|
114
|
-
|
|
115
|
-
|
|
83
|
+
# Return the list of matching folder paths
|
|
84
|
+
return list(matched_folders)
|
|
116
85
|
|
|
117
|
-
|
|
118
|
-
return config or Configuration()
|
|
119
|
-
|
|
120
|
-
def handle(
|
|
121
|
-
self,
|
|
122
|
-
config: Configuration = None,
|
|
123
|
-
**kwargs
|
|
124
|
-
) -> UnitTest:
|
|
86
|
+
def handle(self) -> IUnitTest:
|
|
125
87
|
"""
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
This is the main entry point for running tests. It validates the configuration,
|
|
129
|
-
discovers test files based on specified patterns and paths, configures the
|
|
130
|
-
test suite, and executes all discovered tests.
|
|
131
|
-
|
|
132
|
-
Parameters
|
|
133
|
-
----------
|
|
134
|
-
config : Configuration, optional
|
|
135
|
-
A pre-configured Testing configuration instance. If None, attempts to
|
|
136
|
-
create one from kwargs.
|
|
137
|
-
**kwargs : dict
|
|
138
|
-
Keyword arguments to create a Configuration instance if config is None.
|
|
139
|
-
Common parameters include:
|
|
140
|
-
- base_path : str, base directory for test discovery
|
|
141
|
-
- folder_path : str or list, specific folders to search
|
|
142
|
-
- pattern : str, file pattern for test discovery
|
|
143
|
-
- verbosity : int, output verbosity level
|
|
144
|
-
- execution_mode : str, test execution mode
|
|
145
|
-
- max_workers : int, maximum number of worker threads
|
|
146
|
-
- fail_fast : bool, stop on first failure
|
|
88
|
+
Configure and execute the unit tests based on the current configuration.
|
|
147
89
|
|
|
148
90
|
Returns
|
|
149
91
|
-------
|
|
150
|
-
|
|
151
|
-
The configured and executed test
|
|
152
|
-
|
|
153
|
-
Raises
|
|
154
|
-
------
|
|
155
|
-
OrionisTestConfigException
|
|
156
|
-
If the configuration validation fails.
|
|
92
|
+
IUnitTest
|
|
93
|
+
The configured and executed unit test instance.
|
|
157
94
|
"""
|
|
158
|
-
# Validate and set configuration
|
|
159
|
-
config = self.__checkConfiguration(config, **kwargs)
|
|
160
|
-
|
|
161
|
-
# Initialize the test suite
|
|
162
|
-
tests = UnitTest()
|
|
163
|
-
|
|
164
|
-
# Assign the application instance to the test suite
|
|
165
|
-
tests.setApplication(self.__app)
|
|
166
95
|
|
|
167
|
-
# Configure the test
|
|
168
|
-
|
|
169
|
-
verbosity=
|
|
170
|
-
execution_mode=
|
|
171
|
-
max_workers=
|
|
172
|
-
fail_fast=
|
|
173
|
-
print_result=
|
|
174
|
-
throw_exception=
|
|
175
|
-
persistent=
|
|
176
|
-
persistent_driver=
|
|
177
|
-
web_report=
|
|
96
|
+
# Configure the unit test with parameters from the configuration
|
|
97
|
+
self.__unit_test.configure(
|
|
98
|
+
verbosity=self.__config.verbosity,
|
|
99
|
+
execution_mode=self.__config.execution_mode,
|
|
100
|
+
max_workers=self.__config.max_workers,
|
|
101
|
+
fail_fast=self.__config.fail_fast,
|
|
102
|
+
print_result=self.__config.print_result,
|
|
103
|
+
throw_exception=self.__config.throw_exception,
|
|
104
|
+
persistent=self.__config.persistent,
|
|
105
|
+
persistent_driver=self.__config.persistent_driver,
|
|
106
|
+
web_report=self.__config.web_report
|
|
178
107
|
)
|
|
179
108
|
|
|
180
|
-
#
|
|
181
|
-
base_path =
|
|
182
|
-
folder_path =
|
|
183
|
-
pattern =
|
|
109
|
+
# Prepare paths and pattern for test discovery
|
|
110
|
+
base_path = (Path.cwd() / self.__config.base_path).resolve()
|
|
111
|
+
folder_path = self.__config.folder_path
|
|
112
|
+
pattern = self.__config.pattern
|
|
184
113
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
Discover folders containing files that match the specified pattern.
|
|
114
|
+
# Set to hold discovered folders
|
|
115
|
+
discovered_folders = set()
|
|
188
116
|
|
|
189
|
-
|
|
190
|
-
custom_path and identifies folders that contain files matching the
|
|
191
|
-
given pattern.
|
|
117
|
+
# Discover folders containing test files according to the configuration
|
|
192
118
|
|
|
193
|
-
|
|
194
|
-
----------
|
|
195
|
-
custom_path : str
|
|
196
|
-
The root path to start the search from.
|
|
197
|
-
pattern : str
|
|
198
|
-
The file pattern to match (supports wildcards * and ?).
|
|
199
|
-
|
|
200
|
-
Returns
|
|
201
|
-
-------
|
|
202
|
-
list of str
|
|
203
|
-
List of relative folder paths containing matching files.
|
|
204
|
-
"""
|
|
205
|
-
matched_folders = []
|
|
206
|
-
for root, _, files in walk(custom_path):
|
|
207
|
-
for file in files:
|
|
208
|
-
if re.fullmatch(pattern.replace('*', '.*').replace('?', '.'), file):
|
|
209
|
-
relative_path = root.replace(base_path, '').replace('\\', '/').lstrip('/')
|
|
210
|
-
if relative_path not in matched_folders:
|
|
211
|
-
matched_folders.append(relative_path)
|
|
212
|
-
return matched_folders
|
|
213
|
-
|
|
214
|
-
# Discover test folders based on configuration
|
|
215
|
-
discovered_folders = []
|
|
119
|
+
# Search all folders under base_path
|
|
216
120
|
if folder_path == '*':
|
|
217
|
-
|
|
218
|
-
|
|
121
|
+
discovered_folders.update(self.__listMatchingFolders(base_path, base_path, pattern))
|
|
122
|
+
|
|
123
|
+
# Search each custom folder in the list
|
|
219
124
|
elif isinstance(folder_path, list):
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
discovered_folders.
|
|
125
|
+
for custom in folder_path:
|
|
126
|
+
custom_path = (base_path / custom).resolve()
|
|
127
|
+
discovered_folders.update(self.__listMatchingFolders(base_path, custom_path, pattern))
|
|
128
|
+
|
|
129
|
+
# Search a single custom folder
|
|
223
130
|
else:
|
|
224
|
-
|
|
225
|
-
discovered_folders.
|
|
131
|
+
custom_path = (base_path / folder_path).resolve()
|
|
132
|
+
discovered_folders.update(self.__listMatchingFolders(base_path, custom_path, pattern))
|
|
226
133
|
|
|
227
|
-
#
|
|
134
|
+
# Register discovered folders with the unit test for test discovery
|
|
228
135
|
for folder in discovered_folders:
|
|
229
|
-
|
|
136
|
+
self.__unit_test.discoverTestsInFolder(
|
|
230
137
|
folder_path=folder,
|
|
231
|
-
base_path=base_path,
|
|
138
|
+
base_path=self.__config.base_path,
|
|
232
139
|
pattern=pattern,
|
|
233
|
-
test_name_pattern=
|
|
234
|
-
tags=
|
|
140
|
+
test_name_pattern=self.__config.test_name_pattern or None,
|
|
141
|
+
tags=self.__config.tags or None
|
|
235
142
|
)
|
|
236
143
|
|
|
237
|
-
#
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# Return the test suite instance containing all results
|
|
241
|
-
return tests
|
|
242
|
-
|
|
243
|
-
def handleCLI(
|
|
244
|
-
self,
|
|
245
|
-
sys_argv: list[str],
|
|
246
|
-
) -> UnitTest:
|
|
247
|
-
|
|
248
|
-
"""
|
|
249
|
-
Process command line arguments for test execution.
|
|
250
|
-
This method configures and runs tests based on command line arguments. It extracts
|
|
251
|
-
configuration from the provided TestArguments object, executes the tests, and
|
|
252
|
-
handles output generation.
|
|
253
|
-
Parameters
|
|
254
|
-
----------
|
|
255
|
-
args : TestArguments
|
|
256
|
-
Command line arguments parsed into a TestArguments object.
|
|
257
|
-
base_path : str, optional
|
|
258
|
-
Base directory to search for test files, by default 'tests'.
|
|
259
|
-
folder_path : str, optional
|
|
260
|
-
Pattern for folder selection within base_path, by default '*'.
|
|
261
|
-
pattern : str, optional
|
|
262
|
-
Filename pattern for test files, by default 'test_*.py'.
|
|
263
|
-
Returns
|
|
264
|
-
-------
|
|
265
|
-
UnitTest
|
|
266
|
-
The test suite instance containing all test results.
|
|
267
|
-
Notes
|
|
268
|
-
-----
|
|
269
|
-
The method supports various test execution options including parallel/sequential
|
|
270
|
-
execution mode, fail fast behavior, and result output configuration.
|
|
271
|
-
"""
|
|
272
|
-
|
|
273
|
-
# Validate the provided arguments
|
|
274
|
-
if not isinstance(sys_argv, list):
|
|
275
|
-
raise OrionisTestConfigException("The provided sys_argv must be a list of command line arguments.")
|
|
276
|
-
|
|
277
|
-
# Assign the provided arguments to a TestArguments instance
|
|
278
|
-
parser = TestArgumentParser()
|
|
279
|
-
args:TestArguments = parser.parse(sys_argv)
|
|
280
|
-
|
|
281
|
-
# Extract and validate the configuration from command line arguments
|
|
282
|
-
test = self.handle(
|
|
283
|
-
verbosity = int(args.verbosity),
|
|
284
|
-
execution_mode = ExecutionMode.PARALLEL if args.mode == 'parallel' else ExecutionMode.SEQUENTIAL,
|
|
285
|
-
fail_fast = bool(args.fail_fast),
|
|
286
|
-
print_result = bool(args.print_result),
|
|
287
|
-
throw_exception = bool(args.throw_exception),
|
|
288
|
-
persistent = bool(args.persistent),
|
|
289
|
-
persistent_driver = str(args.persistent_driver) if args.persistent_driver else None,
|
|
290
|
-
web_report = bool(args.web_report)
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
# If requested, print the output buffer
|
|
294
|
-
if args.print_output_buffer:
|
|
295
|
-
test.printOutputBuffer()
|
|
296
|
-
|
|
297
|
-
# Return the test suite instance containing all results
|
|
298
|
-
return test
|
|
299
|
-
|
|
300
|
-
def exit(
|
|
301
|
-
self,
|
|
302
|
-
code: int = 0
|
|
303
|
-
) -> None:
|
|
304
|
-
"""
|
|
305
|
-
Terminate the test execution process and free associated resources.
|
|
306
|
-
|
|
307
|
-
This method performs a clean shutdown of the test kernel by explicitly
|
|
308
|
-
triggering garbage collection to release memory resources and then
|
|
309
|
-
terminating the process with the provided exit code. It ensures that any
|
|
310
|
-
remaining file handles, threads, or other resources are properly released.
|
|
311
|
-
|
|
312
|
-
Parameters
|
|
313
|
-
----------
|
|
314
|
-
code : int
|
|
315
|
-
The exit code to return to the operating system. Should be 0 for
|
|
316
|
-
successful execution or a non-zero value to indicate an error.
|
|
317
|
-
|
|
318
|
-
Returns
|
|
319
|
-
-------
|
|
320
|
-
None
|
|
321
|
-
This method does not return as it terminates the process.
|
|
322
|
-
|
|
323
|
-
Raises
|
|
324
|
-
------
|
|
325
|
-
ValueError
|
|
326
|
-
If the provided code is not a valid integer or outside the allowed range.
|
|
327
|
-
|
|
328
|
-
Notes
|
|
329
|
-
-----
|
|
330
|
-
Using os._exit() bypasses normal Python cleanup mechanisms and
|
|
331
|
-
immediately terminates the process. This can be necessary when
|
|
332
|
-
normal sys.exit() would be caught by exception handlers.
|
|
333
|
-
"""
|
|
334
|
-
# Validate the exit code
|
|
335
|
-
if not isinstance(code, int):
|
|
336
|
-
raise ValueError("Exit code must be an integer")
|
|
337
|
-
|
|
338
|
-
# Check if the code is within the allowed range (typically 0-255)
|
|
339
|
-
if code < 0 or code > 255:
|
|
340
|
-
raise ValueError("Exit code must be between 0 and 255")
|
|
341
|
-
|
|
342
|
-
# Force garbage collection to free memory
|
|
343
|
-
gc.collect()
|
|
344
|
-
|
|
345
|
-
# Terminate the process immediately without running cleanup handlers
|
|
346
|
-
sys.exit(code)
|
|
347
|
-
os._exit(code)
|
|
144
|
+
# Run the unit tests and return the result
|
|
145
|
+
return self.__unit_test.run()
|
orionis/test/output/dumper.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
from orionis.
|
|
3
|
+
from orionis.console.dumper.dump import Debug
|
|
4
|
+
from orionis.test.exceptions import OrionisTestRuntimeError
|
|
4
5
|
from orionis.test.contracts.dumper import ITestDumper
|
|
5
6
|
|
|
6
7
|
class TestDumper(ITestDumper):
|
|
@@ -29,102 +30,165 @@ class TestDumper(ITestDumper):
|
|
|
29
30
|
|
|
30
31
|
def __isTestCaseClass(self, value) -> bool:
|
|
31
32
|
"""
|
|
32
|
-
|
|
33
|
+
Determines whether the provided value is an instance of a recognized test case class.
|
|
34
|
+
|
|
35
|
+
This method checks if the given object is an instance of either AsyncTestCase or SyncTestCase,
|
|
36
|
+
which are the supported test case base classes in the Orionis testing framework.
|
|
33
37
|
|
|
34
38
|
Parameters
|
|
35
39
|
----------
|
|
36
40
|
value : object
|
|
37
|
-
The object to check.
|
|
41
|
+
The object to check for test case class membership.
|
|
38
42
|
|
|
39
43
|
Returns
|
|
40
44
|
-------
|
|
41
45
|
bool
|
|
42
|
-
True if `value` is an instance of AsyncTestCase
|
|
43
|
-
False
|
|
46
|
+
Returns True if `value` is an instance of AsyncTestCase or SyncTestCase.
|
|
47
|
+
Returns False if `value` is None, not an instance of these classes, or if any import error occurs.
|
|
44
48
|
"""
|
|
49
|
+
|
|
50
|
+
# If the value is None, it cannot be a test case instance.
|
|
51
|
+
if value is None:
|
|
52
|
+
return False
|
|
53
|
+
|
|
45
54
|
try:
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
|
|
56
|
+
# Attempt to import the test case base classes.
|
|
48
57
|
from orionis.test.cases.asynchronous import AsyncTestCase
|
|
49
58
|
from orionis.test.cases.synchronous import SyncTestCase
|
|
50
|
-
|
|
59
|
+
import unittest
|
|
60
|
+
|
|
61
|
+
# Check if the value is an instance of either Orionis or native unittest test case class.
|
|
62
|
+
return isinstance(
|
|
63
|
+
value,
|
|
64
|
+
(
|
|
65
|
+
AsyncTestCase,
|
|
66
|
+
SyncTestCase,
|
|
67
|
+
unittest.TestCase,
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
|
|
51
71
|
except Exception:
|
|
72
|
+
|
|
73
|
+
# If imports fail or any other exception occurs, return False.
|
|
52
74
|
return False
|
|
53
75
|
|
|
54
76
|
def dd(self, *args) -> None:
|
|
55
77
|
"""
|
|
56
|
-
|
|
78
|
+
Outputs debugging information using the Debug class and halts further execution.
|
|
57
79
|
|
|
58
|
-
This method captures the caller's file and line number
|
|
59
|
-
|
|
80
|
+
This method captures the caller's file and line number to provide context for the debug output.
|
|
81
|
+
It temporarily redirects standard output and error streams to ensure the debug information is
|
|
82
|
+
displayed correctly. If the first argument is a recognized test case instance, it is skipped
|
|
83
|
+
in the output to avoid redundant information. The method raises an exception if any error
|
|
84
|
+
occurs during the dumping process.
|
|
60
85
|
|
|
61
86
|
Parameters
|
|
62
87
|
----------
|
|
63
88
|
*args : tuple
|
|
64
|
-
Variable length argument list to be dumped.
|
|
89
|
+
Variable length argument list containing the objects to be dumped.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
None
|
|
94
|
+
This method does not return any value. It outputs debug information and may halt execution.
|
|
65
95
|
"""
|
|
96
|
+
|
|
97
|
+
# If no arguments are provided, exit the method early.
|
|
66
98
|
if not args:
|
|
67
99
|
return
|
|
68
100
|
|
|
101
|
+
# Save the original stdout and stderr to restore them later
|
|
69
102
|
original_stdout = sys.stdout
|
|
70
103
|
original_stderr = sys.stderr
|
|
71
104
|
|
|
72
105
|
try:
|
|
73
|
-
from orionis._console.dumper.dump_die import Debug
|
|
74
106
|
|
|
107
|
+
# Redirect stdout and stderr to the system defaults for proper debug output
|
|
75
108
|
sys.stdout = sys.__stdout__
|
|
76
109
|
sys.stderr = sys.__stderr__
|
|
77
110
|
|
|
111
|
+
# Retrieve the caller's frame to get file and line number context
|
|
78
112
|
caller_frame = sys._getframe(1)
|
|
79
113
|
_file = os.path.abspath(caller_frame.f_code.co_filename)
|
|
80
114
|
_line = caller_frame.f_lineno
|
|
81
115
|
|
|
116
|
+
# Initialize the Debug dumper with context information
|
|
82
117
|
dumper = Debug(f"{_file}:{_line}")
|
|
118
|
+
|
|
119
|
+
# If the first argument is a test case instance, skip it in the output
|
|
83
120
|
if self.__isTestCaseClass(args[0]):
|
|
84
121
|
dumper.dd(*args[1:])
|
|
85
122
|
else:
|
|
86
123
|
dumper.dd(*args)
|
|
124
|
+
|
|
87
125
|
except Exception as e:
|
|
126
|
+
|
|
127
|
+
# Raise a custom runtime error if dumping fails
|
|
88
128
|
raise OrionisTestRuntimeError(f"An error occurred while dumping debug information: {e}")
|
|
129
|
+
|
|
89
130
|
finally:
|
|
131
|
+
|
|
132
|
+
# Restore the original stdout and stderr
|
|
90
133
|
sys.stdout = original_stdout
|
|
91
134
|
sys.stderr = original_stderr
|
|
92
135
|
|
|
93
136
|
def dump(self, *args) -> None:
|
|
94
137
|
"""
|
|
95
|
-
|
|
138
|
+
Outputs debugging information using the Debug class.
|
|
96
139
|
|
|
97
|
-
This method captures the caller's file
|
|
98
|
-
|
|
140
|
+
This method captures the caller's file and line number to provide context for the debug output.
|
|
141
|
+
It temporarily redirects standard output and error streams to ensure the debug information is
|
|
142
|
+
displayed correctly. If the first argument is a recognized test case instance, it is skipped
|
|
143
|
+
in the output to avoid redundant information. The method raises an exception if any error
|
|
144
|
+
occurs during the dumping process.
|
|
99
145
|
|
|
100
146
|
Parameters
|
|
101
147
|
----------
|
|
102
148
|
*args : tuple
|
|
103
|
-
Variable length argument list to be dumped.
|
|
149
|
+
Variable length argument list containing the objects to be dumped.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
None
|
|
154
|
+
This method does not return any value. It outputs debug information.
|
|
104
155
|
"""
|
|
156
|
+
|
|
157
|
+
# If no arguments are provided, exit the method early.
|
|
105
158
|
if not args:
|
|
106
159
|
return
|
|
107
160
|
|
|
161
|
+
# Save the original stdout and stderr to restore them later
|
|
108
162
|
original_stdout = sys.stdout
|
|
109
163
|
original_stderr = sys.stderr
|
|
110
164
|
|
|
111
165
|
try:
|
|
112
|
-
from orionis._console.dumper.dump_die import Debug
|
|
113
166
|
|
|
167
|
+
# Redirect stdout and stderr to the system defaults for proper debug output
|
|
114
168
|
sys.stdout = sys.__stdout__
|
|
115
169
|
sys.stderr = sys.__stderr__
|
|
116
170
|
|
|
171
|
+
# Retrieve the caller's frame to get file and line number context
|
|
117
172
|
caller_frame = sys._getframe(1)
|
|
118
173
|
_file = os.path.abspath(caller_frame.f_code.co_filename)
|
|
119
174
|
_line = caller_frame.f_lineno
|
|
120
175
|
|
|
176
|
+
# Initialize the Debug dumper with context information
|
|
121
177
|
dumper = Debug(f"{_file}:{_line}")
|
|
178
|
+
|
|
179
|
+
# If the first argument is a test case instance, skip it in the output
|
|
122
180
|
if self.__isTestCaseClass(args[0]):
|
|
123
181
|
dumper.dump(*args[1:])
|
|
124
182
|
else:
|
|
125
183
|
dumper.dump(*args)
|
|
184
|
+
|
|
126
185
|
except Exception as e:
|
|
186
|
+
|
|
187
|
+
# Raise a custom runtime error if dumping fails
|
|
127
188
|
raise OrionisTestRuntimeError(f"An error occurred while dumping debug information: {e}")
|
|
189
|
+
|
|
128
190
|
finally:
|
|
191
|
+
|
|
192
|
+
# Restore the original stdout and stderr
|
|
129
193
|
sys.stdout = original_stdout
|
|
130
194
|
sys.stderr = original_stderr
|