C6 — Four partial branches in OutputPublisher, three of them guarding a delete #140

Closed
opened 2026-08-27 03:06:52 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-08-27 03:06:52 +00:00 (Migrated from github.com)

Child 6 of 7 decomposing #132 — item 6 there. Depends on C0 (#134) for the cursor half; the rest is plain filesystem.

Why this exists

app/src/main/java/org/libremediaconverter/convert/OutputPublisher.kt:192-199, :234-237, :256-271

Four partial branches, three of them guarding a delete.

1. destinationIsKnownEmpty — :197, 3 of 8 branches cold (needs C0 (#134))

val size = row.getColumnIndex(OpenableColumns.SIZE)
size >= 0 && row.moveToFirst() && !row.isNull(size) && row.getLong(size) == 0L

Three short-circuits — no SIZE column, no row, a null value — and each must answer false. The
KDoc is unambiguous about why:

Every other answer … is false, because this decides whether a delete is allowed and "I could not
tell" must never authorise one.

This is the guard between a failed save and deleting a file the user already had.

2. discardStaged — :235, 2 of 4 branches cold

staged.parentFile returning null, and canonicalOrAbsolute()'s runCatching falling through to
absoluteFile (:271, whose failure path is 9 instructions never executed). OutputPublisherStagingTest
covers the path-climbing and outside-staging refusals; it never supplies a parentless file or a path
whose canonicalisation throws.

3. sweepStaging — :258, 1 of 2 branches cold

dir.listFiles() ?: return. One side is covered by the sweep tolerates a staging directory that does not exist yet; the other is not.

Scope

Item 1 goes in a new test or beside OutputPublisherPublishTest, driving C0 (#134)'s missing-column,
no-row and null-value states. Items 2 and 3 belong in OutputPublisherStagingTest, which already
owns discardStaged and the sweep and needs no scaffolding at all.

Take 2 and 3 first if C0 (#134) has not landed — they are independent of it, and splitting the work that
way keeps this child from being blocked in full.

Done means

Each of the three destinationIsKnownEmpty short-circuits asserted to return false and to leave
the destination undeleted after a failed publish. discardStaged returning false for a parentless
file. The sweep returning quietly when listFiles() gives null.

Mutation: change :197's size >= 0 && to size >= -1 && — the missing-column test must go
red, and it must go red on a deleted destination, which is the consequence that matters.

Not in scope

:174 (the openOutputStream null return) and :267 (the sweep's re-read race). Both need a seam
that does not exist yet and are children of #133.

_Child 6 of 7 decomposing #132 — item 6 there. **Depends on C0 (#134)** for the cursor half; the rest is plain filesystem._ ### Why this exists ``` app/src/main/java/org/libremediaconverter/convert/OutputPublisher.kt:192-199, :234-237, :256-271 ``` Four partial branches, three of them guarding a delete. **1. `destinationIsKnownEmpty` — `:197`, 3 of 8 branches cold** *(needs C0 (#134))* ```kotlin val size = row.getColumnIndex(OpenableColumns.SIZE) size >= 0 && row.moveToFirst() && !row.isNull(size) && row.getLong(size) == 0L ``` Three short-circuits — no `SIZE` column, no row, a null value — and **each must answer `false`**. The KDoc is unambiguous about why: > Every other answer … is false, because this decides whether a delete is allowed and "I could not > tell" must never authorise one. This is the guard between a failed save and deleting a file the user already had. **2. `discardStaged` — `:235`, 2 of 4 branches cold** `staged.parentFile` returning null, and `canonicalOrAbsolute()`'s `runCatching` falling through to `absoluteFile` (`:271`, whose failure path is 9 instructions never executed). `OutputPublisherStagingTest` covers the path-climbing and outside-staging refusals; it never supplies a parentless file or a path whose canonicalisation throws. **3. `sweepStaging` — `:258`, 1 of 2 branches cold** `dir.listFiles() ?: return`. One side is covered by `the sweep tolerates a staging directory that does not exist yet`; the other is not. ### Scope Item 1 goes in a new test or beside `OutputPublisherPublishTest`, driving C0 (#134)'s missing-column, no-row and null-value states. Items 2 and 3 belong in `OutputPublisherStagingTest`, which already owns `discardStaged` and the sweep and needs no scaffolding at all. **Take 2 and 3 first if C0 (#134) has not landed** — they are independent of it, and splitting the work that way keeps this child from being blocked in full. ### Done means Each of the three `destinationIsKnownEmpty` short-circuits asserted to return `false` *and* to leave the destination undeleted after a failed publish. `discardStaged` returning `false` for a parentless file. The sweep returning quietly when `listFiles()` gives null. **Mutation:** change `:197`'s `size >= 0 &&` to `size >= -1 &&` — the missing-column test must go red, and it must go red on a *deleted destination*, which is the consequence that matters. ### Not in scope `:174` (the `openOutputStream` null return) and `:267` (the sweep's re-read race). Both need a seam that does not exist yet and are children of #133.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#140