Conversation
A bad paste/merge left mimeTypeToFormat_mapsUniversalFormats defined three times, two of them as local functions nested inside the other two @test methods. Braces were balanced so it compiled, but a local function annotated @test compiles to a synthetic exterior$interior method that JUnit4's runner rejects, taking the whole class down with InvalidTestClassError. Un-nests the three functions back to class level. No assertion changed: verified all 16 by hand against the current mimeTypeToFormat() — they were already correct, just never executed. mimeTypeToFormat() had zero coverage; now it has 3 passing tests, exercised for the first time. check-baseline.sh / BASELINE-TESTS.md updated to drop this from the known-failures list (local docs, gitignored — not part of this diff).
Adds a descriptor-based overload for reading audio metadata (title,
artist, artwork, lyrics, ...) without needing a filesystem File — the
SAF / downloaded-file path, where a content:// document isn't
guaranteed to have one (C11 of the offline-downloads plan).
Extracted the TagLib read/field-mapping logic (~90 lines) out of
read(File) into a shared private buildAudioMetadata(), parameterized
by a dupFd: () -> Int lambda, instead of duplicating it across both
overloads (GEN-DES-08 — it's the same TagLib property-map -> domain
field mapping, not superficial similarity).
Both overloads now go through pfd.dup().detachFd() for every TagLib
call, including the last one. read(File) used to detachFd() its own
last-owned descriptor directly, which only worked because nothing
touched it afterwards inside its own use {}. The new overload receives
a descriptor it does not own, so that shortcut is no longer safe in
general — folding both paths through the same always-dup() helper
removes the class of bug rather than special-casing it.
No JAudioTagger fallback on the pfd overload: AudioFileIO.read() needs
a real File, which a SAF document may not have. read(File) keeps the
full fallback for the app-private storage backend, which does have
one. Documented as a known, accepted gap on the new overload's KDoc.
Self-review (code-review skill, high effort) surfaced 6 findings; two
applied: both read() overloads now use the existing
PerformanceMetrics.time() helper instead of hand-rolled nanoTime/
finally timing (was duplicated, that helper already exists and is
used elsewhere e.g. MediaItemBuilder.kt), and read(pfd) gained an
optional `label` param so VERBOSE logs of concurrent descriptor reads
stay distinguishable instead of all saying "descriptor". A test
force-unwrap (fromFile.artwork!!) without an assertNotNull guard was
also fixed. Two findings accepted, not changed, and documented: the
refactor adds one extra dup()+close() per artwork read on read(file)'s
pre-existing hot path (correctness-neutral, negligible cost, traded
for removing the fragile bare-detachFd() shortcut structurally); and
dupFd/jAudioTaggerFallbackFile are two independent buildAudioMetadata
params with no compiler-enforced pairing — a sealed MetadataSource
type would close that gap but is more machinery than two call sites
warrant today (GEN-DES-13).
Testing: TagLib is native and nothing in this repo's JVM tests
exercises it (verified — LyricsRepositoryImplTest only covers a pure
function around it), so this needs a device (D-22). 3 new androidTest
cases against a 5 KB tagged MP3 (ffmpeg-generated, embedded ID3 tags +
cover), run on Pixel_10 (AVD): read(pfd) matches read(file), the
caller's descriptor stays valid afterwards, and readArtwork=false
skips the embedded cover. All 3 pass.
JVM baseline unaffected: 388 tests (4 pre-existing failures once P.7 is
applied — this branch is cut from it), assembleDebug succeeds.
5f637ab to
80742a6
Compare
Self-review (code-review, high effort)Ran a full self-review of this diff before asking for a human one. 6 findings, 3 applied, 2 documented as accepted trade-offs, 1 already covered by this PR's own KDoc: Applied:
Accepted, not changed, documented in the commit message:
Already covered: the review flagged that Re-verified after the fixes: |
|
Closing for now — reorganizing how this work is staged. It'll go through our fork first and we'll propose it upstream again, possibly bundled differently, once the larger feature it's part of is further along. Not a rejection, just a process change on our side. |
Stacked on #2821
This branch is cut from
chore/p7-fix-audiometautilstest(#2821), notmasterdirectly — it touches the same area (metadata reading) andshouldn't land without that test class actually running. GitHub can't
target a non-existent branch on the upstream repo, so this PR's diff
includes #2821's commit for now; it'll shrink to just this change once
#2821 merges into
master. One of the small independent fixes listed in#2813.
What
Adds a descriptor-based overload to
AudioMetadataReaderfor reading audiometadata (title, artist, artwork, lyrics, ...) without needing a filesystem
File— the SAF / downloaded-file path, where acontent://document isn'tguaranteed to have one.
The trap this avoids
The existing
read(File)opens its own descriptor and callsfd.dup().detachFd()for most TagLib calls, but a barefd.detachFd()(no
dup()) for the last one. That's harmless today only because nothingtouches the descriptor afterwards inside its own
use { }. A descriptorhanded in by a caller who keeps using it is a different story:
detachFd()steals the underlying native descriptor, and the caller's ownclose()would find it already gone.Change
of
read(File)into a shared privatebuildAudioMetadata(), parameterizedby a
dupFd: () -> Intlambda — avoids duplicating that mapping logicacross two functions.
dup().detachFd()for every TagLib call,including the previously-bare last one.
read(File)'s old shortcut isgone structurally, not just avoided in the new code.
read(pfd)never runs the JAudioTagger fallback (it needs a realFile,which a SAF document may not have) — documented as a known, accepted gap
on its KDoc.
read(File)keeps the full fallback.Testing
TagLib is a native library; nothing in this repo's JVM tests calls into it
(checked — the one existing TagLib-adjacent test,
LyricsRepositoryImplTest,only covers a pure function around it). This needs a device.
3 new
androidTestcases, run on aPixel_10AVD, against a 5 KB tagged MP3(ffmpeg-generated: ID3 title/artist/album + embedded cover):
read(pfd)returns the same metadata asread(file)for the same file.readArtwork = falseskips the embedded cover.All 3 pass. JVM baseline unaffected (388 tests, 4 pre-existing failures once
#2821 is applied),
assembleDebugsucceeds.