diff --git a/src/Middleware/OutputCaching/src/ISystemClock.cs b/src/Middleware/OutputCaching/src/ISystemClock.cs deleted file mode 100644 index 8e2ead45b9d7..000000000000 --- a/src/Middleware/OutputCaching/src/ISystemClock.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.OutputCaching; - -/// -/// Abstracts the system clock to facilitate testing. -/// -internal interface ISystemClock -{ - /// - /// Retrieves the current system time in UTC. - /// - DateTimeOffset UtcNow { get; } -} diff --git a/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs b/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs index 76b0bdb75e2d..15039d7c8815 100644 --- a/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs +++ b/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs @@ -243,7 +243,7 @@ internal async Task TryServeCachedResponseAsync(OutputCacheContext context } context.CachedResponse = cacheEntry; - context.ResponseTime = _options.SystemClock.UtcNow; + context.ResponseTime = _options.TimeProvider.GetUtcNow(); var cacheEntryAge = context.ResponseTime.Value - context.CachedResponse.Created; context.CachedEntryAge = cacheEntryAge > TimeSpan.Zero ? cacheEntryAge : TimeSpan.Zero; @@ -464,7 +464,7 @@ private bool OnStartResponse(OutputCacheContext context) if (!context.ResponseStarted) { context.ResponseStarted = true; - context.ResponseTime = _options.SystemClock.UtcNow; + context.ResponseTime = _options.TimeProvider.GetUtcNow(); return true; } diff --git a/src/Middleware/OutputCaching/src/OutputCacheOptions.cs b/src/Middleware/OutputCaching/src/OutputCacheOptions.cs index 709e6ff858a4..9e58a9c57401 100644 --- a/src/Middleware/OutputCaching/src/OutputCacheOptions.cs +++ b/src/Middleware/OutputCaching/src/OutputCacheOptions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; - namespace Microsoft.AspNetCore.OutputCaching; /// @@ -45,8 +43,7 @@ public class OutputCacheOptions /// /// For testing purposes only. /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal ISystemClock SystemClock { get; set; } = new SystemClock(); + internal TimeProvider TimeProvider { get; set; } = TimeProvider.System; /// /// Defines a which can be referenced by name. diff --git a/src/Middleware/OutputCaching/src/SystemClock.cs b/src/Middleware/OutputCaching/src/SystemClock.cs deleted file mode 100644 index 6cb33a828b8d..000000000000 --- a/src/Middleware/OutputCaching/src/SystemClock.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.OutputCaching; - -/// -/// Provides access to the normal system clock. -/// -internal sealed class SystemClock : ISystemClock -{ - /// - /// Retrieves the current system time in UTC. - /// - public DateTimeOffset UtcNow => DateTimeOffset.UtcNow; -} diff --git a/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs b/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs index 8b01426edf98..d2623feea6eb 100644 --- a/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs +++ b/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs @@ -334,44 +334,38 @@ public void ContentIsNotModified_IfNoneMatch_MatchesAtLeastOneValue_True() [Fact] public void StartResponseAsync_IfAllowResponseCaptureIsTrue_SetsResponseTime() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var middleware = TestUtils.CreateTestMiddleware(options: new OutputCacheOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); context.ResponseTime = null; middleware.StartResponse(context); - Assert.Equal(clock.UtcNow, context.ResponseTime); + Assert.Equal(timeProvider.GetUtcNow(), context.ResponseTime); } [Fact] public void StartResponseAsync_IfAllowResponseCaptureIsTrue_SetsResponseTimeOnlyOnce() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var middleware = TestUtils.CreateTestMiddleware(options: new OutputCacheOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); - var initialTime = clock.UtcNow; + var initialTime = timeProvider.GetUtcNow(); context.ResponseTime = null; middleware.StartResponse(context); Assert.Equal(initialTime, context.ResponseTime); - clock.UtcNow += TimeSpan.FromSeconds(10); + timeProvider.Advance(TimeSpan.FromSeconds(10)); middleware.StartResponse(context); - Assert.NotEqual(clock.UtcNow, context.ResponseTime); + Assert.NotEqual(timeProvider.GetUtcNow(), context.ResponseTime); Assert.Equal(initialTime, context.ResponseTime); } @@ -393,20 +387,17 @@ public void FinalizeCacheHeadersAsync_ResponseValidity_IgnoresExpiryIfAvailable( { // The Expires header should not be used when set in the response - var clock = new TestClock - { - UtcNow = DateTimeOffset.MinValue - }; + var timeProvider = new TestTimeProvider(DateTimeOffset.MinValue); var options = new OutputCacheOptions { - SystemClock = clock + TimeProvider = timeProvider }; var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: options); var context = TestUtils.CreateTestContext(); - context.ResponseTime = clock.UtcNow; - context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11)); + context.ResponseTime = timeProvider.GetUtcNow(); + context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(timeProvider.GetUtcNow() + TimeSpan.FromSeconds(11)); middleware.FinalizeCacheHeaders(context); @@ -419,25 +410,22 @@ public void FinalizeCacheHeadersAsync_ResponseValidity_UseMaxAgeIfAvailable() { // The MaxAge header should not be used if set in the response - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var sink = new TestSink(); var options = new OutputCacheOptions { - SystemClock = clock + TimeProvider = timeProvider }; var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: options); var context = TestUtils.CreateTestContext(); - context.ResponseTime = clock.UtcNow; + context.ResponseTime = timeProvider.GetUtcNow(); context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(12) }.ToString(); - context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11)); + context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(timeProvider.GetUtcNow() + TimeSpan.FromSeconds(11)); middleware.FinalizeCacheHeaders(context); @@ -448,25 +436,22 @@ public void FinalizeCacheHeadersAsync_ResponseValidity_UseMaxAgeIfAvailable() [Fact] public void FinalizeCacheHeadersAsync_ResponseValidity_UseSharedMaxAgeIfAvailable() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var sink = new TestSink(); var options = new OutputCacheOptions { - SystemClock = clock + TimeProvider = timeProvider }; var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: options); var context = TestUtils.CreateTestContext(); - context.ResponseTime = clock.UtcNow; + context.ResponseTime = timeProvider.GetUtcNow(); context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(12), SharedMaxAge = TimeSpan.FromSeconds(13) }.ToString(); - context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11)); + context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(timeProvider.GetUtcNow() + TimeSpan.FromSeconds(11)); middleware.FinalizeCacheHeaders(context); diff --git a/src/Middleware/OutputCaching/test/TestUtils.cs b/src/Middleware/OutputCaching/test/TestUtils.cs index 51039bb81cd9..615cec55e535 100644 --- a/src/Middleware/OutputCaching/test/TestUtils.cs +++ b/src/Middleware/OutputCaching/test/TestUtils.cs @@ -146,7 +146,7 @@ private static IEnumerable CreateBuildersWithOutputCaching( { outputCachingOptions.MaximumBodySize = options.MaximumBodySize; outputCachingOptions.UseCaseSensitivePaths = options.UseCaseSensitivePaths; - outputCachingOptions.SystemClock = options.SystemClock; + outputCachingOptions.TimeProvider = options.TimeProvider; outputCachingOptions.BasePolicies = options.BasePolicies; outputCachingOptions.DefaultExpirationTimeSpan = options.DefaultExpirationTimeSpan; outputCachingOptions.SizeLimit = options.SizeLimit; @@ -347,9 +347,23 @@ public ValueTask SetAsync(string key, byte[] entry, string[]? tags, TimeSpan val } } -internal class TestClock : ISystemClock +internal class TestTimeProvider : TimeProvider { - public DateTimeOffset UtcNow { get; set; } + private DateTimeOffset _current; + + public TestTimeProvider() : this(DateTimeOffset.UtcNow) { } + + public TestTimeProvider(DateTimeOffset current) + { + _current = current; + } + + public override DateTimeOffset GetUtcNow() => _current; + + public void Advance(TimeSpan timeSpan) + { + _current += timeSpan; + } } internal class AllowTestPolicy : IOutputCachePolicy diff --git a/src/Middleware/ResponseCaching/src/Interfaces/ISystemClock.cs b/src/Middleware/ResponseCaching/src/Interfaces/ISystemClock.cs deleted file mode 100644 index 584b847e25e6..000000000000 --- a/src/Middleware/ResponseCaching/src/Interfaces/ISystemClock.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.ResponseCaching; - -/// -/// Abstracts the system clock to facilitate testing. -/// -internal interface ISystemClock -{ - /// - /// Retrieves the current system time in UTC. - /// - DateTimeOffset UtcNow { get; } -} diff --git a/src/Middleware/ResponseCaching/src/ResponseCachingMiddleware.cs b/src/Middleware/ResponseCaching/src/ResponseCachingMiddleware.cs index eaaa433c9351..8131e512e7d3 100644 --- a/src/Middleware/ResponseCaching/src/ResponseCachingMiddleware.cs +++ b/src/Middleware/ResponseCaching/src/ResponseCachingMiddleware.cs @@ -142,7 +142,7 @@ internal async Task TryServeCachedResponseAsync(ResponseCachingContext con context.CachedResponse = cachedResponse; context.CachedResponseHeaders = cachedResponse.Headers; - context.ResponseTime = _options.SystemClock.UtcNow; + context.ResponseTime = _options.TimeProvider.GetUtcNow(); var cachedEntryAge = context.ResponseTime.Value - context.CachedResponse.Created; context.CachedEntryAge = cachedEntryAge > TimeSpan.Zero ? cachedEntryAge : TimeSpan.Zero; @@ -374,7 +374,7 @@ private bool OnStartResponse(ResponseCachingContext context) if (!context.ResponseStarted) { context.ResponseStarted = true; - context.ResponseTime = _options.SystemClock.UtcNow; + context.ResponseTime = _options.TimeProvider.GetUtcNow(); return true; } diff --git a/src/Middleware/ResponseCaching/src/ResponseCachingOptions.cs b/src/Middleware/ResponseCaching/src/ResponseCachingOptions.cs index 06917b322c35..cae036a28641 100644 --- a/src/Middleware/ResponseCaching/src/ResponseCachingOptions.cs +++ b/src/Middleware/ResponseCaching/src/ResponseCachingOptions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; - namespace Microsoft.AspNetCore.ResponseCaching; /// @@ -31,6 +29,5 @@ public class ResponseCachingOptions /// /// For testing purposes only. /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal ISystemClock SystemClock { get; set; } = new SystemClock(); + internal TimeProvider TimeProvider { get; set; } = TimeProvider.System; } diff --git a/src/Middleware/ResponseCaching/src/SystemClock.cs b/src/Middleware/ResponseCaching/src/SystemClock.cs deleted file mode 100644 index 6fc3ad49cb0f..000000000000 --- a/src/Middleware/ResponseCaching/src/SystemClock.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.ResponseCaching; - -/// -/// Provides access to the normal system clock. -/// -internal sealed class SystemClock : ISystemClock -{ - /// - /// Retrieves the current system time in UTC. - /// - public DateTimeOffset UtcNow => DateTimeOffset.UtcNow; -} diff --git a/src/Middleware/ResponseCaching/test/ResponseCachingMiddlewareTests.cs b/src/Middleware/ResponseCaching/test/ResponseCachingMiddlewareTests.cs index 4ef5f2b0ecad..212c77caf4fb 100644 --- a/src/Middleware/ResponseCaching/test/ResponseCachingMiddlewareTests.cs +++ b/src/Middleware/ResponseCaching/test/ResponseCachingMiddlewareTests.cs @@ -354,46 +354,40 @@ public void ContentIsNotModified_IfNoneMatch_MatchesAtLeastOneValue_True() } [Fact] - public void StartResponsegAsync_IfAllowResponseCaptureIsTrue_SetsResponseTime() + public void StartResponseAsync_IfAllowResponseCaptureIsTrue_SetsResponseTime() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var middleware = TestUtils.CreateTestMiddleware(options: new ResponseCachingOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); context.ResponseTime = null; middleware.StartResponse(context); - Assert.Equal(clock.UtcNow, context.ResponseTime); + Assert.Equal(timeProvider.GetUtcNow(), context.ResponseTime); } [Fact] public void StartResponseAsync_IfAllowResponseCaptureIsTrue_SetsResponseTimeOnlyOnce() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var middleware = TestUtils.CreateTestMiddleware(options: new ResponseCachingOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); - var initialTime = clock.UtcNow; + var initialTime = timeProvider.GetUtcNow(); context.ResponseTime = null; middleware.StartResponse(context); Assert.Equal(initialTime, context.ResponseTime); - clock.UtcNow += TimeSpan.FromSeconds(10); + timeProvider.Advance(TimeSpan.FromSeconds(10)); middleware.StartResponse(context); - Assert.NotEqual(clock.UtcNow, context.ResponseTime); + Assert.NotEqual(timeProvider.GetUtcNow(), context.ResponseTime); Assert.Equal(initialTime, context.ResponseTime); } @@ -448,19 +442,16 @@ public void FinalizeCacheHeadersAsync_DefaultResponseValidity_Is10Seconds() [Fact] public void FinalizeCacheHeadersAsync_ResponseValidity_UseExpiryIfAvailable() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.MinValue - }; + var timeProvider = new TestTimeProvider(DateTimeOffset.MinValue); var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: new ResponseCachingOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); - context.ResponseTime = clock.UtcNow; - context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11)); + context.ResponseTime = timeProvider.GetUtcNow(); + context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(timeProvider.GetUtcNow() + TimeSpan.FromSeconds(11)); middleware.FinalizeCacheHeaders(context); @@ -471,24 +462,21 @@ public void FinalizeCacheHeadersAsync_ResponseValidity_UseExpiryIfAvailable() [Fact] public void FinalizeCacheHeadersAsync_ResponseValidity_UseMaxAgeIfAvailable() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: new ResponseCachingOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); - context.ResponseTime = clock.UtcNow; + context.ResponseTime = timeProvider.GetUtcNow(); context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(12) }.ToString(); - context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11)); + context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(timeProvider.GetUtcNow() + TimeSpan.FromSeconds(11)); middleware.FinalizeCacheHeaders(context); @@ -499,24 +487,21 @@ public void FinalizeCacheHeadersAsync_ResponseValidity_UseMaxAgeIfAvailable() [Fact] public void FinalizeCacheHeadersAsync_ResponseValidity_UseSharedMaxAgeIfAvailable() { - var clock = new TestClock - { - UtcNow = DateTimeOffset.UtcNow - }; + var timeProvider = new TestTimeProvider(); var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: new ResponseCachingOptions { - SystemClock = clock + TimeProvider = timeProvider }); var context = TestUtils.CreateTestContext(); - context.ResponseTime = clock.UtcNow; + context.ResponseTime = timeProvider.GetUtcNow(); context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(12), SharedMaxAge = TimeSpan.FromSeconds(13) }.ToString(); - context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11)); + context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(timeProvider.GetUtcNow() + TimeSpan.FromSeconds(11)); middleware.FinalizeCacheHeaders(context); diff --git a/src/Middleware/ResponseCaching/test/TestUtils.cs b/src/Middleware/ResponseCaching/test/TestUtils.cs index 3a4b5584a759..14b5c211cb34 100644 --- a/src/Middleware/ResponseCaching/test/TestUtils.cs +++ b/src/Middleware/ResponseCaching/test/TestUtils.cs @@ -172,7 +172,7 @@ private static IEnumerable CreateBuildersWithResponseCaching( { responseCachingOptions.MaximumBodySize = options.MaximumBodySize; responseCachingOptions.UseCaseSensitivePaths = options.UseCaseSensitivePaths; - responseCachingOptions.SystemClock = options.SystemClock; + responseCachingOptions.TimeProvider = options.TimeProvider; } }); }) @@ -390,7 +390,21 @@ public void Set(string key, IResponseCacheEntry entry, TimeSpan validFor) } } -internal class TestClock : ISystemClock +internal class TestTimeProvider : TimeProvider { - public DateTimeOffset UtcNow { get; set; } + private DateTimeOffset _current; + + public TestTimeProvider() : this(DateTimeOffset.UtcNow) { } + + public TestTimeProvider(DateTimeOffset current) + { + _current = current; + } + + public override DateTimeOffset GetUtcNow() => _current; + + public void Advance(TimeSpan timeSpan) + { + _current += timeSpan; + } }