Allow flutter tools to detach a running Chrome session (#163349)
https://github.com/flutter/flutter/issues/163329 Tested locally to ensure pressing 'd' in a running `flutter run` session detaches and leaves Chrome open. Hitting 'q' or stopping with a signal both terminate Chrome as expected. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: Nate Biggs <natebiggs@google.com>
This commit is contained in:
parent
99ac47897e
commit
4b2a52fdfd
@ -174,10 +174,8 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
debuggingOptions.buildInfo.ddcModuleFormat != DdcModuleFormat.ddc ||
|
||||
debuggingOptions.buildInfo.canaryFeatures != true;
|
||||
|
||||
// TODO(srujzs): Return true when web supports detaching.
|
||||
// https://github.com/flutter/flutter/issues/163329
|
||||
@override
|
||||
bool get supportsDetach => false;
|
||||
bool get supportsDetach => stopAppDuringCleanup;
|
||||
|
||||
ConnectionResult? _connectionResult;
|
||||
StreamSubscription<vmservice.Event>? _stdOutSub;
|
||||
@ -220,7 +218,11 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
await _stdErrSub?.cancel();
|
||||
await _serviceSub?.cancel();
|
||||
await _extensionEventSub?.cancel();
|
||||
await device!.device!.stopApp(null);
|
||||
|
||||
if (stopAppDuringCleanup) {
|
||||
await device!.device!.stopApp(null);
|
||||
}
|
||||
|
||||
_registeredMethodsForService.clear();
|
||||
try {
|
||||
_generatedEntrypointDirectory?.deleteSync(recursive: true);
|
||||
@ -808,7 +810,9 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
||||
|
||||
@override
|
||||
Future<void> exitApp() async {
|
||||
await device!.exitApps();
|
||||
if (stopAppDuringCleanup) {
|
||||
await device!.exitApps();
|
||||
}
|
||||
appFinished();
|
||||
}
|
||||
|
||||
|
@ -970,9 +970,6 @@ abstract class ResidentHandlers {
|
||||
Future<void> cleanupAfterSignal();
|
||||
|
||||
/// Tear down the runner and leave the application running.
|
||||
///
|
||||
/// This is not supported on web devices where the runner is running
|
||||
/// the application server as well.
|
||||
Future<void> detach();
|
||||
|
||||
/// Tear down the runner and exit the application.
|
||||
|
@ -405,6 +405,90 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext(
|
||||
'Detach keeps device running',
|
||||
() async {
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
|
||||
setupMocks();
|
||||
fileSystem.directory('web').deleteSync(recursive: true);
|
||||
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
|
||||
flutterDevice,
|
||||
flutterProject: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
terminal: Terminal.test(),
|
||||
platform: FakePlatform(),
|
||||
outputPreferences: OutputPreferences.test(),
|
||||
analytics: globals.analytics,
|
||||
systemClock: globals.systemClock,
|
||||
devtoolsHandler: createNoOpHandler,
|
||||
);
|
||||
|
||||
mockDevice.dds = DartDevelopmentService(logger: logger);
|
||||
|
||||
expect(mockDevice.isRunning, false);
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
||||
Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(connectionInfoCompleter: connectionInfoCompleter));
|
||||
await connectionInfoCompleter.future;
|
||||
expect(mockDevice.isRunning, true);
|
||||
await residentWebRunner.detach();
|
||||
expect(residentWebRunner.stopAppDuringCleanup, false);
|
||||
await residentWebRunner.exit();
|
||||
await residentWebRunner.cleanupAtFinish();
|
||||
expect(mockDevice.isRunning, true);
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
FeatureFlags: enableExplicitPackageDependencies,
|
||||
Pub: FakePubWithPrimedDeps.new,
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext(
|
||||
'Quit stops device',
|
||||
() async {
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
|
||||
setupMocks();
|
||||
fileSystem.directory('web').deleteSync(recursive: true);
|
||||
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
|
||||
flutterDevice,
|
||||
flutterProject: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
terminal: Terminal.test(),
|
||||
platform: FakePlatform(),
|
||||
outputPreferences: OutputPreferences.test(),
|
||||
analytics: globals.analytics,
|
||||
systemClock: globals.systemClock,
|
||||
devtoolsHandler: createNoOpHandler,
|
||||
);
|
||||
|
||||
mockDevice.dds = DartDevelopmentService(logger: logger);
|
||||
|
||||
expect(mockDevice.isRunning, false);
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter =
|
||||
Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(connectionInfoCompleter: connectionInfoCompleter));
|
||||
await connectionInfoCompleter.future;
|
||||
expect(mockDevice.isRunning, true);
|
||||
expect(residentWebRunner.stopAppDuringCleanup, true);
|
||||
await residentWebRunner.cleanupAtFinish();
|
||||
expect(mockDevice.isRunning, false);
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
FeatureFlags: enableExplicitPackageDependencies,
|
||||
Pub: FakePubWithPrimedDeps.new,
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext(
|
||||
'Listens to stdout and stderr streams before running main',
|
||||
() async {
|
||||
@ -1593,6 +1677,8 @@ class FakeDevice extends Fake implements Device {
|
||||
|
||||
int count = 0;
|
||||
|
||||
bool isRunning = false;
|
||||
|
||||
@override
|
||||
Future<String> get sdkNameAndVersion async => 'SDK Name and Version';
|
||||
|
||||
@ -1613,6 +1699,7 @@ class FakeDevice extends Fake implements Device {
|
||||
bool ipv6 = false,
|
||||
String? userIdentifier,
|
||||
}) async {
|
||||
isRunning = true;
|
||||
return LaunchResult.succeeded();
|
||||
}
|
||||
|
||||
@ -1622,6 +1709,7 @@ class FakeDevice extends Fake implements Device {
|
||||
throw StateError('stopApp called more than once.');
|
||||
}
|
||||
count += 1;
|
||||
isRunning = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user