// 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/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.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/doctor.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/ios_workflow.dart';
import 'package:flutter_tools/src/macos/xcdevice.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/runner/target_devices.dart';
import 'package:test/fake.dart';

import '../../src/common.dart';
import '../../src/context.dart';

void main() {
  testWithoutContext('Ensure factory returns TargetDevicesWithExtendedWirelessDeviceDiscovery on MacOS', () async {
    final BufferLogger logger = BufferLogger.test();
    final Platform platform = FakePlatform(operatingSystem: 'macos');
    final TestDeviceManager deviceManager = TestDeviceManager(
      logger: logger,
      platform: platform,
    );

    final TargetDevices targetDevices = TargetDevices(
      platform: platform,
      deviceManager: deviceManager,
      logger: logger,
    );

    expect(targetDevices is TargetDevicesWithExtendedWirelessDeviceDiscovery, true);
  });

  testWithoutContext('Ensure factory returns default when not on MacOS', () async {
    final BufferLogger logger = BufferLogger.test();
    final Platform platform = FakePlatform();
    final TestDeviceManager deviceManager = TestDeviceManager(
      logger: logger,
      platform: platform,
    );

    final TargetDevices targetDevices = TargetDevices(
      platform: platform,
      deviceManager: deviceManager,
      logger: logger,
    );

    expect(targetDevices is TargetDevicesWithExtendedWirelessDeviceDiscovery, false);
  });

  group('findAllTargetDevices on non-MacOS platform', () {
    late Platform platform;

    final FakeDevice attachedAndroidDevice1 = FakeDevice(deviceName: 'target-device-1');
    final FakeDevice attachedAndroidDevice2 = FakeDevice(deviceName: 'target-device-2');
    final FakeDevice attachedUnsupportedAndroidDevice = FakeDevice(deviceName: 'target-device-3', deviceSupported: false);
    final FakeDevice attachedUnsupportedForProjectAndroidDevice = FakeDevice(deviceName: 'target-device-4', deviceSupportForProject: false);

    final FakeDevice wirelessAndroidDevice1 = FakeDevice.wireless(deviceName: 'target-device-5');
    final FakeDevice wirelessAndroidDevice2 = FakeDevice.wireless(deviceName: 'target-device-6');
    final FakeDevice wirelessUnsupportedAndroidDevice = FakeDevice.wireless(deviceName: 'target-device-7', deviceSupported: false);
    final FakeDevice wirelessUnsupportedForProjectAndroidDevice = FakeDevice.wireless(deviceName: 'target-device-8', deviceSupportForProject: false);

    final FakeDevice nonEphemeralDevice = FakeDevice(deviceName: 'target-device-9', ephemeral: false);
    final FakeDevice fuchsiaDevice = FakeDevice.fuchsia(deviceName: 'target-device-10');

    final FakeDevice exactMatchAndroidDevice = FakeDevice(deviceName: 'target-device');
    final FakeDevice exactMatchWirelessAndroidDevice = FakeDevice.wireless(deviceName: 'target-device');
    final FakeDevice exactMatchAttachedUnsupportedAndroidDevice = FakeDevice(deviceName: 'target-device', deviceSupported: false);
    final FakeDevice exactMatchUnsupportedByProjectDevice = FakeDevice(deviceName: 'target-device', deviceSupportForProject: false);

    setUp(() {
      platform = FakePlatform();
    });

    group('when cannot launch anything', () {
      late BufferLogger logger;
      late FakeDoctor doctor;

      setUp(() {
        logger = BufferLogger.test();
        doctor = FakeDoctor(canLaunchAnything: false);
      });

      testUsingContext('does not search for devices', () async {
        final TestDeviceManager deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];

        final TargetDevices targetDevices = TargetDevices(
          platform: platform,
          deviceManager: deviceManager,
          logger: logger,
        );
        final List<Device>? devices = await targetDevices.findAllTargetDevices();

        expect(logger.errorText, equals('''
Unable to locate a development device; please run 'flutter doctor' for information about installing additional components.
'''));
        expect(devices, isNull);
        expect(deviceManager.androidDiscoverer.devicesCalled, 0);
        expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
      }, overrides: <Type, Generator>{
        Doctor: () => doctor,
      });
    });

    testUsingContext('ensure refresh when deviceDiscoveryTimeout is provided', () async {
      final BufferLogger logger = BufferLogger.test();
      final TestDeviceManager deviceManager = TestDeviceManager(
        logger: logger,
        platform: platform,
      );
      deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];
      deviceManager.androidDiscoverer.refreshDeviceList = <Device>[attachedAndroidDevice1, wirelessAndroidDevice1];
      deviceManager.hasSpecifiedAllDevices = true;

      final TargetDevices targetDevices = TargetDevices(
        platform: platform,
        deviceManager: deviceManager,
        logger: logger,
      );
      final List<Device>? devices = await targetDevices.findAllTargetDevices(
        deviceDiscoveryTimeout: const Duration(seconds: 2),
      );

      expect(logger.statusText, equals(''));
      expect(devices, <Device>[attachedAndroidDevice1, wirelessAndroidDevice1]);
      expect(deviceManager.androidDiscoverer.devicesCalled, 2);
      expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 1);
      expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
    });

    testUsingContext('ensure unsupported for projects are included when includeDevicesUnsupportedByProject is true', () async {
      final BufferLogger logger = BufferLogger.test();
      final TestDeviceManager deviceManager = TestDeviceManager(
        logger: logger,
        platform: platform,
      );
      deviceManager.androidDiscoverer.deviceList = <Device>[attachedUnsupportedAndroidDevice, attachedUnsupportedForProjectAndroidDevice];

      final TargetDevices targetDevices = TargetDevices(
        platform: platform,
        deviceManager: deviceManager,
        logger: logger,
      );
      final List<Device>? devices = await targetDevices.findAllTargetDevices(
        includeDevicesUnsupportedByProject: true,
      );

      expect(logger.statusText, equals(''));
      expect(devices, <Device>[attachedUnsupportedForProjectAndroidDevice]);
      expect(deviceManager.androidDiscoverer.devicesCalled, 2);
      expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
      expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
    });

    group('finds no devices', () {
      late BufferLogger logger;
      late TestDeviceManager deviceManager;
      late TargetDevices targetDevices;

      setUp(() {
        logger = BufferLogger.test();
        deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        targetDevices = TargetDevices(
          platform: platform,
          deviceManager: deviceManager,
          logger: logger,
        );
      });

      group('with device not specified', () {
        testUsingContext('when no devices', () async {
          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No supported devices connected.
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 3);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when device is unsupported by flutter or project', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[
            attachedUnsupportedAndroidDevice,
            attachedUnsupportedForProjectAndroidDevice,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No supported devices connected.

The following devices were found, but are not supported by this project:
target-device-3 (mobile) • xxx • android • Android 10 (unsupported)
target-device-4 (mobile) • xxx • android • Android 10
If you would like your app to run on android, consider running `flutter create .` to generate projects for these platforms.
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 3);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        group('when deviceConnectionInterface does not match', () {
          testUsingContext('filter of wireless', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];

            final TargetDevices targetDevices = TargetDevices(
              platform: platform,
              deviceManager: deviceManager,
              logger: logger,
              deviceConnectionInterface: DeviceConnectionInterface.wireless,
            );
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No supported devices connected.
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 2);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          });

          testUsingContext('filter of attached', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1];

            final TargetDevices targetDevices = TargetDevices(
              platform: platform,
              deviceManager: deviceManager,
              logger: logger,
              deviceConnectionInterface: DeviceConnectionInterface.attached,
            );
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No supported devices connected.
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 2);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          });
        });
      });

      group('with hasSpecifiedDeviceId', () {
        setUp(() {
          deviceManager.specifiedDeviceId = 'target-device';
        });

        testUsingContext('when no devices', () async {
          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 4);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when no devices match', () async {
          final FakeDevice device1 = FakeDevice(deviceName: 'no-match-1');
          final FakeDevice device2 = FakeDevice.wireless(deviceName: 'no-match-2');
          deviceManager.androidDiscoverer.deviceList = <Device>[device1, device2];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.

The following devices were found:
no-match-1 (mobile) • xxx • android • Android 10
no-match-2 (mobile) • xxx • android • Android 10
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 4);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when matching device is unsupported by flutter', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchAttachedUnsupportedAndroidDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.

The following devices were found:
target-device (mobile) • xxx • android • Android 10 (unsupported)
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 4);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        group('when deviceConnectionInterface does not match', () {
          testUsingContext('filter of wireless', () async {
            final FakeDevice device1 = FakeDevice(deviceName: 'not-a-match');
            final FakeDevice device2 = FakeDevice.wireless(deviceName: 'not-a-match-2');
            deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchAndroidDevice, device1, device2];

            final TargetDevices targetDevices = TargetDevices(
              platform: platform,
              deviceManager: deviceManager,
              logger: logger,
              deviceConnectionInterface: DeviceConnectionInterface.wireless,
            );
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.

The following devices were found:
not-a-match-2 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          });

          testUsingContext('filter of attached', () async {
            final FakeDevice device1 = FakeDevice(deviceName: 'not-a-match');
            final FakeDevice device2 = FakeDevice.wireless(deviceName: 'not-a-match-2');
            deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchWirelessAndroidDevice, device1, device2];

            final TargetDevices targetDevices = TargetDevices(
              platform: platform,
              deviceManager: deviceManager,
              logger: logger,
              deviceConnectionInterface: DeviceConnectionInterface.attached,
            );
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.

The following devices were found:
not-a-match (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          });
        });
      });

      group('with hasSpecifiedAllDevices', () {
        setUp(() {
          deviceManager.hasSpecifiedAllDevices = true;
        });

        testUsingContext('when no devices', () async {
          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found.
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 3);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when devices are either unsupported by flutter or project or all', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[
            attachedUnsupportedAndroidDevice,
            attachedUnsupportedForProjectAndroidDevice,
          ];
          deviceManager.otherDiscoverer.deviceList = <Device>[fuchsiaDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found.

The following devices were found, but are not supported by this project:
target-device-3 (mobile)  • xxx • android       • Android 10 (unsupported)
target-device-4 (mobile)  • xxx • android       • Android 10
target-device-10 (mobile) • xxx • fuchsia-arm64 • tester
If you would like your app to run on android or fuchsia, consider running `flutter create .` to generate projects for these platforms.
'''));
          expect(devices, isNull);
          expect(deviceManager.androidDiscoverer.devicesCalled, 3);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

      });
    });

    group('finds single device', () {
      late BufferLogger logger;
      late TestDeviceManager deviceManager;
      late TargetDevices targetDevices;

      setUp(() {
        logger = BufferLogger.test();
        deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        targetDevices = TargetDevices(
          platform: platform,
          deviceManager: deviceManager,
          logger: logger,
        );
      });

      group('with device not specified', () {
        testUsingContext('when single attached device', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[attachedAndroidDevice1]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 2);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when single wireless device', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[wirelessAndroidDevice1]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 2);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when multiple but only one ephemeral', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[nonEphemeralDevice, wirelessAndroidDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[wirelessAndroidDevice1]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 2);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });
      });

      group('with hasSpecifiedDeviceId', () {
        setUp(() {
          deviceManager.specifiedDeviceId = 'target-device';
        });

        testUsingContext('when multiple matches but first is unsupported by flutter', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[
            exactMatchAttachedUnsupportedAndroidDevice,
            exactMatchAndroidDevice,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchAndroidDevice]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 1);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when matching device is unsupported by project', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchUnsupportedByProjectDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchUnsupportedByProjectDevice]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 1);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when matching attached device', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchAndroidDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchAndroidDevice]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 1);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when matching wireless device', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchWirelessAndroidDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchWirelessAndroidDevice]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 1);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });

        testUsingContext('when exact matching an attached device and partial matching a wireless device', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchAndroidDevice, wirelessAndroidDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchAndroidDevice]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 1);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });
      });

      group('with hasSpecifiedAllDevices', () {
        setUp(() {
          deviceManager.hasSpecifiedAllDevices = true;
        });

        testUsingContext('when only one device', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[attachedAndroidDevice1]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 2);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });
      });

    });

    group('finds multiple devices', () {
      late BufferLogger logger;
      late TestDeviceManager deviceManager;
      late TargetDevices targetDevices;

      setUp(() {
        logger = BufferLogger.test();
        deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        targetDevices = TargetDevices(
          platform: platform,
          deviceManager: deviceManager,
          logger: logger,
        );
      });

      group('with device not specified', () {
        group('with stdinHasTerminal', () {
          late FakeTerminal terminal;

          setUp(() {
            terminal = FakeTerminal();
          });

          testUsingContext('including attached, wireless, unsupported devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[
              attachedAndroidDevice1,
              attachedUnsupportedAndroidDevice,
              attachedUnsupportedForProjectAndroidDevice,
              wirelessAndroidDevice1,
              wirelessUnsupportedAndroidDevice,
              wirelessUnsupportedForProjectAndroidDevice,
            ];
            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '2');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:
target-device-1 (mobile) • xxx • android • Android 10

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10

[1]: target-device-1 (xxx)
[2]: target-device-5 (xxx)
'''));
            expect(devices, <Device>[wirelessAndroidDevice1]);
            expect(deviceManager.androidDiscoverer.devicesCalled, 2);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];
            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
'''));
            expect(devices, <Device>[attachedAndroidDevice1]);
            expect(deviceManager.androidDiscoverer.devicesCalled, 2);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];
            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10

[1]: target-device-5 (xxx)
[2]: target-device-6 (xxx)
'''));
            expect(devices, <Device>[wirelessAndroidDevice1]);
            expect(deviceManager.androidDiscoverer.devicesCalled, 2);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });

        group('without stdinHasTerminal', () {
          late FakeTerminal terminal;

          setUp(() {
            terminal = FakeTerminal(stdinHasTerminal: false);
          });

          testUsingContext('including attached, wireless, unsupported devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[
              attachedAndroidDevice1,
              attachedUnsupportedAndroidDevice,
              attachedUnsupportedForProjectAndroidDevice,
              wirelessAndroidDevice1,
              wirelessUnsupportedAndroidDevice,
              wirelessUnsupportedForProjectAndroidDevice,
            ];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.

target-device-1 (mobile) • xxx • android • Android 10
target-device-4 (mobile) • xxx • android • Android 10

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-8 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 4);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.

target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 4);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 4);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });
      });

      group('with hasSpecifiedDeviceId', () {
        setUp(() {
          deviceManager.specifiedDeviceId = 'target-device';
        });

        group('with stdinHasTerminal', () {
          late FakeTerminal terminal;

          setUp(() {
            terminal = FakeTerminal();
          });

          testUsingContext('including attached, wireless, unsupported devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[
              attachedAndroidDevice1,
              attachedUnsupportedAndroidDevice,
              attachedUnsupportedForProjectAndroidDevice,
              wirelessAndroidDevice1,
              wirelessUnsupportedAndroidDevice,
              wirelessUnsupportedForProjectAndroidDevice,
            ];
            terminal.setPrompt(<String>['1', '2', '3', '4', 'q', 'Q'], '2');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 4 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-4 (mobile) • xxx • android • Android 10

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-8 (mobile) • xxx • android • Android 10

[1]: target-device-1 (xxx)
[2]: target-device-4 (xxx)
[3]: target-device-5 (xxx)
[4]: target-device-8 (xxx)
'''));
            expect(devices, <Device>[attachedUnsupportedForProjectAndroidDevice]);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];
            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
'''));
            expect(devices, <Device>[attachedAndroidDevice1]);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];
            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10

[1]: target-device-5 (xxx)
[2]: target-device-6 (xxx)
'''));
            expect(devices, <Device>[wirelessAndroidDevice1]);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });

        group('without stdinHasTerminal', () {
          late FakeTerminal terminal;

          setUp(() {
            terminal = FakeTerminal(stdinHasTerminal: false);
          });

          testUsingContext('including only one ephemeral', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[nonEphemeralDevice, attachedAndroidDevice1];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
target-device-9 (mobile) • xxx • android • Android 10
target-device-1 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including matching attached, wireless, unsupported devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[
              attachedAndroidDevice1,
              attachedUnsupportedAndroidDevice,
              attachedUnsupportedForProjectAndroidDevice,
              wirelessAndroidDevice1,
              wirelessUnsupportedAndroidDevice,
              wirelessUnsupportedForProjectAndroidDevice,
            ];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 4 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-4 (mobile) • xxx • android • Android 10

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-8 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:

Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10
'''));
            expect(devices, isNull);
            expect(deviceManager.androidDiscoverer.devicesCalled, 3);
            expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
            expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });
      });

      group('with hasSpecifiedAllDevices', () {
        setUp(() {
          deviceManager.hasSpecifiedAllDevices = true;
        });

        testUsingContext('including attached, wireless, unsupported devices', () async {
          deviceManager.androidDiscoverer.deviceList = <Device>[
            attachedAndroidDevice1,
            attachedUnsupportedAndroidDevice,
            attachedUnsupportedForProjectAndroidDevice,
            wirelessAndroidDevice1,
            wirelessUnsupportedAndroidDevice,
            wirelessUnsupportedForProjectAndroidDevice,
          ];
          deviceManager.otherDiscoverer.deviceList = <Device>[fuchsiaDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[attachedAndroidDevice1, wirelessAndroidDevice1]);
          expect(deviceManager.androidDiscoverer.devicesCalled, 2);
          expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
          expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
        });
      });
    });
  });

  group('findAllTargetDevices on mac platform', () {
    late Platform platform;

    final FakeIOSDevice attachedIOSDevice1 = FakeIOSDevice(deviceName: 'target-device-1');
    final FakeIOSDevice attachedIOSDevice2 = FakeIOSDevice(deviceName: 'target-device-2');
    final FakeIOSDevice attachedUnsupportedIOSDevice = FakeIOSDevice(deviceName: 'target-device-3', deviceSupported: false);
    final FakeIOSDevice attachedUnsupportedForProjectIOSDevice = FakeIOSDevice(deviceName: 'target-device-4', deviceSupportForProject: false);

    final FakeIOSDevice disconnectedWirelessIOSDevice1 = FakeIOSDevice.notConnectedWireless(deviceName: 'target-device-5');
    final FakeIOSDevice connectedWirelessIOSDevice1 = FakeIOSDevice.connectedWireless(deviceName: 'target-device-5');
    final FakeIOSDevice disconnectedWirelessIOSDevice2 = FakeIOSDevice.notConnectedWireless(deviceName: 'target-device-6');
    final FakeIOSDevice connectedWirelessIOSDevice2 = FakeIOSDevice.connectedWireless(deviceName: 'target-device-6');
    final FakeIOSDevice disconnectedWirelessUnsupportedIOSDevice = FakeIOSDevice.notConnectedWireless(deviceName: 'target-device-7', deviceSupported: false);
    final FakeIOSDevice connectedWirelessUnsupportedIOSDevice = FakeIOSDevice.connectedWireless(deviceName: 'target-device-7', deviceSupported: false);
    final FakeIOSDevice disconnectedWirelessUnsupportedForProjectIOSDevice = FakeIOSDevice.notConnectedWireless(deviceName: 'target-device-8', deviceSupportForProject: false);
    final FakeIOSDevice connectedWirelessUnsupportedForProjectIOSDevice = FakeIOSDevice.connectedWireless(deviceName: 'target-device-8', deviceSupportForProject: false);

    final FakeIOSDevice nonEphemeralDevice = FakeIOSDevice(deviceName: 'target-device-9', ephemeral: false);
    final FakeDevice fuchsiaDevice = FakeDevice.fuchsia(deviceName: 'target-device-10');

    final FakeIOSDevice exactMatchAttachedIOSDevice = FakeIOSDevice(deviceName: 'target-device');
    final FakeIOSDevice exactMatchAttachedUnsupportedIOSDevice = FakeIOSDevice(deviceName: 'target-device', deviceSupported: false);
    final FakeIOSDevice exactMatchUnsupportedByProjectDevice = FakeIOSDevice(deviceName: 'target-device', deviceSupportForProject: false);

    setUp(() {
      platform = FakePlatform(operatingSystem: 'macos');
    });

    group('when cannot launch anything', () {
      late BufferLogger logger;
      late FakeDoctor doctor;

      setUp(() {
        logger = BufferLogger.test();
        doctor = FakeDoctor(canLaunchAnything: false);
      });

      testUsingContext('does not search for devices', () async {
        final TestDeviceManager deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1];

        final TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
          deviceManager: deviceManager,
          logger: logger,
        );
        final List<Device>? devices = await targetDevices.findAllTargetDevices();

        expect(logger.errorText, equals('''
Unable to locate a development device; please run 'flutter doctor' for information about installing additional components.
'''));
        expect(devices, isNull);
        expect(deviceManager.iosDiscoverer.devicesCalled, 0);
        expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 0);
      }, overrides: <Type, Generator>{
        Doctor: () => doctor,
      });
    });

    testUsingContext('ensure refresh when deviceDiscoveryTimeout is provided', () async {
      final BufferLogger logger = BufferLogger.test();
      final TestDeviceManager deviceManager = TestDeviceManager(
        logger: logger,
        platform: platform,
      );
      deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1];
      deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1];

      final TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
        deviceManager: deviceManager,
        logger: logger,
      );
      final List<Device>? devices = await targetDevices.findAllTargetDevices(
        deviceDiscoveryTimeout: const Duration(seconds: 2),
      );

      expect(logger.statusText, equals(''));
      expect(devices, <Device>[connectedWirelessIOSDevice1]);
      expect(deviceManager.iosDiscoverer.devicesCalled, 2);
      expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
      expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 1);
    });

    testUsingContext('ensure no refresh when deviceConnectionInterface is attached', () async {
      final BufferLogger logger = BufferLogger.test();
      final TestDeviceManager deviceManager = TestDeviceManager(
        logger: logger,
        platform: platform,
      );
      deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1];

      final TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
        deviceManager: deviceManager,
        logger: logger,
        deviceConnectionInterface: DeviceConnectionInterface.attached,
      );
      final List<Device>? devices = await targetDevices.findAllTargetDevices();

      expect(logger.statusText, equals(''));
      expect(devices, <Device>[attachedIOSDevice1]);
      expect(deviceManager.iosDiscoverer.devicesCalled, 1);
      expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 0);
      expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 1);
    });

    testUsingContext('ensure unsupported for projects are included when includeDevicesUnsupportedByProject is true', () async {
      final BufferLogger logger = BufferLogger.test();
      final TestDeviceManager deviceManager = TestDeviceManager(
        logger: logger,
        platform: platform,
      );
      deviceManager.iosDiscoverer.deviceList = <Device>[attachedUnsupportedIOSDevice, attachedUnsupportedForProjectIOSDevice];

      final TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
        deviceManager: deviceManager,
        logger: logger,
      );
      final List<Device>? devices = await targetDevices.findAllTargetDevices(
        includeDevicesUnsupportedByProject: true,
      );

      expect(logger.statusText, equals(''));
      expect(devices, <Device>[attachedUnsupportedForProjectIOSDevice]);
      expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
      expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
    });

    group('finds no devices', () {
      late BufferLogger logger;
      late TestDeviceManager deviceManager;
      late TargetDevices targetDevices;

      setUp(() {
        logger = BufferLogger.test();
        deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        targetDevices = TargetDevices(
          platform: platform,
          deviceManager: deviceManager,
          logger: logger,
        );
      });

      group('with device not specified', () {
        testUsingContext('when no devices', () async {
          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No supported devices connected.
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('when device is unsupported by flutter or project', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[
            attachedUnsupportedIOSDevice,
            attachedUnsupportedForProjectIOSDevice,
            disconnectedWirelessUnsupportedIOSDevice,
            disconnectedWirelessUnsupportedForProjectIOSDevice,
          ];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[
            attachedUnsupportedIOSDevice,
            attachedUnsupportedForProjectIOSDevice,
            connectedWirelessUnsupportedIOSDevice,
            connectedWirelessUnsupportedForProjectIOSDevice,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No supported devices connected.

The following devices were found, but are not supported by this project:
target-device-3 (mobile) • xxx • ios • iOS 16 (unsupported)
target-device-4 (mobile) • xxx • ios • iOS 16
target-device-7 (mobile) • xxx • ios • iOS 16 (unsupported)
target-device-8 (mobile) • xxx • ios • iOS 16
If you would like your app to run on ios, consider running `flutter create .` to generate projects for these platforms.
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('when all found devices are not connected', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[
            disconnectedWirelessIOSDevice1,
            disconnectedWirelessIOSDevice2,
          ];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[
            disconnectedWirelessIOSDevice1,
            disconnectedWirelessIOSDevice2,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No supported devices connected.
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        group('when deviceConnectionInterface does not match', () {
          testUsingContext('filter of wireless', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[attachedIOSDevice1];

            final TestTargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
              deviceConnectionInterface: DeviceConnectionInterface.wireless,
            );
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

No supported devices connected.
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 2);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 1);
          });
        });
      });

      group('with hasSpecifiedDeviceId', () {
        setUp(() {
          deviceManager.specifiedDeviceId = 'target-device';
        });

        testUsingContext('when no devices', () async {
          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No supported devices found with name or id matching 'target-device'.
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 4);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when no devices match', () async {
          final FakeIOSDevice device1 = FakeIOSDevice(deviceName: 'no-match-1');
          final FakeIOSDevice device2 = FakeIOSDevice.notConnectedWireless(deviceName: 'no-match-2');
          final FakeIOSDevice device2Connected = FakeIOSDevice.connectedWireless(deviceName: 'no-match-2');
          deviceManager.iosDiscoverer.deviceList = <Device>[device1, device2];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[device1,device2Connected];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No supported devices found with name or id matching 'target-device'.

The following devices were found:
no-match-1 (mobile) • xxx • ios • iOS 16
no-match-2 (mobile) • xxx • ios • iOS 16
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 4);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when matching device is unsupported by flutter', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[exactMatchAttachedUnsupportedIOSDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No supported devices found with name or id matching 'target-device'.

The following devices were found:
target-device (mobile) • xxx • ios • iOS 16 (unsupported)
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 4);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when only matching device is dev mode disabled', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device', devModeEnabled: false)];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
To use 'target-device' for development, enable Developer Mode in Settings → Privacy & Security.
'''));
          expect(devices, isNull);
        });

        testUsingContext('when one of the matching devices has dev mode disabled', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device-1', devModeEnabled: false, isConnected: false),
            FakeIOSDevice(deviceName: 'target-device-2', devModeEnabled: true)];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();
          expect(logger.statusText, equals('''
To use 'target-device-1' for development, enable Developer Mode in Settings → Privacy & Security.
Checking for wireless devices...
'''));
          expect(devices, isNotNull);
        });

        testUsingContext('when all matching devices are dev mode disabled', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device-1', devModeEnabled: false, isConnected: false),
            FakeIOSDevice(deviceName: 'target-device-2', devModeEnabled: false, isConnected: false)];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
To use 'target-device-1' for development, enable Developer Mode in Settings → Privacy & Security.
To use 'target-device-2' for development, enable Developer Mode in Settings → Privacy & Security.
No devices found yet. Checking for wireless devices...

No supported devices found with name or id matching 'target-device'.
'''));
          expect(devices, isNull);
        });

        group('when deviceConnectionInterface does not match', () {
          testUsingContext('filter of wireless', () async {
            final FakeIOSDevice device1 = FakeIOSDevice.notConnectedWireless(deviceName: 'not-a-match');
            final FakeIOSDevice device1Connected = FakeIOSDevice.connectedWireless(deviceName: 'not-a-match');
            deviceManager.iosDiscoverer.deviceList = <Device>[exactMatchAttachedIOSDevice, device1];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[exactMatchAttachedIOSDevice, device1Connected];

            final TestTargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
              deviceConnectionInterface: DeviceConnectionInterface.wireless,
            );
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

No supported devices found with name or id matching 'target-device'.

The following devices were found:
not-a-match (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          });
        });
      });

      group('with hasSpecifiedAllDevices', () {
        setUp(() {
          deviceManager.hasSpecifiedAllDevices = true;
        });

        testUsingContext('when no devices', () async {
          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No devices found.
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('when devices are either unsupported by flutter or project or all', () async {
          deviceManager.otherDiscoverer.deviceList = <Device>[fuchsiaDevice];
          deviceManager.iosDiscoverer.deviceList = <Device>[
            attachedUnsupportedIOSDevice,
            attachedUnsupportedForProjectIOSDevice,
            disconnectedWirelessUnsupportedIOSDevice,
            disconnectedWirelessUnsupportedForProjectIOSDevice,
          ];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[
            attachedUnsupportedIOSDevice,
            attachedUnsupportedForProjectIOSDevice,
            connectedWirelessUnsupportedIOSDevice,
            connectedWirelessUnsupportedForProjectIOSDevice,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

No devices found.

The following devices were found, but are not supported by this project:
target-device-10 (mobile) • xxx • fuchsia-arm64 • tester
target-device-3 (mobile)  • xxx • ios           • iOS 16 (unsupported)
target-device-4 (mobile)  • xxx • ios           • iOS 16
target-device-7 (mobile)  • xxx • ios           • iOS 16 (unsupported)
target-device-8 (mobile)  • xxx • ios           • iOS 16
If you would like your app to run on fuchsia or ios, consider running `flutter create .` to generate projects for these platforms.
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });
      });
    });

    group('finds single device', () {
      late TestBufferLogger logger;
      late TestDeviceManager deviceManager;
      late TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices;

      setUp(() {
        logger = TestBufferLogger.test();
        deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
        targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
          deviceManager: deviceManager,
          logger: logger,
        );
      });

      group('with device not specified', () {
        testUsingContext('when single ephemeral attached device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[attachedIOSDevice1]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('when single wireless device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...
'''));
        expect(devices, <Device>[connectedWirelessIOSDevice1]);
        expect(deviceManager.iosDiscoverer.devicesCalled, 2);
        expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
        expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('when multiple but only one attached ephemeral', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice, attachedIOSDevice1, disconnectedWirelessIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[attachedIOSDevice1]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        group('with stdinHasTerminal', () {
          late FakeTerminal terminal;

          setUp(() {
            terminal = FakeTerminal(supportsColor: true);
            logger = TestBufferLogger.test(terminal: terminal);
          });

          testUsingContext('when single non-ephemeral attached device', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice];

            final TestTargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
            targetDevices.waitForWirelessBeforeInput = true;
            targetDevices.deviceSelection.input = <String>['1'];
            logger.originalStatusText = '''
Connected devices:
target-device-9 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-9 (xxx)
''';

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:
target-device-9 (mobile) • xxx • ios • iOS 16

No wireless devices were found.

[1]: target-device-9 (xxx)
Please choose one (or "q" to quit): '''));
            expect(devices, <Device>[nonEphemeralDevice]);
            expect(deviceManager.iosDiscoverer.devicesCalled, 2);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });


          testUsingContext('handle invalid options for device', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice];

            final TestTargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
            targetDevices.waitForWirelessBeforeInput = true;

            // Having the '0' first is an invalid choice for a device, the second
            // item in the list is a '2' which is out of range since we only have
            // one item in the deviceList. The final item in the list, is '1'
            // which is a valid option though which will return a valid device
            //
            // Important: if none of the values in the list are valid, the test will
            // hang indefinitely since the [userSelectDevice()] method uses a while
            // loop to listen for valid devices
            targetDevices.deviceSelection.input = <String>['0', '2', '1'];
            logger.originalStatusText = '''
Connected devices:
target-device-9 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-9 (xxx)
''';

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:
target-device-9 (mobile) • xxx • ios • iOS 16

No wireless devices were found.

[1]: target-device-9 (xxx)
Please choose one (or "q" to quit): '''));
            expect(devices, <Device>[nonEphemeralDevice]);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });

        group('without stdinHasTerminal', () {
          late FakeTerminal terminal;

          setUp(() {
            terminal = FakeTerminal(stdinHasTerminal: false);
            targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
          });

          testUsingContext('when single non-ephemeral attached device', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
            expect(devices, <Device>[nonEphemeralDevice]);
            expect(deviceManager.iosDiscoverer.devicesCalled, 2);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });
      });

      group('with hasSpecifiedDeviceId', () {
        setUp(() {
          deviceManager.specifiedDeviceId = 'target-device';
        });

        testUsingContext('when multiple matches but first is unsupported by flutter', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[
            exactMatchAttachedUnsupportedIOSDevice,
            exactMatchAttachedIOSDevice,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchAttachedIOSDevice]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when matching device is unsupported by project', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[exactMatchUnsupportedByProjectDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchUnsupportedByProjectDevice]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when matching attached device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[exactMatchAttachedIOSDevice, disconnectedWirelessIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchAttachedIOSDevice]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when exact matching wireless device', () async {
          final FakeIOSDevice exactMatchWirelessDevice = FakeIOSDevice.notConnectedWireless(deviceName: 'target-device');
          deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, exactMatchWirelessDevice];
          deviceManager.setDeviceToWaitFor(exactMatchWirelessDevice, DeviceConnectionInterface.wireless);

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Waiting for target-device to connect...
'''));
          expect(devices, <Device>[exactMatchWirelessDevice]);
          expect(devices?.first.isConnected, true);
          expect(devices?.first.connectionInterface, DeviceConnectionInterface.wireless);
          expect(deviceManager.iosDiscoverer.devicesCalled, 1);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isTrue);
        });

        testUsingContext('when partially matching single wireless devices', () async {
          final FakeIOSDevice partialMatchWirelessDevice = FakeIOSDevice.notConnectedWireless(deviceName: 'target-device-1');
          deviceManager.iosDiscoverer.deviceList = <Device>[partialMatchWirelessDevice];
          deviceManager.setDeviceToWaitFor(partialMatchWirelessDevice, DeviceConnectionInterface.wireless);

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Waiting for target-device-1 to connect...
'''));
          expect(devices, <Device>[partialMatchWirelessDevice]);
          expect(devices?.first.isConnected, true);
          expect(devices?.first.connectionInterface, DeviceConnectionInterface.wireless);
          expect(deviceManager.iosDiscoverer.devicesCalled, 1);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isTrue);
        });

        testUsingContext('when exact matching an attached device and partial matching a wireless device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[exactMatchAttachedIOSDevice, connectedWirelessIOSDevice1];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[exactMatchAttachedIOSDevice, connectedWirelessIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[exactMatchAttachedIOSDevice]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when partially matching multiple device but only one is connected', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, disconnectedWirelessIOSDevice1];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[attachedIOSDevice1, disconnectedWirelessIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
          expect(devices, <Device>[attachedIOSDevice1]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when partially matching single attached device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[attachedIOSDevice1]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when partially matching wireless device and an attached device from different discoverer', () async {
          final FakeDevice androidDevice = FakeDevice(deviceName: 'target-device-android');
          deviceManager.androidDiscoverer.deviceList = <Device>[androidDevice];
          deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[disconnectedWirelessIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
          expect(devices, <Device>[androidDevice]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 3);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
        });

        testUsingContext('when matching single non-ephemeral attached device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals(''));
          expect(devices, <Device>[nonEphemeralDevice]);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });
      });

      group('with hasSpecifiedAllDevices', () {
        setUp(() {
          deviceManager.hasSpecifiedAllDevices = true;
        });

        testUsingContext('when only one device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
          expect(devices, <Device>[attachedIOSDevice1]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('when single non-ephemeral attached device', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
          expect(devices, <Device>[nonEphemeralDevice]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });
      });

    });

    group('finds multiple devices', () {
      late TestBufferLogger logger;
      late TestDeviceManager deviceManager;

      setUp(() {
        logger = TestBufferLogger.test();
        deviceManager = TestDeviceManager(
          logger: logger,
          platform: platform,
        );
      });

      group('with device not specified', () {
        group('with stdinHasTerminal', () {
          late FakeTerminal terminal;
          late TestTargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices;

          setUp(() {
            terminal = FakeTerminal(supportsColor: true);
            logger = TestBufferLogger.test(terminal: terminal);
            targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
          });

          testUsingContext('including attached, wireless, unsupported devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[
              attachedIOSDevice1,
              attachedIOSDevice2,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              disconnectedWirelessIOSDevice1,
              disconnectedWirelessUnsupportedIOSDevice,
              disconnectedWirelessUnsupportedForProjectIOSDevice,
            ];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[
              attachedIOSDevice1,
              attachedIOSDevice2,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              connectedWirelessIOSDevice1,
              connectedWirelessUnsupportedIOSDevice,
              connectedWirelessUnsupportedForProjectIOSDevice,
            ];

            targetDevices.waitForWirelessBeforeInput = true;
            targetDevices.deviceSelection.input = <String>['3'];
            logger.originalStatusText = '''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
''';

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
[3]: target-device-5 (xxx)
Please choose one (or "q" to quit): '''));
          expect(devices, <Device>[connectedWirelessIOSDevice1]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2];

            targetDevices.waitForWirelessBeforeInput = true;
            targetDevices.deviceSelection.input = <String>['2'];
            logger.originalStatusText = '''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
''';

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

No wireless devices were found.

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
Please choose one (or "q" to quit): '''));
          expect(devices, <Device>[attachedIOSDevice2]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1, disconnectedWirelessIOSDevice2];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1, connectedWirelessIOSDevice2];

            targetDevices.waitForWirelessBeforeInput = true;
            targetDevices.deviceSelection.input = <String>['2'];
            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

Connected devices:

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-6 (mobile) • xxx • ios • iOS 16

[1]: target-device-5 (xxx)
[2]: target-device-6 (xxx)
'''));
          expect(devices, <Device>[connectedWirelessIOSDevice1]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          group('but no color support', () {
            setUp(() {
              terminal = FakeTerminal();
              logger = TestBufferLogger.test(terminal: terminal);
              targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
                deviceManager: deviceManager,
                logger: logger,
              );
            });

            testUsingContext('and waits for wireless devices to return', () async {
              deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2, disconnectedWirelessIOSDevice1];
              deviceManager.iosDiscoverer.refreshDeviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2, connectedWirelessIOSDevice1];

              terminal.setPrompt(<String>['1', '2', '3', 'q', 'Q'], '1');
              final List<Device>? devices = await targetDevices.findAllTargetDevices();

              expect(logger.statusText, equals('''
Checking for wireless devices...

Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
[3]: target-device-5 (xxx)
'''));
              expect(devices, <Device>[attachedIOSDevice1]);
              expect(deviceManager.iosDiscoverer.devicesCalled, 2);
              expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
              expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            }, overrides: <Type, Generator>{
              AnsiTerminal: () => terminal,
            });
          });

          group('with verbose logging', () {
            setUp(() {
              logger = TestBufferLogger.test(terminal: terminal, verbose: true);
              targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
                deviceManager: deviceManager,
                logger: logger,
              );
            });

            testUsingContext('including only attached devices', () async {
              deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2];

              targetDevices.waitForWirelessBeforeInput = true;
              targetDevices.deviceSelection.input = <String>['2'];
              logger.originalStatusText = '''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
''';

              final List<Device>? devices = await targetDevices.findAllTargetDevices();

              expect(logger.statusText, equals('''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
No wireless devices were found.
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
Please choose one (or "q" to quit): '''));

              expect(devices, <Device>[attachedIOSDevice2]);
              expect(deviceManager.iosDiscoverer.devicesCalled, 2);
              expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
              expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            }, overrides: <Type, Generator>{
              AnsiTerminal: () => terminal,
            });

            testUsingContext('including attached and wireless devices', () async {
              deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2, disconnectedWirelessIOSDevice1];
              deviceManager.iosDiscoverer.refreshDeviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2, connectedWirelessIOSDevice1];

              targetDevices.waitForWirelessBeforeInput = true;
              targetDevices.deviceSelection.input = <String>['2'];
              logger.originalStatusText = '''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
''';
              final List<Device>? devices = await targetDevices.findAllTargetDevices();

              expect(logger.statusText, equals('''
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
Connected devices:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
[3]: target-device-5 (xxx)
Please choose one (or "q" to quit): '''));

              expect(devices, <Device>[attachedIOSDevice2]);
              expect(deviceManager.iosDiscoverer.devicesCalled, 2);
              expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
              expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            }, overrides: <Type, Generator>{
              AnsiTerminal: () => terminal,
            });
          });
        });

        group('without stdinHasTerminal', () {
          late FakeTerminal terminal;
          late TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices;

          setUp(() {
            terminal = FakeTerminal(stdinHasTerminal: false);
            targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
          });

          testUsingContext('including attached, wireless, unsupported devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[
              attachedIOSDevice1,
              attachedIOSDevice2,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              disconnectedWirelessIOSDevice1,
              disconnectedWirelessUnsupportedIOSDevice,
              disconnectedWirelessUnsupportedForProjectIOSDevice,
            ];
            deviceManager.iosDiscoverer.deviceList = <Device>[
              attachedIOSDevice1,
              attachedIOSDevice2,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              connectedWirelessIOSDevice1,
              connectedWirelessUnsupportedIOSDevice,
              connectedWirelessUnsupportedForProjectIOSDevice,
            ];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.

target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16
target-device-4 (mobile) • xxx • ios • iOS 16

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-8 (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 4);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.

target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16
'''));
          expect(devices, isNull);
          expect(deviceManager.iosDiscoverer.devicesCalled, 4);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1, disconnectedWirelessIOSDevice2];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1, connectedWirelessIOSDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-6 (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 4);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });
      });

      group('with hasSpecifiedDeviceId', () {
        setUp(() {
          deviceManager.specifiedDeviceId = 'target-device';
        });

        group('with stdinHasTerminal', () {
          late FakeTerminal terminal;
          late TestTargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices;

          setUp(() {
            terminal = FakeTerminal(supportsColor: true);
            logger = TestBufferLogger.test(terminal: terminal);
            targetDevices = TestTargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
          });

          testUsingContext('including attached, wireless, unsupported devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[
              attachedIOSDevice1,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              disconnectedWirelessIOSDevice1,
              disconnectedWirelessUnsupportedIOSDevice,
              disconnectedWirelessUnsupportedForProjectIOSDevice,
            ];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[
              attachedIOSDevice1,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              connectedWirelessIOSDevice1,
              connectedWirelessUnsupportedIOSDevice,
              connectedWirelessUnsupportedForProjectIOSDevice,
            ];

            targetDevices.waitForWirelessBeforeInput = true;
            targetDevices.deviceSelection.input = <String>['3'];
            logger.originalStatusText = '''
Found multiple devices with name or id matching target-device:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-4 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-4 (xxx)
''';
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found multiple devices with name or id matching target-device:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-4 (mobile) • xxx • ios • iOS 16

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-8 (mobile) • xxx • ios • iOS 16

[1]: target-device-1 (xxx)
[2]: target-device-4 (xxx)
[3]: target-device-5 (xxx)
[4]: target-device-8 (xxx)
Please choose one (or "q" to quit): '''));
            expect(devices, <Device>[connectedWirelessIOSDevice1]);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2];

            targetDevices.waitForWirelessBeforeInput = true;
            targetDevices.deviceSelection.input = <String>['2'];
            logger.originalStatusText = '''
Found multiple devices with name or id matching target-device:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

Checking for wireless devices...

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
''';
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Found multiple devices with name or id matching target-device:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16

No wireless devices were found.

[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
Please choose one (or "q" to quit): '''));
            expect(devices, <Device>[attachedIOSDevice2]);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1, disconnectedWirelessIOSDevice2];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1, connectedWirelessIOSDevice2];

            terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');
            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

Found 2 devices with name or id matching target-device:

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-6 (mobile) • xxx • ios • iOS 16

[1]: target-device-5 (xxx)
[2]: target-device-6 (xxx)
'''));
            expect(devices, <Device>[connectedWirelessIOSDevice1]);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });

        group('without stdinHasTerminal', () {
          late FakeTerminal terminal;
          late TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices;

          setUp(() {
            terminal = FakeTerminal(stdinHasTerminal: false);
            targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
              deviceManager: deviceManager,
              logger: logger,
            );
          });

          testUsingContext('including only one ephemeral', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[nonEphemeralDevice, attachedIOSDevice1];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

Found 2 devices with name or id matching target-device:
target-device-9 (mobile) • xxx • ios • iOS 16
target-device-1 (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including matching attached, wireless, unsupported devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[
              attachedIOSDevice1,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              disconnectedWirelessIOSDevice1,
              disconnectedWirelessUnsupportedIOSDevice,
              disconnectedWirelessUnsupportedForProjectIOSDevice,
            ];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[
              attachedIOSDevice1,
              attachedUnsupportedIOSDevice,
              attachedUnsupportedForProjectIOSDevice,
              connectedWirelessIOSDevice1,
              connectedWirelessUnsupportedIOSDevice,
              connectedWirelessUnsupportedForProjectIOSDevice,
            ];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

Found 4 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-4 (mobile) • xxx • ios • iOS 16

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-8 (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only attached devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
Checking for wireless devices...

Found 2 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • ios • iOS 16
target-device-2 (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });

          testUsingContext('including only wireless devices', () async {
            deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1, disconnectedWirelessIOSDevice2];
            deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1, connectedWirelessIOSDevice2];

            final List<Device>? devices = await targetDevices.findAllTargetDevices();

            expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...

Found 2 devices with name or id matching target-device:

Wirelessly connected devices:
target-device-5 (mobile) • xxx • ios • iOS 16
target-device-6 (mobile) • xxx • ios • iOS 16
'''));
            expect(devices, isNull);
            expect(deviceManager.iosDiscoverer.devicesCalled, 3);
            expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
            expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
            expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
          }, overrides: <Type, Generator>{
            AnsiTerminal: () => terminal,
          });
        });
      });

      group('with hasSpecifiedAllDevices', () {
        late TargetDevicesWithExtendedWirelessDeviceDiscovery targetDevices;
        setUp(() {
          deviceManager.hasSpecifiedAllDevices = true;
          targetDevices = TargetDevicesWithExtendedWirelessDeviceDiscovery(
            deviceManager: deviceManager,
            logger: logger,
          );
        });

        testUsingContext('including attached, wireless, unsupported devices', () async {
          deviceManager.otherDiscoverer.deviceList = <Device>[fuchsiaDevice];
          deviceManager.iosDiscoverer.deviceList = <Device>[
            attachedIOSDevice1,
            attachedUnsupportedIOSDevice,
            attachedUnsupportedForProjectIOSDevice,
            disconnectedWirelessIOSDevice1,
            disconnectedWirelessUnsupportedIOSDevice,
            disconnectedWirelessUnsupportedForProjectIOSDevice,
          ];
          deviceManager.iosDiscoverer.deviceList = <Device>[
            attachedIOSDevice1,
            attachedUnsupportedIOSDevice,
            attachedUnsupportedForProjectIOSDevice,
            connectedWirelessIOSDevice1,
            connectedWirelessUnsupportedIOSDevice,
            connectedWirelessUnsupportedForProjectIOSDevice,
          ];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
          expect(devices, <Device>[attachedIOSDevice1, connectedWirelessIOSDevice1]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('including only attached devices', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[attachedIOSDevice1, attachedIOSDevice2];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
Checking for wireless devices...
'''));
          expect(devices, <Device>[attachedIOSDevice1, attachedIOSDevice2]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });

        testUsingContext('including only wireless devices', () async {
          deviceManager.iosDiscoverer.deviceList = <Device>[disconnectedWirelessIOSDevice1, disconnectedWirelessIOSDevice2];
          deviceManager.iosDiscoverer.refreshDeviceList = <Device>[connectedWirelessIOSDevice1, connectedWirelessIOSDevice2];

          final List<Device>? devices = await targetDevices.findAllTargetDevices();

          expect(logger.statusText, equals('''
No devices found yet. Checking for wireless devices...
'''));
          expect(devices, <Device>[connectedWirelessIOSDevice1, connectedWirelessIOSDevice2]);
          expect(deviceManager.iosDiscoverer.devicesCalled, 2);
          expect(deviceManager.iosDiscoverer.discoverDevicesCalled, 1);
          expect(deviceManager.iosDiscoverer.numberOfTimesPolled, 2);
        });
      });
    });
  });
}

class TestTargetDevicesWithExtendedWirelessDeviceDiscovery extends TargetDevicesWithExtendedWirelessDeviceDiscovery {
  TestTargetDevicesWithExtendedWirelessDeviceDiscovery({
    required super.deviceManager,
    required super.logger,
    super.deviceConnectionInterface,
  })  : _deviceSelection = TestTargetDeviceSelection(logger);

  final TestTargetDeviceSelection _deviceSelection;

  @override
  TestTargetDeviceSelection get deviceSelection => _deviceSelection;
}

class TestTargetDeviceSelection extends TargetDeviceSelection {
  TestTargetDeviceSelection(super.logger);

  List<String> input = <String>[];

  @override
  Future<String> readUserInput() async {
    // If only one value is provided for the input, continue
    // to return that one input value without popping
    //
    // If more than one input values are provided, we are simulating
    // the user selecting more than one option for a device, so we will pop
    // them out from the front
    if (input.length > 1) {
      return input.removeAt(0);
    }

    return input[0];
  }
}

class TestDeviceManager extends DeviceManager {
  TestDeviceManager({
    required this.logger,
    required this.platform,
  }) : super(logger: logger);

  final Logger logger;
  final Platform platform;

  @override
  String? specifiedDeviceId;

  @override
  bool hasSpecifiedAllDevices = false;

  final TestPollingDeviceDiscovery androidDiscoverer = TestPollingDeviceDiscovery(
    'android',
  );
  final TestPollingDeviceDiscovery otherDiscoverer = TestPollingDeviceDiscovery(
    'other',
  );
  late final TestIOSDeviceDiscovery iosDiscoverer = TestIOSDeviceDiscovery(
    platform: platform,
    xcdevice: FakeXcdevice(),
    iosWorkflow: FakeIOSWorkflow(),
    logger: logger,
  );

  @override
  List<DeviceDiscovery> get deviceDiscoverers {
    return <DeviceDiscovery>[
      androidDiscoverer,
      otherDiscoverer,
      iosDiscoverer,
    ];
  }

  void setDeviceToWaitFor(
    IOSDevice device,
    DeviceConnectionInterface connectionInterface,
  ) {
    final XCDeviceEventInterface eventInterface =
        connectionInterface == DeviceConnectionInterface.wireless
            ? XCDeviceEventInterface.wifi
            : XCDeviceEventInterface.usb;
    iosDiscoverer.xcdevice.waitForDeviceEvent = XCDeviceEventNotification(
      XCDeviceEvent.attach,
      eventInterface,
      device.id,
    );
  }
}

class TestPollingDeviceDiscovery extends PollingDeviceDiscovery {
  TestPollingDeviceDiscovery(super.name);

  List<Device> deviceList = <Device>[];
  List<Device> refreshDeviceList = <Device>[];
  int devicesCalled = 0;
  int discoverDevicesCalled = 0;
  int numberOfTimesPolled = 0;

  @override
  bool get supportsPlatform => true;

  @override
  List<String> get wellKnownIds => const <String>[];

  @override
  Future<List<Device>> pollingGetDevices({Duration? timeout}) async {
    numberOfTimesPolled++;
    return deviceList;
  }

  @override
  Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async {
    devicesCalled += 1;
    return super.devices(filter: filter);
  }

  @override
  Future<List<Device>> discoverDevices({
    Duration? timeout,
    DeviceDiscoveryFilter? filter,
  }) {
    discoverDevicesCalled++;
    if (refreshDeviceList.isNotEmpty) {
      deviceList = refreshDeviceList;
    }
    return super.discoverDevices(timeout: timeout, filter: filter);
  }

  @override
  bool get canListAnything => true;
}

class TestIOSDeviceDiscovery extends IOSDevices {
  TestIOSDeviceDiscovery({
    required super.platform,
    required FakeXcdevice xcdevice,
    required super.iosWorkflow,
    required super.logger,
  })  : _platform = platform,
        _xcdevice = xcdevice,
        super(xcdevice: xcdevice);

  final Platform _platform;
  List<Device> deviceList = <Device>[];
  List<Device> refreshDeviceList = <Device>[];
  int devicesCalled = 0;
  int discoverDevicesCalled = 0;
  int numberOfTimesPolled = 0;

  final FakeXcdevice _xcdevice;

  @override
  FakeXcdevice get xcdevice => _xcdevice;

  @override
  Future<List<Device>> pollingGetDevices({Duration? timeout}) async {
    numberOfTimesPolled++;
    if (!_platform.isMacOS) {
      throw UnsupportedError(
        'Control of iOS devices or simulators only supported on macOS.',
      );
    }
    return deviceList;
  }

  @override
  Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async {
    devicesCalled += 1;
    return super.devices(filter: filter);
  }

  @override
  Future<List<Device>> discoverDevices({
    Duration? timeout,
    DeviceDiscoveryFilter? filter,
  }) {
    discoverDevicesCalled++;
    if (refreshDeviceList.isNotEmpty) {
      deviceList = refreshDeviceList;
    }
    return super.discoverDevices(timeout: timeout, filter: filter);
  }

  @override
  bool get canListAnything => true;
}

class FakeXcdevice extends Fake implements XCDevice {
  XCDeviceEventNotification? waitForDeviceEvent;

  bool waitedForDeviceToConnect = false;

  @override
  Future<XCDeviceEventNotification?> waitForDeviceToConnect(String deviceId) async {
    final XCDeviceEventNotification? waitEvent = waitForDeviceEvent;
    if (waitEvent != null) {
      waitedForDeviceToConnect = true;
      return XCDeviceEventNotification(waitEvent.eventType, waitEvent.eventInterface, waitEvent.deviceIdentifier);
    } else {
      return null;
    }
  }

  @override
  void cancelWaitForDeviceToConnect() {}
}

class FakeIOSWorkflow extends Fake implements IOSWorkflow {}

// 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 FakeDevice extends Fake implements Device {
  FakeDevice({
    String? deviceId,
    String? deviceName,
    bool deviceSupported = true,
    bool deviceSupportForProject = true,
    this.ephemeral = true,
    this.isConnected = true,
    this.connectionInterface = DeviceConnectionInterface.attached,
    this.platformType = PlatformType.android,
    TargetPlatform deviceTargetPlatform = TargetPlatform.android,
  })  : id = deviceId ?? 'xxx',
        name = deviceName ?? 'test',
        _isSupported = deviceSupported,
        _isSupportedForProject = deviceSupportForProject,
        _targetPlatform = deviceTargetPlatform;

  FakeDevice.wireless({
    String? deviceId,
    String? deviceName,
    bool deviceSupported = true,
    bool deviceSupportForProject = true,
    this.ephemeral = true,
    this.isConnected = true,
    this.connectionInterface = DeviceConnectionInterface.wireless,
    this.platformType = PlatformType.android,
    TargetPlatform deviceTargetPlatform = TargetPlatform.android,
  })  : id = deviceId ?? 'xxx',
        name = deviceName ?? 'test',
        _isSupported = deviceSupported,
        _isSupportedForProject = deviceSupportForProject,
        _targetPlatform = deviceTargetPlatform;

  FakeDevice.fuchsia({
    String? deviceId,
    String? deviceName,
    bool deviceSupported = true,
    bool deviceSupportForProject = true,
    this.ephemeral = true,
    this.isConnected = true,
    this.connectionInterface = DeviceConnectionInterface.attached,
    this.platformType = PlatformType.fuchsia,
    TargetPlatform deviceTargetPlatform = TargetPlatform.fuchsia_arm64,
  })  : id = deviceId ?? 'xxx',
        name = deviceName ?? 'test',
        _isSupported = deviceSupported,
        _isSupportedForProject = deviceSupportForProject,
        _targetPlatform = deviceTargetPlatform,
        _sdkNameAndVersion = 'tester';

  final bool _isSupported;
  final bool _isSupportedForProject;
  final TargetPlatform _targetPlatform;
  String _sdkNameAndVersion = 'Android 10';

  @override
  String name;

  @override
  final bool ephemeral;

  @override
  String id;

  @override
  bool isSupported() => _isSupported;

  @override
  bool isSupportedForProject(FlutterProject project) => _isSupportedForProject;

  @override
  DeviceConnectionInterface connectionInterface;

  @override
  bool isConnected;

  @override
  Future<TargetPlatform> get targetPlatform async => _targetPlatform;

  @override
  final PlatformType? platformType;

  @override
  Future<String> get sdkNameAndVersion async => _sdkNameAndVersion;

  @override
  Future<bool> get isLocalEmulator async => false;

  @override
  Category? get category => Category.mobile;

  @override
  Future<String> get targetPlatformDisplayName async =>
      getNameForTargetPlatform(await targetPlatform);
}

// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
class FakeIOSDevice extends Fake implements IOSDevice {
  FakeIOSDevice({
    String? deviceId,
    String? deviceName,
    bool? devModeEnabled,
    bool deviceSupported = true,
    bool deviceSupportForProject = true,
    this.ephemeral = true,
    this.isConnected = true,
    this.platformType = PlatformType.ios,
    this.connectionInterface = DeviceConnectionInterface.attached,
  })  : id = deviceId ?? 'xxx',
        name = deviceName ?? 'test',
        devModeEnabled = devModeEnabled ?? true,
        _isSupported = deviceSupported,
        _isSupportedForProject = deviceSupportForProject;

  FakeIOSDevice.notConnectedWireless({
    String? deviceId,
    String? deviceName,
    bool deviceSupported = true,
    bool deviceSupportForProject = true,
    this.ephemeral = true,
    this.isConnected = false,
    this.platformType = PlatformType.ios,
    this.devModeEnabled = true,
    this.connectionInterface = DeviceConnectionInterface.wireless,
  })  : id = deviceId ?? 'xxx',
        name = deviceName ?? 'test',
        _isSupported = deviceSupported,
        _isSupportedForProject = deviceSupportForProject;

  FakeIOSDevice.connectedWireless({
    String? deviceId,
    String? deviceName,
    bool deviceSupported = true,
    bool deviceSupportForProject = true,
    this.ephemeral = true,
    this.isConnected = true,
    this.devModeEnabled = true,
    this.platformType = PlatformType.ios,
    this.connectionInterface = DeviceConnectionInterface.wireless,
  })  : id = deviceId ?? 'xxx',
        name = deviceName ?? 'test',
        _isSupported = deviceSupported,
        _isSupportedForProject = deviceSupportForProject;

  final bool _isSupported;
  final bool _isSupportedForProject;

  @override
  String name;

  @override
  final bool ephemeral;

  @override
  final bool devModeEnabled;

  @override
  String id;

  @override
  bool isSupported() => _isSupported;

  @override
  bool isSupportedForProject(FlutterProject project) => _isSupportedForProject;

  @override
  DeviceConnectionInterface connectionInterface;

  @override
  bool isConnected;

  @override
  final PlatformType? platformType;

  @override
  Future<String> get sdkNameAndVersion async => 'iOS 16';

  @override
  Future<bool> get isLocalEmulator async => false;

  @override
  Category? get category => Category.mobile;

  @override
  Future<String> get targetPlatformDisplayName async => 'ios';

  @override
  Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester;
}

class FakeTerminal extends Fake implements AnsiTerminal {
  FakeTerminal({
    this.stdinHasTerminal = true,
    this.supportsColor = false,
  });

  @override
  final bool stdinHasTerminal;

  @override
  final bool supportsColor;

  @override
  bool usesTerminalUi = true;

  @override
  bool singleCharMode = false;

  void setPrompt(List<String> characters, String result) {
    _nextPrompt = characters;
    _nextResult = result;
  }

  List<String>? _nextPrompt;
  late String _nextResult;

  @override
  Future<String> promptForCharInput(
    List<String> acceptedCharacters, {
    Logger? logger,
    String? prompt,
    int? defaultChoiceIndex,
    bool displayAcceptedCharacters = true,
  }) async {
    expect(acceptedCharacters, _nextPrompt);
    return _nextResult;
  }

  @override
  String clearLines(int numberOfLines) {
    return 'CLEAR_LINES_$numberOfLines';
  }
}

class TestBufferLogger extends BufferLogger {
  TestBufferLogger.test({
    super.terminal,
    super.outputPreferences,
    super.verbose,
  }) : super.test();

  String originalStatusText = '';

  @override
  void printStatus(
    String message, {
    bool? emphasis,
    TerminalColor? color,
    bool? newline,
    int? indent,
    int? hangingIndent,
    bool? wrap,
  }) {
    if (message.startsWith('CLEAR_LINES_')) {
      expect(statusText, equals(originalStatusText));
      final int numberOfLinesToRemove =
          int.parse(message.split('CLEAR_LINES_')[1]) - 1;
      final List<String> lines = LineSplitter.split(statusText).toList();
      // Clear string buffer and re-add lines not removed
      clear();
      for (int lineNumber = 0; lineNumber < lines.length - numberOfLinesToRemove; lineNumber++) {
        super.printStatus(lines[lineNumber]);
      }
    } else {
      super.printStatus(
        message,
        emphasis: emphasis,
        color: color,
        newline: newline,
        indent: indent,
        hangingIndent: hangingIndent,
        wrap: wrap,
      );
    }
  }
}

class FakeDoctor extends Fake implements Doctor {
  FakeDoctor({
    this.canLaunchAnything = true,
  });

  @override
  bool canLaunchAnything;
}
