mardi 30 janvier 2018

Identity hash check failed when testing Room migrations

I am writing a test following these instructions. My test class has this rule:

@Rule
public MigrationTestHelper testHelper = new MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(),
        AppDatabase.class.getCanonicalName(),
        new FrameworkSQLiteOpenHelperFactory()
);

and my test is as follows:

@Test
public void testMigration9_10() throws IOException {
    // Create the database with version 9
    SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 9);

    // Insert before migration
    ContentValues values = new ContentValues();
    values.put("rowid", 1);
    ...
    db.insert("foo", SQLiteDatabase.CONFLICT_FAIL, values);

    // Check inserted data
    Cursor c = db.query("SELECT * FROM foo WHERE rowid = " + values.get("rowid"));
    Assert.assertTrue(c.moveToFirst());
    Assert.assertEquals(c.getString(c.getColumnIndex("rowid")), values.get("rowid"));

    // Migrate
    db = testHelper.runMigrationsAndValidate(TEST_DB_NAME, 10, true, DatabaseCreator.MIGRATION_9_10);
    ...
}

But this fails at the last line (the previous assertions went well), saying this:

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

Which is wrong. I have traced what happens:

  • create the database with the version 9 identity hash
  • insert data
  • check data
  • call runMigrationsAndValidate, which:
    • opens the database
    • checks the identity hash against version 10, and fails

When checking the identity hash, it does:

private void checkIdentity(SupportSQLiteDatabase db) {
    createMasterTableIfNotExists(db);
    String identityHash = "";
    Cursor cursor = db.query(new SimpleSQLiteQuery(RoomMasterTable.READ_QUERY));
    //noinspection TryFinallyCanBeTryWithResources
    try {
        if (cursor.moveToFirst()) {
            identityHash = cursor.getString(0);
        }
    } finally {
        cursor.close();
    }
    if (!mIdentityHash.equals(identityHash)) {
        throw new IllegalStateException("Room cannot verify the data integrity. Looks like"
                + " you've changed schema but forgot to update the version number. You can"
                + " simply fix this by increasing the version number.");
    }
}

So the loaded identityHash is the one loaded from the DB, which is in version 9; and mIdentityHash is the one loaded directly by runMigrationsAndValidate using the version parameter, which is 10.

So of course it fails.

I'm wondering why it is checking the identity hashes BEFORE doing the migrations.

Here's the migration, even if I think it's not relevant here:

public static final Migration MIGRATION_9_10 = new Migration(9, 10) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.beginTransaction();
        database.execSQL("ALTER TABLE DeclarationEntity ADD latitude REAL NOT NULL DEFAULT 0.0");
        database.execSQL("ALTER TABLE DeclarationEntity ADD longitude REAL NOT NULL DEFAULT 0.0");
        database.endTransaction();
    }
};

Did I do something wrong?

PS: here's the full stack trace in case this is interesting:

at android.arch.persistence.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:119)
at android.arch.persistence.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:100)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:133)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:282)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:175)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:93)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
at android.arch.persistence.room.testing.MigrationTestHelper.openDatabase(MigrationTestHelper.java:203)
at android.arch.persistence.room.testing.MigrationTestHelper.runMigrationsAndValidate(MigrationTestHelper.java:193)

I'm using (versions.arch being 1.0.0):

implementation "android.arch.persistence.room:runtime:${versions.arch}"
annotationProcessor "android.arch.persistence.room:compiler:${versions.arch}"
androidTestImplementation "android.arch.persistence.room:testing:${versions.arch}"

Aucun commentaire:

Enregistrer un commentaire