Skip to content

[P1-22] Harden and test the asynchronous image pipeline #27

Description

@phranck

Problem

Image tests cover conversion but not the asynchronous view lifecycle. Current risks include:

  • URL loading uses a semaphore and nonisolated(unsafe) result state in Sources/TUIkitImage/ImageLoader.swift:248-279.
  • URLImageCache.shared is global (ImageLoader.swift:194-201).
  • _ImageCore constructs its concrete loader and writes source/phase boxes during rendering (Sources/TUIkit/Views/_ImageCore.swift:65-118).
  • Phase updates can arrive outside the correct actor, repeat invalidation, or let an old response overwrite a newer source.
  • Cancellation, status/MIME validation, byte/pixel limits, timeout, cache behavior, and disappear are not covered end to end.

Proposed solution

  • Inject a per-app loader, cache, clock, and transport; keep live networking out of tests.
  • Model request state on the owning actor and cancel on source/identity change, disappear, and shutdown.
  • Deduplicate identical in-flight requests and prevent stale completion from committing.
  • Validate HTTP status, MIME, byte count, dimensions, pixel count, timeout, redirects, and decode/decompression limits.
  • Build public AsyncImage phase/API behavior on this pipeline in the content API issue.

Acceptance criteria

  • Deterministic tests cover cache hit/miss, deduplication, source change, cancellation, timeout, redirect policy, status/MIME rejection, size/decode errors, disappear, and shutdown.
  • A stale response can never replace a newer view phase.
  • Two app runtimes share neither cache nor in-flight requests unless explicitly configured.
  • Rendering does not mutate image phase merely by reading the same source.
  • No semaphore or unsafe cross-actor result variable remains in async loading.
  • Swift 6.0 macOS/Linux build, tests, lint, and DocC are warning-free.

Dependencies

Depends on per-app runtime #8, async loop #9, stable lifecycle identity #10, and pure Swift decoder #16.

Parallelization

After those contracts settle, transport/cache work can proceed beside content API work, with _ImageCore lifecycle integration owned here.

Commit structure

  • Split this issue into small, thematic, independently revertible commits wherever the work can remain coherent.
  • Every commit must build and keep its applicable tests/gates green. Do not commit an intentionally failing regression test; use local/known-issue characterization or land the test with the smallest fix.
  • Keep characterization/fixtures, mechanical renames or moves, semantic changes, and documentation/migration updates separate when each step remains green.
  • The issue boundary is not a commit boundary; multiple commits are expected for independently reversible changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: highHigh-impact correctness, compatibility, or SDK completeness work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions