Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/flutter_tools/lib/src/commands/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
'and this flag can be used to override the default. To disable this for the '
'skwasm renderer, use "--no-cross-origin-isolation".',
hide: !verboseHelp,
)
..addFlag(
'uninstall',
defaultsTo: true,
help:
'Whether to uninstall the app after running integration tests. '
'Set "--no-uninstall" to keep the app installed on the device.',
);

addDdsOptions(verboseHelp: verboseHelp);
Expand Down Expand Up @@ -477,6 +484,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
: null,
printDtd: boolArg(FlutterGlobalOptions.kPrintDtd, global: true),
webUseWasm: useWasm,
uninstallApp: boolArg('uninstall'),
);

final Uri? nativeAssetsJson = _isIntegrationTest
Expand Down
11 changes: 11 additions & 0 deletions packages/flutter_tools/lib/src/device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ class DebuggingOptions {
this.enableFlutterGpu = false,
this.enableVulkanValidation = false,
this.uninstallFirst = false,
this.uninstallApp = true,
this.enableDartProfiling = true,
this.profileStartup = false,
this.enableEmbedderApi = false,
Expand Down Expand Up @@ -1016,6 +1017,7 @@ class DebuggingOptions {
this.enableFlutterGpu = false,
this.enableVulkanValidation = false,
this.uninstallFirst = false,
this.uninstallApp = true,
this.enableDartProfiling = true,
this.profileStartup = false,
this.enableEmbedderApi = false,
Expand Down Expand Up @@ -1099,6 +1101,7 @@ class DebuggingOptions {
required this.enableFlutterGpu,
required this.enableVulkanValidation,
required this.uninstallFirst,
required this.uninstallApp,
required this.enableDartProfiling,
required this.profileStartup,
required this.enableEmbedderApi,
Expand Down Expand Up @@ -1162,6 +1165,12 @@ class DebuggingOptions {
/// This is not implemented for every platform.
final bool uninstallFirst;

/// Whether the tool should uninstall the app after running.
///
/// This is currently only implemented for integration tests.
/// Defaults to true.
final bool uninstallApp;

/// Whether to run the browser in headless mode.
///
/// Some CI environments do not provide a display and fail to launch the
Expand Down Expand Up @@ -1295,6 +1304,7 @@ class DebuggingOptions {
'enableImpeller': enableImpeller.asBool,
'enableFlutterGpu': enableFlutterGpu,
'enableVulkanValidation': enableVulkanValidation,
'uninstallApp': uninstallApp,
'enableDartProfiling': enableDartProfiling,
'profileStartup': profileStartup,
'enableEmbedderApi': enableEmbedderApi,
Expand Down Expand Up @@ -1364,6 +1374,7 @@ class DebuggingOptions {
enableFlutterGpu: json['enableFlutterGpu']! as bool,
enableVulkanValidation: (json['enableVulkanValidation'] as bool?) ?? false,
uninstallFirst: (json['uninstallFirst'] as bool?) ?? false,
uninstallApp: (json['uninstallApp'] as bool?) ?? true,
enableDartProfiling: (json['enableDartProfiling'] as bool?) ?? true,
profileStartup: (json['profileStartup'] as bool?) ?? false,
enableEmbedderApi: (json['enableEmbedderApi'] as bool?) ?? false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ class IntegrationTestTestDevice implements TestDevice {
if (!await device.stopApp(applicationPackage, userIdentifier: userIdentifier)) {
globals.printTrace('Could not stop the Integration Test app.');
}
if (!await device.uninstallApp(applicationPackage, userIdentifier: userIdentifier)) {
globals.printTrace('Could not uninstall the Integration Test app.');
if (debuggingOptions.uninstallApp) {
if (!await device.uninstallApp(applicationPackage, userIdentifier: userIdentifier)) {
globals.printTrace('Could not uninstall the Integration Test app.');
}
}
}

Expand Down
34 changes: 34 additions & 0 deletions packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,40 @@ dev_dependencies:
},
);

testUsingContext(
'uninstallApp defaults to true',
() async {
final testRunner = FakeFlutterTestRunner(0);

final testCommand = TestCommand(testRunner: testRunner);
final CommandRunner<void> commandRunner = createTestCommandRunner(testCommand);

await commandRunner.run(const <String>['test', '--no-pub']);
expect(testRunner.lastDebuggingOptionsValue.uninstallApp, true);
},
overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
},
);

testUsingContext(
'--no-uninstall sets uninstallApp to false',
() async {
final testRunner = FakeFlutterTestRunner(0);

final testCommand = TestCommand(testRunner: testRunner);
final CommandRunner<void> commandRunner = createTestCommandRunner(testCommand);

await commandRunner.run(const <String>['test', '--no-pub', '--no-uninstall']);
expect(testRunner.lastDebuggingOptionsValue.uninstallApp, false);
},
overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
},
);

testUsingContext(
'Passes web renderer into debugging options',
() async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,76 @@ void main() {
},
);

testUsingContext(
'kill() calls uninstallApp when uninstallApp is true',
() async {
final trackingDevice = FakeDeviceTrackingUninstall();
final testDeviceWithUninstall = IntegrationTestTestDevice(
id: 1,
device: trackingDevice,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
userIdentifier: '',
compileExpression: null,
);

await testDeviceWithUninstall.start('entrypointPath');
await testDeviceWithUninstall.kill();

expect(trackingDevice.uninstallAppCalled, isTrue);
expect(testDeviceWithUninstall.finished, completes);
},
overrides: <Type, Generator>{
ApplicationPackageFactory: () => FakeApplicationPackageFactory(),
VMServiceConnector: () =>
(
Uri httpUri, {
ReloadSources? reloadSources,
Restart? restart,
CompileExpression? compileExpression,
FlutterProject? flutterProject,
PrintStructuredErrorLogMethod? printStructuredErrorLogMethod,
io.CompressionOptions? compression,
Device? device,
Logger? logger,
}) async => fakeVmServiceHost.vmService,
},
);

testUsingContext(
'kill() does not call uninstallApp when uninstallApp is false',
() async {
final trackingDevice = FakeDeviceTrackingUninstall();
final testDeviceWithoutUninstall = IntegrationTestTestDevice(
id: 1,
device: trackingDevice,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, uninstallApp: false),
userIdentifier: '',
compileExpression: null,
);

await testDeviceWithoutUninstall.start('entrypointPath');
await testDeviceWithoutUninstall.kill();

expect(trackingDevice.uninstallAppCalled, isFalse);
expect(testDeviceWithoutUninstall.finished, completes);
},
overrides: <Type, Generator>{
ApplicationPackageFactory: () => FakeApplicationPackageFactory(),
VMServiceConnector: () =>
(
Uri httpUri, {
ReloadSources? reloadSources,
Restart? restart,
CompileExpression? compileExpression,
FlutterProject? flutterProject,
PrintStructuredErrorLogMethod? printStructuredErrorLogMethod,
io.CompressionOptions? compression,
Device? device,
Logger? logger,
}) async => fakeVmServiceHost.vmService,
},
);

testUsingContext(
'Can handle closing of the VM service',
() async {
Expand Down Expand Up @@ -274,3 +344,21 @@ class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFa
}

class FakeApplicationPackage extends Fake implements ApplicationPackage {}

class FakeDeviceTrackingUninstall extends FakeDevice {
FakeDeviceTrackingUninstall()
: super(
'ephemeral',
'ephemeral',
type: PlatformType.android,
launchResult: LaunchResult.succeeded(vmServiceUri: vmServiceUri),
);

bool uninstallAppCalled = false;

@override
Future<bool> uninstallApp(ApplicationPackage app, {String? userIdentifier}) async {
uninstallAppCalled = true;
return true;
}
}