summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/java/org/terst/listen/data/LibraryOrderTest.kt23
-rw-r--r--app/src/main/java/org/terst/listen/data/AppDatabase.kt58
2 files changed, 56 insertions, 25 deletions
diff --git a/app/src/androidTest/java/org/terst/listen/data/LibraryOrderTest.kt b/app/src/androidTest/java/org/terst/listen/data/LibraryOrderTest.kt
index a181dd7..4117b79 100644
--- a/app/src/androidTest/java/org/terst/listen/data/LibraryOrderTest.kt
+++ b/app/src/androidTest/java/org/terst/listen/data/LibraryOrderTest.kt
@@ -119,6 +119,29 @@ class LibraryOrderTest {
assertEquals(listOf("b", "a"), order())
}
+ /**
+ * A fresh install gets the default filters too.
+ *
+ * Found on a device: a reinstall creates the schema at the current version and never runs a
+ * migration, so seeding only in MIGRATION_4_5 quietly gave them to upgraders alone.
+ */
+ @Test
+ fun aFreshDatabaseIsSeededWithTheDefaultFilters() = runBlocking {
+ val fresh = Room.inMemoryDatabaseBuilder(
+ InstrumentationRegistry.getInstrumentation().targetContext,
+ AppDatabase::class.java,
+ ).addCallback(AppDatabase.SEED_DEFAULT_FILTERS).build()
+
+ try {
+ // Room creates the database lazily, so the callback has not run until it is used.
+ val rules = fresh.rules().getAll()
+ assertEquals(listOf("trailer", "preview"), rules.map { it.titleContains })
+ assertEquals(listOf(RuleAction.MARK_PLAYED, RuleAction.MARK_PLAYED), rules.map { it.action })
+ } finally {
+ fresh.close()
+ }
+ }
+
/** A show with nothing waiting sorts last, by title, rather than vanishing. */
@Test
fun aShowWithNothingWaitingSortsLast() = runBlocking {
diff --git a/app/src/main/java/org/terst/listen/data/AppDatabase.kt b/app/src/main/java/org/terst/listen/data/AppDatabase.kt
index c33921b..fe73ce0 100644
--- a/app/src/main/java/org/terst/listen/data/AppDatabase.kt
+++ b/app/src/main/java/org/terst/listen/data/AppDatabase.kt
@@ -101,37 +101,44 @@ abstract class AppDatabase : RoomDatabase() {
/**
* Seeds the trailer and preview filters Peter asked for.
*
- * Rules live in the database on the phone, so a migration is the only way to put them
- * there. They are ordinary rules: visible in the Rules screen, and editable, disableable
- * or deletable like any other. Appended after whatever is already there rather than
+ * Rules live in the database on the phone, so code is the only way to put them there.
+ * They are ordinary rules: visible in the Rules screen, and editable, disableable or
+ * deletable like any other. Appended after whatever is already there rather than
* taking position 0, so they cannot pre-empt rules already being relied on, and they
* only act on newly discovered episodes -- nothing already in the library changes.
*
- * INSERT OR IGNORE on a fixed id, so deleting one does not bring it back on the next
- * upgrade.
+ * INSERT OR IGNORE on a fixed id, so deleting one does not bring it back.
*/
+ private fun seedDefaultFilters(db: SupportSQLiteDatabase) {
+ seedFilter(db, "listen-default-trailer", "Trailers", "trailer")
+ seedFilter(db, "listen-default-preview", "Previews", "preview")
+ }
+
+ private fun seedFilter(db: SupportSQLiteDatabase, id: String, name: String, word: String) {
+ db.execSQL(
+ "INSERT OR IGNORE INTO rules " +
+ "(id, name, position, enabled, subscriptionId, titleContains, " +
+ "titleExcludes, minDurationMs, maxDurationMs, maxAgeMs, seasonNumber, " +
+ "action, autoDownload, speed) " +
+ "SELECT ?, ?, COALESCE(MAX(position), -1) + 1, 1, NULL, ?, NULL, " +
+ "NULL, NULL, NULL, NULL, 'MARK_PLAYED', 0, NULL FROM rules",
+ arrayOf(id, name, word),
+ )
+ }
+
val MIGRATION_4_5 = object : Migration(4, 5) {
- override fun migrate(db: SupportSQLiteDatabase) {
- seedFilter(db, "listen-default-trailer", "Trailers", "trailer")
- seedFilter(db, "listen-default-preview", "Previews", "preview")
- }
+ override fun migrate(db: SupportSQLiteDatabase) = seedDefaultFilters(db)
+ }
- private fun seedFilter(
- db: SupportSQLiteDatabase,
- id: String,
- name: String,
- word: String,
- ) {
- db.execSQL(
- "INSERT OR IGNORE INTO rules " +
- "(id, name, position, enabled, subscriptionId, titleContains, " +
- "titleExcludes, minDurationMs, maxDurationMs, maxAgeMs, seasonNumber, " +
- "action, autoDownload, speed) " +
- "SELECT ?, ?, COALESCE(MAX(position), -1) + 1, 1, NULL, ?, NULL, " +
- "NULL, NULL, NULL, NULL, 'MARK_PLAYED', 0, NULL FROM rules",
- arrayOf(id, name, word),
- )
- }
+ /**
+ * The same filters for a fresh install, which never runs a migration.
+ *
+ * Found on a device: a reinstall creates the schema at the current version directly, so
+ * seeding only in MIGRATION_4_5 gave the filters to upgraders and silently to nobody
+ * else.
+ */
+ val SEED_DEFAULT_FILTERS = object : RoomDatabase.Callback() {
+ override fun onCreate(db: SupportSQLiteDatabase) = seedDefaultFilters(db)
}
fun get(context: Context): AppDatabase = instance ?: synchronized(this) {
@@ -143,6 +150,7 @@ abstract class AppDatabase : RoomDatabase() {
// Listed one by one rather than spread from an array: every migration here is a
// deliberate addition, and the list is the record of them.
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
+ .addCallback(SEED_DEFAULT_FILTERS)
// No fallbackToDestructiveMigration. A listener's playback positions and play
// history are not reproducible from anywhere, so a missing migration must fail
// loudly rather than silently wipe the library.