@@ -150,6 +150,7 @@ def summarize_test_results(tests, platform, summary_dir, file_name="summary.log"
150
150
successes = []
151
151
failures = []
152
152
errors = []
153
+ success_testapp_paths = set ()
153
154
154
155
for test in tests :
155
156
results = validate_results (test .logs , platform )
@@ -160,6 +161,7 @@ def summarize_test_results(tests, platform, summary_dir, file_name="summary.log"
160
161
failures .append (test_result_pair )
161
162
else :
162
163
successes .append (test_result_pair )
164
+ success_testapp_paths .add (test .testapp_path )
163
165
164
166
# First log the successes, then the failures and errors, then the summary.
165
167
# This way, debugging will involve checking the summary at the bottom,
@@ -174,6 +176,11 @@ def summarize_test_results(tests, platform, summary_dir, file_name="summary.log"
174
176
for test , _ in errors :
175
177
logging .info ("%s didn't finish normally.\n %s" , test .testapp_path , test .logs )
176
178
179
+ # Testapps that failed first, but succeed after retry. (max 3 retry)
180
+ flaky_testapps = []
181
+ failures_exclude_flakiness = []
182
+ errors_exclude_flakiness = []
183
+
177
184
# The summary is much more terse, to minimize the time it takes to understand
178
185
# what went wrong, without necessarily providing full debugging context.
179
186
summary = []
@@ -184,7 +191,12 @@ def summarize_test_results(tests, platform, summary_dir, file_name="summary.log"
184
191
if errors :
185
192
summary .append ("\n %d TESTAPPS EXPERIENCED ERRORS:" % len (errors ))
186
193
for test , results in errors :
187
- summary .append ("%s:" % test .testapp_path )
194
+ summary .append ("\n %s:" % test .testapp_path )
195
+ if test .testapp_path in success_testapp_paths :
196
+ summary .append ("THIS TESTAPP IS FLAKY" )
197
+ flaky_testapps .append ((test , results ))
198
+ else :
199
+ errors_exclude_flakiness .append ((test , results ))
188
200
if hasattr (test , "ftl_link" ) and test .ftl_link :
189
201
summary .append ("ftl_link: %s" % test .ftl_link )
190
202
if hasattr (test , "raw_result_link" ) and test .raw_result_link :
@@ -198,7 +210,12 @@ def summarize_test_results(tests, platform, summary_dir, file_name="summary.log"
198
210
if failures :
199
211
summary .append ("\n %d TESTAPPS FAILED:" % len (failures ))
200
212
for test , results in failures :
201
- summary .append (test .testapp_path )
213
+ summary .append ("\n %s:" % test .testapp_path )
214
+ if test .testapp_path in success_testapp_paths :
215
+ summary .append ("THIS TESTAPP IS FLAKY" )
216
+ flaky_testapps .append ((test , results ))
217
+ else :
218
+ failures_exclude_flakiness .append ((test , results ))
202
219
if hasattr (test , "ftl_link" ) and test .ftl_link :
203
220
summary .append ("ftl_link: %s" % test .ftl_link )
204
221
if hasattr (test , "raw_result_link" ) and test .raw_result_link :
@@ -208,44 +225,72 @@ def summarize_test_results(tests, platform, summary_dir, file_name="summary.log"
208
225
"%d TESTAPPS TOTAL: %d PASSES, %d FAILURES, %d ERRORS"
209
226
% (len (tests ), len (successes ), len (failures ), len (errors )))
210
227
228
+ if len (flaky_testapps ) > 0 and len (flaky_testapps ) == len (failures ) + len (errors ):
229
+ logging .info ("All failures and errors are due to flakiness." )
230
+ summary .append ("ALL THE FOLLOWING FAILURES AND ERRORS ARE DUE TO FLAKINESS:(" )
231
+
211
232
# summary_json format:
212
233
# { "type": "test",
213
234
# "testapps": [testapp],
214
- # "errors": {testapp:{"log": error_log, "ftl_link": ftl_link, "raw_result_link": raw_result_link}},
215
- # "failures": {testapp:{"log": error_log, "ftl_link": ftl_link, "raw_result_link": raw_result_link,
216
- # "failed_tests": {falied_test: test_log}}}}}
235
+ # "errors": {testapp:{"logs": [error_log], "ftl_links": [ftl_link], "raw_result_links": [raw_result_link]}},
236
+ # "failures": {testapp:{"logs": [error_log], "ftl_links": [ftl_link], "raw_result_links": [raw_result_link],
237
+ # "failed_tests": {failed_test: test_log}}},
238
+ # "flakiness": {testapp:{"logs": [error_log], "ftl_links": [ftl_link], "raw_result_links": [raw_result_link],
239
+ # "flaky_tests": {flaky_test: test_log}}}}
217
240
summary_json = {}
218
241
summary_json ["type" ] = "test"
219
242
summary_json ["testapps" ] = [get_name (test .testapp_path ) for test in tests ]
220
- summary_json ["errors" ] = {get_name (test .testapp_path ):{"logs" : results . summary } for (test , results ) in errors }
221
- for (test , results ) in errors :
243
+ summary_json ["errors" ] = {get_name (test .testapp_path ):{"logs" : [], "ftl_links" : [], "raw_result_links" : [] } for (test , _ ) in errors_exclude_flakiness }
244
+ for (test , results ) in errors_exclude_flakiness :
222
245
testapp = get_name (test .testapp_path )
246
+ summary_json ["errors" ][testapp ]["logs" ].append (results .summary )
223
247
if hasattr (test , "ftl_link" ) and test .ftl_link :
224
- summary_json ["errors" ][testapp ]["ftl_link" ] = test .ftl_link
248
+ summary_json ["errors" ][testapp ]["ftl_links" ]. append ( test .ftl_link )
225
249
if hasattr (test , "raw_result_link" ) and test .raw_result_link :
226
- summary_json ["errors" ][testapp ]["raw_result_link" ] = test .raw_result_link
227
- summary_json ["failures" ] = {get_name (test .testapp_path ):{"logs" : results . summary , "failed_tests" : dict ()} for (test , results ) in failures }
228
- for (test , results ) in failures :
250
+ summary_json ["errors" ][testapp ]["raw_result_links" ]. append ( test .raw_result_link )
251
+ summary_json ["failures" ] = {get_name (test .testapp_path ):{"logs" : [] , "ftl_links" : [], "raw_result_links" : [], " failed_tests" : dict ()} for (test , _ ) in failures_exclude_flakiness }
252
+ for (test , results ) in failures_exclude_flakiness :
229
253
testapp = get_name (test .testapp_path )
254
+ summary_json ["failures" ][testapp ]["logs" ].append (results .summary )
230
255
if hasattr (test , "ftl_link" ) and test .ftl_link :
231
- summary_json ["failures" ][testapp ]["ftl_link" ] = test .ftl_link
256
+ summary_json ["failures" ][testapp ]["ftl_links" ]. append ( test .ftl_link )
232
257
if hasattr (test , "raw_result_link" ) and test .raw_result_link :
233
- summary_json ["failures" ][testapp ]["raw_result_link" ] = test .raw_result_link
258
+ summary_json ["failures" ][testapp ]["raw_result_links" ]. append ( test .raw_result_link )
234
259
failed_tests = re .findall (r"\[ FAILED \] (.+)[.](.+)" , results .summary )
235
260
for failed_test in failed_tests :
236
261
failed_test = failed_test [0 ] + "." + failed_test [1 ]
237
262
pattern = fr'\[ RUN \] { failed_test } (.*?)\[ FAILED \] { failed_test } '
238
263
failure_log = re .search (pattern , test .logs , re .MULTILINE | re .DOTALL )
239
264
summary_json ["failures" ][testapp ]["failed_tests" ][failed_test ] = failure_log .group ()
240
265
summary .append ("\n %s FAILED:\n %s\n " % (failed_test , failure_log .group ()))
266
+ summary_json ["flakiness" ] = {get_name (test .testapp_path ):{"logs" : [], "ftl_links" : [], "raw_result_links" : [], "flaky_tests" : dict ()} for (test , _ ) in flaky_testapps }
267
+ for (test , results ) in flaky_testapps :
268
+ testapp = get_name (test .testapp_path )
269
+ summary_json ["flakiness" ][testapp ]["logs" ].append (results .summary )
270
+ if hasattr (test , "ftl_link" ) and test .ftl_link :
271
+ summary_json ["flakiness" ][testapp ]["ftl_links" ].append (test .ftl_link )
272
+ if hasattr (test , "raw_result_link" ) and test .raw_result_link :
273
+ summary_json ["flakiness" ][testapp ]["raw_result_links" ].append (test .raw_result_link )
274
+ flaky_tests = re .findall (r"\[ FAILED \] (.+)[.](.+)" , results .summary )
275
+ for flaky_test in flaky_tests :
276
+ flaky_test = flaky_test [0 ] + "." + flaky_test [1 ]
277
+ pattern = fr'\[ RUN \] { flaky_test } (.*?)\[ FAILED \] { flaky_test } '
278
+ failure_log = re .search (pattern , test .logs , re .MULTILINE | re .DOTALL )
279
+ if failure_log :
280
+ summary_json ["flakiness" ][testapp ]["flaky_tests" ][flaky_test ] = failure_log .group ()
281
+ summary .append ("\n %s FAILED:\n %s\n " % (flaky_test , failure_log .group ()))
241
282
242
283
with open (os .path .join (summary_dir , file_name + ".json" ), "a" ) as f :
243
284
f .write (json .dumps (summary_json , indent = 2 ))
244
285
245
286
summary = "\n " .join (summary )
246
287
write_summary (summary_dir , summary , file_name )
247
288
248
- return 0 if len (tests ) == len (successes ) else 1
289
+ # success or only flakiness
290
+ if len (tests ) == len (successes ) or len (flaky_testapps ) == len (failures ) + len (errors ):
291
+ return 0
292
+ else :
293
+ return 1
249
294
250
295
251
296
def write_summary (testapp_dir , summary , file_name = "summary.log" ):
0 commit comments