Drive contract-base progress timing via ManualProgressTimerCore (Part B) - #280
Merged
Conversation
Adds a Wolfgang.Etl.TestKit-owned manual progress timer that drives the base's internal timer-core seam (reachable via the TestKit<->Abstractions friend relationship), so a component needs NO per-type IProgressTimer-injection plumbing to be timer-testable: - ManualProgressTimerCore + WithManualProgressTimer(extractor/loader/transformer) extensions, mirroring ManualTimeSource / WithTimeSource. Tick() fires the stage's progress callback exactly once, deterministically. - The three *BaseContractTests timer tests now build the SUT with the standard CreateSut(...) and attach a ManualProgressTimerCore; they no longer require CreateSutWithTimer. That member is demoted abstract -> virtual (throws if the base is invoked) so existing downstream overrides still compile — additive, no downstream change needed to adopt. This is #344 Part B: it lets downstream drop the per-component timer boilerplate (~80 files) and, with Part A (convenience bases), supersedes the #96 generator. Full solution build 0/0; TestKit.Xunit self-tests 313, TestKit unit 244 (incl. new harness tests), DocExamples green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new deterministic, manual progress-timer mechanism to Wolfgang.Etl.TestKit and updates the xUnit contract-test bases to use it, deprecating the older CreateSutWithTimer(IProgressTimer) path while preserving source compatibility for downstream overrides.
Changes:
- Introduces
ManualProgressTimerCoreplusWithManualProgressTimer(...)extension methods for extractor/loader/transformer stages. - Reworks
*BaseContractTestsprogress-timer tests to attachManualProgressTimerCorevia the new extensions; demotesCreateSutWithTimer(IProgressTimer)fromabstracttovirtual(throwing) and documents it as deprecated. - Updates PublicAPI tracking and changelog; adds unit tests for the new timer core.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Wolfgang.Etl.TestKit.Tests.Unit/ManualProgressTimerCoreTests.cs | Adds unit coverage for ManualProgressTimerCore behavior and null-guarding of the new extensions. |
| src/Wolfgang.Etl.TestKit/PublicAPI.Unshipped.txt | Records the new public API surface (ManualProgressTimerCore and ProgressTimerExtensions). |
| src/Wolfgang.Etl.TestKit/ProgressTimerExtensions.cs | Adds WithManualProgressTimer(...) stage extensions that wire the base timer-core seam to the manual core. |
| src/Wolfgang.Etl.TestKit/ManualProgressTimerCore.cs | Implements the manual timer core (captures the stage tick callback and invokes it synchronously via Tick()). |
| src/Wolfgang.Etl.TestKit.Xunit/ExtractorBaseContractTests.cs | Updates contract timer tests to use ManualProgressTimerCore and deprecates CreateSutWithTimer. |
| src/Wolfgang.Etl.TestKit.Xunit/LoaderBaseContractTests.cs | Updates contract timer tests to use ManualProgressTimerCore and deprecates CreateSutWithTimer. |
| src/Wolfgang.Etl.TestKit.Xunit/TransformerBaseContractTests.cs | Updates contract timer tests to use ManualProgressTimerCore and deprecates CreateSutWithTimer. |
| src/Wolfgang.Etl.TestKit.Xunit/PublicAPI.Shipped.txt | Reflects CreateSutWithTimer changing from abstract to virtual in the shipped API surface. |
| CHANGELOG.md | Documents the new API, the contract-test behavior change, and the deprecation path. |
Chris-Wolfgang
added a commit
that referenced
this pull request
Aug 3, 2026
… gate) Stage 1's per-class 90% coverage gate failed on ProgressTimerExtensions (88%) — the #280 timer-harness extension. The extractor overload + the three timer-null guards were covered; the loader/transformer happy-path bodies and their stage-null guards were not. Adds ProgressTimerExtensionsTests: loader/transformer overloads attach + return the stage, and null-argument guards for the loader/transformer overloads → ProgressTimerExtensions now 100%. Tests-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 5, 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.
Drive contract-base progress timing via
ManualProgressTimerCoreImplements Part B of the timer-injection work (tracked in Chris-Wolfgang/ETL-Abstractions#352 / #344). Additive — no downstream change required to adopt.
What
Wolfgang.Etl.TestKit):ManualProgressTimerCore+WithManualProgressTimer(...)extensions for extractor / loader / transformer, mirroring the existingManualTimeSource/WithTimeSourcepattern. Attach the timer, then fire the stage's progress callback deterministically withtimer.Tick(). It drives the base's internal timer-core seam via theTestKit ⇆ Abstractionsfriend relationship — so a component needs no per-typeIProgressTimer-injection plumbing to be timer-testable.Extractor/Loader/TransformerBaseContractTeststimer tests now build the SUT with the standardCreateSut(...)and attach aManualProgressTimerCore. They no longer requireCreateSutWithTimer, which is demotedabstract→virtual(throws if the base impl runs). Existing downstream overrides still compile and run — nothing breaks on the TestKit bump.Why
This is the enabler that lets each downstream repo delete its per-component timer boilerplate (~80
src/files) at its own pace. Combined with Part A (convenience base classes, Abstractions 0.21.0), it supersedes the #96 source generator — the ceremony it targeted is now gone via base/TestKit defaults, no codegen.Deprecation path
CreateSutWithTimer(IProgressTimer)is documented as deprecated and no longer called by the contract. It is intentionally not[Obsolete]-attributed yet (that would turn existing overrides into warnings→errors downstream under warnings-as-errors). Downstream removes its override +IProgressTimerctor per repo; the member is dropped in a future major.Verification
TestKit.Xunitself-tests 313 (exercise the reworked contract timer tests viaTestExtractor/TestLoader/TestTransformer),TestKitunit 244 (incl. newManualProgressTimerCoreharness tests), DocExamples green..TestKitentries;.TestKit.XunitCreateSutWithTimerabstract→virtual.