Issue#3 Implement migration test which runs both on Real device and via Robolectric
This commit is contained in:
parent
4a1254d092
commit
2aca350175
16 changed files with 1028 additions and 2 deletions
|
|
@ -54,17 +54,21 @@ android {
|
|||
sourceSets {
|
||||
androidTest {
|
||||
java.srcDirs += "src/sharedTest/java"
|
||||
assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
androidTestHilt {
|
||||
java.srcDirs += "src/sharedTestHilt/java"
|
||||
assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
androidTestKoin {
|
||||
java.srcDirs += "src/sharedTestKoin/java"
|
||||
assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
|
||||
test {
|
||||
java.srcDirs += "src/sharedTest/java"
|
||||
java.srcDirs += "src/robolectricTest/java"
|
||||
resources.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
testHilt {
|
||||
java.srcDirs += "src/sharedTestHilt/java"
|
||||
|
|
@ -149,6 +153,7 @@ dependencies {
|
|||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||
kaptTest "com.google.dagger:hilt-compiler:$hilt_version"
|
||||
|
||||
androidTestImplementation "androidx.room:room-testing:$androidx_room_version"
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||
androidTestImplementation "io.insert-koin:koin-test-junit4:$koin_version"
|
||||
androidTestImplementation "junit:junit:$testing_junit4_version"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "3723fe73a9d3dc43de8ff3e52ec46490",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "FavouriteEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`content_id` TEXT NOT NULL, PRIMARY KEY(`content_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "contentId",
|
||||
"columnName": "content_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"content_id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3723fe73a9d3dc43de8ff3e52ec46490')"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
import android.app.Instrumentation
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.room.testing.MigrationTestHelper
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
class AndroidMigrationTestRule : SharedMigrationTestRule {
|
||||
|
||||
private val migrationTestHelper: MigrationTestHelper
|
||||
|
||||
constructor(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>
|
||||
) {
|
||||
migrationTestHelper = MigrationTestHelper(instrumentation, databaseClass)
|
||||
}
|
||||
|
||||
constructor(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>
|
||||
) {
|
||||
migrationTestHelper = MigrationTestHelper(instrumentation, databaseClass, specs)
|
||||
}
|
||||
|
||||
constructor(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>,
|
||||
openFactory: SupportSQLiteOpenHelper.Factory
|
||||
) {
|
||||
migrationTestHelper =
|
||||
MigrationTestHelper(instrumentation, databaseClass, specs, openFactory)
|
||||
}
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement =
|
||||
migrationTestHelper.apply(base, description)
|
||||
|
||||
override fun closeWhenFinished(db: RoomDatabase) =
|
||||
migrationTestHelper.closeWhenFinished(db)
|
||||
|
||||
override fun closeWhenFinished(db: SupportSQLiteDatabase) =
|
||||
migrationTestHelper.closeWhenFinished(db)
|
||||
|
||||
override fun createDatabase(name: String, version: Int): SupportSQLiteDatabase =
|
||||
migrationTestHelper.createDatabase(name, version)
|
||||
|
||||
override fun runMigrationsAndValidate(
|
||||
name: String,
|
||||
version: Int,
|
||||
validateDroppedTables: Boolean,
|
||||
vararg migrations: Migration
|
||||
): SupportSQLiteDatabase =
|
||||
migrationTestHelper.runMigrationsAndValidate(
|
||||
name,
|
||||
version,
|
||||
validateDroppedTables,
|
||||
*migrations
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
import android.app.Instrumentation
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
|
||||
object AndroidMigrationTestRuleFactory : SharedMigrationTestRuleFactory {
|
||||
override fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>
|
||||
): SharedMigrationTestRule =
|
||||
AndroidMigrationTestRule(
|
||||
instrumentation,
|
||||
databaseClass
|
||||
)
|
||||
|
||||
override fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>
|
||||
): SharedMigrationTestRule =
|
||||
AndroidMigrationTestRule(
|
||||
instrumentation,
|
||||
databaseClass,
|
||||
specs
|
||||
)
|
||||
|
||||
override fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>,
|
||||
openFactory: SupportSQLiteOpenHelper.Factory
|
||||
): SharedMigrationTestRule =
|
||||
AndroidMigrationTestRule(
|
||||
instrumentation,
|
||||
databaseClass,
|
||||
specs,
|
||||
openFactory,
|
||||
)
|
||||
}
|
||||
|
|
@ -12,4 +12,7 @@ object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
|||
|
||||
override fun createSnackbarVerification(): SnackbarVerificationTestRule =
|
||||
AndroidTestSnackbarVerificationTestRule
|
||||
|
||||
override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory =
|
||||
AndroidMigrationTestRuleFactory
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import org.fnives.test.showcase.storage.favourite.FavouriteEntity
|
|||
|
||||
@Database(
|
||||
entities = [FavouriteEntity::class],
|
||||
version = 1,
|
||||
version = 2,
|
||||
exportSchema = true
|
||||
)
|
||||
abstract class LocalDatabase : RoomDatabase() {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ package org.fnives.test.showcase.storage.database
|
|||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import org.fnives.test.showcase.storage.LocalDatabase
|
||||
import org.fnives.test.showcase.storage.migation.Migration1To2
|
||||
|
||||
object DatabaseInitialization {
|
||||
|
||||
fun create(context: Context): LocalDatabase =
|
||||
Room.databaseBuilder(context, LocalDatabase::class.java, "local_database")
|
||||
.addMigrations(Migration1To2())
|
||||
.allowMainThreadQueries()
|
||||
.build()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package org.fnives.test.showcase.storage.favourite
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity
|
||||
data class FavouriteEntity(@PrimaryKey val contentId: String)
|
||||
data class FavouriteEntity(
|
||||
@ColumnInfo(name = "content_id")
|
||||
@PrimaryKey val contentId: String
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package org.fnives.test.showcase.storage.migation
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration1To2 : Migration(1, 2) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE FavouriteEntity RENAME TO FavouriteEntityOld")
|
||||
database.execSQL("CREATE TABLE FavouriteEntity(content_id TEXT NOT NULL PRIMARY KEY)")
|
||||
database.execSQL("INSERT INTO FavouriteEntity(content_id) SELECT contentId FROM FavouriteEntityOld")
|
||||
database.execSQL("DROP TABLE FavouriteEntityOld")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,632 @@
|
|||
package org.fnives.test.showcase.testutils.configuration;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.arch.core.executor.ArchTaskExecutor;
|
||||
import androidx.room.AutoMigration;
|
||||
import androidx.room.DatabaseConfiguration;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.room.RoomOpenHelper;
|
||||
import androidx.room.RoomOpenHelper.ValidationResult;
|
||||
import androidx.room.migration.AutoMigrationSpec;
|
||||
import androidx.room.migration.Migration;
|
||||
import androidx.room.migration.bundle.DatabaseBundle;
|
||||
import androidx.room.migration.bundle.DatabaseViewBundle;
|
||||
import androidx.room.migration.bundle.EntityBundle;
|
||||
import androidx.room.migration.bundle.FieldBundle;
|
||||
import androidx.room.migration.bundle.ForeignKeyBundle;
|
||||
import androidx.room.migration.bundle.FtsEntityBundle;
|
||||
import androidx.room.migration.bundle.IndexBundle;
|
||||
import androidx.room.migration.bundle.SchemaBundle;
|
||||
import androidx.room.util.FtsTableInfo;
|
||||
import androidx.room.util.TableInfo;
|
||||
import androidx.room.util.ViewInfo;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Copy of {@link androidx.room.testing.MigrationTestHelper MigrationTestHelper} except it uses resources folder instead of assets.
|
||||
*
|
||||
* This is needed since in unit tests we cannot add to the asset folder easily, without polluting the apk.
|
||||
*
|
||||
* reference: https://github.com/robolectric/robolectric/issues/2065
|
||||
*/
|
||||
public class RobolectricMigrationTestHelper extends TestWatcher implements SharedMigrationTestRule {
|
||||
private static final String TAG = "RobolectricMigrationTestHelper";
|
||||
private final String mAssetsFolder;
|
||||
private final SupportSQLiteOpenHelper.Factory mOpenFactory;
|
||||
private List<WeakReference<SupportSQLiteDatabase>> mManagedDatabases = new ArrayList<>();
|
||||
private List<WeakReference<RoomDatabase>> mManagedRoomDatabases = new ArrayList<>();
|
||||
private boolean mTestStarted;
|
||||
private Instrumentation mInstrumentation;
|
||||
@Nullable
|
||||
private List<AutoMigrationSpec> mSpecs;
|
||||
@Nullable
|
||||
private Class<? extends RoomDatabase> mDatabaseClass;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new migration helper. It uses the Instrumentation context to load the schema
|
||||
* (falls back to the app resources) and the target context to create the database.
|
||||
*
|
||||
* @param instrumentation The instrumentation instance.
|
||||
* @param databaseClass The Database class to be tested.
|
||||
*/
|
||||
public RobolectricMigrationTestHelper(@NonNull Instrumentation instrumentation,
|
||||
@NonNull Class<? extends RoomDatabase> databaseClass) {
|
||||
this(instrumentation, databaseClass, new ArrayList<>(),
|
||||
new FrameworkSQLiteOpenHelperFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new migration helper. It uses the Instrumentation context to load the schema
|
||||
* (falls back to the app resources) and the target context to create the database.
|
||||
* <p>
|
||||
* An instance of a class annotated with {@link androidx.room.ProvidedAutoMigrationSpec} has
|
||||
* to be provided to Room using this constructor. MigrationTestHelper will map auto migration
|
||||
* spec classes to their provided instances before running and validatingt the Migrations.
|
||||
*
|
||||
* @param instrumentation The instrumentation instance.
|
||||
* @param databaseClass The Database class to be tested.
|
||||
* @param specs The list of available auto migration specs that will be provided to
|
||||
* Room at runtime.
|
||||
*/
|
||||
public RobolectricMigrationTestHelper(@NonNull Instrumentation instrumentation,
|
||||
@NonNull Class<? extends RoomDatabase> databaseClass,
|
||||
@NonNull List<AutoMigrationSpec> specs) {
|
||||
this(instrumentation, databaseClass, specs, new FrameworkSQLiteOpenHelperFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new migration helper. It uses the Instrumentation context to load the schema
|
||||
* (falls back to the app resources) and the target context to create the database.
|
||||
* <p>
|
||||
* An instance of a class annotated with {@link androidx.room.ProvidedAutoMigrationSpec} has
|
||||
* to be provided to Room using this constructor. MigrationTestHelper will map auto migration
|
||||
* spec classes to their provided instances before running and validatingt the Migrations.
|
||||
*
|
||||
* @param instrumentation The instrumentation instance.
|
||||
* @param databaseClass The Database class to be tested.
|
||||
* @param specs The list of available auto migration specs that will be provided to
|
||||
* Room at runtime.
|
||||
* @param openFactory Factory class that allows creation of {@link SupportSQLiteOpenHelper}
|
||||
*/
|
||||
public RobolectricMigrationTestHelper(@NonNull Instrumentation instrumentation,
|
||||
@NonNull Class<? extends RoomDatabase> databaseClass,
|
||||
@NonNull List<AutoMigrationSpec> specs,
|
||||
@NonNull SupportSQLiteOpenHelper.Factory openFactory
|
||||
) {
|
||||
String assetsFolder = databaseClass.getCanonicalName();
|
||||
mInstrumentation = instrumentation;
|
||||
if (assetsFolder.endsWith("/")) {
|
||||
assetsFolder = assetsFolder.substring(0, assetsFolder.length() - 1);
|
||||
}
|
||||
mAssetsFolder = assetsFolder;
|
||||
mOpenFactory = openFactory;
|
||||
mDatabaseClass = databaseClass;
|
||||
mSpecs = specs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void starting(Description description) {
|
||||
super.starting(description);
|
||||
mTestStarted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database in the given version.
|
||||
* If the database file already exists, it tries to delete it first. If delete fails, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param name The name of the database.
|
||||
* @param version The version in which the database should be created.
|
||||
* @return A database connection which has the schema in the requested version.
|
||||
* @throws IOException If it cannot find the schema description in the assets folder.
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
@Override
|
||||
@NotNull
|
||||
public SupportSQLiteDatabase createDatabase(@NotNull String name, int version) throws IOException {
|
||||
File dbPath = mInstrumentation.getTargetContext().getDatabasePath(name);
|
||||
if (dbPath.exists()) {
|
||||
Log.d(TAG, "deleting database file " + name);
|
||||
if (!dbPath.delete()) {
|
||||
throw new IllegalStateException("There is a database file and I could not delete"
|
||||
+ " it. Make sure you don't have any open connections to that database"
|
||||
+ " before calling this method.");
|
||||
}
|
||||
}
|
||||
SchemaBundle schemaBundle = loadSchema(version);
|
||||
RoomDatabase.MigrationContainer container = new RoomDatabase.MigrationContainer();
|
||||
DatabaseConfiguration configuration = new DatabaseConfiguration(
|
||||
mInstrumentation.getTargetContext(),
|
||||
name,
|
||||
mOpenFactory,
|
||||
container,
|
||||
null,
|
||||
true,
|
||||
RoomDatabase.JournalMode.TRUNCATE,
|
||||
ArchTaskExecutor.getIOThreadExecutor(),
|
||||
ArchTaskExecutor.getIOThreadExecutor(),
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
Collections.<Integer>emptySet(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
RoomOpenHelper roomOpenHelper = new RoomOpenHelper(configuration,
|
||||
new CreatingDelegate(schemaBundle.getDatabase()),
|
||||
schemaBundle.getDatabase().getIdentityHash(),
|
||||
// we pass the same hash twice since an old schema does not necessarily have
|
||||
// a legacy hash and we would not even persist it.
|
||||
schemaBundle.getDatabase().getIdentityHash());
|
||||
return openDatabase(name, roomOpenHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given set of migrations on the provided database.
|
||||
* <p>
|
||||
* It uses the same algorithm that Room uses to choose migrations so the migrations instances
|
||||
* that are provided to this method must be sufficient to bring the database from current
|
||||
* version to the desired version.
|
||||
* <p>
|
||||
* After the migration, the method validates the database schema to ensure that migration
|
||||
* result matches the expected schema. Handling of dropped tables depends on the
|
||||
* {@code validateDroppedTables} argument. If set to true, the verification will fail if it
|
||||
* finds a table that is not registered in the Database. If set to false, extra tables in the
|
||||
* database will be ignored (this is the runtime library behavior).
|
||||
*
|
||||
* @param name The database name. You must first create this database via
|
||||
* {@link #createDatabase(String, int)}.
|
||||
* @param version The final version after applying the migrations.
|
||||
* @param validateDroppedTables If set to true, validation will fail if the database has
|
||||
* unknown
|
||||
* tables.
|
||||
* @param migrations The list of available migrations.
|
||||
* @throws IOException If it cannot find the schema for {@code toVersion}.
|
||||
* @throws IllegalStateException If the schema validation fails.
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
@NotNull
|
||||
public SupportSQLiteDatabase runMigrationsAndValidate(@NotNull String name,
|
||||
int version,
|
||||
boolean validateDroppedTables,
|
||||
@NotNull
|
||||
Migration... migrations) throws IOException {
|
||||
File dbPath = mInstrumentation.getTargetContext().getDatabasePath(name);
|
||||
if (!dbPath.exists()) {
|
||||
throw new IllegalStateException("Cannot find the database file for " + name + ". "
|
||||
+ "Before calling runMigrations, you must first create the database via "
|
||||
+ "createDatabase.");
|
||||
}
|
||||
SchemaBundle schemaBundle = loadSchema(version);
|
||||
RoomDatabase.MigrationContainer container = new RoomDatabase.MigrationContainer();
|
||||
container.addMigrations(getAutoMigrations(mSpecs));
|
||||
container.addMigrations(migrations);
|
||||
DatabaseConfiguration configuration = new DatabaseConfiguration(
|
||||
mInstrumentation.getTargetContext(),
|
||||
name,
|
||||
mOpenFactory,
|
||||
container,
|
||||
null,
|
||||
true,
|
||||
RoomDatabase.JournalMode.TRUNCATE,
|
||||
ArchTaskExecutor.getIOThreadExecutor(),
|
||||
ArchTaskExecutor.getIOThreadExecutor(),
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
Collections.<Integer>emptySet(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
RoomOpenHelper roomOpenHelper = new RoomOpenHelper(configuration,
|
||||
new MigratingDelegate(schemaBundle.getDatabase(), validateDroppedTables),
|
||||
// we pass the same hash twice since an old schema does not necessarily have
|
||||
// a legacy hash and we would not even persist it.
|
||||
schemaBundle.getDatabase().getIdentityHash(),
|
||||
schemaBundle.getDatabase().getIdentityHash());
|
||||
return openDatabase(name, roomOpenHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Migration} of a database that has been generated using
|
||||
* {@link AutoMigration}.
|
||||
*/
|
||||
@NonNull
|
||||
private List<Migration> getAutoMigrations(List<AutoMigrationSpec> userProvidedSpecs) {
|
||||
if (mDatabaseClass == null) {
|
||||
if (userProvidedSpecs.isEmpty()) {
|
||||
// TODO: Detect that there are auto migrations to test when a deprecated
|
||||
// constructor is used.
|
||||
Log.e(TAG, "If you have any AutoMigrations in your implementation, you must use "
|
||||
+ "a non-deprecated MigrationTestHelper constructor to provide the "
|
||||
+ "Database class in order to test them. If you do not have any "
|
||||
+ "AutoMigrations to test, you may ignore this warning.");
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
throw new IllegalStateException("You must provide the database class in the "
|
||||
+ "MigrationTestHelper constructor in order to test auto migrations.");
|
||||
}
|
||||
}
|
||||
|
||||
RoomDatabase db = Room.getGeneratedImplementation(mDatabaseClass, "_Impl");
|
||||
Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs =
|
||||
db.getRequiredAutoMigrationSpecs();
|
||||
return db.getAutoMigrations(
|
||||
createAutoMigrationSpecMap(requiredAutoMigrationSpecs, userProvidedSpecs)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps auto migration spec classes to their provided instance.
|
||||
*/
|
||||
private Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> createAutoMigrationSpecMap(
|
||||
Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs,
|
||||
List<AutoMigrationSpec> userProvidedSpecs) {
|
||||
Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> specMap = new HashMap<>();
|
||||
if (requiredAutoMigrationSpecs.isEmpty()) {
|
||||
return specMap;
|
||||
}
|
||||
|
||||
if (userProvidedSpecs == null) {
|
||||
throw new IllegalStateException(
|
||||
"You must provide all required auto migration specs in the "
|
||||
+ "MigrationTestHelper constructor."
|
||||
);
|
||||
}
|
||||
|
||||
for (Class<? extends AutoMigrationSpec> spec : requiredAutoMigrationSpecs) {
|
||||
boolean found = false;
|
||||
AutoMigrationSpec match = null;
|
||||
for (AutoMigrationSpec provided : userProvidedSpecs) {
|
||||
if (spec.isAssignableFrom(provided.getClass())) {
|
||||
found = true;
|
||||
match = provided;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException(
|
||||
"A required auto migration spec (" + spec.getCanonicalName() + ") has not"
|
||||
+ " been provided."
|
||||
);
|
||||
}
|
||||
specMap.put(spec, match);
|
||||
}
|
||||
return specMap;
|
||||
}
|
||||
|
||||
|
||||
private SupportSQLiteDatabase openDatabase(String name, RoomOpenHelper roomOpenHelper) {
|
||||
SupportSQLiteOpenHelper.Configuration config =
|
||||
SupportSQLiteOpenHelper.Configuration
|
||||
.builder(mInstrumentation.getTargetContext())
|
||||
.callback(roomOpenHelper)
|
||||
.name(name)
|
||||
.build();
|
||||
SupportSQLiteDatabase db = mOpenFactory.create(config).getWritableDatabase();
|
||||
mManagedDatabases.add(new WeakReference<>(db));
|
||||
return db;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finished(Description description) {
|
||||
super.finished(description);
|
||||
for (WeakReference<SupportSQLiteDatabase> dbRef : mManagedDatabases) {
|
||||
SupportSQLiteDatabase db = dbRef.get();
|
||||
if (db != null && db.isOpen()) {
|
||||
try {
|
||||
db.close();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (WeakReference<RoomDatabase> dbRef : mManagedRoomDatabases) {
|
||||
final RoomDatabase roomDatabase = dbRef.get();
|
||||
if (roomDatabase != null) {
|
||||
roomDatabase.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a database connection to be automatically closed when the test finishes.
|
||||
* <p>
|
||||
* This only works if {@code MigrationTestHelper} is registered as a Junit test rule via
|
||||
* {@link org.junit.Rule Rule} annotation.
|
||||
*
|
||||
* @param db The database connection that should be closed after the test finishes.
|
||||
*/
|
||||
@Override
|
||||
public void closeWhenFinished(@NotNull SupportSQLiteDatabase db) {
|
||||
if (!mTestStarted) {
|
||||
throw new IllegalStateException("You cannot register a database to be closed before"
|
||||
+ " the test starts. Maybe you forgot to annotate MigrationTestHelper as a"
|
||||
+ " test rule? (@Rule)");
|
||||
}
|
||||
mManagedDatabases.add(new WeakReference<>(db));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a database connection to be automatically closed when the test finishes.
|
||||
* <p>
|
||||
* This only works if {@code MigrationTestHelper} is registered as a Junit test rule via
|
||||
* {@link org.junit.Rule Rule} annotation.
|
||||
*
|
||||
* @param db The RoomDatabase instance which holds the database.
|
||||
*/
|
||||
@Override
|
||||
public void closeWhenFinished(@NotNull RoomDatabase db) {
|
||||
if (!mTestStarted) {
|
||||
throw new IllegalStateException("You cannot register a database to be closed before"
|
||||
+ " the test starts. Maybe you forgot to annotate MigrationTestHelper as a"
|
||||
+ " test rule? (@Rule)");
|
||||
}
|
||||
mManagedRoomDatabases.add(new WeakReference<>(db));
|
||||
}
|
||||
|
||||
private SchemaBundle loadSchema(int version) throws IOException {
|
||||
try {
|
||||
return loadSchema(mInstrumentation.getContext(), version);
|
||||
} catch (FileNotFoundException testAssetsIOExceptions) {
|
||||
Log.w(TAG, "Could not find the schema file in the test assets. Checking the"
|
||||
+ " application assets");
|
||||
try {
|
||||
return loadSchema(mInstrumentation.getTargetContext(), version);
|
||||
} catch (FileNotFoundException appAssetsException) {
|
||||
// throw the test assets exception instead
|
||||
throw new FileNotFoundException("Cannot find the schema file in the assets folder. "
|
||||
+ "Make sure to include the exported json schemas in your test assert "
|
||||
+ "inputs. See "
|
||||
+ "https://developer.android.com/training/data-storage/room/"
|
||||
+ "migrating-db-versions#export-schema for details. Missing file: "
|
||||
+ testAssetsIOExceptions.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private SchemaBundle loadSchema(Context context, int version) throws IOException {
|
||||
String fileName = mAssetsFolder + "/" + version + ".json";
|
||||
try {
|
||||
InputStream input = getClass().getClassLoader()
|
||||
.getResourceAsStream(fileName);
|
||||
return SchemaBundle.deserialize(input);
|
||||
} catch (NullPointerException nullPointerException) {
|
||||
throw new IOException("File not found: " + fileName, nullPointerException);
|
||||
}
|
||||
// InputStream input = context.getAssets().open(mAssetsFolder + "/" + version + ".json");
|
||||
// return SchemaBundle.deserialize(input);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
static TableInfo toTableInfo(EntityBundle entityBundle) {
|
||||
return new TableInfo(entityBundle.getTableName(), toColumnMap(entityBundle),
|
||||
toForeignKeys(entityBundle.getForeignKeys()), toIndices(entityBundle.getIndices()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
static FtsTableInfo toFtsTableInfo(FtsEntityBundle ftsEntityBundle) {
|
||||
return new FtsTableInfo(ftsEntityBundle.getTableName(), toColumnNamesSet(ftsEntityBundle),
|
||||
ftsEntityBundle.getCreateSql());
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
static ViewInfo toViewInfo(DatabaseViewBundle viewBundle) {
|
||||
return new ViewInfo(viewBundle.getViewName(), viewBundle.createView());
|
||||
}
|
||||
|
||||
private static Set<TableInfo.Index> toIndices(List<IndexBundle> indices) {
|
||||
if (indices == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<TableInfo.Index> result = new HashSet<>();
|
||||
for (IndexBundle bundle : indices) {
|
||||
result.add(new TableInfo.Index(bundle.getName(), bundle.isUnique(),
|
||||
bundle.getColumnNames(), bundle.getOrders()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Set<TableInfo.ForeignKey> toForeignKeys(
|
||||
List<ForeignKeyBundle> bundles) {
|
||||
if (bundles == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<TableInfo.ForeignKey> result = new HashSet<>(bundles.size());
|
||||
for (ForeignKeyBundle bundle : bundles) {
|
||||
result.add(new TableInfo.ForeignKey(bundle.getTable(),
|
||||
bundle.getOnDelete(), bundle.getOnUpdate(),
|
||||
bundle.getColumns(), bundle.getReferencedColumns()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Set<String> toColumnNamesSet(EntityBundle entity) {
|
||||
Set<String> result = new HashSet<>();
|
||||
for (FieldBundle field : entity.getFields()) {
|
||||
result.add(field.getColumnName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, TableInfo.Column> toColumnMap(EntityBundle entity) {
|
||||
Map<String, TableInfo.Column> result = new HashMap<>();
|
||||
for (FieldBundle bundle : entity.getFields()) {
|
||||
TableInfo.Column column = toColumn(entity, bundle);
|
||||
result.put(column.name, column);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static TableInfo.Column toColumn(EntityBundle entity, FieldBundle field) {
|
||||
return new TableInfo.Column(field.getColumnName(), field.getAffinity(),
|
||||
field.isNonNull(), findPrimaryKeyPosition(entity, field), field.getDefaultValue(),
|
||||
TableInfo.CREATED_FROM_ENTITY);
|
||||
}
|
||||
|
||||
private static int findPrimaryKeyPosition(EntityBundle entity, FieldBundle field) {
|
||||
List<String> columnNames = entity.getPrimaryKey().getColumnNames();
|
||||
int i = 0;
|
||||
for (String columnName : columnNames) {
|
||||
i++;
|
||||
if (field.getColumnName().equalsIgnoreCase(columnName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static class MigratingDelegate extends RoomOpenHelperDelegate {
|
||||
private final boolean mVerifyDroppedTables;
|
||||
|
||||
MigratingDelegate(DatabaseBundle databaseBundle, boolean verifyDroppedTables) {
|
||||
super(databaseBundle);
|
||||
mVerifyDroppedTables = verifyDroppedTables;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createAllTables(SupportSQLiteDatabase database) {
|
||||
throw new UnsupportedOperationException("Was expecting to migrate but received create."
|
||||
+ "Make sure you have created the database first.");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected RoomOpenHelper.ValidationResult onValidateSchema(
|
||||
@NonNull SupportSQLiteDatabase db) {
|
||||
final Map<String, EntityBundle> tables = mDatabaseBundle.getEntitiesByTableName();
|
||||
for (EntityBundle entity : tables.values()) {
|
||||
if (entity instanceof FtsEntityBundle) {
|
||||
final FtsTableInfo expected = toFtsTableInfo((FtsEntityBundle) entity);
|
||||
final FtsTableInfo found = FtsTableInfo.read(db, entity.getTableName());
|
||||
if (!expected.equals(found)) {
|
||||
return new ValidationResult(false, expected.name
|
||||
+ "\nExpected: " + expected + "\nFound: " + found);
|
||||
}
|
||||
} else {
|
||||
final TableInfo expected = toTableInfo(entity);
|
||||
final TableInfo found = TableInfo.read(db, entity.getTableName());
|
||||
if (!expected.equals(found)) {
|
||||
return new ValidationResult(false, expected.name
|
||||
+ "\nExpected: " + expected + " \nfound: " + found);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (DatabaseViewBundle view : mDatabaseBundle.getViews()) {
|
||||
final ViewInfo expected = toViewInfo(view);
|
||||
final ViewInfo found = ViewInfo.read(db, view.getViewName());
|
||||
if (!expected.equals(found)) {
|
||||
return new ValidationResult(false, expected
|
||||
+ "\nExpected: " + expected + " \nfound: " + found);
|
||||
}
|
||||
}
|
||||
if (mVerifyDroppedTables) {
|
||||
// now ensure tables that should be removed are removed.
|
||||
Set<String> expectedTables = new HashSet<>();
|
||||
for (EntityBundle entity : tables.values()) {
|
||||
expectedTables.add(entity.getTableName());
|
||||
if (entity instanceof FtsEntityBundle) {
|
||||
expectedTables.addAll(((FtsEntityBundle) entity).getShadowTableNames());
|
||||
}
|
||||
}
|
||||
Cursor cursor = db.query("SELECT name FROM sqlite_master WHERE type='table'"
|
||||
+ " AND name NOT IN(?, ?, ?)",
|
||||
new String[]{Room.MASTER_TABLE_NAME, "android_metadata",
|
||||
"sqlite_sequence"});
|
||||
//noinspection TryFinallyCanBeTryWithResources
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
final String tableName = cursor.getString(0);
|
||||
if (!expectedTables.contains(tableName)) {
|
||||
return new ValidationResult(false, "Unexpected table "
|
||||
+ tableName);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return new ValidationResult(true, null);
|
||||
}
|
||||
}
|
||||
|
||||
static class CreatingDelegate extends RoomOpenHelperDelegate {
|
||||
|
||||
CreatingDelegate(DatabaseBundle databaseBundle) {
|
||||
super(databaseBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createAllTables(SupportSQLiteDatabase database) {
|
||||
for (String query : mDatabaseBundle.buildCreateQueries()) {
|
||||
database.execSQL(query);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected RoomOpenHelper.ValidationResult onValidateSchema(
|
||||
@NonNull SupportSQLiteDatabase db) {
|
||||
throw new UnsupportedOperationException("This open helper just creates the database but"
|
||||
+ " it received a migration request.");
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class RoomOpenHelperDelegate extends RoomOpenHelper.Delegate {
|
||||
final DatabaseBundle mDatabaseBundle;
|
||||
|
||||
RoomOpenHelperDelegate(DatabaseBundle databaseBundle) {
|
||||
super(databaseBundle.getVersion());
|
||||
mDatabaseBundle = databaseBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dropAllTables(SupportSQLiteDatabase database) {
|
||||
throw new UnsupportedOperationException("cannot drop all tables in the test");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(SupportSQLiteDatabase database) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpen(SupportSQLiteDatabase database) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
import android.app.Instrumentation
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
|
||||
object RobolectricMigrationTestHelperFactory : SharedMigrationTestRuleFactory {
|
||||
override fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>
|
||||
): SharedMigrationTestRule =
|
||||
RobolectricMigrationTestHelper(
|
||||
instrumentation,
|
||||
databaseClass
|
||||
)
|
||||
|
||||
override fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>
|
||||
): SharedMigrationTestRule =
|
||||
RobolectricMigrationTestHelper(
|
||||
instrumentation,
|
||||
databaseClass,
|
||||
specs
|
||||
)
|
||||
|
||||
override fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>,
|
||||
openFactory: SupportSQLiteOpenHelper.Factory
|
||||
): SharedMigrationTestRule =
|
||||
RobolectricMigrationTestHelper(
|
||||
instrumentation,
|
||||
databaseClass,
|
||||
specs,
|
||||
openFactory
|
||||
)
|
||||
}
|
||||
|
|
@ -12,4 +12,7 @@ object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
|||
|
||||
override fun createSnackbarVerification(): SnackbarVerificationTestRule =
|
||||
RobolectricSnackbarVerificationTestRule
|
||||
|
||||
override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory =
|
||||
RobolectricMigrationTestHelperFactory
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package org.fnives.test.showcase.storage.migration
|
||||
|
||||
import androidx.room.Room
|
||||
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.fnives.test.showcase.storage.LocalDatabase
|
||||
import org.fnives.test.showcase.storage.favourite.FavouriteEntity
|
||||
import org.fnives.test.showcase.storage.migation.Migration1To2
|
||||
import org.fnives.test.showcase.testutils.configuration.SharedMigrationTestRule
|
||||
import org.fnives.test.showcase.testutils.configuration.createSharedMigrationTestRule
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.koin.core.context.stopKoin
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* reference:
|
||||
* https://medium.com/androiddevelopers/testing-room-migrations-be93cdb0d975
|
||||
* https://developer.android.com/training/data-storage/room/migrating-db-versions
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MigrationToLatest {
|
||||
|
||||
@get:Rule
|
||||
val helper: SharedMigrationTestRule = createSharedMigrationTestRule<LocalDatabase>(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
emptyList(),
|
||||
FrameworkSQLiteOpenHelperFactory()
|
||||
)
|
||||
|
||||
private fun getMigratedRoomDatabase(): LocalDatabase {
|
||||
val database: LocalDatabase = Room.databaseBuilder(
|
||||
InstrumentationRegistry.getInstrumentation().targetContext,
|
||||
LocalDatabase::class.java,
|
||||
TEST_DB
|
||||
)
|
||||
.addMigrations(Migration1To2())
|
||||
.build()
|
||||
// close the database and release any stream resources when the test finishes
|
||||
helper.closeWhenFinished(database)
|
||||
return database
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
stopKoin()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(IOException::class)
|
||||
fun migrate1To2() {
|
||||
val expectedEntities = setOf(
|
||||
FavouriteEntity("123"),
|
||||
FavouriteEntity("124"),
|
||||
FavouriteEntity("125")
|
||||
)
|
||||
val version1DB = helper.createDatabase(
|
||||
name = TEST_DB,
|
||||
version = 1
|
||||
)
|
||||
version1DB.run {
|
||||
execSQL("INSERT OR IGNORE INTO `FavouriteEntity` (`contentId`) VALUES (\"123\")")
|
||||
execSQL("INSERT OR IGNORE INTO `FavouriteEntity` (`contentId`) VALUES (124)")
|
||||
execSQL("INSERT OR IGNORE INTO `FavouriteEntity` (`contentId`) VALUES (125)")
|
||||
}
|
||||
version1DB.close()
|
||||
|
||||
val version2DB = helper.runMigrationsAndValidate(
|
||||
name = TEST_DB,
|
||||
version = 2,
|
||||
validateDroppedTables = true,
|
||||
Migration1To2()
|
||||
)
|
||||
version2DB.close()
|
||||
|
||||
val favouriteDao = getMigratedRoomDatabase().favouriteDao
|
||||
|
||||
val entities = runBlocking { favouriteDao.get().first() }.toSet()
|
||||
|
||||
Assert.assertEquals(expectedEntities, entities)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TEST_DB = "migration-test"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
import android.app.Instrumentation
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
import org.junit.rules.TestRule
|
||||
import java.io.IOException
|
||||
|
||||
interface SharedMigrationTestRule : TestRule {
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun createDatabase(name: String, version: Int): SupportSQLiteDatabase
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun runMigrationsAndValidate(
|
||||
name: String,
|
||||
version: Int,
|
||||
validateDroppedTables: Boolean,
|
||||
vararg migrations: Migration
|
||||
): SupportSQLiteDatabase
|
||||
|
||||
fun closeWhenFinished(db: SupportSQLiteDatabase)
|
||||
fun closeWhenFinished(db: RoomDatabase)
|
||||
}
|
||||
|
||||
inline fun <reified DB : RoomDatabase> createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation
|
||||
): SharedMigrationTestRule =
|
||||
SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory()
|
||||
.createSharedMigrationTestRule(
|
||||
instrumentation,
|
||||
DB::class.java
|
||||
)
|
||||
|
||||
inline fun <reified DB : RoomDatabase> createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
specs: List<AutoMigrationSpec>
|
||||
): SharedMigrationTestRule =
|
||||
SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory()
|
||||
.createSharedMigrationTestRule(
|
||||
instrumentation,
|
||||
DB::class.java,
|
||||
specs
|
||||
)
|
||||
|
||||
inline fun <reified DB : RoomDatabase> createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
specs: List<AutoMigrationSpec>,
|
||||
openFactory: SupportSQLiteOpenHelper.Factory
|
||||
): SharedMigrationTestRule =
|
||||
SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory()
|
||||
.createSharedMigrationTestRule(
|
||||
instrumentation,
|
||||
DB::class.java,
|
||||
specs,
|
||||
openFactory
|
||||
)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
import android.app.Instrumentation
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
|
||||
interface SharedMigrationTestRuleFactory {
|
||||
|
||||
fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
): SharedMigrationTestRule
|
||||
|
||||
fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>
|
||||
): SharedMigrationTestRule
|
||||
|
||||
fun createSharedMigrationTestRule(
|
||||
instrumentation: Instrumentation,
|
||||
databaseClass: Class<out RoomDatabase>,
|
||||
specs: List<AutoMigrationSpec>,
|
||||
openFactory: SupportSQLiteOpenHelper.Factory
|
||||
): SharedMigrationTestRule
|
||||
}
|
||||
|
|
@ -15,4 +15,6 @@ interface TestConfigurationsFactory {
|
|||
fun createLoginRobotConfiguration(): LoginRobotConfiguration
|
||||
|
||||
fun createSnackbarVerification(): SnackbarVerificationTestRule
|
||||
|
||||
fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue