RoomProcessor and Integration test
This CL puts together the very first working version :').
Bug: 32342709
Test: SimpleEntityReadWriteTest.java, DatabaseWriterTest.kt
Change-Id: I7aa449f92b40aac5b682dccc17d276a4e349115b
diff --git a/flatfoot-common/init.gradle b/flatfoot-common/init.gradle
index 5deb735..7de382c 100644
--- a/flatfoot-common/init.gradle
+++ b/flatfoot-common/init.gradle
@@ -37,6 +37,7 @@
ext.compile_sdk_version = 24
ext.build_tools_version = "24.0.0"
ext.intellij_annotation = "12.0"
+ext.espresso_version = "2.2.2"
ext.release_version = "1.0-SNAPSHOT"
ext.enablePublicRepos = System.getenv("ALLOW_PUBLIC_REPOS")
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
new file mode 100644
index 0000000..256d0d9
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package com.android.support.room
+
+import com.android.support.room.processor.Context
+import com.android.support.room.processor.DaoProcessor
+import com.android.support.room.processor.DatabaseProcessor
+import com.android.support.room.processor.EntityProcessor
+import com.android.support.room.writer.DaoWriter
+import com.android.support.room.writer.DatabaseWriter
+import com.android.support.room.writer.EntityCursorConverterWriter
+import com.google.auto.common.BasicAnnotationProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.collect.SetMultimap
+import javax.annotation.processing.SupportedSourceVersion
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Element
+
+/**
+ * The annotation processor for Room.
+ */
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+class RoomProcessor : BasicAnnotationProcessor() {
+ override fun initSteps(): MutableIterable<ProcessingStep>? {
+ val context = Context(processingEnv)
+ return arrayListOf(EntityProcessingStep(context),
+ DaoProcessingStep(context),
+ DatabaseProcessingStep(context))
+ }
+
+ class DaoProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+ override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+ : MutableSet<Element> {
+ elementsByAnnotation[Dao::class.java]
+ ?.map {
+ DaoProcessor(context).parse(MoreElements.asType(it))
+ }
+ ?.forEach {
+ DaoWriter(it).write(context.processingEnv)
+ }
+ return mutableSetOf()
+ }
+
+ override fun annotations(): MutableSet<out Class<out Annotation>> {
+ return mutableSetOf(Dao::class.java)
+ }
+ }
+
+ class DatabaseProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+ override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+ : MutableSet<Element> {
+ elementsByAnnotation[Database::class.java]
+ ?.map {
+ DatabaseProcessor(context).parse(MoreElements.asType(it))
+ }
+ ?.forEach {
+ DatabaseWriter(it).write(context.processingEnv)
+ }
+ return mutableSetOf()
+ }
+
+ override fun annotations(): MutableSet<out Class<out Annotation>> {
+ return mutableSetOf(Database::class.java)
+ }
+ }
+
+ class EntityProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+ override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+ : MutableSet<Element> {
+ elementsByAnnotation[Entity::class.java]
+ ?.map {
+ EntityProcessor(context).parse(MoreElements.asType(it))
+ }
+ ?.forEach {
+ EntityCursorConverterWriter(it).write(context.processingEnv)
+ }
+ return mutableSetOf()
+ }
+
+ override fun annotations(): MutableSet<out Class<out Annotation>> {
+ return mutableSetOf(Entity::class.java)
+ }
+ }
+
+ abstract class ContextBoundProcessingStep(val context: Context) : ProcessingStep
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
index 5ae73d8..5f2a2b3 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
@@ -22,8 +22,7 @@
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
-data class Context(val roundEnv: RoundEnvironment,
- val processingEnv: ProcessingEnvironment) {
+data class Context(val processingEnv: ProcessingEnvironment) {
val logger = RLog(processingEnv)
val checker = Checks(logger)
val COMMON_TYPES = CommonTypes(processingEnv)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
index c6be9c7..cc1af11 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
@@ -28,4 +28,8 @@
val implClassName by lazy {
"${typeName.simpleName()}_Impl"
}
+
+ val implTypeName by lazy {
+ ClassName.get(typeName.packageName(), implClassName)
+ }
}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
index b3b82cf..989fec4 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
@@ -25,7 +25,7 @@
val primaryKey: Boolean, val columnName: String = name) {
lateinit var getter: FieldGetter
lateinit var setter: FieldSetter
- val typeName by lazy { type.typeName() }
+ val typeName: TypeName by lazy { type.typeName() }
/**
* List of names that include variations.
* e.g. if it is mUser, user is added to the list
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
index 3544b87..738ca2c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
@@ -50,7 +50,7 @@
}
override fun createTypeSpec(): TypeSpec {
- val builder = TypeSpec.classBuilder(dao.implClassName)
+ val builder = TypeSpec.classBuilder(dao.implTypeName)
val scope = CodeGenScope()
val insertionMethods = groupAndCreateInsertionMethods(scope)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
index 9ae0510..69ca7f2 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
@@ -20,13 +20,21 @@
import com.android.support.room.ext.N
import com.android.support.room.ext.RoomTypeNames
import com.android.support.room.ext.SupportDbTypeNames
+import com.android.support.room.ext.T
import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.vo.DaoMethod
import com.android.support.room.vo.Database
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterSpec
import com.squareup.javapoet.TypeSpec
+import stripNonJava
import javax.lang.model.element.Modifier
+import javax.lang.model.element.Modifier.PRIVATE
import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.element.Modifier.VOLATILE
/**
* Writes implementation of classes that were annotated with @Database.
@@ -40,9 +48,43 @@
addMethod(createConstructor())
addMethod(createCreateOpenHelper())
}
+ addDaoImpls(builder)
return builder.build()
}
+ private fun addDaoImpls(builder: TypeSpec.Builder) {
+ val scope = CodeGenScope()
+ builder.apply {
+ database.daoMethods.forEach { method ->
+ val name = method.dao.typeName.simpleName().decapitalize().stripNonJava()
+ var fieldName = scope.getTmpVar("_$name")
+ val field = FieldSpec.builder(method.dao.typeName, fieldName,
+ PRIVATE, VOLATILE).build()
+ addField(field)
+ addMethod(createDaoGetter(field, method))
+ }
+ }
+ }
+
+ private fun createDaoGetter(field: FieldSpec, method: DaoMethod) : MethodSpec {
+ return MethodSpec.overriding(MoreElements.asExecutable(method.element)).apply {
+ beginControlFlow("if ($N != null)", field).apply {
+ addStatement("return $N", field)
+ }
+ nextControlFlow("else").apply {
+ beginControlFlow("synchronized(this)").apply {
+ beginControlFlow("if($N == null)", field).apply {
+ addStatement("$N = new $T(this)", field, method.dao.implTypeName)
+ }
+ endControlFlow()
+ addStatement("return $N", field)
+ }
+ endControlFlow()
+ }
+ endControlFlow()
+ }.build()
+ }
+
private fun createConstructor(): MethodSpec {
return MethodSpec.constructorBuilder().apply {
addModifiers(Modifier.PUBLIC)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt
index 15d0a16..f7d1c4c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt
@@ -70,7 +70,7 @@
} else {
"$valueParam.${field.getter.name}()"
}
- adapter.bindToStmt(stmtParam, "$index", varName, bindScope)
+ adapter.bindToStmt(stmtParam, "${index + 1}", varName, bindScope)
}
}
addCode(bindScope.builder().build())
diff --git a/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..db92d17
--- /dev/null
+++ b/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.android.support.room.RoomProcessor
\ No newline at end of file
diff --git a/room/compiler/src/test/data/daoWriter/output/WriterDao.java b/room/compiler/src/test/data/daoWriter/output/WriterDao.java
index 6ca04ca..34f2729 100644
--- a/room/compiler/src/test/data/daoWriter/output/WriterDao.java
+++ b/room/compiler/src/test/data/daoWriter/output/WriterDao.java
@@ -42,18 +42,18 @@
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
- stmt.bindLong(0, value.uid);
+ stmt.bindLong(1, value.uid);
if (value.name == null) {
- stmt.bindNull(1);
- } else {
- stmt.bindString(1, value.name);
- }
- if (value.getLastName() == null) {
stmt.bindNull(2);
} else {
- stmt.bindString(2, value.getLastName());
+ stmt.bindString(2, value.name);
}
- stmt.bindLong(3, value.age);
+ if (value.getLastName() == null) {
+ stmt.bindNull(3);
+ } else {
+ stmt.bindString(3, value.getLastName());
+ }
+ stmt.bindLong(4, value.age);
}
};
this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
@@ -65,18 +65,18 @@
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
- stmt.bindLong(0, value.uid);
+ stmt.bindLong(1, value.uid);
if (value.name == null) {
- stmt.bindNull(1);
- } else {
- stmt.bindString(1, value.name);
- }
- if (value.getLastName() == null) {
stmt.bindNull(2);
} else {
- stmt.bindString(2, value.getLastName());
+ stmt.bindString(2, value.name);
}
- stmt.bindLong(3, value.age);
+ if (value.getLastName() == null) {
+ stmt.bindNull(3);
+ } else {
+ stmt.bindString(3, value.getLastName());
+ }
+ stmt.bindLong(4, value.age);
}
};
}
diff --git a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
index 0a582b7..c71f3ae 100644
--- a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
@@ -22,4 +22,5 @@
public ComplexDatabase(DatabaseConfiguration configuration) {
super(configuration);
}
+ abstract ComplexDao getComplexDao();
}
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
index 16652a5..19060f8 100644
--- a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -5,8 +5,11 @@
import com.android.support.db.SupportSQLiteOpenHelper.Callback;
import com.android.support.db.SupportSQLiteOpenHelper.Configuration;
import com.android.support.room.DatabaseConfiguration;
+import java.lang.Override;
public class ComplexDatabase_Impl extends ComplexDatabase {
+ private volatile ComplexDao _complexDao;
+
public ComplexDatabase_Impl(DatabaseConfiguration configuration) {
super(configuration);
}
@@ -36,4 +39,18 @@
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
+
+ @Override
+ ComplexDao getComplexDao() {
+ if (_complexDao != null) {
+ return _complexDao;
+ } else {
+ synchronized(this) {
+ if(_complexDao == null) {
+ _complexDao = new ComplexDao_Impl(this);
+ }
+ return _complexDao;
+ }
+ }
+ }
}
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt
index 48f880b..6682e36 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -80,7 +80,7 @@
@Test
fun bind() {
simpleRun { invocation ->
- val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val adapter = TypeAdapterStore(Context(invocation.processingEnv))
.findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv))!!
adapter.bindToStmt("st", "6", "inp", scope)
assertThat(scope.generate().trim(), `is`(bindCode))
@@ -94,7 +94,7 @@
return // no-op for those
}
simpleRun { invocation ->
- val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val adapter = TypeAdapterStore(Context(invocation.processingEnv))
.findColumnTypeAdapter(input.getBoxedTypeMirror(invocation.processingEnv))!!
adapter.bindToStmt("st", "6", "inp", scope)
assertThat(scope.generate().trim(), `is`(
@@ -130,7 +130,7 @@
@Test
fun read() {
simpleRun { invocation ->
- val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val adapter = TypeAdapterStore(Context(invocation.processingEnv))
.findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv))!!
adapter.readFromCursor("out", "crs", "9", scope)
assertThat(scope.generate().trim(), `is`(cursorCode))
@@ -144,7 +144,7 @@
return // no-op for those
}
simpleRun { invocation ->
- val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val adapter = TypeAdapterStore(Context(invocation.processingEnv))
.findColumnTypeAdapter(input.getBoxedTypeMirror(invocation.processingEnv))!!
adapter.readFromCursor("out", "crs", "9", scope)
assertThat(scope.generate().trim(), `is`(
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
index e3f5b52..43ed64d 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
@@ -111,7 +111,7 @@
fun forward() {
simpleRun { invocation ->
val stringTypeMirror = invocation.context.COMMON_TYPES.STRING
- val converter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val converter = TypeAdapterStore(Context(invocation.processingEnv))
.findTypeConverter(input.getTypeMirror(invocation.processingEnv),
stringTypeMirror)!!
converter.convertForward("inp", "out", scope)
@@ -125,7 +125,7 @@
fun backward() {
simpleRun { invocation ->
val stringTypeMirror = invocation.context.COMMON_TYPES.STRING
- val converter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val converter = TypeAdapterStore(Context(invocation.processingEnv))
.findTypeConverter(input.getTypeMirror(invocation.processingEnv),
stringTypeMirror)!!
converter.convertBackward("inp", "out", scope)
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt
index b62025d..f47483f 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt
@@ -46,7 +46,7 @@
@Test
fun testDirect() {
singleRun { invocation ->
- val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val store = TypeAdapterStore(Context(invocation.processingEnv))
val primitiveType = invocation.processingEnv.typeUtils.getPrimitiveType(TypeKind.INT)
val adapter = store.findColumnTypeAdapter(primitiveType)
assertThat(adapter, notNullValue())
@@ -56,7 +56,7 @@
@Test
fun testVia1TypeAdapter() {
singleRun { invocation ->
- val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val store = TypeAdapterStore(Context(invocation.processingEnv))
val booleanType = invocation.processingEnv.typeUtils
.getPrimitiveType(TypeKind.BOOLEAN)
val adapter = store.findColumnTypeAdapter(booleanType)
@@ -83,7 +83,7 @@
@Test
fun testVia2TypeAdapters() {
singleRun { invocation ->
- val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv),
+ val store = TypeAdapterStore(Context(invocation.processingEnv),
PointTypeConverter(invocation.processingEnv))
val pointType = invocation.processingEnv.elementUtils
.getTypeElement("foo.bar.Point").asType()
@@ -116,7 +116,7 @@
@Test
fun testIntList() {
singleRun { invocation ->
- val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+ val store = TypeAdapterStore(Context(invocation.processingEnv))
val intType = invocation.processingEnv.elementUtils
.getTypeElement(Integer::class.java.canonicalName)
.asType()
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt b/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt
index c642a9d..81481ca 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt
@@ -24,5 +24,5 @@
data class TestInvocation(val processingEnv: ProcessingEnvironment,
val annotations: MutableSet<out TypeElement>,
val roundEnv: RoundEnvironment) {
- val context = Context(roundEnv, processingEnv)
+ val context = Context(processingEnv)
}
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
index d3ca260..c2fce0f 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
@@ -16,9 +16,8 @@
package com.android.support.room.writer
-import com.android.support.room.processor.DatabaseProcessor
-import com.android.support.room.testing.TestProcessor
-import com.google.auto.common.MoreElements
+import COMMON
+import com.android.support.room.RoomProcessor
import com.google.common.truth.Truth
import com.google.testing.compile.CompileTester
import com.google.testing.compile.JavaSourcesSubjectFactory
@@ -35,7 +34,9 @@
fun simpleDb() {
singleDb(
loadJavaCode("databasewriter/input/ComplexDatabase.java",
- "foo.bar.ComplexDatabase")
+ "foo.bar.ComplexDatabase"),
+ loadJavaCode("daoWriter/input/ComplexDao.java",
+ "foo.bar.ComplexDao")
).compilesWithoutError().and().generatesSources(
loadJavaCode("databasewriter/output/ComplexDatabase.java",
"foo.bar.ComplexDatabase_Impl")
@@ -45,18 +46,6 @@
private fun singleDb(vararg jfo : JavaFileObject): CompileTester {
return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
.that(jfo.toList() + COMMON.USER)
- .processedWith(TestProcessor.builder()
- .forAnnotations(com.android.support.room.Database::class)
- .nextRunHandler { invocation ->
- val dao = invocation.roundEnv
- .getElementsAnnotatedWith(
- com.android.support.room.Database::class.java)
- .first()
- val processor = DatabaseProcessor(invocation.context)
- val db = processor.parse(MoreElements.asType(dao))
- DatabaseWriter(db).write(invocation.processingEnv)
- true
- }
- .build())
+ .processedWith(RoomProcessor())
}
-}
\ No newline at end of file
+}
diff --git a/room/integration-tests/testapp/.gitignore b/room/integration-tests/testapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/room/integration-tests/testapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..b038b23
--- /dev/null
+++ b/room/integration-tests/testapp/build.gradle
@@ -0,0 +1,79 @@
+/*
+ * 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 com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+ compileSdkVersion compile_sdk_version
+ buildToolsVersion build_tools_version
+
+ defaultConfig {
+ minSdkVersion min_sdk_version
+ targetSdkVersion target_sdk_version
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+}
+
+dependencies {
+ compile project(":common")
+ compile project(":db")
+ compile project(":db-impl")
+ compile "com.android.support:appcompat-v7:$support_lib_version"
+
+ androidTestAnnotationProcessor project(":compiler")
+
+ androidTestCompile("com.android.support.test.espresso:espresso-core:$espresso_version", {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ testCompile "junit:junit:$junit_version"
+ testCompile "org.mockito:mockito-core:$mockito_version"
+ compile project(path: ':runtime')
+}
+
+uploadArchives {
+ repositories {
+ mavenDeployer {
+ repository(url: rootProject.ext.localMavenRepo)
+ pom.artifactId = "runtime"
+ }
+ }
+}
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+ def name = variant.buildType.name
+ def suffix = name.capitalize()
+ def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+ dependsOn variant.javaCompile
+ from variant.javaCompile.destinationDir
+ destinationDir new File(project.buildDir, "libJar")
+ }
+}
+
+tasks['check'].dependsOn(tasks['connectedCheck'])
diff --git a/room/integration-tests/testapp/proguard-rules.pro b/room/integration-tests/testapp/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/room/integration-tests/testapp/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
new file mode 100644
index 0000000..58b86ba
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package com.android.support.room.integration.testapp;
+
+import com.android.support.room.Database;
+import com.android.support.room.DatabaseConfiguration;
+import com.android.support.room.RoomDatabase;
+import com.android.support.room.integration.testapp.dao.UserDao;
+import com.android.support.room.integration.testapp.vo.User;
+
+@Database(entities = User.class)
+public abstract class TestDatabase extends RoomDatabase {
+ /**
+ * Creates a RoomDatabase with the given configuration.
+ *
+ * @param configuration The configuration to setup the database.
+ */
+ @SuppressWarnings("WeakerAccess")
+ public TestDatabase(DatabaseConfiguration configuration) {
+ super(configuration);
+ }
+
+ public abstract UserDao getUserDao();
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/dao/UserDao.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/dao/UserDao.java
new file mode 100644
index 0000000..60de420
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/dao/UserDao.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package com.android.support.room.integration.testapp.dao;
+
+import com.android.support.room.Dao;
+import com.android.support.room.Insert;
+import com.android.support.room.Query;
+import com.android.support.room.integration.testapp.vo.User;
+
+import java.util.List;
+
+@Dao
+public interface UserDao {
+ @Query("select * from user where mName like :name")
+ List<User> findUsersByName(String name);
+
+ @Query("select * from user where mId = ?")
+ User load(int id);
+
+ @Insert
+ void insert(User user);
+
+ @Insert(onConflict = Insert.REPLACE)
+ void insertOrReplace(User user);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
new file mode 100644
index 0000000..b69050c
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package com.android.support.room.integration.testapp.test;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.support.room.DatabaseConfiguration;
+import com.android.support.room.integration.testapp.TestDatabase;
+import com.android.support.room.integration.testapp.TestDatabase_Impl;
+import com.android.support.room.integration.testapp.vo.User;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SimpleEntityReadWriteTest {
+ TestDatabase mDb;
+ @Before
+ public void createDb() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ DatabaseConfiguration configuration = new DatabaseConfiguration.Builder(context).build();
+ mDb = new TestDatabase_Impl(configuration);
+ }
+ @Test
+ public void writeUserAndReadInList() throws Exception {
+ User user = new User();
+ user.setId(3);
+ user.setAge(99);
+ user.setName("george");
+ user.setLastName("kloony");
+ mDb.getUserDao().insert(user);
+ List<User> byName = mDb.getUserDao().findUsersByName("george");
+ Assert.assertEquals(user, byName.get(0));
+ }
+
+ @Test
+ public void throwExceptionOnConflict() {
+ User user = new User();
+ user.setId(3);
+ user.setAge(99);
+ user.setName("george");
+ user.setLastName("kloony");
+ mDb.getUserDao().insert(user);
+
+ User user2 = new User();
+ user2.setId(3);
+ user2.setAge(22);
+ user2.setName("michael");
+ user2.setLastName("jordo");
+ try {
+ mDb.getUserDao().insert(user2);
+ throw new AssertionFailedError("didn't throw in conflicting insertion");
+ } catch (SQLiteException ignored) {
+ }
+ }
+
+ @Test
+ public void replaceOnConflict() {
+ User user = new User();
+ user.setId(3);
+ user.setAge(99);
+ user.setName("george");
+ user.setLastName("kloony");
+ mDb.getUserDao().insert(user);
+
+ User user2 = new User();
+ user2.setId(3);
+ user2.setAge(22);
+ user2.setName("michael");
+ user2.setLastName("jordo");
+ mDb.getUserDao().insertOrReplace(user2);
+ Assert.assertEquals(user2, mDb.getUserDao().load(3));
+ }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/vo/User.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/vo/User.java
new file mode 100644
index 0000000..2ccedb0
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/vo/User.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+package com.android.support.room.integration.testapp.vo;
+
+import com.android.support.room.Entity;
+import com.android.support.room.PrimaryKey;
+
+@Entity
+public class User {
+ @PrimaryKey
+ private int mId;
+ private String mName;
+ private String mLastName;
+ private int mAge;
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ this.mId = id;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setName(String name) {
+ this.mName = name;
+ }
+
+ public String getLastName() {
+ return mLastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.mLastName = lastName;
+ }
+
+ public int getAge() {
+ return mAge;
+ }
+
+ public void setAge(int age) {
+ this.mAge = age;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ User user = (User) o;
+
+ if (mId != user.mId) return false;
+ if (mAge != user.mAge) return false;
+ if (mName != null ? !mName.equals(user.mName) : user.mName != null) return false;
+ return mLastName != null ? mLastName.equals(user.mLastName) : user.mLastName == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mId;
+ result = 31 * result + (mName != null ? mName.hashCode() : 0);
+ result = 31 * result + (mLastName != null ? mLastName.hashCode() : 0);
+ result = 31 * result + mAge;
+ return result;
+ }
+}
diff --git a/room/integration-tests/testapp/src/main/AndroidManifest.xml b/room/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..eeacd10
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.support.room.integration.testapp">
+ <application android:allowBackup="true"
+ android:supportsRtl="true"/>
+</manifest>
diff --git a/room/runtime/src/main/java/com/android/support/room/Room.java b/room/runtime/src/main/java/com/android/support/room/Room.java
index 5971349..a7425b1 100644
--- a/room/runtime/src/main/java/com/android/support/room/Room.java
+++ b/room/runtime/src/main/java/com/android/support/room/Room.java
@@ -18,8 +18,6 @@
import android.support.annotation.Nullable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
@@ -58,25 +56,16 @@
final Class<? extends CursorConverter<T>> aClass =
(Class<? extends CursorConverter<T>>) Class.forName(
fullPackage + "." + converterName);
- Constructor<? extends CursorConverter<T>> constructor =
- aClass.getDeclaredConstructor(klass);
- constructor.setAccessible(true);
- return constructor.newInstance();
+ return aClass.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("cannot find cursor converter for "
+ klass.getCanonicalName());
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("CursorConverters must have a no-arg constructor:"
- + klass.getCanonicalName());
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access cursor converter constructor"
+ klass.getCanonicalName());
} catch (InstantiationException e) {
throw new RuntimeException("Failed to create an instance of the cursor converter"
+ klass.getCanonicalName());
- } catch (InvocationTargetException e) {
- throw new RuntimeException("Failed to create an instance of the cursor converter"
- + klass.getCanonicalName());
}
}
diff --git a/room/settings.gradle b/room/settings.gradle
index c1175dc..e5957e9 100644
--- a/room/settings.gradle
+++ b/room/settings.gradle
@@ -18,4 +18,5 @@
include 'compiler'
include 'db'
include ':db-impl'
+include ':integration-tests:testapp'