Skip to content

Add Owned<T> behaviour comparison suite vs Autofac (tests for #147) - #148

Merged
NikolayPianikov merged 2 commits into
DevTeam:devfrom
ekalchev:owned-idisposable-autofac-parity
Jul 19, 2026
Merged

Add Owned<T> behaviour comparison suite vs Autofac (tests for #147)#148
NikolayPianikov merged 2 commits into
DevTeam:devfrom
ekalchev:owned-idisposable-autofac-parity

Conversation

@ekalchev

Copy link
Copy Markdown

Follows up on #147.

Adds an Owned<T> behaviour-comparison suite under tests/Pure.DI.UsageTests/Owned/ (14 files, 28 tests). It uses differential testing: each test asserts Pure.DI and Autofac's Owned<T> against the same expected value, with Autofac as a proven reference implementation. That gives two things at once:

⚠️ 5 tests fail on purpose

They document the defects from #147 and are expected to stay red until Owned<T> is fixed:

  • MessagePumpTests — a captured Func<Owned<T>> reuses one accumulator per resolution, so in the canonical message-pump loop only the first unit of work is disposed; the rest leak.
  • NestedOwnedTests (×2) — outer and inner Owned<T> share that same accumulator, so disposing either disposes both graphs.
  • DisposalExceptionTestsOwned<T>.Dispose() swallows exceptions thrown by a component's Dispose(); Autofac surfaces them.
  • SingletonOwnerTests — a never-released Owned<T> is not disposed on composition disposal; Autofac disposes it as a container-level safety net.

The first three share one root cause (a single Owned accumulator reused per composition-root resolution instead of one per Owned<T>).

23 tests confirm parity

Transient/singleton/scoped disposal, root isolation, owned-of-singleton lifetime, mixed-lifetime nested graphs, diamond (shared) dependencies, LIFO disposal order, factory-created disposables, all async-disposal paths (including sync-over-async and mixed sync/async), double-dispose idempotency, and explicit release by a singleton owner.

No Autofac dependency by default

All Autofac code is compiled only under the AUTOFAC_REFERENCE symbol; the package reference is not committed (it lives in a git-ignored .csproj.user). Without the symbol — the default, and what CI sees — the suite builds and runs as pure Pure.DI tests, no Autofac package or reference. The same 5 tests fail either way; the Autofac side just adds an independent witness that the expected values are correct. See tests/Pure.DI.UsageTests/Owned/README.md for the enable recipe.

Verified locally on net10.0: builds with 0 warnings and runs 23 passed / 5 failed both with and without AUTOFAC_REFERENCE.

Run just this suite

dotnet test tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj --filter "FullyQualifiedName~Owned."

Emil Kalchev added 2 commits July 17, 2026 16:17
Adds a suite under tests/Pure.DI.UsageTests/Owned that pins Pure.DI's Owned<T> disposal and lifetime behaviour against Autofac's Owned<T> as the reference implementation. Every test asserts both containers against the same expected literal; the Autofac side compiles only when AUTOFAC_REFERENCE is defined (via a local, git-ignored .csproj.user), so the committed suite has no Autofac dependency and builds clean without it.

23 tests document confirmed parity: transient/singleton/scoped disposal, root isolation, owned-of-singleton lifetime, mixed-lifetime nested graphs, diamond (shared) dependencies, LIFO disposal order, factory-created disposables, all async-disposal paths (incl. sync-over-async and mixed sync/async), double-dispose idempotency, and explicit release by a singleton owner.

5 tests fail on purpose, documenting divergences from Autofac:
- Func<Owned<T>> reuses a single per-resolution accumulator, so in the canonical message-pump loop only the first unit of work is disposed and every later one leaks (instance never disposed and pinned by the shared accumulator).
- Nested Owned<T> share that same accumulator, so disposing either the outer or the inner Owned<T> disposes both graphs.
- Owned<T>.Dispose() swallows exceptions thrown by a component's Dispose() (routed to the empty OnDisposeException partial); Autofac surfaces them.
- A never-released Owned<T> is not disposed on composition disposal; Autofac disposes it as a container-level safety net.
Explains the suite's purpose, how to enable the Autofac reference oracle
locally (the git-ignored .csproj.user with the AUTOFAC_REFERENCE symbol and the
Autofac package), how to run it, and a table of the parity results and the five
intentional failures with their shared root cause.
@NikolayPianikov
NikolayPianikov changed the base branch from master to dev July 19, 2026 08:35
@NikolayPianikov
NikolayPianikov merged commit 0dc9940 into DevTeam:dev Jul 19, 2026
@ekalchev
ekalchev deleted the owned-idisposable-autofac-parity branch July 19, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants