private async Task ReuseAuthenticationMethod_SingleDevice(Client.TransportType transport)
        {
            TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false);

            var authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: false);

            // Create an instance of the device client, send a test message and then close and dispose it.
            DeviceClient deviceClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport);

            using var message1 = new Client.Message();
            await deviceClient.SendEventAsync(message1).ConfigureAwait(false);

            await deviceClient.CloseAsync();

            deviceClient.Dispose();
            Logger.Trace("Test with instance 1 completed");

            // Perform the same steps again, reusing the previously created authentication method instance to ensure
            // that the sdk did not dispose the user supplied authentication method instance.
            DeviceClient deviceClient2 = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport);

            using var message2 = new Client.Message();
            await deviceClient2.SendEventAsync(message2).ConfigureAwait(false);

            await deviceClient2.CloseAsync();

            deviceClient2.Dispose();
            Logger.Trace("Test with instance 2 completed, reused the previously created authentication method instance for the device client.");

            authenticationMethod.Dispose();
        }
        private async Task AuthenticationMethodDisposesTokenRefresher(Client.TransportType transport)
        {
            TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false);

            var authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: true);

            // Create an instance of the device client, send a test message and then close and dispose it.
            DeviceClient deviceClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport);

            using var message1 = new Client.Message();
            await deviceClient.SendEventAsync(message1).ConfigureAwait(false);

            await deviceClient.CloseAsync();

            deviceClient.Dispose();
            Logger.Trace("Test with instance 1 completed");

            // Perform the same steps again, reusing the previously created authentication method instance.
            // Since the default behavior is to dispose AuthenticationWithTokenRefresh authentication method on DeviceClient disposal,
            // this should now throw an ObjectDisposedException.
            DeviceClient deviceClient2 = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport);

            using var message2 = new Client.Message();

            Func <Task> act = async() => await deviceClient2.SendEventAsync(message2).ConfigureAwait(false);

            await act.Should().ThrowAsync <ObjectDisposedException>();
        }
        private async Task ReuseAuthenticationMethod_MuxedDevices(Client.TransportType transport, int devicesCount)
        {
            IList <TestDevice>   testDevices   = new List <TestDevice>();
            IList <DeviceClient> deviceClients = new List <DeviceClient>();
            IList <AuthenticationWithTokenRefresh> authenticationMethods  = new List <AuthenticationWithTokenRefresh>();
            IList <AmqpConnectionStatusChange>     amqpConnectionStatuses = new List <AmqpConnectionStatusChange>();

            // Set up amqp transport settings to multiplex all device sessions over the same amqp connection.
            var amqpTransportSettings = new AmqpTransportSettings(transport)
            {
                AmqpConnectionPoolSettings = new AmqpConnectionPoolSettings
                {
                    Pooling     = true,
                    MaxPoolSize = 1,
                },
            };

            for (int i = 0; i < devicesCount; i++)
            {
                TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false);

#pragma warning disable CA2000 // Dispose objects before losing scope - the authentication method is disposed at the end of the test.
                var authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: false);
#pragma warning restore CA2000 // Dispose objects before losing scope

                testDevices.Add(testDevice);
                authenticationMethods.Add(authenticationMethod);
            }

            // Initialize the client instances, set the connection status change handler and open the connection.
            for (int i = 0; i < devicesCount; i++)
            {
#pragma warning disable CA2000 // Dispose objects before losing scope - the client instance is disposed during the course of the test.
                DeviceClient deviceClient = DeviceClient.Create(testDevices[i].IoTHubHostName, authenticationMethods[i], new ITransportSettings[] { amqpTransportSettings });
#pragma warning restore CA2000 // Dispose objects before losing scope

                var amqpConnectionStatusChange = new AmqpConnectionStatusChange(testDevices[i].Id, Logger);
                deviceClient.SetConnectionStatusChangesHandler(amqpConnectionStatusChange.ConnectionStatusChangesHandler);
                amqpConnectionStatuses.Add(amqpConnectionStatusChange);

                await deviceClient.OpenAsync().ConfigureAwait(false);

                deviceClients.Add(deviceClient);
            }

            // Close and dispose client instance 1.
            // The closed client should report a status of "disabled" while the rest of them should be connected.
            // This is to ensure that disposal on one multiplexed device doesn't cause cascading failures
            // in the rest of the devices on the same tcp connection.

            await deviceClients[0].CloseAsync().ConfigureAwait(false);
            deviceClients[0].Dispose();

            amqpConnectionStatuses[0].LastConnectionStatus.Should().Be(ConnectionStatus.Disabled);

            Logger.Trace($"{nameof(ReuseAuthenticationMethod_MuxedDevices)}: Confirming the rest of the multiplexed devices are online and operational.");

            bool notRecovered = true;
            var  sw           = Stopwatch.StartNew();
            while (notRecovered && sw.Elapsed < MaxWaitTime)
            {
                notRecovered = false;
                for (int i = 1; i < devicesCount; i++)
                {
                    if (amqpConnectionStatuses[i].LastConnectionStatus != ConnectionStatus.Connected)
                    {
                        await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);

                        notRecovered = true;
                        break;
                    }
                }
            }

            notRecovered.Should().BeFalse();

            // Send a message through the rest of the multiplexed client instances.
            var message = new Client.Message();
            for (int i = 1; i < devicesCount; i++)
            {
                await deviceClients[i].SendEventAsync(message).ConfigureAwait(false);
                Logger.Trace($"Test with client {i} completed.");
            }
            message.Dispose();

            // Close and dispose all of the client instances.
            for (int i = 1; i < devicesCount; i++)
            {
                await deviceClients[i].CloseAsync().ConfigureAwait(false);
                deviceClients[i].Dispose();
            }

            deviceClients.Clear();
            amqpConnectionStatuses.Clear();

            // Initialize the client instances by reusing the created authentication methods and open the connection.
            for (int i = 0; i < devicesCount; i++)
            {
#pragma warning disable CA2000 // Dispose objects before losing scope - the client instance is disposed at the end of the test.
                DeviceClient deviceClient = DeviceClient.Create(testDevices[i].IoTHubHostName, authenticationMethods[i], new ITransportSettings[] { amqpTransportSettings });
#pragma warning restore CA2000 // Dispose objects before losing scope

                var amqpConnectionStatusChange = new AmqpConnectionStatusChange(testDevices[i].Id, Logger);
                deviceClient.SetConnectionStatusChangesHandler(amqpConnectionStatusChange.ConnectionStatusChangesHandler);
                amqpConnectionStatuses.Add(amqpConnectionStatusChange);

                await deviceClient.OpenAsync().ConfigureAwait(false);

                deviceClients.Add(deviceClient);
            }

            // Ensure that all clients are connected successfully, and the close and dispose the instances.
            // Also dispose the authentication methods created.
            for (int i = 0; i < devicesCount; i++)
            {
                amqpConnectionStatuses[i].LastConnectionStatus.Should().Be(ConnectionStatus.Connected);

                await deviceClients[i].CloseAsync();
                deviceClients[i].Dispose();
                authenticationMethods[i].Dispose();

                amqpConnectionStatuses[i].LastConnectionStatus.Should().Be(ConnectionStatus.Disabled);
            }
        }