diff --git a/app/build.gradle b/app/build.gradle index 5952355749a..7241b8dc045 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,9 +23,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import com.github.spotbugs.snom.SpotBugsTask + import com.github.spotbugs.snom.Confidence import com.github.spotbugs.snom.Effort +import com.github.spotbugs.snom.SpotBugsTask apply plugin: 'com.android.application' apply plugin: 'kotlin-android' @@ -91,6 +92,10 @@ android { } } + sourceSets { + androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) + } + testInstrumentationRunnerArgument "TEST_SERVER_URL", "${NC_TEST_SERVER_BASEURL}" testInstrumentationRunnerArgument "TEST_SERVER_USERNAME", "${NC_TEST_SERVER_USERNAME}" testInstrumentationRunnerArgument "TEST_SERVER_PASSWORD", "${NC_TEST_SERVER_PASSWORD}" @@ -238,6 +243,7 @@ dependencies { implementation "androidx.room:room-rxjava2:${roomVersion}" kapt "androidx.room:room-compiler:${roomVersion}" implementation "androidx.room:room-ktx:${roomVersion}" + androidTestImplementation "androidx.room:room-testing:2.6.1" implementation "org.parceler:parceler-api:$parcelerVersion" implementation 'eu.davidea:flexible-adapter:5.1.0' diff --git a/app/src/androidTest/java/com/nextcloud/talk/migrations/MigrationsTest.kt b/app/src/androidTest/java/com/nextcloud/talk/migrations/MigrationsTest.kt new file mode 100644 index 00000000000..d0a31cc05c9 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/talk/migrations/MigrationsTest.kt @@ -0,0 +1,71 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * 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 com.nextcloud.talk.migrations + +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.platform.app.InstrumentationRegistry +import com.nextcloud.talk.data.source.local.Migrations +import com.nextcloud.talk.data.source.local.TalkDatabase +import org.junit.Rule +import org.junit.Test +import java.io.IOException + + +class MigrationsTest { + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + TalkDatabase::class.java.canonicalName!!, + FrameworkSQLiteOpenHelperFactory() + ) + + @Test + @Throws(IOException::class) + fun migrateAll() { + // Create earliest version of the database. + helper.createDatabase(TEST_DB, 8).apply { + close() + } + + // Open latest version of the database. Room validates the schema + // once all migrations execute. + Room.databaseBuilder( + InstrumentationRegistry.getInstrumentation().targetContext, + TalkDatabase::class.java, + TEST_DB + ).addMigrations(*migrations).build().apply { + openHelper.writableDatabase.close() + } + } + + companion object { + private const val TEST_DB = "migration-test" + private val migrations = arrayOf( + Migrations.MIGRATION_6_8, + Migrations.MIGRATION_7_8, + Migrations.MIGRATION_8_9, + Migrations.MIGRATION_9_10 + ) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index ce631df93f7..26e55a96158 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -272,6 +272,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { } else { if (!appPreferences.isDbRoomMigrated) { appPreferences.isDbRoomMigrated = true + Log.d("Julius", "isDbRoomMigrated set to true") } userManager.users.subscribe(object : SingleObserver> { diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt index aca16d0a6b9..6789573f4eb 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt @@ -47,6 +47,13 @@ object Migrations { } } + val MIGRATION_9_10 = object : Migration(9, 10) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i("Migrations", "Migrating 9 to 10") + migrateToTriplePrimaryKeyArbitraryStorage(db) + } + } + fun migrateToRoom(db: SupportSQLiteDatabase) { db.execSQL( "CREATE TABLE User_new (" + @@ -124,4 +131,29 @@ object Migrations { // Change the table name to the correct one db.execSQL("ALTER TABLE ArbitraryStorage_dualPK RENAME TO ArbitraryStorage") } + + fun migrateToTriplePrimaryKeyArbitraryStorage(db: SupportSQLiteDatabase) { + db.execSQL( + "CREATE TABLE ArbitraryStorage_dualPK (" + + "accountIdentifier INTEGER NOT NULL, " + + "\"key\" TEXT NOT NULL, " + + "object TEXT, " + + "value TEXT, " + + "PRIMARY KEY(accountIdentifier, \"key\", object)" + + ")" + ) + // Copy the data + db.execSQL( + "INSERT INTO ArbitraryStorage_dualPK (" + + "accountIdentifier, \"key\", object, value) " + + "SELECT " + + "accountIdentifier, \"key\", object, value " + + "FROM ArbitraryStorage" + ) + // Remove the old table + db.execSQL("DROP TABLE ArbitraryStorage") + + // Change the table name to the correct one + db.execSQL("ALTER TABLE ArbitraryStorage_dualPK RENAME TO ArbitraryStorage") + } } diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt index f736624398a..0825d4a2ccb 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt @@ -45,7 +45,7 @@ import java.util.Locale @Database( entities = [UserEntity::class, ArbitraryStorageEntity::class], - version = 9, + version = 10, exportSchema = true ) @TypeConverters( @@ -76,7 +76,7 @@ abstract class TalkDatabase : RoomDatabase() { val passCharArray = context.getString(R.string.nc_talk_database_encryption_key).toCharArray() val passphrase: ByteArray = SQLiteDatabase.getBytes(passCharArray) - val factory = if (appPreferences.isDbRoomMigrated) { + val factory = if (appPreferences.isDbRoomMigrated) { // Why isn't this set??? This should be set Log.i(TAG, "No cipher migration needed") SupportFactory(passphrase) } else { @@ -95,8 +95,13 @@ abstract class TalkDatabase : RoomDatabase() { return Room .databaseBuilder(context.applicationContext, TalkDatabase::class.java, dbName) // NOTE: comment out openHelperFactory to view the database entries in Android Studio for debugging - .openHelperFactory(factory) - .addMigrations(Migrations.MIGRATION_6_8, Migrations.MIGRATION_7_8, Migrations.MIGRATION_8_9) + // .openHelperFactory(factory) // FIXME crashes when database Migration works + .addMigrations( + Migrations.MIGRATION_6_8, + Migrations.MIGRATION_7_8, + Migrations.MIGRATION_8_9, + Migrations.MIGRATION_9_10 + ) .allowMainThreadQueries() .addCallback( object : RoomDatabase.Callback() {