SQLite database migration appears to only partially apply in Espresso test

夙愿已清 提交于 2019-12-08 21:14:02

问题


We have an SQLite database and a corresponding SQLiteOpenHelper subclass. This helper has an onDowngrade implementation that I would like to write an Espresso test for.

The full onDowngrade implementation is available here. This is a simplified version of it:

@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("CREATE TABLE IF NOT EXISTS foo_tmp (_id integer primary key, bar text not null, baz text not null);");
    db.execSQL("INSERT INTO foo_tmp(_id,bar,baz) SELECT _id,bar,baz FROM foo;");
    db.execSQL("DROP TABLE IF EXISTS foo;");
    db.execSQL("RENAME TABLE foo_tmp TO foo;");
}

The test loads a database dump with a very high version number and added or removed columns. It then gets a readable database and ensures that the version has been downgraded to the current expected version and that the column names are the expected column names. The full source is available here. This is what it looks like:

@Test
public void testMigration() throws IOException {
    writeDatabaseFile("database" + File.separator + dbFilename);
    InstancesDatabaseHelper databaseHelper = new InstancesDatabaseHelper();

    SQLiteDatabase db = databaseHelper.getReadableDatabase();
    assertThat(db.getVersion(), is(InstancesDatabaseHelper.DATABASE_VERSION));

    List<String> newColumnNames = InstancesDatabaseHelper.getInstancesColumnNames(db);

    assertThat(newColumnNames, contains(InstancesDatabaseHelper.CURRENT_VERSION_COLUMN_NAMES));
}

Everything works as intended if I manually load the same database dumps into the app. However, when I run this test, it looks like the last RENAME in the migration is not executed. If I comment out the last two SQL statements in the migration (dropping the original table and renaming the temporary table to the original table name), I can assert that the temporary table has the expected contents (here is a commit that shows this).

With some experimentation, we have found that adding databaseHelper.getReadableDatabase().close(); in the test after instantiating the SQLiteOpenHelper makes the tests pass. Given that the onDowngrade call is wrapped in a transaction, I don't understand how this is possible.

Could this point to a bug in our onDowngrade implementation? Is triggering migrations in Espresso tests different in some way?


回答1:


There probably is a race condition, because SQLite is a shared resource.

eg. when the test runs before the last one COMMIT statement was issued.

Wrap it into a transaction (also see Isolation In SQLite):

if(! BuildConfig.DEBUG) { 
    db.beginTransaction();
} else {
    db.beginTransactionWithListener(new SQLiteTransactionListener() {
        @Override public void onBegin()    {Log.d(LOG_TAG, "onBegin()");}
        @Override public void onCommit()   {Log.d(LOG_TAG, "onCommit()");}
        @Override public void onRollback() {Log.d(LOG_TAG, "onRollback()");}
    });
}

try {

    db.execSQL("CREATE TABLE IF NOT EXISTS foo_tmp (_id integer primary key, bar text not null, baz text not null);");
    db.execSQL("INSERT INTO foo_tmp(_id,bar,baz) SELECT _id,bar,baz FROM foo;");
    db.execSQL("DROP TABLE IF EXISTS foo;");
    db.execSQL("RENAME TABLE foo_tmp TO foo;");
    db.setTransactionSuccessful();

} catch(SQLException e){
    Log.d(LOG_TAG, "" + e.getMessage());
} finally {
    db.endTransaction();
}
db.close();


来源:https://stackoverflow.com/questions/57280904/sqlite-database-migration-appears-to-only-partially-apply-in-espresso-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!