Cancelling a running native session is not executed by any test, in any of the three engines #224
Closed
opened 2026-09-06 02:52:59 +00:00 by JMR-dev
·
1 comment
No Branch/Tag Specified
main
fix/102-picker-back-press-overshoot
fix/268-saf-picker-determinism
feat/ogg-vorbis-libvorbis
feat/expedited-conversion-work
fix/gate-cache-in-worktrees
chore/gate-runs-shellcheck
test/publish-delete-arm-real-provider
docs/e8-instrumented-coverage
docs/api37-carrier-count-drift
docs/e7-second-constraint
test/publish-to-a-real-saf-destination
fix/api37-report-match-line
fix/api37-task-snapshot-crash
test/join-failure-message-on-device
docs/e2e-read-findings-e7
test/cancelling-a-running-export
test/reattach-to-a-running-job
test/content-uri-reaches-ffmpeg
fix/launcher-wiring-waits-for-the-pick
fix/cancel-tests-need-a-slower-encode
test/cancelling-a-running-join
test/cancelling-a-running-session
test/notification-cancel-action
test/ffmpeg-progress-is-observed
test/fallback-asserts-the-path
test/flac-and-opus-assert-their-format
docs/e2e-read-findings
docs/wave4-coverage-numbers
fix/injectable-startup-sweep-scope
test/session-outcome-seam
test/launcher-callback-identity
test/theme-follows-system-dark
test/audio-drop-arm
fix/rotation-waits-for-recreation
fix/convert-guards-on-ready
test/retry-save-mime
test/hardware-progress-reaches-workmanager
test/ffprobe-mapping-seam
test/device-codec-enumeration-seam
test/unknown-container-row
test/null-message-fallbacks
test/cancel-reaches-workmanager
docs/coverage-wave3-recovery
test/concat-engine-seam
docs/coverage-wave3
test/mediaprobe-merge-seam
test/adaptive-shell-wiring
test/aac-audio-args
test/notification-progress-text
test/media3-muxer-guard
test/hardware-fallback-and-cancellation
test/unprobeable-join-clip
test/one-branch-outcomes
test/foreground-type-regimes
fix/bound-wedge-diagnostics
docs/coverage-wave2
test/screen-wiring
test/viewmodel-setters
test/join-state-mapping
test/conversion-state-mapping
test/dedupe-user-messages
fix/restore-stack-merges
test/refused-jobs
test/concatworker-failure-arms
test/container-capabilities-audio
test/readspec-enum-fallbacks
test/outputpublisher-seams
test/mediaprobe-track-seam
test/outputpublisher-partial-branches
test/fake-provider-scaffolding
docs/coverage-read-findings
chore/gitignore-kotlin
test/bound-the-hangs
docs/coverage-remeasure
ci/baseline-counter-precision
fix/invalid-suggestion-chip
ci/wedged-leg-report
fix/reattachment-overwrites-pick
test/theme-live-branches
fix/failed-save-retry
fix/empty-composition-crash
ci/advisory-failure-report
docs/seven-run-counts
test/release-permission-guard
ci/build-workflow-permissions
docs/api37-point-release
docs/benchmark-populate-path
fix/dead-assertion-probe-test
ci/actionlint
test/device-codecs-encode-consequence
fix/sdkmanager-pipefail
docs/readme-restart-claim
fix/saf-picker-root-discovery
fix/probe-dispatcher-seam
test/media3engine-mime-tables
test/mediaprobe-pure-helpers
fix/codec-vocabulary-drift
docs/robolectric-rationale-correction
docs/api37-advisory-counts
test/r38-8-saf-e2e
test/r38-7-join-states
test/r38-6-conversion-states
test/r38-5-state-seam
fix/jacoco-robolectric-coverage
docs/instrumented-tests-correction
test/r38-2-filecard
test/r38-4-advanced-picker
test/r38-3-pickers
tools/file-issue-script
tools/api-37-emulator
fix/review-app-gaps
docs/review-corrections
No results found.
Labels
Clear labels
above-cut
accessibility
backlog
bug
confirmed
documentation
duplicate
enhancement
good first issue
help wanted
invalid
plausible
question
sev:high
sev:low
sev:medium
wontfix
Worked autonomously overnight: local, JVM-verifiable, no product decision
Barrier affecting people with disabilities
Held for manual review: product/UX call, CI/workflow, hardware, or unverifiable here
Something isn't working
Reviewer demonstrated the defect
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Good for newcomers
Extra attention is needed
This doesn't seem right
Reviewer could not fully demonstrate it; treat as unproven
Further information is requested
High severity
Low severity
Medium severity
This will not be worked on
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: JMR-dev/LibreMediaConverter#224
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Filed from the 2026-09-05 e2e read of the instrumented suite on
main@4b02294.Cancelling a conversion or join while it is running is not executed by any test on any source set. Three engines carry the code and none of it runs.
The lines
Also the
if (!cont.isActive) returnearly exit inMedia3Engine.pollProgress(:178).Why nothing covers it
grepforcancelacrossapp/src/androidTest/javareturns onlyWorkManager.cancelWorkById— and every call site cancels work that is queued or already finished, never running:ReattachOnLaunchTest.doesNotResurrectAConversionTheUserCancelledcancels a job enqueued with a one-hour initial delay, so it never starts.ReattachOnLaunchTest.aConversionRequestIsFindableByItsWorkerClassNamecancels immediately after enqueue.On the JVM,
WorkerCancellationTestandHardwareFallbackTest's cancellation case both drive fakes — aSoftwareTranscoderthat records the call. Nothing has ever asked a real native session to stop.This is
docs/defect-audit.mdD10's forcing condition, recorded as never run.Why it is worth a device test
It is the one path where cancelling wrong is silently expensive rather than loudly broken.
FFmpegKit.cancel(sessionId)takes a session id, and getting it wrong — cancelling session 0, or a stale id — leaves the native process encoding to completion while the UI says the job is cancelled. The battery and thermal cost is real and nothing would report it.The staged partial is deleted on the same path (
output.delete()), so a missed cancel also leaks a full-size file intocacheDirthat only the 24-hour sweep will collect.Shape
Headless — no Activity, no Compose. Enqueue through the real worker, wait for the first progress callback, cancel, then assert both:
WorkInfo.State.CANCELLED, andFlake note, and it decides the fixture. The committed clips are 3 s and a
veryfastFFmpeg encode of320x240finishes in a few hundred milliseconds —HardwareFallbackTestcompleted a full transcode in 448 ms on the API 34 leg. Cancelling from a timer would race. Two ways out, both already available:QualityTier.BEST, which routes to FFmpeg with-preset mediumand runs on every leg already.Mutation: drop the
FFmpegKit.cancel(sessionId)call and keep theoutput.delete(). The coroutine still completes as cancelled, so a test that only checksCANCELLEDstays green — which is why the staged-file assertion is the one that bites. Verify both halves separately.Not proposed
Cancelling the Media3 export in the same test.
transformer.cancel()has to run on the engine'sHandlerThreadand its failure mode is different (a hung continuation rather than a runaway process); it deserves its own case, and on emulators the Media3 path is only reachable by pinningDeviceCodecs.PERMISSIVEthe wayForcedFailureTestdoes — which puts it behind the same problem as the fallback ticket.Splitting is cheap; conflating them would make one test that fails for two unrelated reasons.
The
FFmpegEnginehalf is done — PR #236, merged asad2a75d. Leaving this open for the two engines it does not cover.Two findings from doing it are worth having here rather than only in the commit, because both would otherwise be rediscovered by whoever takes
ConcatEngine:1. The output-file assertion cannot fail, so it would be a vacuous test. The obvious check — "the partial output is gone after cancelling" — passes whether or not the cancel reaches the session.
invokeOnCancellationdeletes the path, and on POSIX unlinking a file ffmpeg still holds open leaves ffmpeg writing to the unlinked inode; the path stays gone either way. RemovingFFmpegKit.canceland keepingoutput.delete()passes it every time.What separates them is the session's own verdict —
ReturnCode.isCancel(session.getReturnCode()). A cancelled session ends with the cancel code; a completed one does not. It is a fact about the session rather than about timing.Note for
ConcatEngine: it has nooutput.delete()at all (ConcatEngine.kt:80iscont.invokeOnCancellation { FFmpegKit.cancel(session.getSessionId()) }). Whether that is deliberate is a separate question from this ticket, but it means the file-based assertion is even less available there.2. Cancelling from the first progress callback loses the race. That is what this ticket suggested, and it was tried first. It failed with
state=COMPLETED rc=0: every committed fixture is 2–3 s at 320×240, and the encode finishes before the first statistics callback is delivered and acted on. The progress callback proves the session is running but arrives too late to interrupt it.FFmpegKit.listSessions()shows the sessionRUNNINGfar earlier, so wait on that instead.QualityTier.BESThelps for the same reason —-preset mediumleaves more of the encode ahead of the cancel.What is left
ConcatEngine.kt:80— same shape, same approach should work; see the note above about the missing delete.Media3Engine.kt:120-123—transformer.cancel()on the engine'sHandlerThread. Still deserves its own case, and #223 has since added a reason: reaching the Media3 path on an emulator needs the device profile pinned, and pinning it makes the export behave differently (the goldfish decoder handles the 4:4:4 fixture that a real device rejects). Read #223's class KDoc before designing it.Verification standard used, for consistency: four consecutive green local API 34 runs plus a mutation that must go red, and all five CI legs green.