orionis 0.591.0__py3-none-any.whl → 0.593.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 +0 -16
- orionis/metadata/framework.py +1 -1
- orionis/test/contracts/unit_test.py +0 -81
- orionis/test/core/unit_test.py +737 -604
- orionis/test/output/printer.py +258 -174
- orionis/test/records/logs.py +223 -83
- orionis/test/view/render.py +45 -17
- {orionis-0.591.0.dist-info → orionis-0.593.0.dist-info}/METADATA +1 -1
- {orionis-0.591.0.dist-info → orionis-0.593.0.dist-info}/RECORD +12 -12
- {orionis-0.591.0.dist-info → orionis-0.593.0.dist-info}/WHEEL +0 -0
- {orionis-0.591.0.dist-info → orionis-0.593.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.591.0.dist-info → orionis-0.593.0.dist-info}/top_level.txt +0 -0
orionis/test/output/printer.py
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
import re
|
2
2
|
from datetime import datetime
|
3
|
+
import unittest
|
3
4
|
from typing import Any, Dict, List
|
4
5
|
from rich.console import Console
|
5
6
|
from rich.live import Live
|
6
7
|
from rich.panel import Panel
|
8
|
+
from rich.progress import Progress, BarColumn, TextColumn, TaskProgressColumn
|
7
9
|
from rich.syntax import Syntax
|
8
10
|
from rich.table import Table
|
9
11
|
from rich.text import Text
|
10
|
-
from orionis.
|
12
|
+
from orionis.foundation.config.testing.enums.verbosity import VerbosityMode
|
11
13
|
from orionis.test.contracts.printer import ITestPrinter
|
12
14
|
from orionis.test.entities.result import TestResult
|
13
15
|
from orionis.test.enums import TestStatus
|
@@ -16,99 +18,164 @@ class TestPrinter(ITestPrinter):
|
|
16
18
|
|
17
19
|
def __init__(
|
18
20
|
self,
|
19
|
-
|
21
|
+
verbosity: VerbosityMode | int = VerbosityMode.DETAILED,
|
20
22
|
title: str = "🧪 Orionis Framework - Component Test Suite",
|
21
23
|
width: int = 75
|
22
24
|
) -> None:
|
23
25
|
"""
|
24
|
-
|
26
|
+
Initializes a TestPrinter instance for formatted test output using the Rich library.
|
25
27
|
|
26
28
|
Parameters
|
27
29
|
----------
|
28
30
|
print_result : bool, optional
|
29
|
-
|
31
|
+
If True, enables printing of test results to the console. If False, suppresses all output.
|
32
|
+
verbosity : VerbosityMode or int, optional
|
33
|
+
Specifies the verbosity level for output. Accepts either a VerbosityMode enum or an integer value.
|
34
|
+
Default is VerbosityMode.DETAILED.
|
30
35
|
title : str, optional
|
31
|
-
The title
|
36
|
+
The title displayed in the output panel. Default is "🧪 Orionis Framework - Component Test Suite".
|
32
37
|
width : int, optional
|
33
|
-
The width of the output panel as a percentage of the console width
|
38
|
+
The width of the output panel as a percentage of the console width. Must be between 10 and 100.
|
39
|
+
Default is 75.
|
34
40
|
|
35
41
|
Returns
|
36
42
|
-------
|
37
43
|
None
|
44
|
+
This constructor does not return any value. It initializes the TestPrinter instance.
|
45
|
+
|
46
|
+
Raises
|
47
|
+
------
|
48
|
+
ValueError
|
49
|
+
If any of the input parameters are of invalid type or out of allowed range.
|
50
|
+
|
51
|
+
Notes
|
52
|
+
-----
|
53
|
+
- The Rich Console instance is created for rendering output.
|
54
|
+
- The verbosity level, panel width, panel title, and print_result flag are validated and set.
|
38
55
|
"""
|
39
56
|
# Create a Rich Console instance for output rendering
|
40
57
|
self.__rich_console = Console()
|
41
58
|
|
42
|
-
#
|
43
|
-
|
59
|
+
# Validate and set verbosity level
|
60
|
+
if not isinstance(verbosity, (int, VerbosityMode)):
|
61
|
+
raise ValueError("The 'verbosity' parameter must be an integer or VerbosityMode enum.")
|
62
|
+
self.__verbosity: int = verbosity if isinstance(verbosity, int) else verbosity.value
|
44
63
|
|
45
|
-
#
|
64
|
+
# Validate and set panel width (must be between 10% and 100% of console width)
|
65
|
+
if not isinstance(width, int) or not (10 <= width <= 100):
|
66
|
+
raise ValueError("The 'width' parameter must be an integer between 10 and 100.")
|
46
67
|
self.__panel_width: int = int(self.__rich_console.width * (width / 100))
|
47
68
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
self.__print_result: bool = print_result
|
69
|
+
# Validate and set panel title
|
70
|
+
if not isinstance(title, str):
|
71
|
+
raise ValueError("The 'title' parameter must be a string.")
|
72
|
+
self.__panel_title: str = title
|
53
73
|
|
54
74
|
def print(
|
55
75
|
self,
|
56
76
|
value: Any
|
57
77
|
) -> None:
|
58
78
|
"""
|
59
|
-
Print a value to the console using the Rich library.
|
79
|
+
Print a value to the console using the Rich library, supporting strings, lists, and other objects.
|
60
80
|
|
61
81
|
Parameters
|
62
82
|
----------
|
63
83
|
value : Any
|
64
|
-
The value to be printed. Can be a string,
|
84
|
+
The value to be printed. Can be a string, a list of items, or any other object.
|
65
85
|
|
66
86
|
Returns
|
67
87
|
-------
|
68
88
|
None
|
89
|
+
This method does not return any value. Output is sent directly to the console.
|
90
|
+
|
91
|
+
Notes
|
92
|
+
-----
|
93
|
+
- If result printing is disabled (`self.__print_result` is False), no output will be produced.
|
94
|
+
- Strings are printed as-is.
|
95
|
+
- Lists are iterated and each item is printed on a separate line.
|
96
|
+
- Other objects are converted to string before printing.
|
69
97
|
"""
|
70
|
-
|
71
|
-
|
98
|
+
|
99
|
+
# If printing results is disabled, do not output anything
|
100
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
72
101
|
return
|
73
102
|
|
74
|
-
#
|
103
|
+
# Print string values directly
|
75
104
|
if isinstance(value, str):
|
76
105
|
self.__rich_console.print(value)
|
77
106
|
|
78
|
-
#
|
107
|
+
# Print each item of a list on a new line
|
79
108
|
elif isinstance(value, list):
|
80
109
|
for item in value:
|
81
110
|
self.__rich_console.print(item)
|
82
111
|
|
83
|
-
# For
|
112
|
+
# For other object types, print their string representation
|
84
113
|
else:
|
85
114
|
self.__rich_console.print(str(value))
|
86
115
|
|
116
|
+
def line(
|
117
|
+
self,
|
118
|
+
count: int = 1
|
119
|
+
) -> None:
|
120
|
+
"""
|
121
|
+
Print a specified number of blank lines to the console for spacing.
|
122
|
+
|
123
|
+
Parameters
|
124
|
+
----------
|
125
|
+
count : int, optional
|
126
|
+
The number of blank lines to print (default is 1).
|
127
|
+
Returns
|
128
|
+
-------
|
129
|
+
None
|
130
|
+
This method does not return any value. Blank lines are printed directly to the console.
|
131
|
+
Notes
|
132
|
+
-----
|
133
|
+
- If result printing is disabled (`self.__print_result` is False), no output will be produced.
|
134
|
+
- The method uses the Rich console's built-in line printing functionality.
|
135
|
+
"""
|
136
|
+
|
137
|
+
# If printing results is disabled, do not output anything
|
138
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
139
|
+
return
|
140
|
+
|
141
|
+
# Print the specified number of blank lines
|
142
|
+
self.__rich_console.line(count)
|
143
|
+
|
87
144
|
def zeroTestsMessage(self) -> None:
|
88
145
|
"""
|
89
|
-
Display a message indicating that no tests were found to execute.
|
146
|
+
Display a styled message indicating that no tests were found to execute.
|
147
|
+
|
148
|
+
Parameters
|
149
|
+
----------
|
150
|
+
None
|
90
151
|
|
91
152
|
Returns
|
92
153
|
-------
|
93
154
|
None
|
155
|
+
This method does not return any value. The message is printed directly to the console.
|
156
|
+
|
157
|
+
Notes
|
158
|
+
-----
|
159
|
+
- If result printing is disabled (`self.__print_result` is False), no output will be produced.
|
160
|
+
- The message is displayed in a Rich panel with a yellow border and centered title.
|
94
161
|
"""
|
95
|
-
# If
|
96
|
-
if self.
|
162
|
+
# If printing results is disabled, do not output anything
|
163
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
97
164
|
return
|
98
165
|
|
99
|
-
# Print
|
166
|
+
# Print a styled panel to indicate that no tests were found
|
100
167
|
self.__rich_console.print(
|
101
168
|
Panel(
|
102
169
|
"No tests found to execute.",
|
103
|
-
border_style="yellow",
|
104
|
-
title="No Tests",
|
105
|
-
title_align="center",
|
106
|
-
width=self.__panel_width,
|
107
|
-
padding=(0, 1)
|
170
|
+
border_style="yellow", # Use yellow to indicate a warning or neutral state
|
171
|
+
title="No Tests", # Panel title
|
172
|
+
title_align="center", # Center the title
|
173
|
+
width=self.__panel_width, # Set panel width based on console configuration
|
174
|
+
padding=(0, 1) # Add horizontal padding for better appearance
|
108
175
|
)
|
109
176
|
)
|
110
177
|
|
111
|
-
# Add a blank line after the panel for spacing
|
178
|
+
# Add a blank line after the panel for visual spacing
|
112
179
|
self.__rich_console.line(1)
|
113
180
|
|
114
181
|
def startMessage(
|
@@ -119,53 +186,99 @@ class TestPrinter(ITestPrinter):
|
|
119
186
|
max_workers: int
|
120
187
|
):
|
121
188
|
"""
|
122
|
-
Display a formatted start message for the test execution session.
|
189
|
+
Display a formatted start message for the beginning of a test execution session.
|
123
190
|
|
124
191
|
Parameters
|
125
192
|
----------
|
126
193
|
length_tests : int
|
127
|
-
|
194
|
+
Total number of tests scheduled for execution in the session.
|
128
195
|
execution_mode : str
|
129
|
-
The mode of execution
|
196
|
+
The mode of test execution. Should be either "parallel" or "sequential".
|
130
197
|
max_workers : int
|
131
|
-
|
198
|
+
Number of worker threads or processes to use if running in parallel mode.
|
132
199
|
|
133
200
|
Returns
|
134
201
|
-------
|
135
202
|
None
|
203
|
+
This method does not return any value. It prints a styled panel to the console with session details.
|
204
|
+
|
205
|
+
Notes
|
206
|
+
-----
|
207
|
+
- If result printing is disabled (`self.__print_result` is False), no output will be produced.
|
208
|
+
- The panel displays the total number of tests, execution mode, and the timestamp when the session started.
|
209
|
+
- The execution mode text will indicate parallel execution with the number of workers if applicable.
|
136
210
|
"""
|
137
|
-
|
138
|
-
|
211
|
+
|
212
|
+
# If printing results is disabled, do not output anything
|
213
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
139
214
|
return
|
140
215
|
|
141
|
-
#
|
216
|
+
# Format the execution mode text for display
|
142
217
|
mode_text = f"[stat]Parallel with {max_workers} workers[/stat]" if execution_mode == "parallel" else "Sequential"
|
143
218
|
|
144
219
|
# Prepare the lines of information to display in the panel
|
145
220
|
textlines = [
|
146
|
-
f"[bold]Total Tests:[/bold] [dim]{length_tests}[/dim]",
|
147
|
-
f"[bold]Mode:[/bold] [dim]{mode_text}[/dim]",
|
148
|
-
f"[bold]Started at:[/bold] [dim]{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}[/dim]"
|
221
|
+
f"[bold]Total Tests:[/bold] [dim]{length_tests}[/dim]", # Show total number of tests
|
222
|
+
f"[bold]Mode:[/bold] [dim]{mode_text}[/dim]", # Show execution mode
|
223
|
+
f"[bold]Started at:[/bold] [dim]{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}[/dim]" # Show start timestamp
|
149
224
|
]
|
150
225
|
|
151
|
-
# Add a blank line before the panel
|
152
|
-
self.__rich_console.line(1)
|
153
|
-
|
154
226
|
# Print the panel with the formatted text lines
|
155
227
|
self.__rich_console.print(
|
156
228
|
Panel(
|
157
|
-
str('\n').join(textlines),
|
158
|
-
border_style="blue",
|
159
|
-
title=self.__panel_title,
|
160
|
-
title_align="center",
|
161
|
-
width=self.__panel_width,
|
162
|
-
padding=(0, 1)
|
229
|
+
str('\n').join(textlines), # Join all lines for panel content
|
230
|
+
border_style="blue", # Use blue border for the panel
|
231
|
+
title=self.__panel_title, # Set the panel title
|
232
|
+
title_align="center", # Center the panel title
|
233
|
+
width=self.__panel_width, # Set panel width based on console configuration
|
234
|
+
padding=(0, 1) # Add horizontal padding for better appearance
|
163
235
|
)
|
164
236
|
)
|
165
237
|
|
166
|
-
# Add a blank line after the panel
|
238
|
+
# Add a blank line after the panel for spacing
|
167
239
|
self.__rich_console.line(1)
|
168
240
|
|
241
|
+
def progressBar(
|
242
|
+
self
|
243
|
+
) -> Progress:
|
244
|
+
"""
|
245
|
+
Create and return a Rich Progress bar instance for tracking task progress in the console.
|
246
|
+
|
247
|
+
Parameters
|
248
|
+
----------
|
249
|
+
self : TestPrinter
|
250
|
+
The instance of the TestPrinter class.
|
251
|
+
|
252
|
+
Returns
|
253
|
+
-------
|
254
|
+
Progress
|
255
|
+
A Rich Progress object configured with:
|
256
|
+
- A cyan-colored task description column.
|
257
|
+
- A visual progress bar.
|
258
|
+
- A percentage completion indicator.
|
259
|
+
- Output directed to the configured Rich console.
|
260
|
+
- Transient display (disappears after completion).
|
261
|
+
- Disabled if result printing is turned off.
|
262
|
+
|
263
|
+
Notes
|
264
|
+
-----
|
265
|
+
- If printing is disabled (`self.__print_result` is False), the progress bar will not be shown.
|
266
|
+
- The progress bar is suitable for tracking the progress of tasks such as test execution.
|
267
|
+
"""
|
268
|
+
|
269
|
+
# Flag to disable the progress bar if printing is off or verbosity is silent/minimal
|
270
|
+
disable = self.__verbosity <= VerbosityMode.MINIMAL.value
|
271
|
+
|
272
|
+
# Create and return a Rich Progress bar instance with custom columns and settings
|
273
|
+
return Progress(
|
274
|
+
TextColumn("[cyan]{task.description}"), # Task description in cyan
|
275
|
+
BarColumn(), # Visual progress bar
|
276
|
+
TaskProgressColumn(), # Percentage completion indicator
|
277
|
+
console=self.__rich_console, # Output to the configured Rich console
|
278
|
+
transient=True, # Remove the bar after completion
|
279
|
+
disable=disable # Disable if printing is off
|
280
|
+
)
|
281
|
+
|
169
282
|
def finishMessage(
|
170
283
|
self,
|
171
284
|
*,
|
@@ -187,7 +300,7 @@ class TestPrinter(ITestPrinter):
|
|
187
300
|
None
|
188
301
|
"""
|
189
302
|
# If not printing results, return early
|
190
|
-
if self.
|
303
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
191
304
|
return
|
192
305
|
|
193
306
|
# Determine status icon based on failures and errors
|
@@ -214,57 +327,72 @@ class TestPrinter(ITestPrinter):
|
|
214
327
|
def executePanel(
|
215
328
|
self,
|
216
329
|
*,
|
217
|
-
|
218
|
-
|
219
|
-
):
|
330
|
+
func: callable,
|
331
|
+
live_console: bool = True
|
332
|
+
) -> unittest.TestResult:
|
220
333
|
"""
|
221
|
-
|
334
|
+
Executes a callable within a styled Rich panel, optionally using a live console for dynamic updates.
|
222
335
|
|
223
336
|
Parameters
|
224
337
|
----------
|
225
|
-
|
226
|
-
The
|
227
|
-
|
228
|
-
|
338
|
+
func : callable
|
339
|
+
The function or method to execute. It should take no arguments and return a result.
|
340
|
+
live_console : bool, optional
|
341
|
+
If True, displays a live updating panel during execution (default is True).
|
342
|
+
If False, displays a static panel before execution.
|
229
343
|
|
230
344
|
Returns
|
231
345
|
-------
|
232
|
-
|
233
|
-
|
346
|
+
unittest.TestResult
|
347
|
+
The result returned by the executed callable, typically a `unittest.TestResult` object.
|
348
|
+
|
349
|
+
Notes
|
350
|
+
-----
|
351
|
+
- If result printing is disabled, the callable is executed without any panel or output.
|
352
|
+
- If `live_console` is True, a transient live panel is shown while the callable executes.
|
353
|
+
- If `live_console` is False, a static panel is printed before execution.
|
354
|
+
- The method always returns the result of the provided callable, regardless of output mode.
|
234
355
|
"""
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
356
|
+
|
357
|
+
# Ensure the provided func is actually callable
|
358
|
+
if not callable(func):
|
359
|
+
raise ValueError("The 'func' parameter must be a callable (function or method).")
|
239
360
|
|
240
361
|
# Only display output if printing results is enabled
|
241
|
-
if self.
|
362
|
+
if self.__verbosity != VerbosityMode.SILENT.value:
|
242
363
|
|
243
|
-
#
|
244
|
-
|
245
|
-
"[yellow]⏳ Running...[/yellow]",
|
246
|
-
border_style="yellow",
|
247
|
-
width=self.__panel_width,
|
248
|
-
padding=(0, 1)
|
249
|
-
)
|
364
|
+
# If live_console is True, use a live panel for dynamic updates
|
365
|
+
if live_console:
|
250
366
|
|
251
|
-
|
252
|
-
|
367
|
+
# Prepare a minimal running message as a single line, using the configured panel width
|
368
|
+
running_panel = Panel(
|
369
|
+
"[yellow]⏳ Running...[/yellow]",
|
370
|
+
border_style="yellow",
|
371
|
+
width=self.__panel_width,
|
372
|
+
padding=(0, 1)
|
373
|
+
)
|
253
374
|
|
254
|
-
# Execute the
|
375
|
+
# Execute the callable within a live Rich panel context
|
255
376
|
with Live(running_panel, console=self.__rich_console, refresh_per_second=4, transient=True):
|
256
|
-
return
|
257
|
-
|
377
|
+
return func()
|
258
378
|
else:
|
259
379
|
|
260
|
-
#
|
380
|
+
# Prepare a panel with a message indicating that test results will follow
|
381
|
+
running_panel = Panel(
|
382
|
+
"[yellow]🧪 Running tests...[/yellow]",
|
383
|
+
border_style="green",
|
384
|
+
width=self.__panel_width,
|
385
|
+
padding=(0, 1)
|
386
|
+
)
|
387
|
+
|
388
|
+
# If live_console is False, print a static panel before running
|
261
389
|
self.__rich_console.print(running_panel)
|
262
|
-
return
|
390
|
+
return func()
|
263
391
|
|
264
392
|
else:
|
265
393
|
|
266
|
-
# If result printing is disabled, execute the
|
267
|
-
return
|
394
|
+
# If result printing is disabled, execute the callable without any panel
|
395
|
+
return func()
|
268
396
|
|
269
397
|
def linkWebReport(
|
270
398
|
self,
|
@@ -283,7 +411,7 @@ class TestPrinter(ITestPrinter):
|
|
283
411
|
None
|
284
412
|
"""
|
285
413
|
# If not printing results, do not display the link
|
286
|
-
if self.
|
414
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
287
415
|
return
|
288
416
|
|
289
417
|
# Create the base invitation text with a green style
|
@@ -322,7 +450,7 @@ class TestPrinter(ITestPrinter):
|
|
322
450
|
None
|
323
451
|
"""
|
324
452
|
# If result printing is disabled, do not display the summary table
|
325
|
-
if self.
|
453
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
326
454
|
return
|
327
455
|
|
328
456
|
# Create a Rich Table with headers and styling
|
@@ -379,7 +507,7 @@ class TestPrinter(ITestPrinter):
|
|
379
507
|
None
|
380
508
|
"""
|
381
509
|
# If result printing is disabled, do not display results
|
382
|
-
if
|
510
|
+
if self.__verbosity == VerbosityMode.SILENT.value:
|
383
511
|
return
|
384
512
|
|
385
513
|
# Print one blank line before the summary
|
@@ -388,18 +516,17 @@ class TestPrinter(ITestPrinter):
|
|
388
516
|
# Print the summary table of test results
|
389
517
|
self.summaryTable(summary)
|
390
518
|
|
391
|
-
#
|
519
|
+
# Get the list of individual test results
|
392
520
|
test_details: List[Dict] = summary.get("test_details", [])
|
521
|
+
|
522
|
+
# Iterate through each test result to display failures and errors
|
393
523
|
for test in test_details:
|
394
524
|
|
395
525
|
# If there are no failures or errors, skip to the next test
|
396
526
|
if test["status"] in (TestStatus.FAILED.name, TestStatus.ERRORED.name):
|
397
527
|
|
398
528
|
# Determine the status icon based on the test status
|
399
|
-
if test["status"] == TestStatus.FAILED.name:
|
400
|
-
status_icon = "❌ FAILED: "
|
401
|
-
else:
|
402
|
-
status_icon = "💥 ERRORED: "
|
529
|
+
status_icon = "❌ FAILED:" if test["status"] == TestStatus.FAILED.name else "💥 ERRORED:"
|
403
530
|
|
404
531
|
# Print separator line before each test result with class name and method name
|
405
532
|
self.__rich_console.rule(title=f'🧪 {test["class"]}.{test["method"]}()', align="left")
|
@@ -413,7 +540,6 @@ class TestPrinter(ITestPrinter):
|
|
413
540
|
_file = last_trace_frame.get('file')
|
414
541
|
_line = last_trace_frame.get('line')
|
415
542
|
_code = last_trace_frame.get('code')
|
416
|
-
_function = last_trace_frame.get('function')
|
417
543
|
|
418
544
|
# Print the file and line number if available
|
419
545
|
text = Text("📂 ")
|
@@ -421,43 +547,48 @@ class TestPrinter(ITestPrinter):
|
|
421
547
|
self.__rich_console.print(text)
|
422
548
|
|
423
549
|
# Print the error message with better formatting
|
424
|
-
text = Text(status_icon, style="red")
|
550
|
+
text = Text(f"{status_icon} ", style="red")
|
425
551
|
error_msg = test["error_message"] if test["error_message"] else "Unknown error"
|
426
552
|
text.append(error_msg, style="yellow")
|
427
553
|
self.__rich_console.print(text)
|
428
554
|
|
429
|
-
#
|
430
|
-
|
431
|
-
|
432
|
-
# Open the file and read its lines
|
433
|
-
with open(_file, 'r', encoding='utf-8') as f:
|
434
|
-
file_lines = f.readlines()
|
435
|
-
|
436
|
-
# Convert to 0-based index
|
437
|
-
error_line_num = int(_line) - 1
|
438
|
-
start_line = max(0, error_line_num - 1)
|
439
|
-
end_line = min(len(file_lines), error_line_num + 3)
|
440
|
-
|
441
|
-
# Create a code block with syntax highlighting
|
442
|
-
code_lines = []
|
443
|
-
for i in range(start_line, end_line):
|
444
|
-
line_num = i + 1
|
445
|
-
line_content = file_lines[i].rstrip()
|
446
|
-
if line_num == int(_line):
|
447
|
-
# Highlight the error line
|
448
|
-
code_lines.append(f"* {line_num:3d} | {line_content}")
|
449
|
-
else:
|
450
|
-
code_lines.append(f" {line_num:3d} | {line_content}")
|
451
|
-
|
452
|
-
code_block = '\n'.join(code_lines)
|
453
|
-
syntax = Syntax(code_block, "python", theme="monokai", line_numbers=False)
|
454
|
-
self.__rich_console.print(syntax)
|
555
|
+
# If verbosity is detailed, include file path, line number, error message, and traceback
|
556
|
+
if self.__verbosity == VerbosityMode.DETAILED.value:
|
455
557
|
|
456
|
-
|
558
|
+
try:
|
457
559
|
|
458
|
-
|
459
|
-
|
460
|
-
|
560
|
+
# Open the file and read its lines
|
561
|
+
if isinstance(_file, str) and _file:
|
562
|
+
with open(_file, 'r', encoding='utf-8') as f:
|
563
|
+
file_lines = f.readlines()
|
564
|
+
else:
|
565
|
+
raise ValueError(f"Invalid file path: {_file}")
|
566
|
+
|
567
|
+
# Convert to 0-based index
|
568
|
+
error_line_num = int(_line) - 1
|
569
|
+
start_line = max(0, error_line_num - 1)
|
570
|
+
end_line = min(len(file_lines), error_line_num + 3)
|
571
|
+
|
572
|
+
# Create a code block with syntax highlighting
|
573
|
+
code_lines = []
|
574
|
+
for i in range(start_line, end_line):
|
575
|
+
line_num = i + 1
|
576
|
+
line_content = file_lines[i].rstrip()
|
577
|
+
if line_num == int(_line):
|
578
|
+
# Highlight the error line
|
579
|
+
code_lines.append(f"* {line_num:3d} | {line_content}")
|
580
|
+
else:
|
581
|
+
code_lines.append(f" {line_num:3d} | {line_content}")
|
582
|
+
|
583
|
+
code_block = '\n'.join(code_lines)
|
584
|
+
syntax = Syntax(code_block, "python", theme="monokai", line_numbers=False)
|
585
|
+
self.__rich_console.print(syntax)
|
586
|
+
|
587
|
+
except Exception:
|
588
|
+
|
589
|
+
# Fallback to original behavior if file cannot be read
|
590
|
+
text = Text(f"{_line} | {_code}", style="dim")
|
591
|
+
self.__rich_console.print(text)
|
461
592
|
|
462
593
|
else:
|
463
594
|
|
@@ -467,7 +598,7 @@ class TestPrinter(ITestPrinter):
|
|
467
598
|
self.__rich_console.print(text)
|
468
599
|
|
469
600
|
# Print the error message with better formatting
|
470
|
-
text = Text(status_icon, style="bold red")
|
601
|
+
text = Text(f"{status_icon} ", style="bold red")
|
471
602
|
self.__rich_console.print(text)
|
472
603
|
|
473
604
|
# Print traceback if available
|
@@ -480,7 +611,8 @@ class TestPrinter(ITestPrinter):
|
|
480
611
|
self.__rich_console.print(syntax)
|
481
612
|
|
482
613
|
# Print a separator line after each test result
|
483
|
-
self.
|
614
|
+
if self.__verbosity == VerbosityMode.DETAILED.value:
|
615
|
+
self.__rich_console.rule()
|
484
616
|
|
485
617
|
# Print one blank line after the results
|
486
618
|
self.__rich_console.line(1)
|
@@ -505,7 +637,7 @@ class TestPrinter(ITestPrinter):
|
|
505
637
|
None
|
506
638
|
"""
|
507
639
|
# If result printing is disabled, do not display results
|
508
|
-
if
|
640
|
+
if self.__verbosity < VerbosityMode.DETAILED.value:
|
509
641
|
return
|
510
642
|
|
511
643
|
# Determine the status icon and label based on the test result
|
@@ -529,54 +661,6 @@ class TestPrinter(ITestPrinter):
|
|
529
661
|
display_msg = msg if len(msg) <= max_width else msg[:max_width - 3] + "..."
|
530
662
|
self.__rich_console.print(display_msg, highlight=False)
|
531
663
|
|
532
|
-
def __withDebugger(
|
533
|
-
self,
|
534
|
-
flatten_test_suite: list
|
535
|
-
) -> bool:
|
536
|
-
"""
|
537
|
-
Determine if any test case in the provided flattened test suite contains active debugging or dumping calls.
|
538
|
-
|
539
|
-
Parameters
|
540
|
-
----------
|
541
|
-
flatten_test_suite : list
|
542
|
-
A list of test case instances whose source code will be inspected for debugging or dumping calls.
|
543
|
-
|
544
|
-
Returns
|
545
|
-
-------
|
546
|
-
bool
|
547
|
-
True if any test case contains an active (non-commented) call to a debugging or dumping method.
|
548
|
-
False if no such calls are found or if an exception occurs during inspection.
|
549
|
-
"""
|
550
|
-
try:
|
551
|
-
|
552
|
-
# Iterate through each test case in the flattened test suite
|
553
|
-
for test_case in flatten_test_suite:
|
554
|
-
|
555
|
-
# Retrieve the source code of the test case using reflection
|
556
|
-
source = ReflectionInstance(test_case).getSourceCode()
|
557
|
-
|
558
|
-
# Check each line of the source code
|
559
|
-
for line in source.splitlines():
|
560
|
-
|
561
|
-
# Strip leading and trailing whitespace from the line
|
562
|
-
stripped = line.strip()
|
563
|
-
|
564
|
-
# Skip lines that are commented out
|
565
|
-
if stripped.startswith('#') or re.match(r'^\s*#', line):
|
566
|
-
continue
|
567
|
-
|
568
|
-
# If any debug keyword is present in the line, return True
|
569
|
-
if any(keyword in line for keyword in self.__debbug_keywords):
|
570
|
-
return True
|
571
|
-
|
572
|
-
# No debug or dump calls found in any test case
|
573
|
-
return False
|
574
|
-
|
575
|
-
except Exception:
|
576
|
-
|
577
|
-
# If any error occurs during inspection, return False
|
578
|
-
return False
|
579
|
-
|
580
664
|
def __sanitizeTraceback(
|
581
665
|
self,
|
582
666
|
test_path: str,
|