Support iOS wireless debugging (#118104)
* setup wireless debugging to use device IP * fix tests * fix unused var and missing annotation * remove unneeded try catch * remove commented out line, change null to package id * better way to get package id * update mDNS lookup to continously check for server, add messaging if takes too long to find observatory url, update flutter drive to enable publish-port if using network device * Refactor mDNS Discovery to poll for observatories and better handle multiple instances of the same app. Update drive command to make publish-port more stable. Update attach for iOS to only use Protocol Discovery if applicable, run mDNS and Protocol Discovery simultaneously, handle --debug-port/--debug-url/--device-vmservice-port, continously poll for obseravtories with mDNS, include port in error message when mutliple available * add and update comments, use logger spinner intead of timer in flutter attach, other small improvements * add newline to message so next log won't be on same line * fix install/waiting for permission status progress so it doens't double print the time it took. * only print backtrace if observatory times out on a physical usb connected device * fix test * Update related references from Observatory to Dart VM Service * fix test
This commit is contained in:
parent
67ffaef253
commit
5cd2d4c61e
@ -16,7 +16,7 @@ import '../base/logger.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/signals.dart';
|
||||
import '../base/terminal.dart';
|
||||
import '../build_info.dart';
|
||||
import '../build_info.dart';
|
||||
import '../commands/daemon.dart';
|
||||
import '../compile.dart';
|
||||
import '../daemon.dart';
|
||||
@ -24,6 +24,7 @@ import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../fuchsia/fuchsia_device.dart';
|
||||
import '../ios/devices.dart';
|
||||
import '../ios/iproxy.dart';
|
||||
import '../ios/simulators.dart';
|
||||
import '../macos/macos_ipad_device.dart';
|
||||
import '../mdns_discovery.dart';
|
||||
@ -229,7 +230,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
}
|
||||
if (debugPort != null && debugUri != null) {
|
||||
throwToolExit(
|
||||
'Either --debugPort or --debugUri can be provided, not both.');
|
||||
'Either --debug-port or --debug-url can be provided, not both.');
|
||||
}
|
||||
|
||||
if (userIdentifier != null) {
|
||||
@ -282,8 +283,9 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
final String ipv6Loopback = InternetAddress.loopbackIPv6.address;
|
||||
final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
|
||||
final String hostname = usesIpv6 ? ipv6Loopback : ipv4Loopback;
|
||||
final bool isNetworkDevice = (device is IOSDevice) && device.interfaceType == IOSDeviceConnectionInterface.network;
|
||||
|
||||
if (debugPort == null && debugUri == null) {
|
||||
if ((debugPort == null && debugUri == null) || isNetworkDevice) {
|
||||
if (device is FuchsiaDevice) {
|
||||
final String module = stringArgDeprecated('module')!;
|
||||
if (module == null) {
|
||||
@ -303,16 +305,73 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
rethrow;
|
||||
}
|
||||
} else if ((device is IOSDevice) || (device is IOSSimulator) || (device is MacOSDesignedForIPadDevice)) {
|
||||
final Uri? uriFromMdns =
|
||||
await MDnsObservatoryDiscovery.instance!.getObservatoryUri(
|
||||
appId,
|
||||
device,
|
||||
usesIpv6: usesIpv6,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
// Protocol Discovery relies on logging. On iOS earlier than 13, logging is gathered using syslog.
|
||||
// syslog is not available for iOS 13+. For iOS 13+, Protocol Discovery gathers logs from the VMService.
|
||||
// Since we don't have access to the VMService yet, Protocol Discovery cannot be used for iOS 13+.
|
||||
// Also, network devices must be found using mDNS and cannot use Protocol Discovery.
|
||||
final bool compatibleWithProtocolDiscovery = (device is IOSDevice) &&
|
||||
device.majorSdkVersion < IOSDeviceLogReader.minimumUniversalLoggingSdkVersion &&
|
||||
!isNetworkDevice;
|
||||
|
||||
_logger.printStatus('Waiting for a connection from Flutter on ${device.name}...');
|
||||
final Status discoveryStatus = _logger.startSpinner(
|
||||
timeout: const Duration(seconds: 30),
|
||||
slowWarningCallback: () {
|
||||
// If relying on mDNS to find Dart VM Service, remind the user to allow local network permissions.
|
||||
if (!compatibleWithProtocolDiscovery) {
|
||||
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n\n'
|
||||
'Click "Allow" to the prompt asking if you would like to find and connect devices on your local network. '
|
||||
'If you selected "Don\'t Allow", you can turn it on in Settings > Your App Name > Local Network. '
|
||||
"If you don't see your app in the Settings, uninstall the app and rerun to see the prompt again.\n";
|
||||
}
|
||||
|
||||
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n';
|
||||
},
|
||||
);
|
||||
|
||||
int? devicePort;
|
||||
if (debugPort != null) {
|
||||
devicePort = debugPort;
|
||||
} else if (debugUri != null) {
|
||||
devicePort = debugUri?.port;
|
||||
} else if (deviceVmservicePort != null) {
|
||||
devicePort = deviceVmservicePort;
|
||||
}
|
||||
|
||||
final Future<Uri?> mDNSDiscoveryFuture = MDnsVmServiceDiscovery.instance!.getVMServiceUriForAttach(
|
||||
appId,
|
||||
device,
|
||||
usesIpv6: usesIpv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
deviceVmservicePort: devicePort,
|
||||
);
|
||||
|
||||
Future<Uri?>? protocolDiscoveryFuture;
|
||||
if (compatibleWithProtocolDiscovery) {
|
||||
final ProtocolDiscovery vmServiceDiscovery = ProtocolDiscovery.observatory(
|
||||
device.getLogReader(),
|
||||
portForwarder: device.portForwarder,
|
||||
ipv6: ipv6!,
|
||||
devicePort: devicePort,
|
||||
hostPort: hostVmservicePort,
|
||||
logger: _logger,
|
||||
);
|
||||
observatoryUri = uriFromMdns == null
|
||||
protocolDiscoveryFuture = vmServiceDiscovery.uri;
|
||||
}
|
||||
|
||||
final Uri? foundUrl;
|
||||
if (protocolDiscoveryFuture == null) {
|
||||
foundUrl = await mDNSDiscoveryFuture;
|
||||
} else {
|
||||
foundUrl = await Future.any(
|
||||
<Future<Uri?>>[mDNSDiscoveryFuture, protocolDiscoveryFuture]
|
||||
);
|
||||
}
|
||||
discoveryStatus.stop();
|
||||
|
||||
observatoryUri = foundUrl == null
|
||||
? null
|
||||
: Stream<Uri>.value(uriFromMdns).asBroadcastStream();
|
||||
: Stream<Uri>.value(foundUrl).asBroadcastStream();
|
||||
}
|
||||
// If MDNS discovery fails or we're not on iOS, fallback to ProtocolDiscovery.
|
||||
if (observatoryUri == null) {
|
||||
@ -335,7 +394,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
} else {
|
||||
observatoryUri = Stream<Uri>
|
||||
.fromFuture(
|
||||
buildObservatoryUri(
|
||||
buildVMServiceUri(
|
||||
device,
|
||||
debugUri?.host ?? hostname,
|
||||
debugPort ?? debugUri!.port,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:package_config/package_config_types.dart';
|
||||
|
||||
@ -21,6 +22,8 @@ import '../dart/package_map.dart';
|
||||
import '../device.dart';
|
||||
import '../drive/drive_service.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../ios/devices.dart';
|
||||
import '../ios/iproxy.dart';
|
||||
import '../resident_runner.dart';
|
||||
import '../runner/flutter_command.dart' show FlutterCommandCategory, FlutterCommandResult, FlutterOptions;
|
||||
import '../web/web_device.dart';
|
||||
@ -203,6 +206,27 @@ class DriveCommand extends RunCommandBase {
|
||||
@override
|
||||
bool get cachePubGet => false;
|
||||
|
||||
String? get applicationBinaryPath => stringArgDeprecated(FlutterOptions.kUseApplicationBinary);
|
||||
|
||||
Future<Device?> get targetedDevice async {
|
||||
return findTargetDevice(includeUnsupportedDevices: applicationBinaryPath == null);
|
||||
}
|
||||
|
||||
// Network devices need `publish-port` to be enabled because it requires mDNS.
|
||||
// If the flag wasn't provided as an actual argument and it's a network device,
|
||||
// change it to be enabled.
|
||||
@override
|
||||
Future<bool> get disablePortPublication async {
|
||||
final ArgResults? localArgResults = argResults;
|
||||
final Device? device = await targetedDevice;
|
||||
final bool isNetworkDevice = device is IOSDevice && device.interfaceType == IOSDeviceConnectionInterface.network;
|
||||
if (isNetworkDevice && localArgResults != null && !localArgResults.wasParsed('publish-port')) {
|
||||
_logger.printTrace('Network device is being used. Changing `publish-port` to be enabled.');
|
||||
return false;
|
||||
}
|
||||
return !boolArgDeprecated('publish-port');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> validateCommand() async {
|
||||
if (userIdentifier != null) {
|
||||
@ -223,8 +247,7 @@ class DriveCommand extends RunCommandBase {
|
||||
if (await _fileSystem.type(testFile) != FileSystemEntityType.file) {
|
||||
throwToolExit('Test file not found: $testFile');
|
||||
}
|
||||
final String? applicationBinaryPath = stringArgDeprecated(FlutterOptions.kUseApplicationBinary);
|
||||
final Device? device = await findTargetDevice(includeUnsupportedDevices: applicationBinaryPath == null);
|
||||
final Device? device = await targetedDevice;
|
||||
if (device == null) {
|
||||
throwToolExit(null);
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
|
||||
purgePersistentCache: purgePersistentCache,
|
||||
deviceVmServicePort: deviceVmservicePort,
|
||||
hostVmServicePort: hostVmservicePort,
|
||||
disablePortPublication: disablePortPublication,
|
||||
disablePortPublication: await disablePortPublication,
|
||||
ddsPort: ddsPort,
|
||||
devToolsServerAddress: devToolsServerAddress,
|
||||
verboseSystemLogs: boolArgDeprecated('verbose-system-logs'),
|
||||
|
@ -275,7 +275,7 @@ Future<T> runInContext<T>(
|
||||
featureFlags: featureFlags,
|
||||
platform: globals.platform,
|
||||
),
|
||||
MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(
|
||||
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
||||
logger: globals.logger,
|
||||
flutterUsage: globals.flutterUsage,
|
||||
),
|
||||
|
@ -17,6 +17,7 @@ import 'base/utils.dart';
|
||||
import 'build_info.dart';
|
||||
import 'devfs.dart';
|
||||
import 'device_port_forwarder.dart';
|
||||
import 'ios/iproxy.dart';
|
||||
import 'project.dart';
|
||||
import 'vmservice.dart';
|
||||
|
||||
@ -917,7 +918,13 @@ class DebuggingOptions {
|
||||
/// * https://github.com/dart-lang/sdk/blob/main/sdk/lib/html/doc/NATIVE_NULL_ASSERTIONS.md
|
||||
final bool nativeNullAssertions;
|
||||
|
||||
List<String> getIOSLaunchArguments(EnvironmentType environmentType, String? route, Map<String, Object?> platformArgs) {
|
||||
List<String> getIOSLaunchArguments(
|
||||
EnvironmentType environmentType,
|
||||
String? route,
|
||||
Map<String, Object?> platformArgs, {
|
||||
bool ipv6 = false,
|
||||
IOSDeviceConnectionInterface interfaceType = IOSDeviceConnectionInterface.none
|
||||
}) {
|
||||
final String dartVmFlags = computeDartVmFlags(this);
|
||||
return <String>[
|
||||
if (enableDartProfiling) '--enable-dart-profiling',
|
||||
@ -954,6 +961,9 @@ class DebuggingOptions {
|
||||
// Use the suggested host port.
|
||||
if (environmentType == EnvironmentType.simulator && hostVmServicePort != null)
|
||||
'--observatory-port=$hostVmServicePort',
|
||||
// Tell the observatory to listen on all interfaces, don't restrict to the loopback.
|
||||
if (interfaceType == IOSDeviceConnectionInterface.network)
|
||||
'--observatory-host=${ipv6 ? '::0' : '0.0.0.0'}',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import 'package:process/process.dart';
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
|
||||
import '../application_package.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
@ -21,6 +22,7 @@ import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../macos/xcdevice.dart';
|
||||
import '../mdns_discovery.dart';
|
||||
import '../project.dart';
|
||||
import '../protocol_discovery.dart';
|
||||
import '../vmservice.dart';
|
||||
@ -189,15 +191,6 @@ class IOSDevice extends Device {
|
||||
return majorVersionString != null ? int.tryParse(majorVersionString) ?? 0 : 0;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get supportsHotReload => interfaceType == IOSDeviceConnectionInterface.usb;
|
||||
|
||||
@override
|
||||
bool get supportsHotRestart => interfaceType == IOSDeviceConnectionInterface.usb;
|
||||
|
||||
@override
|
||||
bool get supportsFlutterExit => interfaceType == IOSDeviceConnectionInterface.usb;
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@ -318,7 +311,11 @@ class IOSDevice extends Device {
|
||||
@visibleForTesting Duration? discoveryTimeout,
|
||||
}) async {
|
||||
String? packageId;
|
||||
|
||||
if (interfaceType == IOSDeviceConnectionInterface.network &&
|
||||
debuggingOptions.debuggingEnabled &&
|
||||
debuggingOptions.disablePortPublication) {
|
||||
throwToolExit('Cannot start app on wirelessly tethered iOS device. Try running again with the --publish-port flag');
|
||||
}
|
||||
if (!prebuiltApplication) {
|
||||
_logger.printTrace('Building ${package.name} for $id');
|
||||
|
||||
@ -353,8 +350,10 @@ class IOSDevice extends Device {
|
||||
EnvironmentType.physical,
|
||||
route,
|
||||
platformArgs,
|
||||
ipv6: ipv6,
|
||||
interfaceType: interfaceType,
|
||||
);
|
||||
final Status installStatus = _logger.startProgress(
|
||||
Status startAppStatus = _logger.startProgress(
|
||||
'Installing and launching...',
|
||||
);
|
||||
try {
|
||||
@ -379,9 +378,10 @@ class IOSDevice extends Device {
|
||||
deviceLogReader.debuggerStream = iosDeployDebugger;
|
||||
}
|
||||
}
|
||||
// Don't port foward if debugging with a network device.
|
||||
observatoryDiscovery = ProtocolDiscovery.observatory(
|
||||
deviceLogReader,
|
||||
portForwarder: portForwarder,
|
||||
portForwarder: interfaceType == IOSDeviceConnectionInterface.network ? null : portForwarder,
|
||||
hostPort: debuggingOptions.hostVmServicePort,
|
||||
devicePort: debuggingOptions.deviceVmServicePort,
|
||||
ipv6: ipv6,
|
||||
@ -412,12 +412,59 @@ class IOSDevice extends Device {
|
||||
return LaunchResult.succeeded();
|
||||
}
|
||||
|
||||
_logger.printTrace('Application launched on the device. Waiting for observatory url.');
|
||||
final Timer timer = Timer(discoveryTimeout ?? const Duration(seconds: 30), () {
|
||||
_logger.printError('iOS Observatory not discovered after 30 seconds. This is taking much longer than expected...');
|
||||
iosDeployDebugger?.pauseDumpBacktraceResume();
|
||||
_logger.printTrace('Application launched on the device. Waiting for Dart VM Service url.');
|
||||
|
||||
final int defaultTimeout = interfaceType == IOSDeviceConnectionInterface.network ? 45 : 30;
|
||||
final Timer timer = Timer(discoveryTimeout ?? Duration(seconds: defaultTimeout), () {
|
||||
_logger.printError('The Dart VM Service was not discovered after $defaultTimeout seconds. This is taking much longer than expected...');
|
||||
|
||||
// If debugging with a wireless device and the timeout is reached, remind the
|
||||
// user to allow local network permissions.
|
||||
if (interfaceType == IOSDeviceConnectionInterface.network) {
|
||||
_logger.printError(
|
||||
'\nClick "Allow" to the prompt asking if you would like to find and connect devices on your local network. '
|
||||
'This is required for wireless debugging. If you selected "Don\'t Allow", '
|
||||
'you can turn it on in Settings > Your App Name > Local Network. '
|
||||
"If you don't see your app in the Settings, uninstall the app and rerun to see the prompt again."
|
||||
);
|
||||
} else {
|
||||
iosDeployDebugger?.pauseDumpBacktraceResume();
|
||||
}
|
||||
});
|
||||
final Uri? localUri = await observatoryDiscovery?.uri;
|
||||
|
||||
Uri? localUri;
|
||||
if (interfaceType == IOSDeviceConnectionInterface.network) {
|
||||
// Wait for Dart VM Service to start up.
|
||||
final Uri? serviceURL = await observatoryDiscovery?.uri;
|
||||
if (serviceURL == null) {
|
||||
await iosDeployDebugger?.stopAndDumpBacktrace();
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
|
||||
// If Dart VM Service URL with the device IP is not found within 5 seconds,
|
||||
// change the status message to prompt users to click Allow. Wait 5 seconds because it
|
||||
// should only show this message if they have not already approved the permissions.
|
||||
// MDnsVmServiceDiscovery usually takes less than 5 seconds to find it.
|
||||
final Timer mDNSLookupTimer = Timer(const Duration(seconds: 5), () {
|
||||
startAppStatus.stop();
|
||||
startAppStatus = _logger.startProgress(
|
||||
'Waiting for approval of local network permissions...',
|
||||
);
|
||||
});
|
||||
|
||||
// Get Dart VM Service URL with the device IP as the host.
|
||||
localUri = await MDnsVmServiceDiscovery.instance!.getVMServiceUriForLaunch(
|
||||
packageId,
|
||||
this,
|
||||
usesIpv6: ipv6,
|
||||
deviceVmservicePort: serviceURL.port,
|
||||
isNetworkDevice: true,
|
||||
);
|
||||
|
||||
mDNSLookupTimer.cancel();
|
||||
} else {
|
||||
localUri = await observatoryDiscovery?.uri;
|
||||
}
|
||||
timer.cancel();
|
||||
if (localUri == null) {
|
||||
await iosDeployDebugger?.stopAndDumpBacktrace();
|
||||
@ -429,7 +476,7 @@ class IOSDevice extends Device {
|
||||
_logger.printError(e.message);
|
||||
return LaunchResult.failed();
|
||||
} finally {
|
||||
installStatus.stop();
|
||||
startAppStatus.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -569,7 +616,6 @@ String decodeSyslog(String line) {
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
class IOSDeviceLogReader extends DeviceLogReader {
|
||||
IOSDeviceLogReader._(
|
||||
this._iMobileDevice,
|
||||
|
@ -227,8 +227,16 @@ class XCDevice {
|
||||
|
||||
/// [timeout] defaults to 2 seconds.
|
||||
Future<List<IOSDevice>> getAvailableIOSDevices({ Duration? timeout }) async {
|
||||
Status? loadDevicesStatus;
|
||||
if (timeout != null && timeout.inSeconds > 2) {
|
||||
loadDevicesStatus = _logger.startProgress(
|
||||
'Loading devices...',
|
||||
);
|
||||
}
|
||||
final List<Object>? allAvailableDevices = await _getAllDevices(timeout: timeout ?? const Duration(seconds: 2));
|
||||
|
||||
if (loadDevicesStatus != null) {
|
||||
loadDevicesStatus.stop();
|
||||
}
|
||||
if (allAvailableDevices == null) {
|
||||
return const <IOSDevice>[];
|
||||
}
|
||||
@ -305,12 +313,6 @@ class XCDevice {
|
||||
|
||||
final IOSDeviceConnectionInterface interface = _interfaceType(device);
|
||||
|
||||
// Only support USB devices, skip "network" interface (Xcode > Window > Devices and Simulators > Connect via network).
|
||||
// TODO(jmagman): Remove this check once wirelessly detected devices can be observed and attached, https://github.com/flutter/flutter/issues/15072.
|
||||
if (interface != IOSDeviceConnectionInterface.usb) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String? sdkVersion = _sdkVersion(device);
|
||||
|
||||
if (sdkVersion != null) {
|
||||
|
@ -13,161 +13,427 @@ import 'build_info.dart';
|
||||
import 'device.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
|
||||
/// A wrapper around [MDnsClient] to find a Dart observatory instance.
|
||||
class MDnsObservatoryDiscovery {
|
||||
/// Creates a new [MDnsObservatoryDiscovery] object.
|
||||
/// A wrapper around [MDnsClient] to find a Dart VM Service instance.
|
||||
class MDnsVmServiceDiscovery {
|
||||
/// Creates a new [MDnsVmServiceDiscovery] object.
|
||||
///
|
||||
/// The [_client] parameter will be defaulted to a new [MDnsClient] if null.
|
||||
/// The [applicationId] parameter may be null, and can be used to
|
||||
/// automatically select which application to use if multiple are advertising
|
||||
/// Dart observatory ports.
|
||||
MDnsObservatoryDiscovery({
|
||||
MDnsVmServiceDiscovery({
|
||||
MDnsClient? mdnsClient,
|
||||
MDnsClient? preliminaryMDnsClient,
|
||||
required Logger logger,
|
||||
required Usage flutterUsage,
|
||||
}): _client = mdnsClient ?? MDnsClient(),
|
||||
_logger = logger,
|
||||
_flutterUsage = flutterUsage;
|
||||
}) : _client = mdnsClient ?? MDnsClient(),
|
||||
_preliminaryClient = preliminaryMDnsClient,
|
||||
_logger = logger,
|
||||
_flutterUsage = flutterUsage;
|
||||
|
||||
final MDnsClient _client;
|
||||
|
||||
// Used when discovering VM services with `queryForAttach` to do a preliminary
|
||||
// check for already running services so that results are not cached in _client.
|
||||
final MDnsClient? _preliminaryClient;
|
||||
|
||||
final Logger _logger;
|
||||
final Usage _flutterUsage;
|
||||
|
||||
@visibleForTesting
|
||||
static const String dartObservatoryName = '_dartobservatory._tcp.local';
|
||||
static const String dartVmServiceName = '_dartobservatory._tcp.local';
|
||||
|
||||
static MDnsObservatoryDiscovery? get instance => context.get<MDnsObservatoryDiscovery>();
|
||||
static MDnsVmServiceDiscovery? get instance => context.get<MDnsVmServiceDiscovery>();
|
||||
|
||||
/// Executes an mDNS query for a Dart Observatory.
|
||||
/// Executes an mDNS query for Dart VM Services.
|
||||
/// Checks for services that have already been launched.
|
||||
/// If none are found, it will listen for new services to become active
|
||||
/// and return the first it finds that match the parameters.
|
||||
///
|
||||
/// The [applicationId] parameter may be used to specify which application
|
||||
/// to find. For Android, it refers to the package name; on iOS, it refers to
|
||||
/// the bundle ID.
|
||||
///
|
||||
/// If it is not null, this method will find the port and authentication code
|
||||
/// of the Dart Observatory for that application. If it cannot find a Dart
|
||||
/// Observatory matching that application identifier, it will call
|
||||
/// [throwToolExit].
|
||||
/// The [deviceVmservicePort] parameter may be used to specify which port
|
||||
/// to find.
|
||||
///
|
||||
/// If it is null and there are multiple ports available, the user will be
|
||||
/// prompted with a list of available observatory ports and asked to select
|
||||
/// one.
|
||||
/// The [isNetworkDevice] parameter flags whether to get the device IP
|
||||
/// and the [ipv6] parameter flags whether to get an iPv6 address
|
||||
/// (otherwise it will get iPv4).
|
||||
///
|
||||
/// If it is null and there is only one available instance of Observatory,
|
||||
/// it will return that instance's information regardless of what application
|
||||
/// the Observatory instance is for.
|
||||
/// The [timeout] parameter determines how long to continue to wait for
|
||||
/// services to become active.
|
||||
///
|
||||
/// If [applicationId] is not null, this method will find the port and authentication code
|
||||
/// of the Dart VM Service for that application. If it cannot find a service matching
|
||||
/// that application identifier after the [timeout], it will call [throwToolExit].
|
||||
///
|
||||
/// If [applicationId] is null and there are multiple Dart VM Services available,
|
||||
/// the user will be prompted with a list of available services with the respective
|
||||
/// app-id and device-vmservice-port to use and asked to select one.
|
||||
///
|
||||
/// If it is null and there is only one available or it's the first found instance
|
||||
/// of Dart VM Service, it will return that instance's information regardless of
|
||||
/// what application the service instance is for.
|
||||
@visibleForTesting
|
||||
Future<MDnsObservatoryDiscoveryResult?> query({String? applicationId, int? deviceVmservicePort}) async {
|
||||
_logger.printTrace('Checking for advertised Dart observatories...');
|
||||
try {
|
||||
await _client.start();
|
||||
final List<PtrResourceRecord> pointerRecords = await _client
|
||||
.lookup<PtrResourceRecord>(
|
||||
ResourceRecordQuery.serverPointer(dartObservatoryName),
|
||||
)
|
||||
.toList();
|
||||
if (pointerRecords.isEmpty) {
|
||||
_logger.printTrace('No pointer records found.');
|
||||
return null;
|
||||
Future<MDnsVmServiceDiscoveryResult?> queryForAttach({
|
||||
String? applicationId,
|
||||
int? deviceVmservicePort,
|
||||
bool ipv6 = false,
|
||||
bool isNetworkDevice = false,
|
||||
Duration timeout = const Duration(minutes: 10),
|
||||
}) async {
|
||||
// Poll for 5 seconds to see if there are already services running.
|
||||
// Use a new instance of MDnsClient so results don't get cached in _client.
|
||||
// If no results are found, poll for a longer duration to wait for connections.
|
||||
// If more than 1 result is found, throw an error since it can't be determined which to pick.
|
||||
// If only one is found, return it.
|
||||
final List<MDnsVmServiceDiscoveryResult> results = await _pollingVmService(
|
||||
_preliminaryClient ?? MDnsClient(),
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
ipv6: ipv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
if (results.isEmpty) {
|
||||
return firstMatchingVmService(
|
||||
_client,
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
ipv6: ipv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
timeout: timeout,
|
||||
);
|
||||
} else if (results.length > 1) {
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
buffer.writeln('There are multiple Dart VM Services available.');
|
||||
buffer.writeln('Rerun this command with one of the following passed in as the app-id and device-vmservice-port:');
|
||||
buffer.writeln();
|
||||
for (final MDnsVmServiceDiscoveryResult result in results) {
|
||||
buffer.writeln(
|
||||
' flutter attach --app-id "${result.domainName.replaceAll('.$dartVmServiceName', '')}" --device-vmservice-port ${result.port}');
|
||||
}
|
||||
// We have no guarantee that we won't get multiple hits from the same
|
||||
// service on this.
|
||||
final Set<String> uniqueDomainNames = pointerRecords
|
||||
.map<String>((PtrResourceRecord record) => record.domainName)
|
||||
.toSet();
|
||||
throwToolExit(buffer.toString());
|
||||
}
|
||||
return results.first;
|
||||
}
|
||||
|
||||
String? domainName;
|
||||
if (applicationId != null) {
|
||||
for (final String name in uniqueDomainNames) {
|
||||
if (name.toLowerCase().startsWith(applicationId.toLowerCase())) {
|
||||
domainName = name;
|
||||
/// Executes an mDNS query for Dart VM Services.
|
||||
/// Listens for new services to become active and returns the first it finds that
|
||||
/// match the parameters.
|
||||
///
|
||||
/// The [applicationId] parameter must be set to specify which application
|
||||
/// to find. For Android, it refers to the package name; on iOS, it refers to
|
||||
/// the bundle ID.
|
||||
///
|
||||
/// The [deviceVmservicePort] parameter must be set to specify which port
|
||||
/// to find.
|
||||
///
|
||||
/// [applicationId] and [deviceVmservicePort] are required for launch so that
|
||||
/// if multiple flutter apps are running on different devices, it will
|
||||
/// only match with the device running the desired app.
|
||||
///
|
||||
/// The [isNetworkDevice] parameter flags whether to get the device IP
|
||||
/// and the [ipv6] parameter flags whether to get an iPv6 address
|
||||
/// (otherwise it will get iPv4).
|
||||
///
|
||||
/// The [timeout] parameter determines how long to continue to wait for
|
||||
/// services to become active.
|
||||
///
|
||||
/// If a Dart VM Service matching the [applicationId] and [deviceVmservicePort]
|
||||
/// cannot be found after the [timeout], it will call [throwToolExit].
|
||||
@visibleForTesting
|
||||
Future<MDnsVmServiceDiscoveryResult?> queryForLaunch({
|
||||
required String applicationId,
|
||||
required int deviceVmservicePort,
|
||||
bool ipv6 = false,
|
||||
bool isNetworkDevice = false,
|
||||
Duration timeout = const Duration(minutes: 10),
|
||||
}) async {
|
||||
// Query for a specific application and device port.
|
||||
return firstMatchingVmService(
|
||||
_client,
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
ipv6: ipv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
||||
/// Polls for Dart VM Services and returns the first it finds that match
|
||||
/// the [applicationId]/[deviceVmservicePort] (if applicable).
|
||||
/// Returns null if no results are found.
|
||||
@visibleForTesting
|
||||
Future<MDnsVmServiceDiscoveryResult?> firstMatchingVmService(
|
||||
MDnsClient client, {
|
||||
String? applicationId,
|
||||
int? deviceVmservicePort,
|
||||
bool ipv6 = false,
|
||||
bool isNetworkDevice = false,
|
||||
Duration timeout = const Duration(minutes: 10),
|
||||
}) async {
|
||||
final List<MDnsVmServiceDiscoveryResult> results = await _pollingVmService(
|
||||
client,
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
ipv6: ipv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
timeout: timeout,
|
||||
quitOnFind: true,
|
||||
);
|
||||
if (results.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return results.first;
|
||||
}
|
||||
|
||||
Future<List<MDnsVmServiceDiscoveryResult>> _pollingVmService(
|
||||
MDnsClient client, {
|
||||
String? applicationId,
|
||||
int? deviceVmservicePort,
|
||||
bool ipv6 = false,
|
||||
bool isNetworkDevice = false,
|
||||
required Duration timeout,
|
||||
bool quitOnFind = false,
|
||||
}) async {
|
||||
_logger.printTrace('Checking for advertised Dart VM Services...');
|
||||
try {
|
||||
await client.start();
|
||||
|
||||
final List<MDnsVmServiceDiscoveryResult> results =
|
||||
<MDnsVmServiceDiscoveryResult>[];
|
||||
final Set<String> uniqueDomainNames = <String>{};
|
||||
|
||||
// Listen for mDNS connections until timeout.
|
||||
final Stream<PtrResourceRecord> ptrResourceStream = client.lookup<PtrResourceRecord>(
|
||||
ResourceRecordQuery.serverPointer(dartVmServiceName),
|
||||
timeout: timeout
|
||||
);
|
||||
await for (final PtrResourceRecord ptr in ptrResourceStream) {
|
||||
uniqueDomainNames.add(ptr.domainName);
|
||||
|
||||
String? domainName;
|
||||
if (applicationId != null) {
|
||||
// If applicationId is set, only use records that match it
|
||||
if (ptr.domainName.toLowerCase().startsWith(applicationId.toLowerCase())) {
|
||||
domainName = ptr.domainName;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
domainName = ptr.domainName;
|
||||
}
|
||||
|
||||
_logger.printTrace('Checking for available port on $domainName');
|
||||
final List<SrvResourceRecord> srvRecords = await client
|
||||
.lookup<SrvResourceRecord>(
|
||||
ResourceRecordQuery.service(domainName),
|
||||
)
|
||||
.toList();
|
||||
if (srvRecords.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If more than one SrvResourceRecord found, it should just be a duplicate.
|
||||
final SrvResourceRecord srvRecord = srvRecords.first;
|
||||
if (srvRecords.length > 1) {
|
||||
_logger.printWarning(
|
||||
'Unexpectedly found more than one Dart VM Service report for $domainName '
|
||||
'- using first one (${srvRecord.port}).');
|
||||
}
|
||||
|
||||
// If deviceVmservicePort is set, only use records that match it
|
||||
if (deviceVmservicePort != null && srvRecord.port != deviceVmservicePort) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the IP address of the service if using a network device.
|
||||
InternetAddress? ipAddress;
|
||||
if (isNetworkDevice) {
|
||||
List<IPAddressResourceRecord> ipAddresses = await client
|
||||
.lookup<IPAddressResourceRecord>(
|
||||
ipv6
|
||||
? ResourceRecordQuery.addressIPv6(srvRecord.target)
|
||||
: ResourceRecordQuery.addressIPv4(srvRecord.target),
|
||||
)
|
||||
.toList();
|
||||
if (ipAddresses.isEmpty) {
|
||||
throwToolExit('Did not find IP for service ${srvRecord.target}.');
|
||||
}
|
||||
|
||||
// Filter out link-local addresses.
|
||||
if (ipAddresses.length > 1) {
|
||||
ipAddresses = ipAddresses.where((IPAddressResourceRecord element) => !element.address.isLinkLocal).toList();
|
||||
}
|
||||
|
||||
ipAddress = ipAddresses.first.address;
|
||||
if (ipAddresses.length > 1) {
|
||||
_logger.printWarning(
|
||||
'Unexpectedly found more than one IP for Dart VM Service ${srvRecord.target} '
|
||||
'- using first one ($ipAddress).');
|
||||
}
|
||||
}
|
||||
|
||||
_logger.printTrace('Checking for authentication code for $domainName');
|
||||
final List<TxtResourceRecord> txt = await client
|
||||
.lookup<TxtResourceRecord>(
|
||||
ResourceRecordQuery.text(domainName),
|
||||
)
|
||||
.toList();
|
||||
if (txt == null || txt.isEmpty) {
|
||||
results.add(MDnsVmServiceDiscoveryResult(domainName, srvRecord.port, ''));
|
||||
if (quitOnFind) {
|
||||
return results;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const String authCodePrefix = 'authCode=';
|
||||
String? raw;
|
||||
for (final String record in txt.first.text.split('\n')) {
|
||||
if (record.startsWith(authCodePrefix)) {
|
||||
raw = record;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (domainName == null) {
|
||||
throwToolExit('Did not find a observatory port advertised for $applicationId.');
|
||||
if (raw == null) {
|
||||
results.add(MDnsVmServiceDiscoveryResult(domainName, srvRecord.port, ''));
|
||||
if (quitOnFind) {
|
||||
return results;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (uniqueDomainNames.length > 1) {
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
buffer.writeln('There are multiple observatory ports available.');
|
||||
buffer.writeln('Rerun this command with one of the following passed in as the appId:');
|
||||
buffer.writeln();
|
||||
for (final String uniqueDomainName in uniqueDomainNames) {
|
||||
buffer.writeln(' flutter attach --app-id ${uniqueDomainName.replaceAll('.$dartObservatoryName', '')}');
|
||||
String authCode = raw.substring(authCodePrefix.length);
|
||||
// The Dart VM Service currently expects a trailing '/' as part of the
|
||||
// URI, otherwise an invalid authentication code response is given.
|
||||
if (!authCode.endsWith('/')) {
|
||||
authCode += '/';
|
||||
}
|
||||
throwToolExit(buffer.toString());
|
||||
} else {
|
||||
domainName = pointerRecords[0].domainName;
|
||||
}
|
||||
_logger.printTrace('Checking for available port on $domainName');
|
||||
// Here, if we get more than one, it should just be a duplicate.
|
||||
final List<SrvResourceRecord> srv = await _client
|
||||
.lookup<SrvResourceRecord>(
|
||||
ResourceRecordQuery.service(domainName),
|
||||
)
|
||||
.toList();
|
||||
if (srv.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
if (srv.length > 1) {
|
||||
_logger.printWarning('Unexpectedly found more than one observatory report for $domainName '
|
||||
'- using first one (${srv.first.port}).');
|
||||
}
|
||||
_logger.printTrace('Checking for authentication code for $domainName');
|
||||
final List<TxtResourceRecord> txt = await _client
|
||||
.lookup<TxtResourceRecord>(
|
||||
ResourceRecordQuery.text(domainName),
|
||||
)
|
||||
.toList();
|
||||
if (txt == null || txt.isEmpty) {
|
||||
return MDnsObservatoryDiscoveryResult(srv.first.port, '');
|
||||
}
|
||||
const String authCodePrefix = 'authCode=';
|
||||
String? raw;
|
||||
for (final String record in txt.first.text.split('\n')) {
|
||||
if (record.startsWith(authCodePrefix)) {
|
||||
raw = record;
|
||||
break;
|
||||
|
||||
results.add(MDnsVmServiceDiscoveryResult(
|
||||
domainName,
|
||||
srvRecord.port,
|
||||
authCode,
|
||||
ipAddress: ipAddress
|
||||
));
|
||||
if (quitOnFind) {
|
||||
return results;
|
||||
}
|
||||
}
|
||||
if (raw == null) {
|
||||
return MDnsObservatoryDiscoveryResult(srv.first.port, '');
|
||||
|
||||
// If applicationId is set and quitOnFind is true and no results matching
|
||||
// the applicationId were found but other results were found, throw an error.
|
||||
if (applicationId != null &&
|
||||
quitOnFind &&
|
||||
results.isEmpty &&
|
||||
uniqueDomainNames.isNotEmpty) {
|
||||
String message = 'Did not find a Dart VM Service advertised for $applicationId';
|
||||
if (deviceVmservicePort != null) {
|
||||
message += ' on port $deviceVmservicePort';
|
||||
}
|
||||
throwToolExit('$message.');
|
||||
}
|
||||
String authCode = raw.substring(authCodePrefix.length);
|
||||
// The Observatory currently expects a trailing '/' as part of the
|
||||
// URI, otherwise an invalid authentication code response is given.
|
||||
if (!authCode.endsWith('/')) {
|
||||
authCode += '/';
|
||||
}
|
||||
return MDnsObservatoryDiscoveryResult(srv.first.port, authCode);
|
||||
|
||||
return results;
|
||||
} finally {
|
||||
_client.stop();
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uri?> getObservatoryUri(String? applicationId, Device device, {
|
||||
/// Gets Dart VM Service Uri for `flutter attach`.
|
||||
/// Executes an mDNS query and waits until a Dart VM Service is found.
|
||||
///
|
||||
/// Differs from `getVMServiceUriForLaunch` because it can search for any available Dart VM Service.
|
||||
/// Since [applicationId] and [deviceVmservicePort] are optional, it can either look for any service
|
||||
/// or a specific service matching [applicationId]/[deviceVmservicePort].
|
||||
/// It may find more than one service, which will throw an error listing the found services.
|
||||
Future<Uri?> getVMServiceUriForAttach(
|
||||
String? applicationId,
|
||||
Device device, {
|
||||
bool usesIpv6 = false,
|
||||
int? hostVmservicePort,
|
||||
int? deviceVmservicePort,
|
||||
bool isNetworkDevice = false,
|
||||
Duration timeout = const Duration(minutes: 10),
|
||||
}) async {
|
||||
final MDnsObservatoryDiscoveryResult? result = await query(
|
||||
final MDnsVmServiceDiscoveryResult? result = await queryForAttach(
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
ipv6: usesIpv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
timeout: timeout,
|
||||
);
|
||||
return _handleResult(
|
||||
result,
|
||||
device,
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
hostVmservicePort: hostVmservicePort,
|
||||
usesIpv6: usesIpv6,
|
||||
isNetworkDevice: isNetworkDevice
|
||||
);
|
||||
}
|
||||
|
||||
/// Gets Dart VM Service Uri for `flutter run`.
|
||||
/// Executes an mDNS query and waits until the Dart VM Service service is found.
|
||||
///
|
||||
/// Differs from `getVMServiceUriForAttach` because it only searches for a specific service.
|
||||
/// This is enforced by [applicationId] and [deviceVmservicePort] being required.
|
||||
Future<Uri?> getVMServiceUriForLaunch(
|
||||
String applicationId,
|
||||
Device device, {
|
||||
bool usesIpv6 = false,
|
||||
int? hostVmservicePort,
|
||||
required int deviceVmservicePort,
|
||||
bool isNetworkDevice = false,
|
||||
Duration timeout = const Duration(minutes: 10),
|
||||
}) async {
|
||||
final MDnsVmServiceDiscoveryResult? result = await queryForLaunch(
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
ipv6: usesIpv6,
|
||||
isNetworkDevice: isNetworkDevice,
|
||||
timeout: timeout,
|
||||
);
|
||||
return _handleResult(
|
||||
result,
|
||||
device,
|
||||
applicationId: applicationId,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
hostVmservicePort: hostVmservicePort,
|
||||
usesIpv6: usesIpv6,
|
||||
isNetworkDevice: isNetworkDevice
|
||||
);
|
||||
}
|
||||
|
||||
Future<Uri?> _handleResult(
|
||||
MDnsVmServiceDiscoveryResult? result,
|
||||
Device device, {
|
||||
String? applicationId,
|
||||
int? deviceVmservicePort,
|
||||
int? hostVmservicePort,
|
||||
bool usesIpv6 = false,
|
||||
bool isNetworkDevice = false,
|
||||
}) async {
|
||||
if (result == null) {
|
||||
await _checkForIPv4LinkLocal(device);
|
||||
return null;
|
||||
}
|
||||
final String host;
|
||||
|
||||
final String host = usesIpv6
|
||||
final InternetAddress? ipAddress = result.ipAddress;
|
||||
if (isNetworkDevice && ipAddress != null) {
|
||||
host = ipAddress.address;
|
||||
} else {
|
||||
host = usesIpv6
|
||||
? InternetAddress.loopbackIPv6.address
|
||||
: InternetAddress.loopbackIPv4.address;
|
||||
return buildObservatoryUri(
|
||||
}
|
||||
return buildVMServiceUri(
|
||||
device,
|
||||
host,
|
||||
result.port,
|
||||
hostVmservicePort,
|
||||
result.authCode,
|
||||
isNetworkDevice,
|
||||
);
|
||||
}
|
||||
|
||||
@ -236,18 +502,26 @@ class MDnsObservatoryDiscovery {
|
||||
}
|
||||
}
|
||||
|
||||
class MDnsObservatoryDiscoveryResult {
|
||||
MDnsObservatoryDiscoveryResult(this.port, this.authCode);
|
||||
class MDnsVmServiceDiscoveryResult {
|
||||
MDnsVmServiceDiscoveryResult(
|
||||
this.domainName,
|
||||
this.port,
|
||||
this.authCode, {
|
||||
this.ipAddress
|
||||
});
|
||||
final String domainName;
|
||||
final int port;
|
||||
final String authCode;
|
||||
final InternetAddress? ipAddress;
|
||||
}
|
||||
|
||||
Future<Uri> buildObservatoryUri(
|
||||
Future<Uri> buildVMServiceUri(
|
||||
Device device,
|
||||
String host,
|
||||
int devicePort, [
|
||||
int? hostVmservicePort,
|
||||
String? authCode,
|
||||
bool isNetworkDevice = false,
|
||||
]) async {
|
||||
String path = '/';
|
||||
if (authCode != null) {
|
||||
@ -259,8 +533,16 @@ Future<Uri> buildObservatoryUri(
|
||||
path += '/';
|
||||
}
|
||||
hostVmservicePort ??= 0;
|
||||
final int? actualHostPort = hostVmservicePort == 0 ?
|
||||
|
||||
final int? actualHostPort;
|
||||
if (isNetworkDevice) {
|
||||
// When debugging with a network device, port forwarding is not required
|
||||
// so just use the device's port.
|
||||
actualHostPort = devicePort;
|
||||
} else {
|
||||
actualHostPort = hostVmservicePort == 0 ?
|
||||
await device.portForwarder?.forward(devicePort) :
|
||||
hostVmservicePort;
|
||||
}
|
||||
return Uri(scheme: 'http', host: host, port: actualHostPort, path: path);
|
||||
}
|
||||
|
@ -569,7 +569,7 @@ abstract class FlutterCommand extends Command<void> {
|
||||
);
|
||||
}
|
||||
|
||||
bool get disablePortPublication => !boolArgDeprecated('publish-port');
|
||||
Future<bool> get disablePortPublication async => !boolArgDeprecated('publish-port');
|
||||
|
||||
void usesIpv6Flag({required bool verboseHelp}) {
|
||||
argParser.addFlag(ipv6Flag,
|
||||
|
@ -23,6 +23,7 @@ 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';
|
||||
@ -83,6 +84,7 @@ void main() {
|
||||
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;
|
||||
@ -102,17 +104,17 @@ void main() {
|
||||
fakeLogReader.dispose();
|
||||
});
|
||||
|
||||
testUsingContext('succeeds with iOS device', () async {
|
||||
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) {
|
||||
@ -121,7 +123,20 @@ void main() {
|
||||
completer.complete();
|
||||
}
|
||||
});
|
||||
final Future<void> task = createTestCommandRunner(AttachCommand(
|
||||
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;
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
@ -137,15 +152,309 @@ void main() {
|
||||
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,
|
||||
MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(
|
||||
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.isWaitingForObservatory = 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? observatoryUri = await flutterDevice.observatoryUris?.first;
|
||||
expect(observatoryUri.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.isWaitingForObservatory = 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? observatoryUri = await flutterDevice.observatoryUris?.first;
|
||||
expect(observatoryUri.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.isWaitingForObservatory = 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? observatoryUri = await flutterDevice.observatoryUris?.first;
|
||||
expect(observatoryUri.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.isWaitingForObservatory = 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? observatoryUri = await flutterDevice.observatoryUris?.first;
|
||||
expect(observatoryUri.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(),
|
||||
),
|
||||
@ -979,9 +1288,16 @@ class FakeIOSDevice extends Fake implements IOSDevice {
|
||||
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!;
|
||||
@ -1029,12 +1345,14 @@ class FakeIOSDevice extends Fake implements IOSDevice {
|
||||
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
|
||||
@ -1054,7 +1372,7 @@ class FakeMDnsClient extends Fake implements MDnsClient {
|
||||
ResourceRecordQuery query, {
|
||||
Duration timeout = const Duration(seconds: 5),
|
||||
}) {
|
||||
if (T == PtrResourceRecord && query.fullyQualifiedName == MDnsObservatoryDiscovery.dartObservatoryName) {
|
||||
if (T == PtrResourceRecord && query.fullyQualifiedName == MDnsVmServiceDiscovery.dartVmServiceName) {
|
||||
return Stream<PtrResourceRecord>.fromIterable(ptrRecords) as Stream<T>;
|
||||
}
|
||||
if (T == SrvResourceRecord) {
|
||||
@ -1065,6 +1383,10 @@ class FakeMDnsClient extends Fake implements MDnsClient {
|
||||
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');
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@ import 'package:flutter_tools/src/commands/drive.dart';
|
||||
import 'package:flutter_tools/src/dart/pub.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/drive/drive_service.dart';
|
||||
import 'package:flutter_tools/src/ios/devices.dart';
|
||||
import 'package:flutter_tools/src/ios/iproxy.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
import 'package:test/fake.dart';
|
||||
@ -406,6 +408,94 @@ void main() {
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Port publication not disabled for network device', () async {
|
||||
final DriveCommand command = DriveCommand(
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
signals: signals,
|
||||
);
|
||||
|
||||
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
||||
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
|
||||
fileSystem.file('pubspec.yaml').createSync();
|
||||
|
||||
final Device networkDevice = FakeIosDevice()
|
||||
..interfaceType = IOSDeviceConnectionInterface.network;
|
||||
fakeDeviceManager.devices = <Device>[networkDevice];
|
||||
|
||||
await expectLater(() => createTestCommandRunner(command).run(<String>[
|
||||
'drive',
|
||||
]), throwsToolExit());
|
||||
|
||||
final DebuggingOptions options = await command.createDebuggingOptions(false);
|
||||
expect(options.disablePortPublication, false);
|
||||
}, overrides: <Type, Generator>{
|
||||
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
DeviceManager: () => fakeDeviceManager,
|
||||
});
|
||||
|
||||
testUsingContext('Port publication is disabled for wired device', () async {
|
||||
final DriveCommand command = DriveCommand(
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
signals: signals,
|
||||
);
|
||||
|
||||
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
||||
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
|
||||
fileSystem.file('pubspec.yaml').createSync();
|
||||
|
||||
await expectLater(() => createTestCommandRunner(command).run(<String>[
|
||||
'drive',
|
||||
]), throwsToolExit());
|
||||
|
||||
final Device usbDevice = FakeIosDevice()
|
||||
..interfaceType = IOSDeviceConnectionInterface.usb;
|
||||
fakeDeviceManager.devices = <Device>[usbDevice];
|
||||
|
||||
final DebuggingOptions options = await command.createDebuggingOptions(false);
|
||||
expect(options.disablePortPublication, true);
|
||||
}, overrides: <Type, Generator>{
|
||||
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
DeviceManager: () => fakeDeviceManager,
|
||||
});
|
||||
|
||||
testUsingContext('Port publication does not default to enabled for network device if flag manually added', () async {
|
||||
final DriveCommand command = DriveCommand(
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
signals: signals,
|
||||
);
|
||||
|
||||
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
||||
fileSystem.file('test_driver/main_test.dart').createSync(recursive: true);
|
||||
fileSystem.file('pubspec.yaml').createSync();
|
||||
|
||||
final Device networkDevice = FakeIosDevice()
|
||||
..interfaceType = IOSDeviceConnectionInterface.network;
|
||||
fakeDeviceManager.devices = <Device>[networkDevice];
|
||||
|
||||
await expectLater(() => createTestCommandRunner(command).run(<String>[
|
||||
'drive',
|
||||
'--no-publish-port'
|
||||
]), throwsToolExit());
|
||||
|
||||
final DebuggingOptions options = await command.createDebuggingOptions(false);
|
||||
expect(options.disablePortPublication, true);
|
||||
}, overrides: <Type, Generator>{
|
||||
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
DeviceManager: () => fakeDeviceManager,
|
||||
});
|
||||
}
|
||||
|
||||
// Unfortunately Device, despite not being immutable, has an `operator ==`.
|
||||
@ -577,3 +667,14 @@ class FakeProcessSignal extends Fake implements io.ProcessSignal {
|
||||
@override
|
||||
Stream<io.ProcessSignal> watch() => controller.stream;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@override
|
||||
IOSDeviceConnectionInterface interfaceType = IOSDeviceConnectionInterface.usb;
|
||||
|
||||
@override
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/utils.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/ios/iproxy.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
@ -554,6 +555,53 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Get launch arguments for physical device with iPv4 network connection', () {
|
||||
final DebuggingOptions original = DebuggingOptions.enabled(
|
||||
BuildInfo.debug,
|
||||
);
|
||||
|
||||
final List<String> launchArguments = original.getIOSLaunchArguments(
|
||||
EnvironmentType.physical,
|
||||
null,
|
||||
<String, Object?>{},
|
||||
interfaceType: IOSDeviceConnectionInterface.network,
|
||||
);
|
||||
|
||||
expect(
|
||||
launchArguments.join(' '),
|
||||
<String>[
|
||||
'--enable-dart-profiling',
|
||||
'--enable-checked-mode',
|
||||
'--verify-entry-points',
|
||||
'--observatory-host=0.0.0.0',
|
||||
].join(' '),
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Get launch arguments for physical device with iPv6 network connection', () {
|
||||
final DebuggingOptions original = DebuggingOptions.enabled(
|
||||
BuildInfo.debug,
|
||||
);
|
||||
|
||||
final List<String> launchArguments = original.getIOSLaunchArguments(
|
||||
EnvironmentType.physical,
|
||||
null,
|
||||
<String, Object?>{},
|
||||
ipv6: true,
|
||||
interfaceType: IOSDeviceConnectionInterface.network,
|
||||
);
|
||||
|
||||
expect(
|
||||
launchArguments.join(' '),
|
||||
<String>[
|
||||
'--enable-dart-profiling',
|
||||
'--enable-checked-mode',
|
||||
'--verify-entry-points',
|
||||
'--observatory-host=::0',
|
||||
].join(' '),
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Get launch arguments for physical device with debugging disabled with available launch arguments', () {
|
||||
final DebuggingOptions original = DebuggingOptions.disabled(
|
||||
BuildInfo.debug,
|
||||
|
@ -19,9 +19,11 @@ import 'package:flutter_tools/src/ios/devices.dart';
|
||||
import 'package:flutter_tools/src/ios/ios_deploy.dart';
|
||||
import 'package:flutter_tools/src/ios/iproxy.dart';
|
||||
import 'package:flutter_tools/src/ios/mac.dart';
|
||||
import 'package:flutter_tools/src/mdns_discovery.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fake_devices.dart';
|
||||
import '../../src/fake_process_manager.dart';
|
||||
import '../../src/fakes.dart';
|
||||
@ -66,9 +68,10 @@ const FakeCommand kLaunchDebugCommand = FakeCommand(command: <String>[
|
||||
FakeCommand attachDebuggerCommand({
|
||||
IOSink? stdin,
|
||||
Completer<void>? completer,
|
||||
bool isNetworkDevice = false,
|
||||
}) {
|
||||
return FakeCommand(
|
||||
command: const <String>[
|
||||
command: <String>[
|
||||
'script',
|
||||
'-t',
|
||||
'0',
|
||||
@ -79,9 +82,12 @@ FakeCommand attachDebuggerCommand({
|
||||
'--bundle',
|
||||
'/',
|
||||
'--debug',
|
||||
'--no-wifi',
|
||||
if (!isNetworkDevice) '--no-wifi',
|
||||
'--args',
|
||||
'--enable-dart-profiling --enable-checked-mode --verify-entry-points',
|
||||
if (isNetworkDevice)
|
||||
'--enable-dart-profiling --enable-checked-mode --verify-entry-points --observatory-host=0.0.0.0'
|
||||
else
|
||||
'--enable-dart-profiling --enable-checked-mode --verify-entry-points',
|
||||
],
|
||||
completer: completer,
|
||||
environment: const <String, String>{
|
||||
@ -188,7 +194,7 @@ void main() {
|
||||
expect(await device.stopApp(iosApp), false);
|
||||
});
|
||||
|
||||
testWithoutContext('IOSDevice.startApp prints warning message if discovery takes longer than configured timeout', () async {
|
||||
testWithoutContext('IOSDevice.startApp prints warning message if discovery takes longer than configured timeout for wired device', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final CompleterIOSink stdin = CompleterIOSink();
|
||||
@ -226,12 +232,59 @@ void main() {
|
||||
expect(launchResult.started, true);
|
||||
expect(launchResult.hasObservatory, true);
|
||||
expect(await device.stopApp(iosApp), false);
|
||||
expect(logger.errorText, contains('iOS Observatory not discovered after 30 seconds. This is taking much longer than expected...'));
|
||||
expect(logger.errorText, contains('The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...'));
|
||||
expect(utf8.decoder.convert(stdin.writes.first), contains('process interrupt'));
|
||||
completer.complete();
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
});
|
||||
|
||||
testUsingContext('IOSDevice.startApp prints warning message if discovery takes longer than configured timeout for wireless device', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final CompleterIOSink stdin = CompleterIOSink();
|
||||
final Completer<void> completer = Completer<void>();
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
attachDebuggerCommand(stdin: stdin, completer: completer, isNetworkDevice: true),
|
||||
]);
|
||||
final IOSDevice device = setUpIOSDevice(
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
interfaceType: IOSDeviceConnectionInterface.network,
|
||||
);
|
||||
final IOSApp iosApp = PrebuiltIOSApp(
|
||||
projectBundleId: 'app',
|
||||
bundleName: 'Runner',
|
||||
uncompressedBundle: fileSystem.currentDirectory,
|
||||
applicationPackage: fileSystem.currentDirectory,
|
||||
);
|
||||
final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader();
|
||||
|
||||
device.portForwarder = const NoOpDevicePortForwarder();
|
||||
device.setLogReader(iosApp, deviceLogReader);
|
||||
|
||||
// Start writing messages to the log reader.
|
||||
deviceLogReader.addLine('Foo');
|
||||
deviceLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:456');
|
||||
|
||||
final LaunchResult launchResult = await device.startApp(iosApp,
|
||||
prebuiltApplication: true,
|
||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||
platformArgs: <String, dynamic>{},
|
||||
discoveryTimeout: Duration.zero,
|
||||
);
|
||||
|
||||
expect(launchResult.started, true);
|
||||
expect(launchResult.hasObservatory, true);
|
||||
expect(await device.stopApp(iosApp), false);
|
||||
expect(logger.errorText, contains('The Dart VM Service was not discovered after 45 seconds. This is taking much longer than expected...'));
|
||||
expect(logger.errorText, contains('Click "Allow" to the prompt asking if you would like to find and connect devices on your local network.'));
|
||||
completer.complete();
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
}, overrides: <Type, Generator>{
|
||||
MDnsVmServiceDiscovery: () => FakeMDnsVmServiceDiscovery(),
|
||||
});
|
||||
|
||||
testWithoutContext('IOSDevice.startApp succeeds in release mode', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
@ -505,6 +558,7 @@ IOSDevice setUpIOSDevice({
|
||||
Logger? logger,
|
||||
ProcessManager? processManager,
|
||||
IOSDeploy? iosDeploy,
|
||||
IOSDeviceConnectionInterface interfaceType = IOSDeviceConnectionInterface.usb,
|
||||
}) {
|
||||
final Artifacts artifacts = Artifacts.test();
|
||||
final FakePlatform macPlatform = FakePlatform(
|
||||
@ -542,7 +596,7 @@ IOSDevice setUpIOSDevice({
|
||||
cache: cache,
|
||||
),
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
interfaceType: IOSDeviceConnectionInterface.usb,
|
||||
interfaceType: interfaceType,
|
||||
);
|
||||
}
|
||||
|
||||
@ -554,3 +608,18 @@ class FakeDevicePortForwarder extends Fake implements DevicePortForwarder {
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeMDnsVmServiceDiscovery extends Fake implements MDnsVmServiceDiscovery {
|
||||
@override
|
||||
Future<Uri?> getVMServiceUriForLaunch(
|
||||
String applicationId,
|
||||
Device device, {
|
||||
bool usesIpv6 = false,
|
||||
int? hostVmservicePort,
|
||||
required int deviceVmservicePort,
|
||||
bool isNetworkDevice = false,
|
||||
Duration timeout = Duration.zero,
|
||||
}) async {
|
||||
return Uri.tryParse('http://0.0.0.0:1234');
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +479,7 @@ void main() {
|
||||
stdout: devicesOutput,
|
||||
));
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableIOSDevices();
|
||||
expect(devices, hasLength(3));
|
||||
expect(devices, hasLength(4));
|
||||
expect(devices[0].id, '00008027-00192736010F802E');
|
||||
expect(devices[0].name, 'An iPhone (Space Gray)');
|
||||
expect(await devices[0].sdkNameAndVersion, 'iOS 13.3 17C54');
|
||||
@ -488,10 +488,14 @@ void main() {
|
||||
expect(devices[1].name, 'iPad 1');
|
||||
expect(await devices[1].sdkNameAndVersion, 'iOS 10.1 14C54');
|
||||
expect(devices[1].cpuArchitecture, DarwinArch.armv7);
|
||||
expect(devices[2].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
|
||||
expect(devices[2].name, 'iPad 2');
|
||||
expect(devices[2].id, '234234234234234234345445687594e089dede3c44');
|
||||
expect(devices[2].name, 'A networked iPad');
|
||||
expect(await devices[2].sdkNameAndVersion, 'iOS 10.1 14C54');
|
||||
expect(devices[2].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture.
|
||||
expect(devices[3].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
|
||||
expect(devices[3].name, 'iPad 2');
|
||||
expect(await devices[3].sdkNameAndVersion, 'iOS 10.1 14C54');
|
||||
expect(devices[3].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture.
|
||||
expect(fakeProcessManager, hasNoRemainingExpectations);
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macPlatform,
|
||||
|
@ -17,7 +17,7 @@ import '../src/common.dart';
|
||||
|
||||
void main() {
|
||||
group('mDNS Discovery', () {
|
||||
final int year3000 = DateTime(3000).millisecondsSinceEpoch;
|
||||
final int future = DateTime.now().add(const Duration(days: 1)).millisecondsSinceEpoch;
|
||||
|
||||
setUp(() {
|
||||
setNetworkInterfaceLister(
|
||||
@ -33,209 +33,652 @@ void main() {
|
||||
resetNetworkInterfaceLister();
|
||||
});
|
||||
|
||||
group('for attach', () {
|
||||
late MDnsClient emptyClient;
|
||||
|
||||
testWithoutContext('No ports available', () async {
|
||||
final MDnsClient client = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{});
|
||||
setUp(() {
|
||||
emptyClient = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{});
|
||||
});
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.query())?.port;
|
||||
expect(port, isNull);
|
||||
testWithoutContext('Find result in preliminary client', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: emptyClient,
|
||||
preliminaryMDnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.queryForAttach();
|
||||
expect(result, isNotNull);
|
||||
});
|
||||
|
||||
testWithoutContext('Do not find result in preliminary client, but find in main client', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.queryForAttach();
|
||||
expect(result, isNotNull);
|
||||
});
|
||||
|
||||
testWithoutContext('Find multiple in preliminary client', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'fiz'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
'fiz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('fiz', future, port: 321, weight: 1, priority: 1, target: 'local'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: emptyClient,
|
||||
preliminaryMDnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
|
||||
expect(portDiscovery.queryForAttach, throwsToolExit());
|
||||
});
|
||||
|
||||
testWithoutContext('No ports available', () async {
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: emptyClient,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
|
||||
final int? port = (await portDiscovery.queryForAttach())?.port;
|
||||
expect(port, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext('Prints helpful message when there is no ipv4 link local address.', () async {
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: emptyClient,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: logger,
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForAttach(
|
||||
'',
|
||||
FakeIOSDevice(),
|
||||
);
|
||||
expect(uri, isNull);
|
||||
expect(logger.errorText, contains('Personal Hotspot'));
|
||||
});
|
||||
|
||||
testWithoutContext('One port available, no appId', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.queryForAttach())?.port;
|
||||
expect(port, 123);
|
||||
});
|
||||
|
||||
testWithoutContext('One port available, no appId, with authCode', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
txtResponse: <String, List<TxtResourceRecord>>{
|
||||
'bar': <TxtResourceRecord>[
|
||||
TxtResourceRecord('bar', future, text: 'authCode=xyz\n'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.queryForAttach();
|
||||
expect(result?.port, 123);
|
||||
expect(result?.authCode, 'xyz/');
|
||||
});
|
||||
|
||||
testWithoutContext('Multiple ports available, with appId', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'fiz'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
'fiz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('fiz', future, port: 321, weight: 1, priority: 1, target: 'local'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.queryForAttach(applicationId: 'fiz'))?.port;
|
||||
expect(port, 321);
|
||||
});
|
||||
|
||||
testWithoutContext('Multiple ports available per process, with appId', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'fiz'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 1234, weight: 1, priority: 1, target: 'appId'),
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
'fiz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('fiz', future, port: 4321, weight: 1, priority: 1, target: 'local'),
|
||||
SrvResourceRecord('fiz', future, port: 321, weight: 1, priority: 1, target: 'local'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.queryForAttach(applicationId: 'bar'))?.port;
|
||||
expect(port, 1234);
|
||||
});
|
||||
|
||||
testWithoutContext('Throws Exception when client throws OSError on start', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{},
|
||||
osErrorOnStart: true,
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(
|
||||
() async => portDiscovery.queryForAttach(),
|
||||
throwsException,
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Correctly builds VM Service URI with hostVmservicePort == 0', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForAttach('bar', device, hostVmservicePort: 0);
|
||||
expect(uri.toString(), 'http://127.0.0.1:123/');
|
||||
});
|
||||
|
||||
testWithoutContext('Get network device IP (iPv4)', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 1234, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
ipResponse: <String, List<IPAddressResourceRecord>>{
|
||||
'appId': <IPAddressResourceRecord>[
|
||||
IPAddressResourceRecord('Device IP', 0, address: InternetAddress.tryParse('111.111.111.111')!),
|
||||
],
|
||||
},
|
||||
txtResponse: <String, List<TxtResourceRecord>>{
|
||||
'bar': <TxtResourceRecord>[
|
||||
TxtResourceRecord('bar', future, text: 'authCode=xyz\n'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForAttach(
|
||||
'bar',
|
||||
device,
|
||||
isNetworkDevice: true,
|
||||
);
|
||||
expect(uri.toString(), 'http://111.111.111.111:1234/xyz/');
|
||||
});
|
||||
|
||||
testWithoutContext('Get network device IP (iPv6)', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 1234, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
ipResponse: <String, List<IPAddressResourceRecord>>{
|
||||
'appId': <IPAddressResourceRecord>[
|
||||
IPAddressResourceRecord('Device IP', 0, address: InternetAddress.tryParse('1111:1111:1111:1111:1111:1111:1111:1111')!),
|
||||
],
|
||||
},
|
||||
txtResponse: <String, List<TxtResourceRecord>>{
|
||||
'bar': <TxtResourceRecord>[
|
||||
TxtResourceRecord('bar', future, text: 'authCode=xyz\n'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForAttach(
|
||||
'bar',
|
||||
device,
|
||||
isNetworkDevice: true,
|
||||
);
|
||||
expect(uri.toString(), 'http://[1111:1111:1111:1111:1111:1111:1111:1111]:1234/xyz/');
|
||||
});
|
||||
|
||||
testWithoutContext('Throw error if unable to find VM service with app id and device port', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
||||
PtrResourceRecord('bar', future, domainName: 'srv-bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'srv-boo'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'srv-foo': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-foo', future, port: 123, weight: 1, priority: 1, target: 'target-foo'),
|
||||
],
|
||||
'srv-bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-bar', future, port: 123, weight: 1, priority: 1, target: 'target-bar'),
|
||||
],
|
||||
'srv-baz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-baz', future, port: 123, weight: 1, priority: 1, target: 'target-baz'),
|
||||
],
|
||||
},
|
||||
);
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(
|
||||
portDiscovery.getVMServiceUriForAttach(
|
||||
'srv-bar',
|
||||
device,
|
||||
deviceVmservicePort: 321,
|
||||
),
|
||||
throwsToolExit(
|
||||
message: 'Did not find a Dart VM Service advertised for srv-bar on port 321.'
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Throw error if unable to find VM Service with app id', () async {
|
||||
final MDnsClient client = 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'),
|
||||
],
|
||||
},
|
||||
);
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
preliminaryMDnsClient: emptyClient,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(
|
||||
portDiscovery.getVMServiceUriForAttach(
|
||||
'srv-asdf',
|
||||
device,
|
||||
),
|
||||
throwsToolExit(
|
||||
message: 'Did not find a Dart VM Service advertised for srv-asdf.'
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWithoutContext('Prints helpful message when there is no ipv4 link local address.', () async {
|
||||
final MDnsClient client = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{});
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: logger,
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getObservatoryUri(
|
||||
'',
|
||||
FakeIOSDevice(),
|
||||
);
|
||||
expect(uri, isNull);
|
||||
expect(logger.errorText, contains('Personal Hotspot'));
|
||||
group('for launch', () {
|
||||
testWithoutContext('No ports available', () async {
|
||||
final MDnsClient client = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{});
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.queryForLaunch(
|
||||
applicationId: 'app-id',
|
||||
deviceVmservicePort: 123,
|
||||
);
|
||||
|
||||
expect(result, null);
|
||||
});
|
||||
|
||||
testWithoutContext('Prints helpful message when there is no ipv4 link local address.', () async {
|
||||
final MDnsClient client = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{});
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: logger,
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
||||
'',
|
||||
FakeIOSDevice(),
|
||||
deviceVmservicePort: 0,
|
||||
);
|
||||
expect(uri, isNull);
|
||||
expect(logger.errorText, contains('Personal Hotspot'));
|
||||
});
|
||||
|
||||
testWithoutContext('Throws Exception when client throws OSError on start', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{},
|
||||
osErrorOnStart: true,
|
||||
);
|
||||
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(
|
||||
() async => portDiscovery.queryForLaunch(applicationId: 'app-id', deviceVmservicePort: 123),
|
||||
throwsException,
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Correctly builds VM Service URI with hostVmservicePort == 0', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
||||
'bar',
|
||||
device,
|
||||
hostVmservicePort: 0,
|
||||
deviceVmservicePort: 123,
|
||||
);
|
||||
expect(uri.toString(), 'http://127.0.0.1:123/');
|
||||
});
|
||||
|
||||
testWithoutContext('Get network device IP (iPv4)', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 1234, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
ipResponse: <String, List<IPAddressResourceRecord>>{
|
||||
'appId': <IPAddressResourceRecord>[
|
||||
IPAddressResourceRecord('Device IP', 0, address: InternetAddress.tryParse('111.111.111.111')!),
|
||||
],
|
||||
},
|
||||
txtResponse: <String, List<TxtResourceRecord>>{
|
||||
'bar': <TxtResourceRecord>[
|
||||
TxtResourceRecord('bar', future, text: 'authCode=xyz\n'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
||||
'bar',
|
||||
device,
|
||||
isNetworkDevice: true,
|
||||
deviceVmservicePort: 1234,
|
||||
);
|
||||
expect(uri.toString(), 'http://111.111.111.111:1234/xyz/');
|
||||
});
|
||||
|
||||
testWithoutContext('Get network device IP (iPv6)', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', future, port: 1234, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
ipResponse: <String, List<IPAddressResourceRecord>>{
|
||||
'appId': <IPAddressResourceRecord>[
|
||||
IPAddressResourceRecord('Device IP', 0, address: InternetAddress.tryParse('1111:1111:1111:1111:1111:1111:1111:1111')!),
|
||||
],
|
||||
},
|
||||
txtResponse: <String, List<TxtResourceRecord>>{
|
||||
'bar': <TxtResourceRecord>[
|
||||
TxtResourceRecord('bar', future, text: 'authCode=xyz\n'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
||||
'bar',
|
||||
device,
|
||||
isNetworkDevice: true,
|
||||
deviceVmservicePort: 1234,
|
||||
);
|
||||
expect(uri.toString(), 'http://[1111:1111:1111:1111:1111:1111:1111:1111]:1234/xyz/');
|
||||
});
|
||||
|
||||
testWithoutContext('Throw error if unable to find VM Service with app id and device port', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
||||
PtrResourceRecord('bar', future, domainName: 'srv-bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'srv-boo'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'srv-foo': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-foo', future, port: 123, weight: 1, priority: 1, target: 'target-foo'),
|
||||
],
|
||||
'srv-bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-bar', future, port: 123, weight: 1, priority: 1, target: 'target-bar'),
|
||||
],
|
||||
'srv-baz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-baz', future, port: 123, weight: 1, priority: 1, target: 'target-baz'),
|
||||
],
|
||||
},
|
||||
);
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(
|
||||
portDiscovery.getVMServiceUriForLaunch(
|
||||
'srv-bar',
|
||||
device,
|
||||
deviceVmservicePort: 321,
|
||||
),
|
||||
throwsToolExit(
|
||||
message:'Did not find a Dart VM Service advertised for srv-bar on port 321.'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWithoutContext('One port available, no appId', () async {
|
||||
testWithoutContext('Find firstMatchingVmService with many available and no application id', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', year3000, domainName: 'bar'),
|
||||
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
||||
PtrResourceRecord('bar', future, domainName: 'srv-bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'srv-boo'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
'srv-foo': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-foo', future, port: 123, weight: 1, priority: 1, target: 'target-foo'),
|
||||
],
|
||||
'srv-bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-bar', future, port: 123, weight: 1, priority: 1, target: 'target-bar'),
|
||||
],
|
||||
'srv-baz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-baz', future, port: 123, weight: 1, priority: 1, target: 'target-baz'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.query())?.port;
|
||||
expect(port, 123);
|
||||
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(client);
|
||||
expect(result?.domainName, 'srv-foo');
|
||||
});
|
||||
|
||||
testWithoutContext('One port available, no appId, with authCode', () async {
|
||||
testWithoutContext('Find firstMatchingVmService app id', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', year3000, domainName: 'bar'),
|
||||
PtrResourceRecord('foo', future, domainName: 'srv-foo'),
|
||||
PtrResourceRecord('bar', future, domainName: 'srv-bar'),
|
||||
PtrResourceRecord('baz', future, domainName: 'srv-boo'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
'srv-foo': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-foo', future, port: 111, weight: 1, priority: 1, target: 'target-foo'),
|
||||
],
|
||||
},
|
||||
txtResponse: <String, List<TxtResourceRecord>>{
|
||||
'bar': <TxtResourceRecord>[
|
||||
TxtResourceRecord('bar', year3000, text: 'authCode=xyz\n'),
|
||||
'srv-bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-bar', future, port: 222, weight: 1, priority: 1, target: 'target-bar'),
|
||||
SrvResourceRecord('srv-bar', future, port: 333, weight: 1, priority: 1, target: 'target-bar-2'),
|
||||
],
|
||||
'srv-baz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('srv-baz', future, port: 444, weight: 1, priority: 1, target: 'target-baz'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final MDnsObservatoryDiscoveryResult? result = await portDiscovery.query();
|
||||
expect(result?.port, 123);
|
||||
expect(result?.authCode, 'xyz/');
|
||||
});
|
||||
|
||||
testWithoutContext('Multiple ports available, without appId', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', year3000, domainName: 'bar'),
|
||||
PtrResourceRecord('baz', year3000, domainName: 'fiz'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
'fiz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('fiz', year3000, port: 321, weight: 1, priority: 1, target: 'local'),
|
||||
],
|
||||
},
|
||||
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
|
||||
client,
|
||||
applicationId: 'srv-bar'
|
||||
);
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(portDiscovery.query, throwsToolExit());
|
||||
});
|
||||
|
||||
testWithoutContext('Multiple ports available, with appId', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', year3000, domainName: 'bar'),
|
||||
PtrResourceRecord('baz', year3000, domainName: 'fiz'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
'fiz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('fiz', year3000, port: 321, weight: 1, priority: 1, target: 'local'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.query(applicationId: 'fiz'))?.port;
|
||||
expect(port, 321);
|
||||
});
|
||||
|
||||
testWithoutContext('Multiple ports available per process, with appId', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', year3000, domainName: 'bar'),
|
||||
PtrResourceRecord('baz', year3000, domainName: 'fiz'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', year3000, port: 1234, weight: 1, priority: 1, target: 'appId'),
|
||||
SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
'fiz': <SrvResourceRecord>[
|
||||
SrvResourceRecord('fiz', year3000, port: 4321, weight: 1, priority: 1, target: 'local'),
|
||||
SrvResourceRecord('fiz', year3000, port: 321, weight: 1, priority: 1, target: 'local'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.query(applicationId: 'bar'))?.port;
|
||||
expect(port, 1234);
|
||||
});
|
||||
|
||||
testWithoutContext('Query returns null', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[],
|
||||
<String, List<SrvResourceRecord>>{},
|
||||
);
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final int? port = (await portDiscovery.query(applicationId: 'bar'))?.port;
|
||||
expect(port, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext('Throws Exception when client throws OSError on start', () async {
|
||||
final MDnsClient client = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}, osErrorOnStart: true);
|
||||
|
||||
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
expect(
|
||||
() async => portDiscovery.query(),
|
||||
throwsException,
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Correctly builds Observatory URI with hostVmservicePort == 0', () async {
|
||||
final MDnsClient client = FakeMDnsClient(
|
||||
<PtrResourceRecord>[
|
||||
PtrResourceRecord('foo', year3000, domainName: 'bar'),
|
||||
],
|
||||
<String, List<SrvResourceRecord>>{
|
||||
'bar': <SrvResourceRecord>[
|
||||
SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
|
||||
mdnsClient: client,
|
||||
logger: BufferLogger.test(),
|
||||
flutterUsage: TestUsage(),
|
||||
);
|
||||
final Uri? uri = await portDiscovery.getObservatoryUri('bar', device, hostVmservicePort: 0);
|
||||
expect(uri.toString(), 'http://127.0.0.1:123/');
|
||||
expect(result?.domainName, 'srv-bar');
|
||||
expect(result?.port, 222);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -243,12 +686,14 @@ void main() {
|
||||
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
|
||||
@ -268,7 +713,7 @@ class FakeMDnsClient extends Fake implements MDnsClient {
|
||||
ResourceRecordQuery query, {
|
||||
Duration timeout = const Duration(seconds: 5),
|
||||
}) {
|
||||
if (T == PtrResourceRecord && query.fullyQualifiedName == MDnsObservatoryDiscovery.dartObservatoryName) {
|
||||
if (T == PtrResourceRecord && query.fullyQualifiedName == MDnsVmServiceDiscovery.dartVmServiceName) {
|
||||
return Stream<PtrResourceRecord>.fromIterable(ptrRecords) as Stream<T>;
|
||||
}
|
||||
if (T == SrvResourceRecord) {
|
||||
@ -279,6 +724,10 @@ class FakeMDnsClient extends Fake implements MDnsClient {
|
||||
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');
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user