flutter/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
Ben Konyi 2a2f973120
Update flutter_tools to look for new VM service message (#97683)
* Update flutter_tools to look for new VM service message

The Dart SDK will soon move away from the current Observatory message:

"Observatory listening on ..."

To a new message that no longer references Observatory:

"Dart VM Service listening on ..."

This change updates all tests with mocks to check for the new message
and also adds support for the new message in ProtocolDiscovery.

See https://github.com/dart-lang/sdk/issues/46756

* Fix some parsing locations

* Fix analysis failures

* Update message

* Remove extra comment

* Update message

* Add globals prefix
2022-02-15 07:33:57 -08:00

901 lines
28 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.
// @dart = 2.8
import 'dart:async';
import 'dart:io';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/application_package.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/dds.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.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/attach.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/device_port_forwarder.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/ios/application_package.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_devices.dart';
import '../../src/fake_vm_services.dart';
import '../../src/test_flutter_command_runner.dart';
final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate(
id: '1',
pauseEvent: vm_service.Event(
kind: vm_service.EventKind.kResume,
timestamp: 0
),
breakpoints: <vm_service.Breakpoint>[],
exceptionPauseMode: null,
isolateFlags: <vm_service.IsolateFlag>[],
libraries: <vm_service.LibraryRef>[],
livePorts: 0,
name: 'test',
number: '1',
pauseOnExit: false,
runnable: true,
startTime: 0,
isSystemIsolate: false,
);
void main() {
tearDown(() {
MacOSDesignedForIPadDevices.allowDiscovery = false;
});
group('attach', () {
StreamLogger logger;
FileSystem testFileSystem;
setUp(() {
Cache.disableLocking();
logger = StreamLogger();
testFileSystem = MemoryFileSystem(
style: globals.platform.isWindows
? FileSystemStyle.windows
: FileSystemStyle.posix,
);
testFileSystem.directory('lib').createSync();
testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync();
});
group('with one device and no specified target file', () {
const int devicePort = 499;
const int hostPort = 42;
FakeDeviceLogReader fakeLogReader;
RecordingPortForwarder portForwarder;
FakeDartDevelopmentService fakeDds;
FakeAndroidDevice device;
setUp(() {
fakeLogReader = FakeDeviceLogReader();
portForwarder = RecordingPortForwarder(hostPort);
fakeDds = FakeDartDevelopmentService();
device = FakeAndroidDevice(id: '1')
..portForwarder = portForwarder
..dds = fakeDds;
});
tearDown(() {
fakeLogReader.dispose();
});
testUsingContext('finds observatory port and forwards', () async {
device.onGetLogReader = () {
fakeLogReader.addLine('Foo');
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader;
};
testDeviceManager.addDevice(device);
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') {
// The "Observatory URL on device" message is output by the ProtocolDiscovery when it found the observatory.
completer.complete();
}
});
final Future<void> task = createTestCommandRunner(AttachCommand()).run(<String>['attach']);
await completer.future;
expect(portForwarder.devicePort, devicePort);
expect(portForwarder.hostPort, hostPort);
await fakeLogReader.dispose();
await expectLoggerInterruptEndsTask(task, logger);
await loggerSubscription.cancel();
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
});
testUsingContext('Fails with tool exit on bad Observatory uri', () async {
device.onGetLogReader = () {
fakeLogReader.addLine('Foo');
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
fakeLogReader.dispose();
return fakeLogReader;
};
testDeviceManager.addDevice(device);
expect(() => createTestCommandRunner(AttachCommand()).run(<String>['attach']), throwsToolExit());
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
});
testUsingContext('accepts filesystem parameters', () async {
device.onGetLogReader = () {
fakeLogReader.addLine('Foo');
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader;
};
testDeviceManager.addDevice(device);
const String filesystemScheme = 'foo';
const String filesystemRoot = '/build-output/';
const String projectRoot = '/build-output/project-root';
const String outputDill = '/tmp/output.dill';
final FakeHotRunner hotRunner = FakeHotRunner();
hotRunner.onAttach = (
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
bool allowExistingDdsInstance,
bool enableDevTools,
) async => 0;
hotRunner.exited = false;
hotRunner.isWaitingForObservatory = false;
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
..hotRunner = hotRunner;
final AttachCommand command = AttachCommand(
hotRunnerFactory: hotRunnerFactory,
);
await createTestCommandRunner(command).run(<String>[
'attach',
'--filesystem-scheme',
filesystemScheme,
'--filesystem-root',
filesystemRoot,
'--project-root',
projectRoot,
'--output-dill',
outputDill,
'-v', // enables verbose logging
]);
// Validate the attach call built a fake runner with the right
// project root and output dill.
expect(hotRunnerFactory.projectRootPath, projectRoot);
expect(hotRunnerFactory.dillOutputPath, outputDill);
expect(hotRunnerFactory.devices, hasLength(1));
// Validate that the attach call built a flutter device with the right
// output dill, filesystem scheme, and filesystem root.
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
expect(flutterDevice.buildInfo.fileSystemScheme, filesystemScheme);
expect(flutterDevice.buildInfo.fileSystemRoots, const <String>[filesystemRoot]);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('exits when ipv6 is specified and debug-port is not', () async {
testDeviceManager.addDevice(device);
final AttachCommand command = AttachCommand();
await expectLater(
createTestCommandRunner(command).run(<String>['attach', '--ipv6']),
throwsToolExit(
message: 'When the --debug-port or --debug-url is unknown, this command determines '
'the value of --ipv6 on its own.',
),
);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
},);
testUsingContext('exits when observatory-port is specified and debug-port is not', () async {
device.onGetLogReader = () {
fakeLogReader.addLine('Foo');
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader;
};
testDeviceManager.addDevice(device);
final AttachCommand command = AttachCommand();
await expectLater(
createTestCommandRunner(command).run(<String>['attach', '--observatory-port', '100']),
throwsToolExit(
message: 'When the --debug-port or --debug-url is unknown, this command does not use '
'the value of --observatory-port.',
),
);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
},);
});
group('forwarding to given port', () {
const int devicePort = 499;
const int hostPort = 42;
RecordingPortForwarder portForwarder;
FakeAndroidDevice device;
setUp(() {
final FakeDartDevelopmentService fakeDds = FakeDartDevelopmentService();
portForwarder = RecordingPortForwarder(hostPort);
device = FakeAndroidDevice(id: '1')
..portForwarder = portForwarder
..dds = fakeDds;
});
testUsingContext('succeeds in ipv4 mode', () async {
testDeviceManager.addDevice(device);
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Connecting to service protocol: http://127.0.0.1:42/') {
// Wait until resident_runner.dart tries to connect.
// There's nothing to connect _to_, so that's as far as we care to go.
completer.complete();
}
});
final Future<void> task = createTestCommandRunner(AttachCommand())
.run(<String>['attach', '--debug-port', '$devicePort']);
await completer.future;
expect(portForwarder.devicePort, devicePort);
expect(portForwarder.hostPort, hostPort);
await expectLoggerInterruptEndsTask(task, logger);
await loggerSubscription.cancel();
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
});
testUsingContext('succeeds in ipv6 mode', () async {
testDeviceManager.addDevice(device);
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Connecting to service protocol: http://[::1]:42/') {
// Wait until resident_runner.dart tries to connect.
// There's nothing to connect _to_, so that's as far as we care to go.
completer.complete();
}
});
final Future<void> task = createTestCommandRunner(AttachCommand())
.run(<String>['attach', '--debug-port', '$devicePort', '--ipv6']);
await completer.future;
expect(portForwarder.devicePort, devicePort);
expect(portForwarder.hostPort, hostPort);
await expectLoggerInterruptEndsTask(task, logger);
await loggerSubscription.cancel();
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
});
testUsingContext('skips in ipv4 mode with a provided observatory port', () async {
testDeviceManager.addDevice(device);
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Connecting to service protocol: http://127.0.0.1:42/') {
// Wait until resident_runner.dart tries to connect.
// There's nothing to connect _to_, so that's as far as we care to go.
completer.complete();
}
});
final Future<void> task = createTestCommandRunner(AttachCommand()).run(
<String>[
'attach',
'--debug-port',
'$devicePort',
'--observatory-port',
'$hostPort',
// Ensure DDS doesn't use hostPort by binding to a random port.
'--dds-port',
'0',
],
);
await completer.future;
expect(portForwarder.devicePort, null);
expect(portForwarder.hostPort, 42);
await expectLoggerInterruptEndsTask(task, logger);
await loggerSubscription.cancel();
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
});
testUsingContext('skips in ipv6 mode with a provided observatory port', () async {
testDeviceManager.addDevice(device);
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Connecting to service protocol: http://[::1]:42/') {
// Wait until resident_runner.dart tries to connect.
// There's nothing to connect _to_, so that's as far as we care to go.
completer.complete();
}
});
final Future<void> task = createTestCommandRunner(AttachCommand()).run(
<String>[
'attach',
'--debug-port',
'$devicePort',
'--observatory-port',
'$hostPort',
'--ipv6',
// Ensure DDS doesn't use hostPort by binding to a random port.
'--dds-port',
'0',
],
);
await completer.future;
expect(portForwarder.devicePort, null);
expect(portForwarder.hostPort, 42);
await expectLoggerInterruptEndsTask(task, logger);
await loggerSubscription.cancel();
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
});
});
testUsingContext('exits when no device connected', () async {
final AttachCommand command = AttachCommand();
await expectLater(
createTestCommandRunner(command).run(<String>['attach']),
throwsToolExit(),
);
expect(testLogger.statusText, containsIgnoringWhitespace('No supported devices connected'));
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('fails when targeted device is not Android with --device-user', () async {
final FakeIOSDevice device = FakeIOSDevice();
testDeviceManager.addDevice(device);
expect(createTestCommandRunner(AttachCommand()).run(<String>[
'attach',
'--device-user',
'10',
]), throwsToolExit(message: '--device-user is only supported for Android'));
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('exits when multiple devices connected', () async {
final AttachCommand command = AttachCommand();
testDeviceManager.addDevice(FakeAndroidDevice(id: 'xx1'));
testDeviceManager.addDevice(FakeAndroidDevice(id: 'yy2'));
await expectLater(
createTestCommandRunner(command).run(<String>['attach']),
throwsToolExit(),
);
expect(testLogger.statusText, containsIgnoringWhitespace('More than one device'));
expect(testLogger.statusText, contains('xx1'));
expect(testLogger.statusText, contains('yy2'));
expect(MacOSDesignedForIPadDevices.allowDiscovery, isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Catches service disappeared error', () async {
final FakeAndroidDevice device = FakeAndroidDevice(id: '1')
..portForwarder = const NoOpDevicePortForwarder()
..onGetLogReader = () => NoOpDeviceLogReader('test');
final FakeHotRunner hotRunner = FakeHotRunner();
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
..hotRunner = hotRunner;
hotRunner.onAttach = (
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
bool allowExistingDdsInstance,
bool enableDevTools,
) async {
await null;
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kServiceDisappeared, '');
};
testDeviceManager.addDevice(device);
testFileSystem.file('lib/main.dart').createSync();
final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory);
await expectLater(createTestCommandRunner(command).run(<String>[
'attach',
]), throwsToolExit(message: 'Lost connection to device.'));
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Does not catch generic RPC error', () async {
final FakeAndroidDevice device = FakeAndroidDevice(id: '1')
..portForwarder = const NoOpDevicePortForwarder()
..onGetLogReader = () => NoOpDeviceLogReader('test');
final FakeHotRunner hotRunner = FakeHotRunner();
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
..hotRunner = hotRunner;
hotRunner.onAttach = (
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
bool allowExistingDdsInstance,
bool enableDevTools,
) async {
await null;
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kInvalidParams, '');
};
testDeviceManager.addDevice(device);
testFileSystem.file('lib/main.dart').createSync();
final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory);
await expectLater(createTestCommandRunner(command).run(<String>[
'attach',
]), throwsA(isA<vm_service.RPCError>()));
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});
}
class FakeHotRunner extends Fake implements HotRunner {
Future<int> Function(Completer<DebugConnectionInfo>, Completer<void>, bool, bool) onAttach;
@override
bool exited = false;
@override
bool isWaitingForObservatory = true;
@override
Future<int> attach({
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) {
return onAttach(connectionInfoCompleter, appStartedCompleter, allowExistingDdsInstance, enableDevTools);
}
}
class FakeHotRunnerFactory extends Fake implements HotRunnerFactory {
HotRunner hotRunner;
String dillOutputPath;
String projectRootPath;
List<FlutterDevice> devices;
@override
HotRunner build(
List<FlutterDevice> devices, {
String target,
DebuggingOptions debuggingOptions,
bool benchmarkMode = false,
File applicationBinary,
bool hostIsIde = false,
String projectRootPath,
String packagesFilePath,
String dillOutputPath,
bool stayResident = true,
bool ipv6 = false,
FlutterProject flutterProject,
}) {
this.devices = devices;
this.dillOutputPath = dillOutputPath;
this.projectRootPath = projectRootPath;
return hotRunner;
}
}
class RecordingPortForwarder implements DevicePortForwarder {
RecordingPortForwarder([this.hostPort]);
int devicePort;
int hostPort;
@override
Future<void> dispose() async { }
@override
Future<int> forward(int devicePort, {int hostPort}) async {
this.devicePort = devicePort;
this.hostPort ??= hostPort;
return this.hostPort;
}
@override
List<ForwardedPort> get forwardedPorts => <ForwardedPort>[];
@override
Future<void> unforward(ForwardedPort forwardedPort) async { }
}
class StreamLogger extends Logger {
@override
bool get isVerbose => true;
@override
void printError(
String message, {
StackTrace stackTrace,
bool emphasis,
TerminalColor color,
int indent,
int hangingIndent,
bool wrap,
}) {
hadErrorOutput = true;
_log('[stderr] $message');
}
@override
void printWarning(
String message, {
bool emphasis,
TerminalColor color,
int indent,
int hangingIndent,
bool wrap,
}) {
hadWarningOutput = true;
_log('[stderr] $message');
}
@override
void printStatus(
String message, {
bool emphasis,
TerminalColor color,
bool newline,
int indent,
int hangingIndent,
bool wrap,
}) {
_log('[stdout] $message');
}
@override
void printBox(
String message, {
String title,
}) {
if (title == null) {
_log('[stdout] $message');
} else {
_log('[stdout] $title: $message');
}
}
@override
void printTrace(String message) {
_log('[verbose] $message');
}
@override
Status startProgress(
String message, {
@required Duration timeout,
String progressId,
bool multilineOutput = false,
bool includeTiming = true,
int progressIndicatorPadding = kDefaultStatusPadding,
}) {
_log('[progress] $message');
return SilentStatus(
stopwatch: Stopwatch(),
)..start();
}
@override
Status startSpinner({ VoidCallback onFinish }) {
return SilentStatus(
stopwatch: Stopwatch(),
onFinish: onFinish,
)..start();
}
bool _interrupt = false;
void interrupt() {
_interrupt = true;
}
final StreamController<String> _controller = StreamController<String>.broadcast();
void _log(String message) {
_controller.add(message);
if (_interrupt) {
_interrupt = false;
throw const LoggerInterrupted();
}
}
Stream<String> get stream => _controller.stream;
@override
void sendEvent(String name, [Map<String, dynamic> args]) { }
@override
bool get supportsColor => throw UnimplementedError();
@override
bool get hasTerminal => false;
@override
void clear() => _log('[stdout] ${globals.terminal.clearScreen()}\n');
@override
Terminal get terminal => Terminal.test();
}
class LoggerInterrupted implements Exception {
const LoggerInterrupted();
}
Future<void> expectLoggerInterruptEndsTask(Future<void> task, StreamLogger logger) async {
logger.interrupt(); // an exception during the task should cause it to fail...
await expectLater(
() => task,
throwsA(isA<ToolExit>().having((ToolExit error) => error.exitCode, 'exitCode', 2)),
);
}
VMServiceConnector getFakeVmServiceFactory({
@required Completer<void> vmServiceDoneCompleter,
}) {
assert(vmServiceDoneCompleter != null);
return (
Uri httpUri, {
ReloadSources reloadSources,
Restart restart,
CompileExpression compileExpression,
GetSkSLMethod getSkSLMethod,
PrintStructuredErrorLogMethod printStructuredErrorLogMethod,
CompressionOptions compression,
Device device,
Logger logger,
}) async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
method: kListViewsMethod,
args: null,
jsonResponse: <String, Object>{
'views': <Object>[
<String, Object>{
'id': '1',
'isolate': fakeUnpausedIsolate.toJson()
},
],
},
),
FakeVmServiceRequest(
method: 'getVM',
args: null,
jsonResponse: vm_service.VM.parse(<String, Object>{})
.toJson(),
),
FakeVmServiceRequest(
method: '_createDevFS',
args: <String, Object>{
'fsName': globals.fs.currentDirectory.absolute.path,
},
jsonResponse: <String, Object>{
'uri': globals.fs.currentDirectory.absolute.path,
},
),
FakeVmServiceRequest(
method: kListViewsMethod,
args: null,
jsonResponse: <String, Object>{
'views': <Object>[
<String, Object>{
'id': '1',
'isolate': fakeUnpausedIsolate.toJson()
},
],
},
),
],
);
return fakeVmServiceHost.vmService;
};
}
class TestHotRunnerFactory extends HotRunnerFactory {
HotRunner _runner;
@override
HotRunner build(
List<FlutterDevice> devices, {
String target,
DebuggingOptions debuggingOptions,
bool benchmarkMode = false,
File applicationBinary,
bool hostIsIde = false,
String projectRootPath,
String packagesFilePath,
String dillOutputPath,
bool stayResident = true,
bool ipv6 = false,
FlutterProject flutterProject,
}) {
_runner ??= HotRunner(
devices,
target: target,
debuggingOptions: debuggingOptions,
benchmarkMode: benchmarkMode,
applicationBinary: applicationBinary,
hostIsIde: hostIsIde,
projectRootPath: projectRootPath,
dillOutputPath: dillOutputPath,
stayResident: stayResident,
ipv6: ipv6,
);
return _runner;
}
Future<void> exitApp() async {
assert(_runner != null);
await _runner.exit();
}
}
class FakeDartDevelopmentService extends Fake implements DartDevelopmentService {
@override
Future<void> get done => noopCompleter.future;
final Completer<void> noopCompleter = Completer<void>();
@override
Future<void> startDartDevelopmentService(
Uri observatoryUri, {
@required Logger logger,
int hostPort,
bool ipv6,
bool disableServiceAuthCodes,
}) async {}
@override
Uri get uri => Uri.parse('http://localhost:8181');
}
// 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 FakeAndroidDevice extends Fake implements AndroidDevice {
FakeAndroidDevice({@required this.id});
@override
DartDevelopmentService dds;
@override
final String id;
@override
String get name => 'd$id';
@override
Future<bool> get isLocalEmulator async => false;
@override
Future<String> get sdkNameAndVersion async => 'Android 46';
@override
Future<String> get targetPlatformDisplayName async => 'android';
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;
@override
bool isSupported() => true;
@override
bool get supportsHotRestart => true;
@override
bool get supportsFlutterExit => false;
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
@override
DevicePortForwarder portForwarder;
DeviceLogReader Function() onGetLogReader;
@override
FutureOr<DeviceLogReader> getLogReader({
AndroidApk app,
bool includePastLogs = false,
}) {
return onGetLogReader();
}
@override
OverrideArtifacts get artifactOverrides => null;
@override
final PlatformType platformType = PlatformType.android;
@override
Category get category => Category.mobile;
}
// 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 FakeIOSDevice extends Fake implements IOSDevice {
FakeIOSDevice({this.dds, this.portForwarder, this.logReader});
@override
final DevicePortForwarder portForwarder;
@override
final DartDevelopmentService dds;
final DeviceLogReader logReader;
@override
DeviceLogReader getLogReader({
IOSApp app,
bool includePastLogs = false,
}) => logReader;
@override
OverrideArtifacts get artifactOverrides => null;
@override
final String name = 'name';
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
@override
final PlatformType platformType = PlatformType.ios;
}