[go: nahoru, domu]

Commonize addTypeConverter() API in RoomDatabase.Builder

Also fix converter name in error messages that relied on `KClass.toString()` and now uses `KClass.qualifiedName` which doesn't require Kotlin reflection library.

Bug: 299168035
Bug: 309995729
Test: TypeConverterTest
Change-Id: I440fc9a82f581a36a4946753dea30893f7888180
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
similarity index 96%
rename from room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 300ff8c..765680c 100644
--- a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import kotlinx.coroutines.Dispatchers
 
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class QueryTest : BaseQueryTest() {
 
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
 
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
similarity index 78%
copy from room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
copy to room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index 300ff8c..d89288c 100644
--- a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -17,19 +17,17 @@
 package androidx.room.integration.multiplatformtestapp.test
 
 import androidx.room.Room
+import androidx.room.RoomDatabase
 import androidx.sqlite.driver.bundled.BundledSQLiteDriver
 import androidx.test.platform.app.InstrumentationRegistry
-import kotlinx.coroutines.Dispatchers
 
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class TypeConverterTest : BaseTypeConverterTest() {
 
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
 
-    override fun getRoomDatabase(): SampleDatabase {
-        return Room.inMemoryDatabaseBuilder<SampleDatabase>(
+    override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
+        return Room.inMemoryDatabaseBuilder<TestDatabase>(
             context = instrumentation.targetContext
         ).setDriver(BundledSQLiteDriver())
-            .setQueryCoroutineContext(Dispatchers.IO)
-            .build()
     }
 }
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseSimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
similarity index 99%
rename from room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseSimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index 836f9c4..43909bf 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseSimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -37,7 +37,7 @@
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.yield
 
-abstract class BaseSimpleQueryTest {
+abstract class BaseQueryTest {
 
     private lateinit var db: SampleDatabase
 
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
new file mode 100644
index 0000000..93b06c4
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 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.multiplatformtestapp.test
+
+import androidx.kruth.assertThat
+import androidx.kruth.assertThrows
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.PrimaryKey
+import androidx.room.ProvidedTypeConverter
+import androidx.room.Query
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverter
+import androidx.room.TypeConverters
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+abstract class BaseTypeConverterTest {
+    abstract fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase>
+
+    @Test
+    fun entityWithConverter() = runTest {
+        val database = getDatabaseBuilder()
+            .addTypeConverter(BarConverter())
+            .build()
+        val entity = TestEntity(1, Foo(1979), Bar("Mujer Boricua"))
+        database.getDao().insertItem(entity)
+        assertThat(database.getDao().getItem(1)).isEqualTo(entity)
+        database.close()
+    }
+
+    @Test
+    fun missingTypeConverter() {
+        assertThrows<IllegalArgumentException> {
+            getDatabaseBuilder().build()
+        }.hasMessageThat().isEqualTo("A required type converter (" +
+            "${BarConverter::class.qualifiedName}) for ${TestDao::class.qualifiedName} is " +
+            "missing in the database configuration.")
+    }
+
+    @Database(entities = [TestEntity::class], version = 1, exportSchema = false)
+    @TypeConverters(FooConverter::class, BarConverter::class)
+    abstract class TestDatabase : RoomDatabase() {
+        abstract fun getDao(): TestDao
+    }
+
+    @Dao
+    interface TestDao {
+        @Insert
+        suspend fun insertItem(item: TestEntity)
+
+        @Query("SELECT * FROM TestEntity WHERE id = :id")
+        suspend fun getItem(id: Long): TestEntity
+    }
+
+    @Entity
+    data class TestEntity(@PrimaryKey val id: Long, val foo: Foo, val bar: Bar)
+
+    data class Foo(val number: Int)
+
+    data class Bar(val text: String)
+
+    object FooConverter {
+        @TypeConverter
+        fun toFoo(number: Int): Foo = Foo(number)
+
+        @TypeConverter
+        fun fromFoo(foo: Foo): Int = foo.number
+    }
+
+    @ProvidedTypeConverter
+    class BarConverter {
+        @TypeConverter
+        fun toBar(text: String): Bar = Bar(text)
+
+        @TypeConverter
+        fun fromBar(bar: Bar): String = bar.text
+    }
+}
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
similarity index 95%
rename from room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 6be6358f..fa91c34 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -20,7 +20,7 @@
 import androidx.sqlite.driver.bundled.BundledSQLiteDriver
 import kotlinx.coroutines.Dispatchers
 
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class QueryTest : BaseQueryTest() {
 
     override fun getRoomDatabase(): SampleDatabase {
         return Room.inMemoryDatabaseBuilder<SampleDatabase>()
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
similarity index 74%
copy from room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
copy to room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index 6be6358f..35bdee5 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -17,15 +17,13 @@
 package androidx.room.integration.multiplatformtestapp.test
 
 import androidx.room.Room
+import androidx.room.RoomDatabase
 import androidx.sqlite.driver.bundled.BundledSQLiteDriver
-import kotlinx.coroutines.Dispatchers
 
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class TypeConverterTest : BaseTypeConverterTest() {
 
-    override fun getRoomDatabase(): SampleDatabase {
-        return Room.inMemoryDatabaseBuilder<SampleDatabase>()
+    override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
+        return Room.inMemoryDatabaseBuilder<TestDatabase>()
             .setDriver(BundledSQLiteDriver())
-            .setQueryCoroutineContext(Dispatchers.IO)
-            .build()
     }
 }
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
similarity index 86%
rename from room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 5a0d3c9..9ad0d65 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -21,10 +21,12 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.IO
 
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class QueryTest : BaseQueryTest() {
 
     override fun getRoomDatabase(): SampleDatabase {
-        return Room.inMemoryDatabaseBuilder { SampleDatabase::class.instantiateImpl() }
+        return Room.inMemoryDatabaseBuilder<SampleDatabase> {
+            SampleDatabase::class.instantiateImpl()
+        }
             .setDriver(BundledSQLiteDriver())
             .setQueryCoroutineContext(Dispatchers.IO)
             .build()
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
similarity index 74%
copy from room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
copy to room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index 6be6358f..de89cdd 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -17,15 +17,13 @@
 package androidx.room.integration.multiplatformtestapp.test
 
 import androidx.room.Room
+import androidx.room.RoomDatabase
 import androidx.sqlite.driver.bundled.BundledSQLiteDriver
-import kotlinx.coroutines.Dispatchers
 
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class TypeConverterTest : BaseTypeConverterTest() {
 
-    override fun getRoomDatabase(): SampleDatabase {
-        return Room.inMemoryDatabaseBuilder<SampleDatabase>()
+    override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
+        return Room.inMemoryDatabaseBuilder<TestDatabase> { TestDatabase::class.instantiateImpl() }
             .setDriver(BundledSQLiteDriver())
-            .setQueryCoroutineContext(Dispatchers.IO)
-            .build()
     }
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
index c90ae89..523828e 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
@@ -1536,13 +1536,13 @@
         }
 
         /**
-         * Adds a type converter instance to this database.
+         * Adds a type converter instance to the builder.
          *
-         * @param typeConverter The converter. It must be an instance of a class annotated with
-         * [ProvidedTypeConverter] otherwise Room will throw an exception.
+         * @param typeConverter The converter instance that is annotated with
+         * [ProvidedTypeConverter].
          * @return This builder instance.
          */
-        open fun addTypeConverter(typeConverter: Any) = apply {
+        actual open fun addTypeConverter(typeConverter: Any) = apply {
             this.typeConverters.add(typeConverter)
         }
 
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
index 4b07c62..79856ad 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
@@ -314,6 +314,15 @@
         ): Builder<T>
 
         /**
+         * Adds a type converter instance to the builder.
+         *
+         * @param typeConverter The converter instance that is annotated with
+         * [ProvidedTypeConverter].
+         * @return This builder instance.
+         */
+        fun addTypeConverter(typeConverter: Any): Builder<T>
+
+        /**
          * Sets the [CoroutineContext] that will be used to execute all asynchronous queries and
          * tasks, such as `Flow` emissions and [InvalidationTracker] notifications.
          *
@@ -571,7 +580,7 @@
                 }
             }
             require(foundIndex >= 0) {
-                "A required type converter ($converter) for" +
+                "A required type converter (${converter.qualifiedName}) for" +
                     " ${daoName.qualifiedName} is missing in the database configuration."
             }
             addTypeConverter(converter, configuration.typeConverters[foundIndex])
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
index adabee5..312621d 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
@@ -289,6 +289,7 @@
 
         private var driver: SQLiteDriver? = null
         private val callbacks = mutableListOf<Callback>()
+        private val typeConverters: MutableList<Any> = mutableListOf()
         private var queryCoroutineContext: CoroutineContext? = null
 
         /**
@@ -437,6 +438,17 @@
         }
 
         /**
+         * Adds a type converter instance to the builder.
+         *
+         * @param typeConverter The converter instance that is annotated with
+         * [ProvidedTypeConverter].
+         * @return This builder instance.
+         */
+        actual fun addTypeConverter(typeConverter: Any) = apply {
+            this.typeConverters.add(typeConverter)
+        }
+
+        /**
          * Sets the [CoroutineContext] that will be used to execute all asynchronous queries and
          * tasks, such as `Flow` emissions and [InvalidationTracker] notifications.
          *
@@ -487,7 +499,7 @@
                 requireMigration = requireMigration,
                 allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade,
                 migrationNotRequiredFrom = migrationsNotRequiredFrom,
-                typeConverters = emptyList(),
+                typeConverters = typeConverters,
                 autoMigrationSpecs = autoMigrationSpecs,
                 allowDestructiveMigrationForAllTables = allowDestructiveMigrationForAllTables,
                 sqliteDriver = driver,