| // 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 'package:file/memory.dart'; |
| import 'package:flutter_tools/src/application_package.dart'; |
| import 'package:flutter_tools/src/base/common.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/logger.dart'; |
| import 'package:flutter_tools/src/base/platform.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/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 FakeDeviceManager fakeDeviceManager; |
| |
| setUp(() { |
| fileSystem = MemoryFileSystem.test(); |
| logger = BufferLogger.test(); |
| platform = FakePlatform(); |
| fakeDeviceManager = FakeDeviceManager(); |
| }); |
| |
| setUpAll(() { |
| Cache.disableLocking(); |
| }); |
| |
| tearDownAll(() { |
| Cache.enableLocking(); |
| }); |
| |
| testUsingContext('warns if screenshot is not supported but continues test', () async { |
| final DriveCommand command = DriveCommand(fileSystem: fileSystem, logger: logger, platform: platform); |
| 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.devices = <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('takes screenshot and rethrows on drive exception', () async { |
| final DriveCommand command = DriveCommand(fileSystem: fileSystem, logger: logger, platform: platform); |
| 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.devices = <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, |
| 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.devices = <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); |
| 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.devices = <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('shouldRunPub is true unless user specifies --no-pub', () async { |
| final DriveCommand command = DriveCommand(fileSystem: fileSystem, logger: logger, platform: platform); |
| 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); |
| 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', |
| '--verbose-system-logs', |
| '--null-assertions', |
| '--native-null-assertions', |
| '--enable-impeller', |
| '--trace-systrace', |
| '--enable-software-rendering', |
| '--skia-deterministic-rendering', |
| ]), 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.verboseSystemLogs, true); |
| expect(options.nullAssertions, true); |
| expect(options.nativeNullAssertions, true); |
| expect(options.enableImpeller, true); |
| expect(options.traceSystrace, true); |
| expect(options.enableSoftwareRendering, true); |
| expect(options.skiaDeterministicRendering, true); |
| }, overrides: <Type, Generator>{ |
| Cache: () => Cache.test(processManager: FakeProcessManager.any()), |
| FileSystem: () => MemoryFileSystem.test(), |
| ProcessManager: () => FakeProcessManager.any(), |
| }); |
| } |
| |
| // Unfortunately Device, despite not being immutable, has an `operator ==`. |
| // Until we fix that, we have to also ignore related lints here. |
| 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'); |
| } |
| } |
| |
| // Unfortunately Device, despite not being immutable, has an `operator ==`. |
| // Until we fix that, we have to also ignore related lints here. |
| // ignore: avoid_implementing_value_types |
| class ScreenshotDevice extends Fake implements Device { |
| @override |
| final String name = 'FakeDevice'; |
| |
| @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 |
| 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 {} |
| } |
| |
| class FakePub extends Fake implements Pub { |
| @override |
| Future<void> get({ |
| PubContext? context, |
| required FlutterProject project, |
| bool skipIfAbsent = false, |
| bool upgrade = false, |
| bool offline = false, |
| bool generateSyntheticPackage = false, |
| String? flutterRootOverride, |
| bool checkUpToDate = false, |
| bool shouldSkipThirdPartyGenerator = true, |
| bool printProgress = true, |
| }) async { } |
| } |
| |
| class FakeDeviceManager extends Fake implements DeviceManager { |
| List<Device> devices = <Device>[]; |
| |
| @override |
| String? specifiedDeviceId; |
| |
| @override |
| Future<List<Device>> getDevices() async => devices; |
| |
| @override |
| Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, {Duration? timeout, bool promptUserToChooseDevice = true}) async => devices; |
| } |
| |
| 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, bool ipv6) async { } |
| |
| @override |
| Future<int> startTest( |
| String testFile, |
| List<String> arguments, |
| Map<String, String> environment, |
| PackageConfig packageConfig, { |
| bool? headless, |
| String? chromeBinary, |
| String? browserName, |
| bool? androidEmulator, |
| int? driverPort, |
| List<String>? webBrowserFlags, |
| List<String>? browserDimension, |
| String? profileMemory, |
| }) async => 1; |
| } |