mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
1052 lines
33 KiB
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;
|
|
}
|