Skip to content

test: cover lifetime and diagnostic ownership of a [Module]'s self-compiled [Scan]#128

Merged
vbreuss merged 1 commit into
mainfrom
test/module-scan-coverage
Jul 21, 2026
Merged

test: cover lifetime and diagnostic ownership of a [Module]'s self-compiled [Scan]#128
vbreuss merged 1 commit into
mainfrom
test/module-scan-coverage

Conversation

@vbreuss

@vbreuss vbreuss commented Jul 21, 2026

Copy link
Copy Markdown
Member

Two acceptance criteria of the self-compiled module scan were implemented but never asserted.

Lifetime: ScanLifetimeModule in the referenced support assembly self-compiles two scans, one Singleton and one Transient, over internal matches exposed only through their interfaces. ModuleScanLifetimeTests imports it and asserts a singleton match is one instance per Root (shared across that root's scopes, distinct between roots) while a transient match is a new instance per resolve. The existing coverage stopped at the emitted Lifetime = on the generated registration and never observed the container honoring it at runtime.

Diagnostic ownership: PerMatchScanDiagnosticsLandInTheLibrarysBuildAndNotTheConsumers runs the generator over a library whose scan matches one type with an accessible exposure interface and one with only an internal one, then compiles a consumer against the emitted assembly. The library build reports AWT196 on its own [Scan] line naming its own type; the consumer build reports nothing while still binding the exposable match. This is what evaluating the scan in the module compilation buys, and only the same-compilation case was covered before.


…compiled `[Scan]`

Two acceptance criteria of the self-compiled module scan were implemented but never asserted.

Lifetime: `ScanLifetimeModule` in the referenced support assembly self-compiles two scans, one Singleton and one Transient, over internal matches exposed only through their interfaces. `ModuleScanLifetimeTests` imports it and asserts a singleton match is one instance per `Root` (shared across that root's scopes, distinct between roots) while a transient match is a new instance per resolve. The existing coverage stopped at the emitted `Lifetime =` on the generated registration and never observed the container honoring it at runtime.

Diagnostic ownership: `PerMatchScanDiagnosticsLandInTheLibrarysBuildAndNotTheConsumers` runs the generator over a library whose scan matches one type with an accessible exposure interface and one with only an internal one, then compiles a consumer against the emitted assembly. The library build reports AWT196 on its own `[Scan]` line naming its own type; the consumer build reports nothing while still binding the exposable match. This is what evaluating the scan in the module compilation buys, and only the same-compilation case was covered before.
@vbreuss vbreuss self-assigned this Jul 21, 2026
@vbreuss
vbreuss enabled auto-merge (squash) July 21, 2026 10:37
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Test Results

   18 files  ± 0     18 suites  ±0   9m 59s ⏱️ -7s
1 242 tests + 4  1 241 ✅ + 4  1 💤 ±0  0 ❌ ±0 
5 895 runs  +24  5 894 ✅ +24  1 💤 ±0  0 ❌ ±0 

Results for commit 68997dc. ± Comparison against base commit 66905a0.

@vbreuss
vbreuss merged commit 5d8990f into main Jul 21, 2026
14 checks passed
@vbreuss
vbreuss deleted the test/module-scan-coverage branch July 21, 2026 10:42
@github-actions

Copy link
Copy Markdown

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Build Size Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 8 17.29 ns 0.614 ns 0.575 ns 0.90 144 B 1.00
Awaiten 8 19.27 ns 1.375 ns 1.286 ns 1.00 144 B 1.00
MsDI 8 1,553.99 ns 48.884 ns 43.334 ns 81.01 5688 B 39.50
Autofac 8 31,120.08 ns 140.674 ns 131.587 ns 1,622.22 33098 B 229.85
Jab 8 11.38 ns 0.377 ns 0.353 ns 0.59 96 B 0.67
PureDI 8 17.62 ns 0.373 ns 0.331 ns 0.92 128 B 0.89
DryIoc 8 729.93 ns 6.839 ns 6.063 ns 38.05 1528 B 10.61
SimpleInjector 8 14,242.21 ns 145.288 ns 128.794 ns 742.42 24761 B 171.95
baseline* 256 89.85 ns 2.157 ns 2.018 ns 0.71 2128 B 1.00
Awaiten 256 127.21 ns 4.961 ns 4.641 ns 1.00 2128 B 1.00
MsDI 256 17,350.48 ns 137.835 ns 128.931 ns 136.56 61016 B 28.67
Autofac 256 758,088.53 ns 3,279.398 ns 2,907.101 ns 5,966.84 740791 B 348.12
Jab 256 108.41 ns 7.751 ns 7.250 ns 0.85 2080 B 0.98
PureDI 256 122.37 ns 6.119 ns 5.110 ns 0.96 2112 B 0.99
DryIoc 256 46,356.42 ns 149.574 ns 124.901 ns 364.87 80646 B 37.90
SimpleInjector 256 415,737.84 ns 2,168.601 ns 1,810.880 ns 3,272.23 573042 B 269.29
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.85GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Realistic Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 164.6 ns 7.48 ns 7.00 ns 0.63 568 B 1.00
Awaiten 260.2 ns 1.67 ns 1.48 ns 1.00 568 B 1.00
MsDI 588.0 ns 4.42 ns 3.92 ns 2.26 1104 B 1.94
Autofac 6,216.6 ns 33.49 ns 31.33 ns 23.90 10784 B 18.99
Jab 197.6 ns 1.33 ns 1.18 ns 0.76 432 B 0.76
DryIoc 417.0 ns 5.79 ns 5.42 ns 1.60 944 B 1.66
SimpleInjector 689.7 ns 4.59 ns 4.07 ns 2.65 1096 B 1.93
PureDI 188.7 ns 2.30 ns 2.15 ns 0.73 632 B 1.11
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Resolve Size Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 8 6.998 ns 0.0182 ns 0.0170 ns 1.00 - NA
Awaiten 8 6.985 ns 0.0081 ns 0.0063 ns 1.00 - NA
MsDI 8 7.355 ns 0.0037 ns 0.0029 ns 1.05 - NA
Autofac 8 137.674 ns 6.4175 ns 6.0029 ns 19.71 656 B NA
Jab 8 2.651 ns 0.0061 ns 0.0054 ns 0.38 - NA
PureDI 8 5.426 ns 0.0100 ns 0.0089 ns 0.78 - NA
DryIoc 8 9.241 ns 0.0111 ns 0.0093 ns 1.32 - NA
SimpleInjector 8 10.592 ns 0.0233 ns 0.0218 ns 1.52 - NA
baseline* 256 7.826 ns 0.0067 ns 0.0056 ns 0.96 - NA
Awaiten 256 8.138 ns 0.0152 ns 0.0127 ns 1.00 - NA
MsDI 256 7.311 ns 0.0077 ns 0.0068 ns 0.90 - NA
Autofac 256 138.677 ns 5.7567 ns 5.3848 ns 17.04 656 B NA
Jab 256 42.636 ns 0.0326 ns 0.0255 ns 5.24 - NA
PureDI 256 8.276 ns 0.0138 ns 0.0115 ns 1.02 - NA
DryIoc 256 9.034 ns 0.0227 ns 0.0189 ns 1.11 - NA
SimpleInjector 256 14.053 ns 0.0190 ns 0.0169 ns 1.73 - NA

baseline* rows show the corresponding Awaiten benchmark from the most recent successful main branch build with results, for regression comparison.

github-actions Bot added a commit that referenced this pull request Jul 21, 2026
…ership of a `[Module]`'s self-compiled `[Scan]` (#128) by Valentin Breuß
github-actions Bot added a commit that referenced this pull request Jul 21, 2026
…ership of a `[Module]`'s self-compiled `[Scan]` (#128) by Valentin Breuß
@github-actions

Copy link
Copy Markdown

This is addressed in release v0.7.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Let a [Module] compile its own [Scan], so a library can keep implementations internal

1 participant