orionis 0.200.0__py3-none-any.whl → 0.202.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/framework.py +1 -1
- orionis/luminate/container/container_integrity.py +56 -1
- orionis/luminate/facades/tests/tests_facade.py +1 -1
- orionis/luminate/support/inspection/container_integrity.py +292 -0
- orionis/luminate/support/inspection/functions.py +235 -0
- orionis/luminate/support/inspection/reflection.py +649 -0
- orionis/luminate/support/inspection/reflexion_abstract.py +23 -0
- orionis/luminate/support/inspection/reflexion_concrete.py +24 -0
- orionis/luminate/support/inspection/reflexion_concrete_with_abstract.py +31 -0
- orionis/luminate/support/inspection/reflexion_instance.py +364 -0
- orionis/luminate/support/inspection/reflexion_instance_with_abstract.py +309 -0
- orionis/luminate/support/inspection/reflexion_module.py +19 -0
- orionis/luminate/support/inspection/reflexion_module_with_classname.py +22 -0
- orionis/luminate/test/{exception.py → test_exception.py} +1 -2
- orionis/luminate/test/test_result.py +30 -0
- orionis/luminate/test/test_status.py +22 -0
- orionis/luminate/test/tests.py +67 -0
- orionis/luminate/test/unit_test.py +276 -56
- {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/METADATA +1 -1
- {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/RECORD +26 -14
- tests/main.py +0 -0
- tests/tools/class_example.py +0 -50
- tests/tools/test_reflection.py +0 -128
- {tests/tools → orionis/luminate/support/inspection}/__init__.py +0 -0
- {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/LICENCE +0 -0
- {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/WHEEL +0 -0
- {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/entry_points.txt +0 -0
- {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
class ReflexionModule:
|
2
|
+
"""A reflection object encapsulating a module.
|
3
|
+
|
4
|
+
Parameters
|
5
|
+
----------
|
6
|
+
module : str
|
7
|
+
The module name being reflected upon
|
8
|
+
|
9
|
+
Attributes
|
10
|
+
----------
|
11
|
+
_module : str
|
12
|
+
The encapsulated module name
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(self, module: str) -> None:
|
16
|
+
"""Initialize with the module name."""
|
17
|
+
self._module = module
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ReflexionModuleWithClassName:
|
2
|
+
"""A reflection object encapsulating a module and a class name.
|
3
|
+
|
4
|
+
Parameters
|
5
|
+
----------
|
6
|
+
module : str
|
7
|
+
The module name being reflected upon
|
8
|
+
class_name : str
|
9
|
+
The class name in the module
|
10
|
+
|
11
|
+
Attributes
|
12
|
+
----------
|
13
|
+
_module : str
|
14
|
+
The encapsulated module name
|
15
|
+
_class_name : str
|
16
|
+
The encapsulated class name
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self, module: str, class_name: str) -> None:
|
20
|
+
"""Initialize with the module name and class name."""
|
21
|
+
self._module = module
|
22
|
+
self._class_name = class_name
|
@@ -31,7 +31,6 @@ class OrionisTestFailureException(Exception):
|
|
31
31
|
response : str
|
32
32
|
The message describing the test failure.
|
33
33
|
"""
|
34
|
-
# Pass the response to the base Exception class
|
35
34
|
super().__init__(response)
|
36
35
|
|
37
36
|
def __str__(self) -> str:
|
@@ -43,4 +42,4 @@ class OrionisTestFailureException(Exception):
|
|
43
42
|
str
|
44
43
|
A formatted string containing the exception name and response message.
|
45
44
|
"""
|
46
|
-
return f"
|
45
|
+
return f"OrionisTestFailureException: {self.args[0]}"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import Optional
|
3
|
+
from orionis.luminate.test.test_status import TestStatus
|
4
|
+
|
5
|
+
@dataclass
|
6
|
+
class TestResult:
|
7
|
+
"""
|
8
|
+
Data class containing detailed information about a test result.
|
9
|
+
|
10
|
+
Attributes
|
11
|
+
----------
|
12
|
+
name : str
|
13
|
+
The name of the test.
|
14
|
+
status : TestStatus
|
15
|
+
The status of the test, indicating whether it passed, failed, or was skipped.
|
16
|
+
execution_time : float
|
17
|
+
The time taken to execute the test, in seconds.
|
18
|
+
error_message : str, optional
|
19
|
+
The error message if the test failed, by default None.
|
20
|
+
traceback : str, optional
|
21
|
+
The traceback information if the test failed, by default None.
|
22
|
+
file_path : str, optional
|
23
|
+
The file path where the test is located, by default None.
|
24
|
+
"""
|
25
|
+
name: str
|
26
|
+
status: TestStatus
|
27
|
+
execution_time: float
|
28
|
+
error_message: Optional[str] = None
|
29
|
+
traceback: Optional[str] = None
|
30
|
+
file_path: Optional[str] = None
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from enum import Enum, auto
|
2
|
+
|
3
|
+
class TestStatus(Enum):
|
4
|
+
"""
|
5
|
+
TestStatus(Enum)
|
6
|
+
Enum representing the possible statuses of a test.
|
7
|
+
|
8
|
+
Attributes
|
9
|
+
----------
|
10
|
+
PASSED : auto()
|
11
|
+
Indicates that the test was executed successfully without any issues.
|
12
|
+
FAILED : auto()
|
13
|
+
Indicates that the test was executed but did not meet the expected outcome.
|
14
|
+
ERRORED : auto()
|
15
|
+
Indicates that an error occurred during the execution of the test.
|
16
|
+
SKIPPED : auto()
|
17
|
+
Indicates that the test was intentionally skipped and not executed.
|
18
|
+
"""
|
19
|
+
PASSED = auto()
|
20
|
+
FAILED = auto()
|
21
|
+
ERRORED = auto()
|
22
|
+
SKIPPED = auto()
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from orionis.luminate.test.unit_test import UnitTest as UnitTestClass
|
2
|
+
|
3
|
+
class Tests:
|
4
|
+
"""
|
5
|
+
A class containing test utility methods.
|
6
|
+
|
7
|
+
Methods
|
8
|
+
-------
|
9
|
+
folders(folders: list) -> UnitTestClass
|
10
|
+
Validates folder configurations and initializes test suite.
|
11
|
+
"""
|
12
|
+
|
13
|
+
@staticmethod
|
14
|
+
def execute(folders: list, print_result:bool = True, throw_exception:bool = False):
|
15
|
+
"""
|
16
|
+
Validate folder configurations and initialize test suite.
|
17
|
+
|
18
|
+
Parameters
|
19
|
+
----------
|
20
|
+
folders : list
|
21
|
+
List of folder configuration dictionaries. Each dictionary must contain:
|
22
|
+
- folder_path : str
|
23
|
+
Path to the folder containing test files
|
24
|
+
- base_path : str
|
25
|
+
Base path for the tests
|
26
|
+
- pattern : str
|
27
|
+
File pattern to match test files
|
28
|
+
|
29
|
+
Returns
|
30
|
+
-------
|
31
|
+
UnitTestClass
|
32
|
+
Initialized test suite with added folders
|
33
|
+
|
34
|
+
Raises
|
35
|
+
------
|
36
|
+
TypeError
|
37
|
+
If folders is not a list or contains non-dictionary items
|
38
|
+
KeyError
|
39
|
+
If any folder dictionary is missing required keys
|
40
|
+
|
41
|
+
Examples
|
42
|
+
--------
|
43
|
+
>>> Tests.folders([
|
44
|
+
... {
|
45
|
+
... 'folder_path': 'example',
|
46
|
+
... 'base_path': 'tests',
|
47
|
+
... 'pattern': 'test_*.py'
|
48
|
+
... }
|
49
|
+
... ])
|
50
|
+
"""
|
51
|
+
if not isinstance(folders, list):
|
52
|
+
raise TypeError("folders must be a list")
|
53
|
+
|
54
|
+
for folder in folders:
|
55
|
+
if not isinstance(folder, dict):
|
56
|
+
raise TypeError("each folder must be a dictionary")
|
57
|
+
if not all(key in folder for key in ['folder_path', 'base_path', 'pattern']):
|
58
|
+
raise KeyError("each folder must contain 'folder_path', 'base_path' and 'pattern' keys")
|
59
|
+
|
60
|
+
tests = UnitTestClass()
|
61
|
+
for folder in folders:
|
62
|
+
tests.addFolder(
|
63
|
+
base_path=folder['base_path'],
|
64
|
+
folder_path=folder['folder_path'],
|
65
|
+
pattern=folder['pattern']
|
66
|
+
)
|
67
|
+
return tests.run(print_result, throw_exception)
|
@@ -1,107 +1,327 @@
|
|
1
1
|
import io
|
2
2
|
import re
|
3
|
+
import time
|
4
|
+
import traceback
|
3
5
|
import unittest
|
4
|
-
from contextlib import redirect_stdout
|
6
|
+
from contextlib import redirect_stdout, redirect_stderr
|
7
|
+
from dataclasses import asdict
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
5
9
|
from orionis.luminate.console.output.console import Console
|
10
|
+
from orionis.luminate.test.test_exception import OrionisTestFailureException
|
11
|
+
from orionis.luminate.test.test_result import TestResult
|
12
|
+
from orionis.luminate.test.test_status import TestStatus
|
6
13
|
|
7
14
|
class UnitTest:
|
8
15
|
"""
|
9
|
-
|
16
|
+
An advanced testing framework for discovering, running, and analyzing unit tests.
|
17
|
+
|
18
|
+
Features include:
|
19
|
+
- Detailed test discovery and filtering
|
20
|
+
- Comprehensive result reporting
|
21
|
+
- Performance timing
|
22
|
+
- Customizable output formatting
|
23
|
+
- Failure analysis
|
10
24
|
|
11
25
|
Attributes
|
12
26
|
----------
|
13
27
|
loader : unittest.TestLoader
|
14
|
-
|
28
|
+
Test loader instance for discovering tests
|
15
29
|
suite : unittest.TestSuite
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Adds test cases from a specified folder to the test suite.
|
22
|
-
run_tests() -> None
|
23
|
-
Executes all tests in the test suite and raises an exception if any fail.
|
30
|
+
Test suite holding discovered tests
|
31
|
+
test_results : List[TestResult]
|
32
|
+
Detailed results of executed tests
|
33
|
+
start_time : float
|
34
|
+
Timestamp when test execution began
|
24
35
|
"""
|
25
36
|
|
26
37
|
def __init__(self) -> None:
|
27
|
-
"""
|
28
|
-
Initializes the TestOrionisFramework class, setting up the test loader and suite.
|
29
|
-
"""
|
38
|
+
"""Initialize the testing framework."""
|
30
39
|
self.loader = unittest.TestLoader()
|
31
40
|
self.suite = unittest.TestSuite()
|
41
|
+
self.test_results: List[TestResult] = []
|
42
|
+
self.start_time: float = 0.0
|
32
43
|
|
33
|
-
def
|
44
|
+
def addFolder(
|
45
|
+
self,
|
46
|
+
folder_path: str,
|
47
|
+
base_path: str = "tests",
|
48
|
+
pattern: str = "test_*.py",
|
49
|
+
test_name_pattern: Optional[str] = None
|
50
|
+
) -> 'UnitTest':
|
34
51
|
"""
|
35
|
-
|
52
|
+
Discover and add tests from a specified folder to the test suite.
|
36
53
|
|
37
54
|
Parameters
|
38
55
|
----------
|
39
56
|
folder_path : str
|
40
|
-
|
57
|
+
Path to the folder containing test files
|
41
58
|
pattern : str, optional
|
42
|
-
|
59
|
+
Pattern to match test files (default 'test_*.py')
|
60
|
+
test_name_pattern : Optional[str], optional
|
61
|
+
Regex pattern to filter test names
|
43
62
|
|
44
63
|
Raises
|
45
64
|
------
|
46
65
|
ValueError
|
47
|
-
If the folder
|
66
|
+
If the folder is invalid or no tests are found
|
48
67
|
"""
|
49
|
-
self.loader.discover(f"tests", pattern=pattern)
|
50
|
-
|
51
68
|
try:
|
52
|
-
tests = self.loader.discover(
|
53
|
-
|
54
|
-
|
69
|
+
tests = self.loader.discover(
|
70
|
+
start_dir=f"{base_path}/{folder_path}",
|
71
|
+
pattern=pattern,
|
72
|
+
top_level_dir=None
|
73
|
+
)
|
74
|
+
|
75
|
+
if test_name_pattern:
|
76
|
+
tests = self._filter_tests_by_name(tests, test_name_pattern)
|
77
|
+
|
78
|
+
if not list(tests):
|
79
|
+
raise ValueError(f"No tests found in '{base_path}/{folder_path}' matching pattern '{pattern}'")
|
80
|
+
|
55
81
|
self.suite.addTests(tests)
|
82
|
+
|
83
|
+
return self
|
84
|
+
|
85
|
+
except ImportError as e:
|
86
|
+
raise ValueError(f"Error importing tests from '{base_path}/{folder_path}': {str(e)}")
|
56
87
|
except Exception as e:
|
57
|
-
raise ValueError(f"
|
88
|
+
raise ValueError(f"Unexpected error discovering tests: {str(e)}")
|
89
|
+
|
90
|
+
def _filter_tests_by_name(self, suite: unittest.TestSuite, pattern: str) -> unittest.TestSuite:
|
91
|
+
"""Filter tests based on a name pattern."""
|
92
|
+
filtered_suite = unittest.TestSuite()
|
93
|
+
regex = re.compile(pattern)
|
94
|
+
|
95
|
+
for test in self._flatten_test_suite(suite):
|
96
|
+
if regex.search(test.id()):
|
97
|
+
filtered_suite.addTest(test)
|
58
98
|
|
59
|
-
|
60
|
-
"""Extracts the file path from a traceback message."""
|
61
|
-
match = re.search(r'File "([^"]+)"', traceback)
|
62
|
-
return match.group(1) if match else None
|
99
|
+
return filtered_suite
|
63
100
|
|
64
|
-
def
|
101
|
+
def _flatten_test_suite(self, suite: unittest.TestSuite) -> List[unittest.TestCase]:
|
102
|
+
"""Flatten a test suite into a list of test cases."""
|
103
|
+
tests = []
|
104
|
+
for item in suite:
|
105
|
+
if isinstance(item, unittest.TestSuite):
|
106
|
+
tests.extend(self._flatten_test_suite(item))
|
107
|
+
else:
|
108
|
+
tests.append(item)
|
109
|
+
return tests
|
110
|
+
|
111
|
+
def _extract_error_info(self, traceback_str: str) -> Tuple[Optional[str], Optional[str]]:
|
112
|
+
"""
|
113
|
+
Extract file path and clean traceback from error output.
|
114
|
+
|
115
|
+
Parameters
|
116
|
+
----------
|
117
|
+
traceback_str : str
|
118
|
+
The full traceback string
|
119
|
+
|
120
|
+
Returns
|
121
|
+
-------
|
122
|
+
Tuple[Optional[str], Optional[str]]
|
123
|
+
(file_path, clean_traceback)
|
65
124
|
"""
|
66
|
-
|
125
|
+
file_match = re.search(r'File "([^"]+)"', traceback_str)
|
126
|
+
file_path = file_match.group(1) if file_match else None
|
127
|
+
|
128
|
+
# Clean up traceback by removing framework internals
|
129
|
+
tb_lines = traceback_str.split('\n')
|
130
|
+
clean_tb = '\n'.join(line for line in tb_lines if not any(s in line for s in ['unittest/', 'lib/python']))
|
131
|
+
|
132
|
+
return file_path, clean_tb
|
133
|
+
|
134
|
+
def run(self, print_result:bool = True, throw_exception:bool = False) -> Dict[str, Any]:
|
135
|
+
"""
|
136
|
+
Execute all tests in the test suite with comprehensive reporting.
|
137
|
+
|
138
|
+
Returns
|
139
|
+
-------
|
140
|
+
Dict[str, Any]
|
141
|
+
Detailed summary of test results including:
|
142
|
+
- total_tests
|
143
|
+
- passed
|
144
|
+
- failed
|
145
|
+
- errors
|
146
|
+
- skipped
|
147
|
+
- total_time
|
148
|
+
- test_details
|
67
149
|
|
68
150
|
Raises
|
69
151
|
------
|
70
152
|
OrionisTestFailureException
|
71
|
-
If
|
153
|
+
If any tests fail or error occurs
|
72
154
|
"""
|
73
|
-
|
74
|
-
|
75
|
-
|
155
|
+
self.start_time = time.time()
|
156
|
+
if print_result:
|
157
|
+
Console.newLine()
|
158
|
+
Console.info("🚀 Starting Test Execution...")
|
159
|
+
Console.newLine()
|
76
160
|
|
77
|
-
#
|
161
|
+
# Setup output capture
|
78
162
|
output_buffer = io.StringIO()
|
79
|
-
|
80
|
-
|
163
|
+
error_buffer = io.StringIO()
|
164
|
+
|
165
|
+
# Execute tests
|
166
|
+
with redirect_stdout(output_buffer), redirect_stderr(error_buffer):
|
167
|
+
runner = unittest.TextTestRunner(
|
168
|
+
stream=output_buffer,
|
169
|
+
verbosity=2,
|
170
|
+
resultclass=self._create_custom_result_class()
|
171
|
+
)
|
81
172
|
result = runner.run(self.suite)
|
82
173
|
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
174
|
+
# Process results
|
175
|
+
execution_time = time.time() - self.start_time
|
176
|
+
summary = self._generate_summary(result, execution_time)
|
177
|
+
|
178
|
+
# Print captured output
|
179
|
+
if print_result:
|
180
|
+
self._display_results(summary, result)
|
181
|
+
|
182
|
+
# if there are any errors or failures, raise an exception
|
183
|
+
if not result.wasSuccessful():
|
184
|
+
if throw_exception:
|
185
|
+
raise OrionisTestFailureException(
|
186
|
+
f"{summary['failed'] + summary['errors']} test(s) failed"
|
187
|
+
)
|
188
|
+
|
189
|
+
# Return summary of results
|
190
|
+
return summary
|
191
|
+
|
192
|
+
def _create_custom_result_class(self) -> type:
|
193
|
+
"""Create a custom TestResult class to capture detailed information."""
|
194
|
+
|
195
|
+
this = self
|
196
|
+
class OrionisTestResult(unittest.TextTestResult):
|
197
|
+
def __init__(self, *args, **kwargs):
|
198
|
+
super().__init__(*args, **kwargs)
|
199
|
+
self.test_results = []
|
200
|
+
|
201
|
+
def addSuccess(self, test):
|
202
|
+
super().addSuccess(test)
|
203
|
+
self.test_results.append(
|
204
|
+
TestResult(
|
205
|
+
name=str(test),
|
206
|
+
status=TestStatus.PASSED,
|
207
|
+
execution_time=0.0
|
208
|
+
)
|
209
|
+
)
|
210
|
+
|
211
|
+
def addFailure(self, test, err):
|
212
|
+
super().addFailure(test, err)
|
213
|
+
tb_str = ''.join(traceback.format_exception(*err))
|
214
|
+
file_path, clean_tb = this._extract_error_info(tb_str)
|
215
|
+
self.test_results.append(
|
216
|
+
TestResult(
|
217
|
+
name=str(test),
|
218
|
+
status=TestStatus.FAILED,
|
219
|
+
execution_time=0.0,
|
220
|
+
error_message=str(err[1]),
|
221
|
+
traceback=clean_tb,
|
222
|
+
file_path=file_path
|
223
|
+
)
|
224
|
+
)
|
225
|
+
|
226
|
+
def addError(self, test, err):
|
227
|
+
super().addError(test, err)
|
228
|
+
tb_str = ''.join(traceback.format_exception(*err))
|
229
|
+
file_path, clean_tb = this._extract_error_info(tb_str)
|
230
|
+
self.test_results.append(
|
231
|
+
TestResult(
|
232
|
+
name=str(test),
|
233
|
+
status=TestStatus.ERRORED,
|
234
|
+
execution_time=0.0,
|
235
|
+
error_message=str(err[1]),
|
236
|
+
traceback=clean_tb,
|
237
|
+
file_path=file_path
|
238
|
+
)
|
239
|
+
)
|
240
|
+
|
241
|
+
def addSkip(self, test, reason):
|
242
|
+
super().addSkip(test, reason)
|
243
|
+
self.test_results.append(
|
244
|
+
TestResult(
|
245
|
+
name=str(test),
|
246
|
+
status=TestStatus.SKIPPED,
|
247
|
+
execution_time=0.0,
|
248
|
+
error_message=reason
|
249
|
+
)
|
250
|
+
)
|
251
|
+
|
252
|
+
return OrionisTestResult
|
253
|
+
|
254
|
+
def _generate_summary(self, result: unittest.TestResult, execution_time: float) -> Dict[str, Any]:
|
255
|
+
"""Generate a comprehensive test summary."""
|
256
|
+
|
257
|
+
test_details = []
|
258
|
+
for test_result in result.test_results:
|
259
|
+
rst:dict = asdict(test_result)
|
260
|
+
test_details.append({
|
261
|
+
'name': rst.get('name'),
|
262
|
+
'status': rst.get('status').name,
|
263
|
+
'execution_time': float(rst.get('execution_time', 0)),
|
264
|
+
'error_message': rst.get('error_message', None),
|
265
|
+
'traceback': rst.get('traceback', None),
|
266
|
+
'file_path': rst.get('file_path', None)
|
267
|
+
})
|
268
|
+
|
269
|
+
return {
|
270
|
+
"total_tests": result.testsRun,
|
271
|
+
"passed": result.testsRun - len(result.failures) - len(result.errors) - len(result.skipped),
|
272
|
+
"failed": len(result.failures),
|
273
|
+
"errors": len(result.errors),
|
274
|
+
"skipped": len(result.skipped),
|
275
|
+
"total_time": f"{execution_time:.3f} seconds",
|
276
|
+
"success_rate": f"{((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100):.1f}%",
|
277
|
+
"test_details": test_details
|
88
278
|
}
|
89
|
-
|
279
|
+
|
280
|
+
def _display_results(self, summary: Dict[str, Any], result: unittest.TestResult) -> None:
|
281
|
+
"""Display test results in a formatted manner."""
|
282
|
+
# Summary table
|
283
|
+
Console.table(
|
284
|
+
headers=["Total", "Passed", "Failed", "Errors", "Skipped", "Duration", "Success Rate"],
|
285
|
+
rows=[[
|
286
|
+
summary["total_tests"],
|
287
|
+
summary["passed"],
|
288
|
+
summary["failed"],
|
289
|
+
summary["errors"],
|
290
|
+
summary["skipped"],
|
291
|
+
summary["total_time"],
|
292
|
+
summary["success_rate"]
|
293
|
+
]]
|
294
|
+
)
|
90
295
|
Console.newLine()
|
91
296
|
|
92
|
-
#
|
93
|
-
if result.failures:
|
94
|
-
|
95
|
-
|
297
|
+
# Detailed failure/error reporting
|
298
|
+
if result.failures or result.errors:
|
299
|
+
Console.textSuccessBold("Test Failures and Errors")
|
300
|
+
for test, traceback_str in result.failures + result.errors:
|
301
|
+
file_path, clean_tb = self._extract_error_info(traceback_str)
|
302
|
+
title = f"❌ {test.id()}" + (f" ({file_path})" if file_path else "")
|
96
303
|
Console.fail(title)
|
97
|
-
Console.write(
|
304
|
+
Console.write(clean_tb)
|
305
|
+
Console.newLine()
|
98
306
|
|
99
|
-
|
100
|
-
|
307
|
+
# Performance highlights
|
308
|
+
if len(self.test_results) > 10:
|
309
|
+
slow_tests = sorted(
|
310
|
+
[r for r in self.test_results if r.status == TestStatus.PASSED],
|
311
|
+
key=lambda x: x.execution_time,
|
312
|
+
reverse=True
|
313
|
+
)[:3]
|
314
|
+
if slow_tests:
|
315
|
+
Console.textSuccessBold("⏱️ Slowest Passing Tests")
|
316
|
+
for test in slow_tests:
|
317
|
+
Console.warning(f"{test.name}: {test.execution_time:.3f}s")
|
101
318
|
|
319
|
+
# Final status
|
320
|
+
if result.wasSuccessful():
|
321
|
+
Console.success("✅ All tests passed successfully!")
|
102
322
|
else:
|
103
|
-
Console.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
323
|
+
Console.error(
|
324
|
+
f"❌ {summary['failed'] + summary['errors']} test(s) failed "
|
325
|
+
f"(Success Rate: {summary['success_rate']})"
|
326
|
+
)
|
327
|
+
Console.newLine()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
orionis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
orionis/console.py,sha256=4gYWxf0fWYgJ4RKwARvnTPh06FL3GJ6SAZ7R2NzOICw,1342
|
3
|
-
orionis/framework.py,sha256=
|
3
|
+
orionis/framework.py,sha256=18MaKCn57mg1rxIum1RP9LmF_HV9qnuuhyrYS6ENti0,1469
|
4
4
|
orionis/installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
orionis/installer/manager.py,sha256=Li4TVziRXWfum02xNG4JHwbnLk-u8xzHjdqKz-D894k,2755
|
6
6
|
orionis/installer/output.py,sha256=7O9qa2xtXMB_4ZvVi-Klneom9YazwygAd_4uYAoxhbU,8548
|
@@ -45,7 +45,7 @@ orionis/luminate/console/output/progress_bar.py,sha256=ZiPGcUaN3EINeLRKgLGtS1GAb
|
|
45
45
|
orionis/luminate/console/output/styles.py,sha256=6a4oQCOBOKMh2ARdeq5GlIskJ3wjiylYmh66tUKKmpQ,4053
|
46
46
|
orionis/luminate/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
47
|
orionis/luminate/container/container.py,sha256=9xdODX1h4YK6V-THrfgm5XN95imobExzrb4bllizrWQ,18321
|
48
|
-
orionis/luminate/container/container_integrity.py,sha256=
|
48
|
+
orionis/luminate/container/container_integrity.py,sha256=6d9FsGk-Rm1AXgqBS3Nww49dR7n1ptXTTNyGUuBHgNY,10111
|
49
49
|
orionis/luminate/container/exception.py,sha256=ap1SqYEjQEEHXJJTNmL7V1jrmRjgT5_7geZ95MYkhMA,1691
|
50
50
|
orionis/luminate/container/lifetimes.py,sha256=2lbdiV7R2WlJf1cLD6eBxLnJud_lZvX1IhQH2Djy3Ww,375
|
51
51
|
orionis/luminate/container/resolve.py,sha256=Vg0VI3DGzZO6-D3AHX1BpEoiyC5hPDTR-NuiWkl7U_c,2306
|
@@ -122,7 +122,7 @@ orionis/luminate/facades/files/path_facade.py,sha256=z6DLW7IiBc6nonEwcIbylgpbrM9
|
|
122
122
|
orionis/luminate/facades/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
123
123
|
orionis/luminate/facades/log/log_facade.py,sha256=8WTLtCvfSdF9ve3lrc3GV0hXxNtolah8WJWfkMUQ_JI,699
|
124
124
|
orionis/luminate/facades/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
125
|
-
orionis/luminate/facades/tests/tests_facade.py,sha256=
|
125
|
+
orionis/luminate/facades/tests/tests_facade.py,sha256=TxtxYGLqT3Z199Kox8RL9hGbA4guZie855SJZ6DgfYQ,1662
|
126
126
|
orionis/luminate/foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
127
127
|
orionis/luminate/foundation/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
128
128
|
orionis/luminate/foundation/config/config_bootstrapper.py,sha256=Zdk3C-asIc0zizRI6r_V-e3mQQ1yuh39B8DHdspOxw0,7508
|
@@ -167,9 +167,23 @@ orionis/luminate/support/dot_dict.py,sha256=FVHfBuAGTTVMjNG01Fix645fRNKKUMmNx61p
|
|
167
167
|
orionis/luminate/support/exception_parse.py,sha256=S4KU0s-MCEbulPrf0N9tLZZIN3Sjn-tOKYtQCP0Xe0k,1655
|
168
168
|
orionis/luminate/support/reflection.py,sha256=TbWZ_cer0PXrPlwCYFbUJRymlzYxXT0E4C5XCsSc3mw,11951
|
169
169
|
orionis/luminate/support/std.py,sha256=TqrgMxF_i5ubYGT5LOvHCH7HOHNmI8CE1kG9pNoSniY,1390
|
170
|
+
orionis/luminate/support/inspection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
171
|
+
orionis/luminate/support/inspection/container_integrity.py,sha256=6d9FsGk-Rm1AXgqBS3Nww49dR7n1ptXTTNyGUuBHgNY,10111
|
172
|
+
orionis/luminate/support/inspection/functions.py,sha256=XVDLdygONcfDNlH7CZ6muNdtYHLgNp5RybqC3smL4Y4,6400
|
173
|
+
orionis/luminate/support/inspection/reflection.py,sha256=x_UHBY9pBSKgct13-u1WbhoONqLoGG60OpgoCsKE0AI,20368
|
174
|
+
orionis/luminate/support/inspection/reflexion_abstract.py,sha256=T3Zsi2KT2cS2-Upu0NDcW5FTrcytTLxlmBfZ_KAYA3Y,554
|
175
|
+
orionis/luminate/support/inspection/reflexion_concrete.py,sha256=0WOlLeTWLwMeAUReoaJetqlnT1_TxW_jMnbk_yXRR0g,549
|
176
|
+
orionis/luminate/support/inspection/reflexion_concrete_with_abstract.py,sha256=ZuAFoSPkgbOFMIbVR0hvlkKsm1dIpY1_bsXxZxvXcmU,801
|
177
|
+
orionis/luminate/support/inspection/reflexion_instance.py,sha256=ROmZ-hxYdnrCsYdzjZyH4KhDZxKEWkDM5ZTPg4BmGmc,9079
|
178
|
+
orionis/luminate/support/inspection/reflexion_instance_with_abstract.py,sha256=dag6QdVp1CydREWdpQvzAyOPX3q-lSb0_XzB0u3odQE,9629
|
179
|
+
orionis/luminate/support/inspection/reflexion_module.py,sha256=OgBXpqNJHkmq-gX4rqFStv-WVNe9R38RsgUgfHpak8k,405
|
180
|
+
orionis/luminate/support/inspection/reflexion_module_with_classname.py,sha256=YZHZI0XUZkSWnq9wrGxrIXtI64nY9yVSZoMe7PZXq8Y,620
|
170
181
|
orionis/luminate/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
171
|
-
orionis/luminate/test/
|
172
|
-
orionis/luminate/test/
|
182
|
+
orionis/luminate/test/test_exception.py,sha256=21PILTXnMuL5-wT3HGKjIklt8VeIYDcQDN346i-BbJw,1336
|
183
|
+
orionis/luminate/test/test_result.py,sha256=Px2_M70r_y7BntRITk_h0IPTbSTW5XhDyklMKHm3JJI,999
|
184
|
+
orionis/luminate/test/test_status.py,sha256=vNKRmp1lud_ZGTayf3A8wO_0vEYdFABy_oMw-RcEc1c,673
|
185
|
+
orionis/luminate/test/tests.py,sha256=KZRFHB6C8S2DN6JHqJ2sIuSNAUMh9JW1Yn-XGjXjjTw,2218
|
186
|
+
orionis/luminate/test/unit_test.py,sha256=HtPDWzFXpgFwWYej8z2BArU4k5lItH57K_E-l21MBWo,12070
|
173
187
|
orionis/static/ascii/icon.ascii,sha256=IgrlVjcYxcCrr0cJuJkOnEz0aEdAQBTyLzO5ainKsWc,398
|
174
188
|
orionis/static/ascii/info.ascii,sha256=HF_o2eXaiw5iqcOhHfnPByn5GJ_O2eBwSK3IpTfYOwY,457
|
175
189
|
orionis/static/bg/galaxy.jpg,sha256=_FuPghOe9LBrIWv1eKZ9fiZR72sEz5obvXGDnD7MzTc,172244
|
@@ -180,12 +194,10 @@ orionis/static/logos/OrionisFramework.psd,sha256=QFMRe_HENaIgQi9VWMvNV3OHKOFofFG
|
|
180
194
|
orionis/static/logos/OrionisFramework2.png,sha256=Z_-yBHNSo33QeSTyi-8GfiFozdRqUomIZ28bGx6Py5c,256425
|
181
195
|
orionis/static/logos/OrionisFramework3.png,sha256=BPG9ZB58vDALavI9OMmr8Ym0DQa44s5NL_3M4M6dIYs,193734
|
182
196
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
183
|
-
tests/
|
184
|
-
|
185
|
-
|
186
|
-
orionis-0.
|
187
|
-
orionis-0.
|
188
|
-
orionis-0.
|
189
|
-
orionis-0.
|
190
|
-
orionis-0.200.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
|
191
|
-
orionis-0.200.0.dist-info/RECORD,,
|
197
|
+
tests/main.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
198
|
+
orionis-0.202.0.dist-info/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
|
199
|
+
orionis-0.202.0.dist-info/METADATA,sha256=kiVvQVX5rkTnqeLGvor85Qu4hFAhiCJopnuaX0u1j4E,3003
|
200
|
+
orionis-0.202.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
201
|
+
orionis-0.202.0.dist-info/entry_points.txt,sha256=a_e0faeSqyUCVZd0MqljQ2oaHHdlsz6g9sU_bMqi5zQ,49
|
202
|
+
orionis-0.202.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
|
203
|
+
orionis-0.202.0.dist-info/RECORD,,
|
tests/main.py
ADDED
File without changes
|