Delete the document a failed save could not write (#250)
#226 proved D4's premise -- SAF hands back a document reporting exactly zero bytes, so destinationIsKnownEmpty can answer true -- and then drove the success path, where publish's catch is never entered. So deletePartialOutput had still never run against a real DocumentsProvider; its only assertions were OutputPublisherPublishTest's, against FakeSafProvider under Robolectric. That is the same "asserted only against a fake built to match it" shape #226 was filed to break, one layer down. RecordingPublisher.failOpen makes openDestination return null, which publish turns into error("Could not open destination for writing") AFTER its size probe has run -- so the catch is reached with destinationWasEmpty true on a document DocumentsUI created seconds earlier. Null rather than a throw because openDestination's KDoc says a provider that is present and declines is the half no fake can produce on demand, so that arm is also taken for the first time. Mutation, measured: delete the deletePartialOutput call and this test fails with "publish did not delete the document it could not write". Nothing anywhere went red for that line before. TWO DEAD ACCESSORS #226 LEFT, and the reason is the same one: FixtureDocumentsProvider is declared by the test APK and runs in org.libremediaconverter.test; instrumentation runs in the app's process. A static in the provider is a different object from the one a test can see, so deletedDocumentIds() would have read empty forever, and reset(File) deletes under a filesDir that is not the provider's. Both are removed rather than worked around. That is E7's process wall from a third side, after ACTION_OPEN_DOCUMENT and ActivityScenario. The oracle is the document instead, which crosses the boundary because the app holds a URI grant for it. Still the path rather than the artefact: the size query proves the document existed and was empty moments earlier, and one that no longer answers a query is one something deleted. CLEANUP IS IN TEARDOWN, and the mutation run is why. A failed save keeps its staged file deliberately, so this test ends with a finished job for the next launch to reattach to; its sibling then opened on Converted with no "Choose file" to tap. The first fix tapped Start over at the end of the test body, which does not run when the test fails -- so the mutation run turned one real failure into two, the second looking like an unrelated flake. One cause must produce one red test. Baseline 6 -> 7, with the derived counts in CLAUDE.md, the marker KDoc and status_check.yml moved in the same diff. 71 - 7 is 64, the same gating figure for the third consecutive time, which is how that paragraph goes stale unnoticed. Verified: three API 34 runs at 71/71 failed=0, the mutation red on the right assertion, and the full gate plus pinned actionlint green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -272,13 +272,13 @@ jobs:
|
||||
# has to begin after them, not between them. The name is stale and kept:
|
||||
# read .github/scripts/e2e-run.sh's header, which carries the measurements.
|
||||
#
|
||||
# notAnnotation below keeps six tests off this row. SafPickerRoundTripTest's
|
||||
# notAnnotation below keeps seven tests off this row. SafPickerRoundTripTest's
|
||||
# PICKER test was measured on 2026-08-24 as passing here and was left on the
|
||||
# leg; four gating logcats read on 2026-09-05 show it aborting system_server
|
||||
# from the task-snapshot path on every single run, pass or fail, which is what
|
||||
# had been failing unrelated PRs (#108). All THREE of that class's tests now
|
||||
# carry the marker -- the save through the picker (#226) joined on 2026-09-06
|
||||
# by inheritance rather than measurement, since it opens the same picker.
|
||||
# had been failing unrelated PRs (#108). All FOUR of that class's tests now
|
||||
# carry the marker -- the two saves through the picker (#226, #250) joined on
|
||||
# 2026-09-06 by inheritance rather than measurement, since they open the same picker.
|
||||
# docs/api-37-emulator-crash.md has the timings and the correction, and
|
||||
# FailsOnEmulatorApi37.kt has why the third one cannot be measured here.
|
||||
#
|
||||
@@ -290,11 +290,11 @@ jobs:
|
||||
# docs/api-37-emulator-crash.md measures 37.0 rev 6 and 37.1 rev 8 side
|
||||
# by side, so pinning 37.0 is a decision, not a constraint.
|
||||
#
|
||||
# notAnnotation removes the six tests that cannot be RUN on this image; they
|
||||
# notAnnotation removes the seven tests that cannot be RUN on this image; they
|
||||
# run in the advisory job below, off the same marker so they cannot end up
|
||||
# in both or neither. "Cannot be run" rather than "do not pass" is deliberate:
|
||||
# four fail outright, one of those aborts the framework on its way down, and on
|
||||
# the advisory leg the two picker tests behind it never report at all.
|
||||
# the advisory leg the three picker tests behind it never report at all.
|
||||
# docs/api-37-emulator-crash.md has the measurements.
|
||||
- label: "37"
|
||||
api-level: "37.0"
|
||||
|
||||
Reference in New Issue
Block a user