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

Observatory is being deprecated for Dart 3.0 so it should no longer be referenced in tooling messaging / flags. See https://github.com/dart-lang/sdk/issues/50233
1420 lines
49 KiB
Dart
1420 lines
49 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 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/android/android_device.dart';
|
|
import 'package:flutter_tools/src/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/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/attach.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
|
import 'package:flutter_tools/src/ios/application_package.dart';
|
|
import 'package:flutter_tools/src/ios/devices.dart';
|
|
import 'package:flutter_tools/src/ios/iproxy.dart';
|
|
import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
|
|
import 'package:flutter_tools/src/mdns_discovery.dart';
|
|
import 'package:flutter_tools/src/project.dart';
|
|
import 'package:flutter_tools/src/reporting/reporting.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:multicast_dns/multicast_dns.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/test_flutter_command_runner.dart';
|
|
|
|
class FakeStdio extends Fake implements Stdio {
|
|
@override
|
|
bool stdinHasTerminal = false;
|
|
}
|
|
|
|
class FakeProcessInfo extends Fake implements ProcessInfo {
|
|
@override
|
|
int maxRss = 0;
|
|
}
|
|
|
|
void main() {
|
|
tearDown(() {
|
|
MacOSDesignedForIPadDevices.allowDiscovery = false;
|
|
});
|
|
|
|
group('attach', () {
|
|
late StreamLogger logger;
|
|
late FileSystem testFileSystem;
|
|
late TestDeviceManager testDeviceManager;
|
|
late Artifacts artifacts;
|
|
late Stdio stdio;
|
|
late Terminal terminal;
|
|
late Signals signals;
|
|
late Platform platform;
|
|
late ProcessInfo processInfo;
|
|
|
|
setUp(() {
|
|
Cache.disableLocking();
|
|
logger = StreamLogger();
|
|
platform = FakePlatform();
|
|
testFileSystem = MemoryFileSystem.test();
|
|
testFileSystem.directory('lib').createSync();
|
|
testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync();
|
|
artifacts = Artifacts.test();
|
|
stdio = FakeStdio();
|
|
terminal = FakeTerminal();
|
|
signals = Signals.test();
|
|
processInfo = FakeProcessInfo();
|
|
testDeviceManager = TestDeviceManager(logger: BufferLogger.test());
|
|
});
|
|
|
|
group('with one device and no specified target file', () {
|
|
const int devicePort = 499;
|
|
const int hostPort = 42;
|
|
final int future = DateTime.now().add(const Duration(days: 1)).millisecondsSinceEpoch;
|
|
|
|
late FakeDeviceLogReader fakeLogReader;
|
|
late RecordingPortForwarder portForwarder;
|
|
late FakeDartDevelopmentService fakeDds;
|
|
late FakeAndroidDevice device;
|
|
|
|
setUp(() {
|
|
fakeLogReader = FakeDeviceLogReader();
|
|
portForwarder = RecordingPortForwarder(hostPort);
|
|
fakeDds = FakeDartDevelopmentService();
|
|
device = FakeAndroidDevice(id: '1')
|
|
..portForwarder = portForwarder
|
|
..dds = fakeDds;
|
|
});
|
|
|
|
tearDown(() {
|
|
fakeLogReader.dispose();
|
|
});
|
|
|
|
testUsingContext('succeeds with iOS device with protocol discovery', () async {
|
|
final FakeIOSDevice device = FakeIOSDevice(
|
|
logReader: fakeLogReader,
|
|
portForwarder: portForwarder,
|
|
majorSdkVersion: 12,
|
|
onGetLogReader: () {
|
|
fakeLogReader.addLine('Foo');
|
|
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
|
|
return fakeLogReader;
|
|
},
|
|
);
|
|
testDeviceManager.devices = <Device>[device];
|
|
final Completer<void> completer = Completer<void>();
|
|
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
|
|
if (message == '[verbose] VM Service URL on device: http://127.0.0.1:$devicePort') {
|
|
// The "VM Service URL on device" message is output by the ProtocolDiscovery when it found the VM Service.
|
|
completer.complete();
|
|
}
|
|
});
|
|
final FakeHotRunner hotRunner = FakeHotRunner();
|
|
hotRunner.onAttach = (
|
|
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
|
Completer<void>? appStartedCompleter,
|
|
bool allowExistingDdsInstance,
|
|
bool enableDevTools,
|
|
) async => 0;
|
|
hotRunner.exited = false;
|
|
hotRunner.isWaitingForVmService = false;
|
|
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
|
..hotRunner = hotRunner;
|
|
|
|
await createTestCommandRunner(AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>['attach']);
|
|
await completer.future;
|
|
|
|
expect(portForwarder.devicePort, devicePort);
|
|
expect(portForwarder.hostPort, hostPort);
|
|
|
|
await fakeLogReader.dispose();
|
|
await loggerSubscription.cancel();
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => logger,
|
|
DeviceManager: () => testDeviceManager,
|
|
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
preliminaryMDnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
logger: logger,
|
|
flutterUsage: TestUsage(),
|
|
),
|
|
});
|
|
|
|
testUsingContext('succeeds with iOS device with mDNS', () async {
|
|
final FakeIOSDevice device = FakeIOSDevice(
|
|
logReader: fakeLogReader,
|
|
portForwarder: portForwarder,
|
|
majorSdkVersion: 16,
|
|
onGetLogReader: () {
|
|
fakeLogReader.addLine('Foo');
|
|
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
|
|
return fakeLogReader;
|
|
},
|
|
);
|
|
testDeviceManager.devices = <Device>[device];
|
|
final FakeHotRunner hotRunner = FakeHotRunner();
|
|
hotRunner.onAttach = (
|
|
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
|
Completer<void>? appStartedCompleter,
|
|
bool allowExistingDdsInstance,
|
|
bool enableDevTools,
|
|
) async => 0;
|
|
hotRunner.exited = false;
|
|
hotRunner.isWaitingForVmService = false;
|
|
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
|
..hotRunner = hotRunner;
|
|
|
|
await createTestCommandRunner(AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>['attach']);
|
|
await fakeLogReader.dispose();
|
|
|
|
expect(portForwarder.devicePort, devicePort);
|
|
expect(portForwarder.hostPort, hostPort);
|
|
expect(hotRunnerFactory.devices, hasLength(1));
|
|
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
|
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
|
expect(vmServiceUri.toString(), 'http://127.0.0.1:$hostPort/xyz/');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => logger,
|
|
DeviceManager: () => testDeviceManager,
|
|
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
preliminaryMDnsClient: FakeMDnsClient(
|
|
<PtrResourceRecord>[
|
|
PtrResourceRecord('foo', future, domainName: 'bar'),
|
|
],
|
|
<String, List<SrvResourceRecord>>{
|
|
'bar': <SrvResourceRecord>[
|
|
SrvResourceRecord('bar', future, port: devicePort, weight: 1, priority: 1, target: 'appId'),
|
|
],
|
|
},
|
|
txtResponse: <String, List<TxtResourceRecord>>{
|
|
'bar': <TxtResourceRecord>[
|
|
TxtResourceRecord('bar', future, text: 'authCode=xyz\n'),
|
|
],
|
|
},
|
|
),
|
|
logger: logger,
|
|
flutterUsage: TestUsage(),
|
|
),
|
|
});
|
|
|
|
testUsingContext('succeeds with iOS device with mDNS network device', () async {
|
|
final FakeIOSDevice device = FakeIOSDevice(
|
|
logReader: fakeLogReader,
|
|
portForwarder: portForwarder,
|
|
majorSdkVersion: 16,
|
|
interfaceType: IOSDeviceConnectionInterface.network,
|
|
);
|
|
testDeviceManager.devices = <Device>[device];
|
|
final FakeHotRunner hotRunner = FakeHotRunner();
|
|
hotRunner.onAttach = (
|
|
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
|
Completer<void>? appStartedCompleter,
|
|
bool allowExistingDdsInstance,
|
|
bool enableDevTools,
|
|
) async => 0;
|
|
hotRunner.exited = false;
|
|
hotRunner.isWaitingForVmService = false;
|
|
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
|
..hotRunner = hotRunner;
|
|
|
|
await createTestCommandRunner(AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>['attach']);
|
|
await fakeLogReader.dispose();
|
|
|
|
expect(portForwarder.devicePort, null);
|
|
expect(portForwarder.hostPort, hostPort);
|
|
expect(hotRunnerFactory.devices, hasLength(1));
|
|
|
|
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
|
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
|
expect(vmServiceUri.toString(), 'http://111.111.111.111:123/xyz/');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => logger,
|
|
DeviceManager: () => testDeviceManager,
|
|
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
preliminaryMDnsClient: FakeMDnsClient(
|
|
<PtrResourceRecord>[
|
|
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
|
],
|
|
<String, List<SrvResourceRecord>>{
|
|
'srv-foo': <SrvResourceRecord>[
|
|
SrvResourceRecord('srv-foo', future, port: 123, weight: 1, priority: 1, target: 'target-foo'),
|
|
],
|
|
},
|
|
ipResponse: <String, List<IPAddressResourceRecord>>{
|
|
'target-foo': <IPAddressResourceRecord>[
|
|
IPAddressResourceRecord('target-foo', 0, address: InternetAddress.tryParse('111.111.111.111')!),
|
|
],
|
|
},
|
|
txtResponse: <String, List<TxtResourceRecord>>{
|
|
'srv-foo': <TxtResourceRecord>[
|
|
TxtResourceRecord('srv-foo', future, text: 'authCode=xyz\n'),
|
|
],
|
|
},
|
|
),
|
|
logger: logger,
|
|
flutterUsage: TestUsage(),
|
|
),
|
|
});
|
|
|
|
testUsingContext('succeeds with iOS device with mDNS network device with debug-port', () async {
|
|
final FakeIOSDevice device = FakeIOSDevice(
|
|
logReader: fakeLogReader,
|
|
portForwarder: portForwarder,
|
|
majorSdkVersion: 16,
|
|
interfaceType: IOSDeviceConnectionInterface.network,
|
|
);
|
|
testDeviceManager.devices = <Device>[device];
|
|
final FakeHotRunner hotRunner = FakeHotRunner();
|
|
hotRunner.onAttach = (
|
|
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
|
Completer<void>? appStartedCompleter,
|
|
bool allowExistingDdsInstance,
|
|
bool enableDevTools,
|
|
) async => 0;
|
|
hotRunner.exited = false;
|
|
hotRunner.isWaitingForVmService = false;
|
|
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
|
..hotRunner = hotRunner;
|
|
|
|
await createTestCommandRunner(AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>['attach', '--debug-port', '123']);
|
|
await fakeLogReader.dispose();
|
|
|
|
expect(portForwarder.devicePort, null);
|
|
expect(portForwarder.hostPort, hostPort);
|
|
expect(hotRunnerFactory.devices, hasLength(1));
|
|
|
|
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
|
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
|
expect(vmServiceUri.toString(), 'http://111.111.111.111:123/xyz/');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => logger,
|
|
DeviceManager: () => testDeviceManager,
|
|
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
preliminaryMDnsClient: FakeMDnsClient(
|
|
<PtrResourceRecord>[
|
|
PtrResourceRecord('bar', future, domainName: 'srv-bar'),
|
|
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
|
],
|
|
<String, List<SrvResourceRecord>>{
|
|
'srv-bar': <SrvResourceRecord>[
|
|
SrvResourceRecord('srv-bar', future, port: 321, weight: 1, priority: 1, target: 'target-bar'),
|
|
],
|
|
'srv-foo': <SrvResourceRecord>[
|
|
SrvResourceRecord('srv-foo', future, port: 123, weight: 1, priority: 1, target: 'target-foo'),
|
|
],
|
|
},
|
|
ipResponse: <String, List<IPAddressResourceRecord>>{
|
|
'target-foo': <IPAddressResourceRecord>[
|
|
IPAddressResourceRecord('target-foo', 0, address: InternetAddress.tryParse('111.111.111.111')!),
|
|
],
|
|
},
|
|
txtResponse: <String, List<TxtResourceRecord>>{
|
|
'srv-foo': <TxtResourceRecord>[
|
|
TxtResourceRecord('srv-foo', future, text: 'authCode=xyz\n'),
|
|
],
|
|
},
|
|
),
|
|
logger: logger,
|
|
flutterUsage: TestUsage(),
|
|
),
|
|
});
|
|
|
|
testUsingContext('succeeds with iOS device with mDNS network device with debug-url', () async {
|
|
final FakeIOSDevice device = FakeIOSDevice(
|
|
logReader: fakeLogReader,
|
|
portForwarder: portForwarder,
|
|
majorSdkVersion: 16,
|
|
interfaceType: IOSDeviceConnectionInterface.network,
|
|
);
|
|
testDeviceManager.devices = <Device>[device];
|
|
final FakeHotRunner hotRunner = FakeHotRunner();
|
|
hotRunner.onAttach = (
|
|
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
|
Completer<void>? appStartedCompleter,
|
|
bool allowExistingDdsInstance,
|
|
bool enableDevTools,
|
|
) async => 0;
|
|
hotRunner.exited = false;
|
|
hotRunner.isWaitingForVmService = false;
|
|
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
|
..hotRunner = hotRunner;
|
|
|
|
await createTestCommandRunner(AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>['attach', '--debug-url', 'https://0.0.0.0:123']);
|
|
await fakeLogReader.dispose();
|
|
|
|
expect(portForwarder.devicePort, null);
|
|
expect(portForwarder.hostPort, hostPort);
|
|
expect(hotRunnerFactory.devices, hasLength(1));
|
|
|
|
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
|
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
|
expect(vmServiceUri.toString(), 'http://111.111.111.111:123/xyz/');
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => logger,
|
|
DeviceManager: () => testDeviceManager,
|
|
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
preliminaryMDnsClient: FakeMDnsClient(
|
|
<PtrResourceRecord>[
|
|
PtrResourceRecord('bar', future, domainName: 'srv-bar'),
|
|
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
|
],
|
|
<String, List<SrvResourceRecord>>{
|
|
'srv-bar': <SrvResourceRecord>[
|
|
SrvResourceRecord('srv-bar', future, port: 321, weight: 1, priority: 1, target: 'target-bar'),
|
|
],
|
|
'srv-foo': <SrvResourceRecord>[
|
|
SrvResourceRecord('srv-foo', future, port: 123, weight: 1, priority: 1, target: 'target-foo'),
|
|
],
|
|
},
|
|
ipResponse: <String, List<IPAddressResourceRecord>>{
|
|
'target-foo': <IPAddressResourceRecord>[
|
|
IPAddressResourceRecord('target-foo', 0, address: InternetAddress.tryParse('111.111.111.111')!),
|
|
],
|
|
},
|
|
txtResponse: <String, List<TxtResourceRecord>>{
|
|
'srv-foo': <TxtResourceRecord>[
|
|
TxtResourceRecord('srv-foo', future, text: 'authCode=xyz\n'),
|
|
],
|
|
},
|
|
),
|
|
logger: logger,
|
|
flutterUsage: TestUsage(),
|
|
),
|
|
});
|
|
|
|
testUsingContext('finds VM Service 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.devices = <Device>[device];
|
|
final Completer<void> completer = Completer<void>();
|
|
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
|
|
if (message == '[verbose] VM Service URL on device: http://127.0.0.1:$devicePort') {
|
|
// The "VM Service URL on device" message is output by the ProtocolDiscovery when it found the VM Service.
|
|
completer.complete();
|
|
}
|
|
});
|
|
final Future<void> task = createTestCommandRunner(AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).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,
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('Fails with tool exit on bad VmService 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.devices = <Device>[device];
|
|
expect(() => createTestCommandRunner(AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>['attach']), throwsToolExit());
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => logger,
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
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.devices = <Device>[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.isWaitingForVmService = false;
|
|
|
|
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
|
..hotRunner = hotRunner;
|
|
|
|
final AttachCommand command = AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
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(),
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('exits when ipv6 is specified and debug-port is not', () async {
|
|
testDeviceManager.devices = <Device>[device];
|
|
|
|
final AttachCommand command = AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
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(),
|
|
DeviceManager: () => testDeviceManager,
|
|
},);
|
|
|
|
testUsingContext('exits when vm-service-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.devices = <Device>[device];
|
|
|
|
final AttachCommand command = AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
await expectLater(
|
|
createTestCommandRunner(command).run(<String>['attach', '--vm-service-port', '100']),
|
|
throwsToolExit(
|
|
message: 'When the --debug-port or --debug-url is unknown, this command does not use '
|
|
'the value of --vm-service-port.',
|
|
),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
DeviceManager: () => testDeviceManager,
|
|
},);
|
|
});
|
|
|
|
group('forwarding to given port', () {
|
|
const int devicePort = 499;
|
|
const int hostPort = 42;
|
|
late RecordingPortForwarder portForwarder;
|
|
late 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.devices = <Device>[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(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
))
|
|
.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,
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('succeeds in ipv6 mode', () async {
|
|
testDeviceManager.devices = <Device>[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(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
))
|
|
.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,
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('skips in ipv4 mode with a provided VM Service port', () async {
|
|
testDeviceManager.devices = <Device>[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(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(
|
|
<String>[
|
|
'attach',
|
|
'--debug-port',
|
|
'$devicePort',
|
|
'--vm-service-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,
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('skips in ipv6 mode with a provided VM Service port', () async {
|
|
testDeviceManager.devices = <Device>[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(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(
|
|
<String>[
|
|
'attach',
|
|
'--debug-port',
|
|
'$devicePort',
|
|
'--vm-service-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,
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
});
|
|
|
|
testUsingContext('exits when no device connected', () async {
|
|
final AttachCommand command = AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
await expectLater(
|
|
createTestCommandRunner(command).run(<String>['attach']),
|
|
throwsToolExit(),
|
|
);
|
|
expect(testLogger.statusText, containsIgnoringWhitespace('No supported devices connected'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('fails when targeted device is not Android with --device-user', () async {
|
|
final FakeIOSDevice device = FakeIOSDevice();
|
|
testDeviceManager.devices = <Device>[device];
|
|
expect(createTestCommandRunner(AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
)).run(<String>[
|
|
'attach',
|
|
'--device-user',
|
|
'10',
|
|
]), throwsToolExit(message: '--device-user is only supported for Android'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
testUsingContext('exits when multiple devices connected', () async {
|
|
final AttachCommand command = AttachCommand(
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
testDeviceManager.devices = <Device>[
|
|
FakeAndroidDevice(id: 'xx1'),
|
|
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(),
|
|
DeviceManager: () => testDeviceManager,
|
|
AnsiTerminal: () => FakeTerminal(stdinHasTerminal: false),
|
|
});
|
|
|
|
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.devices = <Device>[device];
|
|
testFileSystem.file('lib/main.dart').createSync();
|
|
|
|
final AttachCommand command = AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
await expectLater(createTestCommandRunner(command).run(<String>[
|
|
'attach',
|
|
]), throwsToolExit(message: 'Lost connection to device.'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
|
|
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.devices = <Device>[device];
|
|
testFileSystem.file('lib/main.dart').createSync();
|
|
|
|
final AttachCommand command = AttachCommand(
|
|
hotRunnerFactory: hotRunnerFactory,
|
|
artifacts: artifacts,
|
|
stdio: stdio,
|
|
logger: logger,
|
|
terminal: terminal,
|
|
signals: signals,
|
|
platform: platform,
|
|
processInfo: processInfo,
|
|
fileSystem: testFileSystem,
|
|
);
|
|
await expectLater(createTestCommandRunner(command).run(<String>[
|
|
'attach',
|
|
]), throwsA(isA<vm_service.RPCError>()));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => testFileSystem,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
DeviceManager: () => testDeviceManager,
|
|
});
|
|
});
|
|
}
|
|
|
|
class FakeHotRunner extends Fake implements HotRunner {
|
|
late Future<int> Function(Completer<DebugConnectionInfo>?, Completer<void>?, bool, bool) onAttach;
|
|
|
|
@override
|
|
bool exited = false;
|
|
|
|
@override
|
|
bool isWaitingForVmService = true;
|
|
|
|
@override
|
|
Future<int> attach({
|
|
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
|
Completer<void>? appStartedCompleter,
|
|
bool allowExistingDdsInstance = false,
|
|
bool enableDevTools = false,
|
|
bool needsFullRestart = true,
|
|
}) {
|
|
return onAttach(connectionInfoCompleter, appStartedCompleter, allowExistingDdsInstance, enableDevTools);
|
|
}
|
|
}
|
|
|
|
class FakeHotRunnerFactory extends Fake implements HotRunnerFactory {
|
|
late HotRunner hotRunner;
|
|
String? dillOutputPath;
|
|
String? projectRootPath;
|
|
late List<FlutterDevice> devices;
|
|
|
|
@override
|
|
HotRunner build(
|
|
List<FlutterDevice> devices, {
|
|
required String target,
|
|
required 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, {
|
|
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,
|
|
Duration? timeout,
|
|
SlowWarningCallback? slowWarningCallback,
|
|
}) {
|
|
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] ${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)),
|
|
);
|
|
}
|
|
|
|
class FakeDartDevelopmentService extends Fake implements DartDevelopmentService {
|
|
@override
|
|
Future<void> get done => noopCompleter.future;
|
|
final Completer<void> noopCompleter = Completer<void>();
|
|
|
|
@override
|
|
Future<void> startDartDevelopmentService(
|
|
Uri vmServiceUri, {
|
|
required Logger logger,
|
|
int? hostPort,
|
|
bool? ipv6,
|
|
bool? disableServiceAuthCodes,
|
|
bool cacheStartupProfile = false,
|
|
}) 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
|
|
late 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({
|
|
ApplicationPackage? app,
|
|
bool includePastLogs = false,
|
|
}) {
|
|
if (onGetLogReader == null) {
|
|
throw UnimplementedError(
|
|
'Called getLogReader but no onGetLogReader callback was supplied in the constructor to FakeAndroidDevice.',
|
|
);
|
|
}
|
|
return onGetLogReader!();
|
|
}
|
|
|
|
@override
|
|
OverrideArtifacts? get artifactOverrides => null;
|
|
|
|
@override
|
|
final PlatformType platformType = PlatformType.android;
|
|
|
|
@override
|
|
Category get category => Category.mobile;
|
|
|
|
@override
|
|
bool get ephemeral => true;
|
|
}
|
|
|
|
// 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({
|
|
DevicePortForwarder? portForwarder,
|
|
DeviceLogReader? logReader,
|
|
this.onGetLogReader,
|
|
this.interfaceType = IOSDeviceConnectionInterface.none,
|
|
this.majorSdkVersion = 0,
|
|
}) : _portForwarder = portForwarder, _logReader = logReader;
|
|
|
|
final DevicePortForwarder? _portForwarder;
|
|
@override
|
|
int majorSdkVersion;
|
|
|
|
@override
|
|
final IOSDeviceConnectionInterface interfaceType;
|
|
|
|
@override
|
|
DevicePortForwarder get portForwarder => _portForwarder!;
|
|
|
|
@override
|
|
DartDevelopmentService get dds => throw UnimplementedError('getter dds not implemented');
|
|
|
|
final DeviceLogReader? _logReader;
|
|
DeviceLogReader get logReader => _logReader!;
|
|
|
|
final DeviceLogReader Function()? onGetLogReader;
|
|
|
|
@override
|
|
DeviceLogReader getLogReader({
|
|
IOSApp? app,
|
|
bool includePastLogs = false,
|
|
}) {
|
|
if (onGetLogReader == null) {
|
|
throw UnimplementedError(
|
|
'Called getLogReader but no onGetLogReader callback was supplied in the constructor to FakeIOSDevice',
|
|
);
|
|
}
|
|
return onGetLogReader!();
|
|
}
|
|
|
|
@override
|
|
OverrideArtifacts? get artifactOverrides => null;
|
|
|
|
@override
|
|
final String name = 'name';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
|
|
|
@override
|
|
final PlatformType platformType = PlatformType.ios;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
bool isSupportedForProject(FlutterProject project) => true;
|
|
}
|
|
|
|
class FakeMDnsClient extends Fake implements MDnsClient {
|
|
FakeMDnsClient(this.ptrRecords, this.srvResponse, {
|
|
this.txtResponse = const <String, List<TxtResourceRecord>>{},
|
|
this.ipResponse = const <String, List<IPAddressResourceRecord>>{},
|
|
this.osErrorOnStart = false,
|
|
});
|
|
|
|
final List<PtrResourceRecord> ptrRecords;
|
|
final Map<String, List<SrvResourceRecord>> srvResponse;
|
|
final Map<String, List<TxtResourceRecord>> txtResponse;
|
|
final Map<String, List<IPAddressResourceRecord>> ipResponse;
|
|
final bool osErrorOnStart;
|
|
|
|
@override
|
|
Future<void> start({
|
|
InternetAddress? listenAddress,
|
|
NetworkInterfacesFactory? interfacesFactory,
|
|
int mDnsPort = 5353,
|
|
InternetAddress? mDnsAddress,
|
|
}) async {
|
|
if (osErrorOnStart) {
|
|
throw const OSError('Operation not supported on socket', 102);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Stream<T> lookup<T extends ResourceRecord>(
|
|
ResourceRecordQuery query, {
|
|
Duration timeout = const Duration(seconds: 5),
|
|
}) {
|
|
if (T == PtrResourceRecord && query.fullyQualifiedName == MDnsVmServiceDiscovery.dartVmServiceName) {
|
|
return Stream<PtrResourceRecord>.fromIterable(ptrRecords) as Stream<T>;
|
|
}
|
|
if (T == SrvResourceRecord) {
|
|
final String key = query.fullyQualifiedName;
|
|
return Stream<SrvResourceRecord>.fromIterable(srvResponse[key] ?? <SrvResourceRecord>[]) as Stream<T>;
|
|
}
|
|
if (T == TxtResourceRecord) {
|
|
final String key = query.fullyQualifiedName;
|
|
return Stream<TxtResourceRecord>.fromIterable(txtResponse[key] ?? <TxtResourceRecord>[]) as Stream<T>;
|
|
}
|
|
if (T == IPAddressResourceRecord) {
|
|
final String key = query.fullyQualifiedName;
|
|
return Stream<IPAddressResourceRecord>.fromIterable(ipResponse[key] ?? <IPAddressResourceRecord>[]) as Stream<T>;
|
|
}
|
|
throw UnsupportedError('Unsupported query type $T');
|
|
}
|
|
|
|
@override
|
|
void stop() {}
|
|
}
|
|
|
|
class TestDeviceManager extends DeviceManager {
|
|
TestDeviceManager({required this.logger}) : super(logger: logger);
|
|
List<Device> devices = <Device>[];
|
|
|
|
final BufferLogger logger;
|
|
|
|
@override
|
|
List<DeviceDiscovery> get deviceDiscoverers {
|
|
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
devices.forEach(discoverer.addDevice);
|
|
return <DeviceDiscovery>[discoverer];
|
|
}
|
|
}
|
|
|
|
class FakeTerminal extends Fake implements AnsiTerminal {
|
|
FakeTerminal({this.stdinHasTerminal = true});
|
|
|
|
@override
|
|
final bool stdinHasTerminal;
|
|
|
|
@override
|
|
bool usesTerminalUi = false;
|
|
}
|