Make the tests that use the Heartbeat more deterministic. (#1724)

- Added Start to Heartbeat and made OnHeartbeat internal
- Explicitly call start in tests
This commit is contained in:
David Fowler 2017-04-20 01:41:37 -07:00 committed by GitHub
parent a053ca4758
commit d1494224f4
4 changed files with 25 additions and 18 deletions

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
private readonly TimeSpan _interval; private readonly TimeSpan _interval;
private readonly ISystemClock _systemClock; private readonly ISystemClock _systemClock;
private readonly IKestrelTrace _trace; private readonly IKestrelTrace _trace;
private readonly Timer _timer; private Timer _timer;
private int _executingOnHeartbeat; private int _executingOnHeartbeat;
public Heartbeat(IHeartbeatHandler[] callbacks, ISystemClock systemClock, IKestrelTrace trace) public Heartbeat(IHeartbeatHandler[] callbacks, ISystemClock systemClock, IKestrelTrace trace)
@ -30,6 +30,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
_interval = interval; _interval = interval;
_systemClock = systemClock; _systemClock = systemClock;
_trace = trace; _trace = trace;
}
public void Start()
{
_timer = new Timer(OnHeartbeat, state: this, dueTime: _interval, period: _interval); _timer = new Timer(OnHeartbeat, state: this, dueTime: _interval, period: _interval);
} }
@ -37,9 +41,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{ {
((Heartbeat)state).OnHeartbeat(); ((Heartbeat)state).OnHeartbeat();
} }
// Called by the Timer (background) thread // Called by the Timer (background) thread
private void OnHeartbeat() internal void OnHeartbeat()
{ {
var now = _systemClock.UtcNow; var now = _systemClock.UtcNow;
@ -69,7 +73,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
public void Dispose() public void Dispose()
{ {
_timer.Dispose(); _timer?.Dispose();
} }
} }
} }

View File

@ -121,6 +121,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
throw new InvalidOperationException("Server has already started."); throw new InvalidOperationException("Server has already started.");
} }
_hasStarted = true; _hasStarted = true;
_heartbeat.Start();
async Task OnBind(ListenOptions endpoint) async Task OnBind(ListenOptions endpoint)
{ {
@ -172,7 +173,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
} }
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
_heartbeat?.Dispose(); _heartbeat.Dispose();
} }
// Ungraceful shutdown // Ungraceful shutdown

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
@ -48,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var dateHeaderValueManager = new DateHeaderValueManager(systemClock); var dateHeaderValueManager = new DateHeaderValueManager(systemClock);
var testKestrelTrace = new TestKestrelTrace(); var testKestrelTrace = new TestKestrelTrace();
using (new Heartbeat(new IHeartbeatHandler[] { dateHeaderValueManager }, systemClock, testKestrelTrace, timerInterval)) using (var heartbeat = new Heartbeat(new IHeartbeatHandler[] { dateHeaderValueManager }, systemClock, testKestrelTrace, timerInterval))
{ {
Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String); Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
systemClock.UtcNow = future; systemClock.UtcNow = future;
@ -59,7 +58,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public async Task GetDateHeaderValue_ReturnsUpdatedValueAfterHeartbeat() public void GetDateHeaderValue_ReturnsUpdatedValueAfterHeartbeat()
{ {
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
var future = now.AddSeconds(10); var future = now.AddSeconds(10);
@ -72,18 +71,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var dateHeaderValueManager = new DateHeaderValueManager(systemClock); var dateHeaderValueManager = new DateHeaderValueManager(systemClock);
var testKestrelTrace = new TestKestrelTrace(); var testKestrelTrace = new TestKestrelTrace();
var heartbeatTcs = new TaskCompletionSource<object>();
var mockHeartbeatHandler = new Mock<IHeartbeatHandler>(); var mockHeartbeatHandler = new Mock<IHeartbeatHandler>();
mockHeartbeatHandler.Setup(h => h.OnHeartbeat(future)).Callback(() => heartbeatTcs.TrySetResult(null)); using (var heartbeat = new Heartbeat(new[] { dateHeaderValueManager, mockHeartbeatHandler.Object }, systemClock, testKestrelTrace, timerInterval))
using (new Heartbeat(new[] { dateHeaderValueManager, mockHeartbeatHandler.Object }, systemClock, testKestrelTrace, timerInterval))
{ {
heartbeat.OnHeartbeat();
Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String); Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
// Wait for the next heartbeat before verifying GetDateHeaderValues picks up new time. // Wait for the next heartbeat before verifying GetDateHeaderValues picks up new time.
systemClock.UtcNow = future; systemClock.UtcNow = future;
await heartbeatTcs.Task;
heartbeat.OnHeartbeat();
Assert.Equal(future.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String); Assert.Equal(future.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
Assert.True(systemClock.UtcNowCalled >= 2); Assert.True(systemClock.UtcNowCalled >= 2);
@ -104,8 +103,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var dateHeaderValueManager = new DateHeaderValueManager(systemClock); var dateHeaderValueManager = new DateHeaderValueManager(systemClock);
var testKestrelTrace = new TestKestrelTrace(); var testKestrelTrace = new TestKestrelTrace();
using (new Heartbeat(new IHeartbeatHandler[] { dateHeaderValueManager }, systemClock, testKestrelTrace, timerInterval)) using (var heatbeat = new Heartbeat(new IHeartbeatHandler[] { dateHeaderValueManager }, systemClock, testKestrelTrace, timerInterval))
{ {
heatbeat.OnHeartbeat();
Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String); Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -27,8 +28,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
heartbeatHandler.Setup(h => h.OnHeartbeat(systemClock.UtcNow)).Callback(() => handlerMre.Wait()); heartbeatHandler.Setup(h => h.OnHeartbeat(systemClock.UtcNow)).Callback(() => handlerMre.Wait());
kestrelTrace.Setup(t => t.TimerSlow(heartbeatInterval, systemClock.UtcNow)).Callback(() => traceMre.Set()); kestrelTrace.Setup(t => t.TimerSlow(heartbeatInterval, systemClock.UtcNow)).Callback(() => traceMre.Set());
using (new Heartbeat(new[] { heartbeatHandler.Object }, systemClock, kestrelTrace.Object, heartbeatInterval)) using (var heartbeat = new Heartbeat(new[] { heartbeatHandler.Object }, systemClock, kestrelTrace.Object, heartbeatInterval))
{ {
heartbeat.Start();
Assert.True(traceMre.Wait(TimeSpan.FromSeconds(10))); Assert.True(traceMre.Wait(TimeSpan.FromSeconds(10)));
} }
@ -49,12 +51,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
heartbeatHandler.Setup(h => h.OnHeartbeat(systemClock.UtcNow)).Throws(ex); heartbeatHandler.Setup(h => h.OnHeartbeat(systemClock.UtcNow)).Throws(ex);
using (new Heartbeat(new[] { heartbeatHandler.Object }, systemClock, kestrelTrace, heartbeatInterval)) using (var heartbeat = new Heartbeat(new[] { heartbeatHandler.Object }, systemClock, kestrelTrace, heartbeatInterval))
{ {
Assert.True(kestrelTrace.Logger.MessageLoggedTask.Wait(TimeSpan.FromSeconds(10))); heartbeat.OnHeartbeat();
} }
Assert.Equal(ex, kestrelTrace.Logger.Messages.First(message => message.LogLevel == LogLevel.Error).Exception); Assert.Equal(ex, kestrelTrace.Logger.Messages.Single(message => message.LogLevel == LogLevel.Error).Exception);
} }
} }
} }