@@ -17,11 +17,22 @@ Class constructor($cs : 4D:C1709.Object)
1717 This:C1470 ._parseTagFilters ()
1818
1919Function run ()
20+ This:C1470 ._prepareErrorHandlingStorage ()
2021 var $handlerState : Object
2122 $handlerState := This:C1470 ._installErrorHandler ()
2223 This:C1470 ._runInternal ()
24+ This:C1470 ._captureGlobalErrors ()
2325 This:C1470 ._restoreErrorHandler ($handlerState )
2426
27+ Function _prepareErrorHandlingStorage ()
28+ Use (Storage:C1525)
29+ If (Storage:C1525 .testErrors = Null:C1517)
30+ Storage:C1525 .testErrors := New shared collection:C1527
31+ Else
32+ Storage:C1525 .testErrors .clear ()
33+ End if
34+ End use
35+
2536Function _runInternal ()
2637 This:C1470 ._prepareSuites ()
2738 This:C1470 ._runSuitesSequentially ()
@@ -50,26 +61,37 @@ Function _runSuitesSequentially()
5061
5162Function _installErrorHandler () : Object
5263 var $previousErrorHandler : Text
53- var $shouldInstall : Boolean
64+ var $previousGlobalHandler : Text
65+ var $shouldInstallLocal : Boolean
66+ var $shouldInstallGlobal : Boolean
5467
5568 $previousErrorHandler := Method called on error:C704
56- $shouldInstall := ($previousErrorHandler# "TestErrorHandler")
69+ $shouldInstallLocal := ($previousErrorHandler# "TestErrorHandler")
5770
58- If ($shouldInstall )
71+ If ($shouldInstallLocal )
5972 ON ERR CALL:C155 ("TestErrorHandler" )
6073 End if
6174
75+ $previousGlobalHandler := Method called on error:C704 (1 )
76+ $shouldInstallGlobal := ($previousGlobalHandler# "TestGlobalErrorHandler")
77+
78+ If ($shouldInstallGlobal)
79+ ON ERR CALL:C155 ("TestGlobalErrorHandler" ; 1 )
80+ End if
81+
6282 return New object:C1471 (\
6383 "previousHandler" ; $previousErrorHandler ; \
64- "installedNewHandler" ; $shouldInstall \
84+ "installedLocalHandler" ; $shouldInstallLocal ; \
85+ "previousGlobalHandler" ; $previousGlobalHandler ; \
86+ "installedGlobalHandler" ; $shouldInstallGlobal \
6587 )
6688
6789Function _restoreErrorHandler ($handlerState : Object)
6890 If ($handlerState= Null:C1517)
6991 return
7092 End if
7193
72- If ($handlerState .installedNewHandler )
94+ If (Bool :C1537 ( $handlerState .installedLocalHandler ) )
7395 var $previousErrorHandler : Text
7496 $previousErrorHandler := $handlerState .previousHandler
7597
@@ -79,7 +101,18 @@ Function _restoreErrorHandler($handlerState : Object)
79101 ON ERR CALL:C155 ("" )
80102 End if
81103 End if
82-
104+
105+ If (Bool:C1537 ($handlerState .installedGlobalHandler ))
106+ var $previousGlobalHandler : Text
107+ $previousGlobalHandler := $handlerState .previousGlobalHandler
108+
109+ If ($previousGlobalHandler# "")
110+ ON ERR CALL:C155 ($previousGlobalHandler ; 1 )
111+ Else
112+ ON ERR CALL:C155 ("" ; 1 )
113+ End if
114+ End if
115+
83116Function discoverTests ()
84117 var $class : 4D:C1709 .Class
85118 For each ($class; This:C1470 ._getTestClasses ())
@@ -127,17 +160,20 @@ Function _filterTestClasses($classStore : Object) : Collection
127160 return $classes
128161
129162Function _initializeResults ()
130- This:C1470 .results := New object:C1471 (\
131- "totalTests" ; 0 ; \
132- "passed" ; 0 ; \
163+ This:C1470 .results := New object:C1471 (\
164+ "totalTests" ; 0 ; \
165+ "passed" ; 0 ; \
133166 "failed" ; 0 ; \
134167 "skipped" ; 0 ; \
135168 "startTime" ; 0 ; \
136169 "endTime" ; 0 ; \
137170 "duration" ; 0 ; \
138171 "suites" ; []; \
139172 "failedTests" ; []; \
140- "assertions" ; 0 \
173+ "assertions" ; 0 ; \
174+ "globalErrors" ; []; \
175+ "hasGlobalErrors" ; False:C215 ; \
176+ "globalErrorCount" ; 0 \
141177 )
142178
143179Function _logHeader ()
@@ -211,8 +247,68 @@ Function _collectSuiteResults($testSuite : cs:C1710._TestSuite)
211247 $suiteResult .assertions + = ($testResult .assertionCount )
212248 End for each
213249
214- This:C1470 .results .suites .push ($suiteResult )
215-
250+ This:C1470 .results .suites .push ($suiteResult )
251+
252+ Function _captureGlobalErrors ()
253+ var $globalErrors : Collection
254+ $globalErrors := This:C1470 ._drainGlobalErrorsFromStorage ()
255+
256+ This:C1470 .results .globalErrors := $globalErrors
257+ This:C1470 .results .globalErrorCount := $globalErrors .length
258+ This:C1470 .results .hasGlobalErrors := Bool:C1537 ($globalErrors .length > 0 )
259+
260+ Function _drainGlobalErrorsFromStorage () : Collection
261+ var $globalErrors : Collection
262+ $globalErrors := New collection:C1472
263+
264+ If (Storage:C1525 .testErrors # Null:C1517)
265+ Use (Storage:C1525 .testErrors )
266+ var $index : Integer
267+ For ($index; Storage:C1525 .testErrors .length - 1; 0; - 1)
268+ var $error : Object
269+ $error := Storage:C1525 .testErrors [$index ]
270+
271+ var $context : Text
272+ $context := $error .context || ""
273+
274+ If ($context= "global")
275+ $globalErrors .push (OB Copy:C1225 ($error ))
276+ Storage:C1525 .testErrors .remove ($index )
277+ End if
278+ End for
279+ End use
280+ End if
281+
282+ return $globalErrors
283+
284+ Function _formatGlobalErrorForLog ($error : Object) : Text
285+ var $codeText : Text
286+ var $processText : Text
287+ var $methodText : Text
288+ var $lineText : Text
289+ var $formulaText : Text
290+ var $message : Text
291+
292+ $codeText := ($error .code # Null:C1517) ? String:C10 ($error .code ) : "?"
293+ $processText := ($error .processNumber # Null:C1517) ? String:C10 ($error .processNumber ) : "?"
294+ $methodText := $error .text || "Unknown location"
295+
296+ If ($error .line # Null:C1517)
297+ $lineText := " line " + String:C10 ($error .line )
298+ Else
299+ $lineText := ""
300+ End if
301+
302+ $formulaText := $error .method || ""
303+
304+ $message := "- [" + $codeText + "] Process " + $processText + ": " + $methodText + $lineText
305+
306+ If ($formulaText# "")
307+ $message := $message + "\r\n " + $formulaText
308+ End if
309+
310+ return $message
311+
216312Function _generateReport ()
217313 If (This:C1470 .outputFormat = "json")
218314 This:C1470 ._generateJSONReport ()
@@ -245,10 +341,14 @@ Function _generateHumanReport()
245341 LOG EVENT:C667 (Into system standard outputs:K38:9 ; "Assertions: " + String:C10 (This:C1470 .results .assertions )+ "\r\n " ; Information message:K38:1 )
246342 LOG EVENT:C667 (Into system standard outputs:K38:9 ; "Pass Rate: " + String:C10 ($passRate ; "##0.0" )+ "%\r\n " ; Information message:K38:1 )
247343 LOG EVENT:C667 (Into system standard outputs:K38:9 ; "Duration: " + String:C10 (This:C1470 .results .duration )+ "ms\r\n " ; Information message:K38:1 )
248-
249- If (This:C1470 .results .failed > 0)
250- LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
251- LOG EVENT:C667 (Into system standard outputs:K38:9 ; "=== Failed Tests ===\r\n " ; Error message:K38:3 )
344+
345+ var $externalMessageType : Integer
346+ $externalMessageType := Choose:C955 (This:C1470 .results .globalErrorCount > 0 ; Error message:K38:3 ; Information message:K38:1 )
347+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "External Errors: " + String:C10 (This:C1470 .results .globalErrorCount )+ "\r\n " ; $externalMessageType )
348+
349+ If (This:C1470 .results .failed > 0)
350+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
351+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "=== Failed Tests ===\r\n " ; Error message:K38:3 )
252352
253353 var $failedTest : Object
254354 For each ($failedTest; This:C1470 .results .failedTests )
@@ -269,10 +369,20 @@ Function _generateHumanReport()
269369 If ($failedTest .callChain # Null)
270370 LOG EVENT:C667 (Into system standard outputs:K38:9 ; This:C1470 ._formatCallChain ($failedTest .callChain )+ "\r\n " ; Error message:K38:3 )
271371 End if
272- End for each
273- End if
274-
275- This:C1470 ._logFooter ()
372+ End for each
373+ End if
374+
375+ If (This:C1470 .results .hasGlobalErrors )
376+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
377+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "=== Runtime Errors Outside Test Processes ===\r\n " ; Error message:K38:3 )
378+
379+ var $globalError : Object
380+ For each ($globalError; This:C1470 .results .globalErrors )
381+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; This:C1470 ._formatGlobalErrorForLog ($globalError )+ "\r\n " ; Error message:K38:3 )
382+ End for each
383+ End if
384+
385+ This:C1470 ._logFooter ()
276386
277387Function _generateJSONReport ()
278388 var $passRate : Real
@@ -284,15 +394,18 @@ Function _generateJSONReport()
284394 $passRate := 0
285395 End if
286396
287- var $jsonReport : Object
288-
289- If (This:C1470 .verboseOutput )
290- // Verbose mode: include all details (original format)
291- $jsonReport := OB Copy:C1225 (This:C1470 .results )
292- $jsonReport .passRate := $passRate
293- $jsonReport .status := (This:C1470 .results .failed = 0) ? "success" : "failure"
294- Else
295- // Terse mode: minimal information
397+ var $jsonReport : Object
398+
399+ var $hasFailures : Boolean
400+ $hasFailures := (This:C1470 .results .failed > 0) || This:C1470 .results .hasGlobalErrors
401+
402+ If (This:C1470 .verboseOutput )
403+ // Verbose mode: include all details (original format)
404+ $jsonReport := OB Copy:C1225 (This:C1470 .results )
405+ $jsonReport .passRate := $passRate
406+ $jsonReport .status := $hasFailures ? "failure" : "success"
407+ Else
408+ // Terse mode: minimal information
296409 $jsonReport := New object:C1471 (\
297410 "tests" ; This:C1470 .results .totalTests ; \
298411 "passed" ; This:C1470 .results .passed ; \
@@ -301,7 +414,10 @@ Function _generateJSONReport()
301414 "assertions" ; This:C1470 .results .assertions ; \
302415 "rate" ; Round:C94 ($passRate ; 1 ); \
303416 "duration" ; This:C1470 .results .duration ; \
304- "status" ; (This:C1470 .results .failed = 0) ? "ok" : "fail" \
417+ "globalErrors" ; This:C1470 .results .globalErrors ; \
418+ "globalErrorCount" ; This:C1470 .results .globalErrorCount ; \
419+ "hasGlobalErrors" ; This:C1470 .results .hasGlobalErrors ; \
420+ "status" ; $hasFailures ? "fail" : "ok" \
305421 )
306422
307423 // Include individual test results with assertions
@@ -400,9 +516,12 @@ Function _buildJUnitXML() : Text
400516 $totalTime := This:C1470 .results .duration / 1000 // Convert ms to seconds
401517
402518 // Calculate errors and failures separately
403- var $totalErrors ; $totalFailures : Integer
404- $totalErrors := This:C1470 ._countTestsWithRuntimeErrors ()
405- $totalFailures := This:C1470 .results .failed - $totalErrors // Failures are failed tests without runtime errors
519+ var $totalErrors ; $totalFailures : Integer
520+ var $globalErrorCount : Integer
521+ $totalErrors := This:C1470 ._countTestsWithRuntimeErrors ()
522+ $globalErrorCount := This:C1470 .results .globalErrorCount
523+ $totalErrors := $totalErrors + $globalErrorCount
524+ $totalFailures := This:C1470 .results .failed - $totalErrors // Failures are failed tests without runtime errors
406525
407526 // XML header and root testsuites element
408527 $xml := "<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>\r\n "
@@ -414,12 +533,16 @@ Function _buildJUnitXML() : Text
414533 $xml := $xml + " timestamp=\" " + This:C1470 ._formatTimestamp (This:C1470 .results.startTime)+ "\" >\r\n "
415534
416535 // Build testsuite elements
417- var $suite : Object
418- For each ($suite; This:C1470 .results .suites )
419- $xml := $xml + This:C1470 ._buildTestSuiteXML ($suite )
420- End for each
421-
422- $xml := $xml + "</testsuites>\r\n "
536+ var $suite : Object
537+ For each ($suite; This:C1470 .results .suites )
538+ $xml := $xml + This:C1470 ._buildTestSuiteXML ($suite )
539+ End for each
540+
541+ If (This:C1470 .results .hasGlobalErrors )
542+ $xml := $xml + This:C1470 ._buildGlobalErrorsSystemErr ()
543+ End if
544+
545+ $xml := $xml + "</testsuites>\r\n "
423546
424547 return $xml
425548
@@ -480,10 +603,10 @@ Function _buildTestCaseXML($test : Object) : Text
480603 return $xml
481604
482605Function _buildFailureXML ($test : Object) : Text
483- var $xml : Text
484- var $failureMessage : Text
485- var $failureDetails : Text
486- var $elementType : Text
606+ var $xml : Text
607+ var $failureMessage : Text
608+ var $failureDetails : Text
609+ var $elementType : Text
487610
488611 // Extract failure message and details, determine element type
489612 If ($test .runtimeErrors .length > 0)
@@ -510,10 +633,25 @@ Function _buildFailureXML($test : Object) : Text
510633 End if
511634
512635 $xml := $xml + "\n ]]>"
513- $xml := $xml + "</" + $elementType + ">\r\n "
514-
515- return $xml
516-
636+ $xml := $xml + "</" + $elementType + ">\r\n "
637+
638+ return $xml
639+
640+ Function _buildGlobalErrorsSystemErr () : Text
641+ var $xml : Text
642+
643+ $xml := " <system-err><![CDATA[\n "
644+ $xml := $xml + "External runtime errors detected: " + String:C10 (This:C1470 .results .globalErrorCount )+ "\n "
645+
646+ var $error : Object
647+ For each ($error; This:C1470 .results .globalErrors )
648+ $xml := $xml + This:C1470 ._formatGlobalErrorForLog ($error )+ "\n "
649+ End for each
650+
651+ $xml := $xml + "]]></system-err>\r\n "
652+
653+ return $xml
654+
517655Function _writeJUnitXMLToFile ($xmlContent : Text; $outputPath : Text)
518656 // Parse the path to determine folder and filename
519657 var $pathParts : Collection
@@ -563,13 +701,27 @@ Function _formatTimestamp($milliseconds : Integer) : Text
563701 return $timestamp
564702
565703Function _logFooter ()
566- LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
567- If (This:C1470 .results .failed = 0)
568- LOG EVENT:C667 (Into system standard outputs:K38:9 ; "All tests passed! 🎉\r\n " ; Information message:K38:1 )
569- Else
570- LOG EVENT:C667 (Into system standard outputs:K38:9 ; String:C10 (This:C1470 .results .failed )+ " test(s) failed\r\n " ; Error message:K38:3 )
571- End if
572- LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
704+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
705+ If ((This:C1470 .results .failed = 0) && Not:C34 (This:C1470 .results .hasGlobalErrors ))
706+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "All tests passed! 🎉\r\n " ; Information message:K38:1 )
707+ Else
708+ var $summaryMessage : Text
709+ $summaryMessage := ""
710+
711+ If (This:C1470 .results .failed > 0)
712+ $summaryMessage := String:C10 (This:C1470 .results .failed )+ " test(s) failed"
713+ End if
714+
715+ If (This:C1470 .results .hasGlobalErrors )
716+ If ($summaryMessage# "")
717+ $summaryMessage := $summaryMessage + "; "
718+ End if
719+ $summaryMessage := $summaryMessage + String:C10 (This:C1470 .results .globalErrorCount )+ " external runtime error(s)"
720+ End if
721+
722+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; $summaryMessage + "\r\n " ; Error message:K38:3 )
723+ End if
724+ LOG EVENT:C667 (Into system standard outputs:K38:9 ; "\r\n " ; Information message:K38:1 )
573725
574726Function getResults () : Object
575727 return This:C1470 .results
0 commit comments