| /* |
| * Copyright (C) 2017 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.processor |
| |
| import COMMON |
| import androidx.room.Embedded |
| import androidx.room.parser.SQLTypeAffinity |
| import androidx.room.compiler.processing.XVariableElement |
| import androidx.room.processor.ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD |
| import androidx.room.processor.ProcessorErrors.CANNOT_FIND_TYPE |
| import androidx.room.processor.ProcessorErrors.POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME |
| import androidx.room.processor.ProcessorErrors.junctionColumnWithoutIndex |
| import androidx.room.processor.ProcessorErrors.relationCannotFindEntityField |
| import androidx.room.processor.ProcessorErrors.relationCannotFindJunctionEntityField |
| import androidx.room.processor.ProcessorErrors.relationCannotFindJunctionParentField |
| import androidx.room.processor.ProcessorErrors.relationCannotFindParentEntityField |
| import androidx.room.testing.TestInvocation |
| import androidx.room.vo.CallType |
| import androidx.room.vo.Constructor |
| import androidx.room.vo.EmbeddedField |
| import androidx.room.vo.Field |
| import androidx.room.vo.Pojo |
| import androidx.room.vo.RelationCollector |
| import com.google.testing.compile.CompileTester |
| import com.squareup.javapoet.ClassName |
| import com.squareup.javapoet.TypeName |
| import org.hamcrest.CoreMatchers.`is` |
| import org.hamcrest.CoreMatchers.instanceOf |
| import org.hamcrest.CoreMatchers.not |
| import org.hamcrest.CoreMatchers.notNullValue |
| import org.hamcrest.CoreMatchers.nullValue |
| import org.hamcrest.CoreMatchers.sameInstance |
| import org.hamcrest.MatcherAssert.assertThat |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.junit.runners.JUnit4 |
| import org.mockito.Mockito.doReturn |
| import org.mockito.Mockito.mock |
| import simpleRun |
| import toJFO |
| import java.io.File |
| import javax.tools.JavaFileObject |
| |
| /** |
| * Some of the functionality is tested via TableEntityProcessor. |
| */ |
| @RunWith(JUnit4::class) |
| class PojoProcessorTest { |
| |
| companion object { |
| val MY_POJO: ClassName = ClassName.get("foo.bar", "MyPojo") |
| val HEADER = """ |
| package foo.bar; |
| import androidx.room.*; |
| import java.util.*; |
| public class MyPojo { |
| """ |
| val FOOTER = "\n}" |
| } |
| |
| @Test |
| fun inheritedPrivate() { |
| val parent = """ |
| package foo.bar.x; |
| import androidx.room.*; |
| public class BaseClass { |
| private String baseField; |
| public String getBaseField(){ return baseField; } |
| public void setBaseField(String baseField){ } |
| } |
| """ |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} extends foo.bar.x.BaseClass { |
| public String myField; |
| } |
| """.toJFO(MY_POJO.toString()), |
| parent.toJFO("foo.bar.x.BaseClass")) { invocation -> |
| val pojo = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| assertThat(pojo.fields.find { it.name == "myField" }, notNullValue()) |
| assertThat(pojo.fields.find { it.name == "baseField" }, notNullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun transient_ignore() { |
| singleRun(""" |
| transient int foo; |
| int bar; |
| """) { pojo -> |
| assertThat(pojo.fields.size, `is`(1)) |
| assertThat(pojo.fields[0].name, `is`("bar")) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun transient_withColumnInfo() { |
| singleRun(""" |
| @ColumnInfo |
| transient int foo; |
| int bar; |
| """) { pojo -> |
| assertThat(pojo.fields.map { it.name }.toSet(), `is`(setOf("bar", "foo"))) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun transient_embedded() { |
| singleRun(""" |
| @Embedded |
| transient Foo foo; |
| int bar; |
| static class Foo { |
| int x; |
| } |
| """) { pojo -> |
| assertThat(pojo.fields.map { it.name }.toSet(), `is`(setOf("x", "bar"))) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun transient_insideEmbedded() { |
| singleRun(""" |
| @Embedded |
| Foo foo; |
| int bar; |
| static class Foo { |
| transient int x; |
| int y; |
| } |
| """) { pojo -> |
| assertThat(pojo.fields.map { it.name }.toSet(), `is`(setOf("bar", "y"))) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun transient_relation() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public transient List<User> user; |
| """, COMMON.USER |
| ) { pojo -> |
| assertThat(pojo.relations.size, `is`(1)) |
| assertThat(pojo.relations.first().entityField.name, `is`("uid")) |
| assertThat(pojo.relations.first().parentField.name, `is`("id")) |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun embedded() { |
| singleRun( |
| """ |
| int id; |
| @Embedded |
| Point myPoint; |
| static class Point { |
| int x; |
| int y; |
| } |
| """ |
| ) { pojo -> |
| assertThat(pojo.fields.size, `is`(3)) |
| assertThat(pojo.fields[1].name, `is`("x")) |
| assertThat(pojo.fields[2].name, `is`("y")) |
| assertThat(pojo.fields[0].parent, nullValue()) |
| assertThat(pojo.fields[1].parent, notNullValue()) |
| assertThat(pojo.fields[2].parent, notNullValue()) |
| val parent = pojo.fields[2].parent!! |
| assertThat(parent.prefix, `is`("")) |
| assertThat(parent.field.name, `is`("myPoint")) |
| assertThat(parent.pojo.typeName, |
| `is`(ClassName.get("foo.bar.MyPojo", "Point") as TypeName)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun embeddedWithPrefix() { |
| singleRun( |
| """ |
| int id; |
| @Embedded(prefix = "foo") |
| Point myPoint; |
| static class Point { |
| int x; |
| @ColumnInfo(name = "y2") |
| int y; |
| } |
| """ |
| ) { pojo -> |
| assertThat(pojo.fields.size, `is`(3)) |
| assertThat(pojo.fields[1].name, `is`("x")) |
| assertThat(pojo.fields[2].name, `is`("y")) |
| assertThat(pojo.fields[1].columnName, `is`("foox")) |
| assertThat(pojo.fields[2].columnName, `is`("fooy2")) |
| val parent = pojo.fields[2].parent!! |
| assertThat(parent.prefix, `is`("foo")) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun nestedEmbedded() { |
| singleRun( |
| """ |
| int id; |
| @Embedded(prefix = "foo") |
| Point myPoint; |
| static class Point { |
| int x; |
| @ColumnInfo(name = "y2") |
| int y; |
| @Embedded(prefix = "bar") |
| Coordinate coordinate; |
| } |
| static class Coordinate { |
| double lat; |
| double lng; |
| @Ignore |
| String ignored; |
| } |
| """ |
| ) { pojo -> |
| assertThat(pojo.fields.size, `is`(5)) |
| assertThat(pojo.fields.map { it.columnName }, `is`( |
| listOf("id", "foox", "fooy2", "foobarlat", "foobarlng"))) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun embedded_generic() { |
| val point = """ |
| package foo.bar; |
| public class Point { |
| public int x; |
| public int y; |
| } |
| """.toJFO("foo.bar.Point") |
| val base = """ |
| package foo.bar; |
| import ${Embedded::class.java.canonicalName}; |
| public class BaseClass<T> { |
| @Embedded |
| public T genericField; |
| } |
| """.toJFO("foo.bar.BaseClass") |
| singleRunFullClass( |
| """ |
| package foo.bar; |
| public class MyPojo extends BaseClass<Point> { |
| public int normalField; |
| } |
| """, |
| point, base |
| ) { pojo, _ -> |
| assertThat(pojo.fields.size, `is`(3)) |
| assertThat(pojo.fields.map { it.columnName }.toSet(), `is`( |
| setOf("x", "y", "normalField"))) |
| val pointField = pojo.embeddedFields.first { it.field.name == "genericField" } |
| assertThat(pointField.pojo.typeName, |
| `is`(ClassName.get("foo.bar", "Point") as TypeName)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun duplicateColumnNames() { |
| singleRun( |
| """ |
| int id; |
| @ColumnInfo(name = "id") |
| int another; |
| """ |
| ) { _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.pojoDuplicateFieldNames("id", listOf("id", "another")) |
| ).and().withErrorContaining( |
| POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME |
| ).and().withErrorCount(3) |
| } |
| |
| @Test |
| fun duplicateColumnNamesFromEmbedded() { |
| singleRun( |
| """ |
| int id; |
| @Embedded |
| Foo foo; |
| static class Foo { |
| @ColumnInfo(name = "id") |
| int x; |
| } |
| """ |
| ) { _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.pojoDuplicateFieldNames("id", listOf("id", "foo > x")) |
| ).and().withErrorContaining( |
| POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME |
| ).and().withErrorCount(3) |
| } |
| |
| @Test |
| fun dropSubPrimaryKeyNoWarningForPojo() { |
| singleRun( |
| """ |
| @PrimaryKey |
| int id; |
| @Embedded |
| Point myPoint; |
| static class Point { |
| @PrimaryKey |
| int x; |
| int y; |
| } |
| """ |
| ) { _ -> |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_view() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<UserSummary> user; |
| """, COMMON.USER_SUMMARY |
| ) { _ -> |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun relation_notCollection() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public User user; |
| """, COMMON.USER |
| ) { _ -> |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun relation_columnInfo() { |
| singleRun( |
| """ |
| int id; |
| @ColumnInfo |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<User> user; |
| """, COMMON.USER |
| ) { _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION) |
| } |
| |
| @Test |
| fun relation_notEntity() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<NotAnEntity> user; |
| """, COMMON.NOT_AN_ENTITY |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(ProcessorErrors.NOT_ENTITY_OR_VIEW) |
| } |
| |
| @Test |
| fun relation_missingParent() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "idk", entityColumn = "uid") |
| public List<User> user; |
| """, COMMON.USER |
| ) { _ -> |
| }.failsToCompile().withErrorContaining( |
| relationCannotFindParentEntityField("foo.bar.MyPojo", "idk", listOf("id")) |
| ) |
| } |
| |
| @Test |
| fun relation_missingEntityField() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "idk") |
| public List<User> user; |
| """, COMMON.USER |
| ) { _ -> |
| }.failsToCompile().withErrorContaining( |
| relationCannotFindEntityField("foo.bar.User", "idk", |
| listOf("uid", "name", "lastName", "age")) |
| ) |
| } |
| |
| @Test |
| fun relation_missingType() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<User> user; |
| """ |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(CANNOT_FIND_TYPE) |
| } |
| |
| @Test |
| fun relation_nestedField() { |
| singleRun( |
| """ |
| static class Nested { |
| @ColumnInfo(name = "foo") |
| public int id; |
| } |
| @Embedded |
| Nested nested; |
| @Relation(parentColumn = "foo", entityColumn = "uid") |
| public List<User> user; |
| """, COMMON.USER |
| ) { pojo -> |
| assertThat(pojo.relations.first().parentField.columnName, `is`("foo")) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun relation_nestedRelation() { |
| singleRun( |
| """ |
| static class UserWithNested { |
| @Embedded |
| public User user; |
| @Relation(parentColumn = "uid", entityColumn = "uid") |
| public List<User> selfs; |
| } |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid", entity = User.class) |
| public List<UserWithNested> user; |
| """, COMMON.USER |
| ) { pojo, _ -> |
| assertThat(pojo.relations.first().parentField.name, `is`("id")) |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_affinityMismatch() { |
| singleRun( |
| """ |
| String id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<User> user; |
| """, COMMON.USER |
| ) { pojo, invocation -> |
| // trigger assignment evaluation |
| RelationCollector.createCollectors(invocation.context, pojo.relations) |
| assertThat(pojo.relations.size, `is`(1)) |
| assertThat(pojo.relations.first().entityField.name, `is`("uid")) |
| assertThat(pojo.relations.first().parentField.name, `is`("id")) |
| }.compilesWithoutError().withWarningContaining( |
| ProcessorErrors.relationAffinityMismatch( |
| parentAffinity = SQLTypeAffinity.TEXT, |
| childAffinity = SQLTypeAffinity.INTEGER, |
| parentColumn = "id", |
| childColumn = "uid") |
| ) |
| } |
| |
| @Test |
| fun relation_simple() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<User> user; |
| """, COMMON.USER |
| ) { pojo -> |
| assertThat(pojo.relations.size, `is`(1)) |
| assertThat(pojo.relations.first().entityField.name, `is`("uid")) |
| assertThat(pojo.relations.first().parentField.name, `is`("id")) |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_badProjection() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid", projection={"i_dont_exist"}) |
| public List<User> user; |
| """, COMMON.USER |
| ) { _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.relationBadProject("foo.bar.User", listOf("i_dont_exist"), |
| listOf("uid", "name", "lastName", "ageColumn")) |
| ) |
| } |
| |
| @Test |
| fun relation_badReturnTypeInGetter() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| private List<User> user; |
| public void setUser(List<User> user){ this.user = user;} |
| public User getUser(){return null;} |
| """, COMMON.USER |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(CANNOT_FIND_GETTER_FOR_FIELD) |
| } |
| |
| @Test |
| fun relation_primitiveList() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid", projection={"uid"}, |
| entity = User.class) |
| public List<Integer> userIds; |
| """, COMMON.USER |
| ) { pojo -> |
| assertThat(pojo.relations.size, `is`(1)) |
| val rel = pojo.relations.first() |
| assertThat(rel.projection, `is`(listOf("uid"))) |
| assertThat(rel.entity.typeName, `is`(COMMON.USER_TYPE_NAME as TypeName)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun relation_stringList() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid", projection={"name"}, |
| entity = User.class) |
| public List<String> userNames; |
| """, COMMON.USER |
| ) { pojo -> |
| assertThat(pojo.relations.size, `is`(1)) |
| val rel = pojo.relations.first() |
| assertThat(rel.projection, `is`(listOf("name"))) |
| assertThat(rel.entity.typeName, `is`(COMMON.USER_TYPE_NAME as TypeName)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun relation_extendsBounds() { |
| singleRun( |
| """ |
| int id; |
| @Relation(parentColumn = "id", entityColumn = "uid") |
| public List<? extends User> user; |
| """, COMMON.USER |
| ) { pojo -> |
| assertThat(pojo.relations.size, `is`(1)) |
| assertThat(pojo.relations.first().entityField.name, `is`("uid")) |
| assertThat(pojo.relations.first().parentField.name, `is`("id")) |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_associateBy() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity( |
| primaryKeys = {"uid","friendId"}, |
| foreignKeys = { |
| @ForeignKey( |
| entity = User.class, |
| parentColumns = "uid", |
| childColumns = "uid", |
| onDelete = ForeignKey.CASCADE), |
| @ForeignKey( |
| entity = User.class, |
| parentColumns = "uid", |
| childColumns = "friendId", |
| onDelete = ForeignKey.CASCADE), |
| }, |
| indices = { @Index("uid"), @Index("friendId") } |
| ) |
| public class UserFriendsXRef { |
| public int uid; |
| public int friendId; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int id; |
| @Relation( |
| parentColumn = "id", entityColumn = "uid", |
| associateBy = @Junction( |
| value = UserFriendsXRef.class, |
| parentColumn = "uid", entityColumn = "friendId") |
| ) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { pojo -> |
| assertThat(pojo.relations.size, `is`(1)) |
| assertThat(pojo.relations.first().junction, notNullValue()) |
| assertThat(pojo.relations.first().junction!!.parentField.columnName, |
| `is`("uid")) |
| assertThat(pojo.relations.first().junction!!.entityField.columnName, |
| `is`("friendId")) |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_associateBy_withView() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @DatabaseView("SELECT 1, 2, FROM User") |
| public class UserFriendsXRefView { |
| public int uid; |
| public int friendId; |
| } |
| """.toJFO("foo.bar.UserFriendsXRefView") |
| singleRun( |
| """ |
| int id; |
| @Relation( |
| parentColumn = "id", entityColumn = "uid", |
| associateBy = @Junction( |
| value = UserFriendsXRefView.class, |
| parentColumn = "uid", entityColumn = "friendId") |
| ) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_associateBy_defaultColumns() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity( |
| primaryKeys = {"uid","friendId"}, |
| foreignKeys = { |
| @ForeignKey( |
| entity = User.class, |
| parentColumns = "uid", |
| childColumns = "uid", |
| onDelete = ForeignKey.CASCADE), |
| @ForeignKey( |
| entity = User.class, |
| parentColumns = "uid", |
| childColumns = "friendId", |
| onDelete = ForeignKey.CASCADE), |
| }, |
| indices = { @Index("uid"), @Index("friendId") } |
| ) |
| public class UserFriendsXRef { |
| public int uid; |
| public int friendId; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int friendId; |
| @Relation( |
| parentColumn = "friendId", entityColumn = "uid", |
| associateBy = @Junction(UserFriendsXRef.class)) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun relation_associateBy_missingParentColumn() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity(primaryKeys = {"friendFrom","uid"}) |
| public class UserFriendsXRef { |
| public int friendFrom; |
| public int uid; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int id; |
| @Relation( |
| parentColumn = "id", entityColumn = "uid", |
| associateBy = @Junction(UserFriendsXRef.class) |
| ) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(relationCannotFindJunctionParentField( |
| "foo.bar.UserFriendsXRef", "id", listOf("friendFrom", "uid"))) |
| } |
| |
| @Test |
| fun relation_associateBy_missingEntityColumn() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity(primaryKeys = {"friendA","friendB"}) |
| public class UserFriendsXRef { |
| public int friendA; |
| public int friendB; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int friendA; |
| @Relation( |
| parentColumn = "friendA", entityColumn = "uid", |
| associateBy = @Junction(UserFriendsXRef.class) |
| ) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(relationCannotFindJunctionEntityField( |
| "foo.bar.UserFriendsXRef", "uid", listOf("friendA", "friendB")) |
| ) |
| } |
| |
| @Test |
| fun relation_associateBy_missingSpecifiedParentColumn() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity(primaryKeys = {"friendA","friendB"}) |
| public class UserFriendsXRef { |
| public int friendA; |
| public int friendB; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int friendA; |
| @Relation( |
| parentColumn = "friendA", entityColumn = "uid", |
| associateBy = @Junction( |
| value = UserFriendsXRef.class, |
| parentColumn = "bad_col") |
| ) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(relationCannotFindJunctionParentField( |
| "foo.bar.UserFriendsXRef", "bad_col", listOf("friendA", "friendB")) |
| ) |
| } |
| |
| @Test |
| fun relation_associateBy_missingSpecifiedEntityColumn() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity(primaryKeys = {"friendA","friendB"}) |
| public class UserFriendsXRef { |
| public int friendA; |
| public int friendB; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int friendA; |
| @Relation( |
| parentColumn = "friendA", entityColumn = "uid", |
| associateBy = @Junction( |
| value = UserFriendsXRef.class, |
| entityColumn = "bad_col") |
| ) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.failsToCompile().withErrorContaining(relationCannotFindJunctionEntityField( |
| "foo.bar.UserFriendsXRef", "bad_col", listOf("friendA", "friendB")) |
| ) |
| } |
| |
| @Test |
| fun relation_associateBy_warnIndexOnJunctionColumn() { |
| val junctionEntity = """ |
| package foo.bar; |
| |
| import androidx.room.*; |
| |
| @Entity |
| public class UserFriendsXRef { |
| @PrimaryKey(autoGenerate = true) |
| public long rowid; |
| public int uid; |
| public int friendId; |
| } |
| """.toJFO("foo.bar.UserFriendsXRef") |
| singleRun( |
| """ |
| int friendId; |
| @Relation( |
| parentColumn = "friendId", entityColumn = "uid", |
| associateBy = @Junction(UserFriendsXRef.class)) |
| public List<User> user; |
| """, COMMON.USER, junctionEntity |
| ) { _ -> |
| }.compilesWithoutError().withWarningCount(2).withWarningContaining( |
| junctionColumnWithoutIndex("foo.bar.UserFriendsXRef", "uid")) |
| } |
| |
| @Test |
| fun cache() { |
| val pojo = """ |
| $HEADER |
| int id; |
| $FOOTER |
| """.toJFO(MY_POJO.toString()) |
| simpleRun(pojo) { invocation -> |
| val element = invocation.processingEnv.requireTypeElement(MY_POJO) |
| val pojo1 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.BIND_TO_STMT, null).process() |
| assertThat(pojo1, notNullValue()) |
| val pojo2 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.BIND_TO_STMT, null).process() |
| assertThat(pojo2, sameInstance(pojo1)) |
| |
| val pojo3 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.READ_FROM_CURSOR, null).process() |
| assertThat(pojo3, notNullValue()) |
| assertThat(pojo3, not(sameInstance(pojo1))) |
| |
| val pojo4 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.TWO_WAY, null).process() |
| assertThat(pojo4, notNullValue()) |
| assertThat(pojo4, not(sameInstance(pojo1))) |
| assertThat(pojo4, not(sameInstance(pojo3))) |
| |
| val pojo5 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.TWO_WAY, null).process() |
| assertThat(pojo5, sameInstance(pojo4)) |
| |
| val type = invocation.context.COMMON_TYPES.STRING |
| val mockElement = mock(XVariableElement::class.java) |
| doReturn(type).`when`(mockElement).type |
| val fakeField = Field( |
| element = mockElement, |
| name = "foo", |
| type = type, |
| affinity = SQLTypeAffinity.TEXT, |
| columnName = "foo", |
| parent = null, |
| indexed = false |
| ) |
| val fakeEmbedded = EmbeddedField(fakeField, "", null) |
| |
| val pojo6 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.TWO_WAY, fakeEmbedded).process() |
| assertThat(pojo6, notNullValue()) |
| assertThat(pojo6, not(sameInstance(pojo1))) |
| assertThat(pojo6, not(sameInstance(pojo3))) |
| assertThat(pojo6, not(sameInstance(pojo4))) |
| |
| val pojo7 = PojoProcessor.createFor(invocation.context, element, |
| FieldProcessor.BindingScope.TWO_WAY, fakeEmbedded).process() |
| assertThat(pojo7, sameInstance(pojo6)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_empty() { |
| val pojoCode = """ |
| public String mName; |
| """ |
| singleRun(pojoCode) { pojo -> |
| assertThat(pojo.constructor, notNullValue()) |
| assertThat(pojo.constructor?.params, `is`(emptyList())) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_ambiguous_twoFieldsExactMatch() { |
| val pojoCode = """ |
| public String mName; |
| public String _name; |
| public MyPojo(String mName) { |
| } |
| """ |
| singleRun(pojoCode) { pojo -> |
| val param = pojo.constructor?.params?.first() |
| assertThat(param, instanceOf(Constructor.Param.FieldParam::class.java)) |
| assertThat((param as Constructor.Param.FieldParam).field.name, `is`("mName")) |
| assertThat(pojo.fields.find { it.name == "mName" }?.setter?.callType, |
| `is`(CallType.CONSTRUCTOR)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_ambiguous_oneTypeMatches() { |
| val pojoCode = """ |
| public String mName; |
| public int _name; |
| public MyPojo(String name) { |
| } |
| """ |
| singleRun(pojoCode) { pojo -> |
| val param = pojo.constructor?.params?.first() |
| assertThat(param, instanceOf(Constructor.Param.FieldParam::class.java)) |
| assertThat((param as Constructor.Param.FieldParam).field.name, `is`("mName")) |
| assertThat(pojo.fields.find { it.name == "mName" }?.setter?.callType, |
| `is`(CallType.CONSTRUCTOR)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_ambiguous_twoFields() { |
| val pojo = """ |
| String mName; |
| String _name; |
| public MyPojo(String name) { |
| } |
| """ |
| singleRun(pojo) { _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.ambigiousConstructor(MY_POJO.toString(), |
| "name", listOf("mName", "_name")) |
| ) |
| } |
| |
| @Test |
| fun constructor_noMatchBadType() { |
| singleRun(""" |
| int foo; |
| public MyPojo(String foo) { |
| } |
| """) { _ -> |
| }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_POJO_CONSTRUCTOR) |
| } |
| |
| @Test |
| fun constructor_noMatch() { |
| singleRun(""" |
| String mName; |
| String _name; |
| public MyPojo(String foo) { |
| } |
| """) { _ -> |
| }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_POJO_CONSTRUCTOR) |
| } |
| |
| @Test |
| fun constructor_noMatchMultiArg() { |
| singleRun(""" |
| String mName; |
| int bar; |
| public MyPojo(String foo, String name) { |
| } |
| """) { _ -> |
| }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_POJO_CONSTRUCTOR) |
| } |
| |
| @Test |
| fun constructor_multipleMatching() { |
| singleRun(""" |
| String mName; |
| String mLastName; |
| public MyPojo(String name) { |
| } |
| public MyPojo(String name, String lastName) { |
| } |
| """) { _ -> |
| }.failsToCompile().withErrorContaining(ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS) |
| } |
| |
| @Test |
| fun constructor_multipleMatchingWithIgnored() { |
| singleRun(""" |
| String mName; |
| String mLastName; |
| @Ignore |
| public MyPojo(String name) { |
| } |
| public MyPojo(String name, String lastName) { |
| } |
| """) { pojo -> |
| assertThat(pojo.constructor, notNullValue()) |
| assertThat(pojo.constructor?.params?.size, `is`(2)) |
| assertThat(pojo.fields.find { it.name == "mName" }?.setter?.callType, |
| `is`(CallType.CONSTRUCTOR)) |
| assertThat(pojo.fields.find { it.name == "mLastName" }?.setter?.callType, |
| `is`(CallType.CONSTRUCTOR)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_dontTryForBindToScope() { |
| singleRun(""" |
| String mName; |
| String mLastName; |
| """) { _, invocation -> |
| val process2 = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT, |
| parent = null).process() |
| assertThat(process2.constructor, nullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_bindForTwoWay() { |
| singleRun(""" |
| String mName; |
| String mLastName; |
| """) { _, invocation -> |
| val process2 = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.TWO_WAY, |
| parent = null).process() |
| assertThat(process2.constructor, notNullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_multipleMatching_withNoArg() { |
| singleRun(""" |
| String mName; |
| String mLastName; |
| public MyPojo() { |
| } |
| public MyPojo(String name, String lastName) { |
| } |
| """) { pojo -> |
| assertThat(pojo.constructor?.params?.size ?: -1, `is`(0)) |
| }.compilesWithoutError().withWarningContaining( |
| ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS_CHOOSING_NO_ARG) |
| } |
| |
| @Test // added for b/69562125 |
| fun constructor_withNullabilityAnnotation() { |
| singleRun(""" |
| String mName; |
| public MyPojo(@androidx.annotation.NonNull String name) {} |
| """) { pojo -> |
| val constructor = pojo.constructor |
| assertThat(constructor, notNullValue()) |
| assertThat(constructor!!.params.size, `is`(1)) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun constructor_relationParameter() { |
| singleRun(""" |
| @Relation(entity = foo.bar.User.class, parentColumn = "uid", entityColumn="uid", |
| projection = "name") |
| public List<String> items; |
| public String uid; |
| public MyPojo(String uid, List<String> items) { |
| } |
| """, COMMON.USER) { _ -> |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun recursion_1Level_embedded() { |
| singleRun( |
| """ |
| @Embedded |
| MyPojo myPojo; |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_1Level_relation() { |
| singleRun( |
| """ |
| long id; |
| long parentId; |
| @Relation(parentColumn = "id", entityColumn = "parentId") |
| Set<MyPojo> children; |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_1Level_relation_specifyEntity() { |
| singleRun( |
| """ |
| @Embedded |
| A a; |
| |
| static class A { |
| long id; |
| long parentId; |
| @Relation(entity = A.class, parentColumn = "id", entityColumn = "parentId") |
| Set<AWithB> children; |
| } |
| |
| static class B { |
| long id; |
| } |
| |
| static class AWithB { |
| @Embedded |
| A a; |
| @Embedded |
| B b; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo.A -> foo.bar.MyPojo.A")) |
| } |
| |
| @Test |
| fun recursion_2Levels_relationToEmbed() { |
| singleRun( |
| """ |
| int pojoId; |
| |
| @Relation(parentColumn = "pojoId", entityColumn = "entityId") |
| List<MyEntity> myEntity; |
| |
| @Entity |
| static class MyEntity { |
| int entityId; |
| |
| @Embedded |
| MyPojo myPojo; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo.MyEntity -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_2Levels_onlyEmbeds_pojoToEntity() { |
| singleRun( |
| """ |
| @Embedded |
| A a; |
| |
| @Entity |
| static class A { |
| @Embedded |
| MyPojo myPojo; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo.A -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_2Levels_onlyEmbeds_onlyPojos() { |
| singleRun( |
| """ |
| @Embedded |
| A a; |
| static class A { |
| @Embedded |
| MyPojo myPojo; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo.A -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_2Level_relationToEmbed() { |
| singleRun( |
| """ |
| @Embedded |
| A a; |
| |
| static class A { |
| long id; |
| long parentId; |
| @Relation(parentColumn = "id", entityColumn = "parentId") |
| Set<AWithB> children; |
| } |
| |
| static class B { |
| long id; |
| } |
| |
| static class AWithB { |
| @Embedded |
| A a; |
| @Embedded |
| B b; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo.A -> foo.bar.MyPojo.AWithB -> foo.bar.MyPojo.A")) |
| } |
| |
| @Test |
| fun recursion_3Levels() { |
| singleRun( |
| """ |
| @Embedded |
| A a; |
| public static class A { |
| @Embedded |
| B b; |
| } |
| public static class B { |
| @Embedded |
| MyPojo myPojo; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo.A -> foo.bar.MyPojo.B -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_1Level_1LevelDown() { |
| singleRun( |
| """ |
| @Embedded |
| A a; |
| static class A { |
| @Embedded |
| B b; |
| } |
| static class B { |
| @Embedded |
| A a; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo.A -> foo.bar.MyPojo.B -> foo.bar.MyPojo.A")) |
| } |
| |
| @Test |
| fun recursion_branchAtLevel0_afterBackTrack() { |
| singleRun( |
| """ |
| @PrimaryKey |
| int id; |
| @Embedded |
| A a; |
| @Embedded |
| C c; |
| static class A { |
| @Embedded |
| B b; |
| } |
| static class B { |
| } |
| static class C { |
| @Embedded |
| MyPojo myPojo; |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo.C -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun recursion_branchAtLevel1_afterBackTrack() { |
| singleRun( |
| """ |
| @PrimaryKey |
| int id; |
| @Embedded |
| A a; |
| static class A { |
| @Embedded |
| B b; |
| @Embedded |
| MyPojo myPojo; |
| } |
| static class B { |
| @Embedded |
| C c; |
| } |
| static class C { |
| } |
| """) { _, _ -> |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format( |
| "foo.bar.MyPojo -> foo.bar.MyPojo.A -> foo.bar.MyPojo")) |
| } |
| |
| @Test |
| fun dataClass_primaryConstructor() { |
| listOf( |
| TestData.AllDefaultVals::class.java.canonicalName!!, |
| TestData.AllDefaultVars::class.java.canonicalName!!, |
| TestData.SomeDefaultVals::class.java.canonicalName!!, |
| TestData.SomeDefaultVars::class.java.canonicalName!!, |
| TestData.WithJvmOverloads::class.java.canonicalName!! |
| ).forEach { |
| simpleRun { invocation -> |
| PojoProcessor.createFor( |
| context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(it), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null |
| ).process() |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| } |
| |
| @Test |
| fun dataClass_withJvmOverloads_primaryConstructor() { |
| simpleRun { invocation -> |
| PojoProcessor.createFor( |
| context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement( |
| TestData.WithJvmOverloads::class |
| ), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null |
| ).process() |
| }.compilesWithoutError().withWarningCount(0) |
| } |
| |
| @Test |
| fun ignoredColumns() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| |
| @Entity(ignoredColumns = {"bar"}) |
| public class ${MY_POJO.simpleName()} { |
| public String foo; |
| public String bar; |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| val pojo = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| assertThat(pojo.fields.find { it.name == "foo" }, notNullValue()) |
| assertThat(pojo.fields.find { it.name == "bar" }, nullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun ignoredColumns_noConstructor() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| |
| @Entity(ignoredColumns = {"bar"}) |
| public class ${MY_POJO.simpleName()} { |
| private final String foo; |
| private final String bar; |
| |
| public ${MY_POJO.simpleName()}(String foo) { |
| this.foo = foo; |
| this.bar = null; |
| } |
| |
| public String getFoo() { |
| return this.foo; |
| } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| val pojo = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| assertThat(pojo.fields.find { it.name == "foo" }, notNullValue()) |
| assertThat(pojo.fields.find { it.name == "bar" }, nullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun ignoredColumns_noSetterGetter() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| |
| @Entity(ignoredColumns = {"bar"}) |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public String getFoo() { |
| return this.foo; |
| } |
| |
| public void setFoo(String foo) { |
| this.foo = foo; |
| } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| val pojo = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| assertThat(pojo.fields.find { it.name == "foo" }, notNullValue()) |
| assertThat(pojo.fields.find { it.name == "bar" }, nullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun ignoredColumns_columnInfo() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| |
| @Entity(ignoredColumns = {"my_bar"}) |
| public class ${MY_POJO.simpleName()} { |
| public String foo; |
| @ColumnInfo(name = "my_bar") |
| public String bar; |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| val pojo = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| assertThat(pojo.fields.find { it.name == "foo" }, notNullValue()) |
| assertThat(pojo.fields.find { it.name == "bar" }, nullValue()) |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun ignoredColumns_missing() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| |
| @Entity(ignoredColumns = {"no_such_column"}) |
| public class ${MY_POJO.simpleName()} { |
| public String foo; |
| public String bar; |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| val pojo = PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| assertThat(pojo.fields.find { it.name == "foo" }, notNullValue()) |
| assertThat(pojo.fields.find { it.name == "bar" }, notNullValue()) |
| }.failsToCompile().withErrorContaining( |
| ProcessorErrors.missingIgnoredColumns(listOf("no_such_column"))) |
| } |
| |
| @Test |
| fun noSetter_scopeBindStmt() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public String getFoo() { return foo; } |
| public String getBar() { return bar; } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT, |
| parent = null).process() |
| }.compilesWithoutError() |
| } |
| |
| @Test |
| fun noSetter_scopeTwoWay() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public String getFoo() { return foo; } |
| public String getBar() { return bar; } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.TWO_WAY, |
| parent = null).process() |
| }.failsToCompile().withErrorContaining("Cannot find setter for field.") |
| } |
| |
| @Test |
| fun noSetter_scopeReadFromCursor() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public String getFoo() { return foo; } |
| public String getBar() { return bar; } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| }.failsToCompile().withErrorContaining("Cannot find setter for field.") |
| } |
| |
| @Test |
| fun noGetter_scopeBindStmt() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public void setFoo(String foo) { this.foo = foo; } |
| public void setBar(String bar) { this.bar = bar; } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT, |
| parent = null).process() |
| }.failsToCompile().withErrorContaining("Cannot find getter for field.") |
| } |
| |
| @Test |
| fun noGetter_scopeTwoWay() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public void setFoo(String foo) { this.foo = foo; } |
| public void setBar(String bar) { this.bar = bar; } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.TWO_WAY, |
| parent = null).process() |
| }.failsToCompile().withErrorContaining("Cannot find getter for field.") |
| } |
| |
| @Test |
| fun noGetter_scopeReadCursor() { |
| simpleRun( |
| """ |
| package foo.bar; |
| import androidx.room.*; |
| public class ${MY_POJO.simpleName()} { |
| private String foo; |
| private String bar; |
| |
| public void setFoo(String foo) { this.foo = foo; } |
| public void setBar(String bar) { this.bar = bar; } |
| } |
| """.toJFO(MY_POJO.toString())) { invocation -> |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null).process() |
| }.compilesWithoutError() |
| } |
| |
| private fun singleRun( |
| code: String, |
| vararg jfos: JavaFileObject, |
| handler: (Pojo) -> Unit |
| ): CompileTester { |
| return singleRun(code, *jfos) { pojo, _ -> |
| handler(pojo) |
| } |
| } |
| |
| private fun singleRun( |
| code: String, |
| vararg jfos: JavaFileObject, |
| classpathFiles: Set<File> = emptySet(), |
| handler: (Pojo, TestInvocation) -> Unit |
| ): CompileTester { |
| val pojoCode = """ |
| $HEADER |
| $code |
| $FOOTER |
| """ |
| return singleRunFullClass( |
| code = pojoCode, |
| jfos = *jfos, |
| classpathFiles = classpathFiles, |
| handler = handler |
| ) |
| } |
| |
| private fun singleRunFullClass( |
| code: String, |
| vararg jfos: JavaFileObject, |
| classpathFiles: Set<File> = emptySet(), |
| handler: (Pojo, TestInvocation) -> Unit |
| ): CompileTester { |
| val pojoJFO = code.toJFO(MY_POJO.toString()) |
| val all = (jfos.toList() + pojoJFO).toTypedArray() |
| return simpleRun(*all, classpathFiles = classpathFiles) { invocation -> |
| handler.invoke( |
| PojoProcessor.createFor(context = invocation.context, |
| element = invocation.processingEnv.requireTypeElement(MY_POJO), |
| bindingScope = FieldProcessor.BindingScope.TWO_WAY, |
| parent = null).process(), |
| invocation |
| ) |
| } |
| } |
| |
| // Kotlin data classes to verify the PojoProcessor. |
| private class TestData { |
| data class AllDefaultVals( |
| val name: String = "", |
| val number: Int = 0, |
| val bit: Boolean = false |
| ) |
| |
| data class AllDefaultVars( |
| var name: String = "", |
| var number: Int = 0, |
| var bit: Boolean = false |
| ) |
| |
| data class SomeDefaultVals( |
| val name: String, |
| val number: Int = 0, |
| val bit: Boolean |
| ) |
| |
| data class SomeDefaultVars( |
| var name: String, |
| var number: Int = 0, |
| var bit: Boolean |
| ) |
| |
| data class WithJvmOverloads @JvmOverloads constructor( |
| val name: String, |
| val lastName: String = "", |
| var number: Int = 0, |
| var bit: Boolean |
| ) |
| } |
| } |