@@ -7,6 +7,10 @@ export interface LogSummary {
77 errorHighlightCount : number ;
88 warningHighlightCount : number ;
99 } ;
10+ failedTests : Array < {
11+ testName : string ;
12+ message ?: string ;
13+ } > ;
1014 highlights : string [ ] ;
1115 excerpt : string ;
1216}
@@ -65,11 +69,16 @@ export function summarizeLogTexts(
6569) : LogSummary {
6670 const normalizedMaxCharacters = Math . max ( 500 , maxCharacters ) ;
6771 const highlightSet = new Set < string > ( ) ;
72+ const failedTests = new Map < string , { testName : string ; message ?: string } > ( ) ;
6873 let warningHighlightCount = 0 ;
6974 let errorHighlightCount = 0 ;
7075
7176 for ( const parsedLogText of parsedLogTexts ) {
72- for ( const line of parsedLogText . split ( / \r ? \n / ) ) {
77+ const lines = parsedLogText . split ( / \r ? \n / ) ;
78+
79+ collectFailedTests ( lines , failedTests ) ;
80+
81+ for ( const line of lines ) {
7382 const normalizedLine = line . trim ( ) ;
7483
7584 if ( ! normalizedLine ) {
@@ -93,20 +102,84 @@ export function summarizeLogTexts(
93102 }
94103 }
95104
96- const combinedText = parsedLogTexts . join ( '\n\n' ) ;
97- const excerpt = combinedText . slice ( 0 , normalizedMaxCharacters ) ;
105+ const excerptSource = [
106+ ...[ ...failedTests . values ( ) ] . flatMap ( ( failedTest ) =>
107+ failedTest . message
108+ ? [ `${ failedTest . testName } : ${ failedTest . message } ` ]
109+ : [ failedTest . testName ] ,
110+ ) ,
111+ ...highlightSet ,
112+ ] . join ( '\n' ) ;
113+ const excerpt = excerptSource . slice ( 0 , normalizedMaxCharacters ) ;
98114
99115 return {
100116 summary : {
101117 parsedArtifactCount : parsedLogTexts . length ,
102118 errorHighlightCount,
103119 warningHighlightCount,
104120 } ,
121+ failedTests : [ ...failedTests . values ( ) ] . sort (
122+ ( leftFailure , rightFailure ) =>
123+ Number ( Boolean ( rightFailure . message ) ) - Number ( Boolean ( leftFailure . message ) ) ,
124+ ) ,
105125 highlights : [ ...highlightSet ] ,
106126 excerpt,
107127 } ;
108128}
109129
130+ function collectFailedTests (
131+ lines : string [ ] ,
132+ failedTests : Map < string , { testName : string ; message ?: string } > ,
133+ ) : void {
134+ for ( let index = 0 ; index < lines . length ; index += 1 ) {
135+ const line = lines [ index ] ?. trim ( ) ;
136+
137+ if ( ! line ) {
138+ continue ;
139+ }
140+
141+ const swiftTestingMatch = line . match ( / ( [ A - Z a - z 0 - 9 _ ] + \( .* \) ) f a i l e d w i t h : ? $ / i) ;
142+
143+ if ( swiftTestingMatch ?. [ 1 ] ) {
144+ const testName = swiftTestingMatch [ 1 ] ;
145+ const message = lines [ index + 1 ] ?. trim ( ) ;
146+ failedTests . set ( testName , {
147+ testName,
148+ message : message && ! isNoiseLine ( message ) ? message : undefined ,
149+ } ) ;
150+ continue ;
151+ }
152+
153+ const swiftTestingIssueMatch = line . match (
154+ / T e s t ( [ A - Z a - z 0 - 9 _ ] + \( .* \) ) r e c o r d e d a n i s s u e .* ?: ( .+ ) $ / i,
155+ ) ;
156+
157+ if ( swiftTestingIssueMatch ?. [ 1 ] && swiftTestingIssueMatch ?. [ 2 ] ) {
158+ failedTests . set ( swiftTestingIssueMatch [ 1 ] , {
159+ testName : swiftTestingIssueMatch [ 1 ] ,
160+ message : swiftTestingIssueMatch [ 2 ] ,
161+ } ) ;
162+ continue ;
163+ }
164+
165+ const xctestMatch = line . match (
166+ / T e s t C a s e .* ?( [ A - Z a - z 0 - 9 _ ] + ) \] ' f a i l e d / i,
167+ ) ;
168+
169+ if ( xctestMatch ?. [ 1 ] ) {
170+ const testName = `${ xctestMatch [ 1 ] } ()` ;
171+ const nextLine = lines [ index + 1 ] ?. trim ( ) ;
172+ const currentRecord = failedTests . get ( testName ) ?? { testName } ;
173+
174+ if ( nextLine && isAssertionLine ( nextLine ) ) {
175+ currentRecord . message = nextLine ;
176+ }
177+
178+ failedTests . set ( testName , currentRecord ) ;
179+ }
180+ }
181+ }
182+
110183function decodeTextBuffer ( buffer : Uint8Array ) : string | null {
111184 try {
112185 const decodedText = new TextDecoder ( 'utf-8' , { fatal : false } ) . decode ( buffer ) ;
@@ -159,3 +232,11 @@ function isErrorLine(line: string): boolean {
159232function isWarningLine ( line : string ) : boolean {
160233 return / w a r n i n g : / i. test ( line ) ;
161234}
235+
236+ function isNoiseLine ( line : string ) : boolean {
237+ return / ^ T e s t C a s e / . test ( line ) || / ^ = = = = = / . test ( line ) ;
238+ }
239+
240+ function isAssertionLine ( line : string ) : boolean {
241+ return / e x p e c t a t i o n f a i l e d | a s s e r t | e r r o r : / i. test ( line ) ;
242+ }
0 commit comments