feat: expose persistent execution counters on timer mocks#1011
Merged
Conversation
Add `ITimerMock.ExecutionCount` and a new `IPeriodicTimerMock` interface with `WaitForNextTickCount` so tests can observe how often a timer callback fired or `WaitForNextTickAsync` was entered, including activity that happened before the assertion subscribed. Counters use `Interlocked.Increment` and `Volatile.Read`, persist across `Change(...)` / `Period` mutations, and freeze on dispose. `IPeriodicTimer` itself is left untouched; the new `IPeriodicTimerMock` lives in the testing project.
There was a problem hiding this comment.
Pull request overview
Exposes persistent execution counters on the testing time-system mocks: adds ExecutionCount to the existing ITimerMock and introduces a new IPeriodicTimerMock (gated by FEATURE_PERIODIC_TIMER) with WaitForNextTickCount. Both counters use Interlocked.Increment/Volatile.Read, survive Change/Period mutations, and freeze on dispose because the increment sites are unreachable after disposal.
Changes:
- Added
ExecutionCounttoITimerMockand incremented it inTimerMock.RunTimerafter each callback invocation (including swallowed-exception cases). - Added new
IPeriodicTimerMockinterface, madePeriodicTimerMockimplement it, and incrementedWaitForNextTickCountinWaitForNextTickAsyncafter the pre-notification disposal/cancellation checks. - Updated API baseline snapshots for all target frameworks and added unit tests covering initial value, increment, cancellation-before-entry, and post-dispose freeze behaviors.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| Source/Testably.Abstractions.Testing/TimeSystem/ITimerMock.cs | Adds ExecutionCount property with XML docs. |
| Source/Testably.Abstractions.Testing/TimeSystem/TimerMock.cs | Backs the counter with an Interlocked.Increment in RunTimer. |
| Source/Testably.Abstractions.Testing/TimeSystem/IPeriodicTimerMock.cs | New interface extending IPeriodicTimer with WaitForNextTickCount. |
| Source/Testably.Abstractions.Testing/TimeSystem/PeriodicTimerMock.cs | Implements the new interface and increments the counter on each entry. |
| Tests/Api/.../Expected/*.txt | Updated public-API baselines for net6/8/9/10 and netstandard2.0/2.1. |
| Tests/Testably.Abstractions.Testing.Tests/TimeSystem/TimerMockTests.cs | Tests for initial zero, persistence across waits, dispose freeze. |
| Tests/Testably.Abstractions.Testing.Tests/TimeSystem/PeriodicTimerMockTests.cs | Tests for cast, increment, pre-entry cancellation, dispose freeze. |
|
|
This is addressed in release v6.4.0. |
This was referenced May 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Add
ITimerMock.ExecutionCountand a newIPeriodicTimerMockinterface withWaitForNextTickCountso tests can observe how often a timer callback fired orWaitForNextTickAsyncwas entered, including activity that happened before the assertion subscribed. Counters useInterlocked.IncrementandVolatile.Read, persist acrossChange(...)/Periodmutations, and freeze on dispose.IPeriodicTimeritself is left untouched; the newIPeriodicTimerMocklives in the testing project.