From 9bd78bc8559f63892d4c6cb5370ad27e85e91e05 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sun, 8 Jan 2023 10:31:28 -0700 Subject: [PATCH] music: add uid and raw tests Add tests for the relatively simple Music.UID and Raw data classes (excluding Song.Raw). This should make sure most of the grouping code works, making the library tests theoretically much simpler. --- .../java/org/oxycblt/auxio/music/Music.kt | 62 ++--- .../java/org/oxycblt/auxio/music/MusicTest.kt | 213 ++++++++++++++++++ 2 files changed, 245 insertions(+), 30 deletions(-) create mode 100644 app/src/test/java/org/oxycblt/auxio/music/MusicTest.kt diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index e4de7b1a2..39aeab02d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -21,6 +21,7 @@ package org.oxycblt.auxio.music import android.content.Context import android.os.Parcelable +import androidx.annotation.VisibleForTesting import java.security.MessageDigest import java.text.CollationKey import java.text.Collator @@ -763,16 +764,15 @@ class Album constructor(raw: Raw, override val songs: List) : MusicParent( override fun hashCode() = hashCode - override fun equals(other: Any?): Boolean { - if (other !is Raw) return false - if (musicBrainzId != null && - other.musicBrainzId != null && - musicBrainzId == other.musicBrainzId) { - return true - } - - return name.equals(other.name, true) && rawArtists == other.rawArtists - } + override fun equals(other: Any?) = + other is Raw && + when { + musicBrainzId != null && other.musicBrainzId != null -> + musicBrainzId == other.musicBrainzId + musicBrainzId == null && other.musicBrainzId == null -> + name.equals(other.name, true) && rawArtists == other.rawArtists + else -> false + } } } @@ -916,21 +916,19 @@ class Artist constructor(private val raw: Raw, songAlbums: List) : MusicP override fun hashCode() = hashCode - override fun equals(other: Any?): Boolean { - if (other !is Raw) return false - - if (musicBrainzId != null && - other.musicBrainzId != null && - musicBrainzId == other.musicBrainzId) { - return true - } - - return when { - name != null && other.name != null -> name.equals(other.name, true) - name == null && other.name == null -> true - else -> false - } - } + override fun equals(other: Any?) = + other is Raw && + when { + musicBrainzId != null && other.musicBrainzId != null -> + musicBrainzId == other.musicBrainzId + musicBrainzId == null && other.musicBrainzId == null -> + when { + name != null && other.name != null -> name.equals(other.name, true) + name == null && other.name == null -> true + else -> false + } + else -> false + } } } @@ -1025,7 +1023,7 @@ class Genre constructor(private val raw: Raw, override val songs: List) : * @return A [UUID] converted from the [String] value, or null if the value was not valid. * @see UUID.fromString */ -fun String.toUuidOrNull(): UUID? = +private fun String.toUuidOrNull(): UUID? = try { UUID.fromString(this) } catch (e: IllegalArgumentException) { @@ -1036,7 +1034,8 @@ fun String.toUuidOrNull(): UUID? = * Update a [MessageDigest] with a lowercase [String]. * @param string The [String] to hash. If null, it will not be hashed. */ -private fun MessageDigest.update(string: String?) { +@VisibleForTesting +fun MessageDigest.update(string: String?) { if (string != null) { update(string.lowercase().toByteArray()) } else { @@ -1048,7 +1047,8 @@ private fun MessageDigest.update(string: String?) { * Update a [MessageDigest] with the string representation of a [Date]. * @param date The [Date] to hash. If null, nothing will be done. */ -private fun MessageDigest.update(date: Date?) { +@VisibleForTesting +fun MessageDigest.update(date: Date?) { if (date != null) { update(date.toString().toByteArray()) } else { @@ -1060,7 +1060,8 @@ private fun MessageDigest.update(date: Date?) { * Update a [MessageDigest] with the lowercase versions of all of the input [String]s. * @param strings The [String]s to hash. If a [String] is null, it will not be hashed. */ -private fun MessageDigest.update(strings: List) { +@VisibleForTesting +fun MessageDigest.update(strings: List) { strings.forEach(::update) } @@ -1068,7 +1069,8 @@ private fun MessageDigest.update(strings: List) { * Update a [MessageDigest] with the little-endian bytes of a [Int]. * @param n The [Int] to write. If null, nothing will be done. */ -private fun MessageDigest.update(n: Int?) { +@VisibleForTesting +fun MessageDigest.update(n: Int?) { if (n != null) { update(byteArrayOf(n.toByte(), n.shr(8).toByte(), n.shr(16).toByte(), n.shr(24).toByte())) } else { diff --git a/app/src/test/java/org/oxycblt/auxio/music/MusicTest.kt b/app/src/test/java/org/oxycblt/auxio/music/MusicTest.kt new file mode 100644 index 000000000..d1262e11f --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/MusicTest.kt @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music + +import java.util.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.oxycblt.auxio.music.tags.Date + +class MusicTest { + @Test + fun musicUid_auxio() { + val uid = + Music.UID.auxio(MusicMode.SONGS) { + update("Wheel") + update(listOf("Parannoul", "Asian Glow")) + update("Paraglow") + update(null as String?) + update(Date.from(2022)) + update(4 as Int?) + update(null as Int?) + } + + assertEquals("org.oxycblt.auxio:a10b-3d29c202-cd52-fbe0-4714-47cd07f07a59", uid.toString()) + } + + @Test + fun musicUid_musicBrainz() { + val uid = + Music.UID.musicBrainz( + MusicMode.ALBUMS, UUID.fromString("9b3b0695-0cdc-4560-8486-8deadee136cb")) + assertEquals("org.musicbrainz:a10a-9b3b0695-0cdc-4560-8486-8deadee136cb", uid.toString()) + } + + @Test + fun albumRaw_equals_inconsistentCase() { + val a = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = null, + name = "Paraglow", + sortName = null, + releaseType = null, + rawArtists = + listOf(Artist.Raw(name = "Parannoul"), Artist.Raw(name = "Asian Glow"))) + val b = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = null, + name = "paraglow", + sortName = null, + releaseType = null, + rawArtists = + listOf(Artist.Raw(name = "Parannoul"), Artist.Raw(name = "Asian glow"))) + assertTrue(a == b) + assertTrue(a.hashCode() == b.hashCode()) + } + + @Test + fun albumRaw_equals_withMbids() { + val a = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = UUID.fromString("c7b245c9-8099-32ea-af95-893acedde2cf"), + name = "Weezer", + sortName = "Blue Album", + releaseType = null, + rawArtists = listOf(Artist.Raw(name = "Weezer"))) + val b = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = UUID.fromString("923d5ba6-7eee-3bce-bcb2-c913b2bd69d4"), + name = "Weezer", + sortName = "Green Album", + releaseType = null, + rawArtists = listOf(Artist.Raw(name = "Weezer"))) + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } + + @Test + fun albumRaw_equals_inconsistentMbids() { + val a = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = UUID.fromString("c7b245c9-8099-32ea-af95-893acedde2cf"), + name = "Weezer", + sortName = "Blue Album", + releaseType = null, + rawArtists = listOf(Artist.Raw(name = "Weezer"))) + val b = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = null, + name = "Weezer", + sortName = "Green Album", + releaseType = null, + rawArtists = listOf(Artist.Raw(name = "Weezer"))) + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } + + @Test + fun albumRaw_equals_withArtists() { + val a = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = null, + name = "Album", + sortName = null, + releaseType = null, + rawArtists = listOf(Artist.Raw(name = "Artist A"))) + val b = + Album.Raw( + mediaStoreId = -1, + musicBrainzId = null, + name = "Album", + sortName = null, + releaseType = null, + rawArtists = listOf(Artist.Raw(name = "Artist B"))) + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } + + @Test + fun artistRaw_equals_inconsistentCase() { + val a = Artist.Raw(musicBrainzId = null, name = "Parannoul") + val b = Artist.Raw(musicBrainzId = null, name = "parannoul") + assertTrue(a == b) + assertTrue(a.hashCode() == b.hashCode()) + } + + @Test + fun artistRaw_equals_withMbids() { + val a = + Artist.Raw( + musicBrainzId = UUID.fromString("677325ef-d850-44bb-8258-0d69bbc0b3f7"), + name = "Artist") + val b = + Artist.Raw( + musicBrainzId = UUID.fromString("6b625592-d88d-48c8-ac1a-c5b476d78bcc"), + name = "Artist") + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } + + @Test + fun artistRaw_equals_inconsistentMbids() { + val a = + Artist.Raw( + musicBrainzId = UUID.fromString("677325ef-d850-44bb-8258-0d69bbc0b3f7"), + name = "Artist") + val b = Artist.Raw(musicBrainzId = null, name = "Artist") + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } + + @Test + fun artistRaw_equals_missingNames() { + val a = Artist.Raw(name = null) + val b = Artist.Raw(name = null) + assertTrue(a == b) + assertTrue(a.hashCode() == b.hashCode()) + } + + @Test + fun artistRaw_equals_inconsistentNames() { + val a = Artist.Raw(name = null) + val b = Artist.Raw(name = "Parannoul") + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } + + @Test + fun genreRaw_equals_inconsistentCase() { + val a = Genre.Raw("Future Garage") + val b = Genre.Raw("future garage") + assertTrue(a == b) + assertTrue(a.hashCode() == b.hashCode()) + } + + @Test + fun genreRaw_equals_missingNames() { + val a = Genre.Raw(name = null) + val b = Genre.Raw(name = null) + assertTrue(a == b) + assertTrue(a.hashCode() == b.hashCode()) + } + + @Test + fun genreRaw_equals_inconsistentNames() { + val a = Genre.Raw(name = null) + val b = Genre.Raw(name = "Future Garage") + assertTrue(a != b) + assertTrue(a.hashCode() != b.hashCode()) + } +}