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

When we discover the Dart VM for iOS Core Devices (iOS 17+), we use both the device logs and mDNS simultaneously. Since mDNS is having issues with macOS 15, don't throw if mDNS errors - this will allow the Dart VM to have a chance to be found by the device logs instead. Improving experience for https://github.com/flutter/flutter/issues/150131. Also should unblock https://github.com/flutter/flutter/issues/166843. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1296 lines
46 KiB
Dart
1296 lines
46 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/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/globals.dart' as globals;
|
|
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:multicast_dns/multicast_dns.dart';
|
|
import 'package:test/fake.dart';
|
|
import 'package:unified_analytics/unified_analytics.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/fakes.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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
expect(portDiscovery.queryForAttach, throwsToolExit());
|
|
});
|
|
|
|
testWithoutContext('Find duplicates in preliminary client', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[
|
|
PtrResourceRecord('foo', future, domainName: 'bar'),
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.queryForAttach();
|
|
expect(result, isNotNull);
|
|
});
|
|
|
|
testWithoutContext('Find similar named in preliminary client', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[
|
|
PtrResourceRecord('foo', future, domainName: 'bar'),
|
|
PtrResourceRecord('foo', future, domainName: 'bar (2)'),
|
|
],
|
|
<String, List<SrvResourceRecord>>{
|
|
'bar': <SrvResourceRecord>[
|
|
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
|
],
|
|
'bar (2)': <SrvResourceRecord>[
|
|
SrvResourceRecord('bar', future, port: 123, weight: 1, priority: 1, target: 'appId'),
|
|
],
|
|
},
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: emptyClient,
|
|
preliminaryMDnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
expect(portDiscovery.queryForAttach, throwsToolExit());
|
|
});
|
|
|
|
testWithoutContext('No ports available', () async {
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: emptyClient,
|
|
preliminaryMDnsClient: emptyClient,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
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 MemoryFileSystem fs = MemoryFileSystem.test();
|
|
final FakeAnalytics fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
|
fs: fs,
|
|
fakeFlutterVersion: FakeFlutterVersion(),
|
|
);
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: emptyClient,
|
|
preliminaryMDnsClient: emptyClient,
|
|
logger: logger,
|
|
analytics: fakeAnalytics,
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForAttach('', FakeIOSDevice());
|
|
expect(uri, isNull);
|
|
expect(logger.errorText, contains('Personal Hotspot'));
|
|
expect(
|
|
fakeAnalytics.sentEvents,
|
|
contains(Event.appleUsageEvent(workflow: 'ios-mdns', parameter: 'no-ipv4-link-local')),
|
|
);
|
|
},
|
|
);
|
|
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForAttach(
|
|
'bar',
|
|
device,
|
|
hostVmservicePort: 0,
|
|
);
|
|
expect(uri.toString(), 'http://127.0.0.1:123/');
|
|
});
|
|
|
|
testWithoutContext('Get wireless 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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForAttach(
|
|
'bar',
|
|
device,
|
|
useDeviceIPAsHost: true,
|
|
);
|
|
expect(uri.toString(), 'http://111.111.111.111:1234/xyz/');
|
|
});
|
|
|
|
testWithoutContext('Get wireless 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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForAttach(
|
|
'bar',
|
|
device,
|
|
useDeviceIPAsHost: 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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
expect(
|
|
portDiscovery.getVMServiceUriForAttach('srv-asdf', device),
|
|
throwsToolExit(message: 'Did not find a Dart VM Service advertised for srv-asdf.'),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('for launch', () {
|
|
testWithoutContext('Ensure either port or device name are provided', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[],
|
|
<String, List<SrvResourceRecord>>{},
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
expect(
|
|
() async => portDiscovery.queryForLaunch(applicationId: 'app-id'),
|
|
throwsAssertionError,
|
|
);
|
|
});
|
|
|
|
testWithoutContext('No ports available', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[],
|
|
<String, List<SrvResourceRecord>>{},
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
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,
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
expect(
|
|
() async =>
|
|
portDiscovery.queryForLaunch(applicationId: 'app-id', deviceVmservicePort: 123),
|
|
throwsException,
|
|
);
|
|
});
|
|
|
|
// On macOS, the mDNS client's socket stream creates a SocketException if
|
|
// the app running the tool does not have Local Network permissions.
|
|
// See: https://github.com/flutter/flutter/issues/150131
|
|
test(
|
|
'On macOS, tool exits with a helpful message when mDNS lookup throws a SocketException',
|
|
() async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[],
|
|
<String, List<SrvResourceRecord>>{},
|
|
socketExceptionOnLookup: true,
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
expect(
|
|
() async => portDiscovery.firstMatchingVmService(client),
|
|
throwsToolExit(
|
|
message:
|
|
'Flutter could not access the local network.\n'
|
|
'\n'
|
|
'Please ensure your IDE or terminal app has permission to access '
|
|
'devices on the local network. This allows Flutter to connect to '
|
|
'the Dart VM.\n'
|
|
'\n'
|
|
'You can grant this permission in System Settings > Privacy & '
|
|
'Security > Local Network.\n',
|
|
),
|
|
);
|
|
},
|
|
// [intended] This tool exit message only works for macOS
|
|
skip: !globals.platform.isMacOS,
|
|
);
|
|
|
|
// On macOS, the mDNS client's socket stream creates a SocketException if
|
|
// the app running the tool does not have Local Network permissions.
|
|
// See: https://github.com/flutter/flutter/issues/150131
|
|
test(
|
|
'On macOS, tool exits with a helpful message when mDNS lookup throws an uncaught SocketException',
|
|
() async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[],
|
|
<String, List<SrvResourceRecord>>{},
|
|
uncaughtSocketExceptionOnLookup: true,
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
expect(
|
|
() async => portDiscovery.firstMatchingVmService(client),
|
|
throwsToolExit(
|
|
message:
|
|
'Flutter could not access the local network.\n'
|
|
'\n'
|
|
'Please ensure your IDE or terminal app has permission to access '
|
|
'devices on the local network. This allows Flutter to connect to '
|
|
'the Dart VM.\n'
|
|
'\n'
|
|
'You can grant this permission in System Settings > Privacy & '
|
|
'Security > Local Network.\n',
|
|
),
|
|
);
|
|
},
|
|
// [intended] This tool exit message only works for macOS
|
|
skip: !globals.platform.isMacOS,
|
|
);
|
|
|
|
test(
|
|
'On macOS, tool prints a helpful message when mDNS lookup throws an uncaught SocketException',
|
|
() async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[],
|
|
<String, List<SrvResourceRecord>>{},
|
|
uncaughtSocketExceptionOnLookup: true,
|
|
);
|
|
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: logger,
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
|
|
client,
|
|
throwOnMissingLocalNetworkPermissionsError: false,
|
|
);
|
|
|
|
expect(result, isNull);
|
|
expect(
|
|
logger.errorText,
|
|
contains(
|
|
'Flutter could not access the local network.\n'
|
|
'\n'
|
|
'Please ensure your IDE or terminal app has permission to access '
|
|
'devices on the local network. This allows Flutter to connect to '
|
|
'the Dart VM.\n'
|
|
'\n'
|
|
'You can grant this permission in System Settings > Privacy & '
|
|
'Security > Local Network.\n',
|
|
),
|
|
);
|
|
},
|
|
// [intended] This tool exit message only works for macOS
|
|
skip: !globals.platform.isMacOS,
|
|
);
|
|
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
|
'bar',
|
|
device,
|
|
hostVmservicePort: 0,
|
|
deviceVmservicePort: 123,
|
|
);
|
|
expect(uri.toString(), 'http://127.0.0.1:123/');
|
|
});
|
|
|
|
testWithoutContext('Get wireless 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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
|
'bar',
|
|
device,
|
|
useDeviceIPAsHost: true,
|
|
deviceVmservicePort: 1234,
|
|
);
|
|
expect(uri.toString(), 'http://111.111.111.111:1234/xyz/');
|
|
});
|
|
|
|
testWithoutContext('Get wireless 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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch(
|
|
'bar',
|
|
device,
|
|
useDeviceIPAsHost: 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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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('Matches on application id and device name', () 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-bar': <SrvResourceRecord>[
|
|
SrvResourceRecord(
|
|
'srv-foo',
|
|
future,
|
|
port: 123,
|
|
weight: 1,
|
|
priority: 1,
|
|
target: 'My-Phone.local',
|
|
),
|
|
],
|
|
},
|
|
);
|
|
final FakeIOSDevice device = FakeIOSDevice(name: 'My Phone');
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
final Uri? uri = await portDiscovery.getVMServiceUriForLaunch('srv-bar', device);
|
|
expect(uri.toString(), 'http://127.0.0.1:123/');
|
|
});
|
|
|
|
testWithoutContext(
|
|
'Throw error if unable to find VM Service with app id and device name',
|
|
() 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',
|
|
),
|
|
],
|
|
},
|
|
);
|
|
final FakeIOSDevice device = FakeIOSDevice(name: 'My Phone');
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
expect(
|
|
portDiscovery.getVMServiceUriForLaunch('srv-bar', device),
|
|
throwsToolExit(message: 'Did not find a Dart VM Service advertised for srv-bar'),
|
|
);
|
|
},
|
|
);
|
|
});
|
|
|
|
group('deviceNameMatchesTargetName', () {
|
|
testWithoutContext('compares case insensitive and without spaces, hyphens, .local', () {
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
|
|
expect(portDiscovery.deviceNameMatchesTargetName('My phone', 'My-Phone.local'), isTrue);
|
|
});
|
|
|
|
testWithoutContext('includes numbers in comparison', () {
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
expect(portDiscovery.deviceNameMatchesTargetName('My phone', 'My-Phone-2.local'), isFalse);
|
|
});
|
|
});
|
|
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
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(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
|
|
client,
|
|
applicationId: 'srv-bar',
|
|
);
|
|
expect(result?.domainName, 'srv-bar');
|
|
expect(result?.port, 222);
|
|
});
|
|
testWithoutContext('find with no txt record', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[PtrResourceRecord('foo', future, domainName: 'srv-foo')],
|
|
<String, List<SrvResourceRecord>>{
|
|
'srv-foo': <SrvResourceRecord>[
|
|
SrvResourceRecord(
|
|
'srv-foo',
|
|
future,
|
|
port: 111,
|
|
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')!,
|
|
),
|
|
],
|
|
},
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
|
|
client,
|
|
applicationId: 'srv-foo',
|
|
useDeviceIPAsHost: true,
|
|
);
|
|
expect(result?.domainName, 'srv-foo');
|
|
expect(result?.port, 111);
|
|
expect(result?.authCode, '');
|
|
expect(result?.ipAddress?.address, '111.111.111.111');
|
|
});
|
|
testWithoutContext('find with empty txt record', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[PtrResourceRecord('foo', future, domainName: 'srv-foo')],
|
|
<String, List<SrvResourceRecord>>{
|
|
'srv-foo': <SrvResourceRecord>[
|
|
SrvResourceRecord(
|
|
'srv-foo',
|
|
future,
|
|
port: 111,
|
|
weight: 1,
|
|
priority: 1,
|
|
target: 'target-foo',
|
|
),
|
|
],
|
|
},
|
|
txtResponse: <String, List<TxtResourceRecord>>{
|
|
'srv-foo': <TxtResourceRecord>[TxtResourceRecord('srv-foo', future, text: '')],
|
|
},
|
|
ipResponse: <String, List<IPAddressResourceRecord>>{
|
|
'target-foo': <IPAddressResourceRecord>[
|
|
IPAddressResourceRecord(
|
|
'target-foo',
|
|
0,
|
|
address: InternetAddress.tryParse('111.111.111.111')!,
|
|
),
|
|
],
|
|
},
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
|
|
client,
|
|
applicationId: 'srv-foo',
|
|
useDeviceIPAsHost: true,
|
|
);
|
|
expect(result?.domainName, 'srv-foo');
|
|
expect(result?.port, 111);
|
|
expect(result?.authCode, '');
|
|
expect(result?.ipAddress?.address, '111.111.111.111');
|
|
});
|
|
testWithoutContext('find with valid txt record', () async {
|
|
final MDnsClient client = FakeMDnsClient(
|
|
<PtrResourceRecord>[PtrResourceRecord('foo', future, domainName: 'srv-foo')],
|
|
<String, List<SrvResourceRecord>>{
|
|
'srv-foo': <SrvResourceRecord>[
|
|
SrvResourceRecord(
|
|
'srv-foo',
|
|
future,
|
|
port: 111,
|
|
weight: 1,
|
|
priority: 1,
|
|
target: 'target-foo',
|
|
),
|
|
],
|
|
},
|
|
txtResponse: <String, List<TxtResourceRecord>>{
|
|
'srv-foo': <TxtResourceRecord>[
|
|
TxtResourceRecord('srv-foo', future, text: 'authCode=xyz\n'),
|
|
],
|
|
},
|
|
ipResponse: <String, List<IPAddressResourceRecord>>{
|
|
'target-foo': <IPAddressResourceRecord>[
|
|
IPAddressResourceRecord(
|
|
'target-foo',
|
|
0,
|
|
address: InternetAddress.tryParse('111.111.111.111')!,
|
|
),
|
|
],
|
|
},
|
|
);
|
|
|
|
final MDnsVmServiceDiscovery portDiscovery = MDnsVmServiceDiscovery(
|
|
mdnsClient: client,
|
|
logger: BufferLogger.test(),
|
|
analytics: const NoOpAnalytics(),
|
|
);
|
|
final MDnsVmServiceDiscoveryResult? result = await portDiscovery.firstMatchingVmService(
|
|
client,
|
|
applicationId: 'srv-foo',
|
|
useDeviceIPAsHost: true,
|
|
);
|
|
expect(result?.domainName, 'srv-foo');
|
|
expect(result?.port, 111);
|
|
expect(result?.authCode, 'xyz/');
|
|
expect(result?.ipAddress?.address, '111.111.111.111');
|
|
});
|
|
});
|
|
}
|
|
|
|
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,
|
|
this.socketExceptionOnLookup = false,
|
|
this.uncaughtSocketExceptionOnLookup = 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;
|
|
final bool socketExceptionOnLookup;
|
|
final bool uncaughtSocketExceptionOnLookup;
|
|
|
|
@override
|
|
Future<void> start({
|
|
InternetAddress? listenAddress,
|
|
NetworkInterfacesFactory? interfacesFactory,
|
|
int mDnsPort = 5353,
|
|
InternetAddress? mDnsAddress,
|
|
Function? onError,
|
|
}) 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 (socketExceptionOnLookup) {
|
|
throw const SocketException('Socket Exception');
|
|
}
|
|
|
|
if (uncaughtSocketExceptionOnLookup) {
|
|
Zone.current.handleUncaughtError(
|
|
const SocketException('Socket Exception'),
|
|
StackTrace.current,
|
|
);
|
|
}
|
|
|
|
if (T == PtrResourceRecord &&
|
|
query.fullyQualifiedName == MDnsVmServiceDiscovery.dartVmServiceName) {
|
|
return Stream<PtrResourceRecord>.fromIterable(ptrRecords) as Stream<T>;
|
|
}
|
|
if (T == SrvResourceRecord) {
|
|
final String key = query.fullyQualifiedName;
|
|
return Stream<SrvResourceRecord>.fromIterable(srvResponse[key] ?? <SrvResourceRecord>[])
|
|
as Stream<T>;
|
|
}
|
|
if (T == TxtResourceRecord) {
|
|
final String key = query.fullyQualifiedName;
|
|
return Stream<TxtResourceRecord>.fromIterable(txtResponse[key] ?? <TxtResourceRecord>[])
|
|
as Stream<T>;
|
|
}
|
|
if (T == IPAddressResourceRecord) {
|
|
final String key = query.fullyQualifiedName;
|
|
return Stream<IPAddressResourceRecord>.fromIterable(
|
|
ipResponse[key] ?? <IPAddressResourceRecord>[],
|
|
)
|
|
as Stream<T>;
|
|
}
|
|
throw UnsupportedError('Unsupported query type $T');
|
|
}
|
|
|
|
@override
|
|
void stop() {}
|
|
}
|
|
|
|
class FakeIOSDevice extends Fake implements IOSDevice {
|
|
FakeIOSDevice({this.name = 'iPhone'});
|
|
|
|
@override
|
|
final String name;
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
|
|
|
@override
|
|
DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();
|
|
}
|