Skip to content

Commit bb5875d

Browse files
committed
feat: Added support for netstandard2.0
1 parent 8ab8dad commit bb5875d

File tree

9 files changed

+55
-28
lines changed

9 files changed

+55
-28
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to TimeProviderExtensions will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.0.0-preview.7]
9+
10+
- Added support for netstandard2.0, as this is supported by the back-port package https://www.nuget.org/packages/Microsoft.Bcl.TimeProvider.
11+
812
## [1.0.0-preview.6]
913

1014
- Added `Jump(TimeSpan)` and `Jump(DateTimeOffset)` methods that will jump time to the specified place. Any timer callbacks between the start and end of the jump will be invoked the expected number of times, but the date/time returned from `GetUtcNow()` and `GetTimestamp()` will always be the jump time. This differs from how `Advance` and `SetUtcNow` works. See the readme for a detailed description.

README.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
1+
[![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/egil/TimeProviderExtensions?include_prereleases&logo=github&style=flat-square)](https://github.com/egil/TimeProviderExtensions/releases)
2+
[![Nuget](https://img.shields.io/nuget/dt/TimeProviderExtensions?logo=nuget&style=flat-square)](https://www.nuget.org/packages/TimeProviderExtensions/)
3+
[![Issues Open](https://img.shields.io/github/issues/egil/TimeProviderExtensions.svg?style=flat-square&logo=github)](https://github.com/egil/TimeProviderExtensions/issues)
4+
15
# TimeProvider Extensions
26

3-
Extensions for [`System.TimeProvider`](https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider) API. It includes a version of the `TimeProvider` type, named `ManualTimeProvider`, that allows you to control the progress of time during testing deterministically.
7+
Extensions for the [`System.TimeProvider`](https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider) API. It includes:
8+
9+
- A test/fake version of `TimeProvider` type, named `ManualTimeProvider`, that allows you to control the progress of time during testing deterministically.
10+
- A backported version of `PeriodicTimer` that supports `TimeProvider` in .NET 6.
11+
12+
## Quick start
13+
14+
This describes how to get started:
15+
16+
1. Get the latest release from https://www.nuget.org/packages/TimeProviderExtensions.
17+
18+
2. Take a dependency on `TimeProvider` in your code. Inject the production version of `TimeProvider` available via the [`TimeProvider.System`](https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider.system?#system-timeprovider-system) property during production.
19+
20+
3. During testing, inject the `ManualTimeProvider` from this library. With `ManualTimeProvider`, you can move advance time by calling `Advance(TimeSpan)` or `SetUtcNow(DateTimeOffset)` and jump ahead in time using `Jump(TimeSpan)` or `Jump(DateTimeOffset)`. This allows you to write tests that run fast and predictably, even if the system under test pauses execution for multiple minutes using e.g. `TimeProvider.Delay(TimeSpan)`, the replacement for `Task.Delay(TimeSpan)`.
21+
22+
Read the rest of this README for further details and examples.
423

5-
An instance of `TimeProvider` for production use is available on the [`TimeProvider.System`](https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider.system?#system-timeprovider-system) property, and `ManualTimeProvider` can be used during testing.
24+
## ManualTimeProvider API
625

7-
During testing, you can move time forward by calling `Advance(TimeSpan)` or `SetUtcNow(DateTimeOffset)` on `ManualTimeProvider`. This allows you to write tests that run fast and predictably, even if the system under test pauses execution for multiple minutes using e.g. `TimeProvider.Delay(TimeSpan)`, the replacement for `Task.Delay(TimeSpan)`.
26+
The ManualTimeProvider represents a synthetic time provider that can be used to enable deterministic behavior in tests.
827

928
## Difference between `ManualTimeProvider` and `FakeTimeProvider`
1029

@@ -49,11 +68,11 @@ To support testing this scenario, `ManualtTimeProvider` includes a method that w
4968
fixture.Customize<ManualTimeProvider>(x => x.With(tp => tp.AutoAdvanceAmount, TimeSpan.Zero));
5069
```
5170

52-
## Installation
71+
## Installation and Usage
5372

5473
Get the latest release from https://www.nuget.org/packages/TimeProviderExtensions
5574

56-
## Set up in production
75+
### Set up in production
5776

5877
To use in production, pass in `TimeProvider.System` to the types that depend on `TimeProvider`.
5978
This can be done directly or via an IoC Container, e.g. .NETs built-in `IServiceCollection` like so:
@@ -84,7 +103,7 @@ public class MyService
84103

85104
This allows you to explicitly pass in a `ManualTimeProvider` during testing.
86105

87-
## Example - control time during tests
106+
### Example - control time during tests
88107

89108
If a system under test (SUT) uses things like `Task.Delay`, `DateTimeOffset.UtcNow`, `Task.WaitAsync`, or `PeriodicTimer`,
90109
it becomes hard to create tests that run fast and predictably.

src/TimeProviderExtensions/ManualTimeProvider.ManualTimer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ public ValueTask DisposeAsync()
5555
{
5656
Dispose(true);
5757
GC.SuppressFinalize(this);
58+
#if NET5_0_OR_GREATER
5859
return ValueTask.CompletedTask;
60+
#else
61+
return default;
62+
#endif
5963
}
6064

6165
private void Dispose(bool _)

src/TimeProviderExtensions/ManualTimeProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ public void Jump(DateTimeOffset value)
424424
// Calculates how many callbacks should have happened
425425
// in the jump period and invokes the callback that
426426
// number of times. Has to happen at least one time.
427-
var callbacksPassed = Math.Max(1, Math.Floor(jump / scheduler.Period));
427+
var callbacksPassed = Math.Max(1, Math.Floor((double)jump.Ticks / scheduler.Period.Ticks));
428428
for (int i = 0; i < callbacksPassed; i++)
429429
{
430430
scheduler.TimerElapsed();

src/TimeProviderExtensions/TimeProviderExtensions.csproj

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
<Company>Egil Hansen</Company>
77
<Authors>Egil Hansen</Authors>
88
<Description>
9-
Extensions for `System.TimeProvider` API. It includes a test version of the `TimeProvider`, named `ManualTimeProvider`, that allows you to control the progress of time during testing deterministically.
9+
Extensions for the `System.TimeProvider` API. It includes a test version of the `TimeProvider`, named `ManualTimeProvider`,
10+
that allows you to control the progress of time during testing deterministically and a backport of the `PeriodicTimer`
11+
version from .NET 8 to .NET 6/7 that supports `TimeProvider`.
1012
</Description>
1113
<PackageTags>TimeProvider, testing</PackageTags>
1214
<Copyright>Egil Hansen</Copyright>
@@ -31,7 +33,7 @@
3133
</Target>
3234

3335
<PropertyGroup>
34-
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
36+
<TargetFrameworks>net8.0;net6.0;netstandard2.0</TargetFrameworks>
3537
<ImplicitUsings>enable</ImplicitUsings>
3638
<Nullable>enable</Nullable>
3739
<Deterministic>true</Deterministic>
@@ -51,12 +53,12 @@
5153
</PropertyGroup>
5254

5355
<ItemGroup>
56+
<PackageReference Include="Microsoft.Bcl.TimeProvider" Version="8.0.0-preview.*" />
5457
<PackageReference Include="DotNet.ReproducibleBuilds" Version="1.1.1">
5558
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
5659
<PrivateAssets>all</PrivateAssets>
5760
</PackageReference>
5861
<PackageReference Include="MinVer" Version="4.3.0" PrivateAssets="All" />
59-
<PackageReference Include="Microsoft.Bcl.TimeProvider" Version="8.0.0-preview.*" />
6062
</ItemGroup>
6163

6264
<ItemGroup>

test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ await Task.Delay(TimeSpan.FromDays(1))
7676
}
7777
#endif
7878

79-
#if !NET8_0_OR_GREATER
79+
#if !NET8_0_OR_GREATER && NET6_0_OR_GREATER
8080
[Fact(Skip = "Include after NET 8 rc1 release")]
8181
public async Task Callbacks_happens_in_schedule_order()
8282
{
@@ -108,7 +108,7 @@ async Task AsyncCallbacks(PeriodicTimerWrapper periodicTimer)
108108
}
109109
}
110110
}
111-
#else
111+
#elif NET8_0_OR_GREATER
112112
[Fact]
113113
public async Task Callbacks_happens_in_schedule_order()
114114
{

test/TimeProviderExtensions.Tests/ManualTimeProviderTimerTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,14 @@ public void Advancing_causes_multiple_timers_invokes_callback_in_order()
155155
sut.Advance(TimeSpan.FromSeconds(11));
156156

157157
callbacks.Should().Equal(
158-
(1, oneSec * 2),
159-
(2, oneSec * 3),
160-
(1, oneSec * 4),
161-
(2, oneSec * 6),
162-
(1, oneSec * 6),
163-
(1, oneSec * 8),
164-
(2, oneSec * 9),
165-
(1, oneSec * 10));
158+
(1, TimeSpan.FromSeconds(2)),
159+
(2, TimeSpan.FromSeconds(3)),
160+
(1, TimeSpan.FromSeconds(4)),
161+
(2, TimeSpan.FromSeconds(6)),
162+
(1, TimeSpan.FromSeconds(6)),
163+
(1, TimeSpan.FromSeconds(8)),
164+
(2, TimeSpan.FromSeconds(9)),
165+
(1, TimeSpan.FromSeconds(10)));
166166
}
167167

168168
[Fact]

test/TimeProviderExtensions.Tests/Microsoft.Extensions.Time.Testing.Test/FakeTimeProviderTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,7 @@ public async Task Delay_Cancelled()
210210

211211
cs.Cancel();
212212

213-
#pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks
214213
await Assert.ThrowsAsync<TaskCanceledException>(async () => await delay);
215-
#pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks
216214
}
217215

218216
[Fact]

test/TimeProviderExtensions.Tests/TimeProviderExtensions.Tests.csproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
4+
<TargetFrameworks>net8.0;net6.0;netcoreapp3.1</TargetFrameworks>
55
<LangVersion>latest</LangVersion>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
<IsPackable>false</IsPackable>
99
<RootNamespace>TimeProviderExtensions</RootNamespace>
10-
<TargetMicrosoftTestTimeProvider>false</TargetMicrosoftTestTimeProvider>
10+
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="FluentAssertions" Version="6.9.0" />
15-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
16-
<PackageReference Include="xunit" Version="2.4.2" />
14+
<PackageReference Include="FluentAssertions" Version="6.12.0" />
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
16+
<PackageReference Include="xunit" Version="2.5.0" />
1717
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
1818
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1919
<PrivateAssets>all</PrivateAssets>
2020
</PackageReference>
21-
<PackageReference Include="coverlet.collector" Version="3.2.0">
21+
<PackageReference Include="coverlet.collector" Version="6.0.0">
2222
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2323
<PrivateAssets>all</PrivateAssets>
2424
</PackageReference>

0 commit comments

Comments
 (0)