test(android): pin the extension and MIME tables

ExtensionMimeTablesTest fixes the outputs of extFromFileName,
extFromMimeType, SafDownloadHandler.normalizeExt and mimeTypeForExt, and
NativeFinalizationPolicy.normalizeExtension for every table entry plus
unknown, blank, uppercase and dotted inputs. The expected values come
from reading the code; the tables are not inverse maps, so each keeps
its own entries.

The two normalizers trim, lowercase with Locale.ROOT and add one leading
dot, with "" for blank input; checking the dot before or after
lowercasing cannot differ. normalizeExt now delegates to
normalizeExtension, which becomes internal. The test passed against
both original bodies before the delegation.
This commit is contained in:
zarzet committed 2026-10-19 04:58:25 +07:00
1 parent a19a528735
commit 202923fbd9
3 files changed
+149 -6

No files matched your search

@@ -462,7 +462,7 @@ internal object NativeFinalizationPolicy {
return trimmed
}
private fun normalizeExtension(extension: String?): String {
internal fun normalizeExtension(extension: String?): String {
val trimmed = extension?.trim().orEmpty()
if (trimmed.isEmpty()) return ""
val lower = trimmed.lowercase(Locale.ROOT)
@@ -632,11 +632,9 @@ object SafDownloadHandler {
}
}
internal fun normalizeExt(ext: String?): String {
val trimmed = ext?.trim().orEmpty()
if (trimmed.isEmpty()) return ""
return if (trimmed.startsWith(".")) trimmed.lowercase(Locale.ROOT) else ".${trimmed.lowercase(Locale.ROOT)}"
}
// Same result for every input; pinned by ExtensionMimeTablesTest.
internal fun normalizeExt(ext: String?): String =
NativeFinalizationPolicy.normalizeExtension(ext)
internal fun mimeTypeForExt(ext: String?): String {
return when (normalizeExt(ext)) {
@@ -0,0 +1,145 @@
package com.zarz.spotiflac
import org.junit.Assert.assertEquals
import org.junit.Test
// Pins the extension and MIME tables used by the SAF and finalizer paths.
// They are not inverse maps: "audio/ogg" names ".opus", while ".ogg" and
// ".opus" both map back to "audio/ogg", and ".aac" has no MIME entry.
class ExtensionMimeTablesTest {
@Test
fun extFromFileNameMatchesLowercaseSuffixesOnly() {
for ((input, expected) in listOf(
"song.m4a" to ".m4a",
"song.mp4" to ".mp4",
"song.aac" to ".aac",
"song.mp3" to ".mp3",
"song.opus" to ".opus",
"song.flac" to ".flac",
"song.ogg" to ".ogg",
"song.wave" to ".wav",
"song.wav" to ".wav",
"song.aiff" to ".aiff",
"song.aifc" to ".aifc",
"song.aif" to ".aif",
"song.dsf" to ".dsf",
"song.dff" to ".dff",
".flac" to ".flac",
"my.mp3.flac" to ".flac",
"song.wv" to "",
"song.cue" to "",
"song.lrc" to "",
"song.flac.tmp" to "",
"song.flac " to "",
"SONG.FLAC" to "",
"song.Mp3" to "",
"flac" to "",
"" to "",
" " to "",
)) {
assertEquals(input, expected, extFromFileName(input))
}
}
@Test
fun extFromMimeTypeMatchesExactTypesOnly() {
for ((input, expected) in listOf(
"audio/mp4" to ".m4a",
"audio/aac" to ".aac",
"audio/eac3" to ".m4a",
"audio/ac3" to ".m4a",
"audio/ac4" to ".m4a",
"audio/mpeg" to ".mp3",
"audio/ogg" to ".opus",
"audio/flac" to ".flac",
"audio/wav" to ".wav",
"audio/x-wav" to ".wav",
"audio/wave" to ".wav",
"audio/vnd.wave" to ".wav",
"audio/aiff" to ".aiff",
"audio/x-aiff" to ".aiff",
null to "",
"" to "",
" " to "",
"AUDIO/FLAC" to "",
" audio/flac" to "",
"audio/x-flac" to "",
"audio/opus" to "",
"audio/mp3" to "",
"application/octet-stream" to "",
)) {
assertEquals(input.toString(), expected, extFromMimeType(input))
}
}
@Test
fun normalizersTrimLowercaseAndAddOneLeadingDot() {
for ((input, expected) in normalizerCases) {
assertEquals(input.toString(), expected, SafDownloadHandler.normalizeExt(input))
assertEquals(input.toString(), expected, NativeFinalizationPolicy.normalizeExtension(input))
}
}
@Test
fun mimeTypeForExtNormalizesBeforeLookup() {
val octetStream = "application/octet-stream"
for ((input, expected) in listOf(
".m4a" to "audio/mp4",
"m4a" to "audio/mp4",
"M4A" to "audio/mp4",
".mp4" to "audio/mp4",
".mp3" to "audio/mpeg",
".opus" to "audio/ogg",
".ogg" to "audio/ogg",
".flac" to "audio/flac",
" FLAC " to "audio/flac",
".wav" to "audio/wav",
".aiff" to "audio/aiff",
".aif" to "audio/aiff",
".aifc" to "audio/aiff",
".lrc" to octetStream,
".aac" to octetStream,
".wave" to octetStream,
".dsf" to octetStream,
".dff" to octetStream,
".wv" to octetStream,
"..flac" to octetStream,
"audio/flac" to octetStream,
null to octetStream,
"" to octetStream,
" " to octetStream,
)) {
assertEquals(input.toString(), expected, SafDownloadHandler.mimeTypeForExt(input))
}
}
private val normalizerCases = listOf(
null to "",
"" to "",
" " to "",
"\t\n" to "",
"m4a" to ".m4a",
"mp4" to ".mp4",
"aac" to ".aac",
"mp3" to ".mp3",
"opus" to ".opus",
"ogg" to ".ogg",
"flac" to ".flac",
"wav" to ".wav",
"aiff" to ".aiff",
"aif" to ".aif",
"aifc" to ".aifc",
"lrc" to ".lrc",
".flac" to ".flac",
"FLAC" to ".flac",
".FLAC" to ".flac",
" .Mp3 " to ".mp3",
"\tFlac\n" to ".flac",
" m4a" to ".m4a",
"." to ".",
".." to "..",
"..flac" to "..flac",
"tar.gz" to ".tar.gz",
"ÉXT" to ".éxt",
)
}