IEnumerable <TestScenarioStep> GetPart2SpecificSteps(Func <object> currentMessageFunc)
            {
                yield return(TestScenarioStep.ReadMore());

                var packets = new Packet[2];

                for (int i = packets.Length - 1; i >= 0; i--)
                {
                    packets[i] = Assert.IsAssignableFrom <Packet>(currentMessageFunc());
                    if (i > 0)
                    {
                        yield return(TestScenarioStep.ReadMore());
                    }
                }

                PublishPacket qos1Packet = Assert.Single(packets.OfType <PublishPacket>());

                Assert.Equal(QualityOfService.AtLeastOnce, qos1Packet.QualityOfService);
                this.AssertPacketCoreValue(qos1Packet, Encoding.ASCII.GetString(qos1Packet.Payload.ToArray()));

                PubRelPacket pubRelQos2Packet = Assert.Single(packets.OfType <PubRelPacket>());

                yield return(TestScenarioStep.Write(
                                 false,
                                 PubAckPacket.InResponseTo(qos1Packet),
                                 PubCompPacket.InResponseTo(pubRelQos2Packet),
                                 DisconnectPacket.Instance));
            }
            IEnumerable <TestScenarioStep> GetConnectSteps(Func <object> currentMessageFunc)
            {
                yield return(TestScenarioStep.Write(new ConnectPacket
                {
                    ClientId = this.clientId,
                    HasUsername = true,
                    Username = this.iotHubName + "/" + this.clientId,
                    HasPassword = true,
                    Password = this.password,
                    KeepAliveInSeconds = 120,
                    HasWill = true,
                    WillTopicName = "last/word",
                    WillMessage = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("oops"))
                }));

                var connAckPacket = Assert.IsType <ConnAckPacket>(currentMessageFunc());

                Assert.Equal(ConnectReturnCode.Accepted, connAckPacket.ReturnCode);
            }
            IEnumerable <TestScenarioStep> GetSubscriptionSteps(Func <object> currentMessageFunc)
            {
                int subscribePacket1Id = GetRandomPacketId();
                int subscribePacket2Id = GetRandomPacketId();

                yield return(TestScenarioStep.Write(
                                 new SubscribePacket(subscribePacket1Id, new SubscriptionRequest($"devices/{this.clientId}/messages/devicebound/#", QualityOfService.ExactlyOnce)),
                                 new SubscribePacket(subscribePacket2Id, new SubscriptionRequest("multi/subscribe/check", QualityOfService.AtMostOnce))));

                var subAck1Packet = Assert.IsType <SubAckPacket>(currentMessageFunc());

                Assert.Equal(subscribePacket1Id, subAck1Packet.PacketId);
                Assert.Equal(1, subAck1Packet.ReturnCodes.Count);
                Assert.Equal(QualityOfService.ExactlyOnce, subAck1Packet.ReturnCodes[0]);

                yield return(TestScenarioStep.ReadMore());

                var subAck2Packet = Assert.IsType <SubAckPacket>(currentMessageFunc());

                Assert.Equal(subscribePacket2Id, subAck2Packet.PacketId);
                Assert.Equal(1, subAck2Packet.ReturnCodes.Count);
                Assert.Equal(QualityOfService.AtMostOnce, subAck2Packet.ReturnCodes[0]);
            }
            IEnumerable <TestScenarioStep> GetPart1SpecificSteps(Func <object> currentMessageFunc)
            {
                int publishQoS1PacketId = GetRandomPacketId();

                yield return(TestScenarioStep.Write(
                                 new PublishPacket(QualityOfService.AtMostOnce, false, false)
                {
                    //TopicName = string.Format("devices/{0}/messages/log/verbose/", clientId),
                    TopicName = $"devices/{this.clientId}/messages/events",
                    Payload = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("{\"test\": \"telemetry-QoS0\"}"))
                },
                                 new PublishPacket(QualityOfService.AtLeastOnce, false, false)
                {
                    PacketId = publishQoS1PacketId,
                    TopicName = $"devices/{this.clientId}/messages/events",
                    Payload = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("{\"test\": \"telemetry\"}"))
                }));

                var packets = new Packet[5];

                for (int i = packets.Length - 1; i >= 0; i--)
                {
                    packets[i] = Assert.IsAssignableFrom <Packet>(currentMessageFunc());
                    if (i > 0)
                    {
                        yield return(TestScenarioStep.ReadMore());
                    }
                }

                PubAckPacket pubAckPacket = Assert.Single(packets.OfType <PubAckPacket>());

                Assert.Equal(publishQoS1PacketId, pubAckPacket.PacketId);

                PublishPacket publishQoS0Packet = Assert.Single(packets.OfType <PublishPacket>().Where(x => x.QualityOfService == QualityOfService.AtMostOnce));

                this.AssertPacketCoreValue(publishQoS0Packet, NotificationQoS0Content);

                PublishPacket publishQoS1Packet = Assert.Single(packets.OfType <PublishPacket>().Where(x => x.QualityOfService == QualityOfService.AtLeastOnce));

                this.AssertPacketCoreValue(publishQoS1Packet, NotificationQoS1Content);

                PublishPacket[] publishQoS2Packets = packets.OfType <PublishPacket>().Where(x => x.QualityOfService == QualityOfService.ExactlyOnce).Reverse().ToArray();
                Assert.Equal(2, publishQoS2Packets.Length);
                PublishPacket publishQoS2Packet1 = publishQoS2Packets[0];

                this.AssertPacketCoreValue(publishQoS2Packet1, NotificationQoS2Content);
                PublishPacket publishQoS2Packet2 = publishQoS2Packets[1];

                this.AssertPacketCoreValue(publishQoS2Packet2, NotificationQoS2Content2);

                yield return(TestScenarioStep.Write(
                                 PubAckPacket.InResponseTo(publishQoS1Packet),
                                 PubRecPacket.InResponseTo(publishQoS2Packet1),
                                 PubRecPacket.InResponseTo(publishQoS2Packet2)));

                var pubRelQoS2Packet1 = Assert.IsAssignableFrom <PubRelPacket>(currentMessageFunc());

                Assert.Equal(publishQoS2Packet1.PacketId, pubRelQoS2Packet1.PacketId);

                yield return(TestScenarioStep.ReadMore());

                var pubRelQoS2Packet2 = Assert.IsAssignableFrom <PubRelPacket>(currentMessageFunc());

                Assert.Equal(publishQoS2Packet2.PacketId, pubRelQoS2Packet2.PacketId);

                yield return(TestScenarioStep.Write(
                                 false, // it is a final step and we do not expect response
                                 PubCompPacket.InResponseTo(pubRelQoS2Packet1),
                                 DisconnectPacket.Instance));

                // device queue still contains QoS 2 packet 2 which was PUBRECed but not PUBCOMPed.
            }
        static IEnumerable <TestScenarioStep> GetClientScenario(Func <object> currentMessageFunc, string iotHubName, string clientId, string password)
        {
            yield return(TestScenarioStep.Write(new ConnectPacket
            {
                ClientId = clientId,
                HasUsername = true,
                Username = iotHubName + "/" + clientId,
                HasPassword = true,
                Password = password,
                KeepAliveInSeconds = 120,
                HasWill = true,
                WillTopicName = "last/word",
                WillMessage = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("oops"))
            }));

            var connAckPacket = Assert.IsType <ConnAckPacket>(currentMessageFunc());

            Assert.Equal(ConnectReturnCode.Accepted, connAckPacket.ReturnCode);

            int subscribePacketId = GetRandomPacketId();

            yield return(TestScenarioStep.Write(new SubscribePacket
            {
                PacketId = subscribePacketId,
                Requests = new[]
                {
                    new SubscriptionRequest(string.Format("devices/{0}/messages/devicebound/#", clientId), QualityOfService.ExactlyOnce)
                }
            }));

            var subAckPacket = Assert.IsType <SubAckPacket>(currentMessageFunc());

            Assert.Equal(subscribePacketId, subAckPacket.PacketId);
            Assert.Equal(1, subAckPacket.ReturnCodes.Count);
            Assert.Equal(QualityOfService.ExactlyOnce, subAckPacket.ReturnCodes[0]);

            int publishQoS1PacketId = GetRandomPacketId();

            yield return(TestScenarioStep.Write(
                             new PublishPacket(QualityOfService.AtMostOnce, false, false)
            {
                //TopicName = string.Format("devices/{0}/messages/log/verbose/", clientId),
                TopicName = string.Format("devices/{0}/messages/events", clientId),
                Payload = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("{\"test\": \"telemetry-QoS0\"}"))
            },
                             new PublishPacket(QualityOfService.AtLeastOnce, false, false)
            {
                PacketId = publishQoS1PacketId,
                TopicName = string.Format("devices/{0}/messages/events", clientId),
                Payload = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("{\"test\": \"telemetry\"}"))
            }));

            var packets = new Packet[4];

            for (int i = packets.Length - 1; i >= 0; i--)
            {
                packets[i] = Assert.IsAssignableFrom <Packet>(currentMessageFunc());
                if (i > 0)
                {
                    yield return(TestScenarioStep.ReadMore());
                }
            }

            PubAckPacket pubAckPacket = Assert.Single(packets.OfType <PubAckPacket>());

            Assert.Equal(publishQoS1PacketId, pubAckPacket.PacketId);

            PublishPacket publishQoS0Packet = Assert.Single(packets.OfType <PublishPacket>().Where(x => x.QualityOfService == QualityOfService.AtMostOnce));

            //Assert.Equal(string.Format("devices/{0}/messages/devicebound/tips", clientId), publishQoS0Packet.TopicName);
            Assert.Equal(string.Format("devices/{0}/messages/devicebound", clientId), publishQoS0Packet.TopicName);
            Assert.Equal(NotificationQoS0Content, Encoding.UTF8.GetString(publishQoS0Packet.Payload.ToArray()));

            PublishPacket publishQoS1Packet = Assert.Single(packets.OfType <PublishPacket>().Where(x => x.QualityOfService == QualityOfService.AtLeastOnce));

            //Assert.Equal(string.Format("devices/{0}/messages/devicebound/firmware-update", clientId), publishQoS1Packet.TopicName);
            Assert.Equal(string.Format("devices/{0}/messages/devicebound", clientId), publishQoS1Packet.TopicName);
            Assert.Equal(NotificationQoS1Content, Encoding.UTF8.GetString(publishQoS1Packet.Payload.ToArray()));

            PublishPacket publishQoS2Packet = Assert.Single(packets.OfType <PublishPacket>().Where(x => x.QualityOfService == QualityOfService.ExactlyOnce));

            //Assert.Equal(string.Format("devices/{0}/messages/devicebound/critical-alert", clientId), publishQoS2Packet.TopicName);
            Assert.Equal(string.Format("devices/{0}/messages/devicebound", clientId), publishQoS2Packet.TopicName);
            Assert.Equal(NotificationQoS2Content, Encoding.UTF8.GetString(publishQoS2Packet.Payload.ToArray()));

            yield return(TestScenarioStep.Write(
                             PubAckPacket.InResponseTo(publishQoS1Packet),
                             PubRecPacket.InResponseTo(publishQoS2Packet)));

            var pubRelQoS2Packet = Assert.IsAssignableFrom <PubRelPacket>(currentMessageFunc());

            Assert.Equal(publishQoS2Packet.PacketId, pubRelQoS2Packet.PacketId);

            yield return(TestScenarioStep.Write(
                             false, // it is a final step and we do not expect response
                             PubCompPacket.InResponseTo(pubRelQoS2Packet),
                             DisconnectPacket.Instance));
        }