package org.schabi.newpipe.database import android.content.ContentValues import android.database.sqlite.SQLiteDatabase import androidx.room.Room import androidx.room.testing.MigrationTestHelper import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.schabi.newpipe.extractor.ServiceList import org.schabi.newpipe.extractor.stream.StreamType @RunWith(AndroidJUnit4::class) class DatabaseMigrationTest { companion object { private const val DEFAULT_SERVICE_ID = 0 private const val DEFAULT_URL = "https://www.youtube.com/watch?v=cDphUib5iG4" private const val DEFAULT_TITLE = "Test Title" private val DEFAULT_TYPE = StreamType.VIDEO_STREAM private const val DEFAULT_DURATION = 480L private const val DEFAULT_UPLOADER_NAME = "Uploader Test" private const val DEFAULT_THUMBNAIL = "https://example.com/example.jpg" private const val DEFAULT_SECOND_SERVICE_ID = 0 private const val DEFAULT_SECOND_URL = "https://www.youtube.com/watch?v=ncQU6iBn5Fc" } @get:Rule val testHelper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), AppDatabase::class.java ) @Test fun migrateDatabaseFrom2to3() { val databaseInV2 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_2) databaseInV2.run { insert( "streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SERVICE_ID) put("url", DEFAULT_URL) put("title", DEFAULT_TITLE) put("stream_type", DEFAULT_TYPE.name) put("duration", DEFAULT_DURATION) put("uploader", DEFAULT_UPLOADER_NAME) put("thumbnail_url", DEFAULT_THUMBNAIL) } ) insert( "streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SECOND_SERVICE_ID) put("url", DEFAULT_SECOND_URL) } ) insert( "streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SERVICE_ID) } ) close() } testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_3, true, Migrations.MIGRATION_2_3 ) testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_4, true, Migrations.MIGRATION_3_4 ) testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_5, true, Migrations.MIGRATION_4_5 ) testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, true, Migrations.MIGRATION_5_6 ) testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_7, true, Migrations.MIGRATION_6_7 ) testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_8, true, Migrations.MIGRATION_7_8 ) val migratedDatabaseV3 = getMigratedDatabase() val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst() // Only expect 2, the one with the null url will be ignored assertEquals(2, listFromDB.size) val streamFromMigratedDatabase = listFromDB[0] assertEquals(DEFAULT_SERVICE_ID, streamFromMigratedDatabase.serviceId) assertEquals(DEFAULT_URL, streamFromMigratedDatabase.url) assertEquals(DEFAULT_TITLE, streamFromMigratedDatabase.title) assertEquals(DEFAULT_TYPE, streamFromMigratedDatabase.streamType) assertEquals(DEFAULT_DURATION, streamFromMigratedDatabase.duration) assertEquals(DEFAULT_UPLOADER_NAME, streamFromMigratedDatabase.uploader) assertEquals(DEFAULT_THUMBNAIL, streamFromMigratedDatabase.thumbnailUrl) assertNull(streamFromMigratedDatabase.viewCount) assertNull(streamFromMigratedDatabase.textualUploadDate) assertNull(streamFromMigratedDatabase.uploadDate) assertNull(streamFromMigratedDatabase.isUploadDateApproximation) val secondStreamFromMigratedDatabase = listFromDB[1] assertEquals(DEFAULT_SECOND_SERVICE_ID, secondStreamFromMigratedDatabase.serviceId) assertEquals(DEFAULT_SECOND_URL, secondStreamFromMigratedDatabase.url) assertEquals("", secondStreamFromMigratedDatabase.title) // Should fallback to VIDEO_STREAM assertEquals(StreamType.VIDEO_STREAM, secondStreamFromMigratedDatabase.streamType) assertEquals(0, secondStreamFromMigratedDatabase.duration) assertEquals("", secondStreamFromMigratedDatabase.uploader) assertEquals("", secondStreamFromMigratedDatabase.thumbnailUrl) assertNull(secondStreamFromMigratedDatabase.viewCount) assertNull(secondStreamFromMigratedDatabase.textualUploadDate) assertNull(secondStreamFromMigratedDatabase.uploadDate) assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation) } @Test fun migrateDatabaseFrom7to8() { val databaseInV7 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_7) val defaultSearch1 = " abc " val defaultSearch2 = " abc" val serviceId = DEFAULT_SERVICE_ID // YouTube // Use id different to YouTube because two searches with the same query // but different service are considered not equal. val otherServiceId = ServiceList.SoundCloud.serviceId databaseInV7.run { insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", serviceId) put("search", defaultSearch1) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", serviceId) put("search", defaultSearch2) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", otherServiceId) put("search", defaultSearch1) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", otherServiceId) put("search", defaultSearch2) } ) close() } testHelper.runMigrationsAndValidate( AppDatabase.DATABASE_NAME, Migrations.DB_VER_8, true, Migrations.MIGRATION_7_8 ) val migratedDatabaseV8 = getMigratedDatabase() val listFromDB = migratedDatabaseV8.searchHistoryDAO().all.blockingFirst() assertEquals(2, listFromDB.size) assertEquals("abc", listFromDB[0].search) assertEquals("abc", listFromDB[1].search) assertNotEquals(listFromDB[0].serviceId, listFromDB[1].serviceId) } private fun getMigratedDatabase(): AppDatabase { val database: AppDatabase = Room.databaseBuilder( ApplicationProvider.getApplicationContext(), AppDatabase::class.java, AppDatabase.DATABASE_NAME ) .build() testHelper.closeWhenFinished(database) return database } }