blob: 6a5c6e3e8f450c82d5ae7c5a4e28d402523bbaa3 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room.integration.testapp.migration;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Dao;
import androidx.room.Database;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.Fts4;
import androidx.room.FtsOptions;
import androidx.room.Insert;
import androidx.room.PrimaryKey;
import androidx.room.Query;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.room.testing.MigrationTestHelper;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@MediumTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
public class FtsMigrationTest {
private static final String TEST_DB = "migration-test";
@Rule
public MigrationTestHelper helper;
public FtsMigrationTest() {
helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
FtsMigrationDb.class.getCanonicalName());
}
@Database(entities = {Book.class, User.class, AddressFts.class, Mail.class}, version = 6)
abstract static class FtsMigrationDb extends RoomDatabase {
abstract BookDao getBookDao();
abstract UserDao getUserDao();
}
@Dao
interface BookDao {
@Insert
void insert(Book book);
@Query("SELECT * FROM BOOK WHERE title MATCH :title")
Book getBook(String title);
@Query("SELECT * FROM BOOK")
Book getAllBooks();
}
@Dao
interface UserDao {
@Query("SELECT * FROM AddressFts WHERE AddressFts MATCH :searchQuery")
List<Address> searchAddress(String searchQuery);
}
@Entity
@Fts4(matchInfo = FtsOptions.MatchInfo.FTS3)
static class Book {
public String title;
public String author;
public int numOfPages;
public String text;
}
@Entity
static class User {
@PrimaryKey
public long id;
public String firstName;
public String lastName;
@Embedded
public Address address;
}
@Entity
@Fts4(contentEntity = User.class)
static class AddressFts {
@Embedded
public Address address;
}
static class Address {
public String line1;
public String line2;
public String state;
public int zipcode;
}
@Entity
@Fts4(languageId = "lid")
static class Mail {
@PrimaryKey
@ColumnInfo(name = "rowid")
public long mailId;
public String content;
public int lid;
}
@Test
public void validMigration() throws Exception {
SupportSQLiteDatabase db;
db = helper.createDatabase(TEST_DB, 1);
db.execSQL("INSERT INTO BOOK VALUES('Ready Player One', 'Ernest Cline', 402, "
+ "'Everyone my age remembers where they were and what they were doing...')");
db.close();
helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2);
Book book = getLatestDb().getBookDao().getBook("Ready Player");
assertThat(book.title, is("Ready Player One"));
assertThat(book.author, is("Ernest Cline"));
assertThat(book.numOfPages, is(402));
}
@Test
public void invalidMigration_missingFtsOption() throws Exception {
SupportSQLiteDatabase supportSQLiteDatabase = helper.createDatabase(TEST_DB, 1);
supportSQLiteDatabase.close();
try {
Context targetContext = ApplicationProvider.getApplicationContext();
FtsMigrationDb db = Room.databaseBuilder(targetContext, FtsMigrationDb.class, TEST_DB)
.addMigrations(BAD_MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5,
MIGRATION_5_6)
.build();
helper.closeWhenFinished(db);
db.getBookDao().getAllBooks();
fail("Should have failed migration.");
} catch (IllegalStateException ex) {
assertThat(ex.getMessage(), containsString("Migration didn't properly handle"));
}
}
@Test
public void validFtsContentMigration() throws Exception {
SupportSQLiteDatabase db;
db = helper.createDatabase(TEST_DB, 3);
db.execSQL("INSERT INTO Person VALUES(1, 'Ernest', 'Cline', 'Ruth Ave', '', 'TX', 78757)");
db.close();
helper.runMigrationsAndValidate(TEST_DB, 4, true, MIGRATION_3_4);
List<Address> addresses = getLatestDb().getUserDao().searchAddress("Ruth");
assertThat(addresses.size(), is(1));
assertThat(addresses.get(0).line1, is("Ruth Ave"));
}
@Test
public void validFtsWithNamedRowIdMigration() throws Exception {
SupportSQLiteDatabase db;
db = helper.createDatabase(TEST_DB, 4);
db.close();
helper.runMigrationsAndValidate(TEST_DB, 5, true, MIGRATION_4_5);
}
@Test
public void validFtsWithLanguageIdMigration() throws Exception {
SupportSQLiteDatabase db;
db = helper.createDatabase(TEST_DB, 5);
db.close();
helper.runMigrationsAndValidate(TEST_DB, 6, true, MIGRATION_5_6);
}
@SuppressWarnings("deprecation")
private FtsMigrationDb getLatestDb() {
FtsMigrationDb db = Room.databaseBuilder(
InstrumentationRegistry.getInstrumentation().getTargetContext(),
FtsMigrationDb.class, TEST_DB).addMigrations(ALL_MIGRATIONS).build();
// trigger open
db.beginTransaction();
db.endTransaction();
helper.closeWhenFinished(db);
return db;
}
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Book RENAME TO Book_old");
database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Book` USING FTS4("
+ "`title`, `author`, `numOfPages`, `text`, matchinfo=fts3)");
database.execSQL("INSERT INTO Book SELECT * FROM Book_old");
database.execSQL("DROP TABLE Book_old");
}
};
private static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL(
"CREATE TABLE IF NOT EXISTS `Person` (`id` INTEGER NOT NULL, "
+ "`firstName` TEXT, `lastName` TEXT, `line1` TEXT, `line2` TEXT, "
+ "`state` TEXT, `zipcode` INTEGER, PRIMARY KEY(`id`))");
database.execSQL(
"CREATE VIRTUAL TABLE IF NOT EXISTS `AddressFts` USING FTS4(`line1` TEXT, "
+ "`line2` TEXT, `state` TEXT, `zipcode` INTEGER, content=`Person`)");
}
};
private static final Migration MIGRATION_3_4 = new Migration(3, 4) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `Person` RENAME TO `User`");
database.execSQL("DROP TABLE `AddressFts`");
database.execSQL(
"CREATE VIRTUAL TABLE IF NOT EXISTS `AddressFts` USING FTS4(`line1` TEXT, "
+ "`line2` TEXT, `state` TEXT, `zipcode` INTEGER, content=`User`)");
database.execSQL(
"INSERT INTO `AddressFts` (`docid`, `line1`, `line2`, `state`, `zipcode`) "
+ "SELECT `rowid`, `line1`, `line2`, `state`, `zipcode` FROM `User`");
}
};
private static final Migration MIGRATION_4_5 = new Migration(4, 5) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Mail` USING FTS4("
+ "`content` TEXT NOT NULL)");
}
};
private static final Migration MIGRATION_5_6 = new Migration(5, 6) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("DROP TABLE `Mail`");
database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Mail` USING FTS4("
+ "`content` TEXT NOT NULL, languageid=`lid`)");
}
};
private static final Migration BAD_MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("DROP TABLE Book");
database.execSQL("CREATE VIRTUAL TABLE `Book` USING FTS4("
+ "`title`, `author`, `numOfPages`, `text`)");
}
};
private static final Migration[] ALL_MIGRATIONS = new Migration[]{
MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6
};
}