private async Task Twin_DeviceSetsReportedPropertyAndGetsItBackPoolOverAmqp(
            TestDeviceType type,
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _)
            {
                Logger.Trace($"{nameof(TwinE2EPoolAmqpTests)}: Setting reported propery and verifying twin for device {testDevice.Id}");
                await TwinE2ETests.Twin_DeviceSetsReportedPropertyAndGetsItBackAsync(deviceClient, testDevice.Id, Guid.NewGuid().ToString(), Logger).ConfigureAwait(false);
            }

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                _devicePrefix,
                transport,
                poolSize,
                devicesCount,
                null,
                TestOperationAsync,
                null,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }
        private async Task SendMessagePoolOverAmqp(
            TestDeviceType type,
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _)
            {
                Logger.Trace($"{nameof(MessageSendE2EPoolAmqpTests)}: Preparing to send message for device {testDevice.Id}");
                await deviceClient.OpenAsync().ConfigureAwait(false);

                (Client.Message testMessage, string payload, string p1Value) = MessageSendE2ETests.ComposeD2cTestMessage(Logger);
                Logger.Trace($"{nameof(MessageSendE2EPoolAmqpTests)}.{testDevice.Id}: messageId='{testMessage.MessageId}' payload='{payload}' p1Value='{p1Value}'");
                await deviceClient.SendEventAsync(testMessage).ConfigureAwait(false);
            }

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                _devicePrefix,
                transport,
                poolSize,
                devicesCount,
                null,
                TestOperationAsync,
                null,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }
        private async Task ServiceSetsDesiredPropertyAndDeviceReceivesEventPoolOverAmqp(
            TestDeviceType type,
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            Func <DeviceClient, string, string, MsTestLogger, Task <Task> > setTwinPropertyUpdateCallbackAsync,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            var twinPropertyMap = new Dictionary <string, List <string> >();

            async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _)
            {
                string propName  = Guid.NewGuid().ToString();
                string propValue = Guid.NewGuid().ToString();

                twinPropertyMap.Add(testDevice.Id, new List <string> {
                    propName, propValue
                });

                Logger.Trace($"{nameof(TwinE2EPoolAmqpTests)}: Setting desired propery callback for device {testDevice.Id}");
                Logger.Trace($"{nameof(ServiceSetsDesiredPropertyAndDeviceReceivesEventPoolOverAmqp)}: name={propName}, value={propValue}");
                Task updateReceivedTask = await setTwinPropertyUpdateCallbackAsync(deviceClient, propName, propValue, Logger).ConfigureAwait(false);
            }

            async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _)
            {
                Logger.Trace($"{nameof(TwinE2EPoolAmqpTests)}: Updating the desired properties for device {testDevice.Id}");
                List <string> twinProperties = twinPropertyMap[testDevice.Id];
                string        propName       = twinProperties[0];
                string        propValue      = twinProperties[1];

                await TwinE2ETests.RegistryManagerUpdateDesiredPropertyAsync(testDevice.Id, propName, propValue).ConfigureAwait(false);
            }

            Task CleanupOperationAsync()
            {
                twinPropertyMap.Clear();
                return(Task.FromResult(0));
            }

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                _devicePrefix,
                transport,
                poolSize,
                devicesCount,
                InitOperationAsync,
                TestOperationAsync,
                CleanupOperationAsync,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }
        private async Task ReceiveMessagePoolOverAmqpAsync(
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            var messagesSent = new Dictionary <string, Tuple <Message, string> >();

            // Initialize the service client
            var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);

            async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _)
            {
                (Message msg, string payload, string p1Value) = MessageReceiveE2ETests.ComposeC2dTestMessage(Logger);
                messagesSent.Add(testDevice.Id, Tuple.Create(msg, payload));

                await serviceClient.SendAsync(testDevice.Id, msg).ConfigureAwait(false);
            }

            async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _)
            {
                Logger.Trace($"{nameof(MessageReceiveE2EPoolAmqpTests)}: Preparing to receive message for device {testDevice.Id}");
                await deviceClient.OpenAsync().ConfigureAwait(false);

                Tuple <Message, string> msgSent = messagesSent[testDevice.Id];
                await MessageReceiveE2ETests.VerifyReceivedC2DMessageAsync(transport, deviceClient, testDevice.Id, msgSent.Item1, msgSent.Item2, Logger).ConfigureAwait(false);
            }

            async Task CleanupOperationAsync()
            {
                await serviceClient.CloseAsync().ConfigureAwait(false);

                serviceClient.Dispose();
                messagesSent.Clear();
            }

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                DevicePrefix,
                transport,
                poolSize,
                devicesCount,
                InitOperationAsync,
                TestOperationAsync,
                CleanupOperationAsync,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }
        private async Task ReceiveMessageUsingCallbackPoolOverAmqpAsync(
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            // Initialize the service client
            var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);

            async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler)
            {
                (Message msg, string payload, string p1Value) = MessageReceiveE2ETests.ComposeC2dTestMessage(Logger);

                await testDeviceCallbackHandler.SetMessageReceiveCallbackHandlerAsync().ConfigureAwait(false);

                testDeviceCallbackHandler.ExpectedMessageSentByService = msg;

                await serviceClient.SendAsync(testDevice.Id, msg).ConfigureAwait(false);
            }

            async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler)
            {
                Logger.Trace($"{nameof(MessageReceiveE2EPoolAmqpTests)}: Preparing to receive message for device {testDevice.Id}");

                using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
                await testDeviceCallbackHandler.WaitForReceiveMessageCallbackAsync(cts.Token).ConfigureAwait(false);
            }

            async Task CleanupOperationAsync()
            {
                await serviceClient.CloseAsync().ConfigureAwait(false);

                serviceClient.Dispose();
            }

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                DevicePrefix,
                transport,
                poolSize,
                devicesCount,
                InitOperationAsync,
                TestOperationAsync,
                CleanupOperationAsync,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }
        private async Task SendMethodAndRespondPoolOverAmqp(
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            Func <DeviceClient, string, MsTestLogger, Task <Task> > setDeviceReceiveMethod,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            Func <DeviceClient, TestDevice, Task> initOperation = async(deviceClient, testDevice) =>
            {
                Logger.Trace($"{nameof(MethodE2EPoolAmqpTests)}: Setting method for device {testDevice.Id}");
                Task methodReceivedTask = await setDeviceReceiveMethod(deviceClient, MethodName, Logger).ConfigureAwait(false);
            };

            Func <DeviceClient, TestDevice, Task> testOperation = async(deviceClient, testDevice) =>
            {
                Logger.Trace($"{nameof(MethodE2EPoolAmqpTests)}: Preparing to receive method for device {testDevice.Id}");
                await MethodE2ETests
                .ServiceSendMethodAndVerifyResponseAsync(
                    testDevice.Id,
                    MethodName,
                    MethodE2ETests.DeviceResponseJson,
                    MethodE2ETests.ServiceRequestJson,
                    Logger)
                .ConfigureAwait(false);
            };

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                _devicePrefix,
                transport,
                poolSize,
                devicesCount,
                initOperation,
                testOperation,
                null,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }
        private async Task ReceiveMessageUsingCallbackAndUnsubscribePoolOverAmqpAsync(
            Client.TransportType transport,
            int poolSize,
            int devicesCount,
            ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device)
        {
            // Initialize the service client
            var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);

            async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler)
            {
                await testDeviceCallbackHandler.SetMessageReceiveCallbackHandlerAsync().ConfigureAwait(false);
            }

            async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler)
            {
                var timeout = TimeSpan.FromSeconds(20);

                using var cts = new CancellationTokenSource(timeout);

                // Send a message to the device from the service.
                (Message firstMessage, string payload, _) = MessageReceiveE2ETests.ComposeC2dTestMessage(Logger);
                testDeviceCallbackHandler.ExpectedMessageSentByService = firstMessage;
                await serviceClient.SendAsync(testDevice.Id, firstMessage).ConfigureAwait(false);

                Logger.Trace($"Sent 1st C2D message from service - to be received on callback: deviceId={testDevice.Id}, messageId={firstMessage.MessageId}");

                // The message should be received on the callback, while a call to ReceiveAsync() should return null.
                Client.Message receivedMessage = await deviceClient.ReceiveAsync(timeout).ConfigureAwait(false);

                await testDeviceCallbackHandler.WaitForReceiveMessageCallbackAsync(cts.Token).ConfigureAwait(false);

                receivedMessage.Should().BeNull();

                // Now unsubscribe from receiving c2d messages over the callback.
                await deviceClient.SetReceiveMessageHandlerAsync(null, deviceClient).ConfigureAwait(false);

                // Send a message to the device from the service.
                (Message secondMessage, _, _) = MessageReceiveE2ETests.ComposeC2dTestMessage(Logger);
                await serviceClient.SendAsync(testDevice.Id, secondMessage).ConfigureAwait(false);

                Logger.Trace($"Sent 2nd C2D message from service - to be received on polling ReceiveAsync(): deviceId={testDevice.Id}, messageId={secondMessage.MessageId}");

                // This time, the message should not be received on the callback, rather it should be received on a call to ReceiveAsync().
                Func <Task> receiveMessageOverCallback = async() =>
                {
                    await testDeviceCallbackHandler.WaitForReceiveMessageCallbackAsync(cts.Token).ConfigureAwait(false);
                };

                Client.Message message = await deviceClient.ReceiveAsync(timeout).ConfigureAwait(false);

                message.MessageId.Should().Be(secondMessage.MessageId);
                receiveMessageOverCallback.Should().Throw <OperationCanceledException>();
            }

            async Task CleanupOperationAsync()
            {
                await serviceClient.CloseAsync().ConfigureAwait(false);

                serviceClient.Dispose();
            }

            await PoolingOverAmqp
            .TestPoolAmqpAsync(
                DevicePrefix,
                transport,
                poolSize,
                devicesCount,
                InitOperationAsync,
                TestOperationAsync,
                CleanupOperationAsync,
                authScope,
                true,
                Logger)
            .ConfigureAwait(false);
        }