Skip to content

Commit 113cc36

Browse files
committed
docs: correct diagram in readme
1 parent 837bc3c commit 113cc36

File tree

2 files changed

+23
-23
lines changed

2 files changed

+23
-23
lines changed

README.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ For example, if we create an `ITimer` with a *due time* and *period* set to **1
1616

1717
If we call `Advance(TimeSpan.FromSeconds(1))` three times, effectively moving time forward by three seconds, the timer callback will be invoked once at times `00:01`, `00:02`, and `00:03`, as illustrated in the drawing below. Both `FakeTimeProvider` and `ManualTimeProvider` behaves like this:
1818

19-
![Advancing time by three seconds in one-second increments.](docs/advance-1-second.svg)
19+
![Advancing time by three seconds in one-second increments.](https://raw.githubusercontent.com/egil/TimeProviderExtensions/main/docs/advance-1-second.svg)
2020

2121
However, if we instead call `Advance(TimeSpan.FromSeconds(3))` once, the two implementations behave differently. `ManualTimeProvider` will invoke the timer callback at the same time as if we had called `Advance(TimeSpan.FromSeconds(1))` three times, as illustrated in the drawing below:
2222

23-
![Advancing time by three seconds in one step using ManualTimeProvider.](docs/ManualTimeProvider-advance-3-seconds.svg)
23+
![Advancing time by three seconds in one step using ManualTimeProvider.](https://raw.githubusercontent.com/egil/TimeProviderExtensions/main/docs/ManualTimeProvider-advance-3-seconds.svg)
2424

2525
However, `FakeTimeProvider` will invoke the timer callback at time `00:03` three times, as illustrated in the drawings below:
2626

27-
![Advancing time by three seconds in one step using FakeTimeProvider.](docs/FakeTimeProvider-advance-3-seconds.svg)
27+
![Advancing time by three seconds in one step using FakeTimeProvider.](https://raw.githubusercontent.com/egil/TimeProviderExtensions/main/docs/FakeTimeProvider-advance-3-seconds.svg)
2828

2929
Technically, both implementations are correct since the `ITimer` abstractions only promise to invoke the callback timer *on or after the due time/period has elapsed*, never before.
3030

@@ -33,15 +33,15 @@ However, I strongly prefer the `ManualTimeProvider` approach since it behaves co
3333
## Known limitations:
3434

3535
- If running on .NET versions earlier than .NET 8.0, there is a constraint when invoking `CancellationTokenSource.CancelAfter(TimeSpan)` on the `CancellationTokenSource` object returned by `CreateCancellationTokenSource(TimeSpan delay)`. This action will not terminate the initial timer indicated by the `delay` argument initially passed the `CreateCancellationTokenSource` method. However, this restriction does not apply to .NET 8.0 and later versions.
36-
- To enable controlling `PeriodicTimer` via `TimeProvider` in versions of .NET earlier than .NET 8.0, the `TimeProvider.CreatePeriodicTimer` returns a `PeriodicTimerWrapper` object instead of a `PeriodicTimer` object. The `PeriodicTimerWrapper` type is just a lightweight wrapper around the original `System.Threading.PeriodicTimer` and will behave identically to it.
36+
- To enable controlling `PeriodicTimer` via `TimeProvider` in versions of .NET earlier than .NET 8.0, the `TimeProvider.CreatePeriodicTimer` returns a `PeriodicTimerWrapper` object instead of a `PeriodicTimer` object. The `PeriodicTimerWrapper` type is just a lightweight wrapper around the original `System.Threading.PeriodicTimer` and will behave identically to it.
3737

3838
## Installation
3939

4040
Get the latest release from https://www.nuget.org/packages/TimeProviderExtensions
4141

4242
## Set up in production
4343

44-
To use in production, pass in `TimeProvider.System` to the types that depend on `TimeProvider`.
44+
To use in production, pass in `TimeProvider.System` to the types that depend on `TimeProvider`.
4545
This can be done directly or via an IoC Container, e.g., .NETs built-in `IServiceCollection` like so:
4646

4747
```c#
@@ -56,11 +56,11 @@ and in the existing constructor(s) you have, just new up `TimeProvider.System` d
5656
public class MyService
5757
{
5858
private readonly TimeProvider timeProvider;
59-
59+
6060
public MyService() : this(TimeProvider.System)
6161
{
6262
}
63-
63+
6464
public MyService(TimeProvider timeProvider)
6565
{
6666
this.timeProvider = timeProvider;
@@ -72,15 +72,15 @@ This allows you to explicitly pass in a `ManualTimeProvider` during testing.
7272

7373
## Example - control time during tests
7474

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

7878
The idea is to replace the use of e.g. `Task.Delay` with an abstraction, the `TimeProvider`, that in production
7979
is represented by the `TimeProvider.System`, which just uses the real `Task.Delay`. During testing it is now possible to
8080
pass in `ManualTimeProvider`, which allows the test to control the progress of time, making it possible to skip ahead,
8181
e.g. 10 minutes, and also pause time, leading to fast and predictable tests.
8282

83-
As an example, let us test the "Stuff Service" below that performs specific tasks every 10 seconds with an additional
83+
As an example, let us test the "Stuff Service" below that performs specific tasks every 10 seconds with an additional
8484
1-second delay. We have two versions, one that uses the standard types in .NET, and one that uses the `TimeProvider`.
8585

8686
```c#
@@ -94,13 +94,13 @@ public class StuffService
9494
{
9595
this.container = container;
9696
}
97-
97+
9898
public async Task DoStuff(CancellationToken cancelllationToken)
9999
{
100100
using var periodicTimer = new PeriodicTimer(doStuffDelay);
101-
101+
102102
while (await periodicTimer.WaitForNextTickAsync(cancellationToken))
103-
{
103+
{
104104
await Task.Delay(TimeSpan.FromSeconds(1));
105105
container.Add(DateTimeOffset.UtcNow);
106106
}
@@ -119,13 +119,13 @@ public class StuffServiceUsingTimeProvider
119119
this.timeProvider = timeProvider;
120120
this.container = container;
121121
}
122-
122+
123123
public async Task DoStuff(CancellationToken cancelllationToken)
124124
{
125125
using var periodicTimer = timeProvider.CreatePeriodicTimer(doStuffDelay);
126-
126+
127127
while (await periodicTimer.WaitForNextTickAsync(cancellationToken))
128-
{
128+
{
129129
await timeProvider.Delay(TimeSpan.FromSeconds(1));
130130
container.Add(timeProvider.GetUtcNow());
131131
}
@@ -141,13 +141,13 @@ public void DoStuff_does_stuff_every_11_seconds()
141141
{
142142
// Arrange
143143
var timeProvider = new ManualTimeProvider();
144-
var container = new List<DateTimeOffset>();
144+
var container = new List<DateTimeOffset>();
145145
var sut = new StuffServiceUsingTimeProvider(timeProvider, container);
146-
146+
147147
// Act
148148
_ = sut.DoStuff(CancellationToken.None);
149149
timeProvider.Advance(TimeSpan.FromSeconds(11));
150-
150+
151151
// Assert
152152
container
153153
.Should()
@@ -167,13 +167,13 @@ Compare that to the similar test below for `StuffService` that needs to wait for
167167
public async Task DoStuff_does_stuff_every_11_seconds()
168168
{
169169
// Arrange
170-
var container = new List<DateTimeOffset>();
170+
var container = new List<DateTimeOffset>();
171171
var sut = new StuffService(container);
172-
172+
173173
// Act
174174
_ = sut.DoStuff(CancellationToken.None);
175175
await Task.Delay(TimeSpan.FromSeconds(11));
176-
176+
177177
// Assert
178178
container
179179
.Should()

0 commit comments

Comments
 (0)