Skip to content

Commit 0e51886

Browse files
committed
Await main in hot restart tests to fix some race conditions
- Recompile is needed for the frontend server even when no edits are made - Also use expectAll in hot_restart_breakpoints_test
1 parent aa54169 commit 0e51886

File tree

3 files changed

+74
-14
lines changed

3 files changed

+74
-14
lines changed

dwds/test/common/hot_restart_common.dart

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
@Timeout(Duration(minutes: 5))
88
library;
99

10+
import 'dart:async';
11+
1012
import 'package:dwds/dwds.dart';
1113
import 'package:dwds/expression_compiler.dart';
1214
import 'package:test/test.dart';
@@ -32,26 +34,54 @@ void runTests({
3234

3335
tearDownAll(provider.dispose);
3436

35-
Future<void> makeEditAndRecompile() async {
36-
context.makeEditToDartEntryFile(
37-
toReplace: originalString,
38-
replaceWith: newString,
39-
);
37+
Future<void> recompile({bool hasEdits = false}) async {
4038
if (compilationMode == CompilationMode.frontendServer) {
4139
await context.recompile(fullRestart: true);
4240
} else {
4341
assert(compilationMode == CompilationMode.buildDaemon);
44-
await context.waitForSuccessfulBuild(propagateToBrowser: true);
42+
if (hasEdits) {
43+
// Only gets a new build if there were edits.
44+
await context.waitForSuccessfulBuild(propagateToBrowser: true);
45+
}
4546
}
4647
}
4748

49+
Future<void> makeEditAndRecompile() async {
50+
context.makeEditToDartEntryFile(
51+
toReplace: originalString,
52+
replaceWith: newString,
53+
);
54+
await recompile(hasEdits: true);
55+
}
56+
4857
void undoEdit() {
4958
context.makeEditToDartEntryFile(
5059
toReplace: newString,
5160
replaceWith: originalString,
5261
);
5362
}
5463

64+
/// Wait for main to finish executing before checking expectations by checking
65+
/// for a log output.
66+
///
67+
/// If [debuggingEnabled] is false, we can't check for Chrome logs and instead
68+
/// wait 1 second.
69+
// TODO(srujzs): We should do something less prone to race conditions when
70+
// debugging is disabled.
71+
Future<void> waitForMainToExecute({bool debuggingEnabled = true}) async {
72+
if (!debuggingEnabled) return Future.delayed(const Duration(seconds: 1));
73+
final completer = Completer<void>();
74+
final expectedString = 'main executed';
75+
final subscription = context.webkitDebugger.onConsoleAPICalled.listen((e) {
76+
if (e.args.first.value == expectedString) {
77+
completer.complete();
78+
}
79+
});
80+
await completer.future.then((_) {
81+
subscription.cancel();
82+
});
83+
}
84+
5585
group(
5686
'Injected client with live reload',
5787
() {
@@ -74,7 +104,9 @@ void runTests({
74104
});
75105

76106
test('can live reload changes ', () async {
107+
final mainDone = waitForMainToExecute();
77108
await makeEditAndRecompile();
109+
await mainDone;
78110
final source = await context.webDriver.pageSource;
79111

80112
// A full reload should clear the state.
@@ -105,8 +137,9 @@ void runTests({
105137
});
106138

107139
test('can live reload changes ', () async {
140+
final mainDone = waitForMainToExecute(debuggingEnabled: false);
108141
await makeEditAndRecompile();
109-
142+
await mainDone;
110143
final source = await context.webDriver.pageSource;
111144

112145
// A full reload should clear the state.
@@ -138,8 +171,9 @@ void runTests({
138171
});
139172

140173
test('can live reload changes ', () async {
174+
final mainDone = waitForMainToExecute(debuggingEnabled: false);
141175
await makeEditAndRecompile();
142-
176+
await mainDone;
143177
final source = await context.webDriver.pageSource;
144178

145179
// A full reload should clear the state.
@@ -281,13 +315,15 @@ void runTests({
281315
]),
282316
),
283317
);
318+
final mainDone = waitForMainToExecute();
284319
final hotRestart = context.getRegisteredServiceExtension('hotRestart');
285320
expect(
286321
await fakeClient.callServiceExtension(hotRestart!),
287322
const TypeMatcher<Success>(),
288323
);
289324

290325
await eventsDone;
326+
await mainDone;
291327

292328
final source = await context.webDriver.pageSource;
293329
// Main is re-invoked which shouldn't clear the state.
@@ -324,6 +360,8 @@ void runTests({
324360
"registerExtension('ext.foo', $callback)",
325361
);
326362

363+
await recompile();
364+
final mainDone = waitForMainToExecute();
327365
final hotRestart = context.getRegisteredServiceExtension('hotRestart');
328366
expect(
329367
await fakeClient.callServiceExtension(hotRestart!),
@@ -342,6 +380,7 @@ void runTests({
342380
);
343381

344382
await eventsDone;
383+
await mainDone;
345384

346385
final source = await context.webDriver.pageSource;
347386
// Main is re-invoked which shouldn't clear the state.
@@ -363,14 +402,15 @@ void runTests({
363402
]),
364403
),
365404
);
366-
405+
final mainDone = waitForMainToExecute();
367406
final fullReload = context.getRegisteredServiceExtension('fullReload');
368407
expect(
369408
await fakeClient.callServiceExtension(fullReload!),
370409
isA<Success>(),
371410
);
372411

373412
await eventsDone;
413+
await mainDone;
374414

375415
final source = await context.webDriver.pageSource;
376416
// Should see only the new text
@@ -399,8 +439,12 @@ void runTests({
399439
);
400440

401441
await makeEditAndRecompile();
442+
final mainDone = waitForMainToExecute();
402443
final hotRestart = context.getRegisteredServiceExtension('hotRestart');
403444
await fakeClient.callServiceExtension(hotRestart!);
445+
446+
await mainDone;
447+
404448
final source = await context.webDriver.pageSource;
405449

406450
// Main is re-invoked which shouldn't clear the state.
@@ -418,6 +462,7 @@ void runTests({
418462
test('can evaluate expressions after hot restart', () async {
419463
final client = context.debugConnection.vmService;
420464

465+
await recompile();
421466
final hotRestart = context.getRegisteredServiceExtension('hotRestart');
422467
await fakeClient.callServiceExtension(hotRestart!);
423468

@@ -461,8 +506,9 @@ void runTests({
461506
});
462507

463508
test('can hot restart changes ', () async {
509+
final mainDone = waitForMainToExecute();
464510
await makeEditAndRecompile();
465-
511+
await mainDone;
466512
final source = await context.webDriver.pageSource;
467513

468514
// Main is re-invoked which shouldn't clear the state.
@@ -521,8 +567,9 @@ void runTests({
521567
});
522568

523569
test('can hot restart changes ', () async {
570+
final mainDone = waitForMainToExecute(debuggingEnabled: false);
524571
await makeEditAndRecompile();
525-
572+
await mainDone;
526573
final source = await context.webDriver.pageSource;
527574

528575
// Main is re-invoked which shouldn't clear the state.
@@ -582,6 +629,7 @@ void runTests({
582629
),
583630
);
584631

632+
final mainDone = waitForMainToExecute();
585633
final hotRestart = context.getRegisteredServiceExtension('hotRestart');
586634
expect(
587635
await fakeClient.callServiceExtension(hotRestart!),
@@ -597,6 +645,8 @@ void runTests({
597645
final isolateId = vm.isolates!.first.id!;
598646
await client.resume(isolateId);
599647

648+
await mainDone;
649+
600650
final sourceAfterResume = await context.webDriver.pageSource;
601651
expect(sourceAfterResume.contains(newString), isTrue);
602652
},
@@ -605,8 +655,8 @@ void runTests({
605655
test(
606656
'after page refresh, does not run app until there is a resume event',
607657
() async {
658+
final mainDone = waitForMainToExecute();
608659
await makeEditAndRecompile();
609-
610660
await context.webDriver.driver.refresh();
611661

612662
final eventsDone = expectLater(
@@ -629,6 +679,8 @@ void runTests({
629679
final isolateId = vm.isolates!.first.id!;
630680
await client.resume(isolateId);
631681

682+
await mainDone;
683+
632684
final sourceAfterResume = await context.webDriver.pageSource;
633685
expect(sourceAfterResume.contains(newString), isTrue);
634686
},

dwds/test/hot_restart_breakpoints_test.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,9 @@ void main() {
181181
),
182182
);
183183

184-
final waitForPausePost = stream.firstWhere(
185-
(event) => event.kind == EventKind.kPausePostRequest,
184+
final waitForPausePost = expectLater(
185+
stream,
186+
emitsThrough(_hasKind(EventKind.kPausePostRequest)),
186187
);
187188

188189
final hotRestart = context.getRegisteredServiceExtension('hotRestart');

fixtures/_testSound/example/append_body/main.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import 'dart:developer';
77
// TODO: https://github.com/dart-lang/webdev/issues/2508
88
// ignore: deprecated_member_use
99
import 'dart:html';
10+
import 'dart:js_interop';
11+
12+
@JS('console.log')
13+
external void log(String _);
1014

1115
void main() {
1216
var count = 0;
@@ -23,4 +27,7 @@ void main() {
2327
document.body?.appendText('end disassemble ');
2428
return ServiceExtensionResponse.result('{}');
2529
});
30+
31+
// Wait for this print statement so that we know main is done executing.
32+
log('main executed');
2633
}

0 commit comments

Comments
 (0)