public void Construct_WithClockAndEpoch_ShouldUseThose() { var factory = new FluidIdGenerator(isProduction: false, Clock, ApplicationInstanceId, epoch: Epoch, BitDistribution); var fluid = factory.CreateFluid(); Assert.Equal(GetMilliseconds(ClockValue, factory.Epoch), GetMilliseconds(fluid)); }
public void Construct_WithApplicationInstanceId_ShouldUseThatValue() { var factory = new FluidIdGenerator(isProduction: false, FluidIdGenerator.GetUtcNow, applicationInstanceId: ApplicationInstanceId, bitDistribution: BitDistribution); var fluid = factory.CreateFluid(); Assert.Equal(ApplicationInstanceId, GetApplicationInstanceId(fluid)); }
public void GetUtcNow_WhenInvoked_ShouldReturnUtcNow() { var getUtcNowResult = FluidIdGenerator.GetUtcNow(); var utcNow = DateTime.UtcNow; Assert.True((utcNow - getUtcNowResult) < TimeSpan.FromMilliseconds(1000)); }
public void CreateFluid_TheFirstTime_ShouldUseCounterValue0() { var factory = new FluidIdGenerator(isProduction: false, Clock, ApplicationInstanceId, epoch: Epoch, BitDistribution); var fluid = factory.CreateFluid(); Assert.Equal(0UL, GetCounterValue(fluid)); }
public void GetMillisecondsSinceEpoch_Regularly_ShouldReturnExpectedValue() { var factory = new FluidIdGenerator(isProduction: false, utcClock: () => new DateTime(2020, 01, 01, 0, 0, 0, millisecond: 1, DateTimeKind.Utc), ApplicationInstanceId, epoch: new DateTime(2020, 01, 01, 0, 0, 0, DateTimeKind.Utc)); var milliseconds = factory.GetMillisecondsSinceEpoch(); Assert.Equal(1UL, milliseconds); }
public void Construct_WithClock_ShouldUseThat() { var clockValue = FluidIdGenerator.DefaultEpoch; DateTime FixedClock() => clockValue; var factory = new FluidIdGenerator(isProduction: false, FixedClock, applicationInstanceId: ApplicationInstanceId, bitDistribution: BitDistribution); var fluid = factory.CreateFluid(); Assert.Equal(GetMilliseconds(clockValue, factory.Epoch), GetMilliseconds(fluid)); }
private static IIdGenerator CreateIdGenerator(InvocationDetector invocationDetector) { return(new FluidIdGenerator(isProduction: false, utcClock: GetUtcNow, applicationInstanceId: 1)); // Local function that acts as the UTC clock and also toggles a ref bool DateTime GetUtcNow() { invocationDetector.WasInvoked = true; return(FluidIdGenerator.GetUtcNow()); } }
public void CreateFluid_TwiceWithAdvancingTimestamps_ShouldCount0Twice() { var invocationCount = 0; DateTime AutoAdvancingClock() => Epoch.AddMilliseconds(invocationCount++); var factory = new FluidIdGenerator(isProduction: false, utcClock: AutoAdvancingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution); var fluid1 = factory.CreateFluid(); var fluid2 = factory.CreateFluid(); Assert.Equal(0UL, GetCounterValue(fluid1)); Assert.Equal(0UL, GetCounterValue(fluid2)); }
public void CreateFluid_Regularly_ShouldCreateExpectedValue() { var factory = new FluidIdGenerator(isProduction: false, Clock, ApplicationInstanceId, Epoch, BitDistribution); var fluid = factory.CreateFluid(); Debug.Assert((ulong)(ClockValue - Epoch).TotalMilliseconds == 1); var milliseconds = 1UL << BitDistribution.ApplicationInstanceIdBitCount << BitDistribution.CounterBitCount; var applicationInstanceId = (ulong)ApplicationInstanceId << BitDistribution.CounterBitCount; const ulong counterValue = 0; var expectedValue = milliseconds | applicationInstanceId | counterValue; Assert.Equal(expectedValue, (ulong)fluid); }
public void CreateFluid_With10CounterBitsAnd1024Calls_ShouldCountUpTo1023() { Debug.Assert(BitDistribution.CounterBitCount == 10); // Timestamp advances after 1024 invocations var counter = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(counter++ >= 1024 ? 2 : 1), ApplicationInstanceId, epoch: Epoch, BitDistribution); counter = 0; // Reset from initial internal call for (var i = 0UL; i < 1024; i++) { Assert.Equal(i, GetCounterValue(factory.CreateFluid())); } }
public void CreateFluid_With10CounterBitsAnd1025Calls_ShouldSleepNoMoreThan1Second() { Debug.Assert(BitDistribution.CounterBitCount == 10); var sleepMs = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(sleepMs == 0 ? 1 : 2), ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepMs += ms); for (var i = 0; i < 1025; i++) { factory.CreateFluid(); } Assert.True(sleepMs <= 1000); }
public void CreateFluid_With10CounterBitsAnd1024Calls_ShouldNotSleep() { Debug.Assert(BitDistribution.CounterBitCount == 10); var sleepCount = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(sleepCount == 0 ? 1 : 2), ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepCount++); for (var i = 0; i < 1024; i++) { factory.CreateFluid(); } Assert.Equal(0, sleepCount); }
public void CreateFluid_With10CounterBitsAnd1025CallsAndStuckClock_ShouldThrowOnLastCall() { Debug.Assert(BitDistribution.CounterBitCount == 10); var sleepMs = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddDays(1), ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepMs += ms); for (var i = 0; i < 1024; i++) { factory.CreateFluid(); } Assert.Throws <TimeoutException>(() => factory.CreateFluid()); }
public void CreateFluid_TwiceWithRewindingClockMoreThanMaxAllowedMilliseconds_ShouldThrow() { var invocationCount = 0UL; DateTime AutorewindingClock() => Epoch.AddDays(1).AddMilliseconds(++invocationCount == 2 ? 0 : (1UL + FluidIdGenerator.MaxClockCatchupSleepMilliseconds) * invocationCount); // E.g. +2001, 0, +4002, +6003, ... var sleepMs = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: AutorewindingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepMs += ms); invocationCount = 0; // Reset the clock factory.CreateFluid(); Assert.Throws <TimeoutException>(() => factory.CreateFluid()); }
public void CreateFluid_TwiceWithRewindingClock1Second_ShouldSleep1Second() { var invocationCount = 0; DateTime AutorewindingClock() => Epoch.AddDays(1).AddSeconds(++invocationCount == 2 ? 0 : invocationCount); // +1, 0, +2, +3, +4, ... var sleepMs = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: AutorewindingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepMs += ms); invocationCount = 0; // Reset the clock factory.CreateFluid(); factory.CreateFluid(); Assert.Equal(1000, sleepMs); // We would need a minute to catch up, but will never sleep for more than a second }
public void CreateFluid_TwiceWithRewindingClockMaxAllowedMilliseconds_ShouldSleepThatLong() { var invocationCount = 0UL; DateTime AutorewindingClock() => Epoch.AddDays(1).AddMilliseconds(++invocationCount == 2 ? 0 : FluidIdGenerator.MaxClockCatchupSleepMilliseconds * invocationCount); // E.g. +2000, 0, +4000, +6000, ... var sleepMs = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: AutorewindingClock, ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepMs += ms); invocationCount = 0; // Reset the clock factory.CreateFluid(); factory.CreateFluid(); Assert.Equal(FluidIdGenerator.MaxClockCatchupSleepMilliseconds, (ulong)sleepMs); }
public void CreateFluid_With10CounterBitsAnd1026Calls_ShouldCount0And1OnTheLastTwoCalls() { Debug.Assert(BitDistribution.CounterBitCount == 10); // Timestamp advances after 1024 invocations var counter = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: () => Epoch.AddMilliseconds(counter++ >= 1024 ? 2 : 1), ApplicationInstanceId, epoch: Epoch, BitDistribution); counter = 0; for (var i = 0UL; i < 1024; i++) { factory.CreateFluid(); } var fluid1024 = factory.CreateFluid(); var fluid1025 = factory.CreateFluid(); Assert.Equal(0UL, GetCounterValue(fluid1024)); Assert.Equal(1UL, GetCounterValue(fluid1025)); }
public void CreateFluid_With10CounterBitsAnd1025CallsAndTemporarilyStuckClock_ShouldSleep1Millisecond() { Debug.Assert(BitDistribution.CounterBitCount == 10); var i = 0; var lastI = 0; var clockMilliseconds = 0; DateTime StuckClockUnlesInvokedAgainBySameIteration() => Epoch.AddMilliseconds(clockMilliseconds += i == lastI ? 1 : (lastI = i) - i); // Advances only when invoked multiple times by same iteration var sleepMs = 0; var factory = new FluidIdGenerator(isProduction: false, utcClock: StuckClockUnlesInvokedAgainBySameIteration, ApplicationInstanceId, epoch: Epoch, BitDistribution, sleepAction: ms => sleepMs += ms); for (i = 0; i < 1025; i++) { factory.CreateFluid(); } Assert.Equal(1, sleepMs); }
public void CreateFluid_WithClockThrowingOnSecondInvocationForOneFluid_ShouldNotChangeCounter() { var invocationCount = 0; DateTime ClockThatThrowsOnThirdInvocation() { if (++invocationCount == 3) { throw new Exception("Deliberately throws on third invocation."); } return(ClockValue); } var factory = new FluidIdGenerator(isProduction: false, ClockThatThrowsOnThirdInvocation, ApplicationInstanceId, Epoch, BitDistribution); factory.CreateFluid(); // Ensure that a "previous timestamp" was registered Assert.Throws <Exception>(() => factory.CreateFluid()); // This one throws var fluid = factory.CreateFluid(); // This one's counter value should not have been affected by the throwing invocation Assert.Equal(1UL, GetCounterValue(fluid)); }
public void Construct_WithBitDistribution_ShouldDistributeBitsAsExpected() { var factory = new FluidIdGenerator(isProduction: false, FluidIdGenerator.GetUtcNow, ApplicationInstanceId, bitDistribution: BitDistribution); var maxTimestamp = (ulong)(FluidIdGenerator.DefaultEpoch.AddMilliseconds((1UL << FluidBitDistribution.Default.TimestampBitCount) - 1) - FluidIdGenerator.DefaultEpoch).TotalMilliseconds; var timestampPartial = factory.GetTimestampPartialFluid(maxTimestamp); var applicationInstanceIdPartial = factory.GetApplicationInstanceIdPartialFluid(); var counterValuePartial = factory.GetCounterValuePartialFluid(2UL); var timestampMask = BitDistribution.MaxTimestamp << BitDistribution.ApplicationInstanceIdBitCount << BitDistribution.CounterBitCount; Assert.Equal(0UL, timestampPartial & ~timestampMask); Assert.Equal(maxTimestamp, timestampPartial >> BitDistribution.ApplicationInstanceIdBitCount >> BitDistribution.CounterBitCount); var applicationInstanceIdMask = BitDistribution.MaxApplicationInstanceId << BitDistribution.CounterBitCount; Assert.Equal(0UL, applicationInstanceIdPartial & ~applicationInstanceIdMask); Assert.Equal(ApplicationInstanceId, applicationInstanceIdPartial >> BitDistribution.CounterBitCount); var counterMask = BitDistribution.MaxCounterValue; Assert.Equal(0UL, counterValuePartial & ~counterMask); Assert.Equal(2UL, counterValuePartial); }
public void Construct_WithZeroApplicationInstanceIdOutsideProduction_ShouldSucceed() { _ = new FluidIdGenerator(isProduction: false, FluidIdGenerator.GetUtcNow, applicationInstanceId: 0); }