music: use factory to create known names
Implement a new Name.Known.Factory instance that replaces the usage of Name.Known.from. This again allows songs to be differentiated on tag interpretation and is generally easier to test.
This commit is contained in:
parent
c1655a9eca
commit
fcffb56021
9 changed files with 263 additions and 207 deletions
|
@ -223,7 +223,8 @@ constructor(
|
|||
private val mediaStoreExtractor: MediaStoreExtractor,
|
||||
private val tagExtractor: TagExtractor,
|
||||
private val deviceLibraryFactory: DeviceLibrary.Factory,
|
||||
private val userLibraryFactory: UserLibrary.Factory
|
||||
private val userLibraryFactory: UserLibrary.Factory,
|
||||
private val musicSettings: MusicSettings
|
||||
) : MusicRepository {
|
||||
private val updateListeners = mutableListOf<MusicRepository.UpdateListener>()
|
||||
private val indexingListeners = mutableListOf<MusicRepository.IndexingListener>()
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.oxycblt.auxio.music.MusicSettings
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.fs.contentResolverSafe
|
||||
import org.oxycblt.auxio.music.fs.useQuery
|
||||
import org.oxycblt.auxio.music.info.Name
|
||||
import org.oxycblt.auxio.music.metadata.Separators
|
||||
import org.oxycblt.auxio.util.logW
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
@ -108,7 +109,7 @@ interface DeviceLibrary {
|
|||
*/
|
||||
suspend fun create(
|
||||
rawSongs: Channel<RawSong>,
|
||||
processedSongs: Channel<RawSong>
|
||||
processedSongs: Channel<RawSong>,
|
||||
): DeviceLibraryImpl
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +120,8 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
|
|||
rawSongs: Channel<RawSong>,
|
||||
processedSongs: Channel<RawSong>
|
||||
): DeviceLibraryImpl {
|
||||
val separators = Separators.from(musicSettings.separators)
|
||||
val nameFactory = Name.Known.Factory.from(musicSettings)
|
||||
val separators = Separators.from(musicSettings)
|
||||
|
||||
val songGrouping = mutableMapOf<Music.UID, SongImpl>()
|
||||
val albumGrouping = mutableMapOf<RawAlbum.Key, Grouping<RawAlbum, SongImpl>>()
|
||||
|
@ -130,7 +132,7 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
|
|||
|
||||
// All music information is grouped as it is indexed by other components.
|
||||
for (rawSong in rawSongs) {
|
||||
val song = SongImpl(rawSong, musicSettings, separators)
|
||||
val song = SongImpl(rawSong, nameFactory, separators)
|
||||
// At times the indexer produces duplicate songs, try to filter these. Comparing by
|
||||
// UID is sufficient for something like this, and also prevents collisions from
|
||||
// causing severe issues elsewhere.
|
||||
|
@ -210,7 +212,7 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
|
|||
|
||||
// Now that all songs are processed, also process albums and group them into their
|
||||
// respective artists.
|
||||
val albums = albumGrouping.values.mapTo(mutableSetOf()) { AlbumImpl(it, musicSettings) }
|
||||
val albums = albumGrouping.values.mapTo(mutableSetOf()) { AlbumImpl(it, nameFactory) }
|
||||
for (album in albums) {
|
||||
for (rawArtist in album.rawArtists) {
|
||||
val key = RawArtist.Key(rawArtist)
|
||||
|
@ -246,8 +248,8 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
|
|||
}
|
||||
|
||||
// Artists and genres do not need to be grouped and can be processed immediately.
|
||||
val artists = artistGrouping.values.mapTo(mutableSetOf()) { ArtistImpl(it, musicSettings) }
|
||||
val genres = genreGrouping.values.mapTo(mutableSetOf()) { GenreImpl(it, musicSettings) }
|
||||
val artists = artistGrouping.values.mapTo(mutableSetOf()) { ArtistImpl(it, nameFactory) }
|
||||
val genres = genreGrouping.values.mapTo(mutableSetOf()) { GenreImpl(it, nameFactory) }
|
||||
|
||||
return DeviceLibraryImpl(songGrouping.values.toSet(), albums, artists, genres)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicSettings
|
||||
import org.oxycblt.auxio.music.MusicType
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.fs.MimeType
|
||||
|
@ -48,12 +47,15 @@ import org.oxycblt.auxio.util.update
|
|||
* Library-backed implementation of [Song].
|
||||
*
|
||||
* @param rawSong The [RawSong] to derive the member data from.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
* @param separators The [Separators] to parse multi-value tags with.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings, separators: Separators) :
|
||||
Song {
|
||||
class SongImpl(
|
||||
private val rawSong: RawSong,
|
||||
nameFactory: Name.Known.Factory,
|
||||
separators: Separators
|
||||
) : Song {
|
||||
override val uid =
|
||||
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
|
||||
rawSong.musicBrainzId?.toUuidOrNull()?.let { Music.UID.musicBrainz(MusicType.SONGS, it) }
|
||||
|
@ -72,10 +74,8 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings, separ
|
|||
update(rawSong.albumArtistNames)
|
||||
}
|
||||
override val name =
|
||||
Name.Known.from(
|
||||
requireNotNull(rawSong.name) { "Invalid raw: No title" },
|
||||
rawSong.sortName,
|
||||
musicSettings)
|
||||
nameFactory.parse(
|
||||
requireNotNull(rawSong.name) { "Invalid raw: No title" }, rawSong.sortName)
|
||||
|
||||
override val track = rawSong.track
|
||||
override val disc = rawSong.disc?.let { Disc(it, rawSong.subtitle) }
|
||||
|
@ -256,13 +256,10 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings, separ
|
|||
* Library-backed implementation of [Album].
|
||||
*
|
||||
* @param grouping [Grouping] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class AlbumImpl(
|
||||
grouping: Grouping<RawAlbum, SongImpl>,
|
||||
musicSettings: MusicSettings,
|
||||
) : Album {
|
||||
class AlbumImpl(grouping: Grouping<RawAlbum, SongImpl>, nameFactory: Name.Known.Factory) : Album {
|
||||
private val rawAlbum = grouping.raw.inner
|
||||
|
||||
override val uid =
|
||||
|
@ -275,7 +272,7 @@ class AlbumImpl(
|
|||
update(rawAlbum.name)
|
||||
update(rawAlbum.rawArtists.map { it.name })
|
||||
}
|
||||
override val name = Name.Known.from(rawAlbum.name, rawAlbum.sortName, musicSettings)
|
||||
override val name = nameFactory.parse(rawAlbum.name, rawAlbum.sortName)
|
||||
override val dates: Date.Range?
|
||||
override val releaseType = rawAlbum.releaseType ?: ReleaseType.Album(null)
|
||||
override val coverUri = CoverUri(rawAlbum.mediaStoreId.toCoverUri(), grouping.raw.src.uri)
|
||||
|
@ -376,10 +373,10 @@ class AlbumImpl(
|
|||
* Library-backed implementation of [Artist].
|
||||
*
|
||||
* @param grouping [Grouping] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ArtistImpl(grouping: Grouping<RawArtist, Music>, musicSettings: MusicSettings) : Artist {
|
||||
class ArtistImpl(grouping: Grouping<RawArtist, Music>, nameFactory: Name.Known.Factory) : Artist {
|
||||
private val rawArtist = grouping.raw.inner
|
||||
|
||||
override val uid =
|
||||
|
@ -387,7 +384,7 @@ class ArtistImpl(grouping: Grouping<RawArtist, Music>, musicSettings: MusicSetti
|
|||
rawArtist.musicBrainzId?.let { Music.UID.musicBrainz(MusicType.ARTISTS, it) }
|
||||
?: Music.UID.auxio(MusicType.ARTISTS) { update(rawArtist.name) }
|
||||
override val name =
|
||||
rawArtist.name?.let { Name.Known.from(it, rawArtist.sortName, musicSettings) }
|
||||
rawArtist.name?.let { nameFactory.parse(it, rawArtist.sortName) }
|
||||
?: Name.Unknown(R.string.def_artist)
|
||||
|
||||
override val songs: Set<Song>
|
||||
|
@ -473,15 +470,15 @@ class ArtistImpl(grouping: Grouping<RawArtist, Music>, musicSettings: MusicSetti
|
|||
* Library-backed implementation of [Genre].
|
||||
*
|
||||
* @param grouping [Grouping] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class GenreImpl(grouping: Grouping<RawGenre, SongImpl>, musicSettings: MusicSettings) : Genre {
|
||||
class GenreImpl(grouping: Grouping<RawGenre, SongImpl>, nameFactory: Name.Known.Factory) : Genre {
|
||||
private val rawGenre = grouping.raw.inner
|
||||
|
||||
override val uid = Music.UID.auxio(MusicType.GENRES) { update(rawGenre.name) }
|
||||
override val name =
|
||||
rawGenre.name?.let { Name.Known.from(it, rawGenre.name, musicSettings) }
|
||||
rawGenre.name?.let { nameFactory.parse(it, rawGenre.name) }
|
||||
?: Name.Unknown(R.string.def_genre)
|
||||
|
||||
override val songs: Set<Song>
|
||||
|
|
|
@ -57,36 +57,6 @@ sealed interface Name : Comparable<Name> {
|
|||
/** A tokenized version of the name that will be compared. */
|
||||
@VisibleForTesting(VisibleForTesting.PROTECTED) abstract val sortTokens: List<SortToken>
|
||||
|
||||
/** An individual part of a name string that can be compared intelligently. */
|
||||
@VisibleForTesting(VisibleForTesting.PROTECTED)
|
||||
data class SortToken(val collationKey: CollationKey, val type: Type) :
|
||||
Comparable<SortToken> {
|
||||
override fun compareTo(other: SortToken): Int {
|
||||
// Numeric tokens should always be lower than lexicographic tokens.
|
||||
val modeComp = type.compareTo(other.type)
|
||||
if (modeComp != 0) {
|
||||
return modeComp
|
||||
}
|
||||
|
||||
// Numeric strings must be ordered by magnitude, thus immediately short-circuit
|
||||
// the comparison if the lengths do not match.
|
||||
if (type == Type.NUMERIC &&
|
||||
collationKey.sourceString.length != other.collationKey.sourceString.length) {
|
||||
return collationKey.sourceString.length - other.collationKey.sourceString.length
|
||||
}
|
||||
|
||||
return collationKey.compareTo(other.collationKey)
|
||||
}
|
||||
|
||||
/** Denotes the type of comparison to be performed with this token. */
|
||||
enum class Type {
|
||||
/** Compare as a digit string, like "65". */
|
||||
NUMERIC,
|
||||
/** Compare as a standard alphanumeric string, like "65daysofstatic" */
|
||||
LEXICOGRAPHIC
|
||||
}
|
||||
}
|
||||
|
||||
final override val thumb: String
|
||||
get() =
|
||||
// TODO: Remove these safety checks once you have real unit testing
|
||||
|
@ -110,20 +80,30 @@ sealed interface Name : Comparable<Name> {
|
|||
is Unknown -> 1
|
||||
}
|
||||
|
||||
companion object {
|
||||
interface Factory {
|
||||
/**
|
||||
* Create a new instance of [Name.Known]
|
||||
*
|
||||
* @param raw The raw name obtained from the music item
|
||||
* @param sort The raw sort name obtained from the music item
|
||||
* @param musicSettings [MusicSettings] required for name configuration.
|
||||
*/
|
||||
fun from(raw: String, sort: String?, musicSettings: MusicSettings): Known =
|
||||
if (musicSettings.intelligentSorting) {
|
||||
IntelligentKnownName(raw, sort)
|
||||
} else {
|
||||
SimpleKnownName(raw, sort)
|
||||
}
|
||||
fun parse(raw: String, sort: String?): Known
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a new instance from the **current state** of the given [MusicSettings]'s
|
||||
* user-defined name configuration.
|
||||
*
|
||||
* @param settings The [MusicSettings] to use.
|
||||
* @return A new [Factory] instance reflecting the configuration state.
|
||||
*/
|
||||
fun from(settings: MusicSettings) =
|
||||
if (settings.intelligentSorting) {
|
||||
IntelligentKnownName.Factory()
|
||||
} else {
|
||||
SimpleKnownName.Factory()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,8 +137,8 @@ private val punctRegex by lazy { Regex("[\\p{Punct}+]") }
|
|||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private data class SimpleKnownName(override val raw: String, override val sort: String?) :
|
||||
Name.Known() {
|
||||
@VisibleForTesting
|
||||
data class SimpleKnownName(override val raw: String, override val sort: String?) : Name.Known() {
|
||||
override val sortTokens = listOf(parseToken(sort ?: raw))
|
||||
|
||||
private fun parseToken(name: String): SortToken {
|
||||
|
@ -168,6 +148,10 @@ private data class SimpleKnownName(override val raw: String, override val sort:
|
|||
// Always use lexicographic mode since we aren't parsing any numeric components
|
||||
return SortToken(collationKey, SortToken.Type.LEXICOGRAPHIC)
|
||||
}
|
||||
|
||||
class Factory : Name.Known.Factory {
|
||||
override fun parse(raw: String, sort: String?) = SimpleKnownName(raw, sort)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +159,8 @@ private data class SimpleKnownName(override val raw: String, override val sort:
|
|||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private data class IntelligentKnownName(override val raw: String, override val sort: String?) :
|
||||
@VisibleForTesting
|
||||
data class IntelligentKnownName(override val raw: String, override val sort: String?) :
|
||||
Name.Known() {
|
||||
override val sortTokens = parseTokens(sort ?: raw)
|
||||
|
||||
|
@ -223,7 +208,40 @@ private data class IntelligentKnownName(override val raw: String, override val s
|
|||
}
|
||||
}
|
||||
|
||||
class Factory : Name.Known.Factory {
|
||||
override fun parse(raw: String, sort: String?) = IntelligentKnownName(raw, sort)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TOKEN_REGEX by lazy { Regex("(\\d+)|(\\D+)") }
|
||||
}
|
||||
}
|
||||
|
||||
/** An individual part of a name string that can be compared intelligently. */
|
||||
@VisibleForTesting(VisibleForTesting.PROTECTED)
|
||||
data class SortToken(val collationKey: CollationKey, val type: Type) : Comparable<SortToken> {
|
||||
override fun compareTo(other: SortToken): Int {
|
||||
// Numeric tokens should always be lower than lexicographic tokens.
|
||||
val modeComp = type.compareTo(other.type)
|
||||
if (modeComp != 0) {
|
||||
return modeComp
|
||||
}
|
||||
|
||||
// Numeric strings must be ordered by magnitude, thus immediately short-circuit
|
||||
// the comparison if the lengths do not match.
|
||||
if (type == Type.NUMERIC &&
|
||||
collationKey.sourceString.length != other.collationKey.sourceString.length) {
|
||||
return collationKey.sourceString.length - other.collationKey.sourceString.length
|
||||
}
|
||||
|
||||
return collationKey.compareTo(other.collationKey)
|
||||
}
|
||||
|
||||
/** Denotes the type of comparison to be performed with this token. */
|
||||
enum class Type {
|
||||
/** Compare as a digit string, like "65". */
|
||||
NUMERIC,
|
||||
/** Compare as a standard alphanumeric string, like "65daysofstatic" */
|
||||
LEXICOGRAPHIC
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
package org.oxycblt.auxio.music.metadata
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.oxycblt.auxio.music.MusicSettings
|
||||
|
||||
/**
|
||||
* Defines the user-specified parsing of multi-value tags. This should be used to parse any tags
|
||||
* that may be delimited with a separator character.
|
||||
|
@ -40,9 +43,22 @@ interface Separators {
|
|||
const val SLASH = '/'
|
||||
const val PLUS = '+'
|
||||
const val AND = '&'
|
||||
/**
|
||||
* Creates a new instance from the **current state** of the given [MusicSettings]'s
|
||||
* user-defined separator configuration.
|
||||
*
|
||||
* @param settings The [MusicSettings] to use.
|
||||
* @return A new [Separators] instance reflecting the configuration state.
|
||||
*/
|
||||
fun from(settings: MusicSettings) = from(settings.separators)
|
||||
|
||||
fun from(selector: String) =
|
||||
if (selector.isNotEmpty()) CharSeparators(selector.toSet()) else NoSeparators
|
||||
@VisibleForTesting
|
||||
fun from(chars: String) =
|
||||
if (chars.isNotEmpty()) {
|
||||
CharSeparators(chars.toSet())
|
||||
} else {
|
||||
NoSeparators
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ private class TagWorkerImpl(
|
|||
private val rawSong: RawSong,
|
||||
private val future: Future<TrackGroupArray>
|
||||
) : TagWorker {
|
||||
|
||||
override fun poll(): RawSong? {
|
||||
if (!future.isDone) {
|
||||
// Not done yet, nothing to do.
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package org.oxycblt.auxio.music.user
|
||||
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicSettings
|
||||
import org.oxycblt.auxio.music.MusicType
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
@ -51,10 +50,10 @@ private constructor(
|
|||
* Clone the data in this instance to a new [PlaylistImpl] with the given [name].
|
||||
*
|
||||
* @param name The new name to use.
|
||||
* @param musicSettings [MusicSettings] required for name configuration.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
*/
|
||||
fun edit(name: String, musicSettings: MusicSettings) =
|
||||
PlaylistImpl(uid, Name.Known.from(name, null, musicSettings), songs)
|
||||
fun edit(name: String, nameFactory: Name.Known.Factory) =
|
||||
PlaylistImpl(uid, nameFactory.parse(name, null), songs)
|
||||
|
||||
/**
|
||||
* Clone the data in this instance to a new [PlaylistImpl] with the given [Song]s.
|
||||
|
@ -76,29 +75,26 @@ private constructor(
|
|||
*
|
||||
* @param name The name of the playlist.
|
||||
* @param songs The songs to initially populate the playlist with.
|
||||
* @param musicSettings [MusicSettings] required for name configuration.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
*/
|
||||
fun from(name: String, songs: List<Song>, musicSettings: MusicSettings) =
|
||||
PlaylistImpl(
|
||||
Music.UID.auxio(MusicType.PLAYLISTS),
|
||||
Name.Known.from(name, null, musicSettings),
|
||||
songs)
|
||||
fun from(name: String, songs: List<Song>, nameFactory: Name.Known.Factory) =
|
||||
PlaylistImpl(Music.UID.auxio(MusicType.PLAYLISTS), nameFactory.parse(name, null), songs)
|
||||
|
||||
/**
|
||||
* Populate a new instance from a read [RawPlaylist].
|
||||
*
|
||||
* @param rawPlaylist The [RawPlaylist] to read from.
|
||||
* @param deviceLibrary The [DeviceLibrary] to initialize from.
|
||||
* @param musicSettings [MusicSettings] required for name configuration.
|
||||
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
|
||||
*/
|
||||
fun fromRaw(
|
||||
rawPlaylist: RawPlaylist,
|
||||
deviceLibrary: DeviceLibrary,
|
||||
musicSettings: MusicSettings
|
||||
nameFactory: Name.Known.Factory
|
||||
) =
|
||||
PlaylistImpl(
|
||||
rawPlaylist.playlistInfo.playlistUid,
|
||||
Name.Known.from(rawPlaylist.playlistInfo.name, null, musicSettings),
|
||||
nameFactory.parse(rawPlaylist.playlistInfo.name, null),
|
||||
rawPlaylist.songs.mapNotNull { deviceLibrary.findSong(it.songUid) })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.oxycblt.auxio.music.MusicSettings
|
|||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.device.DeviceLibrary
|
||||
import org.oxycblt.auxio.music.info.Name
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logE
|
||||
|
||||
|
@ -144,7 +145,9 @@ constructor(private val playlistDao: PlaylistDao, private val musicSettings: Mus
|
|||
UserLibrary.Factory {
|
||||
override suspend fun query() =
|
||||
try {
|
||||
playlistDao.readRawPlaylists()
|
||||
val rawPlaylists = playlistDao.readRawPlaylists()
|
||||
logD("Successfully read ${rawPlaylists.size} playlists")
|
||||
rawPlaylists
|
||||
} catch (e: Exception) {
|
||||
logE("Unable to read playlists: $e")
|
||||
listOf()
|
||||
|
@ -154,11 +157,10 @@ constructor(private val playlistDao: PlaylistDao, private val musicSettings: Mus
|
|||
rawPlaylists: List<RawPlaylist>,
|
||||
deviceLibrary: DeviceLibrary
|
||||
): MutableUserLibrary {
|
||||
logD("Successfully read ${rawPlaylists.size} playlists")
|
||||
// Convert the database playlist information to actual usable playlists.
|
||||
val nameFactory = Name.Known.Factory.from(musicSettings)
|
||||
val playlistMap = mutableMapOf<Music.UID, PlaylistImpl>()
|
||||
for (rawPlaylist in rawPlaylists) {
|
||||
val playlistImpl = PlaylistImpl.fromRaw(rawPlaylist, deviceLibrary, musicSettings)
|
||||
val playlistImpl = PlaylistImpl.fromRaw(rawPlaylist, deviceLibrary, nameFactory)
|
||||
playlistMap[playlistImpl.uid] = playlistImpl
|
||||
}
|
||||
return UserLibraryImpl(playlistDao, playlistMap, musicSettings)
|
||||
|
@ -184,7 +186,7 @@ private class UserLibraryImpl(
|
|||
override fun findPlaylist(name: String) = playlistMap.values.find { it.name.raw == name }
|
||||
|
||||
override suspend fun createPlaylist(name: String, songs: List<Song>): Playlist? {
|
||||
val playlistImpl = PlaylistImpl.from(name, songs, musicSettings)
|
||||
val playlistImpl = PlaylistImpl.from(name, songs, Name.Known.Factory.from(musicSettings))
|
||||
synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl }
|
||||
val rawPlaylist =
|
||||
RawPlaylist(
|
||||
|
@ -207,7 +209,9 @@ private class UserLibraryImpl(
|
|||
val playlistImpl =
|
||||
synchronized(this) {
|
||||
requireNotNull(playlistMap[playlist.uid]) { "Cannot rename invalid playlist" }
|
||||
.also { playlistMap[it.uid] = it.edit(name, musicSettings) }
|
||||
.also {
|
||||
playlistMap[it.uid] = it.edit(name, Name.Known.Factory.from(musicSettings))
|
||||
}
|
||||
}
|
||||
|
||||
return try {
|
||||
|
|
|
@ -22,342 +22,352 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.oxycblt.auxio.music.MusicSettings
|
||||
|
||||
class NameTest {
|
||||
private fun mockIntelligentSorting(enabled: Boolean) =
|
||||
mockk<MusicSettings>() { every { intelligentSorting } returns enabled }
|
||||
@Test
|
||||
fun name_simple_from_settings() {
|
||||
val musicSettings = mockk<MusicSettings> { every { intelligentSorting } returns false }
|
||||
assertTrue(Name.Known.Factory.from(musicSettings) is SimpleKnownName.Factory)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_simple_withoutPunct() {
|
||||
val name = Name.Known.from("Loveless", null, mockIntelligentSorting(false))
|
||||
fun name_intelligent_from_settings() {
|
||||
val musicSettings = mockk<MusicSettings> { every { intelligentSorting } returns true }
|
||||
assertTrue(Name.Known.Factory.from(musicSettings) is IntelligentKnownName.Factory)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_simple_withoutPunct() {
|
||||
val name = SimpleKnownName("Loveless", null)
|
||||
assertEquals("Loveless", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("L", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("Loveless", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_simple_withPunct() {
|
||||
val name = Name.Known.from("alt-J", null, mockIntelligentSorting(false))
|
||||
fun name_simple_withPunct() {
|
||||
val name = SimpleKnownName("alt-J", null)
|
||||
assertEquals("alt-J", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("A", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("altJ", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_simple_oopsAllPunct() {
|
||||
val name = Name.Known.from("!!!", null, mockIntelligentSorting(false))
|
||||
fun name_simple_oopsAllPunct() {
|
||||
val name = SimpleKnownName("!!!", null)
|
||||
assertEquals("!!!", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("!", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("!!!", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_simple_spacedPunct() {
|
||||
val name = Name.Known.from("& Yet & Yet", null, mockIntelligentSorting(false))
|
||||
fun name_simple_spacedPunct() {
|
||||
val name = SimpleKnownName("& Yet & Yet", null)
|
||||
assertEquals("& Yet & Yet", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("Y", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Yet Yet", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_simple_withSort() {
|
||||
val name = Name.Known.from("The Smile", "Smile", mockIntelligentSorting(false))
|
||||
fun name_simple_withSort() {
|
||||
val name = SimpleKnownName("The Smile", "Smile")
|
||||
assertEquals("The Smile", name.raw)
|
||||
assertEquals("Smile", name.sort)
|
||||
assertEquals("S", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("Smile", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withoutNumerics() {
|
||||
val name = Name.Known.from("Loveless", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withoutNumerics() {
|
||||
val name = IntelligentKnownName("Loveless", null)
|
||||
assertEquals("Loveless", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("L", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("Loveless", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withSpacedStartNumerics() {
|
||||
val name = Name.Known.from("15 Step", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withSpacedStartNumerics() {
|
||||
val name = IntelligentKnownName("15 Step", null)
|
||||
assertEquals("15 Step", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("#", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("15", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, first.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("Step", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, second.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, second.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withPackedStartNumerics() {
|
||||
val name = Name.Known.from("23Kid", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withPackedStartNumerics() {
|
||||
val name = IntelligentKnownName("23Kid", null)
|
||||
assertEquals("23Kid", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("#", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("23", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, first.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("Kid", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, second.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, second.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withSpacedMiddleNumerics() {
|
||||
val name = Name.Known.from("Foo 1 2 Bar", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withSpacedMiddleNumerics() {
|
||||
val name = IntelligentKnownName("Foo 1 2 Bar", null)
|
||||
assertEquals("Foo 1 2 Bar", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("F", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Foo", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("1", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, second.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, second.type)
|
||||
val third = name.sortTokens[2]
|
||||
assertEquals(" ", third.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, third.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, third.type)
|
||||
val fourth = name.sortTokens[3]
|
||||
assertEquals("2", fourth.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, fourth.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, fourth.type)
|
||||
val fifth = name.sortTokens[4]
|
||||
assertEquals("Bar", fifth.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, fifth.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, fifth.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withPackedMiddleNumerics() {
|
||||
val name = Name.Known.from("Foo12Bar", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withPackedMiddleNumerics() {
|
||||
val name = IntelligentKnownName("Foo12Bar", null)
|
||||
assertEquals("Foo12Bar", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("F", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Foo", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("12", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, second.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, second.type)
|
||||
val third = name.sortTokens[2]
|
||||
assertEquals("Bar", third.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, third.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, third.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withSpacedEndNumerics() {
|
||||
val name = Name.Known.from("Foo 1", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withSpacedEndNumerics() {
|
||||
val name = IntelligentKnownName("Foo 1", null)
|
||||
assertEquals("Foo 1", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("F", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Foo", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("1", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, second.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, second.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withoutArticle_withPackedEndNumerics() {
|
||||
val name = Name.Known.from("Error404", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withoutArticle_withPackedEndNumerics() {
|
||||
val name = IntelligentKnownName("Error404", null)
|
||||
assertEquals("Error404", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("E", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Error", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("404", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, second.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, second.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withThe_withoutNumerics() {
|
||||
val name = Name.Known.from("The National Anthem", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withThe_withoutNumerics() {
|
||||
val name = IntelligentKnownName("The National Anthem", null)
|
||||
assertEquals("The National Anthem", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("N", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("National Anthem", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withAn_withoutNumerics() {
|
||||
val name = Name.Known.from("An Eagle in Your Mind", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withAn_withoutNumerics() {
|
||||
val name = IntelligentKnownName("An Eagle in Your Mind", null)
|
||||
assertEquals("An Eagle in Your Mind", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("E", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Eagle in Your Mind", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_withA_withoutNumerics() {
|
||||
val name = Name.Known.from("A Song For Our Fathers", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_withA_withoutNumerics() {
|
||||
val name = IntelligentKnownName("A Song For Our Fathers", null)
|
||||
assertEquals("A Song For Our Fathers", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("S", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Song For Our Fathers", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withPunct_withoutArticle_withoutNumerics() {
|
||||
val name = Name.Known.from("alt-J", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withPunct_withoutArticle_withoutNumerics() {
|
||||
val name = IntelligentKnownName("alt-J", null)
|
||||
assertEquals("alt-J", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("A", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("altJ", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_oopsAllPunct_withoutArticle_withoutNumerics() {
|
||||
val name = Name.Known.from("!!!", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_oopsAllPunct_withoutArticle_withoutNumerics() {
|
||||
val name = IntelligentKnownName("!!!", null)
|
||||
assertEquals("!!!", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("!", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("!!!", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withoutPunct_shortArticle_withNumerics() {
|
||||
val name = Name.Known.from("the 1", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withoutPunct_shortArticle_withNumerics() {
|
||||
val name = IntelligentKnownName("the 1", null)
|
||||
assertEquals("the 1", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("#", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("1", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, first.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, first.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_spacedPunct_withoutArticle_withoutNumerics() {
|
||||
val name = Name.Known.from("& Yet & Yet", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_spacedPunct_withoutArticle_withoutNumerics() {
|
||||
val name = IntelligentKnownName("& Yet & Yet", null)
|
||||
assertEquals("& Yet & Yet", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("Y", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Yet Yet", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withPunct_withoutArticle_withNumerics() {
|
||||
val name = Name.Known.from("Design : 2 : 3", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_withPunct_withoutArticle_withNumerics() {
|
||||
val name = IntelligentKnownName("Design : 2 : 3", null)
|
||||
assertEquals("Design : 2 : 3", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("D", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("Design", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals("2", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, second.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, second.type)
|
||||
val third = name.sortTokens[2]
|
||||
assertEquals(" ", third.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, third.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, third.type)
|
||||
val fourth = name.sortTokens[3]
|
||||
assertEquals("3", fourth.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, fourth.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, fourth.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_oopsAllPunct_withoutArticle_oopsAllNumerics() {
|
||||
val name = Name.Known.from("2 + 2 = 5", null, mockIntelligentSorting(true))
|
||||
fun name_intelligent_oopsAllPunct_withoutArticle_oopsAllNumerics() {
|
||||
val name = IntelligentKnownName("2 + 2 = 5", null)
|
||||
assertEquals("2 + 2 = 5", name.raw)
|
||||
assertEquals(null, name.sort)
|
||||
assertEquals("#", name.thumb)
|
||||
val first = name.sortTokens[0]
|
||||
assertEquals("2", first.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, first.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, first.type)
|
||||
val second = name.sortTokens[1]
|
||||
assertEquals(" ", second.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, second.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, second.type)
|
||||
val third = name.sortTokens[2]
|
||||
assertEquals("2", third.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, third.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, third.type)
|
||||
val fourth = name.sortTokens[3]
|
||||
assertEquals(" ", fourth.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, fourth.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, fourth.type)
|
||||
val fifth = name.sortTokens[4]
|
||||
assertEquals("5", fifth.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.NUMERIC, fifth.type)
|
||||
assertEquals(SortToken.Type.NUMERIC, fifth.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_from_intelligent_withSort() {
|
||||
val name = Name.Known.from("The Smile", "Smile", mockIntelligentSorting(true))
|
||||
fun name_intelligent_withSort() {
|
||||
val name = IntelligentKnownName("The Smile", "Smile")
|
||||
assertEquals("The Smile", name.raw)
|
||||
assertEquals("Smile", name.sort)
|
||||
assertEquals("S", name.thumb)
|
||||
val only = name.sortTokens.single()
|
||||
assertEquals("Smile", only.collationKey.sourceString)
|
||||
assertEquals(Name.Known.SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
assertEquals(SortToken.Type.LEXICOGRAPHIC, only.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_equals_simple() {
|
||||
val a = Name.Known.from("The Same", "Same", mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("The Same", "Same", mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("The Same", "Same")
|
||||
val b = SimpleKnownName("The Same", "Same")
|
||||
assertEquals(a, b)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_equals_differentSort() {
|
||||
val a = Name.Known.from("The Same", "Same", mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("The Same", null, mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("The Same", "Same")
|
||||
val b = SimpleKnownName("The Same", null)
|
||||
assertNotEquals(a, b)
|
||||
assertNotEquals(a.hashCode(), b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_equals_intelligent_differentTokens() {
|
||||
val a = Name.Known.from("The Same", "Same", mockIntelligentSorting(true))
|
||||
val b = Name.Known.from("Same", "Same", mockIntelligentSorting(true))
|
||||
val a = IntelligentKnownName("The Same", "Same")
|
||||
val b = IntelligentKnownName("Same", "Same")
|
||||
assertNotEquals(a, b)
|
||||
assertNotEquals(a.hashCode(), b.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_simple_withoutSort_withoutArticle_withoutNumeric() {
|
||||
val a = Name.Known.from("A", null, mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("B", null, mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("A", null)
|
||||
val b = SimpleKnownName("B", null)
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_simple_withoutSort_withArticle_withoutNumeric() {
|
||||
val a = Name.Known.from("A Brain in a Bottle", null, mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("Acid Rain", null, mockIntelligentSorting(false))
|
||||
val c = Name.Known.from("Boralis / Contrastellar", null, mockIntelligentSorting(false))
|
||||
val d = Name.Known.from("Breathe In", null, mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("A Brain in a Bottle", null)
|
||||
val b = SimpleKnownName("Acid Rain", null)
|
||||
val c = SimpleKnownName("Boralis / Contrastellar", null)
|
||||
val d = SimpleKnownName("Breathe In", null)
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
assertEquals(-1, a.compareTo(c))
|
||||
assertEquals(-1, a.compareTo(d))
|
||||
|
@ -365,40 +375,40 @@ class NameTest {
|
|||
|
||||
@Test
|
||||
fun name_compareTo_simple_withSort_withoutArticle_withNumeric() {
|
||||
val a = Name.Known.from("15 Step", null, mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("128 Harps", null, mockIntelligentSorting(false))
|
||||
val c = Name.Known.from("1969", null, mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("15 Step", null)
|
||||
val b = SimpleKnownName("128 Harps", null)
|
||||
val c = SimpleKnownName("1969", null)
|
||||
assertEquals(1, a.compareTo(b))
|
||||
assertEquals(-1, a.compareTo(c))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_simple_withPartialSort() {
|
||||
val a = Name.Known.from("A", "C", mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("B", null, mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("A", "C")
|
||||
val b = SimpleKnownName("B", null)
|
||||
assertEquals(1, a.compareTo(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_simple_withSort() {
|
||||
val a = Name.Known.from("D", "A", mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("C", "B", mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("D", "A")
|
||||
val b = SimpleKnownName("C", "B")
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_intelligent_withoutSort_withoutArticle_withoutNumeric() {
|
||||
val a = Name.Known.from("A", null, mockIntelligentSorting(true))
|
||||
val b = Name.Known.from("B", null, mockIntelligentSorting(true))
|
||||
val a = IntelligentKnownName("A", null)
|
||||
val b = IntelligentKnownName("B", null)
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_intelligent_withoutSort_withArticle_withoutNumeric() {
|
||||
val a = Name.Known.from("A Brain in a Bottle", null, mockIntelligentSorting(true))
|
||||
val b = Name.Known.from("Acid Rain", null, mockIntelligentSorting(true))
|
||||
val c = Name.Known.from("Boralis / Contrastellar", null, mockIntelligentSorting(true))
|
||||
val d = Name.Known.from("Breathe In", null, mockIntelligentSorting(true))
|
||||
val a = IntelligentKnownName("A Brain in a Bottle", null)
|
||||
val b = IntelligentKnownName("Acid Rain", null)
|
||||
val c = IntelligentKnownName("Boralis / Contrastellar", null)
|
||||
val d = IntelligentKnownName("Breathe In", null)
|
||||
assertEquals(1, a.compareTo(b))
|
||||
assertEquals(1, a.compareTo(c))
|
||||
assertEquals(-1, a.compareTo(d))
|
||||
|
@ -406,9 +416,9 @@ class NameTest {
|
|||
|
||||
@Test
|
||||
fun name_compareTo_intelligent_withoutSort_withoutArticle_withNumeric() {
|
||||
val a = Name.Known.from("15 Step", null, mockIntelligentSorting(true))
|
||||
val b = Name.Known.from("128 Harps", null, mockIntelligentSorting(true))
|
||||
val c = Name.Known.from("1969", null, mockIntelligentSorting(true))
|
||||
val a = IntelligentKnownName("15 Step", null)
|
||||
val b = IntelligentKnownName("128 Harps", null)
|
||||
val c = IntelligentKnownName("1969", null)
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
assertEquals(-1, b.compareTo(c))
|
||||
assertEquals(-2, a.compareTo(c))
|
||||
|
@ -416,15 +426,28 @@ class NameTest {
|
|||
|
||||
@Test
|
||||
fun name_compareTo_intelligent_withPartialSort_withoutArticle_withoutNumeric() {
|
||||
val a = Name.Known.from("A", "C", mockIntelligentSorting(false))
|
||||
val b = Name.Known.from("B", null, mockIntelligentSorting(false))
|
||||
val a = SimpleKnownName("A", "C")
|
||||
val b = SimpleKnownName("B", null)
|
||||
assertEquals(1, a.compareTo(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_intelligent_withSort_withoutArticle_withoutNumeric() {
|
||||
val a = Name.Known.from("D", "A", mockIntelligentSorting(true))
|
||||
val b = Name.Known.from("C", "B", mockIntelligentSorting(true))
|
||||
val a = IntelligentKnownName("D", "A")
|
||||
val b = IntelligentKnownName("C", "B")
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_unknown() {
|
||||
val a = Name.Unknown(0)
|
||||
assertEquals("?", a.thumb)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_compareTo_mixed() {
|
||||
val a = Name.Unknown(0)
|
||||
val b = IntelligentKnownName("A", null)
|
||||
assertEquals(-1, a.compareTo(b))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue