44
55import 'dart:async' ;
66import 'dart:convert' ;
7+ // import 'dart:core' as core;
78import 'dart:io' ;
89
910import 'package:flutter_devicelab/common.dart' ;
@@ -15,6 +16,16 @@ import 'devices.dart';
1516import 'task_result.dart' ;
1617import 'utils.dart' ;
1718
19+ /// Run a list of tasks.
20+ ///
21+ /// For each task, an auto rerun will be triggered when task fails.
22+ ///
23+ /// If the task succeeds the first time, it will be recorded as successful.
24+ ///
25+ /// If the task fails first, but gets passed in the end, the
26+ /// test will be recorded as successful but with a flake flag.
27+ ///
28+ /// If the task fails all reruns, it will be recorded as failed.
1829Future <void > runTasks (
1930 List <String > taskNames, {
2031 bool exitOnFirstTestFailure = false ,
@@ -26,33 +37,45 @@ Future<void> runTasks(
2637 String ? luciBuilder,
2738 String ? resultsPath,
2839 List <String >? taskArgs,
40+ @visibleForTesting Map <String , String >? isolateParams,
41+ @visibleForTesting Function (String ) print = print,
42+ @visibleForTesting List <String >? logs,
2943}) async {
3044 for (final String taskName in taskNames) {
31- section ('Running task "$taskName "' );
32- final TaskResult result = await runTask (
33- taskName,
34- deviceId: deviceId,
35- localEngine: localEngine,
36- localEngineSrcPath: localEngineSrcPath,
37- silent: silent,
38- taskArgs: taskArgs,
39- );
40-
41- print ('Task result:' );
42- print (const JsonEncoder .withIndent (' ' ).convert (result));
43- section ('Finished task "$taskName "' );
44-
45- if (resultsPath != null ) {
46- final Cocoon cocoon = Cocoon ();
47- await cocoon.writeTaskResultToFile (
48- builderName: luciBuilder,
49- gitBranch: gitBranch,
50- result: result,
45+ TaskResult result = TaskResult .success (null );
46+ int retry = 0 ;
47+ while (retry <= Cocoon .retryNumber) {
48+ result = await rerunTask (
49+ taskName,
50+ deviceId: deviceId,
51+ localEngine: localEngine,
52+ localEngineSrcPath: localEngineSrcPath,
53+ silent: silent,
54+ taskArgs: taskArgs,
5155 resultsPath: resultsPath,
56+ gitBranch: gitBranch,
57+ luciBuilder: luciBuilder,
58+ isolateParams: isolateParams,
5259 );
60+
61+ section ('Flaky status for "$taskName "' );
62+ if (! result.succeeded) {
63+ retry++ ;
64+ } else {
65+ if (retry > 0 ) {
66+ print ('Total ${retry +1 } executions: $retry failures and 1 success' );
67+ print ('flaky: true' );
68+ } else {
69+ print ('Total ${retry +1 } executions: 1 success' );
70+ print ('flaky: false' );
71+ }
72+ break ;
73+ }
5374 }
5475
5576 if (! result.succeeded) {
77+ print ('Total $retry executions: 0 success' );
78+ print ('flaky: false' );
5679 exitCode = 1 ;
5780 if (exitOnFirstTestFailure) {
5881 return ;
@@ -61,6 +84,48 @@ Future<void> runTasks(
6184 }
6285}
6386
87+ /// A rerun wrapper for `runTask` .
88+ ///
89+ /// This separates reruns in separate sections.
90+ Future <TaskResult > rerunTask (
91+ String taskName, {
92+ String ? deviceId,
93+ String ? localEngine,
94+ String ? localEngineSrcPath,
95+ bool silent = false ,
96+ List <String >? taskArgs,
97+ String ? resultsPath,
98+ String ? gitBranch,
99+ String ? luciBuilder,
100+ @visibleForTesting Map <String , String >? isolateParams,
101+ }) async {
102+ section ('Running task "$taskName "' );
103+ final TaskResult result = await runTask (
104+ taskName,
105+ deviceId: deviceId,
106+ localEngine: localEngine,
107+ localEngineSrcPath: localEngineSrcPath,
108+ silent: silent,
109+ taskArgs: taskArgs,
110+ isolateParams: isolateParams,
111+ );
112+
113+ print ('Task result:' );
114+ print (const JsonEncoder .withIndent (' ' ).convert (result));
115+ section ('Finished task "$taskName "' );
116+
117+ if (resultsPath != null ) {
118+ final Cocoon cocoon = Cocoon ();
119+ await cocoon.writeTaskResultToFile (
120+ builderName: luciBuilder,
121+ gitBranch: gitBranch,
122+ result: result,
123+ resultsPath: resultsPath,
124+ );
125+ }
126+ return result;
127+ }
128+
64129/// Runs a task in a separate Dart VM and collects the result using the VM
65130/// service protocol.
66131///
0 commit comments