blob: 218215b741903398807d1a1f7e6b70d9bf683fd4 [file] [log] [blame]
/*
* Copyright (C) 2016 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.
*/
import androidx.room.DatabaseView
import androidx.room.Entity
import androidx.room.ext.GuavaUtilConcurrentTypeNames
import androidx.room.ext.KotlinTypeNames
import androidx.room.ext.LifecyclesTypeNames
import androidx.room.ext.PagingTypeNames
import androidx.room.ext.ReactiveStreamsTypeNames
import androidx.room.ext.RoomGuavaTypeNames
import androidx.room.ext.RoomRxJava2TypeNames
import androidx.room.ext.RoomRxJava3TypeNames
import androidx.room.ext.RxJava2TypeNames
import androidx.room.ext.RxJava3TypeNames
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XVariableElement
import androidx.room.processor.DatabaseViewProcessor
import androidx.room.processor.TableEntityProcessor
import androidx.room.solver.CodeGenScope
import androidx.room.testing.TestInvocation
import androidx.room.testing.TestProcessor
import androidx.room.verifier.DatabaseVerifier
import androidx.room.writer.ClassWriter
import com.google.common.io.Files
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import com.google.testing.compile.CompileTester
import com.google.testing.compile.JavaFileObjects
import com.google.testing.compile.JavaSourcesSubjectFactory
import com.squareup.javapoet.ClassName
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import java.io.File
import java.nio.charset.StandardCharsets.UTF_8
import java.util.Locale
import javax.tools.JavaFileObject
import javax.tools.StandardLocation
import javax.tools.ToolProvider.getSystemJavaCompiler
object COMMON {
val USER by lazy {
loadJavaCode("common/input/User.java", "foo.bar.User")
}
val USER_SUMMARY by lazy {
loadJavaCode("common/input/UserSummary.java", "foo.bar.UserSummary")
}
val USER_TYPE_NAME by lazy {
ClassName.get("foo.bar", "User")
}
val BOOK by lazy {
loadJavaCode("common/input/Book.java", "foo.bar.Book")
}
val NOT_AN_ENTITY by lazy {
loadJavaCode("common/input/NotAnEntity.java", "foo.bar.NotAnEntity")
}
val PARENT by lazy {
loadJavaCode("common/input/Parent.java", "foo.bar.Parent")
}
val CHILD1 by lazy {
loadJavaCode("common/input/Child1.java", "foo.bar.Child1")
}
val CHILD2 by lazy {
loadJavaCode("common/input/Child2.java", "foo.bar.Child2")
}
val INFO by lazy {
loadJavaCode("common/input/Info.java", "foo.bar.Info")
}
val NOT_AN_ENTITY_TYPE_NAME by lazy {
ClassName.get("foo.bar", "NotAnEntity")
}
val MULTI_PKEY_ENTITY by lazy {
loadJavaCode("common/input/MultiPKeyEntity.java", "MultiPKeyEntity")
}
val LIVE_DATA by lazy {
loadJavaCode("common/input/LiveData.java", LifecyclesTypeNames.LIVE_DATA.toString())
}
val COMPUTABLE_LIVE_DATA by lazy {
loadJavaCode("common/input/ComputableLiveData.java",
LifecyclesTypeNames.COMPUTABLE_LIVE_DATA.toString())
}
val PUBLISHER by lazy {
loadJavaCode("common/input/reactivestreams/Publisher.java",
ReactiveStreamsTypeNames.PUBLISHER.toString())
}
val RX2_FLOWABLE by lazy {
loadJavaCode("common/input/rxjava2/Flowable.java",
RxJava2TypeNames.FLOWABLE.toString())
}
val RX2_OBSERVABLE by lazy {
loadJavaCode("common/input/rxjava2/Observable.java",
RxJava2TypeNames.OBSERVABLE.toString())
}
val RX2_SINGLE by lazy {
loadJavaCode("common/input/rxjava2/Single.java",
RxJava2TypeNames.SINGLE.toString())
}
val RX2_MAYBE by lazy {
loadJavaCode("common/input/rxjava2/Maybe.java",
RxJava2TypeNames.MAYBE.toString())
}
val RX2_COMPLETABLE by lazy {
loadJavaCode("common/input/rxjava2/Completable.java",
RxJava2TypeNames.COMPLETABLE.toString())
}
val RX2_ROOM by lazy {
loadJavaCode("common/input/Rx2Room.java", RoomRxJava2TypeNames.RX_ROOM.toString())
}
val RX3_FLOWABLE by lazy {
loadJavaCode("common/input/rxjava3/Flowable.java",
RxJava3TypeNames.FLOWABLE.toString())
}
val RX3_OBSERVABLE by lazy {
loadJavaCode("common/input/rxjava3/Observable.java",
RxJava3TypeNames.OBSERVABLE.toString())
}
val RX3_SINGLE by lazy {
loadJavaCode("common/input/rxjava3/Single.java",
RxJava3TypeNames.SINGLE.toString())
}
val RX3_MAYBE by lazy {
loadJavaCode("common/input/rxjava3/Maybe.java",
RxJava3TypeNames.MAYBE.toString())
}
val RX3_COMPLETABLE by lazy {
loadJavaCode("common/input/rxjava3/Completable.java",
RxJava3TypeNames.COMPLETABLE.toString())
}
val RX3_ROOM by lazy {
loadJavaCode("common/input/Rx3Room.java", RoomRxJava3TypeNames.RX_ROOM.toString())
}
val DATA_SOURCE_FACTORY by lazy {
loadJavaCode("common/input/DataSource.java", "androidx.paging.DataSource")
}
val POSITIONAL_DATA_SOURCE by lazy {
loadJavaCode("common/input/PositionalDataSource.java",
PagingTypeNames.POSITIONAL_DATA_SOURCE.toString())
}
val LISTENABLE_FUTURE by lazy {
loadJavaCode("common/input/guava/ListenableFuture.java",
GuavaUtilConcurrentTypeNames.LISTENABLE_FUTURE.toString())
}
val GUAVA_ROOM by lazy {
loadJavaCode("common/input/GuavaRoom.java",
RoomGuavaTypeNames.GUAVA_ROOM.toString())
}
val CHANNEL by lazy {
loadJavaCode("common/input/coroutines/Channel.java",
KotlinTypeNames.CHANNEL.toString())
}
val SEND_CHANNEL by lazy {
loadJavaCode("common/input/coroutines/SendChannel.java",
KotlinTypeNames.SEND_CHANNEL.toString())
}
val RECEIVE_CHANNEL by lazy {
loadJavaCode("common/input/coroutines/ReceiveChannel.java",
KotlinTypeNames.RECEIVE_CHANNEL.toString())
}
}
fun testCodeGenScope(): CodeGenScope {
return CodeGenScope(mock(ClassWriter::class.java))
}
fun simpleRun(
vararg jfos: JavaFileObject,
classpathFiles: Set<File> = emptySet(),
options: List<String> = emptyList(),
f: (TestInvocation) -> Unit
): CompileTester {
return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
.that(jfos.toList() + JavaFileObjects.forSourceLines("NoOp", "final class NoOp {}"))
.apply {
if (classpathFiles.isNotEmpty()) {
withClasspath(classpathFiles)
}
}
.withCompilerOptions(options)
.processedWith(TestProcessor.builder()
.nextRunHandler {
f(it)
true
}
.forAnnotations("*")
.build())
}
fun loadJavaCode(fileName: String, qName: String): JavaFileObject {
val contents = File("src/test/data/$fileName").readText(Charsets.UTF_8)
return JavaFileObjects.forSourceString(qName, contents)
}
fun createVerifierFromEntitiesAndViews(invocation: TestInvocation): DatabaseVerifier {
return DatabaseVerifier.create(invocation.context, mock(XElement::class.java),
invocation.getEntities(), invocation.getViews())!!
}
fun TestInvocation.getViews(): List<androidx.room.vo.DatabaseView> {
return roundEnv.getElementsAnnotatedWith(DatabaseView::class.java).map {
DatabaseViewProcessor(context, it.asTypeElement()).process()
}
}
fun TestInvocation.getEntities(): List<androidx.room.vo.Entity> {
val entities = roundEnv.getElementsAnnotatedWith(Entity::class.java).map {
TableEntityProcessor(context, it.asTypeElement()).process()
}
return entities
}
/**
* Create mocks of [XElement] and [XType] so that they can be used for instantiating a fake
* [androidx.room.vo.Field].
*/
fun mockElementAndType(): Pair<XVariableElement, XType> {
val element = mock(XVariableElement::class.java)
val type = mock(XType::class.java)
doReturn(type).`when`(element).type
return element to type
}
fun compileLibrarySource(sourceName: String, code: String): Set<File> {
val sourceCode = """
package test.library;
import androidx.room.*;
$code
"""
return compileLibrarySources(JavaFileObjects.forSourceString(sourceName, sourceCode))
}
/**
* Compiles an array of sources and returns a set of files to be used as classpath pointing
* to the .class files generated by the compilation. This method is useful for creating an
* environment where the annotation processor has to process compiled classes from a library.
*/
fun compileLibrarySources(vararg sources: JavaFileObject): Set<File> {
val lib = Files.createTempDir()
val compiler = getSystemJavaCompiler()
val fileManager = compiler.getStandardFileManager(null, Locale.getDefault(), UTF_8)
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(lib))
val task = compiler.getTask(null, fileManager, null, emptyList(), null, listOf(*sources))
assertThat(task.call()).isTrue()
return getSystemClasspathFiles() + lib
}
private fun getSystemClasspathFiles(): Set<File> {
val pathSeparator = System.getProperty("path.separator")!!
return System.getProperty("java.class.path")!!.split(pathSeparator).map { File(it) }.toSet()
}
fun String.toJFO(qName: String): JavaFileObject = JavaFileObjects.forSourceLines(qName, this)