@RunWith(RobolectricTestRunner::class) @Config(sdk = [Build.VERSION_CODES.TIRAMISU], application = android.app.Application::class) class TrackProcessorTest { private lateinit var context: Context @Before fun setUp() { context = ApplicationProvider.getApplicationContext() // Reset shared preferences before each test to ensure a clean slate PreferenceManager.getDefaultSharedPreferences(context).edit().clear().apply() } private fun clean(input: String): String { return TrackProcessor.process(context, "", input).displayTitle } private fun cleanArtist(input: String): String { return TrackProcessor.process(context, input, "").displayArtist } private fun initials(artist: String): String { return TrackProcessor.process(context, artist, "").displayInitials } private fun avatarColor(artist: String): Int { return TrackProcessor.process(context, artist, "").avatarColor } // ==================================================================== // TITLE CLEANING & PROCESSING TESTS // ==================================================================== @Test fun `title handles empty and blank inputs`() { assertEquals("Unknown Title", clean("")) assertEquals("Unknown Title", clean(" ")) } @Test fun `title preserves basic strings`() { assertEquals("Bohemian Rhapsody", clean("Bohemian Rhapsody")) assertEquals("SUBEME LA RADIO", clean("SUBEME LA RADIO")) assertEquals("Guerrilla Radio", clean("Guerrilla Radio")) assertEquals("99 Red Balloons", clean("99 Red Balloons")) assertEquals("1985", clean("1985")) assertEquals("+1", clean("+1")) assertEquals("10:35", clean("10:35")) assertEquals("21 Guns", clean("21 Guns")) } @Test fun `title preserves essential title parentheticals`() { assertEquals("Ignition (Remix)", clean("Ignition feat. R. Kelly (Remix)")) assertEquals("Cheerleader (Felix Jaehn Remix)", clean("Cheerleader (Felix Jaehn Remix Radio Edit)")) assertEquals("Deviance (Dirtyphonics Remix)", clean("Deviance (Dirtyphonics Remix)")) assertEquals("Old Town Road (Remix)", clean("Old Town Road (Remix)")) assertEquals("Mosaico Salsero (Popurri)", clean("Mosaico Salsero (Popurri)")) assertEquals("Christmas (Baby Please Come Home)", clean("Christmas (Baby Please Come Home)")) assertEquals("Intro (Rework) (Remix)", clean("Intro (Rework) (Remix)")) assertEquals("Everybody Everybody (Le Freak Mix)", clean("Everybody Everybody (Le Freak Mix)")) assertEquals("21st Century (Digital Boy)", clean("21st Century (Digital Boy)")) assertEquals("100% (Pure Love)", clean("100% (Pure Love)")) assertEquals("Need U (100%)", clean("Need U (100%)")) assertEquals("Klanga (De Hofnar Remix)", clean("Klanga (De Hofnar Remix)")) } @Test fun `title removes feature tags in parentheses`() { assertEquals("Levitating", clean("Levitating (feat. DaBaby)")) assertEquals("Uptown Funk", clean("Uptown Funk (FEAT Bruno Mars)")) assertEquals("Timber", clean("Timber (ft. Kesha)")) assertEquals("Promises", clean("Promises (Ft Sam Smith)")) assertEquals("Nothin' On You", clean("Nothin' On You (featuring Bruno Mars)")) assertEquals("Re-Rewind (w/ Craig David)", clean("Re-Rewind (w/ Craig David)")) assertEquals("Quit Playing Games (With My Heart)", clean("Quit Playing Games (With My Heart)")) } @Test fun `title removes feature tags in square brackets`() { assertEquals("Savage Love", clean("Savage Love [feat. Jason Derulo]")) assertEquals("Bad Blood", clean("Bad Blood [FEAT Kendrick Lamar]")) assertEquals("Despacito", clean("Despacito [ft. Daddy Yankee]")) assertEquals("Say So", clean("Say So [Ft Nicki Minaj]")) assertEquals("Empire State of Mind", clean("Empire State of Mind [featuring Alicia Keys]")) assertEquals("Where Are Ü Now (w/ Justin Bieber)", clean("Where Are Ü Now [w/ Justin Bieber]")) assertEquals("Numb/Encore (with JAY-Z)", clean("Numb/Encore [with JAY-Z]")) } @Test fun `title removes un-bracketed feature tags`() { assertEquals("Peaches", clean("Peaches feat. Daniel Caesar")) assertEquals("Love The Way You Lie", clean("Love The Way You Lie FEAT Rihanna")) assertEquals("Lean On", clean("Lean On ft. MØ")) assertEquals("Mood", clean("Mood Ft iann dior")) assertEquals("Without You", clean("Without You featuring The Kid LAROI")) assertEquals("Take Ü There w/ Kiesza", clean("Take Ü There w/ Kiesza")) assertEquals("Dusk Till Dawn with Sia", clean("Dusk Till Dawn with Sia")) } @Test fun `title removes mix suffixes`() { assertEquals("Take on Me", clean("Take on Me (1985 Single Mix)")) assertEquals("Rock Your Body Rock", clean("Rock Your Body Rock (Extended Mix)")) assertEquals("What Is Love", clean("What Is Love (7" Mix)")) assertEquals("Need To Feel Loved", clean("Need To Feel Loved (Adam K & Soha Vocal Mix)")) assertEquals("Show Me Love (EDX Remix)", clean("Show Me Love (EDX Remix) (Radio Edit)")) assertEquals("Melody", clean("Melody (Radio Mix)")) assertEquals("For an Angel", clean("For an Angel (Radio Mix '09)")) assertEquals("Take on Me", clean("Take on Me (Single Mix)")) assertEquals("Closer (Single Remix)", clean("Closer (Single Remix)")) assertEquals("Strobe", clean("Strobe (Original Mix)")) assertEquals("Relax", clean("Relax (7" Mix)")) assertEquals("Hey Ya!", clean("Hey Ya! (Radio Mix / Club Mix)")) assertEquals("Ya Kidding", clean("Ya Kidding (Original Mix)")) assertEquals("Milkshake", clean("Milkshake (Radio Mix)")) assertEquals("Nobody To Love", clean("Nobody To Love (Extended Mix)")) assertEquals("Ain't Nobody (Loves Me Better)", clean("Ain't Nobody (Loves Me Better) (Extended Mix)")) assertEquals("The Game of Love", clean("The Game of Love (Main / Radio Mix)")) assertEquals("Asking", clean("Asking [Extended Mix]")) } @Test fun `title removes radio edit and video edit suffixes`() { assertEquals("Samurai (Go Hard)", clean("Samurai (Go Hard) (Mix Edit)")) assertEquals("Heroes", clean("Heroes (Single Edit)")) assertEquals("Wake Me Up", clean("Wake Me Up (Radio Edit)")) assertEquals("Animals", clean("Animals [Radio Edit]")) assertEquals("Booyah", clean("Booyah (Showtek Edit)")) assertEquals("Infinity", clean("Infinity (Klaas Vocal Edit)")) assertEquals("One More Time", clean("One More Time (Edit)")) assertEquals("Sandstorm", clean("Sandstorm (Original Radio Edit)")) assertEquals("Blue Monday", clean("Blue Monday (8 Minute Edit)")) assertEquals("Samurai", clean("Samurai (Mix Edit)")) assertEquals("Push The Feeling On", clean("Push The Feeling On (MK Dub Revisited Edit)")) assertEquals("Fly On The Wings Of Love", clean("Fly On The Wings Of Love (Xtm Remix Edit)")) assertEquals("Secrets", clean("Secrets (Radio Edit)")) assertEquals("Freestyler", clean("Freestyler (Radio Edit)")) assertEquals("Bailando", clean("Bailando [Video Edit]")) } @Test fun `title removes version and edition suffixes`() { assertEquals("Somebody To Love", clean("Somebody To Love (Mono Single Version)")) assertEquals("She's A Rainbow", clean("She's A Rainbow (Full Version / With Intro)")) assertEquals("Smooth Criminal", clean("Smooth Criminal (Album Version)")) assertEquals("Space Oddity", clean("Space Oddity (50th Anniversary Edition)")) assertEquals("Thriller", clean("Thriller (Special Edition)")) assertEquals("YMCA", clean("YMCA (Original Version 1978)")) assertEquals("Everybody (Backstreet's Back)", clean("Everybody (Backstreet's Back) (Extended Version)")) assertEquals("Pop Champagne", clean("Pop Champagne (Explicit Album Version)")) assertEquals("Children", clean("Children (Dream Version)")) assertEquals("Cruisin'", clean("Cruisin' (Mono Version)")) assertEquals("Hey Ma", clean("Hey Ma [Spanish Version]")) assertEquals("All The Things She Said", clean("All The Things She Said (Culture Shock Version)")) assertEquals("Get Down Saturday Night", clean("Get Down Saturday Night (Special Extended Version)")) assertEquals("San Francisco (Be Sure to Wear Flowers in Your Hair)", clean("San Francisco (Be Sure to Wear Flowers in Your Hair) (Single Version)")) assertEquals("Living in a Box", clean("Living in a Box (Reprise: Full 12" Version)")) assertEquals("Get Up (Rattle)", clean("Get Up (Rattle) (Vocal Extended Version)")) assertEquals("Beautiful Life", clean("Beautiful Life (12" Extended Version)")) assertEquals("Girl On Fire", clean("Girl On Fire (Inferno Version)")) assertEquals("Bailando", clean("Bailando (English Version)")) assertEquals("Lose Yourself", clean("Lose Yourself (Soundtrack Version)")) assertEquals("Carol of the Bells", clean("Carol of the Bells (Instrumental)")) } @Test fun `title removes single track suffixes`() { assertEquals("Stan", clean("Stan (Single)")) assertEquals("Stan", clean("Stan (Single)")) assertEquals("Creep", clean("Creep (Single Version)")) assertEquals("Teardrops", clean("Teardrops (7” Single Version)")) } @Test fun `title removes remaster and digital remaster tags`() { assertEquals("Creep", clean("Creep - 2017 Remaster")) assertEquals("I Want It All", clean("I Want It All (Digital Remaster)")) assertEquals("Ain't That A Kick In The Head", clean("Ain't That A Kick In The Head (1996 Digital Remaster)")) assertEquals("From Me To You", clean("From Me To You (Mono / Remastered)")) assertEquals("The Logical Song", clean("The Logical Song (Remastered Version/2010)")) assertEquals("Macarena (Bayside Boys Remix)", clean("Macarena (Bayside Boys Remix) (Remasterizado)")) assertEquals("Werewolves of London", clean("Werewolves of London (2007 Remaster)")) assertEquals("Don't Worry Baby", clean("Don't Worry Baby (Remastered 2001)")) assertEquals("Please Please Me", clean("Please Please Me (Remastered 2009)")) assertEquals("Blitzkrieg Bop", clean("Blitzkrieg Bop (2001 Remaster)")) assertEquals("2 Minutes to Midnight", clean("2 Minutes to Midnight (1998 Remaster)")) assertEquals("Go Your Own Way", clean("Go Your Own Way (2004 Remaster)")) assertEquals("Take The Long Way Home", clean("Take The Long Way Home (2010 Remastered)")) assertEquals("Always on My Mind", clean("Always on My Mind (2003 Remaster)")) assertEquals("Unbelievable", clean("Unbelievable (2013 Remastered)")) assertEquals("Lola", clean("Lola (2020 Stereo Remaster)")) } @Test fun `title removes soundtrack and theme tags`() { assertEquals("Back in Time", clean("Back in Time (featured in "Men In Black 3")")) assertEquals("Circle of Life", clean("Circle of Life (From "The Lion King")")) assertEquals("Main Theme", clean("Main Theme [Featured in Soundtrack]")) assertEquals("Day One", clean("Day One (Interstellar Theme)")) assertEquals("I'm Alright", clean("I'm Alright (Theme from "Caddyshack")")) assertEquals("My Heart Will Go On", clean("My Heart Will Go On (Love Theme from "Titanic")")) assertEquals("Main Theme", clean("Main Theme (From "Jurassic Park")")) assertEquals("If You Leave", clean("If You Leave (From "Pretty In Pink")")) assertEquals("Tokyo Drift (Fast & Furious)", clean("Tokyo Drift (Fast & Furious) (From "The Fast And The Furious: Tokyo Drift" Soundtrack)")) assertEquals("The Heat Is On", clean("The Heat Is On (From "Beverly Hills Cop" Soundtrack)")) assertEquals("Footloose", clean("Footloose (From "Footloose" Soundtrack)")) } @Test fun `title removes live performance and contextual event suffixes`() { assertEquals("Sweet Caroline", clean("Sweet Caroline (Live At The Greek Theatre/2012)")) } @Test fun `title removes audio quality suffixes`() { assertEquals("Don't Know Why", clean("Don't Know Why (192kHz/24bit)")) assertEquals("Hotel California", clean("Hotel California (96kHz/24bit)")) assertEquals("Money For Nothing", clean("Money For Nothing (44.1kHz/16bit)")) assertEquals("Dreams", clean("Dreams (192.0kHz/24bit)")) assertEquals("For An Angel", clean("For An Angel (Lossless Explicit Mono)")) assertEquals("9 PM (Till I Come)", clean("9 PM (Till I Come) (Stereo Ultra-Clean)")) } @Test fun `title removes anime OP and ED suffixes`() { assertEquals("Unravel", clean("Unravel (OP 1)")) assertEquals("Again", clean("Again [Opening 2]")) assertEquals("Real Folk Blues", clean("Real Folk Blues - ED")) assertEquals("Fly Me To The Moon", clean("Fly Me To The Moon (Ending)")) assertEquals("Gurenge", clean("【OP】 Gurenge")) assertEquals("Blue Bird", clean("[Naruto OP 3] Blue Bird")) } @Test fun `title removes bonus tracks and handles regional formatting`() { assertEquals("A Cruel Angel's Thesis", clean("A Cruel Angel's Thesis 【Director's Cut】")) assertEquals("Welcome to New York", clean("Welcome to New York - Bonus Track")) } @Test fun `title standardizes exact bracket contents with irregular spacing`() { assertEquals("God's Plan", clean("God's Plan ( Official Video )")) assertEquals("Blinding Lights (Remix)", clean("Blinding Lights ( Official Music Video ) [Remix]")) assertEquals("Thriller", clean("Thriller [ music video ]")) } @Test fun `title removes syntax anomalies and typos`() { assertEquals("Booyah", clean("Booyah (Radio -)")) assertEquals("Lethal Industry", clean("Lethal Industry (Original Mix ~)")) assertEquals("Jumpin' Jack Flash", clean("Jumpin' Jack Flash ((Original Single Mono Version))")) assertEquals("Sandstorm", clean("Sandstorm [[Extended Mix]]")) assertEquals("Fix You", clean("Fix You (foo]")) assertEquals("Fix You", clean("Fix You [Remix)")) } @Test fun `title balances nested and split internal suffixes`() { assertEquals("Silence (Niels van Gogh vs Thomas Gold Remix)", clean("Silence (Niels van Gogh vs Thomas Gold Remix [Radio Edit])")) assertEquals("Together Onetime (Butch Remix)", clean("Together Onetime (Butch Remix [Mixed])")) assertEquals("Insomnia (Monster Mix)", clean("Insomnia (Monster Mix) [Radio Edit]")) assertEquals("Summertime Sadness (Cedric Gervais Remix)", clean("Summertime Sadness (Cedric Gervais Remix / Radio Edit)")) assertEquals("From Me To You", clean("From Me To You (Mono / Remastered)")) assertEquals("Children (Robert Miles Mix)", clean("Children (Robert Miles Mix | Radio Cut)")) assertEquals("Born Slippy (Nuxx)", clean("Born Slippy (Nuxx / Remastered)")) assertEquals("Where the Wild Roses Grow", clean("Where the Wild Roses Grow (2011 - Remaster)")) assertEquals("Money (That's What I Want)", clean("Money (That's What I Want) (Single Version / Mono)")) assertEquals("Just My Imagination (Running Away With Me)", clean("Just My Imagination (Running Away With Me) (Single Version/Mono)")) } @Test fun `title iteratively peels complex and compound tags`() { assertEquals("Alive", clean("Alive (Live at Tokyo Dome) [Bonus Track]")) assertEquals("Cornfield Chase", clean("Cornfield Chase (Original Soundtrack) - Deluxe Edition")) assertEquals("Surrender", clean("Surrender (Remastered 2024) [Live at Budokan] (Bonus Track)")) assertEquals("Creep", clean("Creep (New Version) (New Version; 2017 Remaster)")) assertEquals("Take on Me", clean("Take on Me (1985 Single Mix) (1985 Single Mix; 2015 Remaster)")) assertEquals("Epic Anthem", clean("Epic Anthem (Remastered 2026) [Live at Wembley] (Bonus Track) [Radio Edit]")) } @Test fun `title matches un-bracketed trailing tags`() { assertEquals("Tacata", clean("Tacata Radio Edit")) assertEquals("Language", clean("Language Extended Mix")) assertEquals("Smooth Criminal", clean("Smooth Criminal Album Version")) } @Test fun `title sanitizes leading track numbers`() { assertEquals("Smells Like Teen Spirit", clean("01. Smells Like Teen Spirit")) assertEquals("Mr. Brightside", clean("(02) Mr. Brightside")) assertEquals("Wonderwall", clean("03 Wonderwall")) } // ==================================================================== // ARTIST CLEANING & COLLABORATOR TESTS // ==================================================================== @Test fun `artist handles empty and blank inputs`() { assertEquals("Unknown Artist", cleanArtist("")) assertEquals("Unknown Artist", cleanArtist(" ")) } @Test fun `artist preserves basic strings`() { assertEquals("Gostan", cleanArtist("Gostan")) assertEquals("KSHMR", cleanArtist("KSHMR")) assertEquals("Tiësto", cleanArtist("Tiësto")) } @Test fun `artist preserves specific multi-artist names`() { assertEquals("Pola & Bryson", cleanArtist("Pola & Bryson")) assertEquals("Calvin Harris & Alesso", cleanArtist("Calvin Harris & Alesso")) assertEquals("Chase & Status & Bou", cleanArtist("Chase & Status & Bou")) } @Test fun `artist removes contextual streaming tags`() { assertEquals("Norah Jones", cleanArtist("Norah Jones • Lossless")) assertEquals("Taylor Swift", cleanArtist("Taylor Swift • Recommended for you")) assertEquals("The Weeknd", cleanArtist("The Weeknd • Daily Mix 1")) } @Test fun `artist splits un-bracketed feature tags`() { assertEquals("Eminem", cleanArtist("Eminem feat. Rihanna")) assertEquals("Drake", cleanArtist("Drake FEAT Future")) assertEquals("DJ Snake", cleanArtist("DJ Snake ft. Justin Bieber")) assertEquals("Kendrick Lamar", cleanArtist("Kendrick Lamar Ft SZA")) assertEquals("Bruno Mars", cleanArtist("Bruno Mars featuring Cardi B")) assertEquals("Skrillex w/ Diplo", cleanArtist("Skrillex w/ Diplo")) assertEquals("Linkin Park with Jay-Z", cleanArtist("Linkin Park with Jay-Z")) } @Test fun `artist appropriately segments split multiple artists`() { assertEquals("Dimitri Vegas", cleanArtist("Dimitri Vegas, Like Mike & Steve Aoki vs Ummet Ozcan")) assertEquals("Dimitri Vegas & Like Mike", cleanArtist("Dimitri Vegas & Like Mike, Steve Aoki & Ummet Ozcan")) } @Test fun `artist accurately extracts primary and collaborating artists`() { // Complex Collab String TrackProcessor.process(context, artist = "Dimitri Vegas, Like Mike & Steve Aoki vs Ummet Ozcan", title = "").let { assertEquals("Dimitri Vegas", it.primaryArtist) assertEquals(listOf("Like Mike", "Steve Aoki", "Ummet Ozcan"), it.collaboratingArtists) } // Title Mixed Collab String TrackProcessor.process(context, artist = "Lost Frequencies, Andromedik & Bonn", title = "The Feeling (Lost Frequencies & Andromedik Deluxe Mix)").let { assertEquals("Lost Frequencies", it.primaryArtist) assertEquals(listOf("Andromedik", "Bonn"), it.collaboratingArtists) } // Expanded Complex Collab TrackProcessor.process(context, artist = "Dimitri Vegas & Like Mike, Tiësto, W&W & Dido", title = "Thank You: Not So Bad (Dimitri Vegas & Pirupa Remix)").let { assertEquals("Dimitri Vegas", it.primaryArtist) assertEquals(listOf("Like Mike", "Tiësto", "W&W", "Dido"), it.collaboratingArtists) } // Traditional Feature TrackProcessor.process(context, artist = "Calvin Harris feat. Rihanna", title = "").let { assertEquals("Calvin Harris", it.primaryArtist) assertEquals(listOf("Rihanna"), it.collaboratingArtists) } // Ampersand split TrackProcessor.process(context, "Grafix & WHAT EVA", "Unknown Title").let { assertEquals("Grafix & WHAT EVA", it.displayArtist) assertEquals("Grafix", it.primaryArtist) assertEquals(listOf("WHAT EVA"), it.collaboratingArtists) } // Commas and ampersands TrackProcessor.process(context, "Supermode, Axwell & Steve Angello", "Unknown Title").let { assertEquals("Supermode", it.primaryArtist) assertEquals(listOf("Axwell", "Steve Angello"), it.collaboratingArtists) } } @Test fun `artist handles zero collaborators gracefully`() { TrackProcessor.process(context, artist = "Madonna", title = "").let { assertEquals("Madonna", it.primaryArtist) assertEquals(emptyList(), it.collaboratingArtists) } } // ==================================================================== // NORMALIZATION & FORMATTING TESTS // ==================================================================== @Test fun `normalization standardizes varied punctuation and quotes`() { // Single Quotes / Apostrophes assertEquals("It's My Life", clean("It`s My Life")) assertEquals("It's My Life", clean("It´s My Life")) assertEquals("It's My Life", clean("It‘s My Life")) assertEquals("It's My Life", clean("It’s My Life")) // Asian Bracket Single Quotes -> Single Quotes assertEquals("'Gurenge'", clean("「Gurenge」")) // Double Quotes / Smart Quotes assertEquals(""Heroes"", clean("“Heroes”")) assertEquals(""Heroes"", clean("„Heroes“")) // Asian Bracket Double Quotes / Guillemets -> Standard Double Quotes assertEquals(""Demon Slayer"", clean("『Demon Slayer』")) assertEquals(""Into the Unknown"", clean("«Into the Unknown»")) // Dashes / Hyphens assertEquals("B-Side", clean("B–Side")) assertEquals("B-Side", clean("B—Side")) assertEquals("B-Side", clean("B•Side")) // Multiple spaces flattening assertEquals("A B C", clean("A B C")) // Ensure stripping still works after normalization assertEquals("Into the Unknown", clean("Into the Unknown (From “Frozen II”)")) assertEquals("A Cruel Angel's Thesis", clean("A Cruel Angel's Thesis [From 『Evangelion』]")) } @Test fun `normalization preserves apostrophes and special characters`() { val track = TrackProcessor.process(context, "Nu Look", "Pa Anmede'm") assertEquals("Nu Look", track.displayArtist) assertEquals("Pa Anmede'm", track.displayTitle) assertEquals(">>>hands on me<<<", clean(">>>hands on me<<<")) } @Test fun `normalization preserves intentional mixed case stylization`() { val track1 = TrackProcessor.process(context, "Redfoo", "New Thang") assertEquals("Redfoo", track1.displayArtist) val track2 = TrackProcessor.process(context, "t.A.T.u.", "All The Things She Said") assertEquals("t.A.T.u.", track2.displayArtist) val track3 = TrackProcessor.process(context, "McDonald", "Title") assertEquals("McDonald", track3.displayArtist) } // ==================================================================== // CORE COMPONENTS, SORTING & PREFERENCES TESTS // ==================================================================== @Test fun `integration processes standard dataset metadata correctly`() { val track1 = TrackProcessor.process(context, "Ramones", "Blitzkrieg Bop") assertEquals("Ramones", track1.displayArtist) assertEquals("Blitzkrieg Bop", track1.displayTitle) val track2 = TrackProcessor.process(context, "Queen", "Keep Yourself Alive") assertEquals("Queen", track2.displayArtist) assertEquals("Keep Yourself Alive", track2.displayTitle) val track3 = TrackProcessor.process(context, "James Hype", "More Than Friends") assertEquals("James Hype", track3.displayArtist) assertEquals("More Than Friends", track3.displayTitle) } @Test fun `sorting implements correct natural order`() { val track2 = TrackProcessor.process(context, "Beethoven", "Symphony No. 2") val track10 = TrackProcessor.process(context, "Beethoven", "Symphony No. 10") val track1 = TrackProcessor.process(context, "Beethoven", "Symphony No. 1") val track3 = TrackProcessor.process(context, "Beethoven", "Symphony No. 03") val track20 = TrackProcessor.process(context, "Beethoven", "Symphony No. 20") val track12 = TrackProcessor.process(context, "Beethoven", "Symphony No. 12") val sortedList = listOf(track2, track10, track1, track3, track20, track12).sorted() assertEquals("Symphony No. 1", sortedList[0].displayTitle) assertEquals("Symphony No. 2", sortedList[1].displayTitle) assertEquals("Symphony No. 03", sortedList[2].displayTitle) assertEquals("Symphony No. 10", sortedList[3].displayTitle) assertEquals("Symphony No. 12", sortedList[4].displayTitle) assertEquals("Symphony No. 20", sortedList[5].displayTitle) } @Test fun `sorting maintains integrity with dataset examples`() { val trackA = TrackProcessor.process(context, "Queen", "Keep Yourself Alive") val trackB = TrackProcessor.process(context, "Ramones", "Blitzkrieg Bop") val trackC = TrackProcessor.process(context, "Nu Look", "Pa Anmede'm") val trackList = mutableListOf(trackA, trackB, trackC) trackList.sort() assertEquals("Blitzkrieg Bop", trackList[0].displayTitle) assertEquals("Keep Yourself Alive", trackList[1].displayTitle) assertEquals("Pa Anmede'm", trackList[2].displayTitle) } @Test fun `preferences respect custom regex title replacement`() { val prefs = PreferenceManager.getDefaultSharedPreferences(context) prefs.edit().putString(context.getString(R.string.preferences_custom_regex_title), "VIP Mix -> VIP").apply() val trackCustom = TrackProcessor.process(context, "Martin Garrix", "Animals VIP Mix") assertEquals("Animals VIP", trackCustom.displayTitle) } @Test fun `preferences respect split multiple artists toggle`() { val prefs = PreferenceManager.getDefaultSharedPreferences(context) // When enabled prefs.edit().putBoolean(context.getString(R.string.preferences_split_multiple_artists), true).apply() val trackSplit = TrackProcessor.process(context, "Kanye West, Jay-Z", "Niggas In Paris") assertEquals("Kanye West", trackSplit.displayArtist) // When disabled prefs.edit().putBoolean(context.getString(R.string.preferences_split_multiple_artists), false).apply() val trackNoSplit = TrackProcessor.process(context, "Kanye West, Jay-Z", "Niggas In Paris") assertEquals("Kanye West, Jay-Z", trackNoSplit.displayArtist) } // ========================================================================= // Billboard & Spotify Top 100 Metadata Patterns // ========================================================================= @Test fun `test chart hits with complex collaborations`() { // Pattern: Main Artist, Collaborator 1 & Collaborator 2 val track1 = TrackProcessor.process(context, "Metro Boomin, The Weeknd, 21 Savage", "Creepin'") assertEquals("Metro Boomin", track1.primaryArtist) assertEquals(listOf("The Weeknd", "21 Savage"), track1.collaboratingArtists) assertEquals("Creepin'", track1.displayTitle) // Pattern: '&' Ampersand splits val track2 = TrackProcessor.process(context, "The Kid LAROI & Justin Bieber", "STAY") assertEquals("The Kid LAROI", track2.primaryArtist) assertEquals(listOf("Justin Bieber"), track2.collaboratingArtists) // Pattern: Multiple ampersands/commas in dance/DJ tracks val track3 = TrackProcessor.process(context, "David Guetta, Bebe Rexha & J Balvin", "Say My Name") assertEquals("David Guetta", track3.primaryArtist) assertEquals(listOf("Bebe Rexha", "J Balvin"), track3.collaboratingArtists) } @Test fun `test chart hits with extreme title stylization (Capitalization Normalization)`() { // Billie Eilish uses all caps for her recent albums val track1 = TrackProcessor.process(context, "Billie Eilish", "LUNCH") assertEquals("LUNCH", track1.displayTitle) val track2 = TrackProcessor.process(context, "Billie Eilish", "BIRDS OF A FEATHER") assertEquals("BIRDS OF A FEATHER", track2.displayTitle) // Olivia Rodrigo and Ariana Grande often use all lowercase val track3 = TrackProcessor.process(context, "Olivia Rodrigo", "bad idea right?") assertEquals("bad idea right?", track3.displayTitle) val track4 = TrackProcessor.process(context, "Ariana Grande", "we can't be friends") assertEquals("we can't be friends", track4.displayTitle) } @Test fun `test chart hits with parentheses and special tags (Remixes, Features, Versions)`() { // Taylor Swift "(Taylor's Version)" and "(From The Vault)" tags assertEquals( "All Too Well", clean("All Too Well (10 Minute Version) (Taylor's Version) (From The Vault)") ) // Standard Remix tags assertEquals("Save Your Tears (Remix)", clean("Save Your Tears (Remix)")) assertEquals("Karma", clean("Karma (feat. Ice Spice)")) assertEquals("Levitating", clean("Levitating (feat. DaBaby)")) // Live / Acoustic versions assertEquals("Hotel California", clean("Hotel California - Live On MTV Unplugged, 1994")) } @Test fun `test contemporary chart artists with numbers and symbols`() { // P!nk: Prefix is 'P', first letter is 'P', remainder is '!nk' assertEquals("P!nk", cleanArtist("P!nk")) // 6LACK: First letter is 'L'. assertEquals("6LACK", cleanArtist("6LACK")) // 21 Savage: First word "21" has no letters. Second word "Savage". assertEquals("21 Savage", cleanArtist("21 Savage")) // A$AP Rocky: Special characters mixed with letters assertEquals("A$AP Rocky", cleanArtist("A$AP Rocky")) } @Test fun `test k-pop and international chart hits`() { // BTS / BLACKPINK chart hits often feature dual languages or mixed capitalization val track1 = TrackProcessor.process(context, "BLACKPINK", "DDU-DU DDU-DU") // "BLACKPINK" is all caps assertEquals("BLACKPINK", track1.displayArtist) // "DDU-DU" is all caps assertEquals("DDU-DU DDU-DU", track1.displayTitle) val track2 = TrackProcessor.process(context, "BTS", "Dynamite") assertEquals("BTS", track2.displayArtist) assertEquals("Dynamite", track2.displayTitle) } @Test fun `test top favorited songs`() { assertEquals("1982", clean("1982")) assertEquals("500 Miles Away from Home", clean("500 Miles Away from Home")) assertEquals("5 Foot 9", clean("5 Foot 9")) assertEquals("A Cowboy Would", clean("A Cowboy Would")) assertEquals("A Dustland Fairytale", clean("A Dustland Fairytale")) assertEquals("A Fire I Can't Put Out", clean("A Fire I Can't Put Out")) assertEquals("A Girl Called Johnny", clean("A Girl Called Johnny")) assertEquals("Ain't A Bad Life", clean("Ain't A Bad Life")) assertEquals("Ain't It the Truth", clean("Ain't It the Truth")) assertEquals("Ain't Nobody (Loves Me Better)", clean("Ain't Nobody (Loves Me Better)")) assertEquals("All For You", clean("All For You")) assertEquals("All Over The Map (Commentary)", clean("All Over The Map (Commentary)")) assertEquals("All These Things That I've Done", clean("All These Things That I've Done")) assertEquals("Alluring Smiles", clean("Alluring Smiles")) assertEquals("Almost", clean("Almost")) assertEquals("Among the Stars", clean("Among the Stars")) assertEquals("And a Bang on the Ear", clean("And a Bang on the Ear")) assertEquals("Another Lonely Night", clean("Another Lonely Night")) assertEquals("Anything But Mine", clean("Anything But Mine")) assertEquals("Anytime Soon", clean("Anytime Soon")) assertEquals("A Pagan Place", clean("A Pagan Place")) assertEquals("Appetite", clean("Appetite")) assertEquals("Armageddon It", clean("Armageddon It")) assertEquals("Around the World", clean("Around the World")) assertEquals("A Woman Loves", clean("A Woman Loves")) assertEquals("Backroads Of My Mind", clean("Backroads Of My Mind")) assertEquals("Beat It", clean("Beat It")) assertEquals("Be Her", clean("Be Her")) assertEquals("Best Friend (Remix)", clean("Best Friend (Remix)")) assertEquals("Be Still", clean("Be Still")) assertEquals("Better Than I Used To Be", clean("Better Than I Used To Be")) assertEquals("Billie Jean", clean("Billie Jean")) assertEquals("Black Water", clean("Black Water")) assertEquals("Blasphemous Rumours", clean("Blasphemous Rumours")) assertEquals("Blinkin Bad", clean("Blinkin Bad")) assertEquals("Blowback", clean("Blowback")) assertEquals("Born Too Late", clean("Born Too Late")) assertEquals("Bottom Of Your Boots", clean("Bottom Of Your Boots")) assertEquals("Breathe (Until We Are No More)", clean("Breathe (Until We Are No More)")) assertEquals("Brighter Days", clean("Brighter Days")) assertEquals("Burma Road", clean("Burma Road")) assertEquals("Cala de Mallorca", clean("Cala de Mallorca")) assertEquals("Can She Have This Dance", clean("Can She Have This Dance")) assertEquals("Carrie", clean("Carrie")) assertEquals("Caution", clean("Caution")) assertEquals("Chal Kechal", clean("Chal Kechal")) assertEquals("Chaska", clean("Chaska")) assertEquals("CHE CHE KULE", clean("CHE CHE KULE")) assertEquals("Church Not Made with Hands", clean("Church Not Made with Hands")) assertEquals("Circus for a Psycho", clean("Circus for a Psycho")) assertEquals("Code Of The Streets", clean("Code Of The Streets")) assertEquals("Come Sundown", clean("Come Sundown")) assertEquals("Concerto No. 2 in C Minor for Piano and Orchestra, Op. 18: II. Adagio sostenuto", clean("Concerto No. 2 in C Minor for Piano and Orchestra, Op. 18: II. Adagio sostenuto")) assertEquals("Cowboys And Dreamers", clean("Cowboys And Dreamers")) assertEquals("Cry for You", clean("Cry for You")) assertEquals("Dale Don Dale", clean("Dale Don Dale")) assertEquals("Damn You", clean("Damn You")) assertEquals("Danza Kuduro", clean("Danza Kuduro")) assertEquals("Darkness", clean("Darkness")) assertEquals("Dearly Beloved", clean("Dearly Beloved (-KINGDOM HEARTS II Version-)")) assertEquals("Dein Ist Mein Ganzes Herz", clean("Dein Ist Mein Ganzes Herz")) assertEquals("Disenchanted", clean("Disenchanted")) assertEquals("Don't Say You Love Me", clean("Don’t Say You Love Me")) assertEquals("Don't Say You Love Me (90s Pop Remix)", clean("Don’t Say You Love Me (90s Pop Remix)")) assertEquals("Don't Say You Love Me (Synthwave Remix)", clean("Don’t Say You Love Me (Synthwave Remix)")) assertEquals("Don't Start Now", clean("Don't Start Now")) assertEquals("Don't Worry Be Happy", clean("Don't Worry Be Happy")) assertEquals("Dreiton", clean("Dreiton")) assertEquals("Drinking About You", clean("Drinking About You")) assertEquals("Drowning", clean("Drowning")) assertEquals("Drowning in Berlin", clean("Drowning in Berlin")) assertEquals("Elevators (Me & You)", clean("Elevators (Me & You)")) assertEquals("Elle Varner (Pour Me)", clean("Elle Varner (Pour Me Feat. Wale)")) assertEquals("Empty Room", clean("Empty Room")) assertEquals("Erased Dreams (1 Hour Rain Piano)", clean("Erased Dreams (1 Hour Rain Piano)")) assertEquals("Even If I Wanted To", clean("Even If I Wanted To")) assertEquals("Experiment in Terror", clean("Experiment in Terror")) assertEquals("Face To The Floor", clean("Face To The Floor")) assertEquals("Fall For Those I's", clean("Fall For Those I's")) assertEquals("Farandole", clean("Farandole")) assertEquals("Foolish Pride", clean("Foolish Pride")) assertEquals("Forever Aligned", clean("Forever Aligned")) assertEquals("Forever Together", clean("Forever Together")) assertEquals("Forgotten Land", clean("Forgotten Land")) assertEquals("For Reasons Unknown", clean("For Reasons Unknown")) assertEquals("Galway Girl", clean("Galway Girl")) assertEquals("Games People Play", clean("Games People Play")) assertEquals("Gehra Hua", clean("Gehra Hua")) assertEquals("Generous", clean("Generous")) assertEquals("Ghost House", clean("Ghost House")) assertEquals("Ghosts", clean("Ghosts")) assertEquals("Girl I Can't Control It", clean("Girl I Can't Control It")) assertEquals("Girl On The Billboard", clean("Girl On The Billboard")) assertEquals("Give Me the Night", clean("Give Me the Night")) assertEquals("God And Country Music", clean("God And Country Music")) assertEquals("Gods Of War", clean("Gods Of War")) assertEquals("Good Thing Going", clean("Good Thing Going")) assertEquals("Guillotine (Swordz)", clean("Guillotine (Swordz)")) assertEquals("Hang of Hangin' On", clean("Hang of Hangin’ On")) assertEquals("Haute Tension", clean("Haute Tension")) assertEquals("Have You Ever Needed Someone So Bad", clean("Have You Ever Needed Someone So Bad")) assertEquals("Hello Juliet", clean("Hello Juliet")) assertEquals("Here Without You", clean("Here Without You")) assertEquals("Her Goodbye Hit Me In The Heart", clean("Her Goodbye Hit Me In The Heart")) assertEquals("Hideaway", clean("Hideaway")) assertEquals("High Noon Neon", clean("High Noon Neon")) assertEquals("Holy Land", clean("Holy Land")) assertEquals("Hot Sex", clean("Hot Sex")) assertEquals("How Far Does A Goodbye Go", clean("How Far Does A Goodbye Go")) assertEquals("How Long Will I Love You", clean("How Long Will I Love You")) assertEquals("How Would You Feel (Paean)", clean("How Would You Feel (Paean)")) assertEquals("Human", clean("Human")) assertEquals("Humble And Kind", clean("Humble And Kind")) assertEquals("If Tomorrow Never Comes", clean("If Tomorrow Never Comes")) assertEquals("I Get It", clean("I Get It")) assertEquals("I Got Better", clean("I Got Better")) assertEquals("I'll Take The Dog", clean("I'll Take The Dog")) assertEquals("I Love You More", clean("I Love You More")) assertEquals("I'm Gonna Leave You Tomorrow", clean("I'm Gonna Leave You Tomorrow")) assertEquals("I'm Not in Love", clean("I'm Not in Love")) assertEquals("I'm Out", clean("I'm Out")) assertEquals("Imploding The Mirage", clean("Imploding The Mirage")) assertEquals("I'm Walking the Dog", clean("I'm Walking the Dog")) assertEquals("In My Dreams", clean("In My Dreams")) assertEquals("Interlude", clean("Interlude")) assertEquals("In the Misty Moonlight", clean("In the Misty Moonlight")) assertEquals("Invictus", clean("Invictus")) assertEquals("In Your Eyes", clean("In Your Eyes")) assertEquals("It Ain't Over 'Til It's Over", clean("It Ain't Over 'Til It's Over")) assertEquals("I Took A Pill In Ibiza (Seeb Remix)", clean("I Took A Pill In Ibiza (Seeb Remix)")) assertEquals("I Would Have Loved You All Night Long", clean("I Would Have Loved You All Night Long")) assertEquals("Jack and Diet Coke (Juicy J Remix)", clean("Jack and Diet Coke (Juicy J Remix)")) assertEquals("Jenny Was A Friend Of Mine", clean("Jenny Was A Friend Of Mine")) assertEquals("Juke", clean("Juke")) assertEquals("Just Another Girl", clean("Just Another Girl")) assertEquals("Just for tonight-Vanessa Williams", clean("Just for tonight-Vanessa Williams")) assertEquals("Just the Way It Is, Baby", clean("Just the Way It Is, Baby")) assertEquals("Know The Ledge", clean("Know The Ledge")) assertEquals("Let Me Love You", clean("Let Me Love You")) assertEquals("Levitating", clean("Levitating")) assertEquals("Lightning Fields", clean("Lightning Fields")) assertEquals("Little Hometown Left", clean("Little Hometown Left")) assertEquals("Looking Back to See", clean("Looking Back to See")) assertEquals("Loser", clean("Loser")) assertEquals("Lose This Feeling (Dimension Extended Remix)", clean("Lose This Feeling (Dimension Extended Remix)")) assertEquals("Losing Touch", clean("Losing Touch")) assertEquals("Love Again", clean("Love Again")) assertEquals("Lovefool", clean("Lovefool")) assertEquals("Love Me Tonight", clean("Love Me Tonight")) assertEquals("Love to Hate You", clean("Love to Hate You (2019 (Remaster))")) assertEquals("Love Train", clean("Love Train")) assertEquals("Love You Again", clean("Love You Again")) assertEquals("Luv U Bwoi R.I.P", clean("Luv U Bwoi R.I.P")) assertEquals("Maid Of Orleans (The Waltz Joan Of Arc)", clean("Maid Of Orleans (The Waltz Joan Of Arc)")) assertEquals("Manchild", clean("Manchild")) assertEquals("Mandy", clean("Mandy")) assertEquals("May the Bird of Paradise Fly up Your Nose", clean("May the Bird of Paradise Fly up Your Nose")) assertEquals("Midnight Show", clean("Midnight Show")) assertEquals("Misery Loves Company", clean("Misery Loves Company")) assertEquals("Move Ya Body", clean("Move Ya Body")) assertEquals("Mr. Brightside", clean("Mr. Brightside")) assertEquals("My Babe", clean("My Babe")) assertEquals("My Faith In You", clean("My Faith In You")) assertEquals("My God", clean("My God")) assertEquals("My Head & My Heart", clean("My Head & My Heart")) assertEquals("My Own Soul's Warning", clean("My Own Soul’s Warning")) assertEquals("Neon Odyssey", clean("Neon Odyssey")) assertEquals("Never Ever", clean("Never Ever")) assertEquals("NIEMAND", clean("NIEMAND")) assertEquals("Nobody's Home", clean("Nobody's Home")) assertEquals("No Diggity", clean("No Diggity")) assertEquals("Nothing Without Your Love", clean("Nothing Without Your Love")) assertEquals("Ocean Front Property", clean("Ocean Front Property")) assertEquals("Old England", clean("Old England")) assertEquals("One Goodbye", clean("One Goodbye")) assertEquals("One Kiss", clean("One Kiss")) assertEquals("One More Won't Hurt", clean("One More Won’t Hurt")) assertEquals("One Time", clean("One Time")) assertEquals("One To Willie", clean("One To Willie")) assertEquals("Only the Young", clean("Only the Young")) assertEquals("On The Rocks", clean("On The Rocks")) assertEquals("On Top", clean("On Top")) assertEquals("Oye Mi Canto", clean("Oye Mi Canto (Radio)")) assertEquals("Paint It, Black", clean("Paint It, Black")) assertEquals("Park", clean("Park")) assertEquals("Peel Me A 'Nanner", clean("Peel Me A 'Nanner")) assertEquals("Pick-Up Lines", clean("Pick-Up Lines")) assertEquals("Please Don't Squeeze My Sharmon", clean("Please Don't Squeeze My Sharmon")) assertEquals("Please Help Me, I'm Falling", clean("Please Help Me, I'm Falling")) assertEquals("Point of Light", clean("Point of Light")) assertEquals("Proud Mary", clean("Proud Mary")) assertEquals("Purple Hearts", clean("Purple Hearts")) assertEquals("Quicksilver", clean("Quicksilver")) assertEquals("Quiero Conocer A Jesús (Yeshua) / Nuestro Dios", clean("Quiero Conocer A Jesús (Yeshua) / Nuestro Dios")) assertEquals("Quiet Town", clean("Quiet Town")) assertEquals("Quittin' Time", clean("Quittin’ Time")) assertEquals("R. Kelly (Bump N' Grind)", clean("R. Kelly (Bump N' Grind)")) assertEquals("(There's) No Gettin' Over Me", clean("(There's) No Gettin' Over Me")) assertEquals("(We Don't Need This) Fascist Groove Thang", clean("(We Don't Need This) Fascist Groove Thang (2006 Digital Remaster)")) } @Test fun `test top favorited artists`() { assertEquals("Aditya Rikhari", cleanArtist("Aditya Rikhari")) assertEquals("Alabama", cleanArtist("Alabama")) assertEquals("Alan Jackson", cleanArtist("Alan Jackson")) assertEquals("Alligatoah", cleanArtist("Alligatoah")) assertEquals("All Saints", cleanArtist("All Saints")) assertEquals("Almah", cleanArtist("Almah")) assertEquals("Amistades Peligrosas", cleanArtist("Amistades Peligrosas")) assertEquals("Angra", cleanArtist("Angra")) assertEquals("Armin van Buuren", cleanArtist("Armin van Buuren")) assertEquals("Atmosphere", cleanArtist("Atmosphere")) assertEquals("A Tribe Called Quest", cleanArtist("A Tribe Called Quest")) assertEquals("Ava Max", cleanArtist("Ava Max")) assertEquals("Bailey Zimmerman", cleanArtist("Bailey Zimmerman")) assertEquals("Barry Manilow", cleanArtist("Barry Manilow")) assertEquals("Bennie Mellies", cleanArtist("Bennie Mellies")) assertEquals("Beyoncé", cleanArtist("Beyoncé")) assertEquals("Blackstreet", cleanArtist("Blackstreet")) assertEquals("Blake Whiten", cleanArtist("Blake Whiten")) assertEquals("Bobby Bare", cleanArtist("Bobby Bare")) assertEquals("Bobby McFerrin", cleanArtist("Bobby McFerrin")) assertEquals("Brantley Gilbert", cleanArtist("Brantley Gilbert")) assertEquals("Brooks & Dunn", cleanArtist("Brooks & Dunn")) assertEquals("BTS", cleanArtist("BTS")) assertEquals("C418", cleanArtist("C418")) assertEquals("Calvin Harris & Dua Lipa", cleanArtist("Calvin Harris & Dua Lipa")) assertEquals("Carter Ryan", cleanArtist("Carter Ryan")) assertEquals("Catpack & Jacob Mann", cleanArtist("Catpack & Jacob Mann")) assertEquals("Charlie Walker", cleanArtist("Charlie Walker")) assertEquals("Chase Matthew", cleanArtist("Chase Matthew")) assertEquals("Chevelle", cleanArtist("Chevelle")) assertEquals("Chillaxing Jazz Kollektion", cleanArtist("Chillaxing Jazz Kollektion")) assertEquals("Chris Lorenzo", cleanArtist("Chris Lorenzo")) assertEquals("Chris Young", cleanArtist("Chris Young")) assertEquals("Ciara", cleanArtist("Ciara")) assertEquals("Cinderella", cleanArtist("Cinderella")) assertEquals("Clarion", cleanArtist("Clarion")) assertEquals("Clint Black", cleanArtist("Clint Black")) assertEquals("Cole Swindell", cleanArtist("Cole Swindell")) assertEquals("Creedence Clearwater Revival", cleanArtist("Creedence Clearwater Revival")) assertEquals("Daft Punk", cleanArtist("Daft Punk")) assertEquals("Daryle Singletary", cleanArtist("Daryle Singletary")) assertEquals("Def Leppard", cleanArtist("Def Leppard")) assertEquals("Del Reeves", cleanArtist("Del Reeves")) assertEquals("Depeche Mode", cleanArtist("Depeche Mode")) assertEquals("DJ Snake", cleanArtist("DJ Snake")) assertEquals("Don Henley", cleanArtist("Don Henley")) assertEquals("Don Omar", cleanArtist("Don Omar")) assertEquals("Dream", cleanArtist("Dream")) assertEquals("Drew Baldridge", cleanArtist("Drew Baldridge")) assertEquals("Drew Green", cleanArtist("Drew Green")) assertEquals("Dua Lipa", cleanArtist("Dua Lipa")) assertEquals("Dylan Marlowe", cleanArtist("Dylan Marlowe")) assertEquals("Ed Sheeran", cleanArtist("Ed Sheeran")) assertEquals("Ella Langley", cleanArtist("Ella Langley")) assertEquals("Erasure", cleanArtist("Erasure")) assertEquals("Eric B. & Rakim", cleanArtist("Eric B. & Rakim")) assertEquals("Europe", cleanArtist("Europe")) assertEquals("Evanescence", cleanArtist("Evanescence")) assertEquals("Felix Jaehn", cleanArtist("Felix Jaehn")) assertEquals("Freddy Weller", cleanArtist("Freddy Weller")) assertEquals("Gang Starr", cleanArtist("Gang Starr")) assertEquals("Garth Brooks", cleanArtist("Garth Brooks")) assertEquals("Gene Krupa", cleanArtist("Gene Krupa")) assertEquals("Generación 12 & Fernando Ramos", cleanArtist("Generación 12 & Fernando Ramos")) assertEquals("George Benson", cleanArtist("George Benson")) assertEquals("George Hamilton IV", cleanArtist("George Hamilton IV")) assertEquals("George Jones", cleanArtist("George Jones")) assertEquals("George Strait", cleanArtist("George Strait")) assertEquals("Ghosty Girl", cleanArtist("Ghosty Girl")) assertEquals("Goth Money", cleanArtist("Goth Money")) assertEquals("Greg Adams", cleanArtist("Greg Adams")) assertEquals("Greta Van Fleet", cleanArtist("Greta Van Fleet")) assertEquals("HANA", cleanArtist("HANA")) assertEquals("Hank Locklin", cleanArtist("Hank Locklin")) assertEquals("Heaven 17", cleanArtist("Heaven 17")) assertEquals("Heinz Rudolf Kunze", cleanArtist("Heinz Rudolf Kunze")) assertEquals("Henry Mancini & His Orchestra", cleanArtist("Henry Mancini & His Orchestra")) assertEquals("Holly Johnson", cleanArtist("Holly Johnson")) assertEquals("Jamison Ross", cleanArtist("Jamison Ross")) assertEquals("Jason Aldean", cleanArtist("Jason Aldean")) assertEquals("Jay Webb", cleanArtist("Jay Webb")) assertEquals("Jean Shepard", cleanArtist("Jean Shepard")) assertEquals("Jin", cleanArtist("Jin")) assertEquals("John Schneider", cleanArtist("John Schneider")) assertEquals("Journey", cleanArtist("Journey")) assertEquals("Justin Bieber", cleanArtist("Justin Bieber")) assertEquals("Justin Moore", cleanArtist("Justin Moore")) assertEquals("Keith Whitley", cleanArtist("Keith Whitley")) assertEquals("Kenny Chesney", cleanArtist("Kenny Chesney")) assertEquals("Kiesza", cleanArtist("Kiesza")) assertEquals("Killswitch Engage", cleanArtist("Killswitch Engage")) assertEquals("Koe Wetzel", cleanArtist("Koe Wetzel")) assertEquals("La Bouche", cleanArtist("La Bouche")) assertEquals("Lamb of God", cleanArtist("Lamb of God")) assertEquals("Lefty Frizzell", cleanArtist("Lefty Frizzell")) assertEquals("Lenny Kravitz", cleanArtist("Lenny Kravitz")) assertEquals("Leroy Van Dyke", cleanArtist("Leroy Van Dyke")) assertEquals("Little Jimmy Dickens", cleanArtist("Little Jimmy Dickens")) assertEquals("Little Walter", cleanArtist("Little Walter")) assertEquals("Lucenzo", cleanArtist("Lucenzo")) assertEquals("Lynyrd Skynyrd", cleanArtist("Lynyrd Skynyrd")) assertEquals("Macaco", cleanArtist("Macaco")) assertEquals("Marc Almond & Gene Pitney", cleanArtist("Marc Almond & Gene Pitney")) assertEquals("Matt Stell", cleanArtist("Matt Stell")) assertEquals("Max Cruise & Avantris", cleanArtist("Max Cruise & Avantris")) assertEquals("Metallica", cleanArtist("Metallica")) assertEquals("Michael Jackson", cleanArtist("Michael Jackson")) assertEquals("Mike Oldfield", cleanArtist("Mike Oldfield")) assertEquals("Mike Posner", cleanArtist("Mike Posner")) assertEquals("Moonspell", cleanArtist("Moonspell")) assertEquals("Morgan Wallen", cleanArtist("Morgan Wallen")) assertEquals("MUSIC", cleanArtist("MUSIC")) assertEquals("MyAnnairam", cleanArtist("MyAnnairam")) assertEquals("Neneh Cherry", cleanArtist("Neneh Cherry")) assertEquals("Nina Sky", cleanArtist("Nina Sky")) assertEquals("N.O.R.E.", cleanArtist("N.O.R.E.")) assertEquals("Nu Breed & Jesse Howard", cleanArtist("Nu Breed & Jesse Howard")) assertEquals("OGUZ", cleanArtist("OGUZ")) assertEquals("Olivia Holt", cleanArtist("Olivia Holt")) assertEquals("Orchestral Manoeuvres In The Dark", cleanArtist("Orchestral Manoeuvres In The Dark")) assertEquals("Outkast", cleanArtist("Outkast")) assertEquals("Porter Wagoner", cleanArtist("Porter Wagoner")) assertEquals("Raekwon", cleanArtist("Raekwon")) assertEquals("Ragal Ironbull", cleanArtist("Ragal Ironbull")) assertEquals("Raja Baath & Yo Yo Honey Singh", cleanArtist("Raja Baath & Yo Yo Honey Singh")) assertEquals("Ralph Tresvant", cleanArtist("Ralph Tresvant")) assertEquals("Randall King", cleanArtist("Randall King")) assertEquals("RANDALL KING", cleanArtist("RANDALL KING")) assertEquals("Randy Travis", cleanArtist("Randy Travis")) assertEquals("RDLF", cleanArtist("RDLF")) assertEquals("Redferrin", cleanArtist("Redferrin")) assertEquals("Red Hot Chili Peppers", cleanArtist("Red Hot Chili Peppers")) assertEquals("Riley Green", cleanArtist("Riley Green")) assertEquals("R. Kelly Content", cleanArtist("R. Kelly Content")) assertEquals("Robert L. Petty", cleanArtist("Robert L. Petty")) assertEquals("Ronnie Milsap", cleanArtist("Ronnie Milsap")) assertEquals("Roy Clark", cleanArtist("Roy Clark")) assertEquals("Roy Drusky", cleanArtist("Roy Drusky")) assertEquals("Saweetie", cleanArtist("Saweetie")) assertEquals("September", cleanArtist("September")) assertEquals("Sergei Rachmaninoff", cleanArtist("Sergei Rachmaninoff")) assertEquals("Shashwat Sachdev", cleanArtist("Shashwat Sachdev")) assertEquals("Sister Hazel", cleanArtist("Sister Hazel")) assertEquals("Skeeter Davis", cleanArtist("Skeeter Davis")) assertEquals("Skillet", cleanArtist("Skillet")) assertEquals("Skinny Fabulous", cleanArtist("Skinny Fabulous")) assertEquals("Soulful Soundz 500", cleanArtist("Soulful Soundz 500")) assertEquals("Steve Wariner", cleanArtist("Steve Wariner")) assertEquals("Sundar Popo", cleanArtist("Sundar Popo")) assertEquals("Superorganism", cleanArtist("Superorganism")) assertEquals("Teddy Swims", cleanArtist("Teddy Swims")) assertEquals("The Browns", cleanArtist("The Browns")) assertEquals("The Cardigans", cleanArtist("The Cardigans")) assertEquals("The Communards", cleanArtist("The Communards")) assertEquals("The Doobie Brothers", cleanArtist("The Doobie Brothers")) assertEquals("The Human League", cleanArtist("The Human League")) assertEquals("The Killers", cleanArtist("The Killers")) assertEquals("The Louvin Brothers", cleanArtist("The Louvin Brothers")) assertEquals("The Midnight & Avantris", cleanArtist("The Midnight & Avantris")) assertEquals("The Mobiles", cleanArtist("The Mobiles")) assertEquals("The Rembrandts", cleanArtist("The Rembrandts")) assertEquals("The Rolling Stones", cleanArtist("The Rolling Stones")) assertEquals("The Sad Guitar", cleanArtist("The Sad Guitar")) assertEquals("The Waterboys", cleanArtist("The Waterboys")) assertEquals("Thomas Rhett", cleanArtist("Thomas Rhett")) assertEquals("Tim McGraw", cleanArtist("Tim McGraw")) assertEquals("Tommy Collins", cleanArtist("Tommy Collins")) assertEquals("Tom T. Hall", cleanArtist("Tom T. Hall")) assertEquals("Tracy Lawrence", cleanArtist("Tracy Lawrence")) assertEquals("Travis Tritt", cleanArtist("Travis Tritt")) assertEquals("Tyler Farr", cleanArtist("Tyler Farr")) assertEquals("Tyler Hubbard", cleanArtist("Tyler Hubbard")) assertEquals("Ultravox", cleanArtist("Ultravox")) assertEquals("Up and away adventures", cleanArtist("Up and away adventures")) assertEquals("Various Artists", cleanArtist("VARIOUS ARTISTS")) assertEquals("Vern Gosdin", cleanArtist("Vern Gosdin")) assertEquals("VMRs", cleanArtist("VMRs")) assertEquals("VNV Nation", cleanArtist("VNV Nation")) assertEquals("Webb Pierce", cleanArtist("Webb Pierce")) assertEquals("William Michael Morgan", cleanArtist("William Michael Morgan")) assertEquals("Will to Power", cleanArtist("Will to Power")) assertEquals("Wynn Stewart", cleanArtist("Wynn Stewart")) assertEquals("Yoko Shimomura", cleanArtist("Yoko Shimomura")) assertEquals("Yuko", cleanArtist("Yuko")) assertEquals("YUNGBLUD", cleanArtist("YUNGBLUD")) assertEquals("Zach Top", cleanArtist("Zach Top")) assertEquals("zero/zero", cleanArtist("zero/zero")) } }