flutter/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart

1052 lines
33 KiB
Dart

// 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 'dart:io' as io;
import 'package:fake_async/fake_async.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/async_guard.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/signals.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/drive.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/drive/drive_service.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:package_config/package_config.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/test_flutter_command_runner.dart';
void main() {
late FileSystem fileSystem;
late BufferLogger logger;
late Platform platform;
late Terminal terminal;
late OutputPreferences outputPreferences;
late FakeDeviceManager fakeDeviceManager;
late FakeSignals signals;
setUp(() {
fileSystem = MemoryFileSystem.test();
logger = BufferLogger.test();
platform = FakePlatform();
terminal = Terminal.test();
outputPreferences = OutputPreferences.test();
fakeDeviceManager = FakeDeviceManager();
signals = FakeSignals();
});
setUpAll(() {
Cache.disableLocking();
});
tearDownAll(() {
Cache.enableLocking();
});
testUsingContext(
'fails if the specified --target is not found',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
await expectLater(
() => createTestCommandRunner(
command,
).run(<String>['drive', '--no-pub', '--target', 'lib/app.dart']),
throwsToolExit(message: 'Target file "lib/app.dart" not found'),
);
expect(logger.errorText, isEmpty);
expect(logger.statusText, isEmpty);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.empty(),
},
);
testUsingContext(
'fails if the default --target is not found',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/app.dart').createSync(recursive: true);
fileSystem.file('test_driver/app_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
await expectLater(
() => createTestCommandRunner(command).run(<String>['drive', '--no-pub']),
throwsToolExit(message: 'Target file "lib/main.dart" not found'),
);
expect(logger.errorText, isEmpty);
expect(logger.statusText, isEmpty);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.empty(),
},
);
testUsingContext(
'fails with an informative error message if --target looks like --driver',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
await expectLater(
() => createTestCommandRunner(
command,
).run(<String>['drive', '--no-pub', '--target', 'test_driver/main_test.dart']),
throwsToolExit(message: 'Test file not found: /test_driver/main_test_test.dart'),
);
expect(
logger.errorText,
contains('The file path passed to --target should be an app entrypoint'),
);
expect(logger.statusText, isEmpty);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.empty(),
},
);
testUsingContext(
'warns if screenshot is not supported but continues test',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.directory('drive_screenshots').createSync();
final Device screenshotDevice = ThrowingScreenshotDevice()..supportsScreenshot = false;
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--screenshot',
'drive_screenshots',
]),
throwsToolExit(message: 'cannot start app'),
);
expect(logger.errorText, contains('Screenshot not supported for FakeDevice'));
expect(logger.statusText, isEmpty);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'does not register screenshot signal handler if --screenshot not provided',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
flutterDriverFactory: FailingFakeFlutterDriverFactory(),
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.directory('drive_screenshots').createSync();
final Device screenshotDevice = ScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--use-existing-app',
'http://localhost:8181',
'--keep-app-running',
]),
throwsToolExit(),
);
expect(logger.statusText, isNot(contains('Screenshot written to ')));
expect(signals.addedHandlers, isEmpty);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'takes screenshot and rethrows on drive exception',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.directory('drive_screenshots').createSync();
final Device screenshotDevice = ThrowingScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--screenshot',
'drive_screenshots',
]),
throwsToolExit(message: 'cannot start app'),
);
expect(logger.statusText, contains('Screenshot written to drive_screenshots/drive_01.png'));
expect(logger.statusText, isNot(contains('drive_02.png')));
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'takes screenshot on drive test failure',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
flutterDriverFactory: FailingFakeFlutterDriverFactory(),
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.directory('drive_screenshots').createSync();
final Device screenshotDevice = ScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--use-existing-app',
'http://localhost:8181',
'--keep-app-running',
'--screenshot',
'drive_screenshots',
]),
throwsToolExit(),
);
// Takes the screenshot before the application would be killed (if --keep-app-running not passed).
expect(
logger.statusText,
contains(
'Screenshot written to drive_screenshots/drive_01.png\n'
'Leaving the application running.',
),
);
expect(logger.statusText, isNot(contains('drive_02.png')));
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'drive --screenshot errors but does not fail if screenshot fails',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('drive_screenshots').createSync();
final Device screenshotDevice = ThrowingScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--screenshot',
'drive_screenshots',
]),
throwsToolExit(message: 'cannot start app'),
);
expect(logger.statusText, isEmpty);
expect(
logger.errorText,
contains('Error taking screenshot: FileSystemException: Not a directory'),
);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'drive --timeout takes screenshot and tool exits after timeout',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: Signals.test(),
flutterDriverFactory: FakeFlutterDriverFactory(),
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.directory('drive_screenshots').createSync();
final ScreenshotDevice screenshotDevice = ScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
expect(screenshotDevice.screenshots, isEmpty);
bool caughtToolExit = false;
FakeAsync().run<void>((FakeAsync time) {
// Because the tool exit will be thrown asynchronously by a [Timer],
// use [asyncGuard] to catch it
asyncGuard<void>(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--use-existing-app',
'http://localhost:8181',
'--screenshot',
'drive_screenshots',
'--timeout',
'300', // 5 minutes
]),
onError: (Object error) {
expect(error, isA<ToolExit>());
expect((error as ToolExit).message, contains('Timed out after 300 seconds'));
caughtToolExit = true;
},
);
time.elapse(const Duration(seconds: 299));
expect(screenshotDevice.screenshots, isEmpty);
time.elapse(const Duration(seconds: 2));
expect(
screenshotDevice.screenshots,
contains(
isA<File>().having((File file) => file.path, 'path', 'drive_screenshots/drive_01.png'),
),
);
});
expect(caughtToolExit, isTrue);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'drive --screenshot takes screenshot if sent a registered signal',
() async {
final FakeProcessSignal signal = FakeProcessSignal();
final ProcessSignal signalUnderTest = ProcessSignal(signal);
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: Signals.test(),
flutterDriverFactory: FakeFlutterDriverFactory(
onStartTest: () {
signal.controller.add(signal);
return Completer<int>().future;
},
),
signalsToHandle: <ProcessSignal>{signalUnderTest},
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.directory('drive_screenshots').createSync();
final ScreenshotDevice screenshotDevice = ScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
expect(screenshotDevice.screenshots, isEmpty);
// This command will never complete. In reality, a real signal would have
// shut down the Dart process.
unawaited(
createTestCommandRunner(command).run(<String>[
'drive',
'--no-pub',
'-d',
screenshotDevice.id,
'--use-existing-app',
'http://localhost:8181',
'--screenshot',
'drive_screenshots',
]),
);
await screenshotDevice.firstScreenshot;
expect(
screenshotDevice.screenshots,
contains(
isA<File>().having((File file) => file.path, 'path', 'drive_screenshots/drive_01.png'),
),
);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'shouldRunPub is true unless user specifies --no-pub',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
try {
await createTestCommandRunner(command).run(const <String>['drive', '--no-pub']);
} on Exception {
// Expected to throw
}
expect(command.shouldRunPub, false);
try {
await createTestCommandRunner(command).run(const <String>['drive']);
} on Exception {
// Expected to throw
}
expect(command.shouldRunPub, true);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
},
);
testUsingContext(
'flags propagate to debugging options',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
await expectLater(
() => createTestCommandRunner(command).run(<String>[
'drive',
'--start-paused',
'--disable-service-auth-codes',
'--trace-skia',
'--trace-systrace',
'--trace-to-file=path/to/trace.binpb',
'--verbose-system-logs',
'--native-null-assertions',
'--enable-impeller',
'--trace-systrace',
'--enable-software-rendering',
'--skia-deterministic-rendering',
'--enable-embedder-api',
'--ci',
'--debug-logs-dir=path/to/logs',
]),
throwsToolExit(),
);
final DebuggingOptions options = await command.createDebuggingOptions(false);
expect(options.startPaused, true);
expect(options.disableServiceAuthCodes, true);
expect(options.traceSkia, true);
expect(options.traceSystrace, true);
expect(options.traceToFile, 'path/to/trace.binpb');
expect(options.verboseSystemLogs, true);
expect(options.nativeNullAssertions, true);
expect(options.enableImpeller, ImpellerStatus.enabled);
expect(options.traceSystrace, true);
expect(options.enableSoftwareRendering, true);
expect(options.skiaDeterministicRendering, true);
expect(options.usingCISystem, true);
expect(options.debugLogsDirectoryPath, 'path/to/logs');
},
overrides: <Type, Generator>{
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
},
);
testUsingContext(
'Port publication not disabled for wireless device',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
final Device wirelessDevice =
FakeIosDevice()..connectionInterface = DeviceConnectionInterface.wireless;
fakeDeviceManager.wirelessDevices = <Device>[wirelessDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>['drive']),
throwsToolExit(),
);
final DebuggingOptions options = await command.createDebuggingOptions(false);
expect(options.disablePortPublication, false);
},
overrides: <Type, Generator>{
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'Port publication is disabled for wired device',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
await expectLater(
() => createTestCommandRunner(command).run(<String>['drive']),
throwsToolExit(),
);
final Device usbDevice =
FakeIosDevice()..connectionInterface = DeviceConnectionInterface.attached;
fakeDeviceManager.attachedDevices = <Device>[usbDevice];
final DebuggingOptions options = await command.createDebuggingOptions(false);
expect(options.disablePortPublication, true);
},
overrides: <Type, Generator>{
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'Port publication does not default to enabled for wireless device if flag manually added',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
final Device wirelessDevice =
FakeIosDevice()..connectionInterface = DeviceConnectionInterface.wireless;
fakeDeviceManager.wirelessDevices = <Device>[wirelessDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>['drive', '--no-publish-port']),
throwsToolExit(),
);
final DebuggingOptions options = await command.createDebuggingOptions(false);
expect(options.disablePortPublication, true);
},
overrides: <Type, Generator>{
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'--use-existing-app keeps the app running',
() async {
bool wasStopped = false;
final FakeProcessSignal signal = FakeProcessSignal();
final ProcessSignal signalUnderTest = ProcessSignal(signal);
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: Signals.test(),
flutterDriverFactory: FakeFlutterDriverFactory(
onStartTest: () async {
signal.controller.add(signal);
return 0;
},
onStop: () {
wasStopped = true;
},
),
signalsToHandle: <ProcessSignal>{signalUnderTest},
);
final Device screenshotDevice = ThrowingScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
final Future<void> runningCommand = createTestCommandRunner(command).run(<String>[
'drive',
'-d',
screenshotDevice.id,
'--no-pub',
'--use-existing-app',
'http://localhost:8181',
]);
signal.controller.add(io.ProcessSignal.sigint);
await runningCommand;
expect(
wasStopped,
false,
reason: 'Using --use-existing-app without --no-keep-app-running does not stop the app',
);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'--no-keep-app-running always stops the app running',
() async {
bool wasStopped = false;
final FakeProcessSignal signal = FakeProcessSignal();
final ProcessSignal signalUnderTest = ProcessSignal(signal);
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: Signals.test(),
flutterDriverFactory: FakeFlutterDriverFactory(
onStartTest: () async {
signal.controller.add(signal);
return 0;
},
onStop: () {
wasStopped = true;
},
),
signalsToHandle: <ProcessSignal>{signalUnderTest},
);
final Device screenshotDevice = ThrowingScreenshotDevice();
fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
final Future<void> runningCommand = createTestCommandRunner(command).run(<String>[
'drive',
'-d',
screenshotDevice.id,
'--no-pub',
'--no-keep-app-running',
'--use-existing-app',
'http://localhost:8181',
]);
signal.controller.add(io.ProcessSignal.sigint);
await runningCommand;
expect(
wasStopped,
true,
reason: 'Using --use-existing-app with --no-keep-app-running stops the app',
);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Pub: () => FakePub(),
DeviceManager: () => fakeDeviceManager,
},
);
testUsingContext(
'flutter drive --help explains how to use the command',
() async {
final DriveCommand command = DriveCommand(
fileSystem: fileSystem,
logger: logger,
platform: platform,
terminal: terminal,
outputPreferences: outputPreferences,
signals: signals,
);
await createTestCommandRunner(command).run(<String>['drive', '--help']);
expect(
logger.statusText,
stringContainsInOrder(<String>['flutter drive', '--target', '--driver']),
);
},
overrides: <Type, Generator>{Logger: () => logger},
);
}
class ThrowingScreenshotDevice extends ScreenshotDevice {
@override
Future<LaunchResult> startApp(
ApplicationPackage? package, {
String? mainPath,
String? route,
DebuggingOptions? debuggingOptions,
Map<String, dynamic>? platformArgs,
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
String? userIdentifier,
}) async {
throwToolExit('cannot start app');
}
}
class ScreenshotDevice extends Fake implements Device {
final List<File> screenshots = <File>[];
final Completer<void> _firstScreenshotCompleter = Completer<void>();
/// A Future that completes when [takeScreenshot] is called the first time.
Future<void> get firstScreenshot => _firstScreenshotCompleter.future;
@override
final String name = 'FakeDevice';
@override
String get displayName => name;
@override
final Category category = Category.mobile;
@override
final String id = 'fake_device';
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android;
@override
bool supportsScreenshot = true;
@override
bool get isConnected => true;
@override
Future<LaunchResult> startApp(
ApplicationPackage? package, {
String? mainPath,
String? route,
DebuggingOptions? debuggingOptions,
Map<String, dynamic>? platformArgs,
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
String? userIdentifier,
}) async => LaunchResult.succeeded();
@override
Future<void> takeScreenshot(File outputFile) async {
if (!_firstScreenshotCompleter.isCompleted) {
_firstScreenshotCompleter.complete();
}
screenshots.add(outputFile);
}
}
class FakePub extends Fake implements Pub {
@override
Future<void> get({
PubContext? context,
required FlutterProject project,
bool upgrade = false,
bool offline = false,
bool generateSyntheticPackage = false,
String? flutterRootOverride,
bool checkUpToDate = false,
bool shouldSkipThirdPartyGenerator = true,
PubOutputMode outputMode = PubOutputMode.all,
}) async {}
}
/// A [FlutterDriverFactory] that creates a [FakeDriverService].
class FakeFlutterDriverFactory extends Fake implements FlutterDriverFactory {
FakeFlutterDriverFactory({this.onStartTest, this.onStop});
final Future<int> Function()? onStartTest;
final void Function()? onStop;
@override
DriverService createDriverService(bool web) {
return FakeDriverService(onStartTest: onStartTest, onStop: onStop);
}
}
/// A [DriverService] that will return a Future from [startTest] that will never complete.
///
/// This is to simulate when the test will take a long time, but a signal is
/// expected to interrupt the process.
class FakeDriverService extends Fake implements DriverService {
FakeDriverService({this.onStartTest, this.onStop});
final Future<int> Function()? onStartTest;
final void Function()? onStop;
@override
Future<void> reuseApplication(
Uri vmServiceUri,
Device device,
DebuggingOptions debuggingOptions,
) async {}
@override
Future<int> startTest(
String testFile,
List<String> arguments,
PackageConfig packageConfig, {
bool? headless,
String? chromeBinary,
String? browserName,
bool? androidEmulator,
int? driverPort,
List<String>? webBrowserFlags,
List<String>? browserDimension,
String? profileMemory,
}) async {
final Future<int> result = onStartTest?.call() ?? Completer<int>().future;
return result;
}
@override
Future<void> stop({String? userIdentifier}) async {
return onStop?.call();
}
}
class FailingFakeFlutterDriverFactory extends Fake implements FlutterDriverFactory {
@override
DriverService createDriverService(bool web) => FailingFakeDriverService();
}
class FailingFakeDriverService extends Fake implements DriverService {
@override
Future<void> reuseApplication(
Uri vmServiceUri,
Device device,
DebuggingOptions debuggingOptions,
) async {}
@override
Future<int> startTest(
String testFile,
List<String> arguments,
PackageConfig packageConfig, {
bool? headless,
String? chromeBinary,
String? browserName,
bool? androidEmulator,
int? driverPort,
List<String>? webBrowserFlags,
List<String>? browserDimension,
String? profileMemory,
}) async => 1;
}
class FakeProcessSignal extends Fake implements io.ProcessSignal {
final StreamController<io.ProcessSignal> controller = StreamController<io.ProcessSignal>();
@override
Stream<io.ProcessSignal> watch() => controller.stream;
}
class FakeIosDevice extends Fake implements IOSDevice {
@override
DeviceConnectionInterface connectionInterface = DeviceConnectionInterface.attached;
@override
bool get isWirelesslyConnected => connectionInterface == DeviceConnectionInterface.wireless;
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
}
class FakeSignals extends Fake implements Signals {
List<SignalHandler> addedHandlers = <SignalHandler>[];
@override
Object addHandler(ProcessSignal signal, SignalHandler handler) {
addedHandlers.add(handler);
return const Object();
}
@override
Future<bool> removeHandler(ProcessSignal signal, Object token) async => true;
}