Unverified Commit 57b0ddbd authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Uninstall app flag (#53385)

parent be3a4b37
......@@ -19,14 +19,36 @@ void main() {
final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
await inDirectory(appDir, () async {
final Completer<void> ready = Completer<void>();
final List<String> stdout = <String>[];
final List<String> stderr = <String>[];
// Uninstall if the app is already installed on the device to get to a clean state.
print('uninstalling...');
final Process uninstall = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['--suppress-analytics', 'install', '--uninstall-only', '-d', device.deviceId],
)..stdout
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) {
print('uninstall:stdout: $line');
})..stderr
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) {
print('uninstall:stderr: $line');
stderr.add(line);
});
if (await uninstall.exitCode != 0) {
throw 'flutter install --uninstall-only failed.';
}
print('run: starting...');
final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['--suppress-analytics', 'run', '--release', '-d', device.deviceId, 'lib/main.dart'],
isBot: false, // we just want to test the output, not have any debugging info
);
final List<String> stdout = <String>[];
final List<String> stderr = <String>[];
int runExitCode;
run.stdout
.transform<String>(utf8.decoder)
......
......@@ -15,6 +15,11 @@ import '../runner/flutter_command.dart';
class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
InstallCommand() {
requiresPubspecYaml();
argParser.addFlag('uninstall-only',
negatable: true,
defaultsTo: false,
help: 'Uninstall the app if already on the device. Skip install.',
);
}
@override
......@@ -25,6 +30,8 @@ class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts
Device device;
bool get uninstallOnly => boolArg('uninstall-only');
@override
Future<void> validateCommand() async {
await super.validateCommand();
......@@ -40,13 +47,31 @@ class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts
Cache.releaseLockEarly();
if (uninstallOnly) {
await _uninstallApp(package);
} else {
await _installApp(package);
}
return FlutterCommandResult.success();
}
Future<void> _uninstallApp(ApplicationPackage package) async {
if (await device.isAppInstalled(package)) {
globals.printStatus('Uninstalling $package from $device...');
if (!await device.uninstallApp(package)) {
globals.printError('Uninstalling old version failed');
}
} else {
globals.printStatus('$package not found on $device, skipping uninstall');
}
}
Future<void> _installApp(ApplicationPackage package) async {
globals.printStatus('Installing $package to $device...');
if (!await installApp(device, package)) {
throwToolExit('Install failed');
}
return FlutterCommandResult.success();
}
}
......
......@@ -145,6 +145,8 @@ class IOSDeploy {
'--id',
deviceId,
'--exists',
'--timeout', // If the device is not connected, ios-deploy will wait forever.
'10',
'--bundle_id',
bundleId,
];
......@@ -152,10 +154,16 @@ class IOSDeploy {
launchCommand,
environment: iosDeployEnv,
);
// Device successfully connected, but app not installed.
if (result.exitCode == 255) {
_logger.printTrace('$bundleId not installed on $deviceId');
return false;
}
if (result.exitCode != 0) {
_logger.printTrace('App install check failed: ${result.stderr}');
return false;
}
return result.stdout.contains(bundleId);
return true;
}
// Maps stdout line stream. Must return original line.
......
......@@ -76,28 +76,110 @@ void main() {
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('IOSDevice.isAppInstalled catches ProcessException from ios-deploy', () async {
final IOSApp iosApp = PrebuiltIOSApp(projectBundleId: 'app');
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(command: const <String>[
'ios-deploy',
'--id',
'1234',
'--exists',
'--bundle_id',
'app',
], environment: const <String, String>{
'PATH': '/usr/bin:null',
...kDyLdLibEntry,
}, onRun: () {
throw const ProcessException('ios-deploy', <String>[]);
})
]);
final IOSDevice device = setUpIOSDevice(processManager: processManager);
final bool isAppInstalled = await device.isAppInstalled(iosApp);
group('isAppInstalled', () {
testWithoutContext('catches ProcessException from ios-deploy', () async {
final IOSApp iosApp = PrebuiltIOSApp(projectBundleId: 'app');
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(command: const <String>[
'ios-deploy',
'--id',
'1234',
'--exists',
'--timeout',
'10',
'--bundle_id',
'app',
], environment: const <String, String>{
'PATH': '/usr/bin:null',
...kDyLdLibEntry,
}, onRun: () {
throw const ProcessException('ios-deploy', <String>[]);
})
]);
final IOSDevice device = setUpIOSDevice(processManager: processManager);
final bool isAppInstalled = await device.isAppInstalled(iosApp);
expect(isAppInstalled, false);
expect(processManager.hasRemainingExpectations, false);
expect(isAppInstalled, false);
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('returns true when app is installed', () async {
final IOSApp iosApp = PrebuiltIOSApp(projectBundleId: 'app');
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>[
'ios-deploy',
'--id',
'1234',
'--exists',
'--timeout',
'10',
'--bundle_id',
'app',
], environment: <String, String>{
'PATH': '/usr/bin:null',
...kDyLdLibEntry,
}, exitCode: 0)
]);
final IOSDevice device = setUpIOSDevice(processManager: processManager);
final bool isAppInstalled = await device.isAppInstalled(iosApp);
expect(isAppInstalled, isTrue);
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('returns false when app is not installed', () async {
final IOSApp iosApp = PrebuiltIOSApp(projectBundleId: 'app');
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>[
'ios-deploy',
'--id',
'1234',
'--exists',
'--timeout',
'10',
'--bundle_id',
'app',
], environment: <String, String>{
'PATH': '/usr/bin:null',
...kDyLdLibEntry,
}, exitCode: 255)
]);
final BufferLogger logger = BufferLogger.test();
final IOSDevice device = setUpIOSDevice(processManager: processManager, logger: logger);
final bool isAppInstalled = await device.isAppInstalled(iosApp);
expect(isAppInstalled, isFalse);
expect(processManager.hasRemainingExpectations, false);
expect(logger.traceText, contains('${iosApp.id} not installed on ${device.id}'));
});
testWithoutContext('returns false on command timeout or other error', () async {
final IOSApp iosApp = PrebuiltIOSApp(projectBundleId: 'app');
const String stderr = '2020-03-26 17:48:43.484 ios-deploy[21518:5501783] [ !! ] Timed out waiting for device';
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>[
'ios-deploy',
'--id',
'1234',
'--exists',
'--timeout',
'10',
'--bundle_id',
'app',
], environment: <String, String>{
'PATH': '/usr/bin:null',
...kDyLdLibEntry,
}, stderr: stderr,
exitCode: 253)
]);
final BufferLogger logger = BufferLogger.test();
final IOSDevice device = setUpIOSDevice(processManager: processManager, logger: logger);
final bool isAppInstalled = await device.isAppInstalled(iosApp);
expect(isAppInstalled, isFalse);
expect(processManager.hasRemainingExpectations, false);
expect(logger.traceText, contains(stderr));
});
});
testWithoutContext('IOSDevice.installApp catches ProcessException from ios-deploy', () async {
......@@ -154,7 +236,9 @@ void main() {
IOSDevice setUpIOSDevice({
@required ProcessManager processManager,
FileSystem fileSystem,
Logger logger,
}) {
logger ??= BufferLogger.test();
final FakePlatform platform = FakePlatform(
operatingSystem: 'macos',
environment: <String, String>{},
......@@ -167,19 +251,19 @@ IOSDevice setUpIOSDevice({
return IOSDevice(
'1234',
name: 'iPhone 1',
logger: BufferLogger.test(),
logger: logger,
fileSystem: fileSystem ?? MemoryFileSystem.test(),
sdkVersion: '13.3',
cpuArchitecture: DarwinArch.arm64,
platform: platform,
iMobileDevice: IMobileDevice(
logger: BufferLogger.test(),
logger: logger,
processManager: processManager,
artifacts: artifacts,
cache: cache,
),
iosDeploy: IOSDeploy(
logger: BufferLogger.test(),
logger: logger,
platform: platform,
processManager: processManager,
artifacts: artifacts,
......
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