// [InlineData(200, 500, 100, 100, 1)] public async Task When_Multiple_Devices_And_Conflicting_DevAddr_Send_Telemetry_Queue_Sends_Messages_To_IoTHub( int searchDelay, int getTwinDelay, int sendMessageDelay, int receiveDelay, int delayBetweenMessages) { const int messagePerDeviceCount = 10; const int payloadInitialFcnt = 2; var device1 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1)); var device1Twin = TestUtils.CreateABPTwin(device1); var device2 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(2)) { DevAddr = device1.DevAddr }; var device2Twin = TestUtils.CreateABPTwin(device2); var device3 = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(3)); device3.SetupJoin(TestKeys.CreateAppSessionKey(0x88), TestKeys.CreateNetworkSessionKey(0x88), new DevAddr(0x02000088)); var device3Twin = TestUtils.CreateOTAATwin(device3); var device4 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(4)); var device4Twin = TestUtils.CreateABPTwin(device4); var device1And2Result = new IoTHubDeviceInfo[] { new IoTHubDeviceInfo(device1.DevAddr, device1.DevEUI, "1"), new IoTHubDeviceInfo(device2.DevAddr, device2.DevEUI, "2"), }; LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(device1.DevAddr.Value)) .ReturnsAsync(new SearchDevicesResult(device1And2Result), TimeSpan.FromMilliseconds(searchDelay)); LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(device3.DevAddr.Value)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(device3.DevAddr, device3.DevEUI, "3").AsList()), TimeSpan.FromMilliseconds(searchDelay)); LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(device4.DevAddr.Value)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(device4.DevAddr, device4.DevEUI, "3").AsList()), TimeSpan.FromMilliseconds(searchDelay)); var deviceClient1 = new Mock <ILoRaDeviceClient>(); var deviceClient1Telemetry = new List <LoRaDeviceTelemetry>(); deviceClient1.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(device1Twin, TimeSpan.FromMilliseconds(getTwinDelay)); deviceClient1.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, d) => deviceClient1Telemetry.Add(t)) .ReturnsAsync(true, TimeSpan.FromMilliseconds(sendMessageDelay)); deviceClient1.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync(null, TimeSpan.FromMilliseconds(receiveDelay)); deviceClient1.Setup(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>())).ReturnsAsync(true); var deviceClient2 = new Mock <ILoRaDeviceClient>(); var deviceClient2Telemetry = new List <LoRaDeviceTelemetry>(); deviceClient2.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(device2Twin, TimeSpan.FromMilliseconds(getTwinDelay)); deviceClient2.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, d) => deviceClient2Telemetry.Add(t)) .ReturnsAsync(true, TimeSpan.FromMilliseconds(sendMessageDelay)); deviceClient2.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync(null, TimeSpan.FromMilliseconds(receiveDelay)); deviceClient2.Setup(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>())).ReturnsAsync(true); var deviceClient3 = new Mock <ILoRaDeviceClient>(); var deviceClient3Telemetry = new List <LoRaDeviceTelemetry>(); deviceClient3.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(device3Twin, TimeSpan.FromMilliseconds(getTwinDelay)); deviceClient3.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, d) => deviceClient3Telemetry.Add(t)) .ReturnsAsync(true, TimeSpan.FromMilliseconds(sendMessageDelay)); deviceClient3.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync(null, TimeSpan.FromMilliseconds(receiveDelay)); deviceClient3.Setup(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>())).ReturnsAsync(true); var deviceClient4 = new Mock <ILoRaDeviceClient>(); var deviceClient4Telemetry = new List <LoRaDeviceTelemetry>(); deviceClient4.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(device4Twin, TimeSpan.FromMilliseconds(getTwinDelay)); deviceClient4.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, d) => deviceClient4Telemetry.Add(t)) .ReturnsAsync(true, TimeSpan.FromMilliseconds(sendMessageDelay)); deviceClient4.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync(null, TimeSpan.FromMilliseconds(receiveDelay)); deviceClient4.Setup(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>())).ReturnsAsync(true); LoRaDeviceFactory.SetClient(device1.DevEUI, deviceClient1.Object); LoRaDeviceFactory.SetClient(device2.DevEUI, deviceClient2.Object); LoRaDeviceFactory.SetClient(device3.DevEUI, deviceClient3.Object); LoRaDeviceFactory.SetClient(device4.DevEUI, deviceClient4.Object); using var cache = NewMemoryCache(); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); using var messageDispatcher = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); var device1Messages = await SendMessages(device1, messageDispatcher, payloadInitialFcnt, delayBetweenMessages, messagePerDeviceCount); var device2Messages = await SendMessages(device2, messageDispatcher, payloadInitialFcnt, delayBetweenMessages, messagePerDeviceCount); var device3Messages = await SendMessages(device3, messageDispatcher, payloadInitialFcnt, delayBetweenMessages, messagePerDeviceCount); var device4Messages = await SendMessages(device4, messageDispatcher, payloadInitialFcnt, delayBetweenMessages, messagePerDeviceCount); var allMessages = device1Messages .Concat(device2Messages) .Concat(device3Messages) .Concat(device4Messages) .ToList(); Assert.Equal(messagePerDeviceCount * 4, allMessages.Count); await Task.WhenAll(allMessages.Select(x => x.WaitCompleteAsync())); Assert.All(allMessages, m => Assert.True(m.ProcessingSucceeded)); var telemetries = new[] { deviceClient1Telemetry, deviceClient2Telemetry, deviceClient3Telemetry, deviceClient4Telemetry }; foreach (var telemetry in telemetries) { Assert.Equal(messagePerDeviceCount, telemetry.Count); for (var i = 0; i < messagePerDeviceCount; ++i) { Assert.Equal(payloadInitialFcnt + i, telemetry[i].Fcnt); } } deviceClient1.VerifyAll(); deviceClient1.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Exactly(messagePerDeviceCount)); deviceClient2.VerifyAll(); deviceClient2.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Exactly(messagePerDeviceCount)); deviceClient3.VerifyAll(); deviceClient3.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Exactly(messagePerDeviceCount)); deviceClient4.VerifyAll(); deviceClient4.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Exactly(messagePerDeviceCount)); LoRaDeviceApi.VerifyAll(); }
public async Task ABP_Load_And_Receiving_Multiple_Unconfirmed_Should_Send_All_ToHub(ParallelTestConfiguration parallelTestConfiguration) { Console.WriteLine("---"); var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(parallelTestConfiguration.DeviceID ?? 1, gatewayID: null)); var devEui = simulatedDevice.LoRaDevice.DevEui; var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value; // Using loose mock because sometimes we might call receive async var looseDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Loose); LoRaDeviceFactory.SetClient(devEui, looseDeviceClient.Object); looseDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); // message will be sent var sentTelemetry = new List <LoRaDeviceTelemetry>(); looseDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Returns <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => { sentTelemetry.Add(t); var duration = parallelTestConfiguration.SendEventDuration.Next(); Console.WriteLine($"{nameof(looseDeviceClient.Object.SendEventAsync)} sleeping for {duration}"); return(Task.Delay(duration) .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)); }); // twin will be loaded var initialTwin = new Twin(); initialTwin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString(); initialTwin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString(); initialTwin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString(); initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey?.ToString(); initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey?.ToString(); initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr.ToString(); if (parallelTestConfiguration.GatewayID != null) { initialTwin.Properties.Desired[TwinProperty.GatewayID] = parallelTestConfiguration.GatewayID; } initialTwin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; if (parallelTestConfiguration.DeviceTwinFcntDown.HasValue) { initialTwin.Properties.Reported[TwinProperty.FCntDown] = parallelTestConfiguration.DeviceTwinFcntDown.Value; } if (parallelTestConfiguration.DeviceTwinFcntUp.HasValue) { initialTwin.Properties.Reported[TwinProperty.FCntUp] = parallelTestConfiguration.DeviceTwinFcntUp.Value; } looseDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .Returns(() => { var duration = parallelTestConfiguration.LoadTwinDuration.Next(); Console.WriteLine($"{nameof(looseDeviceClient.Object.GetTwinAsync)} sleeping for {duration}"); return(Task.Delay(duration) .ContinueWith(_ => initialTwin, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)); }); // twin will be updated with new fcnt var expectedToSaveTwin = parallelTestConfiguration.DeviceTwinFcntDown > 0 || parallelTestConfiguration.DeviceTwinFcntUp > 0; uint?fcntUpSavedInTwin = null; uint?fcntDownSavedInTwin = null; if (expectedToSaveTwin) { looseDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Returns <TwinCollection, CancellationToken>((t, _) => { fcntUpSavedInTwin = (uint)t[TwinProperty.FCntUp]; fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown]; var duration = parallelTestConfiguration.UpdateTwinDuration.Next(); Console.WriteLine($"{nameof(looseDeviceClient.Object.UpdateReportedPropertiesAsync)} sleeping for {duration}"); return(Task.Delay(duration, CancellationToken.None) .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)); }); } if (expectedToSaveTwin && string.IsNullOrEmpty(parallelTestConfiguration.GatewayID)) { LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEui, It.IsAny <uint>(), It.IsNotNull <string>())) .Returns(() => { var duration = parallelTestConfiguration.DeviceApiResetFcntDuration.Next(); Console.WriteLine($"{nameof(LoRaDeviceApi.Object.ABPFcntCacheResetAsync)} sleeping for {duration}"); return(Task.Delay(duration) .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)); }); } // device api will be searched for payload LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr)) .Returns(() => { var duration = parallelTestConfiguration.SearchByDevAddrDuration.Next(); Console.WriteLine($"{nameof(LoRaDeviceApi.Object.SearchByDevAddrAsync)} sleeping for {duration}"); return(Task.Delay(duration) .ContinueWith((a) => new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "abc").AsList()), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)); }); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); // Send to message processor using var messageDispatcher = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // sends unconfirmed message var unconfirmedMessage1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 1); var unconfirmedMessage2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 2); var unconfirmedMessage3 = simulatedDevice.CreateUnconfirmedDataUpMessage("3", fcnt: 3); var req1 = CreateWaitableRequest(unconfirmedMessage1, this.downstreamMessageSender); messageDispatcher.DispatchRequest(req1); await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next()); var req2 = CreateWaitableRequest(unconfirmedMessage2, this.downstreamMessageSender); messageDispatcher.DispatchRequest(req2); await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next()); var req3 = CreateWaitableRequest(unconfirmedMessage3, this.downstreamMessageSender); messageDispatcher.DispatchRequest(req3); await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next()); await Task.WhenAll(req1.WaitCompleteAsync(20000), req2.WaitCompleteAsync(20000), req3.WaitCompleteAsync(20000)); var allRequests = new[] { req1, req2, req3 }; Assert.All(allRequests, x => Assert.Null(x.ResponseDownlink)); Assert.All(allRequests, x => Assert.True(x.ProcessingSucceeded)); looseDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Exactly(1)); if (expectedToSaveTwin) { looseDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Exactly(1)); } LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(devAddr), Times.Once); // Ensure that all telemetry was sent Assert.Equal(3, sentTelemetry.Count); // Ensure data was sent in order Assert.Equal(1, sentTelemetry[0].Fcnt); Assert.Equal(2, sentTelemetry[1].Fcnt); Assert.Equal(3, sentTelemetry[2].Fcnt); // Ensure that the device twins were saved if (expectedToSaveTwin) { Assert.NotNull(fcntDownSavedInTwin); Assert.NotNull(fcntUpSavedInTwin); Assert.Equal(0U, fcntDownSavedInTwin.Value); Assert.Equal(0U, fcntUpSavedInTwin.Value); } // verify that the device in device registry has correct properties and frame counters Assert.True(DeviceCache.TryGetForPayload(req1.Payload, out var loRaDevice)); Assert.Equal(devAddr, loRaDevice.DevAddr); Assert.Equal(devEui, loRaDevice.DevEUI); Assert.True(loRaDevice.IsABP); Assert.Equal(3U, loRaDevice.FCntUp); Assert.Equal(0U, loRaDevice.FCntDown); Assert.True(loRaDevice.HasFrameCountChanges); // should have changes! // looseDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }