Unverified Commit d306c37b authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] fix --use-existing-app (#69237)

parent 58a74af8
...@@ -82,6 +82,9 @@ void main() { ...@@ -82,6 +82,9 @@ void main() {
}); });
int result; int result;
result = await drive.exitCode; result = await drive.exitCode;
await flutter('install', options: <String>[
'--uninstall-only',
]);
if (result != 0) if (result != 0)
throw 'Failed to drive test app (exit code $result).'; throw 'Failed to drive test app (exit code $result).';
result = await run.exitCode; result = await run.exitCode;
......
...@@ -440,7 +440,6 @@ tasks: ...@@ -440,7 +440,6 @@ tasks:
Verifies that `flutter drive --route` still works. No performance numbers. Verifies that `flutter drive --route` still works. No performance numbers.
stage: devicelab stage: devicelab
required_agent_capabilities: ["linux/android"] required_agent_capabilities: ["linux/android"]
flaky: true
linux_chrome_dev_mode: linux_chrome_dev_mode:
description: > description: >
......
...@@ -186,22 +186,35 @@ class DriveCommand extends RunCommandBase { ...@@ -186,22 +186,35 @@ class DriveCommand extends RunCommandBase {
? null ? null
: _fileSystem.file(stringArg('use-application-binary')); : _fileSystem.file(stringArg('use-application-binary'));
await driverService.start( if (stringArg('use-existing-app') == null) {
buildInfo, await driverService.start(
device, buildInfo,
debuggingOptions, device,
ipv6, debuggingOptions,
applicationBinary: applicationBinary, ipv6,
route: route, applicationBinary: applicationBinary,
userIdentifier: userIdentifier, route: route,
mainPath: targetFile, userIdentifier: userIdentifier,
platformArgs: <String, Object>{ mainPath: targetFile,
if (traceStartup) platformArgs: <String, Object>{
'trace-startup': traceStartup, if (traceStartup)
if (web) 'trace-startup': traceStartup,
'--no-launch-chrome': true, if (web)
'--no-launch-chrome': true,
}
);
} else {
final Uri uri = Uri.tryParse(stringArg('use-existing-app'));
if (uri == null) {
throwToolExit('Invalid VM Service URI: ${stringArg('use-existing-app')}');
} }
); await driverService.reuseApplication(
uri,
device,
debuggingOptions,
ipv6,
);
}
final int testResult = await driverService.startTest( final int testResult = await driverService.startTest(
testFile, testFile,
......
...@@ -64,6 +64,14 @@ abstract class DriverService { ...@@ -64,6 +64,14 @@ abstract class DriverService {
Map<String, Object> platformArgs = const <String, Object>{}, Map<String, Object> platformArgs = const <String, Object>{},
}); });
/// If --use-existing-app is provided, configured the correct VM Service URI.
Future<void> reuseApplication(
Uri vmServiceUri,
Device device,
DebuggingOptions debuggingOptions,
bool ipv6,
);
/// Start the test file with the provided [arguments] and [environment], returning /// Start the test file with the provided [arguments] and [environment], returning
/// the test process exit code. /// the test process exit code.
Future<int> startTest( Future<int> startTest(
...@@ -168,10 +176,26 @@ class FlutterDriverService extends DriverService { ...@@ -168,10 +176,26 @@ class FlutterDriverService extends DriverService {
if (result == null || !result.started) { if (result == null || !result.started) {
throwToolExit('Application failed to start. Will not run test. Quitting.', exitCode: 1); throwToolExit('Application failed to start. Will not run test. Quitting.', exitCode: 1);
} }
_vmServiceUri = result.observatoryUri.toString(); return reuseApplication(
result.observatoryUri,
device,
debuggingOptions,
ipv6,
);
}
@override
Future<void> reuseApplication(
Uri vmServiceUri,
Device device,
DebuggingOptions debuggingOptions,
bool ipv6,
) async {
_vmServiceUri = vmServiceUri.toString();
_device = device;
try { try {
await device.dds.startDartDevelopmentService( await device.dds.startDartDevelopmentService(
result.observatoryUri, vmServiceUri,
debuggingOptions.ddsPort, debuggingOptions.ddsPort,
ipv6, ipv6,
debuggingOptions.disableServiceAuthCodes, debuggingOptions.disableServiceAuthCodes,
...@@ -228,20 +252,24 @@ class FlutterDriverService extends DriverService { ...@@ -228,20 +252,24 @@ class FlutterDriverService extends DriverService {
); );
await sharedSkSlWriter(_device, result, outputFile: writeSkslOnExit, logger: _logger); await sharedSkSlWriter(_device, result, outputFile: writeSkslOnExit, logger: _logger);
} }
try { // If the application package is available, stop and uninstall.
if (_applicationPackage != null) {
if (!await _device.stopApp(_applicationPackage, userIdentifier: userIdentifier)) { if (!await _device.stopApp(_applicationPackage, userIdentifier: userIdentifier)) {
_logger.printError('Failed to stop app'); _logger.printError('Failed to stop app');
} }
} on Exception catch (err) {
_logger.printError('Failed to stop app due to unhandled error: $err');
}
try {
if (!await _device.uninstallApp(_applicationPackage, userIdentifier: userIdentifier)) { if (!await _device.uninstallApp(_applicationPackage, userIdentifier: userIdentifier)) {
_logger.printError('Failed to uninstall app'); _logger.printError('Failed to uninstall app');
} }
} on Exception catch (err) { } else if (_device.supportsFlutterExit) {
_logger.printError('Failed to uninstall app due to unhandled error: $err'); // Otherwise use the VM Service URI to stop the app as a best effort approach.
final vm_service.VM vm = await _vmService.getVM();
final vm_service.IsolateRef isolateRef = vm.isolates
.firstWhere((vm_service.IsolateRef element) {
return !element.isSystemIsolate;
}, orElse: () => null);
unawaited(_vmService.flutterExit(isolateId: isolateRef.id));
} else {
_logger.printTrace('No application package for $_device, leaving app running');
} }
await _device.dispose(); await _device.dispose();
} }
......
...@@ -153,6 +153,11 @@ class WebDriverService extends DriverService { ...@@ -153,6 +153,11 @@ class WebDriverService extends DriverService {
'ANDROID_CHROME_ON_EMULATOR': (_browserNameToEnum(browserName) == Browser.androidChrome && androidEmulator).toString(), 'ANDROID_CHROME_ON_EMULATOR': (_browserNameToEnum(browserName) == Browser.androidChrome && androidEmulator).toString(),
}; };
} }
@override
Future<void> reuseApplication(Uri vmServiceUri, Device device, DebuggingOptions debuggingOptions, bool ipv6) async {
throwToolExit('--use-existing-app is not supported with flutter web driver');
}
} }
/// A list of supported browsers. /// A list of supported browsers.
......
...@@ -255,6 +255,47 @@ void main() { ...@@ -255,6 +255,47 @@ void main() {
'data': <String, Object>{'A': 'B'} 'data': <String, Object>{'A': 'B'}
}); });
}); });
testWithoutContext('Can connect to existing application and stop it during cleanup', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <FakeVmServiceRequest>[
getVM,
getVM,
const FakeVmServiceRequest(
method: 'ext.flutter.exit',
args: <String, Object>{
'isolateId': '1',
}
)
]);
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
final DriverService driverService = setUpDriverService(processManager: processManager, vmService: fakeVmServiceHost.vmService);
final FakeDevice device = FakeDevice(LaunchResult.failed());
await driverService.reuseApplication(
Uri.parse('http://127.0.0.1:63426/1UasC_ihpXY=/'),
device,
DebuggingOptions.enabled(BuildInfo.debug),
false,
);
await driverService.stop();
});
testWithoutContext('Does not call flutterExit on device types that do not support it', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <FakeVmServiceRequest>[
getVM,
]);
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
final DriverService driverService = setUpDriverService(processManager: processManager, vmService: fakeVmServiceHost.vmService);
final FakeDevice device = FakeDevice(LaunchResult.failed(), supportsFlutterExit: false);
await driverService.reuseApplication(
Uri.parse('http://127.0.0.1:63426/1UasC_ihpXY=/'),
device,
DebuggingOptions.enabled(BuildInfo.debug),
false,
);
await driverService.stop();
});
} }
FlutterDriverService setUpDriverService({ FlutterDriverService setUpDriverService({
...@@ -301,7 +342,7 @@ class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFa ...@@ -301,7 +342,7 @@ class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFa
class FakeApplicationPackage extends Fake implements ApplicationPackage {} class FakeApplicationPackage extends Fake implements ApplicationPackage {}
class FakeDevice extends Fake implements Device { class FakeDevice extends Fake implements Device {
FakeDevice(this.result); FakeDevice(this.result, {this.supportsFlutterExit = true});
LaunchResult result; LaunchResult result;
bool didStopApp = false; bool didStopApp = false;
...@@ -312,6 +353,9 @@ class FakeDevice extends Fake implements Device { ...@@ -312,6 +353,9 @@ class FakeDevice extends Fake implements Device {
@override @override
String get name => 'test'; String get name => 'test';
@override
final bool supportsFlutterExit;
@override @override
final DartDevelopmentService dds = FakeDartDevelopmentService(); final DartDevelopmentService dds = FakeDartDevelopmentService();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment