From 2d9a5ad5cd3c56d1488a35c1fcc19183bf9bfe9e Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Mon, 20 Feb 2023 15:44:32 -0700 Subject: [PATCH] music: add more tests Add tests for MusicViewModel, MusicMode, and MusicRepository. Going further requires me to really learn instrumentation tests. --- .../oxycblt/auxio/detail/SongDetailDialog.kt | 2 +- .../org/oxycblt/auxio/home/HomeViewModel.kt | 1 + .../java/org/oxycblt/auxio/music/Music.kt | 12 ++ .../java/org/oxycblt/auxio/music/FakeMusic.kt | 144 ++++++++++++++++++ .../oxycblt/auxio/music/FakeMusicSettings.kt | 2 +- .../org/oxycblt/auxio/music/MusicModeTest.kt | 31 ++++ .../auxio/music/MusicRepositoryTest.kt | 52 +++++++ .../oxycblt/auxio/music/MusicViewModelTest.kt | 108 +++++++++++++ .../auxio/music/metadata/TagUtilTest.kt | 24 ++- .../oxycblt/auxio/music/model/FakeLibrary.kt | 49 ++++++ .../oxycblt/auxio/music/system/FakeIndexer.kt | 57 +++++++ .../org/oxycblt/auxio/util/TestingUtil.kt | 27 ++++ 12 files changed, 493 insertions(+), 16 deletions(-) create mode 100644 app/src/test/java/org/oxycblt/auxio/music/FakeMusic.kt create mode 100644 app/src/test/java/org/oxycblt/auxio/music/MusicModeTest.kt create mode 100644 app/src/test/java/org/oxycblt/auxio/music/MusicRepositoryTest.kt create mode 100644 app/src/test/java/org/oxycblt/auxio/music/MusicViewModelTest.kt create mode 100644 app/src/test/java/org/oxycblt/auxio/music/model/FakeLibrary.kt create mode 100644 app/src/test/java/org/oxycblt/auxio/music/system/FakeIndexer.kt create mode 100644 app/src/test/java/org/oxycblt/auxio/util/TestingUtil.kt diff --git a/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt index f84fab0bf..1ebf9ff46 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt @@ -91,7 +91,7 @@ class SongDetailDialog : ViewBindingDialogFragment() { val formattedNumber = getString(R.string.fmt_number, it.number) val zipped = if (it.name != null) { - getString(R.string.fmt_zipped_names, it.name, formattedNumber) + getString(R.string.fmt_zipped_names, formattedNumber, it.name) } else { formattedNumber } diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt index d949b13ce..7fe81ed7f 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt @@ -42,6 +42,7 @@ constructor( private val musicRepository: MusicRepository, private val musicSettings: MusicSettings ) : ViewModel(), MusicRepository.Listener, HomeSettings.Listener { + private val _songsList = MutableStateFlow(listOf()) /** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */ val songsList: StateFlow> diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index 4cd809b3a..665010655 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -347,9 +347,21 @@ interface Genre : MusicParent { val durationMs: Long } +/** + * Run [Music.resolveName] on each instance in the given list and concatenate them into a [String] + * in a localized manner. + * @param context [Context] required + * @return A concatenated string. + */ fun List.resolveNames(context: Context) = concatLocalized(context) { it.resolveName(context) } +/** + * Returns if [Music.rawName] matches for each item in a list. Useful for scenarios where the + * display information of an item must be compared without a context. + * @param other The list of items to compare to. + * @return True if they are the same (by [Music.rawName]), false otherwise. + */ fun List.areRawNamesTheSame(other: List): Boolean { for (i in 0 until max(size, other.size)) { val a = getOrNull(i) ?: return false diff --git a/app/src/test/java/org/oxycblt/auxio/music/FakeMusic.kt b/app/src/test/java/org/oxycblt/auxio/music/FakeMusic.kt new file mode 100644 index 000000000..bd9534472 --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/FakeMusic.kt @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music + +import android.content.Context +import android.net.Uri +import java.text.CollationKey +import org.oxycblt.auxio.music.* +import org.oxycblt.auxio.music.metadata.Date +import org.oxycblt.auxio.music.metadata.Disc +import org.oxycblt.auxio.music.metadata.ReleaseType +import org.oxycblt.auxio.music.storage.MimeType +import org.oxycblt.auxio.music.storage.Path + +open class FakeSong : Song { + override val rawName: String? + get() = throw NotImplementedError() + override val rawSortName: String? + get() = throw NotImplementedError() + override val date: Date? + get() = throw NotImplementedError() + override val dateAdded: Long + get() = throw NotImplementedError() + override val disc: Disc? + get() = throw NotImplementedError() + override val genres: List + get() = throw NotImplementedError() + override val mimeType: MimeType + get() = throw NotImplementedError() + override val track: Int? + get() = throw NotImplementedError() + override val path: Path + get() = throw NotImplementedError() + override val size: Long + get() = throw NotImplementedError() + override val uri: Uri + get() = throw NotImplementedError() + override val album: Album + get() = throw NotImplementedError() + override val artists: List + get() = throw NotImplementedError() + override val collationKey: CollationKey? + get() = throw NotImplementedError() + override val durationMs: Long + get() = throw NotImplementedError() + override val uid: Music.UID + get() = throw NotImplementedError() + + override fun resolveName(context: Context): String { + throw NotImplementedError() + } +} + +open class FakeAlbum : Album { + override val rawName: String? + get() = throw NotImplementedError() + override val rawSortName: String? + get() = throw NotImplementedError() + override val coverUri: Uri + get() = throw NotImplementedError() + override val dateAdded: Long + get() = throw NotImplementedError() + override val dates: Date.Range? + get() = throw NotImplementedError() + override val releaseType: ReleaseType + get() = throw NotImplementedError() + override val artists: List + get() = throw NotImplementedError() + override val collationKey: CollationKey? + get() = throw NotImplementedError() + override val durationMs: Long + get() = throw NotImplementedError() + override val songs: List + get() = throw NotImplementedError() + override val uid: Music.UID + get() = throw NotImplementedError() + + override fun resolveName(context: Context): String { + throw NotImplementedError() + } +} + +open class FakeArtist : Artist { + override val rawName: String? + get() = throw NotImplementedError() + override val rawSortName: String? + get() = throw NotImplementedError() + override val albums: List + get() = throw NotImplementedError() + override val genres: List + get() = throw NotImplementedError() + override val isCollaborator: Boolean + get() = throw NotImplementedError() + override val collationKey: CollationKey? + get() = throw NotImplementedError() + override val durationMs: Long + get() = throw NotImplementedError() + override val songs: List + get() = throw NotImplementedError() + override val uid: Music.UID + get() = throw NotImplementedError() + + override fun resolveName(context: Context): String { + throw NotImplementedError() + } +} + +open class FakeGenre : Genre { + override val rawName: String? + get() = throw NotImplementedError() + override val rawSortName: String? + get() = throw NotImplementedError() + override val albums: List + get() = throw NotImplementedError() + override val artists: List + get() = throw NotImplementedError() + override val collationKey: CollationKey? + get() = throw NotImplementedError() + override val durationMs: Long + get() = throw NotImplementedError() + override val songs: List + get() = throw NotImplementedError() + override val uid: Music.UID + get() = throw NotImplementedError() + + override fun resolveName(context: Context): String { + throw NotImplementedError() + } +} diff --git a/app/src/test/java/org/oxycblt/auxio/music/FakeMusicSettings.kt b/app/src/test/java/org/oxycblt/auxio/music/FakeMusicSettings.kt index 2e632fb84..08ccde454 100644 --- a/app/src/test/java/org/oxycblt/auxio/music/FakeMusicSettings.kt +++ b/app/src/test/java/org/oxycblt/auxio/music/FakeMusicSettings.kt @@ -20,7 +20,7 @@ package org.oxycblt.auxio.music import org.oxycblt.auxio.list.Sort import org.oxycblt.auxio.music.storage.MusicDirectories -interface FakeMusicSettings : MusicSettings { +open class FakeMusicSettings : MusicSettings { override fun registerListener(listener: MusicSettings.Listener) = throw NotImplementedError() override fun unregisterListener(listener: MusicSettings.Listener) = throw NotImplementedError() override var musicDirs: MusicDirectories diff --git a/app/src/test/java/org/oxycblt/auxio/music/MusicModeTest.kt b/app/src/test/java/org/oxycblt/auxio/music/MusicModeTest.kt new file mode 100644 index 000000000..b89236e80 --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/MusicModeTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music + +import org.junit.Assert.assertEquals +import org.junit.Test + +class MusicModeTest { + @Test + fun intCode() { + assertEquals(MusicMode.SONGS, MusicMode.fromIntCode(MusicMode.SONGS.intCode)) + assertEquals(MusicMode.ALBUMS, MusicMode.fromIntCode(MusicMode.ALBUMS.intCode)) + assertEquals(MusicMode.ARTISTS, MusicMode.fromIntCode(MusicMode.ARTISTS.intCode)) + assertEquals(MusicMode.GENRES, MusicMode.fromIntCode(MusicMode.GENRES.intCode)) + } +} diff --git a/app/src/test/java/org/oxycblt/auxio/music/MusicRepositoryTest.kt b/app/src/test/java/org/oxycblt/auxio/music/MusicRepositoryTest.kt new file mode 100644 index 000000000..59c988fdb --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/MusicRepositoryTest.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.oxycblt.auxio.music.model.FakeLibrary +import org.oxycblt.auxio.music.model.Library + +class MusicRepositoryTest { + @Test + fun listeners() { + val listener = TestListener() + val impl = + MusicRepositoryImpl().apply { + library = null + addListener(listener) + } + impl.library = TestLibrary(0) + assertEquals(listOf(null, TestLibrary(0)), listener.updates) + + val listener2 = TestListener() + impl.addListener(listener2) + impl.library = TestLibrary(1) + assertEquals(listOf(TestLibrary(0), TestLibrary(1)), listener2.updates) + } + + private class TestListener : MusicRepository.Listener { + val updates = mutableListOf() + + override fun onLibraryChanged(library: Library?) { + updates.add(library) + } + } + + private data class TestLibrary(private val id: Int) : FakeLibrary() +} diff --git a/app/src/test/java/org/oxycblt/auxio/music/MusicViewModelTest.kt b/app/src/test/java/org/oxycblt/auxio/music/MusicViewModelTest.kt new file mode 100644 index 000000000..a6b7cd1d3 --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/MusicViewModelTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.oxycblt.auxio.music.model.FakeLibrary +import org.oxycblt.auxio.music.system.FakeIndexer +import org.oxycblt.auxio.music.system.Indexer +import org.oxycblt.auxio.util.forceClear + +class MusicViewModelTest { + @Test + fun indexerState() { + val indexer = + TestIndexer().apply { state = Indexer.State.Indexing(Indexer.Indexing.Indeterminate) } + val musicViewModel = MusicViewModel(indexer) + assertTrue(indexer.listener is MusicViewModel) + assertEquals( + Indexer.Indexing.Indeterminate, + (musicViewModel.indexerState.value as Indexer.State.Indexing).indexing) + indexer.state = null + assertEquals(null, musicViewModel.indexerState.value) + musicViewModel.forceClear() + assertTrue(indexer.listener == null) + } + + @Test + fun statistics() { + val indexer = + TestIndexer().apply { state = Indexer.State.Complete(Result.success(TestLibrary())) } + val musicViewModel = MusicViewModel(indexer) + assertEquals( + MusicViewModel.Statistics( + 2, + 3, + 4, + 1, + 161616 * 2, + ), + musicViewModel.statistics.value) + } + + @Test + fun requests() { + val indexer = TestIndexer() + val musicViewModel = MusicViewModel(indexer) + musicViewModel.refresh() + musicViewModel.rescan() + assertEquals(listOf(true, false), indexer.requests) + } + + private class TestIndexer : FakeIndexer() { + var listener: Indexer.Listener? = null + var state: Indexer.State? = null + set(value) { + field = value + listener?.onIndexerStateChanged(value) + } + + val requests = mutableListOf() + + override fun registerListener(listener: Indexer.Listener) { + this.listener = listener + listener.onIndexerStateChanged(state) + } + + override fun unregisterListener(listener: Indexer.Listener) { + this.listener = null + } + + override fun requestReindex(withCache: Boolean) { + requests.add(withCache) + } + } + + private class TestLibrary : FakeLibrary() { + override val songs: List + get() = listOf(TestSong(), TestSong()) + override val albums: List + get() = listOf(FakeAlbum(), FakeAlbum(), FakeAlbum()) + override val artists: List + get() = listOf(FakeArtist(), FakeArtist(), FakeArtist(), FakeArtist()) + override val genres: List + get() = listOf(FakeGenre()) + } + + private class TestSong : FakeSong() { + override val durationMs: Long + get() = 161616 + } +} diff --git a/app/src/test/java/org/oxycblt/auxio/music/metadata/TagUtilTest.kt b/app/src/test/java/org/oxycblt/auxio/music/metadata/TagUtilTest.kt index 96d34b98d..992059857 100644 --- a/app/src/test/java/org/oxycblt/auxio/music/metadata/TagUtilTest.kt +++ b/app/src/test/java/org/oxycblt/auxio/music/metadata/TagUtilTest.kt @@ -24,22 +24,20 @@ import org.oxycblt.auxio.music.FakeMusicSettings class TagUtilTest { @Test fun parseMultiValue_single() { - assertEquals( - listOf("a", "b", "c"), listOf("a,b,c").parseMultiValue(SeparatorMusicSettings(","))) + assertEquals(listOf("a", "b", "c"), listOf("a,b,c").parseMultiValue(TestMusicSettings(","))) } @Test fun parseMultiValue_many() { assertEquals( - listOf("a", "b", "c"), - listOf("a", "b", "c").parseMultiValue(SeparatorMusicSettings(","))) + listOf("a", "b", "c"), listOf("a", "b", "c").parseMultiValue(TestMusicSettings(","))) } @Test fun parseMultiValue_several() { assertEquals( listOf("a", "b", "c", "d", "e", "f"), - listOf("a,b;c/d+e&f").parseMultiValue(SeparatorMusicSettings(",;/+&"))) + listOf("a,b;c/d+e&f").parseMultiValue(TestMusicSettings(",;/+&"))) } @Test @@ -132,43 +130,41 @@ class TagUtilTest { fun parseId3v2Genre_multi() { assertEquals( listOf("Post-Rock", "Shoegaze", "Glitch"), - listOf("Post-Rock", "Shoegaze", "Glitch") - .parseId3GenreNames(SeparatorMusicSettings(","))) + listOf("Post-Rock", "Shoegaze", "Glitch").parseId3GenreNames(TestMusicSettings(","))) } @Test fun parseId3v2Genre_multiId3v1() { assertEquals( listOf("Post-Rock", "Shoegaze", "Glitch"), - listOf("176", "178", "Glitch").parseId3GenreNames(SeparatorMusicSettings(","))) + listOf("176", "178", "Glitch").parseId3GenreNames(TestMusicSettings(","))) } @Test fun parseId3v2Genre_wackId3() { - assertEquals(listOf("2941"), listOf("2941").parseId3GenreNames(SeparatorMusicSettings(","))) + assertEquals(listOf("2941"), listOf("2941").parseId3GenreNames(TestMusicSettings(","))) } @Test fun parseId3v2Genre_singleId3v23() { assertEquals( listOf("Post-Rock", "Shoegaze", "Remix", "Cover", "Glitch"), - listOf("(176)(178)(RX)(CR)Glitch").parseId3GenreNames(SeparatorMusicSettings(","))) + listOf("(176)(178)(RX)(CR)Glitch").parseId3GenreNames(TestMusicSettings(","))) } @Test fun parseId3v2Genre_singleSeparated() { assertEquals( listOf("Post-Rock", "Shoegaze", "Glitch"), - listOf("Post-Rock, Shoegaze, Glitch").parseId3GenreNames(SeparatorMusicSettings(","))) + listOf("Post-Rock, Shoegaze, Glitch").parseId3GenreNames(TestMusicSettings(","))) } @Test fun parsId3v2Genre_singleId3v1() { - assertEquals( - listOf("Post-Rock"), listOf("176").parseId3GenreNames(SeparatorMusicSettings(","))) + assertEquals(listOf("Post-Rock"), listOf("176").parseId3GenreNames(TestMusicSettings(","))) } - class SeparatorMusicSettings(private val separators: String) : FakeMusicSettings { + class TestMusicSettings(private val separators: String) : FakeMusicSettings() { override var multiValueSeparators: String get() = separators set(_) = throw NotImplementedError() diff --git a/app/src/test/java/org/oxycblt/auxio/music/model/FakeLibrary.kt b/app/src/test/java/org/oxycblt/auxio/music/model/FakeLibrary.kt new file mode 100644 index 000000000..ec041b37e --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/model/FakeLibrary.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music.model + +import android.content.Context +import android.net.Uri +import org.oxycblt.auxio.music.* + +open class FakeLibrary : Library { + override val songs: List + get() = throw NotImplementedError() + override val albums: List + get() = throw NotImplementedError() + override val artists: List + get() = throw NotImplementedError() + override val genres: List + get() = throw NotImplementedError() + + override fun find(uid: Music.UID): T? { + throw NotImplementedError() + } + + override fun findSongForUri(context: Context, uri: Uri): Song? { + throw NotImplementedError() + } + + override fun sanitize(parent: T): T? { + throw NotImplementedError() + } + + override fun sanitize(song: Song): Song? { + throw NotImplementedError() + } +} diff --git a/app/src/test/java/org/oxycblt/auxio/music/system/FakeIndexer.kt b/app/src/test/java/org/oxycblt/auxio/music/system/FakeIndexer.kt new file mode 100644 index 000000000..51ec72754 --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/music/system/FakeIndexer.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.music.system + +import android.content.Context +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job + +open class FakeIndexer : Indexer { + override val isIndeterminate: Boolean + get() = throw NotImplementedError() + override val isIndexing: Boolean + get() = throw NotImplementedError() + + override fun index(context: Context, withCache: Boolean, scope: CoroutineScope): Job { + throw NotImplementedError() + } + + override fun registerController(controller: Indexer.Controller) { + throw NotImplementedError() + } + + override fun unregisterController(controller: Indexer.Controller) { + throw NotImplementedError() + } + + override fun registerListener(listener: Indexer.Listener) { + throw NotImplementedError() + } + + override fun unregisterListener(listener: Indexer.Listener) { + throw NotImplementedError() + } + + override fun requestReindex(withCache: Boolean) { + throw NotImplementedError() + } + + override fun reset() { + throw NotImplementedError() + } +} diff --git a/app/src/test/java/org/oxycblt/auxio/util/TestingUtil.kt b/app/src/test/java/org/oxycblt/auxio/util/TestingUtil.kt new file mode 100644 index 000000000..7ef0d2c19 --- /dev/null +++ b/app/src/test/java/org/oxycblt/auxio/util/TestingUtil.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Auxio Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.oxycblt.auxio.util + +import androidx.lifecycle.ViewModel + +private val VM_CLEAR_METHOD = + ViewModel::class.java.getDeclaredMethod("clear").apply { isAccessible = true } + +fun ViewModel.forceClear() { + VM_CLEAR_METHOD.invoke(this) +}