mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

* 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
901 lines
28 KiB
Dart
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;
|
|
}
|