in DeviceBridgeTests/Services/SubscriptionSchedulerTests.cs [307:365]
public async Task AttemptDeviceConnectionBackoff()
{
using (ShimsContext.Create())
{
// Make random(min, max) always return max so we always schedule the connection at the maximum offset.
System.Fakes.ShimRandom.AllInstances.NextInt32Int32 = (@this, min, max) => max;
// Make connection fail once so it will be rescheduled for 5 minutes.
_storageProviderMock.Setup(p => p.ListAllSubscriptionsOrderedByDeviceId(It.IsAny<Logger>())).Returns(Task.FromResult(new List<DeviceSubscription>() { }));
_storageProviderMock.Setup(p => p.ListDeviceSubscriptions(It.IsAny<Logger>(), "test-device")).Returns(Task.FromResult(new List<DeviceSubscription>() { TestUtils.GetTestSubscription("test-device", DeviceSubscriptionType.C2DMessages) }));
var subscriptionCallbackFactory = new SubscriptionCallbackFactory(LogManager.GetCurrentClassLogger(), _httpClientFactoryMock.Object);
var subscriptionScheduler = new SubscriptionScheduler(LogManager.GetCurrentClassLogger(), _connectionManagerMock.Object, _storageProviderMock.Object, subscriptionCallbackFactory, 2, 10);
await subscriptionScheduler.SynchronizeDeviceDbAndEngineDataSubscriptionsAsync("test-device", false);
_connectionManagerMock.Setup(p => p.AssertDeviceConnectionOpenAsync("test-device", false, null)).Returns(Task.FromException(new Exception("Open failed")));
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// Attempt connections forwarding the clock 4 and 5 minutes, to make sure it only attempts to connect after 5 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(4);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(5);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// Second failure should back off 10 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(9);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(10);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// Third failure should back off 15 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(14);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(15);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// Fourth failure should back off 20 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(19);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(20);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// Fifth failure should back off 25 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(24);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(25);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// Sixth failure should back off 30 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(29);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(30);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
// All subsequent failures should back off 30 minutes.
TestUtils.ShimUtcNowAheadOnceAndRevert(29);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 0, 10);
TestUtils.ShimUtcNowAheadOnceAndRevert(30);
await RunSchedulerOnceAndWaitConnectionAttempts(subscriptionScheduler, 1, 10);
}
}