// 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:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/mdns_discovery.dart'; import 'package:mockito/mockito.dart'; import 'package:multicast_dns/multicast_dns.dart'; import '../src/common.dart'; import '../src/context.dart'; import '../src/mocks.dart'; void main() { group('mDNS Discovery', () { final int year3000 = DateTime(3000).millisecondsSinceEpoch; setUp(() { setNetworkInterfaceLister( ({ bool includeLoopback, bool includeLinkLocal, InternetAddressType type, }) async => [], ); }); tearDown(() { resetNetworkInterfaceLister(); }); MDnsClient getMockClient( List ptrRecords, Map> srvResponse, { Map> txtResponse = const >{}, }) { final MDnsClient client = MockMDnsClient(); when(client.lookup( ResourceRecordQuery.serverPointer(MDnsObservatoryDiscovery.dartObservatoryName), )).thenAnswer((_) => Stream.fromIterable(ptrRecords)); for (final MapEntry> entry in srvResponse.entries) { when(client.lookup( ResourceRecordQuery.service(entry.key), )).thenAnswer((_) => Stream.fromIterable(entry.value)); } for (final MapEntry> entry in txtResponse.entries) { when(client.lookup( ResourceRecordQuery.text(entry.key), )).thenAnswer((_) => Stream.fromIterable(entry.value)); } return client; } testUsingContext('No ports available', () async { final MDnsClient client = getMockClient([], >{}); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); final int port = (await portDiscovery.query())?.port; expect(port, isNull); }); testUsingContext('Prints helpful message when there is no ipv4 link local address.', () async { final MDnsClient client = getMockClient([], >{}); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); final Uri uri = await portDiscovery.getObservatoryUri( '', MockIOSDevice(), ); expect(uri, isNull); expect(testLogger.errorText, contains('Personal Hotspot')); }); testUsingContext('One port available, no appId', () async { final MDnsClient client = getMockClient( [ PtrResourceRecord('foo', year3000, domainName: 'bar'), ], >{ 'bar': [ SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'), ], }, ); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); final int port = (await portDiscovery.query())?.port; expect(port, 123); }); testUsingContext('One port available, no appId, with authCode', () async { final MDnsClient client = getMockClient( [ PtrResourceRecord('foo', year3000, domainName: 'bar'), ], >{ 'bar': [ SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'), ], }, txtResponse: >{ 'bar': [ TxtResourceRecord('bar', year3000, text: 'authCode=xyz\n'), ], }, ); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery( mdnsClient: client, ); final MDnsObservatoryDiscoveryResult result = await portDiscovery.query(); expect(result?.port, 123); expect(result?.authCode, 'xyz/'); }); testUsingContext('Multiple ports available, without appId', () async { final MDnsClient client = getMockClient( [ PtrResourceRecord('foo', year3000, domainName: 'bar'), PtrResourceRecord('baz', year3000, domainName: 'fiz'), ], >{ 'bar': [ SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'), ], 'fiz': [ SrvResourceRecord('fiz', year3000, port: 321, weight: 1, priority: 1, target: 'local'), ], }, ); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); expect(() => portDiscovery.query(), throwsToolExit()); }); testUsingContext('Multiple ports available, with appId', () async { final MDnsClient client = getMockClient( [ PtrResourceRecord('foo', year3000, domainName: 'bar'), PtrResourceRecord('baz', year3000, domainName: 'fiz'), ], >{ 'bar': [ SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'), ], 'fiz': [ SrvResourceRecord('fiz', year3000, port: 321, weight: 1, priority: 1, target: 'local'), ], }, ); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); final int port = (await portDiscovery.query(applicationId: 'fiz'))?.port; expect(port, 321); }); testUsingContext('Multiple ports available per process, with appId', () async { final MDnsClient client = getMockClient( [ PtrResourceRecord('foo', year3000, domainName: 'bar'), PtrResourceRecord('baz', year3000, domainName: 'fiz'), ], >{ 'bar': [ SrvResourceRecord('bar', year3000, port: 1234, weight: 1, priority: 1, target: 'appId'), SrvResourceRecord('bar', year3000, port: 123, weight: 1, priority: 1, target: 'appId'), ], 'fiz': [ 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); final int port = (await portDiscovery.query(applicationId: 'bar'))?.port; expect(port, 1234); }); testUsingContext('Query returns null', () async { final MDnsClient client = getMockClient( [], >{}, ); final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(mdnsClient: client); final int port = (await portDiscovery.query(applicationId: 'bar'))?.port; expect(port, isNull); }); }); } class MockMDnsClient extends Mock implements MDnsClient {}