Skip to content

test(ios): delete a duplicate processor mock that hid a failed write - #689

Open
jkmassel wants to merge 1 commit into
docs/media-field-decode-invariantfrom
test/media-mock-cleanup
Open

jkmassel wants to merge 1 commit into
docs/media-field-decode-invariantfrom
test/media-mock-cleanup

Conversation

@jkmassel

@jkmassel jkmassel commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Stacked on #688.

What?

Deletes the TranscodingProcessor test mock and points its one call site at the existing ResizingProcessor. Drops @unchecked Sendable from ThrowingUploader. Test-only: one file.

Why?

TranscodingProcessor was a second copy of ResizingProcessor — both return .processed(_, mimeType: "video/mp4", filename: "clip.mp4"), and each had exactly one call site — and it was the weaker copy on two counts.

A failed write still produced a passing test. It wrapped its write in try? and returned .processed(<URL>, …) either way, so when the write failed the test asserted against a file that was never created, and passed. ResizingProcessor uses try, so the same failure surfaces as a thrown error instead of a green run.

It wrote to a fixed path it did not own. TranscodingProcessor wrote to $TMPDIR/clip.mp4 — constant across runs and shared process-wide. The server deletes whatever URL a processor hands back (that cleanup lives inside processAndUpload so the throw paths are covered too), so the mock was pointing the server at a shared filename to unlink. ResizingProcessor writes processed-<UUID> into the same managed uploads directory the server already created for the incoming file, so the file the server deletes is one the processor made for that request.

ThrowingUploader did not need @unchecked Sendable. It has no stored properties — a nested struct Failure: Error {} and one method — and MediaUploader already refines Sendable, so the checked conformance holds on its own. The escape hatch is only needed by the mocks carrying NSLock-guarded state (ProcessOnlyProcessor, DeclineByMetadataProcessor, ResizingProcessor, MockInternalMediaClient). Carrying it on a stateless one normalizes it as boilerplate, which is how an unsynchronized property gets added later without a diagnostic. ContentTypeDeleteClient keeps it — it subclasses InternalMediaClient, itself an @unchecked Sendable class, and must restate the conformance.

How?

ios/Tests/GutenbergKitTests/Media/MediaUploadServerTests.swift: TranscodingProcessor deleted; processesForHostReleasedProcessor — "still processes for a processor the host has dropped its reference to" — now builds a ResizingProcessor. @unchecked Sendable dropped from ThrowingUploader.

The swap changes nothing the test admits or asserts. TranscodingProcessor declared handlesFile returning true; ResizingProcessor omits it and inherits the protocol default in MediaHandlers.swift, which also returns true, so the upload still clears the admission gate. Both mocks return the same processed metadata, so all four #expects stand as written — including the one carrying the test's meaning, !mockUploader.passthroughUploadCalled.

The test is still load-bearing, confirmed by mutation rather than by assertion. Holding the handler's processor weakly fails all four expectations, !passthroughUploadCalled among them. Reaching that mutation took an @unchecked Sendable weak box: MediaProcessor is not class-bound, so weak var processor: (any MediaProcessor)? does not compile, and Handler must stay Sendable, so a bare weak var … : AnyObject? does not either. Two type-system barriers now stand between this code and the regression the test was written for; the test is the third.

Testing Instructions

No reviewer steps — the diff is test-only and the suite below exercises it.

  • swift test --filter MediaUploadServerTests — 38 tests in 3 suites, green
  • swift test — 983 green: GutenbergKitTests 587 in 35 suites, GutenbergKitHTTPTests 396 in 22 suites
  • swift build --build-tests — clean, and confirms ThrowingUploader satisfies Sendable without the annotation
  • make lint-swift — no violations
  • Mutation check — processor held weakly, all four expectations fail

Related

Follow-up from a review of #625, which sits upstream in this stack and is still open. Two other findings from that review are fixed there rather than here: stop() now drops the listener's connection handler, so teardown releases what the handler captured on the caller's thread instead of Network.framework's queue; and the #WeakMutability warning.

#625 also deleted EditorViewControllerMediaLifetimeTests, vacuous for the same class of reason since it never loaded the editor, and with it LifetimeProbeDelegate. This mock was the remainder.

@jkmassel jkmassel added [Type] Automated Testing Testing infrastructure changes impacting the execution of end-to-end (E2E) and/or unit tests. iOS labels Sep 17, 2026
@jkmassel
jkmassel added this pull request to stack #690 September 17, 2026 18:33
@wpmobilebot

wpmobilebot commented Sep 17, 2026

Copy link
Copy Markdown

XCFramework Build

This PR's XCFramework is available for testing. Add the following to your Package.swift:

.package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/689")

Built from c80ff5b

@jkmassel
jkmassel force-pushed the test/media-mock-cleanup branch from e6493f1 to 4b1df73 Compare September 17, 2026 20:31
@jkmassel
jkmassel force-pushed the test/media-mock-cleanup branch from 4b1df73 to c822f7c Compare September 17, 2026 22:04
@jkmassel
jkmassel requested review from dcalhoun and removed request for dcalhoun September 17, 2026 22:05
@jkmassel jkmassel changed the title test(ios): reuse ResizingProcessor instead of a second transcoding mock test(ios): delete a duplicate processor mock that hid a failed write Sep 17, 2026
`TranscodingProcessor` duplicated `ResizingProcessor` — same
`.processed(_, mimeType: "video/mp4", filename: "clip.mp4")` result, one call
site — and was the weaker of the two. It wrote to a fixed `$TMPDIR/clip.mp4`
instead of a per-call UUID path inside the managed upload directory, and
swallowed the write with `try?`, so a failed write still returned
`.processed(<nonexistent URL>, …)` and the test passed green against a file
that never existed. `ResizingProcessor` uses `try` and a unique path.

Also drops `@unchecked Sendable` from `ThrowingUploader`, which has no stored
properties and so satisfies `MediaUploader`'s inherited `Sendable` conformance
on its own. The escape hatch is only needed by the mocks holding
`NSLock`-guarded state; carrying it on a stateless one normalizes it as
boilerplate, which is how an unsynchronized property gets added later without
a diagnostic. `ContentTypeDeleteClient` keeps it — it subclasses
`InternalMediaClient`, itself an `@unchecked Sendable` class, and must restate
the conformance.
@jkmassel
jkmassel force-pushed the test/media-mock-cleanup branch from c822f7c to c80ff5b Compare September 17, 2026 22:30
@jkmassel
jkmassel requested a review from dcalhoun September 17, 2026 22:37

@dcalhoun dcalhoun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Captured one finding from Claude that is worth considering.


/// A processor that transcodes, used to check the server holds it across the whole
/// request rather than re-reading a reference the host may have dropped.
private final class TranscodingProcessor: MediaProcessor, @unchecked Sendable {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finding from Claude:

Consolidating onto one mock is right, but this deletes the better name. The survivor, ResizingProcessor (:1196), returns .processed(_, mimeType: "video/mp4", filename: "clip.mp4") — a transcode, not a resize — and processesThenDelivers feeds it photo.jpg/image/jpeg. Android's equivalent mock is still TranscodingProcessor (MediaUploadServerTest.kt:1002), so a repo-wide grep for that name now returns Android only.

Worth catching while you're here: 42bdb8d2 inserted ValueTypeProcessor between the transcode doc comment and the class it described, so :1184 now stacks two doc comments on ValueTypeProcessor and leaves ResizingProcessor with none.

Renaming the survivor to TranscodingProcessor and re-attaching that comment would make this a pure consolidation and restore parity with Android.

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

Labels

iOS [Type] Automated Testing Testing infrastructure changes impacting the execution of end-to-end (E2E) and/or unit tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants