Skip to content

Commit c3de901

Browse files
authored
Serve DevTools when running flutter test (#123607)
Also clean up messaging output by `flutter test` when `--start-paused` is provided.
1 parent 302c087 commit c3de901

File tree

2 files changed

+61
-6
lines changed

2 files changed

+61
-6
lines changed

packages/flutter_tools/lib/src/test/flutter_tester_device.dart

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import '../convert.dart';
2121
import '../device.dart';
2222
import '../globals.dart' as globals;
2323
import '../project.dart';
24+
import '../resident_runner.dart';
2425
import '../vmservice.dart';
2526

2627
import 'font_config_manager.dart';
@@ -72,6 +73,7 @@ class FlutterTesterTestDevice extends TestDevice {
7273

7374
Process? _process;
7475
HttpServer? _server;
76+
DevtoolsLauncher? _devToolsLauncher;
7577

7678
/// Starts the device.
7779
///
@@ -163,9 +165,11 @@ class FlutterTesterTestDevice extends TestDevice {
163165
debuggingOptions.hostVmServicePort == detectedUri.port);
164166

165167
Uri? forwardingUri;
168+
DartDevelopmentService? dds;
169+
166170
if (debuggingOptions.enableDds) {
167171
logger.printTrace('test $id: Starting Dart Development Service');
168-
final DartDevelopmentService dds = await startDds(
172+
dds = await startDds(
169173
detectedUri,
170174
uriConverter: uriConverter,
171175
);
@@ -193,10 +197,10 @@ class FlutterTesterTestDevice extends TestDevice {
193197
}));
194198

195199
if (debuggingOptions.startPaused && !machine!) {
196-
logger.printStatus('The test process has been started.');
197-
logger.printStatus('You can now connect to it using vmService. To connect, load the following Web site in your browser:');
198-
logger.printStatus(' $forwardingUri');
199-
logger.printStatus('You should first set appropriate breakpoints, then resume the test in the debugger.');
200+
logger.printStatus('The Dart VM service is listening on $forwardingUri');
201+
await _startDevTools(forwardingUri, dds);
202+
logger.printStatus('');
203+
logger.printStatus('The test process has been started. Set any relevant breakpoints and then resume the test in the debugger.');
200204
}
201205
_gotProcessVmServiceUri.complete(forwardingUri);
202206
},
@@ -215,6 +219,9 @@ class FlutterTesterTestDevice extends TestDevice {
215219
logger.printTrace('test $id: Terminating flutter_tester process');
216220
_process?.kill(io.ProcessSignal.sigkill);
217221

222+
logger.printTrace('test $id: Shutting down DevTools server');
223+
await _devToolsLauncher?.close();
224+
218225
logger.printTrace('test $id: Shutting down test harness socket server');
219226
await _server?.close(force: true);
220227
await finished;
@@ -261,6 +268,29 @@ class FlutterTesterTestDevice extends TestDevice {
261268
);
262269
}
263270

271+
Future<void> _startDevTools(Uri forwardingUri, DartDevelopmentService? dds) async {
272+
_devToolsLauncher = DevtoolsLauncher.instance;
273+
logger.printTrace('test $id: Serving DevTools...');
274+
final DevToolsServerAddress? devToolsServerAddress = await _devToolsLauncher?.serve();
275+
276+
if (devToolsServerAddress == null) {
277+
logger.printTrace('test $id: Failed to start DevTools');
278+
return;
279+
}
280+
await _devToolsLauncher?.ready;
281+
logger.printTrace('test $id: DevTools is being served at ${devToolsServerAddress.uri}');
282+
283+
// Notify the DDS instance that there's a DevTools instance available so it can correctly
284+
// redirect DevTools related requests.
285+
dds?.setExternalDevToolsUri(devToolsServerAddress.uri!);
286+
287+
final Uri devToolsUri = devToolsServerAddress.uri!.replace(
288+
// Use query instead of queryParameters to avoid unnecessary encoding.
289+
query: 'uri=$forwardingUri',
290+
);
291+
logger.printStatus('The Flutter DevTools debugger and profiler is available at: $devToolsUri');
292+
}
293+
264294
/// Binds an [HttpServer] serving from `host` on `port`.
265295
///
266296
/// Only intended to be overridden in tests.

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ void main() {
222222
final Completer<Uri> completer = Completer<Uri>();
223223
final RegExp vmServiceUriRegExp = RegExp(r'((http)?:\/\/)[^\s]+');
224224
sub = process.stdout.transform(utf8.decoder).listen((String e) {
225-
if (vmServiceUriRegExp.hasMatch(e)) {
225+
if (!completer.isCompleted && vmServiceUriRegExp.hasMatch(e)) {
226226
completer.complete(Uri.parse(vmServiceUriRegExp.firstMatch(e)!.group(0)!));
227227
}
228228
});
@@ -237,6 +237,31 @@ void main() {
237237
process.kill();
238238
}
239239
});
240+
241+
testWithoutContext('flutter test should serve DevTools', () async {
242+
late final Process process;
243+
late final StreamSubscription<String> sub;
244+
try {
245+
process = await _runFlutterTestConcurrent('trivial', automatedTestsDirectory, flutterTestDirectory,
246+
extraArguments: const <String>['--start-paused']);
247+
final Completer<Uri> completer = Completer<Uri>();
248+
final RegExp devToolsUriRegExp = RegExp(r'The Flutter DevTools debugger and profiler is available at: (http://[^\s]+)');
249+
sub = process.stdout.transform(utf8.decoder).listen((String e) {
250+
if (!completer.isCompleted && devToolsUriRegExp.hasMatch(e)) {
251+
completer.complete(Uri.parse(devToolsUriRegExp.firstMatch(e)!.group(1)!));
252+
}
253+
});
254+
final Uri devToolsUri = await completer.future;
255+
final HttpClient client = HttpClient();
256+
final HttpClientRequest request = await client.getUrl(devToolsUri);
257+
final HttpClientResponse response = await request.close();
258+
final String content = await response.transform(utf8.decoder).join();
259+
expect(content.contains('DevTools'), true);
260+
} finally {
261+
await sub.cancel();
262+
process.kill();
263+
}
264+
});
240265
}
241266

242267
Future<void> _testFile(

0 commit comments

Comments
 (0)