Skip to content

Commit 8aa6b41

Browse files
srujzsCommit Queue
authored andcommitted
[ddc] Update hot reload test suite to use recompile-restart
A new optional suffix to the file names is added: '.restart'. This allows us to differentiate between hot reload generations and hot restart generations. All files with the same generation should either have the suffix or don't. If present, uses the recompile-request instruction in the frontend server. If not, uses the recompile instruction. Tests are renamed and a WIP README is added to document this change. Change-Id: I2691c5ec0b7336115a76dc0e0369213e74b6ea21 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406183 Commit-Queue: Srujan Gaddam <[email protected]> Reviewed-by: Nicholas Shahan <[email protected]>
1 parent 4617198 commit 8aa6b41

File tree

21 files changed

+75
-31
lines changed

21 files changed

+75
-31
lines changed

pkg/dev_compiler/test/hot_reload_suite.dart

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,12 @@ class HotReloadTest {
169169
// TODO(nshahan): Support multiple expected errors for a single generation.
170170
final Map<int, String> expectedErrors;
171171

172+
/// Map of generation number to whether a hot restart was triggered before the
173+
/// generation.
174+
final Map<int, bool> isHotRestart;
175+
172176
HotReloadTest(this.directory, this.name, this.generationCount, this.files,
173-
ReloadTestConfiguration config)
177+
ReloadTestConfiguration config, this.isHotRestart)
174178
: excludedPlatforms = config.excludedPlatforms,
175179
expectedErrors = config.expectedErrors;
176180

@@ -376,8 +380,9 @@ abstract class HotReloadSuiteRunner {
376380
r'^(?<name>[\w,-]+)'
377381
// Followed by a dot and 1 or more digits.
378382
r'\.(?<generation>\d+)'
379-
// Optionally a dot and the word 'reject'.
380-
r'(?<reject>\.reject)?'
383+
// Optionally a dot and either the word 'restart' indicating a hot
384+
// restart, or the word 'reject', indicating a hot reload rejection.
385+
r'((?<restart>\.restart)|(?<reject>\.reject))?'
381386
// Ending with a dot and the word 'dart'
382387
r'\.dart$');
383388
final testSuite = <HotReloadTest>[];
@@ -409,6 +414,7 @@ abstract class HotReloadSuiteRunner {
409414
label: testName);
410415
continue;
411416
}
417+
final isHotRestart = <int, bool>{};
412418
final expectedErrors = testConfig.expectedErrors;
413419
final dartFiles = testDir
414420
.listSync()
@@ -420,15 +426,26 @@ abstract class HotReloadSuiteRunner {
420426
final matches = validTestSourceName.allMatches(fileName);
421427
if (matches.length != 1) {
422428
throw Exception('Invalid test source file name: $fileName\n'
423-
'Valid names look like "file_name.10.dart" '
424-
'or "file_name.10.reject.dart".');
429+
'Valid names look like "file_name.10.dart", '
430+
'"file_name.10.restart.dart" or "file_name.10.reject.dart".');
425431
}
426432
final match = matches.single;
427433
final name = match.namedGroup('name');
428434
final restoredName = '$name.dart';
429435
final generation = int.parse(match.namedGroup('generation')!);
430436
maxGenerations = max(maxGenerations, generation);
437+
final restart = match.namedGroup('restart') != null;
438+
if (!isHotRestart.containsKey(generation)) {
439+
isHotRestart[generation] = restart;
440+
} else {
441+
if (restart != isHotRestart[generation]) {
442+
throw Exception('Expected all files for generation $generation to '
443+
"be consistent about having a '.restart' suffix, but $fileName "
444+
'does not match other files in the same generation.');
445+
}
446+
}
431447
final rejectExpected = match.namedGroup('reject') != null;
448+
assert(!(rejectExpected && restart));
432449
if (rejectExpected && !expectedErrors.containsKey(generation)) {
433450
throw Exception(
434451
'Expected error for generation file missing from config.json: '
@@ -465,8 +482,8 @@ abstract class HotReloadSuiteRunner {
465482
{for (final edit in fileEdits) edit.generation: edit});
466483
testFiles.add(TestFile(fileName, editsByGeneration));
467484
}
468-
testSuite.add(HotReloadTest(
469-
testDir, testName, maxGenerations + 1, testFiles, testConfig));
485+
testSuite.add(HotReloadTest(testDir, testName, maxGenerations + 1,
486+
testFiles, testConfig, isHotRestart));
470487
}
471488
return testSuite;
472489
}
@@ -986,7 +1003,8 @@ abstract class DdcSuiteRunner extends HotReloadSuiteRunner {
9861003
outputDillPath = outputIncrementalDillUri.toFilePath();
9871004
compilerOutput = await controller.sendRecompile(
9881005
snapshotEntrypointWithScheme,
989-
invalidatedFiles: updatedFiles);
1006+
invalidatedFiles: updatedFiles,
1007+
recompileRestart: test.isHotRestart[generation]!);
9901008
}
9911009
final expectedError = test.expectedErrors[generation];
9921010
if (compilerOutput.errorCount > 0) {
@@ -1289,7 +1307,10 @@ class VMSuiteRunner extends HotReloadSuiteRunner {
12891307
// TODO(markzipan): Add logic to reject bad compiles.
12901308
compilerOutput = await controller.sendRecompile(
12911309
snapshotEntrypointWithScheme,
1292-
invalidatedFiles: updatedFiles);
1310+
invalidatedFiles: updatedFiles,
1311+
// The VM never uses the `recompile-restart` instruction as it does
1312+
// not recompile during a hot restart.
1313+
recompileRestart: false);
12931314
}
12941315
var hasExpectedCompileError = false;
12951316
final expectedError = test.expectedErrors[generation];

pkg/reload_test/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Testing Framework for Hot Reload/Restart Tests for DDC
2+
3+
<!--- TODO(srujzs): Document this package more. For now, document some
4+
conventions that may not be obvious. --->
5+
6+
This package contains utilities to test multiple generations of files in order
7+
to validate hot reload and hot restart.
8+
9+
## Test File Conventions
10+
11+
- Different generations of files have a generation number after the file name
12+
e.g. `file_name.1.dart` is the generation 1-version of the file.
13+
- If a generation file is intended to be rejected, it should contain `.reject`
14+
in the file name. The `config.json` file should contain a key `"expectedErrors"`
15+
with its value being a map of generation number string to the error string.
16+
- If a generation does a hot restart instead of a reload, it should contain
17+
`.restart` in every file name in the same generation.
18+
- It is an error to specify both `.reject` and `.restart`.

pkg/reload_test/lib/frontend_server_controller.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@ class HotReloadFrontendServerController {
227227

228228
Future<CompilerOutput> sendRecompile(String entrypointPath,
229229
{List<String> invalidatedFiles = const [],
230-
String boundaryKey = fakeBoundaryKey}) async {
230+
String boundaryKey = fakeBoundaryKey,
231+
required bool recompileRestart}) async {
231232
// Currently the `FrontendCompiler` used in this test suite clears errors
232233
// before performing a recompile but not an initial compile. Since we reuse
233234
// the same instance and issue an initial compile request for each test we
@@ -237,7 +238,8 @@ class HotReloadFrontendServerController {
237238
totalErrors = 0;
238239
if (!started) throw Exception('Frontend Server has not been started yet.');
239240
_state = FrontendServerState.awaitingResult;
240-
final command = 'recompile $entrypointPath $boundaryKey\n'
241+
final instruction = recompileRestart ? 'recompile-restart' : 'recompile';
242+
final command = '$instruction $entrypointPath $boundaryKey\n'
241243
'${invalidatedFiles.join('\n')}\n$boundaryKey\n';
242244
if (debug) print('Sending instruction to Frontend Server:\n$command');
243245
input.add(command.codeUnits);
@@ -247,9 +249,12 @@ class HotReloadFrontendServerController {
247249

248250
Future<void> sendRecompileAndAccept(String entrypointPath,
249251
{List<String> invalidatedFiles = const [],
250-
String boundaryKey = fakeBoundaryKey}) async {
252+
String boundaryKey = fakeBoundaryKey,
253+
required bool recompileRestart}) async {
251254
await sendRecompile(entrypointPath,
252-
invalidatedFiles: invalidatedFiles, boundaryKey: boundaryKey);
255+
invalidatedFiles: invalidatedFiles,
256+
boundaryKey: boundaryKey,
257+
recompileRestart: recompileRestart);
253258
sendAccept();
254259
}
255260

tests/hot_reload/framework_timing_test/main.0.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

tests/hot_reload/framework_timing_test/main.1.dart renamed to tests/hot_reload/framework_timing_test/main.1.restart.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

tests/hot_reload/framework_timing_test/main.2.dart renamed to tests/hot_reload/framework_timing_test/main.2.restart.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

tests/hot_reload/hot_restart_constant_equality/library_b.0.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

tests/hot_reload/hot_restart_constant_equality/library_b.1.dart renamed to tests/hot_reload/hot_restart_constant_equality/library_b.1.restart.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

tests/hot_reload/hot_restart_constant_equality/main.0.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

tests/hot_reload/hot_restart_constant_equality/main.1.dart renamed to tests/hot_reload/hot_restart_constant_equality/main.1.restart.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

0 commit comments

Comments
 (0)