// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/device_port_forwarder.dart'; import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/mdns_discovery.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:multicast_dns/multicast_dns.dart'; import 'package:test/fake.dart'; import '../src/common.dart'; void main() { group('mDNS Discovery', () { final int future = DateTime.now().add(const Duration(days: 1)).millisecondsSinceEpoch; setUp(() { setNetworkInterfaceLister( ({ bool? includeLoopback, bool? includeLinkLocal, InternetAddressType? type, }) async => <NetworkInterface>[], ); }); tearDown(() { resetNetworkInterfaceLister(); }); group('for attach', () { late MDnsClient emptyClient; setUp(() { emptyClient = FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}); }); 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.' ), ); }); }); 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('Find firstMatchingVmService with many available and no application id', () 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 MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery( mdnsClient: client, logger: BufferLogger.test(), flutterUsage: TestUsage(), ); final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(client); expect(result?.domainName, 'srv-foo'); }); testWithoutContext('Find firstMatchingVmService app id', () 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: 111, weight: 1, priority: 1, target: 'target-foo'), ], '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 MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery( mdnsClient: client, logger: BufferLogger.test(), flutterUsage: TestUsage(), ); final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService( client, applicationId: 'srv-bar' ); expect(result?.domainName, 'srv-bar'); expect(result?.port, 222); }); }); } class FakeMDnsClient extends Fake implements MDnsClient { FakeMDnsClient(this.ptrRecords, this.srvResponse, { this.txtResponse = const <String, List<TxtResourceRecord>>{}, this.ipResponse = const <String, List<IPAddressResourceRecord>>{}, this.osErrorOnStart = false, }); final List<PtrResourceRecord> ptrRecords; final Map<String, List<SrvResourceRecord>> srvResponse; final Map<String, List<TxtResourceRecord>> txtResponse; final Map<String, List<IPAddressResourceRecord>> ipResponse; final bool osErrorOnStart; @override Future<void> start({ InternetAddress? listenAddress, NetworkInterfacesFactory? interfacesFactory, int mDnsPort = 5353, InternetAddress? mDnsAddress, }) async { if (osErrorOnStart) { throw const OSError('Operation not supported on socket', 102); } } @override Stream<T> lookup<T extends ResourceRecord>( ResourceRecordQuery query, { Duration timeout = const Duration(seconds: 5), }) { if (T == PtrResourceRecord && query.fullyQualifiedName == MDnsVmServiceDiscovery.dartVmServiceName) { return Stream<PtrResourceRecord>.fromIterable(ptrRecords) as Stream<T>; } if (T == SrvResourceRecord) { final String key = query.fullyQualifiedName; return Stream<SrvResourceRecord>.fromIterable(srvResponse[key] ?? <SrvResourceRecord>[]) as Stream<T>; } if (T == TxtResourceRecord) { final String key = query.fullyQualifiedName; return Stream<TxtResourceRecord>.fromIterable(txtResponse[key] ?? <TxtResourceRecord>[]) as Stream<T>; } if (T == IPAddressResourceRecord) { final String key = query.fullyQualifiedName; return Stream<IPAddressResourceRecord>.fromIterable(ipResponse[key] ?? <IPAddressResourceRecord>[]) as Stream<T>; } throw UnsupportedError('Unsupported query type $T'); } @override void stop() {} } // 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 Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; @override bool isSupported() => true; @override bool isSupportedForProject(FlutterProject flutterProject) => true; @override DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder(); }