Skip to content

Commit be63e3f

Browse files
liamappelbeCommit Queue
authored andcommitted
Reapply "[test_runner] Improve timeout deflaking"
The fix is in https://dart-review.googlesource.com/c/sdk/+/441460/1..2 The issue was that the TestConfiguration.selectors field was set incorrectly, because _expandSelectors was still reading configuration['test-list-contents'] without accounting for the format changes. It just needs the test name list, so the fix is to pass in the already parsed testList variable. Example build: https://ci.chromium.org/ui/p/dart/builders/try/vm-aot-linux-release-x64-try/7213/overview compare_results output: https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8708774547991696017/+/u/deflaking/list_tests_to_deflake__vm_tests_/raw_io.output_text Deflaking run flags: https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8708774547991696017/+/u/deflaking/vm_tests/l_execution_details Deflaking run output: https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8708774547991696017/+/u/deflaking/vm_tests/stdout This reverts commit cc5f351. TEST=CI Bug: #55044 Change-Id: Ie9ff1ba71f44ade7b4cd1c9b2f75b9efc1dee24a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/441460 Commit-Queue: Liam Appelbe <[email protected]> Reviewed-by: Alexander Thomas <[email protected]>
1 parent 5ef1c08 commit be63e3f

File tree

9 files changed

+102
-22
lines changed

9 files changed

+102
-22
lines changed

pkg/test_runner/bin/compare_results.dart

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
// readable mode that explains the results and how they changed.
99

1010
import 'dart:collection';
11+
import 'dart:convert';
1112
import 'dart:io';
1213

1314
import 'package:args/args.dart';
1415
import 'package:test_runner/bot_results.dart';
16+
import 'package:test_runner/src/deflake_info.dart';
1517

1618
class Event {
1719
final Result? before;
@@ -50,6 +52,18 @@ class Event {
5052
throw Exception("Unreachable");
5153
}
5254
}
55+
56+
String deflakeInfo(String name) {
57+
final isTimeout = after.outcome == 'Timeout';
58+
final lastTimeMs = before?.timeMs;
59+
return jsonEncode(DeflakeInfo(
60+
name: name,
61+
repeat: isTimeout ? 2 : 5,
62+
timeout: isTimeout && lastTimeMs != null
63+
? ((2 * lastTimeMs) / 1000).ceil()
64+
: -1,
65+
).toJson());
66+
}
5367
}
5468

5569
class Options {
@@ -70,6 +84,7 @@ class Options {
7084
Iterable<String> get statusFilter => ["passing", "flaky", "failing"]
7185
.where((option) => _options[option] as bool);
7286
bool get unchanged => _options["unchanged"] as bool;
87+
bool get nameOnly => _options["name-only"] as bool;
7388
bool get verbose => _options["verbose"] as bool;
7489
List<String> get rest => _options.rest;
7590
}
@@ -148,8 +163,10 @@ bool search(
148163
"${before?.matches} ${after.matches} "
149164
"${before?.flaked} ${after.flaked}";
150165
}
151-
} else {
166+
} else if (options.nameOnly) {
152167
output = name;
168+
} else {
169+
output = event.deflakeInfo(name);
153170
}
154171
final log = logs[event.after.key];
155172
final bar = '=' * (output.length + 2);
@@ -191,6 +208,8 @@ void main(List<String> args) async {
191208
abbr: 'u',
192209
negatable: false,
193210
help: "Show only tests with unchanged results.");
211+
parser.addFlag("name-only",
212+
help: "Only show the test names.", negatable: false);
194213
parser.addFlag("verbose",
195214
abbr: "v",
196215
help: "Show the old and new result for each test",

pkg/test_runner/lib/bot_results.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Result {
3030
final bool flaked; // From optional flakiness_data argument to constructor.
3131
final bool? isFlaky; // From results.json after it is extended.
3232
final String? previousOutcome;
33+
final int? timeMs;
3334

3435
Result(
3536
this.configuration,
@@ -40,7 +41,8 @@ class Result {
4041
this.changed,
4142
this.commitHash,
4243
this.isFlaky,
43-
this.previousOutcome, {
44+
this.previousOutcome,
45+
this.timeMs, {
4446
this.flaked = false,
4547
});
4648

@@ -56,6 +58,7 @@ class Result {
5658
commitHash = map["commit_hash"] as String?,
5759
isFlaky = map["flaky"] as bool?,
5860
previousOutcome = map["previous_result"] as String?,
61+
timeMs = map["timeMs"] as int?,
5962
flaked = flakinessData != null &&
6063
(flakinessData["active"] ?? true) == true &&
6164
(flakinessData["outcomes"] as List).contains(map["result"]);

pkg/test_runner/lib/src/configuration.dart

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:smith/configuration.dart';
1111
import 'package:smith/smith.dart';
1212

1313
import 'compiler_configuration.dart';
14+
import 'deflake_info.dart';
1415
import 'feature.dart';
1516
import 'path.dart';
1617
import 'repository.dart';
@@ -31,7 +32,8 @@ class TestConfiguration {
3132
this.selectors = const {},
3233
this.build = false,
3334
this.testList = const [],
34-
this.repeat = 1,
35+
this.deflakeInfoMap = const {},
36+
int repeat = 1,
3537
this.batch = false,
3638
this.copyCoreDumps = false,
3739
this.rr = false,
@@ -74,7 +76,8 @@ class TestConfiguration {
7476
: packages = packages ??
7577
Repository.uri
7678
.resolve('.dart_tool/package_config.json')
77-
.toFilePath();
79+
.toFilePath(),
80+
_repeat = repeat;
7881

7982
final Map<String, RegExp?> selectors;
8083
final Progress progress;
@@ -136,11 +139,12 @@ class TestConfiguration {
136139
final String? dartPrecompiledPath;
137140
final String? genSnapshotPath;
138141
final List<String>? testList;
142+
final Map<String, DeflakeInfo> deflakeInfoMap;
139143

140144
final int taskCount;
141145
final int shardCount;
142146
final int shard;
143-
final int repeat;
147+
final int _repeat;
144148

145149
final int testServerPort;
146150
final int testServerCrossOriginPort;
@@ -221,15 +225,15 @@ class TestConfiguration {
221225
/// build/ReleaseX64
222226
String get buildDirectory => system.outputDirectory + configurationDirectory;
223227

224-
int? _timeout;
228+
int? _defaultTimeout;
225229

226230
// TODO(whesse): Put non-default timeouts explicitly in configs, not this.
227231
/// Calculates a default timeout based on the compiler and runtime used,
228232
/// and the mode, architecture, etc.
229-
int get timeout {
230-
if (_timeout == null) {
233+
int get defaultTimeout {
234+
if (_defaultTimeout == null) {
231235
if (configuration.timeout > 0) {
232-
_timeout = configuration.timeout;
236+
_defaultTimeout = configuration.timeout;
233237
} else {
234238
var isReload = hotReload || hotReloadRollback;
235239

@@ -241,13 +245,22 @@ class TestConfiguration {
241245
arch: architecture,
242246
system: system);
243247

244-
_timeout = 30 * compilerMultiplier * runtimeMultiplier;
248+
_defaultTimeout = 30 * compilerMultiplier * runtimeMultiplier;
245249
}
246250
}
247251

248-
return _timeout!;
252+
return _defaultTimeout!;
249253
}
250254

255+
/// Returns the timeout for the given test name.
256+
int timeout(String name) {
257+
final t = deflakeInfoMap[name]?.timeout ?? -1;
258+
return t >= 0 ? t : defaultTimeout;
259+
}
260+
261+
/// Returns the repeat count for the given test name.
262+
int repeat(String name) => deflakeInfoMap[name]?.repeat ?? _repeat;
263+
251264
List<String> get standardOptions {
252265
if (compiler != Compiler.dart2js) {
253266
return const ["--ignore-unrecognized-flags"];
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// During deflaking, each test can be run with a custom timeout and repetition
6+
/// count.
7+
class DeflakeInfo {
8+
String name;
9+
int repeat;
10+
int timeout;
11+
12+
DeflakeInfo(
13+
{required this.name, required this.repeat, required this.timeout});
14+
15+
Map<dynamic, dynamic> toJson() => {
16+
'name': name,
17+
'repeat': repeat,
18+
'timeout': timeout,
19+
};
20+
21+
static DeflakeInfo fromJson(Map<dynamic, dynamic> json) => DeflakeInfo(
22+
name: json['name'] as String,
23+
repeat: json['repeat'] as int? ?? 5,
24+
timeout: json['timeout'] as int? ?? -1);
25+
}

pkg/test_runner/lib/src/options.dart

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:args/args.dart';
1111
import 'package:path/path.dart' as path;
1212

1313
import 'configuration.dart';
14+
import 'deflake_info.dart';
1415
import 'path.dart';
1516
import 'repository.dart';
1617
import 'test_configurations.dart';
@@ -626,12 +627,16 @@ has been specified on the command line.''')
626627

627628
void addConfiguration(Configuration innerConfiguration,
628629
[String? namedConfiguration]) {
630+
final (testList, deflakeInfoMap) =
631+
parseTestList(data["test-list-contents"] as List<String>?);
632+
629633
var configuration = TestConfiguration(
630634
configuration: innerConfiguration,
631635
progress: progress,
632-
selectors: _expandSelectors(data),
636+
selectors: _expandSelectors(data, testList),
633637
build: data["build"] as bool,
634-
testList: data["test-list-contents"] as List<String>?,
638+
testList: testList,
639+
deflakeInfoMap: deflakeInfoMap ?? const {},
635640
repeat: int.parse(data["repeat"] as String),
636641
batch: !(data["no-batch"] as bool),
637642
copyCoreDumps: data["copy-coredumps"] as bool,
@@ -812,18 +817,15 @@ has been specified on the command line.''')
812817
///
813818
/// If no selectors are explicitly given, uses the default suite patterns.
814819
Map<String, RegExp> _expandSelectors(
815-
Map<String, dynamic> configuration) {
820+
Map<String, dynamic> configuration, List<String>? testList) {
816821
var selectors = configuration['selectors'] as List<String>? ?? [];
817822

818823
if (selectors.isEmpty || configuration['default-suites'] as bool) {
819824
if (configuration['suite-dir'] != null) {
820825
var suitePath = Path(configuration['suite-dir'] as String);
821826
selectors.add(suitePath.filename);
822-
} else if (configuration['test-list-contents'] != null) {
823-
selectors = (configuration['test-list-contents'] as List<String>)
824-
.map((t) => t.split('/').first)
825-
.toSet()
826-
.toList();
827+
} else if (testList != null) {
828+
selectors = testList.map((t) => t.split('/').first).toSet().toList();
827829
} else {
828830
selectors.addAll(_defaultTestSelectors);
829831
}
@@ -1009,3 +1011,16 @@ final Map<String, String> sanitizerEnvironmentVariables = (() {
10091011

10101012
return environment;
10111013
})();
1014+
1015+
(List<String>?, Map<String, DeflakeInfo>?) parseTestList(List<String>? raw) {
1016+
final isJson = raw != null && raw.isNotEmpty && raw[0].startsWith('{');
1017+
if (!isJson) return (raw, null);
1018+
final deflakes = <DeflakeInfo>[
1019+
for (var line in raw)
1020+
DeflakeInfo.fromJson(jsonDecode(line) as Map<dynamic, dynamic>),
1021+
];
1022+
return (
1023+
[for (var i in deflakes) i.name],
1024+
{for (var i in deflakes) i.name: i},
1025+
);
1026+
}

pkg/test_runner/lib/src/process_queue.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ class TestCaseEnqueuer {
253253
/// test completing successfully, just on it completing.
254254
void _add(TestCase testCase) {
255255
Node<Command>? lastNode;
256-
for (var i = 0; i < testCase.configuration.repeat; ++i) {
256+
for (var i = 0; i < testCase.repeat; ++i) {
257257
if (i > 0) {
258258
testCase = testCase.indexedCopy(i);
259259
}

pkg/test_runner/lib/src/test_case.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class TestCase {
133133
}
134134

135135
int get timeout {
136-
var result = configuration.timeout;
136+
var result = configuration.timeout(displayName);
137137
if (expectedOutcomes.contains(Expectation.slow)) {
138138
result *= _slowTimeoutMultiplier;
139139
} else if (expectedOutcomes.contains(Expectation.extraSlow)) {
@@ -142,6 +142,8 @@ class TestCase {
142142
return result;
143143
}
144144

145+
int get repeat => configuration.repeat(displayName);
146+
145147
String get configurationString {
146148
var compiler = configuration.compiler.name;
147149
var runtime = configuration.runtime.name;

pkg/test_runner/lib/test_runner.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ Future<void> deflake(Directory outDirectory, List<String> configurations,
577577
// Find the list of tests to deflake.
578578
var deflakeListOutput = await runProcess(Platform.resolvedExecutable, [
579579
"tools/bots/compare_results.dart",
580+
"--name-only",
580581
"--changed",
581582
"--failing",
582583
"--passing",

pkg/test_runner/test/compare_results/compare_results_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ Result _result(
197197
String commitHash = 'abcdabcd',
198198
bool flaked = false,
199199
bool isFlaky = false,
200-
String previousOutcome = 'Pass'}) {
200+
String previousOutcome = 'Pass',
201+
int timeMs = -1}) {
201202
return Result(
202203
configuration,
203204
name,
@@ -208,6 +209,7 @@ Result _result(
208209
commitHash,
209210
isFlaky,
210211
previousOutcome,
212+
timeMs,
211213
flaked: flaked,
212214
);
213215
}

0 commit comments

Comments
 (0)