// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'base/dds.dart'; import 'base/file_system.dart'; import 'base/logger.dart'; import 'build_info.dart'; import 'globals.dart' as globals; import 'resident_runner.dart'; import 'tracing.dart'; import 'vmservice.dart'; const kFlutterTestOutputsDirEnvName = 'FLUTTER_TEST_OUTPUTS_DIR'; class ColdRunner extends ResidentRunner { ColdRunner( super.flutterDevices, { required super.target, required super.debuggingOptions, this.traceStartup = false, this.awaitFirstFrameWhenTracing = true, this.applicationBinary, super.stayResident, super.machine, super.dartBuilder, }) : super(hotMode: false); final bool traceStartup; final bool awaitFirstFrameWhenTracing; final File? applicationBinary; var _didAttach = false; @override bool get canHotReload => false; @override Logger get logger => globals.logger; @override FileSystem get fileSystem => globals.fs; @override bool get supportsDetach => _didAttach; @override Future run({ Completer? connectionInfoCompleter, Completer? appStartedCompleter, String? route, }) async { try { for (final FlutterDevice? device in flutterDevices) { final int result = await device!.runCold(coldRunner: this, route: route); if (result != 0) { appFailedToStart(); return result; } } } on Exception catch (err, stack) { globals.printError('$err\n$stack'); appFailedToStart(); return 1; } // Connect to the VM Service. if (debuggingEnabled) { try { await connectToServiceProtocol(); } on Exception catch (exception) { globals.printError(exception.toString()); appFailedToStart(); return 2; } } final FlutterDevice flutterDevice = flutterDevices.first; if (flutterDevice.vmServiceUris != null) { final FlutterVmService? vmService = flutterDevice.vmService; final DartDevelopmentService dds = flutterDevice.device!.dds; // For now, only support one debugger connection. connectionInfoCompleter?.complete( DebugConnectionInfo( httpUri: vmService!.httpAddress, wsUri: vmService.wsAddress, devToolsUri: dds.devToolsUri, dtdUri: dds.dtdUri, ), ); } globals.printTrace('Application running.'); for (final FlutterDevice? device in flutterDevices) { if (device!.vmService == null) { continue; } globals.printTrace('Connected to ${device.device!.displayName}'); } if (traceStartup) { // Only trace startup for the first device. final FlutterDevice device = flutterDevices.first; if (device.vmService != null) { globals.printStatus('Tracing startup on ${device.device!.displayName}.'); final String outputPath = globals.platform.environment[kFlutterTestOutputsDirEnvName] ?? getBuildDirectory(); await downloadStartupTrace( device.vmService!, awaitFirstFrame: awaitFirstFrameWhenTracing, logger: globals.logger, output: globals.fs.directory(outputPath), ); } appFinished(); } appStartedCompleter?.complete(); writeVmServiceFile(); if (stayResident && !traceStartup) { return waitForAppToFinish(); } await cleanupAtFinish(); return 0; } @override Future attach({ Completer? connectionInfoCompleter, Completer? appStartedCompleter, bool needsFullRestart = true, }) async { _didAttach = true; try { await connectToServiceProtocol(); } on Exception catch (error) { globals.printError('Error connecting to the service protocol: $error'); return 2; } for (final FlutterDevice? device in flutterDevices) { final List views = await device!.vmService!.getFlutterViews(); for (final view in views) { globals.printTrace('Connected to $view.'); } } appStartedCompleter?.complete(); if (stayResident) { return waitForAppToFinish(); } await cleanupAtFinish(); return 0; } @override Future cleanupAfterSignal() async { await stopEchoingDeviceLog(); if (_didAttach) { appFinished(); } await exitApp(); } @override Future cleanupAtFinish() async { for (final FlutterDevice? flutterDevice in flutterDevices) { await flutterDevice!.device!.dispose(); } await stopEchoingDeviceLog(); } @override Future preExit() async { for (final FlutterDevice? device in flutterDevices) { // If we're running in release mode, stop the app using the device logic. if (device!.vmService == null) { await device.device!.stopApp(device.package, userIdentifier: device.userIdentifier); } } await super.preExit(); } }