Skip to content

Commit a3e859c

Browse files
jyameosrujzs
authored andcommitted
Create Hot Restart over websocket test (flutter#173852)
- Create hot restart over websocket test - refactored websocket_dwds_test_common Closes dart-lang/webdev#2669 Blocked by hot restart bug: Fix in dart-lang/webdev#2668 and flutter#173777 - This PR can be landed once the bug above is fixed in dwds and the dwds version is updated --------- Co-authored-by: Srujan Gaddam <[email protected]>
1 parent c578fed commit a3e859c

File tree

3 files changed

+288
-122
lines changed

3 files changed

+288
-122
lines changed

packages/flutter_tools/test/integration.shard/hot_reload_websocket_test.dart

Lines changed: 13 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66
library;
77

88
import 'dart:async';
9-
import 'dart:io' as io;
109

1110
import 'package:file/file.dart';
12-
import 'package:flutter_tools/src/web/chrome.dart';
13-
import 'package:flutter_tools/src/web/web_device.dart' show WebServerDevice;
1411

1512
import '../src/common.dart';
1613
import 'test_data/hot_reload_project.dart';
14+
import 'test_data/websocket_dwds_test_common.dart';
1715
import 'test_driver.dart';
1816
import 'test_utils.dart';
1917
import 'transition_test_utils.dart';
@@ -26,8 +24,6 @@ void testAll({List<String> additionalCommandArgs = const <String>[]}) {
2624
group('WebSocket DWDS connection'
2725
'${additionalCommandArgs.isEmpty ? '' : ' with args: $additionalCommandArgs'}', () {
2826
// Test configuration constants
29-
const debugUrlTimeout = Duration(seconds: 20);
30-
const appStartTimeout = Duration(seconds: 15);
3127
const hotReloadTimeout = Duration(seconds: 10);
3228

3329
late Directory tempDir;
@@ -48,62 +44,17 @@ void testAll({List<String> additionalCommandArgs = const <String>[]}) {
4844
testWithoutContext(
4945
'hot reload with headless Chrome WebSocket connection',
5046
() async {
51-
debugPrint('Starting WebSocket DWDS test with headless Chrome...');
52-
53-
// Set up listening for app output before starting
54-
final stdout = StringBuffer();
55-
final sawDebugUrl = Completer<String>();
56-
final StreamSubscription<String> subscription = flutter.stdout.listen((String e) {
57-
stdout.writeln(e);
58-
// Extract the debug connection URL
59-
if (e.contains('Waiting for connection from Dart debug extension at http://')) {
60-
final debugUrlPattern = RegExp(
61-
r'Waiting for connection from Dart debug extension at (http://[^\s]+)',
62-
);
63-
final Match? match = debugUrlPattern.firstMatch(e);
64-
if (match != null && !sawDebugUrl.isCompleted) {
65-
sawDebugUrl.complete(match.group(1)!);
66-
}
67-
}
68-
});
69-
70-
io.Process? chromeProcess;
71-
try {
72-
// Step 1: Start Flutter app with web-server device (will wait for debug connection)
73-
debugPrint('Step 1: Starting Flutter app with web-server device...');
74-
// Start the app but don't wait for it to complete - it won't complete until Chrome connects
75-
final Future<void> appStartFuture = runFlutterWithWebServerDevice(
76-
flutter,
77-
additionalCommandArgs: [...additionalCommandArgs, '--no-web-resources-cdn'],
78-
);
47+
debugPrint('Starting WebSocket DWDS test with headless Chrome for hot reload...');
7948

80-
// Step 2: Wait for DWDS debug URL to be available
81-
debugPrint('Step 2: Waiting for DWDS debug service URL...');
82-
final String debugUrl = await sawDebugUrl.future.timeout(
83-
debugUrlTimeout,
84-
onTimeout: () {
85-
throw Exception('DWDS debug URL not found - app may not have started correctly');
86-
},
87-
);
88-
debugPrint('✓ DWDS debug service available at: $debugUrl');
89-
90-
// Step 3: Launch headless Chrome to connect to DWDS
91-
debugPrint('Step 3: Launching headless Chrome to connect to DWDS...');
92-
chromeProcess = await _launchHeadlessChrome(debugUrl);
93-
debugPrint('✓ Headless Chrome launched and connecting to DWDS');
94-
95-
// Step 4: Wait for app to start (Chrome connection established)
96-
debugPrint('Step 4: Waiting for Flutter app to start after Chrome connection...');
97-
await appStartFuture.timeout(
98-
appStartTimeout,
99-
onTimeout: () {
100-
throw Exception('App startup did not complete after Chrome connection');
101-
},
102-
);
103-
debugPrint('✓ Flutter app started successfully with WebSocket connection');
49+
// Set up WebSocket connection
50+
final WebSocketDwdsTestSetup setup = await WebSocketDwdsTestUtils.setupWebSocketConnection(
51+
flutter,
52+
additionalCommandArgs: additionalCommandArgs,
53+
);
10454

105-
// Step 5: Test hot reload functionality
106-
debugPrint('Step 5: Testing hot reload with WebSocket connection...');
55+
try {
56+
// Test hot reload functionality
57+
debugPrint('Step 6: Testing hot reload with WebSocket connection...');
10758
await flutter.hotReload().timeout(
10859
hotReloadTimeout,
10960
onTimeout: () {
@@ -114,80 +65,20 @@ void testAll({List<String> additionalCommandArgs = const <String>[]}) {
11465
// Give some time for logs to capture
11566
await Future<void>.delayed(const Duration(seconds: 2));
11667

117-
final output = stdout.toString();
68+
final output = setup.stdout.toString();
11869
expect(output, contains('Reloaded'), reason: 'Hot reload should complete successfully');
11970
debugPrint('✓ Hot reload completed successfully with WebSocket connection');
12071

12172
// Verify the correct infrastructure was used
122-
expect(
123-
output,
124-
contains('Waiting for connection from Dart debug extension'),
125-
reason: 'Should wait for debug connection (WebSocket infrastructure)',
126-
);
127-
expect(output, contains('web-server'), reason: 'Should use web-server device');
73+
WebSocketDwdsTestUtils.verifyWebSocketInfrastructure(output);
12874

12975
debugPrint('✓ WebSocket DWDS test completed successfully');
13076
debugPrint('✓ Verified: web-server device + DWDS + WebSocket connection + hot reload');
13177
} finally {
132-
await _cleanupResources(chromeProcess, subscription);
78+
await cleanupWebSocketTestResources(setup.chromeProcess, setup.subscription);
13379
}
13480
},
13581
skip: !platform.isMacOS, // Skip on non-macOS platforms where Chrome paths may differ
13682
);
13783
});
13884
}
139-
140-
/// Launches headless Chrome with the given debug URL.
141-
/// Uses findChromeExecutable to locate Chrome on the current platform.
142-
Future<io.Process> _launchHeadlessChrome(String debugUrl) async {
143-
const chromeArgs = [
144-
'--headless',
145-
'--disable-gpu',
146-
'--no-sandbox',
147-
'--disable-extensions',
148-
'--disable-dev-shm-usage',
149-
'--remote-debugging-port=0',
150-
];
151-
152-
final String chromePath = findChromeExecutable(platform, fileSystem);
153-
154-
try {
155-
return await io.Process.start(chromePath, [...chromeArgs, debugUrl]);
156-
} on Exception catch (e) {
157-
throw Exception(
158-
'Could not launch Chrome at $chromePath: $e. Please ensure Chrome is installed.',
159-
);
160-
}
161-
}
162-
163-
/// Cleans up test resources (Chrome process and stdout subscription).
164-
Future<void> _cleanupResources(
165-
io.Process? chromeProcess,
166-
StreamSubscription<String> subscription,
167-
) async {
168-
if (chromeProcess != null) {
169-
try {
170-
chromeProcess.kill();
171-
await chromeProcess.exitCode;
172-
debugPrint('Chrome process cleaned up');
173-
} on Exception catch (e) {
174-
debugPrint('Warning: Failed to clean up Chrome process: $e');
175-
}
176-
}
177-
await subscription.cancel();
178-
}
179-
180-
// Helper to run flutter with web-server device using WebSocket connection.
181-
Future<void> runFlutterWithWebServerDevice(
182-
FlutterRunTestDriver flutter, {
183-
bool verbose = false,
184-
bool withDebugger = true, // Enable debugger by default for WebSocket connection
185-
bool startPaused = false, // Don't start paused for this test
186-
List<String> additionalCommandArgs = const <String>[],
187-
}) => flutter.run(
188-
verbose: verbose,
189-
withDebugger: withDebugger, // Enable debugger to establish WebSocket connection
190-
startPaused: startPaused, // Let the app start normally after debugger connects
191-
device: WebServerDevice.kWebServerDeviceId,
192-
additionalCommandArgs: additionalCommandArgs,
193-
);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@Tags(<String>['flutter-test-driver'])
6+
library;
7+
8+
import 'dart:async';
9+
10+
import 'package:file/file.dart';
11+
12+
import '../src/common.dart';
13+
import 'test_data/hot_reload_project.dart';
14+
import 'test_data/websocket_dwds_test_common.dart';
15+
import 'test_driver.dart';
16+
import 'test_utils.dart';
17+
import 'transition_test_utils.dart';
18+
19+
void main() {
20+
testAll();
21+
}
22+
23+
void testAll({List<String> additionalCommandArgs = const <String>[]}) {
24+
group('WebSocket DWDS connection for hot restart'
25+
'${additionalCommandArgs.isEmpty ? '' : ' with args: $additionalCommandArgs'}', () {
26+
// Test configuration constants
27+
const hotRestartTimeout = Duration(seconds: 15);
28+
29+
late Directory tempDir;
30+
final project = HotReloadProject();
31+
late FlutterRunTestDriver flutter;
32+
33+
setUp(() async {
34+
tempDir = createResolvedTempDirectorySync('hot_restart_websocket_test.');
35+
await project.setUpIn(tempDir);
36+
flutter = FlutterRunTestDriver(tempDir);
37+
});
38+
39+
tearDown(() async {
40+
await flutter.stop();
41+
tryToDelete(tempDir);
42+
});
43+
44+
testWithoutContext(
45+
'hot restart with headless Chrome WebSocket connection',
46+
() async {
47+
debugPrint('Starting WebSocket DWDS test with headless Chrome for hot restart...');
48+
49+
// Set up WebSocket connection
50+
final WebSocketDwdsTestSetup setup = await WebSocketDwdsTestUtils.setupWebSocketConnection(
51+
flutter,
52+
additionalCommandArgs: additionalCommandArgs,
53+
);
54+
55+
try {
56+
// Test hot restart functionality
57+
debugPrint('Step 6: Testing hot restart with WebSocket connection...');
58+
await flutter.hotRestart().timeout(
59+
hotRestartTimeout,
60+
onTimeout: () {
61+
throw Exception('Hot restart timed out');
62+
},
63+
);
64+
65+
// Give some time for logs to capture
66+
await Future<void>.delayed(const Duration(seconds: 2));
67+
68+
final output = setup.stdout.toString();
69+
expect(
70+
output,
71+
contains('Restarted application'),
72+
reason: 'Hot restart should complete successfully',
73+
);
74+
debugPrint('✓ Hot restart completed successfully with WebSocket connection');
75+
76+
// Verify the correct infrastructure was used
77+
WebSocketDwdsTestUtils.verifyWebSocketInfrastructure(output);
78+
79+
debugPrint('✓ WebSocket DWDS test completed successfully');
80+
debugPrint('✓ Verified: web-server device + DWDS + WebSocket connection + hot restart');
81+
} finally {
82+
await cleanupWebSocketTestResources(setup.chromeProcess, setup.subscription);
83+
}
84+
},
85+
skip: !platform.isMacOS, // Skip on non-macOS platforms where Chrome paths may differ
86+
);
87+
});
88+
}

0 commit comments

Comments
 (0)