orionis 0.293.0__py3-none-any.whl → 0.295.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/foundation/config/testing/entities/testing.py +20 -6
- orionis/metadata/framework.py +1 -1
- orionis/test/suites/test_suite.py +1 -0
- orionis/test/suites/test_unit.py +115 -22
- {orionis-0.293.0.dist-info → orionis-0.295.0.dist-info}/METADATA +1 -1
- {orionis-0.293.0.dist-info → orionis-0.295.0.dist-info}/RECORD +10 -10
- {orionis-0.293.0.dist-info → orionis-0.295.0.dist-info}/WHEEL +0 -0
- {orionis-0.293.0.dist-info → orionis-0.295.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.293.0.dist-info → orionis-0.295.0.dist-info}/top_level.txt +0 -0
- {orionis-0.293.0.dist-info → orionis-0.295.0.dist-info}/zip-safe +0 -0
@@ -145,6 +145,15 @@ class Testing:
|
|
145
145
|
}
|
146
146
|
)
|
147
147
|
|
148
|
+
web_report: bool = field(
|
149
|
+
default=False,
|
150
|
+
metadata={
|
151
|
+
"description": "Whether to generate a web report for the test results. Default is False.",
|
152
|
+
"required": True,
|
153
|
+
"default": False
|
154
|
+
}
|
155
|
+
)
|
156
|
+
|
148
157
|
def __post_init__(self):
|
149
158
|
"""
|
150
159
|
Post-initialization validation for the testing configuration entity.
|
@@ -245,14 +254,19 @@ class Testing:
|
|
245
254
|
f"Invalid type for 'persistent': {type(self.persistent).__name__}. It must be a boolean (True or False)."
|
246
255
|
)
|
247
256
|
|
248
|
-
if
|
249
|
-
|
250
|
-
|
251
|
-
|
257
|
+
if self.persistent:
|
258
|
+
if not isinstance(self.persistent_driver, str):
|
259
|
+
raise OrionisIntegrityException(
|
260
|
+
f"Invalid type for 'persistent_driver': {type(self.persistent_driver).__name__}. It must be a string."
|
261
|
+
)
|
262
|
+
if self.persistent_driver not in ['sqlite', 'json']:
|
263
|
+
raise OrionisIntegrityException(
|
264
|
+
f"Invalid value for 'persistent_driver': {self.persistent_driver}. It must be one of: ['sqlite', 'json']."
|
265
|
+
)
|
252
266
|
|
253
|
-
if self.
|
267
|
+
if not isinstance(self.web_report, bool):
|
254
268
|
raise OrionisIntegrityException(
|
255
|
-
f"Invalid
|
269
|
+
f"Invalid type for 'web_report': {type(self.web_report).__name__}. It must be a boolean (True or False)."
|
256
270
|
)
|
257
271
|
|
258
272
|
def toDict(self) -> dict:
|
orionis/metadata/framework.py
CHANGED
orionis/test/suites/test_unit.py
CHANGED
@@ -16,17 +16,17 @@ from rich.live import Live
|
|
16
16
|
from rich.panel import Panel
|
17
17
|
from rich.syntax import Syntax
|
18
18
|
from rich.table import Table
|
19
|
+
from rich.text import Text
|
19
20
|
from orionis.console.output.console import Console
|
20
21
|
from orionis.test.entities.test_result import TestResult
|
21
22
|
from orionis.test.enums.test_mode import ExecutionMode
|
22
23
|
from orionis.test.enums.test_status import TestStatus
|
23
|
-
from orionis.test.exceptions.test_persistence_error import OrionisTestPersistenceError
|
24
24
|
from orionis.test.exceptions.test_failure_exception import OrionisTestFailureException
|
25
|
+
from orionis.test.exceptions.test_persistence_error import OrionisTestPersistenceError
|
25
26
|
from orionis.test.exceptions.test_value_error import OrionisTestValueError
|
26
27
|
from orionis.test.logs.history import TestHistory
|
27
28
|
from orionis.test.suites.contracts.test_unit import IUnitTest
|
28
29
|
from orionis.test.view.render import TestingResultRender
|
29
|
-
from rich.text import Text
|
30
30
|
|
31
31
|
class UnitTest(IUnitTest):
|
32
32
|
"""
|
@@ -131,7 +131,9 @@ class UnitTest(IUnitTest):
|
|
131
131
|
self.throw_exception: bool = False
|
132
132
|
self.persistent: bool = False
|
133
133
|
self.persistent_driver: str = 'sqlite'
|
134
|
+
self.web_report: bool = False
|
134
135
|
self.base_path: str = "tests"
|
136
|
+
self.withliveconsole: bool = True
|
135
137
|
|
136
138
|
def configure(
|
137
139
|
self,
|
@@ -142,7 +144,8 @@ class UnitTest(IUnitTest):
|
|
142
144
|
print_result: bool = None,
|
143
145
|
throw_exception: bool = False,
|
144
146
|
persistent: bool = False,
|
145
|
-
persistent_driver: str = 'sqlite'
|
147
|
+
persistent_driver: str = 'sqlite',
|
148
|
+
web_report: bool = False
|
146
149
|
) -> 'UnitTest':
|
147
150
|
"""
|
148
151
|
Configures the UnitTest instance with the specified parameters.
|
@@ -197,6 +200,9 @@ class UnitTest(IUnitTest):
|
|
197
200
|
if persistent_driver is not None:
|
198
201
|
self.persistent_driver = persistent_driver
|
199
202
|
|
203
|
+
if web_report is not None:
|
204
|
+
self.web_report = web_report
|
205
|
+
|
200
206
|
return self
|
201
207
|
|
202
208
|
def discoverTestsInFolder(
|
@@ -387,17 +393,27 @@ class UnitTest(IUnitTest):
|
|
387
393
|
OrionisTestFailureException
|
388
394
|
If `throw_exception` is True and there are test failures or errors.
|
389
395
|
"""
|
396
|
+
|
397
|
+
# Check if required print_result and throw_exception
|
390
398
|
if print_result is not None:
|
391
399
|
self.print_result = print_result
|
392
400
|
if throw_exception is not None:
|
393
401
|
self.throw_exception = throw_exception
|
394
402
|
|
403
|
+
# Dynamically determine if live console should be enabled based on test code usage
|
404
|
+
self._withLiveConsole()
|
405
|
+
|
406
|
+
# Start the timer and print the start message
|
395
407
|
self.start_time = time.time()
|
396
408
|
self._startMessage()
|
397
409
|
|
398
|
-
#
|
410
|
+
# Prepare the running message based on whether live console is enabled
|
411
|
+
message = "[bold yellow]⏳ Running tests...[/bold yellow]\n"
|
412
|
+
message += "[dim]This may take a few seconds. Please wait...[/dim]" if self.withliveconsole else "[dim]Please wait, results will appear below...[/dim]"
|
413
|
+
|
414
|
+
# Panel for running message
|
399
415
|
running_panel = Panel(
|
400
|
-
|
416
|
+
message,
|
401
417
|
border_style="yellow",
|
402
418
|
title="In Progress",
|
403
419
|
title_align="left",
|
@@ -405,18 +421,18 @@ class UnitTest(IUnitTest):
|
|
405
421
|
padding=(1, 2)
|
406
422
|
)
|
407
423
|
|
408
|
-
#
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
424
|
+
# Elegant "running" message using Rich Panel
|
425
|
+
if self.withliveconsole:
|
426
|
+
with Live(running_panel, console=self.rich_console, refresh_per_second=4, transient=True):
|
427
|
+
result, output_buffer, error_buffer = self._runSuite()
|
428
|
+
else:
|
429
|
+
self.rich_console.print(running_panel)
|
430
|
+
result, output_buffer, error_buffer = self._runSuite()
|
414
431
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
result = self._runTestsSequentially(output_buffer, error_buffer)
|
432
|
+
# Capture and display the output and error buffers only if not empty
|
433
|
+
output_content = output_buffer.getvalue()
|
434
|
+
if output_content.strip():
|
435
|
+
print(output_buffer.getvalue())
|
420
436
|
|
421
437
|
# Process results
|
422
438
|
execution_time = time.time() - self.start_time
|
@@ -433,6 +449,80 @@ class UnitTest(IUnitTest):
|
|
433
449
|
# Return the summary of the test results
|
434
450
|
return summary
|
435
451
|
|
452
|
+
def _withLiveConsole(self) -> None:
|
453
|
+
"""
|
454
|
+
Determines if the live console should be used based on the presence of debug or dump calls in the test code.
|
455
|
+
|
456
|
+
Returns
|
457
|
+
-------
|
458
|
+
bool
|
459
|
+
True if the live console should be used, False otherwise.
|
460
|
+
"""
|
461
|
+
if self.withliveconsole:
|
462
|
+
|
463
|
+
try:
|
464
|
+
|
465
|
+
# Flatten the test suite to get all test cases
|
466
|
+
for test_case in self._flattenTestSuite(self.suite):
|
467
|
+
|
468
|
+
# Get the source code of the test case class
|
469
|
+
source = inspect.getsource(test_case.__class__)
|
470
|
+
|
471
|
+
# Only match if the keyword is not inside a comment
|
472
|
+
for keyword in ('self.dd', 'self.dump'):
|
473
|
+
|
474
|
+
# Find all lines containing the keyword
|
475
|
+
for line in source.splitlines():
|
476
|
+
if keyword in line:
|
477
|
+
|
478
|
+
# Remove leading/trailing whitespace
|
479
|
+
stripped = line.strip()
|
480
|
+
|
481
|
+
# Ignore lines that start with '#' (comments)
|
482
|
+
if not stripped.startswith('#') and not re.match(r'^\s*#', line):
|
483
|
+
self.withliveconsole = False
|
484
|
+
break
|
485
|
+
|
486
|
+
# If we found a keyword, no need to check further
|
487
|
+
if not self.withliveconsole:
|
488
|
+
break
|
489
|
+
|
490
|
+
# If we found a keyword in any test case, no need to check further
|
491
|
+
if not self.withliveconsole:
|
492
|
+
break
|
493
|
+
|
494
|
+
except Exception:
|
495
|
+
pass
|
496
|
+
|
497
|
+
def _runSuite(self):
|
498
|
+
"""
|
499
|
+
Run the test suite according to the selected execution mode (parallel or sequential),
|
500
|
+
capturing standard output and error streams during execution.
|
501
|
+
|
502
|
+
Returns
|
503
|
+
-------
|
504
|
+
tuple
|
505
|
+
result : unittest.TestResult
|
506
|
+
The result object from the test execution.
|
507
|
+
output_buffer : io.StringIO
|
508
|
+
Captured standard output during test execution.
|
509
|
+
error_buffer : io.StringIO
|
510
|
+
Captured standard error during test execution.
|
511
|
+
"""
|
512
|
+
|
513
|
+
# Setup output capture
|
514
|
+
output_buffer = io.StringIO()
|
515
|
+
error_buffer = io.StringIO()
|
516
|
+
|
517
|
+
# Execute tests based on selected mode
|
518
|
+
if self.execution_mode == ExecutionMode.PARALLEL.value:
|
519
|
+
result = self._runTestsInParallel(output_buffer, error_buffer)
|
520
|
+
else:
|
521
|
+
result = self._runTestsSequentially(output_buffer, error_buffer)
|
522
|
+
|
523
|
+
# Return the result along with captured output and error streams
|
524
|
+
return result, output_buffer, error_buffer
|
525
|
+
|
436
526
|
def _runTestsSequentially(self, output_buffer: io.StringIO, error_buffer: io.StringIO) -> unittest.TestResult:
|
437
527
|
"""
|
438
528
|
Executes the test suite sequentially, capturing the output and error streams.
|
@@ -760,13 +850,16 @@ class UnitTest(IUnitTest):
|
|
760
850
|
self._persistTestResults(report)
|
761
851
|
|
762
852
|
# Handle Web Report Rendering
|
763
|
-
|
853
|
+
if self.web_report:
|
854
|
+
|
855
|
+
# Generate the web report and get the path
|
856
|
+
path = self._webReport(report)
|
764
857
|
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
858
|
+
# Elegant invitation to view the results, with underlined path
|
859
|
+
invite_text = Text("Test results saved. ", style="green")
|
860
|
+
invite_text.append("View report: ", style="bold green")
|
861
|
+
invite_text.append(str(path), style="underline blue")
|
862
|
+
self.rich_console.print(invite_text)
|
770
863
|
|
771
864
|
# Return the summary
|
772
865
|
return {
|
@@ -222,11 +222,11 @@ orionis/foundation/config/session/enums/same_site_policy.py,sha256=Oo05CJ-5keJWz
|
|
222
222
|
orionis/foundation/config/session/helpers/secret_key.py,sha256=yafjzQ9KVQdXzCQCMthpgizlNCo5F5UTLtAnInipUMk,447
|
223
223
|
orionis/foundation/config/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
224
224
|
orionis/foundation/config/testing/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
225
|
-
orionis/foundation/config/testing/entities/testing.py,sha256=
|
225
|
+
orionis/foundation/config/testing/entities/testing.py,sha256=AuhPU9O15Aeqs8jQVHWJwamgrrcvmC4ThsJ31jyrWic,11849
|
226
226
|
orionis/foundation/config/testing/enums/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
227
227
|
orionis/foundation/config/testing/enums/test_mode.py,sha256=IbFpauu7J-iSAfmC8jDbmTEYl8eZr-AexL-lyOh8_74,337
|
228
228
|
orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
229
|
-
orionis/metadata/framework.py,sha256=
|
229
|
+
orionis/metadata/framework.py,sha256=klmDRFqvLc8HnxhUeqOIpWsQhC1a4vZm3oDq2LUAxQs,4960
|
230
230
|
orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
|
231
231
|
orionis/patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
232
232
|
orionis/patterns/singleton/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -344,14 +344,14 @@ orionis/test/output/dumper.py,sha256=y-6du3n1IU2Cd2MFbMuEiLcpMqEOqENkuAXwMMhcsEI
|
|
344
344
|
orionis/test/output/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
345
345
|
orionis/test/output/contracts/dumper.py,sha256=5OqGc4GEXCXX76sCX185giQMyKwwZvlOv3I7tTwV2fQ,1324
|
346
346
|
orionis/test/suites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
347
|
-
orionis/test/suites/test_suite.py,sha256=
|
348
|
-
orionis/test/suites/test_unit.py,sha256=
|
347
|
+
orionis/test/suites/test_suite.py,sha256=nJhToYdvHFETSNqunk-_i6Pe716842eaFKDBhChjigA,5303
|
348
|
+
orionis/test/suites/test_unit.py,sha256=dnLEEeBnGkE7DRM2XXJPtxHw25JLzP9ZtcGImmBNBM4,54916
|
349
349
|
orionis/test/suites/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
350
350
|
orionis/test/suites/contracts/test_suite.py,sha256=eluzYwkNBbKjxYStj_tHN_Fm3YDPpGQdqMu5eiluh-E,1059
|
351
351
|
orionis/test/suites/contracts/test_unit.py,sha256=l1LQllODyvcSByXMl1lGrUkoLsXbBHZZLWZI4A-mlQg,5881
|
352
352
|
orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
353
353
|
orionis/test/view/render.py,sha256=jXZkbITBknbUwm_mD8bcTiwLDvsFkrO9qrf0ZgPwqxc,4903
|
354
|
-
orionis-0.
|
354
|
+
orionis-0.295.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
|
355
355
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
356
356
|
tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
357
357
|
tests/example/test_example.py,sha256=byd_lI6tVDgGPEIrr7PLZbBu0UoZOymmdmyA_4u-QUw,601
|
@@ -455,8 +455,8 @@ tests/support/inspection/fakes/fake_reflection_instance_with_abstract.py,sha256=
|
|
455
455
|
tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
456
456
|
tests/testing/test_testing_result.py,sha256=MrGK3ZimedL0b5Ydu69Dg8Iul017AzLTm7VPxpXlpfU,4315
|
457
457
|
tests/testing/test_testing_unit.py,sha256=A6QkiOkP7GPC1Szh_GqsrV7GxjWjK8cIwFez6YfrzmM,7683
|
458
|
-
orionis-0.
|
459
|
-
orionis-0.
|
460
|
-
orionis-0.
|
461
|
-
orionis-0.
|
462
|
-
orionis-0.
|
458
|
+
orionis-0.295.0.dist-info/METADATA,sha256=fPQLKOQTJqwhW2rscOTMOoRqQjcIAfMW3fKU2w1e5Wc,4772
|
459
|
+
orionis-0.295.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
460
|
+
orionis-0.295.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
|
461
|
+
orionis-0.295.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
462
|
+
orionis-0.295.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|