Skip to content

hotfix(3.2.4): blank Library line on support reports (PP-5078) + LCP audiobook streaming (PP-4957) - #1455

Open
mauricecarrier7 wants to merge 13 commits into
mainfrom
hotfix/3.2.4-blank-library-field
Open

hotfix(3.2.4): blank Library line on support reports (PP-5078) + LCP audiobook streaming (PP-4957)#1455
mauricecarrier7 wants to merge 13 commits into
mainfrom
hotfix/3.2.4-blank-library-field

Conversation

@mauricecarrier7

Copy link
Copy Markdown
Contributor

3.2.4 carries two patron-facing fixes and nothing else.

PP-5078 — support reports arrived with a blank Library line

Patrons tapped Report an Issue and the report reached support with Library: and nothing after it, so nobody could tell which library the person was writing about.

The line was built from the library's display name alone. The app learns that name from the server, and there is a window — fresh install, slow connection, just after switching libraries — where it knows which library is selected but not yet its name. In that window it printed an empty string: no "unknown", no fallback. The information was never actually missing; the app held the library's unique ID the whole time and simply was not writing it down. That is also why it looked random rather than reproducible — it depended on timing, not on the patron or the library.

The field now falls back through what it actually knows: name → unique ID → (none selected). Support will no longer receive a blank library line.

PP-4957 — LCP audiobook streaming, back-ported

Streaming from an LCP license broke in a Readium update; the org fork restores it. The back-port initially carried the streaming provider without one of its consumers, which both reviewers blocked: on the 3.2.x line there is no MyBooksDownloadCenter+RegistryDownloadServicing.swift, so the equivalent logic lives inline in BookRegistrySync.contentPresence, which was not flag-aware. A .lcpl-only book would resolve .licenseOnly.downloadNeeded, and the re-download that would heal it is suppressed by the streaming guard — stranding the book permanently, with Listen gone after the first relaunch and never recovering. contentPresence is now flag-aware.

The toolkit pin is deliberately narrow

The first pass pinned the toolkit 33 commits past the 3.2.3 base. The full suite proved that was not free: PlaybackRateTests (×5) and AudiobookLoaderTests failed 3-of-3 iterations here while green on every other branch. PP-4518 had changed the playback ladder from presets [0.75, 1.0, 1.2, 1.5, 2.0] to [0.50, 1.0, 1.5, 2.0, 2.50, 3.0] — a user-visible change to how audiobooks play, which is not what a hotfix for a blank support field should carry.

The pin is now the 3.2.3 base plus exactly the two dependency-pin commits the streaming fix needs (ThePalaceProject/ios-audiobooktoolkit@a408d718). Both are pin-only and say so.

iOS 16 is preserved. The 17.0 floor on the first pass was not a decision — the wide pin's toolkit requires 17.0 while the narrowed one requires 13.0. All ten configs return to 16.0, matching 3.2.3, so this hotfix does not drop iOS 16 patrons.

Deliberately not carried: the F1/F2 download-durability fixes. They are real bugs present in 3.2.3, but all five are already ancestors of the 3.3.0 toolkit and 3.3.0 is at RC; they touch OverDrive/OpenAccess rather than the LCP path this hotfix fixes; and one of them exists only to fix a retain cycle another introduced — an area subtle enough to want a release cycle.

Verification

  • Full CI-parity suite green (scripts/xcode-test-optimized.sh) — 0 infra errors, 0 timeouts. Sampled ≈0.99×, with one retry-masked load-shaped flake (TPPBookRegistryLargeCorpusTests, failed 126s / passed 16s, green in CI history at 8–26s).
  • Device-verified on iPhone 17 Pro Max / iOS 26.6.1. Two Palace Marketplace LCP audiobooks borrowed with streaming on produced two 3 KB .lcpl licenses and zero .lcpa. After SIGKILL + cold launch both stayed download-successful, while the other ten records in the same registry read download-needed — a control showing reconciliation ran, so the survivors are not an artifact of load() never firing.
  • Both fixes falsified, not just passed. Deleting the contentPresence streaming arm fails 2 named assertions; deleting the LocalBookContentService guard fails 2; mutating AudiobookSessionManager:2056 fails 3. A green test that was never capable of red proves nothing.
  • AudiobookContentGateTests was 13/7 RED before this branch and nobody knew: every construction site left the flag provider defaulted, so the tests read live Firebase (TRUE at 100%) while asserting download-first behaviour. All three sites are now pinned; 14/0.

Build 500. 493 was not free — Palace-3.3.0.493.zip had already shipped. 500 verified against two independent sources that agree: the highest build across every git ref is 499, and the highest in ios-binaries is likewise 499.

Merge policy

Per CLAUDE.md, hotfix → main uses a regular merge commit (--no-ff), never squash, and this branch must then be forward-ported to develop with its original SHAs. Squashing is what produced the 296-conflict storm on 3.1.0.

Not done

  • Audio playback itself was not verified by artifact — the on-disk and registry evidence proves the streaming path is taken and survives relaunch, but says nothing about sound.
  • The LocalBookContentService streaming guard is unit-tested only; with contentPresence returning .present, no re-download is scheduled, so that guard is not reached on the device path.
  • No iOS 17.x device pass, and no coverage of the 3.2.0–3.2.3 upgrade cohort.

🤖 Generated with Claude Code

t and others added 12 commits September 3, 2026 10:51
…078)

`generateBody` rendered `Library: \(accountsManager.currentAccount?.name ?? "")`.
`currentAccount` resolves through the library registry and is nil until that
registry has loaded the selected account, so a patron who reports a problem
before it settles sends a report whose Library line is empty — while the patron
ID, a separate per-library lookup, resolves normally.

Real ticket 18864 (1 Sep 2026, app 3.2.3) reached support as:

    Palace Version: 3.2.3
    Library:
    Patron ID: 21467001510417

The agent triaging it recorded the summary as "Sign in prompt - no library?" —
the blank actively implied the patron had no library configured. The patron had
written that they were a member of Park Ridge Public Library.

The line is now produced by `libraryFieldValue(name:uuid:)`, which yields three
outcomes a reader can tell apart:

  * a real name       — the common case, passed through verbatim
  * the identifier    — the app knows WHICH library but cannot name it yet;
                        support can resolve a UUID, and cannot resolve a blank
  * "(none selected)" — the app genuinely has no library

A blank could equally have meant the line was lost in mail transit. None of
these can. Whitespace-only names fall back too, since a space renders in the
email exactly like nothing at all.

Intermittent, not universal: of the recent problem reports sampled in HelpSpot,
most carried a library name and a minority did not, which is why this has been
easy to miss.

**Scope:** the Library line in the problem-report body, plus its tests. The
patron-ID resolution, the report's other fields, and registry loading are all
untouched.

**Not done — the XCTest suite was NOT run.** It could not be: on this branch
`xcodebuild` refuses with "an out-of-date resolved file was detected at
Package.resolved", a pre-existing condition of main's committed lockfile rather
than anything this change introduces, and forcing resolution instead pulls a
newer swift-toolkit that conflicts on sqlite.swift. CI runs on a clean
environment and is the verification of record here. What WAS verified locally is
the decision itself, executed standalone against all four cells: the old
expression returns an empty string for a nil name (the shipped defect,
reproduced), and each new cell returns what it should. That proves the logic,
not the wiring into `generateBody`.

**Not done:** no build-number bump. This is deliberate — bumping now would
presume this ships alone, and un-bumping after a merge is worse than adding it.
Bump `CURRENT_PROJECT_VERSION` (492 on main today) before cutting the build.

**Deferred:** develop has since extracted this into
`AccountsManager+ProblemReportContext`, where the name still comes from
`currentAccount?.name` while the patron ID comes from the requested library — so
develop additionally mislabels a report filed about library A while B is
selected. The forward-port needs to carry the fallback into that extraction
rather than apply this diff verbatim; the file's own header comment records the
current behaviour as an intentional replication of the pre-extraction code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bumps MARKETING_VERSION 3.2.3 -> 3.2.4 and CURRENT_PROJECT_VERSION 492 -> 493
across the four app-target configs (Palace + Palace-noDRM, Debug/Release). The
two 1.0 entries belong to a non-app target and are untouched.

3.2.4 carries, relative to shipped 3.2.3:

  * PP-5078 — problem reports arrived with a blank Library line

The LCP archive-download fix is already in this base: build 492 (cfe530b,
"stop the doubled archive download, show its progress, and stop offering Listen
before the audio exists") is the commit this branch was cut from, so it is
inherited rather than needing a port.

LCP audiobook STREAMING is a separate thing and is deliberately NOT here. That
feature does not exist on this line at all — 3.2.3 has zero references to
`lcpAudiobookStreaming`, against five files on develop — and it depends on the
swift-toolkit `fork/3.11.0-issue-579` pin, where this branch is on exactVersion
3.9.0. Back-porting it would be a release, not a hotfix.

**Scope:** version strings only. No code change in this commit.

**Not done:** this branch does not build. `xcodebuild` fails before compiling
with "Could not resolve package dependencies: swift-toolkit depends on
sqlite.swift 0.16.0..<1.0.0 and root depends on 0.15.0..<0.16.0" — recorded
during the 3.3.0 regression as F-INFRA-006, "the shipped 3.2.3 tag no longer
builds from a clean checkout". swift-toolkit is pinned at exactVersion 3.9.0, so
the fork's own dependencies appear to have moved under a fixed tag. That blocks
shipping ANY 3.2.4 build until it is resolved, and it is not caused by anything
on this branch.

**Deferred:** the likely remedy is moving swift-toolkit to the fork revision
develop already uses (58413f868), which is a known-good combination with its
sqlite range — but that is a dependency bump on a release line and wants its own
change and its own verification, not a quiet addition to a support-copy fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ace Readium fork

`xcodebuild` on this line fails before compiling anything:

    Could not resolve package dependencies:
      'swift-toolkit' depends on 'sqlite.swift' 0.16.0..<1.0.0
      and root depends on 'sqlite.swift' 0.15.0..<0.16.0

3.2.3 pins `readium/swift-toolkit` at exactVersion 3.9.0 — upstream, not our
fork — and that tag's own dependency range has since moved to require
sqlite.swift 0.16+, while this project caps sqlite at 0.15.x. A fixed version
tag is not a fixed dependency graph, so a release that built at cut time stopped
building without a single commit landing on it.

Recorded during the 3.3.0 regression as F-INFRA-006: "THE SHIPPED 3.2.3 TAG NO
LONGER BUILDS FROM A CLEAN CHECKOUT."

This moves the same two references develop already carries:

    swift-toolkit  readium/swift-toolkit @ exactVersion 3.9.0
                -> ThePalaceProject/swift-toolkit @ revision 58413f868
    SQLite.swift   upToNextMinorVersion 0.15.0 -> 0.16.0

`58413f868` is `fork/3.11.0-issue-579`, the Palace fork carrying "Do not report
cancelled HTTP reads as loading errors" — the Readium regression that broke LCP
audiobook streaming, which is why the fork exists. It is also the revision
develop has run on since PP-4957, so this is a known-good combination rather
than a new one, and no later fork bump exists to chase.

Verified: `-resolvePackageDependencies` succeeds, and the app target reaches
`** BUILD SUCCEEDED **`.

**Scope:** two package references and the resulting lockfile. No source change.

**Not done — the TEST target does not compile after this.** 14 test files use
`ManifestJSON` and `Manifest.from`, which this line has no definition for;
develop gained them in `PalaceTests/Audiobook/ManifestFixture.swift` (added by
050c941, a later toolkit bump) and that fixture was never brought back to
main. The next commit ports it. Splitting them keeps this commit to the single
question of whether the branch can resolve at all.

**Deferred:** nothing here stops the same class recurring. An `exactVersion` pin
on a fork whose maintainers can retag will drift again; only a `revision` pin is
immune, which is what this now uses for swift-toolkit but not for the other
version-pinned packages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…es again

After the Readium fork bump, 14 test files on this line stopped compiling:

    error: cannot find type 'ManifestJSON' in scope
    error: type 'Manifest' has no member 'from'

Both symbols were previously reachable from the audiobook toolkit's own test
target. develop replaced that arrangement with a local fixture,
`PalaceTests/Audiobook/ManifestFixture.swift`, added by 050c941 (a toolkit
bump) — 95 lines defining `enum ManifestJSON` and
`extension Manifest { static func from(jsonFileName:bundle:) }`. It was never
brought back to main, so this line had no definition for either.

Ported verbatim from develop. It imports only Foundation and XCTest, so it
carries none of the decomposition that separates the two branches.

Verified: the test target compiles and `ProblemReportEmailTests` runs
12 tests / 0 failures — the first time the four PP-5078 cases have executed on
this branch at all. Before this they could not be built, only reasoned about.

**Scope:** one test fixture plus its two pbxproj registrations. No production
code.

**Not done:** the other 13 files that reference these symbols were not
individually run; the fixture is what they were missing, but only
ProblemReportEmailTests was executed here. A full-suite pass on this branch is
still owed and will surface anything else the fork bump moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… helper

SoD review (qa_test) found the gap: the four cases added in 8044e27 pin
`libraryFieldValue` itself, and measured properly — reverting it to `name ?? ""`
is killed by three of them by name. But nothing pinned that `generateBody`
CALLS it. Reverting the call site to `currentAccount?.name ?? ""` left every
test green while shipping the exact defect of ticket 18864.

That is the "test the helper, not the producer" trap, and it is the more
dangerous half here: the helper is new code nobody had a reason to change, while
the call site is a one-line interpolation inside a 60-character string that a
merge could plausibly clobber.

This asserts on the emitted body, because the bare `Library:` line IS the
artifact support receives — a return value would be one indirection away from
the thing that was wrong. It also covers the whitespace and end-of-body forms,
which render identically in an email.

Proven to bite: reverting the call site fails
`testPP5078_generateBody_neverEmitsABareLibraryLine` by name. 13 tests green
with the fix in place.

Deliberately asserts a PROPERTY (never empty) rather than an expected value,
because what the accounts manager resolves in a test environment is not fixed —
pinning "(none selected)" would make this a test of the environment.

**Scope:** one test. No production change.

**Not done:** the producer is still only exercised through whatever the real
`AppContainer.production().accountsManager` returns under test. Driving both the
resolved-name and unresolved-name branches through `generateBody` needs an
injectable accounts manager; `generateBody` takes the concrete `AccountsManager`
class and no test double exists on this line. The helper's four cases cover that
matrix directly, so the gap is in wiring coverage, not decision coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ports the streaming path to the 3.2.x line. LCP audiobook streaming worked
until a Readium change made cancelled HTTP reads report as loading errors,
which broke streaming-from-license; the Palace fork of swift-toolkit carries the
fix (fork/3.11.0-issue-579, "Do not report cancelled HTTP reads as loading
errors"). Restoring streaming here therefore means moving BOTH Readium pins —
the app's, done in b444854, and the audiobook toolkit's own, done here — plus
the Palace-side gate that decides when to stream.

Behaviour is unchanged until the flag is on. With
`lcp_audiobook_streaming_enabled` false, an LCP audiobook downloads the full
`.lcpa` before playback exactly as it does today. With it true, the book is
playable on its `.lcpl` license alone and the player streams on demand.

Ported from develop's a0968e6. The four core files applied cleanly; the flag
plumbing did not, because develop has since extracted `PalaceFeatureFlags` into
its own package and split `DeveloperSettingsViewModel` out — neither exists on
this line. The flag is therefore hand-wired into this line's shapes:
`RemoteFeatureFlags.FeatureFlag` gains the case, the local-override key and the
`isLCPAudiobookStreamingEnabled` accessor (local override > Firebase > false),
and `FirebaseManager` gains the `RemoteConfigKey` case plus its `setDefaults`
entry. The `managerKey` mapping is included deliberately: without it
`isFeatureEnabled` falls through to the in-app default and the flag reads false
forever no matter what Firebase says — an inert flag that looks wired.

Submodule ios-audiobooktoolkit 7a53098c -> e446a8e, the earliest revision whose
project points Readium at the fork rather than upstream 3.9.0. That is 33
commits, not the 106 to develop's pin, and it was chosen for that reason.

**This raises the deployment target from iOS 16.0 to 17.0**, on all ten app
configs, because e446a8e's toolkit requires it. That is the real cost of this
port and it is not reversible within it: iOS 16 devices will not receive 3.2.4.
Flagged explicitly because a floor raise in a patch release is not something
anyone should discover from a build log.

Verified: `** BUILD SUCCEEDED **`, and 43 tests pass across
LCPFulfillmentHandlerTests, AudiobookContentGateTests and
ProblemReportEmailTests. The flag chain was traced end to end — wire key,
managerKey mapping, and five consumer call sites.

**Scope:** the streaming gate, its flag plumbing, the toolkit pin, and the
deployment-target raise that pin forces.

**Not done — streaming itself is NOT verified on this line.** The tests prove
the gate's decisions and that everything compiles and links; they do not prove
an LCP audiobook actually streams. That needs a device, an LCP title, and the
flag on. On develop it was proven live on sim (borrow -> instant Listen ->
`.lcpa` stays 0 bytes -> plays); no equivalent run has happened here.

**Not done:** no full-suite pass on this branch. Three classes were run, chosen
because they cover the ported code; the other ~8,500 tests have not been
executed against the new toolkit and Readium fork on this line.

**Deferred, and it matters before shipping:** Firebase currently has
`lcp_audiobook_streaming_enabled` set to TRUE at 100% (published 2026-08-24).
It is inert today because no shipped 3.2.x build has the flag. The moment 3.2.4
ships, those users get streaming ON immediately — the in-app default of false is
overridden by the remote value. If that is not intended for this cohort, the
Firebase value or a condition must be set BEFORE release, not after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ts consumer

SoD review (architect and qa_test, independently) blocked the streaming port for
a real defect, confirmed before acting on it.

76f0206ed ported the streaming PROVIDER without its CONSUMER.
`MyBooksDownloadCenter:258` declared `lcpStreamingEnabledProvider` and nothing
read it. On develop the consumer lives in
`MyBooksDownloadCenter+RegistryDownloadServicing.swift`; that file does not
exist on this line, where the equivalent logic is inline in
`BookRegistrySync.contentPresence` — and that was not flag-aware.

The consequence, with streaming ON:

    fulfillment writes .lcpl only  ->  .downloadSuccessful
    relaunch -> contentPresence = .licenseOnly
    armDecision -> .downloadNeeded          (every .licenseOnly arm)
    redownload suppressed by LocalBookContentService's streaming guard
    -> book stranded permanently: Listen gone after first relaunch, never heals

Not hypothetical: Firebase has `lcp_audiobook_streaming_enabled` TRUE at 100%,
so this is the shipping path, and the failure is silent and unrecoverable from
the app.

`contentPresence` now returns `.present` for a license-only LCP audiobook when
streaming is on, so load-time reconciliation keeps the book playable across
launches. The provider is injected as a closure, matching the other providers on
this type, so the flag is read at CALL time rather than construction — this
object is built during `TPPBookRegistry.init`, before Remote Config has
necessarily fetched.

Verified by executing the decision chain both ways:

    streaming ON, license-only on disk
      without fix: .licenseOnly -> .downloadNeeded   -> STRANDED
      with fix:    .present     -> .downloadSuccessful -> plays

    streaming OFF (must not change)
      without fix: .licenseOnly -> .downloadNeeded
      with fix:    .licenseOnly -> .downloadNeeded   (identical)

`** BUILD SUCCEEDED **`; 86 tests pass across LCPFulfillmentHandlerTests,
AudiobookContentGateTests, ProblemReportEmailTests and BookRegistrySyncTests —
the last chosen because it covers the file this changes.

**Scope:** the one `.licenseOnly` branch in `contentPresence`, plus the provider
that feeds it. Flag OFF is byte-identical to before.

**Not done:** no regression test for the strand itself. The decision chain was
proven by executing it standalone, not through `BookRegistrySync`, because
reaching `contentPresence` needs a book on disk, an LCP-capable fixture and the
`#if LCP` path — a fixture this line has no precedent for. A test that drives
`load()` twice with a license-only file and asserts the state survives is the
right shape and is owed.

**Not done:** develop's `LocalBookContentServiceTests` (+41) and
`RegistryDownloadServicingSeamTests` (+62) are still unported; the architect
flagged the first as the test that would have caught this. `LocalBookContentServiceTests`
exists on this line and could take the port.

**Deferred:** streaming still unverified end to end on this line — that needs a
device, an LCP title and the flag on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he dead provider

Second round of SoD review. Three findings, all correct, all acted on.

**1. The strand had no regression test, and my reason for that was false.**
I wrote that reaching `contentPresence` needed an LCP fixture "this line has no
precedent for". It does:
`BookRegistrySyncTests.test_load_licenseWithoutContent_schedulesTheContentRedownload`
already writes an `.lcpl`, builds a book via `makeLCPAudiobook`, drives `load()`
and asserts the resulting state — in a suite I had already run. The new test is
its mirror with streaming ON: the book must stay `.downloadSuccessful` and NO
re-download may be scheduled. Without the flag-aware branch it downgrades to
`.downloadNeeded` and is stranded, because the heal is suppressed by the
streaming guard.

**2. Tests were reading the live Firebase flag.** `LocalBookContentServiceTests`
built its service without a `streamingEnabledProvider`, so ~14 tests consulted
`RemoteFeatureFlags.shared` — whose Firebase value is TRUE at 100%. Three would
fail and three would pass vacuously, and which depended on a remote config
rather than on this code. Pinned in both `makeService` and `setUp`, defaulting
OFF, matching what develop already does. Same pin applied to
`BookRegistrySyncTests` (its `setUp` instance to a literal false; its scheduling
factory threads the new parameter).

**3. `MyBooksDownloadCenter.lcpStreamingEnabledProvider` was dead** — declared,
read by nothing, and carrying a comment asserting a
`RegistryDownloadServicing.contentPresence` seam that does not exist on this
line. That false claim is what produced the original block. Removed; the real
consumer lives in `BookRegistrySync.contentPresence`.

**The previous "86 tests green" was measured against the WRONG toolkit and is
withdrawn.** `harness test` runs `worktree doctor` and self-heals a reported
drift by re-running `setup-ios`, which re-copies the toolkit from the CANONICAL
checkout — release/3.3.0, pinning ca0f4cac. The doctor always reports drift for
a hand-provisioned copy because it reads the module git DB rather than the
copy, so every `harness test` invocation silently replaced the correctly-pinned
e446a8e tree. I told the reviewer their finding was wrong; it was not, and their
method (hashing the toolkit's pbxproj) was better than my content-marker check,
which was true when run and overwritten afterwards.

Re-verified properly: toolkit provisioned at e446a8e and confirmed by pbxproj
hash 38a0cd7bf1bd BEFORE and AFTER the run, built with raw xcodebuild so no
self-heal could intervene. **110 tests, 0 failures**, across BookRegistrySync,
LocalBookContentService, LCPFulfillmentHandler, AudiobookContentGate and
ProblemReportEmail.

An intermediate run showed 7 failures. Those were operator contamination, not a
defect: a stale `RemoteFeatureFlags.lcpAudiobookStreamingLocalOverride = 1` left
on the simulator by an earlier manual experiment forced streaming ON in the test
host. Cleared, and the same command then passed. Recorded because "my own
leftover state" is a real explanation that looks exactly like a code failure.

**Scope:** test pinning, one regression test, one dead declaration removed.

**Not done:** the full suite still has not run on this branch — 110 of ~8,600.
The five suites chosen are the ones covering changed code, but a toolkit move,
a Readium fork repin and an iOS floor raise all have reach beyond them.

**Not done:** streaming is still not verified end to end. The device pass is
next and is the evidence that matters; PP-4957 was closed on sim evidence with
this pass explicitly still owed.

**Deferred:** 20 other `BookRegistrySync` constructions across 9 test files do
not pin the flag. They are only exposed if a test builds an LCP audiobook with
an `.lcpl` on disk, which none currently does — but the production default
reading `RemoteFeatureFlags.shared` means that is a latent coupling, not a safe
one. The `harness test` self-heal clobbering a hand-pinned submodule copy is a
harness defect and is not fixed here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s measured

Round 3 blocked on two findings. The architect reached both by reading; the QA
reviewer reached both by MEASURING against deleted production branches, which is
the stronger evidence and the reason these are fixed rather than argued.

(1) The streaming guard in `LocalBookContentService.redownloadLCPContentFile`
had no test at flag ON. `makeService(streamingEnabled:)` gained the parameter in
the same commit that was supposed to cover it, and nothing ever passed `true` —
so deleting `if streamingEnabledProvider() { return }` left the suite green.
That is the round-1 defect's shape exactly: a seam with no consumer. The new
test is differential rather than one-sided, because the ON half alone is
satisfied by a service that never fulfills at all; the OFF half is what proves
the seed, the spy and the call site are live, and therefore that the ON half's
zero is the guard's doing.

(2) `BookRegistrySyncTests` asserted `XCTAssertFalse(scheduledRedownload)`
immediately off `load`'s completion, but scheduling is
`main.asyncAfter(+contentRedownloadDelay)`. The absence was guaranteed, not
earned — QA confirmed the assertion does not fire even when the mutant DOES
schedule. This file already carries a comment recording the same trap being
measured on the in-flight test; this now uses the same barrier.

**Scope:** test-only. No production code changed.

**Not done:** the full suite has still not run on this branch. The
`review:qa_test` gate is absent from this branch's `plan.required`, so the QA
verdict that found both of these would not have been consulted at merge — a
gate that cannot fail. Tracking separately; not fixed here.

**Deferred:** `AccountsManager(defaults:suiteName)` injection for the
host-conditional ProblemReportEmail mutant (QA: does not block).
Build 493 was NOT free. `Palace-3.3.0.493.zip` already exists in ios-binaries,
so the hotfix was carrying a build number a shipped 3.3.0 artifact had already
used. Two different binaries under one number is exactly the confusion a build
number exists to prevent.

500 chosen because it is the first number above the global maximum, verified
from two independent sources that agree: the highest CURRENT_PROJECT_VERSION
across every remote branch and tag is 499 (origin/release/3.3.0), and the
highest build in ios-binaries is likewise 499 (Palace-3.3.0.499.zip).

Note that this project numbers builds as ONE monotonic sequence spanning all
version trains — 3.2.3.492, 3.3.0.493, 3.4.0.498 — not per-version. That is why
`scripts/ios-check-version.sh` would not have caught this: it tests the exact
`<version>.<build>` filename, so it sees `3.2.4.493` as absent and reports free
while `3.3.0.493` sits beside it.

**Scope:** version only — 4 CURRENT_PROJECT_VERSION lines, no other change.

**Not done:** App Store Connect was not consulted; both sources here are git and
ios-binaries. A build uploaded without its number ever being committed would be
invisible to both, though nothing in this project's history works that way.
QA round 4 found a surviving mutant: replacing `lcpStreamingEnabledProvider()`
with a literal `false` at AudiobookSessionManager.swift:2056 left this suite
green. The provider was wired and nothing consumed it — the third instance of
that shape on this branch.

Fixing it surfaced something worse. Run unmutated at 25d57af this suite is
**13 executed, 7 failures**. It has been red, and the reason no one saw it is
instructive: every construction site here left `lcpStreamingEnabledProvider`
defaulted, so the tests read `RemoteFeatureFlags.shared`, which is TRUE at 100%
in Firebase. The suite asserts download-first behaviour, so a live TRUE fails
it. QA's mutant run reported "13 executed, 0 failures" — but that zero was
produced BY the mutation, which forced the flag off and accidentally supplied
the pinning the suite was missing. The mutant looked survived and the suite
looked green off one run, and both readings were artifacts of the same defect.

All three construction sites are now pinned to `false` — `setUp` via a
`makeManager` factory, and the two contract-snapshot tests, which build their
own manager and whose recorded call ORDER only exists with streaming off.
Result: 14 executed, 0 failures.

The added test is differential for the reason the other two on this branch are:
the ON half alone is satisfied by a gate that does nothing, so the OFF half
asserts the trigger DID fire, making the ON half's empty trigger list
attributable to the flag rather than to a dead test.

Falsified both directions. Baseline 14/0. Mutating the argument to `false`
fails 3 named assertions in `testGate_contentMissing_streamingOn_…` (:190 the
await must not be reached, :194 contentUnavailable != proceed, :196 the trigger
list must stay empty). Production file restored and verified clean after.

**Scope:** test-only. No production code changed.

**Not done:** the other suites' pins were added earlier in this branch; this
commit does not audit the whole test tree for further unpinned reads of
`RemoteFeatureFlags.shared`. That census is worth doing and is not done here.
… 16 back

The hotfix pinned the toolkit at e446a8e — 33 commits past the 3.2.3 base. Two
of them are the LCP streaming fix. The other 31 are features and fixes that have
no bearing on either thing 3.2.4 exists to do, and the full suite proved they
were not free: PlaybackRateTests (5 tests) and AudiobookLoaderTests failed 3 of
3 iterations, green on every other branch, so this branch introduced them.

The cause was a real product change riding in on the bump. PP-4518 moved the
playback ladder from presets [0.75, 1.0, 1.2, 1.5, 2.0] to
[0.50, 1.0, 1.5, 2.0, 2.50, 3.0] and steps from 0.75×–2.0× to 0.50×–3.0×. That
is a user-visible change to how audiobooks play, and a hotfix for a blank
support-report field is not where it belongs.

Narrowed to the 3.2.3 toolkit base 7a53098 plus exactly the two streaming
commits, cherry-picked with no conflicts. Both are dependency-pin only and say
so: 06afaac bumps Readium 3.9.0 → 3.11.0 ("no source changes needed for the
3.10/3.11 API deltas"), e446a8e repins to ThePalaceProject/swift-toolkit @
58413f868 = 3.11.0 + fix-issue-579 ("No source changes"). Toolkit now
a408d718 on hotfix/3.2.4-streaming-only.

**iOS 16 comes back, and that is the bigger win.** The 17.0 floor on this branch
was not a decision — it was a consequence. The wide pin's toolkit requires 17.0;
the narrowed one requires 13.0. All ten configs return to 16.0, matching 3.2.3,
so this hotfix no longer drops iOS 16 patrons. Verified by building: the app
source compiles clean at 16.0 and the suites run.

Deliberately NOT carried: the F1/F2 download-durability fixes. They are real
patron-facing bugs present in 3.2.3 — per-launch-random background session
identifiers meant a track finishing while the app was killed could never be
finalized, leaving the book manifest-only against expiring signed URLs (the
-1008 class). They are excluded because all five are already ancestors of the
3.3.0 toolkit and 3.3.0 is at RC, they touch OverDrive/OpenAccess rather than
the LCP path this hotfix fixes, and cb284b9 exists only because F2 introduced a
retain cycle that architect SoD caught — an area subtle enough to want a release
cycle rather than a hotfix.

Verified: 99 executed, 0 failures across PlaybackRate, AudiobookLoader,
AudiobookContentGate, BookRegistrySync and LocalBookContentService. The 18
assertions in the two previously-failing suites all pass.

**Scope:** toolkit pin + deployment target. No app source changed.

**Not done:** the full CI-parity suite has not been re-run since this change —
the run that found the failures is now invalid and the verdict must be re-earned.

**Deferred:** hotfix/3.2.4-streaming-only exists only on this machine. It must
be pushed to ThePalaceProject/ios-audiobooktoolkit before this branch can
resolve for CI or anyone else.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🏗️ CodeAtlas Ledger Analysis

✅ All Checks Passed


♿ Accessibility (via AccessLint)

💡 Static analysis against WCAG 2.1 AA guidelines for iOS accessibility.

No accessibility issues detected


🧪 Test Coverage (via QAAtlas)

⚠️ No coverage data available - QAAtlas requires an OpenAI API key.
Set OPENAI_API_KEY in repository secrets to enable AI-powered test coverage analysis.


🏛️ Architecture Analysis

Metric Value ℹ️ What This Means
Components 22 Distinct modules/layers detected in your codebase
Dependency Cycles 0 Circular dependencies (A→B→C→A). Goal: 0
Layer Violations 1 Dependencies that break architectural boundaries
Hotspots 0 Files with high complexity + frequent changes
Avg Coupling 0.55 How interconnected modules are (lower is better, <1.0 is good)

🔍 Reachability Analysis

💡 Detects code that cannot be reached from entry points (dead code).

No dead code detected


📊 0 files analyzed | 📦 Download Full Report

Powered by CodeAtlas Ledger
• Accessibility: AccessLint

…o ledger entry

`tooling-integrity` failed on this PR, correctly. Two separate problems.

The gate reads only `state.version` from Package.resolved. This hotfix pins
Readium by REVISION — ThePalaceProject/swift-toolkit @ 58413f868, the
fix-issue-579 series — because the streaming fix is in no upstream release. A
revision pin has no `version` key, so the gate asserted "swift-toolkit pin not
found in Package.resolved" while the pin sat right there in the file. That
message describes a missing dependency; the reality was a pin it could not read.
It now accepts either identifier and still requires the ledger to name whichever
one is in use, so a fork pin is held to the same standard rather than exempted.

The second problem is the one the gate exists for, and it was a real finding:
`readium-money-path-validation.md` had no entry for this pin. Its own 3.9.0
section anticipated exactly this move — it records LCP streaming as `fail`
pending "the unmerged fix-issue-579 branch" — so the ledger was asking for this
entry by name.

Added, with the evidence actually gathered rather than an assertion of success:
two LCP audiobooks borrowed on Moes Max produced `.lcpl` licenses and ZERO
`.lcpa`, and survived SIGKILL + cold launch as `download-successful` while the
other ten registry records read `download-needed` — a control proving
reconciliation ran. Paths not exercised are recorded as `not validated`, not as
passes: EPUB/PDF, Findaway and OverDrive. Audio actually SOUNDING is called out
as still owed, because no artifact here demonstrates it.

**Scope:** one gate script and one ledger doc. No production code.

**Not done:** the EPUB/PDF reader paths were not device-exercised against this
pin. The suite is green, which is weaker evidence than a device pass, and the
ledger says so rather than rounding it up.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🧪 Unit Test Results

📊 View Full Interactive Report

❌ 2 TESTS FAILED

7434 tests | 7321 passed | 2 failed | 111 skipped | ⏱️ 18m 35s | 📊 98.5% | 📈 49.1% coverage

Tests by Class — 839 classes, 2 with failures
Class Tests Passed Failed Duration
✅ AccessLintComplianceTests 11 11 0 26ms
✅ AccessibilityAnnouncementCenterTests 20 20 0 2.01s
✅ AccessibilityLabelTests 9 9 0 21ms
✅ AccessibilityPreferencesTests 26 26 0 138ms
✅ AccessibilityServiceTests 11 11 0 270ms
✅ AccountAuthDocCarryoverTests 5 5 0 1.00s
✅ AccountAuthSurfaceHostsTests 7 7 0 37ms
✅ AccountAwareNetworkTests 10 10 0 176ms
✅ AccountDetailCredentialStateTests 7 0 0 60ms
✅ AccountDetailPINVisibilityTests 25 0 0 922ms
✅ AccountDetailSignOutConfirmationTests 2 0 0 38ms
✅ AccountDetailViewModelGapTests 1 1 0 175ms
✅ AccountDetailViewModelTests 19 0 0 236ms
✅ AccountDetailsAuthenticationIsBrowserBasedTests 10 10 0 242ms
✅ AccountDetailsNeedsAuthAggregateTests 10 10 0 29ms
✅ AccountDetailsURLTests 17 17 0 148ms
✅ AccountModelGapTests 9 9 0 999ms
✅ AccountModelTests 20 20 0 693ms
✅ AccountProfileDocumentTests 3 3 0 25ms
✅ AccountStateMachineTests 13 13 0 3.10s
✅ AccountSwitchCleanupTests 8 8 0 165ms
✅ AccountSwitchIntegrationTests 8 8 0 216ms
✅ AccountSwitchLifecycleTests 9 0 0 296ms
✅ AccountsManagerCacheTests 16 16 0 708ms
✅ AccountsManagerCancellationTests 5 5 0 553ms
✅ AccountsManagerGapTests 3 3 0 11ms
✅ AccountsManagerHelpersTests 12 12 0 40ms
✅ AccountsManagerIsolationLintTests 2 2 0 986ms
✅ AccountsManagerStateMachineWiringTests 15 15 0 4.77s
✅ AccountsManagerTests 51 51 0 2m 6s
✅ ActiveSessionsViewModelTests 12 12 0 281ms
✅ AdobeActivationTests 6 6 0 34ms
✅ AdobeCertificateGapTests 7 7 0 124ms
✅ AdobeDRMCharacterizationTests 21 21 0 60ms
✅ AdobeDRMErrorGapTests 3 3 0 31ms
✅ AdobeDRMHandlerTests 12 12 0 416ms
✅ AdobeDRMServiceGapTests 2 2 0 7ms
✅ AlertModelCoverageTests 6 6 0 17ms
✅ AlertModelRetryTests 7 7 0 22ms
✅ AlertModelTests 2 2 0 8ms
✅ AlertUtilsTests 20 20 0 647ms
✅ AnnotationContractTests 3 3 0 11ms
✅ AnnotationDeviceIDTests 2 2 0 9ms
✅ AnnotationPostResponseContractTests 1 1 0 6ms
✅ AnnouncementChainTests 5 5 0 13ms
✅ AnnouncementTests 3 3 0 43ms
✅ AnonymousBorrowBaselineFixtureTests 13 13 0 140ms
✅ AnonymousBorrowCandidateFixtureTests 6 6 0 126ms
✅ AnonymousBorrowDeltaTests 2 2 0 43ms
✅ AppContainerAudiobookFactoryTests 3 3 0 18ms
✅ AppContainerAuthCoordinatorRegistrationTests 3 3 0 8ms
✅ AppContainerImageLoaderInjectionTests 4 4 0 116ms
✅ AppContainerIsolationLintTests 5 5 0 1.45s
✅ AppContainerResetTests 5 5 0 738ms
✅ AppContainerTests 4 4 0 12ms
✅ AppContainerWithSignInModalSheetPresenterTests 2 2 0 13ms
✅ AppHealthViewModelTests 8 8 0 1.34s
✅ AppLaunchTrackerExtendedTests 16 16 0 450ms
✅ AppLaunchTrackerTests 10 10 0 349ms
✅ AppRouteTests 5 5 0 195ms
✅ AppTabHostMiniPlayerIntegrationTests 6 6 0 596ms
✅ AppTabHostViewBadgeCountTests 10 10 0 91ms
✅ AppTabRouterCoverageTests 4 4 0 20ms
✅ AppTabRouterGapTests 3 3 0 14ms
✅ ArrayExtensionsTests 6 6 0 60ms
✅ AudioBookmarkGapTests 6 6 0 44ms
✅ AudioEngineWrapperTests 8 8 0 296ms
✅ AudioInterruptionLogicTests 6 6 0 44ms
✅ AudioSessionActivatorTests 8 8 0 76ms
✅ AudiobookAccessibilityTests 7 7 0 40ms
✅ AudiobookBackgroundAudioTests 2 2 0 48ms
✅ AudiobookBearerTokenRecoveryTests 21 21 0 287ms
✅ AudiobookBookmarkBusinessLogicConcurrencyTests 3 3 0 143ms
✅ AudiobookBookmarkBusinessLogicPositionWriteTests 8 8 0 263ms
✅ AudiobookBookmarkBusinessLogicTests 21 21 0 4.47s
✅ AudiobookChapterTOCNormalizationTests 6 6 0 69ms
✅ AudiobookColdLoadRecoveryTests 4 4 0 14ms
✅ AudiobookContentGateTests 14 14 0 261ms
✅ AudiobookCrossVendorSmokeTests 4 4 0 1.48s
✅ AudiobookDataManagerEmptyQueueTests 1 1 0 10ms
✅ AudiobookDataManagerErrorHandlingTests 5 5 0 10.24s
✅ AudiobookDataManagerModelsTests 20 20 0 1.68s
✅ AudiobookDataManagerNetworkSyncTests 5 5 0 5.34s
✅ AudiobookDataManagerSaveTests 4 4 0 25ms
✅ AudiobookDataManagerStoreRecoveryTests 5 5 0 2.09s
✅ AudiobookFileLoggerTests 14 14 0 357ms
✅ AudiobookFirstOpenHangTests 9 9 0 2.08s
✅ AudiobookFullPlayerCoverContainerTests 9 9 0 156ms
✅ AudiobookLoadFailureSAMLReauthTests 10 10 0 1.35s
✅ AudiobookLoaderDispatchTests 7 7 0 136ms
✅ AudiobookLoaderFinalizeBuildTests 9 9 0 370ms
✅ AudiobookLoaderOPDSShapeMatrixTests 8 0 0 229ms
✅ AudiobookLoaderPredicateTests 11 11 0 29ms
✅ AudiobookLoaderTests 2 2 0 112ms
✅ AudiobookMiniPlayerViewTests 13 13 0 343ms
✅ AudiobookNetworkValidationTests 3 3 0 12ms
✅ AudiobookOpenStateRaceTests 3 3 0 906ms
✅ AudiobookPhoneAlertContentTests 3 3 0 39ms
✅ AudiobookPlaybackStateTests 3 3 0 91ms
✅ AudiobookPlaybackTests 26 26 0 600ms
✅ AudiobookPlaytimesLifecycleTests 6 6 0 44.40s
✅ AudiobookPositionAdapterContractTests 3 3 0 492ms
✅ AudiobookPositionPolicyValidatorTests 14 14 0 55ms
✅ AudiobookPositionRestoreTests 20 20 0 812ms
✅ AudiobookSAMLReauthTests 6 6 0 51ms
✅ AudiobookSessionErrorDescriptionTests 4 4 0 10ms
✅ AudiobookSessionErrorExtTests 4 4 0 34ms
✅ AudiobookSessionErrorTests 2 2 0 28ms
✅ AudiobookSessionManagerErrorMappingTests 6 6 0 38ms
✅ AudiobookSessionManagerFlagGatePresentationTests 4 4 0 60ms
✅ AudiobookSessionManagerPresenterMigrationTests 8 8 0 271ms
✅ AudiobookSessionManagerShutdownTests 8 8 0 91ms
✅ AudiobookSessionPresenterTests 16 16 0 817ms
✅ AudiobookSessionStateTests 6 6 0 44ms
✅ AudiobookSessionStateTransitionTests 18 18 0 27.28s
✅ AudiobookSleepTimerIntegrationTests 5 5 0 276ms
✅ AudiobookStorageLocationTests 3 3 0 31ms
✅ AudiobookTOCTests 18 18 0 449ms
✅ AudiobookTimeEntryTests 6 6 0 46ms
✅ AudiobookTimeTrackerEdgeTests 8 8 0 165ms
✅ AudiobookTimeTrackerLifecycleTests 5 5 0 1.16s
✅ AudiobookTimeTrackerTests 9 9 0 187ms
✅ AudiobookTrackCompletionTests 2 2 0 166ms
✅ AudiobookTypeRoutingTests 5 5 0 36ms
✅ AudiobookVendorAdapterTests 5 5 0 48ms
✅ AudiobookVendorRecoveryContractTests 1 1 0 119ms
✅ AudiobookmarkTests 4 4 0 42ms
✅ AuthCoordinatorTelemetryTests 5 5 0 49ms
✅ AuthDecisionEventEmissionTests 7 7 0 36ms
✅ AuthDocumentContractTests 2 2 0 10ms
✅ AuthDocumentVariantsContractTests 5 5 0 43ms
✅ AuthErrorCategoryTests 12 12 0 62ms
✅ AuthErrorProblemDocSeamTests 6 6 0 82ms
✅ AuthFlowSecurityTests 3 0 0 94ms
✅ AuthReducerTests 21 21 0 207ms
✅ AuthTypeTests 7 7 0 28ms
✅ AuthenticationTests 16 16 0 116ms
✅ BackgroundDownloadHandlerTests 28 28 0 753ms
✅ BackgroundListenerTests 2 2 0 79ms
✅ BackupExclusionMigrationTests 3 3 0 48ms
✅ BadgeDefinitionTests 33 33 0 109ms
✅ BadgeServiceTests 16 16 0 225ms
✅ BadgesViewModelTests 14 14 0 172ms
✅ BasicAuthEmptyCredentialTests 4 4 0 12ms
✅ BearerTokenAdapterTests 5 4 0 89ms
✅ BearerTokenFulfillFlowTests 4 4 0 41ms
✅ BearerTokenRefreshTests 4 4 0 14ms
✅ BearerTokenResponseDetectionTests 7 7 0 32ms
✅ BeginningPositionPolicyTests 8 8 0 60ms
✅ BookAvailabilityFormatterTests 18 18 0 108ms
✅ BookButtonMapperHoldReadyTests 10 10 0 85ms
✅ BookButtonMapperTests 21 21 0 500ms
✅ BookButtonMapperViewModelTests 18 18 0 49ms
✅ BookButtonStateTests 8 8 0 74ms
✅ BookButtonTypeMetaTests 4 4 0 23ms
✅ BookButtonTypeTests 13 13 0 96ms
✅ BookCellModelActionTests 18 18 0 827ms
✅ BookCellModelCacheInvalidationTests 8 8 0 118ms
✅ BookCellModelCachePrefetchSafetyTests 9 9 0 370ms
✅ BookCellModelCacheTests 22 22 0 866ms
✅ BookCellModelComputedPropertyTests 19 19 0 246ms
✅ BookCellModelLCPProgressTests 7 7 0 427ms
✅ BookCellModelOfflineTests 9 9 0 649ms
✅ BookCellModelRegistryBindingTests 4 4 0 1.67s
✅ BookCellModelStateTests 16 16 0 691ms
✅ BookCellModelStreamingHTMLTests 2 2 0 217ms
✅ BookCellStateComprehensiveTests 14 14 0 97ms
✅ BookContentResetServiceTests 2 2 0 11ms
✅ BookDetailLCPContentProgressTests 7 7 0 335ms
✅ BookDetailMetadataHydrationTests 6 6 0 153ms
✅ BookDetailViewModelTests 87 87 0 2.56s
✅ BookFileManagerTests 8 8 0 78ms
✅ BookListViewAccessibilityTests 9 9 0 39ms
✅ BookPreviewTests 4 4 0 38ms
✅ BookRegistryReconciliationTableTests 10 10 0 161ms
✅ BookRegistryStoreTests 26 26 0 864ms
✅ BookRegistrySyncReadinessTests 4 3 0 1.14s
✅ BookRegistrySyncReentrancyTests 4 4 0 212ms
✅ BookRegistrySyncTests 43 41 0 2.23s
✅ BookReturnCleverReauthTests 1 1 0 32ms
✅ BookReturnServiceAuthCoordinatorTests 3 3 0 4.77s
✅ BookReturnServiceContractTests 5 5 0 243ms
✅ BookReturnServiceTests 20 20 0 1.16s
✅ BookSignInRedirectHandlerTests 8 8 0 239ms
✅ BookStateIntegrationTests 8 8 0 66ms
✅ BookmarkBusinessLogicExtendedTests 6 6 0 745ms
✅ BookmarkDeletionLogTests 3 3 0 411ms
✅ BookmarkDeviceIdMatchingTests 3 3 0 457ms
✅ BookmarkExistenceTests 4 4 0 633ms
✅ BookmarkManagerTests 24 24 0 479ms
✅ BookmarkSortingTests 1 1 0 175ms
✅ BookmarkSyncTests 3 3 0 429ms
✅ BorrowAndDownloadIntegrationTests 7 7 0 419ms
✅ BorrowErrorMessageTests 13 13 0 43ms
✅ BorrowErrorPresenterTests 6 6 0 554ms
✅ BorrowOperationAuthCoordinatorTests 6 6 0 4.34s
✅ BorrowOperationCleverReauthTests 2 2 0 344ms
✅ BorrowOperationContractTests 6 6 0 479ms
✅ BorrowOperationStreamingHTMLTests 3 3 0 392ms
✅ BorrowOperationTests 13 13 0 2.05s
✅ BorrowOperationTimeoutTests 3 3 0 117ms
✅ BorrowReducerContractTests 2 2 0 33ms
✅ BorrowReducerTests 21 21 0 72ms
✅ BundledRegistrySnapshotTests 5 5 0 1.02s
✅ ButtonStateTests 16 16 0 151ms
✅ ButtonStyleTypeTests 2 2 0 33ms
✅ C64ConversionTests 6 6 0 30ms
✅ CarPlayAudiobookBridgePresenterMigrationTests 2 2 0 76ms
✅ CarPlayAuthHelperReadinessTests 3 3 0 574ms
✅ CarPlayChapterListTests 3 3 0 9ms
✅ CarPlayIntegrationTests 2 2 0 89ms
✅ CarPlayLibraryRefreshTests 3 3 0 2.43s
✅ CarPlayNowPlayingTemplateTests 4 4 0 740ms
✅ CarPlayOpenAppAlertTests 6 6 0 1.48s
✅ CarPlayPlaybackErrorTests 8 8 0 30ms
✅ CarPlayTests 12 12 0 79ms
✅ CarPlayTimeTrackingTests 3 3 0 89ms
✅ CatalogAPIDedupeTests 3 3 0 436ms
✅ CatalogAPIEntryPointTests 1 1 0 5ms
✅ CatalogAccessibilityTests 8 8 0 25ms
✅ CatalogCacheKeyAndIsolationTests 12 12 0 425ms
✅ CatalogCacheMetadataExactBoundaryTests 4 4 0 11ms
✅ CatalogCacheMetadataTests 21 21 0 101ms
✅ CatalogFeedModelTests 4 4 0 14ms
✅ CatalogFilterGroupModelTests 17 17 0 63ms
✅ CatalogFilterModelTests 17 17 0 102ms
✅ CatalogFilterServiceTests 29 29 0 111ms
✅ CatalogFilterTests 1 1 0 7ms
✅ CatalogLaneAssemblyTests 7 7 0 40ms
✅ CatalogLaneModelStructTests 18 18 0 394ms
✅ CatalogLaneModelTests 1 1 0 8ms
✅ CatalogLaneMoreFilterStateTests 8 8 0 93ms
✅ CatalogLaneMoreViewModelTests 43 43 0 855ms
✅ CatalogLaneRowViewAccessibilityTests 11 11 0 229ms
✅ CatalogLaneSortingTests 5 5 0 148ms
✅ CatalogLoadIntegrationTests 6 6 0 35ms
✅ CatalogOPDS2NegotiationTests 12 12 0 188ms
✅ CatalogPreloaderTests 6 6 0 58ms
✅ CatalogProblemDocumentTests 6 6 0 51ms
✅ CatalogRepositoryCoreTests 9 9 0 412ms
✅ CatalogRepositoryStaleWhileRevalidateTests 12 12 0 339ms
✅ CatalogRepositoryTests 19 19 0 200ms
✅ CatalogSearchViewModelRegistryUpdateTests 5 5 0 197ms
✅ CatalogSearchViewModelTests 67 67 0 7.55s
✅ CatalogSelectorsTests 2 2 0 8ms
✅ CatalogSortServiceTests 14 14 0 1.53s
✅ CatalogStateTests 7 7 0 47ms
✅ CatalogViewContinueRowsIntegrationTests 3 3 0 177ms
✅ CatalogViewModelStateMachineTests 10 10 0 140ms
✅ ChaosFaultInjectionTests 5 5 0 83ms
✅ ChapterChangeDetectorTests 5 5 0 726ms
✅ ChapterTOCNormalizerTests 7 7 0 27ms
✅ CirculationAnalyticsTests 4 4 0 40ms
✅ ColdStartResumeIntegrationTests 10 10 0 1.04s
✅ ColorExtensionTests 5 5 0 14ms
✅ ConcurrentBookStateTests 3 3 0 60ms
✅ ConcurrentDownloadStateTests 3 3 0 49ms
✅ ConcurrentTokenRefreshTests 2 2 0 34ms
✅ ContinueRowSectionTests 6 6 0 61ms
✅ ContinuousPlaybackTrackingTests 3 3 0 413ms
✅ CookiePersistenceTests 10 10 0 1.24s
✅ CrawlStateTests 16 16 0 78ms
✅ CrawlableFeedAnalysisTests 17 17 0 74ms
✅ CrawlerFallbackTests 12 12 0 153ms
✅ CredentialEdgeCaseTests 6 6 0 30ms
✅ CredentialPrivacyTests 4 4 0 15ms
✅ CredentialPromptCoordinatorTests 4 4 0 2.22s
✅ CrossDeviceBookmarkSyncTests 12 12 0 77ms
✅ CrossDeviceSyncE2ETests 8 8 0 12.62s
✅ CrossDomain401Tests 8 8 0 95ms
✅ CrossFormatMappingTests 14 14 0 44ms
✅ DPLAErrorTests 3 3 0 8ms
✅ DRMAdversarialTests 4 1 0 96ms
✅ DRMFulfilledPublicationTests 6 6 0 56ms
✅ DataBase64Tests 3 3 0 14ms
✅ DataReceptionComparisonTests 2 2 0 37ms
✅ DateExtensionTests 9 9 0 34ms
✅ DateFormattingTests 4 4 0 54ms
✅ Date_NYPLAdditionsTests 7 7 0 508ms
✅ DebugSettingsTests 27 27 0 175ms
✅ DefaultCatalogAPITests 31 31 0 254ms
✅ DefaultRecentlyReadingServiceTests 12 12 0 91ms
✅ DeriveInitialStateTests 4 4 0 25ms
✅ DeveloperSettingsTierTests 4 4 0 19ms
✅ DeviceLogCollectorGapTests 2 2 0 25.95s
✅ DeviceLogCollectorTests 9 9 0 3m 43s
✅ DeviceOrientationTests 7 7 0 23ms
✅ DeviceSpecificErrorMonitorTests 11 11 0 55ms
✅ DictionaryExtensionsTests 5 5 0 106ms
✅ DiskBudgetManagerTests 7 7 0 32ms
✅ DiskBudgetTests 2 2 0 15ms
✅ DownloadAlertPresenterTests 8 8 0 332ms
✅ DownloadAnnouncementServiceTests 12 12 0 589ms
✅ DownloadAuthRetryHandlerAuthCoordinatorTests 6 6 0 2.60s
✅ DownloadAuthRetryHandlerTaskLifecycleTests 4 4 0 412ms
✅ DownloadAuthRetryHandlerTests 17 17 0 2.32s
✅ DownloadCancellationHandlerTests 7 7 0 317ms
✅ DownloadCompletionParserTests 9 9 0 146ms
✅ DownloadCoordinatorIntegrationTests 10 10 0 107ms
✅ DownloadCoordinatorTests 11 11 0 56ms
✅ DownloadDiskSpaceTests 2 2 0 6ms
✅ DownloadErrorInfoTests 3 3 0 14ms
✅ DownloadErrorRecoveryPolicyTests 11 11 0 110ms
✅ DownloadErrorRecoveryTests 3 3 0 25ms
✅ DownloadFreeSpaceExhaustionTests 11 11 0 266ms
✅ DownloadInfoTests 5 5 0 110ms
✅ DownloadIntegrityTests 10 10 0 91ms
✅ DownloadOnlyOnWiFiTests 10 10 0 100ms
✅ DownloadPersistenceStoreTests 5 5 0 77ms
✅ DownloadProgressPublisherTests 29 29 0 871ms
✅ DownloadQueueIntegrationTests 3 3 0 77ms
✅ DownloadQueueOrchestratorTests 9 9 0 216ms
✅ DownloadRMSDKHandoffTests 1 1 0 4ms
✅ DownloadRedirectTests 7 7 0 21ms
✅ DownloadResumeAfterKillTests 7 7 0 567ms
✅ DownloadSlotManagementTests 5 5 0 44ms
✅ DownloadStartCoordinatorContractTests 5 5 0 380ms
✅ DownloadStartCoordinatorTests 11 11 0 331ms
✅ DownloadStartDispatcherTests 26 26 0 602ms
✅ DownloadStateMachineIntegrationTests 15 15 0 79ms
✅ DownloadStateMachineTests 5 5 0 34ms
✅ DownloadStateManagerTests 16 16 0 187ms
✅ DownloadTaskLifecycleServiceTests 9 9 0 263ms
✅ DownloadThrottlingServiceTests 10 10 0 394ms
✅ DownloadWatchdogTests 3 3 0 22ms
✅ EPUBKeyCommandsPP4289Tests 4 4 0 45ms
✅ EPUBModuleTests 4 4 0 76ms
✅ EPUBPositionTests 10 10 0 123ms
✅ EPUBSearchViewModelTests 18 18 0 206ms
✅ EPUBToolbarToggleTests 11 11 0 36ms
✅ EmailAddressTests 16 16 0 254ms
✅ EpubSampleFactoryTests 5 5 0 25ms
✅ ErrorActivityTrackerTests 12 12 0 64ms
✅ ErrorDetailTests 12 12 0 53ms
✅ ErrorDetailViewControllerGapTests 3 3 0 1.14s
✅ ErrorDetailViewControllerTests 14 14 0 259ms
✅ ErrorLogExporterTests 5 5 0 59ms
✅ ExpiredLoanStringsTests 5 5 0 21ms
✅ FacetEnumTests 3 3 0 11ms
✅ FacetToolbarAccessibilityTests 5 5 0 21ms
✅ FacetViewModelLogoDelegateTests 4 4 0 24ms
✅ FacetViewModelTests 18 18 0 147ms
✅ FetchManifestWithBearerTokenLCPSafetyTests 1 1 0 25ms
✅ FetchManifestWithBearerTokenTests 9 9 0 150ms
✅ FetchOpenAccessManifestLCPSafetyTests 4 4 0 44ms
✅ FileURLGenerationTests 3 3 0 264ms
✅ FindawayChapterStatusGuardTests 1 1 0 5ms
✅ FindawaySavedVsPlayedTests 1 1 0 22ms
✅ FloatTPPAdditionsTests 5 5 0 19ms
✅ FocusIndicationTests 7 7 0 95ms
✅ FontManagerTests 17 17 0 73ms
✅ ForceResetTests 6 6 0 70ms
✅ GeneralCacheTests 20 20 0 302ms
✅ GroupEnumTests 1 1 0 6ms
✅ HTMLTextViewTests 70 70 0 17.48s
✅ HalfSheetProgressCueTests 11 11 0 97ms
✅ HoldNotificationClassificationTests 2 2 0 34ms
✅ HoldsBadgeCountTests 9 9 0 102ms
✅ HoldsBookViewModelTests 8 8 0 185ms
✅ HoldsReducerTests 11 11 0 192ms
✅ HoldsSyncFailureTests 12 12 0 314ms
❌ HoldsViewModelTests 23 22 1 2m 3s
✅ ImageCacheContinuationTests 1 1 0 445ms
✅ ImageCacheTypeTests 1 1 0 4ms
✅ ImageLoaderTests 11 11 0 222ms
✅ IntExtensionsTests 4 4 0 23ms
✅ IsReaderActiveTrackingModifierTests 4 4 0 141ms
✅ KeyboardNavigationFKATests 11 11 0 274ms
✅ KeyboardNavigationHandlerTests 16 16 0 161ms
✅ KeyboardVoiceOverTests 5 5 0 91ms
✅ LCPAcquisitionPredicateTests 4 4 0 17ms
✅ LCPAdapterTests 8 8 0 82ms
✅ LCPAudiobookURLSchemeTests 4 4 0 126ms
✅ LCPAudiobooksTests 21 21 0 121ms
✅ LCPBotanCRLGuardTests 5 5 0 25ms
✅ LCPCharacterizationTests 31 31 0 547ms
✅ LCPClientTests 8 8 0 46ms
✅ LCPFulfillmentHandlerTests 18 18 0 502ms
✅ LCPKeychainMigrationTests 3 3 0 75ms
✅ LCPLibraryServiceTests 20 20 0 168ms
✅ LCPLicenseDocumentDetectionTests 5 5 0 23ms
✅ LCPLicenseFilePathTests 3 3 0 25ms
✅ LCPOrphanedDownloadRegistryTests 4 4 0 21ms
✅ LCPPDFAcquisitionPredicateTests 5 5 0 17ms
✅ LCPPDFDiskExtractTests 5 5 0 46ms
✅ LCPPDFOpenProgressTests 13 13 0 165ms
✅ LCPPassphraseReadinessTests 2 2 0 49ms
✅ LCPSessionIdentifierTests 3 3 0 14ms
✅ LegacySAMLProblemDocumentPropagationTests 7 7 0 1.09s
✅ LibrariesSectionViewModelTests 11 11 0 110ms
✅ LibraryCatalogMergerTests 9 9 0 43ms
✅ LibraryRegistryCrawlerTests 14 14 0 258ms
✅ LicensesServiceTests 4 4 0 33ms
✅ LiveCrawlableParsingTest 4 0 0 33ms
✅ LocalBookContentServiceTests 24 24 0 306ms
✅ LocalFileAdapterTests 6 5 0 82ms
✅ LogTests 14 14 0 363ms
✅ LoginKeyboardTests 8 8 0 43ms
✅ MainActorHelpersTests 22 22 0 634ms
✅ ManifestFixtureTests 2 2 0 24ms
✅ MappedCatalogBridgeTests 3 3 0 9ms
✅ MappedCatalogModelTests 11 11 0 730ms
✅ MockBackendExpiredCredentialsTests 3 3 0 89ms
✅ MockBackendIntegrationTests 4 4 0 249ms
✅ MockBackendLoanLimitTests 2 2 0 60ms
✅ MockBackendRouteMatchingTests 4 4 0 322ms
✅ MockBackendServerDownTests 1 1 0 93ms
✅ MockIsolationLintTests 5 5 0 1.84s
✅ MultiLibraryTokenIsolationTests 14 14 0 284ms
✅ MyBooksDownloadCenterAccountIdThreadingTests 6 6 0 153ms
✅ MyBooksDownloadCenterAdeptGapTests 3 3 0 569ms
✅ MyBooksDownloadCenterConcurrencyTests 21 21 0 490ms
✅ MyBooksDownloadCenterEvictionTests 7 7 0 115ms
✅ MyBooksDownloadCenterOfflineTests 8 8 0 701ms
✅ MyBooksDownloadCenterProgressPublishingTests 2 2 0 327ms
✅ MyBooksDownloadSessionInvalidationTests 3 3 0 19ms
✅ MyBooksSimplifiedBearerTokenTests 17 17 0 99ms
✅ MyBooksViewModelBooksPublisherTests 3 3 0 87ms
✅ MyBooksViewModelConcurrencyTests 4 4 0 59ms
✅ MyBooksViewModelDownloadStateTests 3 3 0 40ms
✅ MyBooksViewModelEmptyArrayTests 3 3 0 12ms
✅ MyBooksViewModelEmptyStateTests 4 4 0 59ms
✅ MyBooksViewModelExtendedTests 15 15 0 181ms
✅ MyBooksViewModelFacetIntegrationTests 4 4 0 40ms
✅ MyBooksViewModelFacetPublisherTests 3 3 0 249ms
✅ MyBooksViewModelFilterSortInteractionTests 2 2 0 30ms
✅ MyBooksViewModelFilterTests 9 9 0 75ms
✅ MyBooksViewModelGuardConditionsTests 2 2 0 34ms
✅ MyBooksViewModelLargeDatasetTests 2 2 0 591ms
✅ MyBooksViewModelLoadAccountTests 2 2 0 526ms
✅ MyBooksViewModelLoginStateTests 4 4 0 359ms
✅ MyBooksViewModelMultipleAuthorSortingTests 3 3 0 26ms
✅ MyBooksViewModelNotificationTests 4 4 0 359ms
✅ MyBooksViewModelOfflineFilteringTests 3 3 0 37ms
✅ MyBooksViewModelPublisherTests 7 7 0 1.39s
✅ MyBooksViewModelSearchEdgeCaseTests 6 6 0 60ms
✅ MyBooksViewModelSearchQueryTests 3 3 0 39ms
✅ MyBooksViewModelSortPersistenceTests 3 3 0 150ms
✅ MyBooksViewModelSortingIntegrationTests 5 5 0 49ms
✅ MyBooksViewModelSortingTests 6 6 0 64ms
✅ MyBooksViewModelStateTransitionTests 3 3 0 890ms
✅ MyBooksViewModelUIBindingTests 3 3 0 205ms
✅ NSErrorAdditionsTests 7 7 0 803ms
✅ NSNotificationTPPTests 3 3 0 11ms
✅ NavigationCoordinatorTests 17 17 0 52ms
✅ NavigationFreezePreventionTests 5 5 0 14ms
✅ NetworkExecutorCredentialGuardTests 8 8 0 118ms
✅ NetworkExecutorResponseRegressionTests 4 4 0 82ms
✅ NetworkExecutorTaskTypeTests 3 3 0 112ms
✅ NetworkOfflineDetectionTests 3 3 0 14ms
✅ NetworkQueueTests 11 11 0 132ms
✅ NetworkRequestQueueTests 2 2 0 10.10s
✅ NetworkRetryLogicTests 7 7 0 3.69s
✅ NetworkTimeoutTests 2 2 0 6ms
✅ NotificationEventTypeContractTests 7 7 0 47ms
✅ NotificationPayloadContractTests 10 10 0 44ms
✅ NotificationServiceStateMachineTests 9 9 0 2.40s
✅ NotificationServiceTests 16 16 0 60ms
✅ NotificationServiceTokenTests 13 13 0 64ms
✅ NotificationSyncThrottleTests 5 5 0 16ms
✅ NotificationTokenDataTests 4 4 0 16ms
✅ NotificationTokenRegistrationTests 10 10 0 45ms
✅ NowPlayingCoordinatorBackgroundTests 6 6 0 515ms
✅ NowPlayingCoordinatorTests 19 19 0 724ms
✅ OAuthSAMLRedirectRegressionTests 4 4 0 594ms
✅ OIDCAuthDocumentParsingTests 4 4 0 931ms
✅ OIDCAuthTypeTests 5 5 0 20ms
✅ OIDCAuthenticationPropertyTests 8 8 0 812ms
✅ OIDCCallbackEdgeCaseTests 9 9 0 1.14s
✅ OIDCCallbackHandlingTests 5 5 0 883ms
✅ OIDCCallbackSchemeTests 3 3 0 8ms
✅ OIDCIsolationRegressionTests 6 6 0 744ms
✅ OIDCLoginRoutingTests 3 3 0 518ms
✅ OIDCMakeRequestTests 3 3 0 351ms
✅ OIDCNSCodingTests 1 1 0 143ms
✅ OIDCNetworkLayer401Tests 5 5 0 610ms
✅ OIDCReauthOnExpiredTokenTests 5 5 0 549ms
✅ OIDCRedirectURIConstructionTests 6 6 0 1.40s
✅ OIDCRegressionTests 9 9 0 1.70s
✅ OIDCSelectedAuthenticationTests 2 2 0 271ms
✅ OIDCSignOutRegressionTests 6 6 0 3.10s
✅ OIDCTokenRefreshRegressionTests 6 6 0 585ms
✅ OIDCUpdateUserAccountTests 5 5 0 569ms
✅ OIDCViewModelRegressionTests 1 1 0 112ms
✅ OIDCViewModelSignInTests 2 2 0 25ms
✅ OPDS1BorrowEntryContractTests 4 4 0 27ms
✅ OPDS1CatalogGroupedContractTests 3 3 0 24ms
✅ OPDS1HoldEntriesContractTests 4 4 0 97ms
✅ OPDS1LoansFeedContractTests 6 6 0 160ms
✅ OPDS1ParsingTests 34 34 0 257ms
✅ OPDS1RevokeResponseContractTests 2 2 0 18ms
✅ OPDS2AuthenticationDocumentTests 18 18 0 216ms
✅ OPDS2AvailabilityTests 4 4 0 93ms
✅ OPDS2BookBridgeTests 44 44 0 187ms
✅ OPDS2BorrowResponseContractTests 3 3 0 40ms
✅ OPDS2CatalogWiringTests 21 21 0 212ms
✅ OPDS2CatalogsFeedTests 3 3 0 374ms
✅ OPDS2ContributorTests 2 2 0 28ms
✅ OPDS2EmptyFeedContractTests 1 1 0 5ms
✅ OPDS2FeedContractTests 4 4 0 45ms
✅ OPDS2FeedParsingTests 11 11 0 548ms
✅ OPDS2FeedTests 13 13 0 124ms
✅ OPDS2FullMetadataTests 4 4 0 17ms
✅ OPDS2FullPublicationTests 13 13 0 82ms
✅ OPDS2IntegrationTests 18 18 0 394ms
✅ OPDS2LinkArrayTests 5 5 0 12ms
✅ OPDS2LinkComputedPropertyTests 20 20 0 166ms
✅ OPDS2LinkRelTests 1 1 0 3ms
✅ OPDS2LinkTests 2 2 0 362ms
✅ OPDS2ParsingTests 38 38 0 555ms
✅ OPDS2PublicationExtendedTests 53 53 0 155ms
✅ OPDS2PublicationImageTests 6 6 0 34ms
✅ OPDS2PublicationNarratorTests 3 3 0 32ms
✅ OPDS2PublicationTests 2 2 0 245ms
✅ OPDS2SamlIDPTests 6 6 0 58ms
✅ OPDS2SearchResultsContractTests 3 3 0 35ms
✅ OPDS2SubjectTests 2 2 0 6ms
✅ OPDS2SupportingTypesTests 5 5 0 48ms
✅ OPDSAcquisitionPathExpandedTests 15 15 0 566ms
✅ OPDSFeedCacheTests 14 14 0 98ms
✅ OPDSFeedMigrationTests 11 11 0 59ms
✅ OPDSFeedParsingTests 2 2 0 212ms
✅ OPDSFeedServiceStateMachineTests 3 3 0 534ms
✅ OPDSFeedServiceTests 2 2 0 10ms
✅ OPDSFormatTests 13 13 0 323ms
✅ OPDSParserCoreTests 4 4 0 16ms
✅ OPDSParserTests 4 4 0 42ms
✅ OPDSParsingTests 57 57 0 8.88s
✅ OfflineActionTests 29 29 0 148ms
✅ OfflineQueueServiceExtendedTests 13 13 0 5.21s
✅ OfflineQueueServiceTests 17 17 0 7.02s
✅ OpenAccessAdapterTests 13 13 0 156ms
✅ OverdriveDeferredFulfillmentTests 6 6 0 22ms
✅ OverdriveDownloadHandlerTests 9 9 0 204ms
✅ OverdriveFulfillmentTests 14 13 0 138ms
✅ PDFExtensionsTests 20 20 0 60ms
✅ PDFReaderTests 12 12 0 60ms
✅ PP3596RegressionTests 3 3 0 56ms
❌ Palace 2 1 1 <1ms
✅ PalaceCheckPropertyTests 8 8 0 217ms
✅ PalaceErrorCategoryTests 20 20 0 142ms
✅ PalaceErrorExtendedTests 23 23 0 75ms
✅ PalaceErrorTests 11 11 0 57ms
✅ PalacePDFViewTests 12 12 0 163ms
✅ PalaceTestSetupObservationTests 4 4 0 297ms
✅ PalaceWiringTestCaseTests 4 4 0 21ms
✅ ParserFuzzTests 4 4 0 48.52s
✅ PatronProfileContractTests 4 4 0 31ms
✅ PerformanceMonitorTests 14 14 0 158ms
✅ PerformanceReportTests 14 14 0 88ms
✅ PersistentLoggerTests 9 9 0 2.54s
✅ PlaybackBootstrapperTests 8 8 0 415ms
✅ PlaybackFailureRecordTests 5 5 0 58ms
✅ PlaybackOpenPolicyTests 7 7 0 25ms
✅ PlaybackRateTests 16 16 0 115ms
✅ PlaybackTrackingRegressionTests 5 5 0 156ms
✅ PositionPersistenceLogicTests 6 6 0 31ms
✅ PositionPersistenceTests 2 2 0 15ms
✅ PositionSyncServiceTests 13 13 0 247ms
✅ PositionSyncTests 5 5 0 14ms
✅ PositionWriterContractTests 6 6 0 62ms
✅ PostUpdateMigrationTests 5 5 0 64ms
✅ ProblemDocumentContractTests 4 4 0 18ms
✅ ProblemDocumentLoanExpiryTests 5 5 0 29ms
✅ ProblemDocumentTests 12 12 0 69ms
✅ ProblemReportEmailTests 13 13 0 160ms
✅ ReachabilityTests 10 10 0 46ms
✅ Reader2BookmarkContractTests 3 3 0 65ms
✅ Reader2PositionAdapterContractTests 4 3 0 365ms
✅ Reader2PositionResumeContractTests 3 3 0 139ms
✅ ReaderAccessibilityTests 7 7 0 39ms
✅ ReaderEditingActionsTests 5 5 0 53ms
✅ ReaderErrorTests 5 5 0 38ms
✅ ReaderNavBarVoiceOverTests 2 2 0 9ms
✅ ReaderServiceSyncTests 3 3 0 206ms
✅ ReaderThemeTests 24 24 0 108ms
✅ ReadingPositionTests 22 22 0 1.54s
✅ ReadingSessionTrackerTests 13 13 0 218ms
✅ ReadingStatsServiceTests 12 12 0 103ms
✅ ReadingStatsStoreTests 9 9 0 247ms
✅ RedirectHandlingIntegrationTests 4 4 0 12ms
✅ RedirectPolicyTests 9 9 0 108ms
✅ RegistryFileRecoveryTests 30 30 0 415ms
✅ RemoteFeatureFlagsGapTests 4 4 0 31ms
✅ RemoteFeatureFlagsTests 11 11 0 283ms
✅ ResourcePropertiesLengthTests 3 3 0 12ms
✅ RetryClassificationTests 17 17 0 271ms
✅ ReturnFlowTests 1 1 0 4ms
✅ RightsManagementDetectionTests 5 5 0 89ms
✅ RightsManagementDispatcherTests 10 10 0 120ms
✅ RuntimeQuiescenceGateTests 8 8 0 1.35s
✅ RuntimeQuiescenceLintTests 5 5 0 1.39s
✅ SAMLCookieSyncTests 5 5 0 18ms
✅ SAMLLogoutCallbackDetectionTests 4 4 0 10ms
✅ SAMLLogoutLinkParsingTests 5 5 0 93ms
✅ SAMLLogoutURLTests 4 4 0 21ms
✅ SAMLPlusBiblioBoardExpirationTests 8 8 0 865ms
✅ SEMigrationsTests 6 6 0 427ms
✅ SafeDictionaryTests 21 21 0 181ms
✅ SamplePlayerErrorTests 5 5 0 12ms
✅ SampleTypeTests 8 8 0 74ms
✅ SceneDelegateTests 1 1 0 2ms
✅ ScopedResetTests 9 9 0 191ms
✅ SearchAccessibilityTests 6 6 0 20ms
✅ SearchFlowIntegrationTests 8 8 0 128ms
✅ SettingsViewModelComputedPropertyTests 6 6 0 74ms
✅ SettingsViewModelEdgeCaseTests 7 7 0 59ms
✅ SettingsViewModelGapTests 1 1 0 5ms
✅ SettingsViewModelSyncTests 14 14 0 169ms
✅ SettingsViewModelTests 33 33 0 646ms
✅ SignInModalLifecycleTests 9 9 0 79ms
✅ SignInModalPredicateTests 3 3 0 7ms
✅ SignInModalSAMLOIDCTests 6 6 0 46ms
✅ SignInOAuthErrorPropagationTests 8 8 0 1.95s
✅ SignInToReadFlowIntegrationTests 5 5 0 844ms
✅ SignInWebSheetIntegrationTests 3 3 0 2.86s
✅ SignInWebSheetViewModelTests 31 31 0 132ms
✅ SignOutCacheClearingTests 3 3 0 15ms
✅ SingletonResetRegistryTests 5 5 0 116ms
✅ StatsViewModelTests 10 10 0 2.52s
✅ StatusAnnouncementTests 22 22 0 359ms
✅ StopPositionSaveTests 2 2 0 6ms
✅ StoreTests 5 5 0 56ms
✅ StreamingReaderPresentationContractTests 1 1 0 96ms
✅ StreamingReaderProgressStoreTests 7 7 0 96ms
✅ StreamingReaderViewControllerScrollRestoreTests 12 12 0 717ms
✅ StreamingReaderViewModelTests 9 9 0 83ms
✅ StringExtensionTests 8 8 0 54ms
✅ StringExtensionsTests 3 3 0 35ms
✅ StringHTMLEntitiesTests 7 7 0 68ms
✅ StringNYPLAdditionsTests 4 4 0 15ms
✅ String_NYPLAdditionsTests 4 4 0 123ms
✅ SupportSectionDecisionTests 5 5 0 18ms
✅ SyncConflictResolutionTests 3 3 0 6ms
✅ SyncDeletionGuardTests 5 5 0 17ms
✅ SyncDeletionRatioTests 6 6 0 53ms
✅ SyncPermissionTests 5 5 0 215ms
✅ TPPAccountAuthStateEnumTests 5 5 0 23ms
✅ TPPAccountListDataSourceTests 3 3 0 50ms
✅ TPPAdobeActivationSkipTests 6 6 0 888ms
✅ TPPAgeCheckCompletionTests 5 5 0 691ms
✅ TPPAgeCheckIsValidTests 5 5 0 17ms
✅ TPPAgeCheckStateMachineTests 4 4 0 686ms
✅ TPPAgeCheckTests 6 6 0 1.60s
✅ TPPAgeCheckVerifyDecisionTests 5 5 0 1.04s
✅ TPPAlertUtilsTests 45 45 0 1.24s
✅ TPPAnnotationsHermeticTests 15 15 0 97ms
✅ TPPAnnotationsOverrideTests 4 4 0 417ms
✅ TPPAnnotationsTests 29 29 0 4.53s
✅ TPPAnnouncementManagerTests 3 3 0 17ms
✅ TPPAuthDocumentContractTests 3 3 0 23ms
✅ TPPBackgroundExecutorTests 3 3 0 172ms
✅ TPPBadgeImageGapTests 2 2 0 50ms
✅ TPPBaseReaderViewControllerInitialLocationTests 10 10 0 236ms
✅ TPPBasicAuthTests 11 11 0 33ms
✅ TPPBookAccessibilityLabelTests 8 8 0 59ms
✅ TPPBookAuthorCoverageTests 3 3 0 26ms
✅ TPPBookAuthorTests 6 6 0 16ms
✅ TPPBookBearerTokenTests 9 8 0 121ms
✅ TPPBookButtonsStateTests 7 7 0 256ms
✅ TPPBookContentMetadataFilesHelperTests 9 9 0 58ms
✅ TPPBookContentTypeConverterStreamingHTMLTests 2 2 0 23ms
✅ TPPBookContentTypeConverterTests 4 4 0 9ms
✅ TPPBookContentTypeExtendedTests 4 4 0 17ms
✅ TPPBookContentTypeTests 14 14 0 29ms
✅ TPPBookCoverRegistryTests 14 14 0 701ms
✅ TPPBookCreationTests 7 7 0 96ms
✅ TPPBookExtensionsTests 21 21 0 154ms
✅ TPPBookIsDRMProtectedTests 9 9 0 42ms
✅ TPPBookLocationCoverageTests 7 7 0 67ms
✅ TPPBookLocationEdgeCaseTests 27 27 0 144ms
✅ TPPBookLocationKeyTests 3 3 0 9ms
✅ TPPBookLocationTests 11 11 0 51ms
✅ TPPBookModelGapTests 4 4 0 42ms
✅ TPPBookRegistryAsyncReadinessTests 3 3 0 475ms
✅ TPPBookRegistryAtomicWriteTests 7 7 0 1.13s
✅ TPPBookRegistryBookRetrievalTests 7 7 0 110ms
✅ TPPBookRegistryBookmarkTests 7 7 0 116ms
✅ TPPBookRegistryCorruptedDataTests 5 5 0 87ms
✅ TPPBookRegistryDataTests 4 4 0 54ms
✅ TPPBookRegistryDependencyTests 4 4 0 42ms
✅ TPPBookRegistryFulfillmentIdTests 4 4 0 39ms
✅ TPPBookRegistryLargeCorpusTests 5 5 0 1m 44s
✅ TPPBookRegistryLoadReentrancyTests 2 2 0 13ms
✅ TPPBookRegistryLocationTests 4 4 0 56ms
✅ TPPBookRegistryMigrationTests 16 16 0 1.76s
✅ TPPBookRegistryPersistenceTests 10 10 0 4.70s
✅ TPPBookRegistryProcessingTests 2 2 0 12ms
✅ TPPBookRegistryPublisherTests 6 6 0 184ms
✅ TPPBookRegistryRecordPersistenceTests 3 3 0 34ms
✅ TPPBookRegistryRecordTests 10 10 0 104ms
✅ TPPBookRegistryStateManagementTests 11 11 0 498ms
✅ TPPBookRegistryThreadSafetyTests 3 3 0 447ms
✅ TPPBookRegistryUpdateAndRemoveTests 1 1 0 19ms
✅ TPPBookRequiresAdobeDRMTests 6 6 0 28ms
✅ TPPBookSerializationTests 13 13 0 59ms
✅ TPPBookStateInitializationTests 4 4 0 15ms
✅ TPPBookStateTests 4 4 0 13ms
✅ TPPBookTests 98 98 0 623ms
✅ TPPBookmarkDeletionLogTests 11 11 0 1.22s
✅ TPPBookmarkFactoryInitTests 2 2 0 12ms
✅ TPPBookmarkFactoryServerAnnotationEdgeCaseTests 5 5 0 28ms
✅ TPPBookmarkFactoryTests 15 15 0 132ms
✅ TPPBookmarkR3ConversionTests 5 5 0 32ms
✅ TPPBookmarkR3LocationTests 13 13 0 45ms
✅ TPPBookmarkSpecTests 1 1 0 23ms
✅ TPPCachingTests 3 3 0 63ms
✅ TPPCapturedCredentialsTests 5 5 0 853ms
✅ TPPConfigurationTests 22 22 0 170ms
✅ TPPContentTypeTests 9 9 0 19ms
✅ TPPCredentialConcurrencyTests 3 3 0 43ms
✅ TPPCredentialIsolationE2ETests 5 0 0 458ms
✅ TPPCredentialPersistenceTests 6 6 0 807ms
✅ TPPCredentialSnapshotCoherenceTests 3 0 0 57ms
✅ TPPCredentialSnapshotTests 8 8 0 17ms
✅ TPPCredentialsCoverageTests 9 9 0 76ms
✅ TPPCredentialsTests 26 26 0 127ms
✅ TPPCrossLibrarySignOutTests 6 6 0 873ms
✅ TPPDRMFailureCredentialPreservationTests 4 4 0 481ms
✅ TPPErrorLoggerTests 27 27 0 273ms
✅ TPPIdleSignOutRegressionTests 13 13 0 1.79s
✅ TPPJWKConversionTest 1 1 0 32ms
✅ TPPKeychainManagerTests 5 5 0 66ms
✅ TPPLastReadPositionPosterTests 13 13 0 406ms
✅ TPPLastReadPositionSynchronizerIntegrationTests 5 5 0 34ms
✅ TPPLastReadPositionSynchronizerTests 23 23 0 109ms
✅ TPPLastReadPositionSynchronizer_BehaviorDocumentationTests 5 5 0 25ms
✅ TPPLastReadPositionSynchronizer_BookLocationTests 9 9 0 37ms
✅ TPPLastReadPositionSynchronizer_ConcurrencyTests 3 3 0 185ms
✅ TPPLastReadPositionSynchronizer_ReadiumBookmarkTests 9 9 0 293ms
✅ TPPLastReadPositionSynchronizer_SyncLogicTests 10 10 0 51ms
✅ TPPLastReadPositionSynchronizer_WriterDelegationTests 4 4 0 56ms
✅ TPPLoginNoActivationTests 3 3 0 649ms
✅ TPPMainThreadCheckerTests 4 4 0 23ms
✅ TPPMigrationManagerTests 15 15 0 127ms
✅ TPPNetworkExecutorAPITests 14 14 0 199ms
✅ TPPNetworkExecutorStubbedTests 17 17 0 231ms
✅ TPPNetworkExecutorTests 3 3 0 24ms
✅ TPPNetworkResponderAuthCoordinatorTests 5 5 0 111ms
✅ TPPNetworkResponderTests 12 12 0 71ms
✅ TPPOPDSAcquisitionPathTests 5 5 0 38ms
✅ TPPOPDSEntryTests 5 5 0 30ms
✅ TPPOPDSFeedTests 3 3 0 333ms
✅ TPPOPDSGroupSwiftTests 3 3 0 10ms
✅ TPPOPDSLinkTests 7 7 0 180ms
✅ TPPOpenSearchDescriptionExpandedTests 10 10 0 175ms
✅ TPPOpenSearchDescriptionTests 1 1 0 2ms
✅ TPPPDFDocumentMetadataTests 15 15 0 156ms
✅ TPPPDFDocumentTests 8 8 0 30ms
✅ TPPPDFLocationCoverageTests 7 7 0 62ms
✅ TPPPDFLocationTests 10 10 0 32ms
✅ TPPPDFPageBookmarkTests 9 9 0 111ms
✅ TPPPDFPageTests 5 5 0 21ms
✅ TPPPDFReaderModeTests 6 6 0 19ms
✅ TPPPerAccountIsolationTests 8 0 0 339ms
✅ TPPPreferredAuthSelectionTests 8 8 0 870ms
✅ TPPProblemDocumentCacheManagerTests 12 12 0 64ms
✅ TPPProblemDocumentTests 21 21 0 63ms
✅ TPPReaderAppearanceTests 4 4 0 19ms
✅ TPPReaderBookmarksBusinessLogicTests 12 12 0 2.52s
✅ TPPReaderBookmarksReadinessTests 2 2 0 331ms
✅ TPPReaderFontTests 4 4 0 17ms
✅ TPPReaderPreferencesLoadTests 3 3 0 18ms
✅ TPPReaderSettingsTests 28 28 0 245ms
✅ TPPReaderTOCBusinessLogicTests 15 15 0 3.15s
✅ TPPReaderTOCFlattenTests 2 2 0 1.02s
✅ TPPReadiumBookmarkLocationMatchingTests 5 5 0 57ms
✅ TPPReadiumBookmarkTests 21 21 0 227ms
✅ TPPReauthenticatorMockTests 2 2 0 8ms
✅ TPPReauthenticatorTests 4 4 0 194ms
✅ TPPReturnPromptHelperTests 5 5 0 33ms
✅ TPPSAMLCookieExpirationTests 7 7 0 47ms
✅ TPPSAMLFlowTests 10 10 0 271ms
✅ TPPSAMLReauthFlowTests 2 2 0 260ms
✅ TPPSAMLRegressionTests 4 4 0 410ms
✅ TPPSAMLSignInTests 26 26 0 3.43s
✅ TPPSAMLStateIsolationTests 4 4 0 208ms
✅ TPPSAMLStateMachineTests 6 6 0 659ms
✅ TPPSettingsTests 6 6 0 4.47s
✅ TPPSignInAdobeSkipTests 14 14 0 1.75s
✅ TPPSignInAuthStateTransitionTests 3 3 0 397ms
✅ TPPSignInBusinessLogicExtendedTests 58 58 0 6.86s
✅ TPPSignInBusinessLogicOAuthTests 11 11 0 1.21s
✅ TPPSignInBusinessLogicSignOutTests 11 11 0 1.77s
✅ TPPSignInBusinessLogicStateMachineTests 10 10 0 1.70s
✅ TPPSignInBusinessLogicTests 21 21 0 3.77s
✅ TPPSignInBusinessLogicTokenFlowTests 3 3 0 362ms
✅ TPPSignInBusinessLogicValidationCallbackOrderTests 2 2 0 1.40s
✅ TPPSignInErrorHandlingTests 2 2 0 496ms
✅ TPPSignInProfileDocEdgeCaseTests 3 3 0 1.22s
✅ TPPSignedInStateProviderTests 3 3 0 28ms
✅ TPPUserAccountAuthStateTests 6 6 0 73ms
✅ TPPUserAccountGapTests 4 4 0 19ms
✅ TPPUserAccountIsolationLintTests 3 3 0 1.33s
✅ TPPUserAccountTestFactoryTests 7 0 0 57ms
✅ TPPUserFriendlyErrorTests 11 11 0 55ms
✅ TPPUserNotificationsTests 10 10 0 163ms
✅ TPPXMLSwiftTests 16 16 0 175ms
✅ TPPXMLTests 3 3 0 11ms
✅ TearDownRequiredLintTests 5 5 0 2.40s
✅ TestAppContainerFactoryTests 5 5 0 41ms
✅ TimeEntryTests 3 3 0 42ms
✅ TokenRefreshAndRetryQueueTests 9 9 0 6.38s
✅ TokenRefreshIntegrationTests 2 2 0 141ms
✅ TokenRefreshInterceptorAuthCoordinatorTests 8 8 0 4.33s
✅ TokenRefreshInterceptorTests 24 24 0 7.27s
✅ TokenRefreshOnForegroundTests 10 10 0 2.91s
✅ TokenRefreshTests 25 25 0 155ms
✅ TokenRequestCredentialGuardTests 13 13 0 204ms
✅ TokenRequestTests 11 11 0 411ms
✅ TokenResponseTests 21 21 0 874ms
✅ TriageBotKeyAdminTests 4 4 0 10ms
✅ TypographyPresetTests 21 21 0 204ms
✅ TypographyServiceTests 31 31 0 1.33s
✅ TypographySettingsViewModelTests 27 27 0 741ms
✅ UIAlertCACommitGuardTests 8 8 0 391ms
✅ UIColor_NYPLAdditionsTests 1 1 0 10ms
✅ URLBackupExclusionTests 3 3 0 19ms
✅ URLExtensionTests 16 16 0 132ms
✅ URLExtensionsTests 6 6 0 26ms
✅ URLRequestExtensionsCoverageTests 3 3 0 12ms
✅ URLRequestExtensionsTests 11 11 0 56ms
✅ URLRequestNYPLAdditionsTests 11 11 0 43ms
✅ URLRequest_NYPLTests 1 1 0 4ms
✅ URLResponseAuthenticationTests 10 10 0 31ms
✅ URLResponseNYPLTests 14 14 0 51ms
✅ URLSessionCredentialStorageTests 3 3 0 42ms
✅ URLSessionStubbingResetTests 2 2 0 11ms
✅ URLTypeTests 2 2 0 33ms
✅ URLValidationTests 5 5 0 54ms
✅ UnifiedOPDSServiceStateMachineTests 2 2 0 417ms
✅ UserAccountPublisherAuthStateTests 5 5 0 76ms
✅ UserAccountPublisherTests 14 14 0 463ms
✅ UserAccountValidationTests 11 11 0 1.75s
✅ UserDefaultsIsolationLintTests 2 2 0 985ms
✅ UserProfileDocumentTests 7 7 0 75ms
✅ UserRetryTrackerTests 10 10 0 150ms
✅ XCTestCase_testUserDefaultsTests 3 3 0 45ms
✅ iPadOnMacRMSDKGuardTests 7 7 0 24ms
Failed Tests (click to expand)
Palace.PalaceTests
HoldsViewModelTests.testFilterBooks_WithTitleMatch_ReturnsMatchingBooks

📊 Testing Coverage Breakdown

Unit Test Line Coverage (testable surfaces): 49.1%

Total coverage incl. UI/lifecycle: 47.2% (18 files excluded from testable denominator — see scripts/coverage-exclude.json)

Target Lines Covered
Palace.app 47.2%

Why two coverage numbers? Testable coverage subtracts files that can't be exercised from xcodebuild — SwiftUI views, UIKit VCs, lifecycle (see scripts/coverage-exclude.json) — so raising it means more testable logic is tested, not that we shipped less UI. Total coverage is kept for continuity. The excluded paths are covered by simdrive E2E journeys (see chaos-replay-on-pr.yml).


🔗 Interactive HTML Report | CI Run Details

📦 Downloadable Artifacts
Artifact Description
test-report 📄 Markdown + HTML reports
test-data 📊 JSON data for tooling
test-results 🔍 Full xcresult (open in Xcode)

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.

1 participant