[go: nahoru, domu]

blob: 4e010f35e5bb8b2aaf0bae30c2bfd7f90894a8f6 [file] [log] [blame]
/*
* Copyright 2021 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.kotlintestapp.test
import androidx.lifecycle.LiveData
import androidx.lifecycle.asFlow
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Entity
import androidx.room.Insert
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.integration.kotlintestapp.RoomTestConfig
import androidx.room.integration.kotlintestapp.assumeKsp
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import com.google.common.collect.ImmutableList
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.ListenableFuture
import io.reactivex.Flowable
import io.reactivex.Observable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.util.Optional
/**
* This test matters in KSP specifically where we might use primitive adapter for non-null java
* primitives.
*/
@RunWith(AndroidJUnit4::class)
@SmallTest
class BoxedNonNullTypesTest {
lateinit var db: MyDb
@Before
fun init() {
db = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
MyDb::class.java
).build()
}
@Test
fun list() {
db.myDao().insert(MyEntity(3))
assertThat(db.myDao().getAsList()).containsExactly(3L)
}
@Test
fun list_nullable() {
assumeKsp()
db.myDao().insert(MyNullableEntity(null), MyNullableEntity(3L))
assertThat(db.myDao().getAsNullableList()).containsExactly(null, 3L)
}
@Test
fun immutableList() {
db.myDao().insert(MyEntity(4))
assertThat(db.myDao().getAsImmutableList()).containsExactly(4L)
}
@Test
@SdkSuppress(minSdkVersion = 24)
fun javaOptional() {
assertThat(db.myDao().getAsJavaOptional()).isEqualTo(
Optional.empty<Long>()
)
db.myDao().insert(MyEntity(5))
assertThat(db.myDao().getAsJavaOptional()).isEqualTo(
Optional.of(5L)
)
}
@Test
fun guavaOptional() {
assertThat(db.myDao().getAsGuavaOptional()).isEqualTo(
com.google.common.base.Optional.absent<Long>()
)
db.myDao().insert(MyEntity(6))
assertThat(db.myDao().getAsGuavaOptional()).isEqualTo(
com.google.common.base.Optional.of(6L)
)
}
@Test
fun getAsLiveData() = runBlocking<Unit> {
db.myDao().insert(MyEntity(7))
assertThat(db.myDao().getAsLiveData().asFlow().first()).isEqualTo(7L)
}
@Test
fun getAsLiveData_nullable() = runBlocking<Unit> {
assumeKsp()
db.myDao().insert(MyNullableEntity(null))
assertThat(db.myDao().getAsNullableLiveData().asFlow().first()).isNull()
}
@Test
fun getAsFlow() = runBlocking<Unit> {
db.myDao().insert(MyEntity(8))
assertThat(db.myDao().getAsFlow().first()).isEqualTo(8L)
}
@Test
fun getAsFlow_nullable() = runBlocking<Unit> {
assumeKsp()
db.myDao().insert(MyNullableEntity(null))
assertThat(db.myDao().getAsNullableFlow().first()).isNull()
}
@Test
fun getAsRx2Observable() {
db.myDao().insert(MyEntity(9))
assertThat(db.myDao().getAsRx2Observable().blockingFirst()).isEqualTo(9L)
}
@Test // repro for: b/211822920
fun getAsRx2ObservableUnknownNullabilityInCursor() {
if (RoomTestConfig.isKsp) {
// only in KSP we know the value is non-null, hence default to 0.
// in RX, it would generate code that would return null and get filtered by RxRoom
// Even though this becomes inconsistent between KSP and KAPT, the KSP path is more
// consistent with the non-observable version of the query.
assertThat(db.myDao().getAsRx2ObservableUnknownTypeInCursor().blockingFirst())
.isEqualTo(0L)
}
db.myDao().insert(MyEntity(9))
assertThat(db.myDao().getAsRx2ObservableUnknownTypeInCursor().blockingFirst())
.isEqualTo(9L)
}
@Test
fun getAsRx2Flowable() {
db.myDao().insert(MyEntity(10))
assertThat(db.myDao().getAsRx2Flowable().blockingFirst()).isEqualTo(10L)
}
@Test
fun getAsRx3Observable() {
db.myDao().insert(MyEntity(11))
assertThat(db.myDao().getAsRx3Observable().blockingFirst()).isEqualTo(11L)
}
@Test
fun getAsRx3Flowable() {
db.myDao().insert(MyEntity(12))
assertThat(db.myDao().getAsRx3Flowable().blockingFirst()).isEqualTo(12L)
}
@Test
fun getAsListenableFuture() {
db.myDao().insert(MyEntity(13))
assertThat(
db.myDao().getAsListenableFuture().get()
).isEqualTo(13L)
}
@Test
fun getAsListenableFuture_nullable() {
assumeKsp()
db.myDao().insert(MyNullableEntity(null))
assertThat(
db.myDao().getAsNullableListenableFuture().get()
).isEqualTo(null)
}
@Entity
data class MyEntity(
val value: Long,
@PrimaryKey(autoGenerate = true) val id: Int = 0,
)
@Entity
data class MyNullableEntity(
val value: Long?,
@PrimaryKey(autoGenerate = true) val id: Int = 0,
)
@Database(
entities = [MyEntity::class, MyNullableEntity::class],
version = 1,
exportSchema = false
)
abstract class MyDb : RoomDatabase() {
abstract fun myDao(): MyDao
}
@Dao
interface MyDao {
@Query("SELECT value FROM MyEntity")
fun getAsList(): List<Long>
@Suppress("ROOM_UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE")
@Query("SELECT value FROM MyNullableEntity")
fun getAsNullableList(): List<Long?>
// immutable list does not allow nulls, hence no nullable test for it
@Query("SELECT value FROM MyEntity")
fun getAsImmutableList(): ImmutableList<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsJavaOptional(): Optional<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsGuavaOptional(): com.google.common.base.Optional<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsLiveData(): LiveData<Long>
@Query("SELECT value FROM MyNullableEntity LIMIT 1")
fun getAsNullableLiveData(): LiveData<Long?>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsFlow(): Flow<Long>
@Query("SELECT value FROM MyNullableEntity LIMIT 1")
fun getAsNullableFlow(): Flow<Long?>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsRx2Observable(): Observable<Long>
@Query("SELECT max(value) FROM MyEntity")
fun getAsRx2ObservableUnknownTypeInCursor(): Observable<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsRx2Flowable(): Flowable<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsRx3Observable(): io.reactivex.rxjava3.core.Observable<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsRx3Flowable(): io.reactivex.rxjava3.core.Flowable<Long>
@Query("SELECT value FROM MyEntity LIMIT 1")
fun getAsListenableFuture(): ListenableFuture<Long>
@Query("SELECT value FROM MyNullableEntity LIMIT 1")
fun getAsNullableListenableFuture(): ListenableFuture<Long?>
@Insert
fun insert(vararg entities: MyEntity)
@Insert
fun insert(vararg entities: MyNullableEntity)
}
}