Home | History | Annotate | Download | only in processor
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.room.processor
     18 
     19 import COMMON
     20 import androidx.room.parser.SQLTypeAffinity
     21 import androidx.room.processor.ProcessorErrors.RELATION_IN_ENTITY
     22 import androidx.room.vo.CallType
     23 import androidx.room.vo.Field
     24 import androidx.room.vo.FieldGetter
     25 import androidx.room.vo.FieldSetter
     26 import androidx.room.vo.Index
     27 import androidx.room.vo.Pojo
     28 import com.google.testing.compile.JavaFileObjects
     29 import org.hamcrest.CoreMatchers.`is`
     30 import org.hamcrest.MatcherAssert.assertThat
     31 import org.junit.Test
     32 import org.junit.runner.RunWith
     33 import org.junit.runners.JUnit4
     34 import javax.lang.model.type.TypeKind.INT
     35 
     36 @RunWith(JUnit4::class)
     37 class EntityProcessorTest : BaseEntityParserTest() {
     38     @Test
     39     fun simple() {
     40         singleEntity("""
     41                 @PrimaryKey
     42                 private int id;
     43                 public int getId() { return id; }
     44                 public void setId(int id) { this.id = id; }
     45             """) { entity, invocation ->
     46             assertThat(entity.type.toString(), `is`("foo.bar.MyEntity"))
     47             assertThat(entity.fields.size, `is`(1))
     48             val field = entity.fields.first()
     49             val intType = invocation.processingEnv.typeUtils.getPrimitiveType(INT)
     50             assertThat(field, `is`(Field(
     51                     element = field.element,
     52                     name = "id",
     53                     type = intType,
     54                     columnName = "id",
     55                     affinity = SQLTypeAffinity.INTEGER)))
     56             assertThat(field.setter, `is`(FieldSetter("setId", intType, CallType.METHOD)))
     57             assertThat(field.getter, `is`(FieldGetter("getId", intType, CallType.METHOD)))
     58             assertThat(entity.primaryKey.fields, `is`(listOf(field)))
     59         }.compilesWithoutError()
     60     }
     61 
     62     @Test
     63     fun noGetter() {
     64         singleEntity("""
     65                 @PrimaryKey
     66                 private int id;
     67                 public void setId(int id) {this.id = id;}
     68                 """) { _, _ -> }
     69                 .failsToCompile()
     70                 .withErrorContaining(ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD)
     71     }
     72 
     73     @Test
     74     fun getterWithBadType() {
     75         singleEntity("""
     76                 @PrimaryKey
     77                 private int id;
     78                 public float getId() {return 0f;}
     79                 public void setId(int id) {this.id = id;}
     80                 """) { _, _ -> }
     81                 .failsToCompile()
     82                 .withErrorContaining(ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD)
     83     }
     84 
     85     @Test
     86     fun setterWithBadType() {
     87         singleEntity("""
     88                 @PrimaryKey
     89                 private int id;
     90                 public int getId() {return id;}
     91                 public void setId(float id) {}
     92                 """) { _, _ -> }
     93                 .failsToCompile()
     94                 .withErrorContaining(ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD)
     95     }
     96 
     97     @Test
     98     fun setterWithAssignableType() {
     99         singleEntity("""
    100                 @PrimaryKey
    101                 private int id;
    102                 public int getId() {return id;}
    103                 public void setId(Integer id) {}
    104                 """) { _, _ -> }
    105                 .compilesWithoutError()
    106     }
    107 
    108     @Test
    109     fun getterWithAssignableType() {
    110         singleEntity("""
    111                 @PrimaryKey
    112                 private int id;
    113                 public Integer getId() {return id;}
    114                 public void setId(int id) {}
    115                 """) { _, _ -> }
    116                 .compilesWithoutError()
    117     }
    118 
    119     @Test
    120     fun noSetter() {
    121         singleEntity("""
    122                 @PrimaryKey
    123                 private int id;
    124                 public int getId(){ return id; }
    125                 """) { _, _ -> }
    126                 .failsToCompile()
    127                 .withErrorContaining(ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD)
    128     }
    129 
    130     @Test
    131     fun tooManyGetters() {
    132         singleEntity("""
    133                 @PrimaryKey
    134                 private int id;
    135                 public void setId(int id) {}
    136                 public int getId(){ return id; }
    137                 public int id(){ return id; }
    138                 """) { _, _ -> }
    139                 .failsToCompile()
    140                 .withErrorContaining("getId, id")
    141     }
    142 
    143     @Test
    144     fun tooManyGettersWithIgnore() {
    145         singleEntity("""
    146                 @PrimaryKey
    147                 private int id;
    148                 public void setId(int id) {}
    149                 public int getId(){ return id; }
    150                 @Ignore public int id(){ return id; }
    151                 """) { entity, _ ->
    152             assertThat(entity.fields.first().getter.name, `is`("getId"))
    153         }.compilesWithoutError()
    154     }
    155 
    156     @Test
    157     fun tooManyGettersWithDifferentVisibility() {
    158         singleEntity("""
    159                 @PrimaryKey
    160                 private int id;
    161                 public void setId(int id) {}
    162                 public int getId(){ return id; }
    163                 protected int id(){ return id; }
    164                 """) { entity, _ ->
    165             assertThat(entity.fields.first().getter.name, `is`("getId"))
    166         }.compilesWithoutError()
    167     }
    168 
    169     @Test
    170     fun tooManyGettersWithDifferentTypes() {
    171         singleEntity("""
    172                 @PrimaryKey
    173                 public int id;
    174                 public void setId(int id) {}
    175                 public int getId(){ return id; }
    176                 """) { entity, _ ->
    177             assertThat(entity.fields.first().getter.name, `is`("id"))
    178             assertThat(entity.fields.first().getter.callType, `is`(CallType.FIELD))
    179         }.compilesWithoutError()
    180     }
    181 
    182     @Test
    183     fun tooManySetters() {
    184         singleEntity("""
    185                 @PrimaryKey
    186                 private int id;
    187                 public void setId(int id) {}
    188                 public void id(int id) {}
    189                 public int getId(){ return id; }
    190                 """) { _, _ -> }
    191                 .failsToCompile()
    192                 .withErrorContaining("setId, id")
    193     }
    194 
    195     @Test
    196     fun tooManySettersWithIgnore() {
    197         singleEntity("""
    198                 @PrimaryKey
    199                 private int id;
    200                 public void setId(int id) {}
    201                 @Ignore public void id(int id) {}
    202                 public int getId(){ return id; }
    203                 """) { entity, _ ->
    204             assertThat(entity.fields.first().setter.name, `is`("setId"))
    205         }.compilesWithoutError()
    206     }
    207 
    208     @Test
    209     fun tooManySettersWithDifferentVisibility() {
    210         singleEntity("""
    211                 @PrimaryKey
    212                 private int id;
    213                 public void setId(int id) {}
    214                 protected void id(int id) {}
    215                 public int getId(){ return id; }
    216                 """) { entity, _ ->
    217             assertThat(entity.fields.first().setter.name, `is`("setId"))
    218         }.compilesWithoutError()
    219     }
    220 
    221     @Test
    222     fun tooManySettersWithDifferentTypes() {
    223         singleEntity("""
    224                 @PrimaryKey
    225                 public int id;
    226                 public void setId(int id) {}
    227                 public int getId(){ return id; }
    228                 """) { entity, _ ->
    229             assertThat(entity.fields.first().setter.name, `is`("id"))
    230             assertThat(entity.fields.first().setter.callType, `is`(CallType.FIELD))
    231         }.compilesWithoutError()
    232     }
    233 
    234     @Test
    235     fun preferPublicOverProtected() {
    236         singleEntity("""
    237                 @PrimaryKey
    238                 int id;
    239                 public void setId(int id) {}
    240                 public int getId(){ return id; }
    241                 """) { entity, _ ->
    242             assertThat(entity.fields.first().setter.name, `is`("setId"))
    243             assertThat(entity.fields.first().getter.name, `is`("getId"))
    244         }.compilesWithoutError()
    245     }
    246 
    247     @Test
    248     fun customName() {
    249         singleEntity("""
    250                 @PrimaryKey
    251                 int x;
    252                 """, hashMapOf(Pair("tableName", "\"foo_table\""))) { entity, _ ->
    253             assertThat(entity.tableName, `is`("foo_table"))
    254         }.compilesWithoutError()
    255     }
    256 
    257     @Test
    258     fun emptyCustomName() {
    259         singleEntity("""
    260                 @PrimaryKey
    261                 int x;
    262                 """, hashMapOf(Pair("tableName", "\" \""))) { _, _ ->
    263         }.failsToCompile().withErrorContaining(ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY)
    264     }
    265 
    266     @Test
    267     fun missingPrimaryKey() {
    268         singleEntity("""
    269                 """) { _, _ ->
    270         }.failsToCompile()
    271                 .withErrorContaining(ProcessorErrors.MISSING_PRIMARY_KEY)
    272     }
    273 
    274     @Test
    275     fun missingColumnAdapter() {
    276         singleEntity("""
    277                 @PrimaryKey
    278                 public java.util.Date myDate;
    279                 """) { _, _ ->
    280         }.failsToCompile().withErrorContaining(ProcessorErrors.CANNOT_FIND_COLUMN_TYPE_ADAPTER)
    281     }
    282 
    283     @Test
    284     fun dropSubPrimaryKey() {
    285         singleEntity(
    286                 """
    287                 @PrimaryKey
    288                 int id;
    289                 @Embedded
    290                 Point myPoint;
    291                 static class Point {
    292                     @PrimaryKey
    293                     int x;
    294                     int y;
    295                 }
    296                 """
    297         ) { entity, _ ->
    298             assertThat(entity.primaryKey.fields.map { it.name }, `is`(listOf("id")))
    299         }.compilesWithoutError()
    300                 .withWarningCount(1)
    301                 .withWarningContaining(ProcessorErrors.embeddedPrimaryKeyIsDropped(
    302                         "foo.bar.MyEntity", "x"))
    303     }
    304 
    305     @Test
    306     fun ignoreDropSubPrimaryKey() {
    307         singleEntity(
    308                 """
    309                 @PrimaryKey
    310                 int id;
    311                 @Embedded
    312                 @SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
    313                 Point myPoint;
    314                 static class Point {
    315                     @PrimaryKey
    316                     int x;
    317                     int y;
    318                 }
    319                 """
    320         ) { entity, _ ->
    321             assertThat(entity.primaryKey.fields.map { it.name }, `is`(listOf("id")))
    322         }.compilesWithoutError().withWarningCount(0)
    323     }
    324 
    325     @Test
    326     fun notNull() {
    327         singleEntity(
    328                 """
    329                 @PrimaryKey int id;
    330                 @NonNull public String name;
    331                 """
    332         ) { entity, _ ->
    333             val field = fieldsByName(entity, "name").first()
    334             assertThat(field.name, `is`("name"))
    335             assertThat(field.columnName, `is`("name"))
    336             assertThat(field.nonNull, `is`(true))
    337         }.compilesWithoutError()
    338     }
    339 
    340     private fun fieldsByName(entity: Pojo, vararg fieldNames: String): List<Field> {
    341         return fieldNames.mapNotNull { name -> entity.fields.find { it.name == name } }
    342     }
    343 
    344     @Test
    345     fun index_simple() {
    346         val annotation = mapOf(
    347                 "indices" to """@Index("foo")"""
    348         )
    349         singleEntity(
    350                 """
    351                 @PrimaryKey
    352                 public int id;
    353                 public String foo;
    354                 """
    355                 , annotation) { entity, _ ->
    356             assertThat(entity.indices, `is`(
    357                     listOf(Index(name = "index_MyEntity_foo",
    358                             unique = false,
    359                             fields = fieldsByName(entity, "foo")))))
    360         }.compilesWithoutError()
    361     }
    362 
    363     @Test
    364     fun index_fromField() {
    365         singleEntity(
    366                 """
    367                 @PrimaryKey
    368                 public int id;
    369                 @ColumnInfo(index = true)
    370                 public String foo;
    371                 """) { entity, _ ->
    372             assertThat(entity.indices, `is`(
    373                     listOf(Index(name = "index_MyEntity_foo",
    374                             unique = false,
    375                             fields = fieldsByName(entity, "foo")))
    376             ))
    377         }.compilesWithoutError()
    378     }
    379 
    380     @Test
    381     fun index_multiColumn() {
    382         val annotation = mapOf(
    383                 "indices" to """@Index({"foo", "id"})"""
    384         )
    385         singleEntity(
    386                 """
    387                 @PrimaryKey
    388                 public int id;
    389                 public String foo;
    390                 """
    391                 , annotation) { entity, _ ->
    392             assertThat(entity.indices, `is`(
    393                     listOf(Index(name = "index_MyEntity_foo_id",
    394                             unique = false,
    395                             fields = fieldsByName(entity, "foo", "id")))
    396             ))
    397         }.compilesWithoutError()
    398     }
    399 
    400     @Test
    401     fun index_multiple() {
    402         val annotation = mapOf(
    403                 "indices" to """{@Index({"foo", "id"}), @Index({"bar_column", "foo"})}"""
    404         )
    405         singleEntity(
    406                 """
    407                 @PrimaryKey
    408                 public int id;
    409                 public String foo;
    410                 @ColumnInfo(name = "bar_column")
    411                 public String bar;
    412                 """
    413                 , annotation) { entity, _ ->
    414             assertThat(entity.indices, `is`(
    415                     listOf(Index(name = "index_MyEntity_foo_id",
    416                             unique = false,
    417                             fields = fieldsByName(entity, "foo", "id")),
    418                             Index(name = "index_MyEntity_bar_column_foo",
    419                                     unique = false,
    420                                     fields = fieldsByName(entity, "bar", "foo")))
    421             ))
    422         }.compilesWithoutError()
    423     }
    424 
    425     @Test
    426     fun index_unique() {
    427         val annotation = mapOf(
    428                 "indices" to """@Index(value = {"foo", "id"}, unique = true)"""
    429         )
    430         singleEntity(
    431                 """
    432                 @PrimaryKey
    433                 public int id;
    434                 public String foo;
    435                 """
    436                 , annotation) { entity, _ ->
    437             assertThat(entity.indices, `is`(
    438                     listOf(Index(
    439                             name = "index_MyEntity_foo_id",
    440                             unique = true,
    441                             fields = fieldsByName(entity, "foo", "id")))
    442             ))
    443         }.compilesWithoutError()
    444     }
    445 
    446     @Test
    447     fun index_customName() {
    448         val annotation = mapOf(
    449                 "indices" to """@Index(value = {"foo"}, name = "myName")"""
    450         )
    451         singleEntity(
    452                 """
    453                 @PrimaryKey
    454                 public int id;
    455                 public String foo;
    456                 """
    457                 , annotation) { entity, _ ->
    458             assertThat(entity.indices, `is`(
    459                     listOf(Index(name = "myName",
    460                             unique = false,
    461                             fields = fieldsByName(entity, "foo")))
    462             ))
    463         }.compilesWithoutError()
    464     }
    465 
    466     @Test
    467     fun index_customTableName() {
    468         val annotation = mapOf(
    469                 "tableName" to "\"MyTable\"",
    470                 "indices" to """@Index(value = {"foo"})"""
    471         )
    472         singleEntity(
    473                 """
    474                 @PrimaryKey
    475                 public int id;
    476                 public String foo;
    477                 """
    478                 , annotation) { entity, _ ->
    479             assertThat(entity.indices, `is`(
    480                     listOf(Index(name = "index_MyTable_foo",
    481                             unique = false,
    482                             fields = fieldsByName(entity, "foo")))
    483             ))
    484         }.compilesWithoutError()
    485     }
    486 
    487     @Test
    488     fun index_empty() {
    489         val annotation = mapOf(
    490                 "indices" to """@Index({})"""
    491         )
    492         singleEntity(
    493                 """
    494                 @PrimaryKey
    495                 public int id;
    496                 public String foo;
    497                 """
    498                 , annotation) { _, _ ->
    499         }.failsToCompile().withErrorContaining(
    500                 ProcessorErrors.INDEX_COLUMNS_CANNOT_BE_EMPTY
    501         )
    502     }
    503 
    504     @Test
    505     fun index_missingColumn() {
    506         val annotation = mapOf(
    507                 "indices" to """@Index({"foo", "bar"})"""
    508         )
    509         singleEntity(
    510                 """
    511                 @PrimaryKey
    512                 public int id;
    513                 public String foo;
    514                 """
    515                 , annotation) { _, _ ->
    516         }.failsToCompile().withErrorContaining(
    517                 ProcessorErrors.indexColumnDoesNotExist("bar", listOf("id, foo"))
    518         )
    519     }
    520 
    521     @Test
    522     fun index_nameConflict() {
    523         val annotation = mapOf(
    524                 "indices" to """@Index({"foo"})"""
    525         )
    526         singleEntity(
    527                 """
    528                 @PrimaryKey
    529                 public int id;
    530                 @ColumnInfo(index = true)
    531                 public String foo;
    532                 """
    533                 , annotation) { _, _ ->
    534         }.failsToCompile().withErrorContaining(
    535                 ProcessorErrors.duplicateIndexInEntity("index_MyEntity_foo")
    536         )
    537     }
    538 
    539     @Test
    540     fun index_droppedParentFieldIndex() {
    541         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    542                 """
    543                 package foo.bar;
    544                 import androidx.room.*;
    545                 public class Base {
    546                     @PrimaryKey
    547                     long baseId;
    548                     @ColumnInfo(index = true)
    549                     String name;
    550                     String lastName;
    551                 }
    552                 """)
    553         singleEntity(
    554                 """
    555                 @PrimaryKey
    556                 public int id;
    557                 """, baseClass = "foo.bar.Base", jfos = listOf(parent)) { entity, _ ->
    558             assertThat(entity.indices.isEmpty(), `is`(true))
    559         }.compilesWithoutError()
    560                 .withWarningContaining(
    561                         ProcessorErrors.droppedSuperClassFieldIndex(
    562                                 fieldName = "name",
    563                                 childEntity = "foo.bar.MyEntity",
    564                                 superEntity = "foo.bar.Base")
    565                 )
    566     }
    567 
    568     @Test
    569     fun index_keptGrandParentEntityIndex() {
    570         val grandParent = JavaFileObjects.forSourceLines("foo.bar.Base",
    571                 """
    572                 package foo.bar;
    573                 import androidx.room.*;
    574                 @Entity(indices = @Index({"name", "lastName"}))
    575                 public class Base {
    576                     @PrimaryKey
    577                     long baseId;
    578                     String name, lastName;
    579                 }
    580                 """)
    581         val parent = JavaFileObjects.forSourceLines("foo.bar.Parent",
    582                 """
    583                 package foo.bar;
    584                 import androidx.room.*;
    585 
    586                 public class Parent extends Base {
    587                     String iHaveAField;
    588                 }
    589                 """)
    590         singleEntity(
    591                 """
    592                 @PrimaryKey
    593                 public int id;
    594                 """,
    595                 baseClass = "foo.bar.Parent",
    596                 attributes = hashMapOf("inheritSuperIndices" to "true"),
    597                 jfos = listOf(parent, grandParent)) {
    598             entity, _ ->
    599             assertThat(entity.indices.size, `is`(1))
    600             assertThat(entity.indices.first(),
    601                     `is`(Index(name = "index_MyEntity_name_lastName",
    602                             unique = false,
    603                             fields = fieldsByName(entity, "name", "lastName"))))
    604         }.compilesWithoutError().withWarningCount(0)
    605     }
    606 
    607     @Test
    608     fun index_keptParentEntityIndex() {
    609         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    610                 """
    611                 package foo.bar;
    612                 import androidx.room.*;
    613                 @Entity(indices = @Index({"name", "lastName"}))
    614                 public class Base {
    615                     @PrimaryKey
    616                     long baseId;
    617                     String name, lastName;
    618                 }
    619                 """)
    620         singleEntity(
    621                 """
    622                 @PrimaryKey
    623                 public int id;
    624                 """,
    625                 baseClass = "foo.bar.Base",
    626                 attributes = hashMapOf("inheritSuperIndices" to "true"),
    627                 jfos = listOf(parent)) { entity, _ ->
    628             assertThat(entity.indices.size, `is`(1))
    629             assertThat(entity.indices.first(),
    630                     `is`(Index(name = "index_MyEntity_name_lastName",
    631                             unique = false,
    632                             fields = fieldsByName(entity, "name", "lastName"))))
    633         }.compilesWithoutError().withWarningCount(0)
    634     }
    635 
    636     @Test
    637     fun index_keptParentFieldIndex() {
    638         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    639                 """
    640                 package foo.bar;
    641                 import androidx.room.*;
    642                 public class Base {
    643                     @PrimaryKey
    644                     long baseId;
    645                     @ColumnInfo(index = true)
    646                     String name;
    647                     String lastName;
    648                 }
    649                 """)
    650         singleEntity(
    651                 """
    652                 @PrimaryKey
    653                 public int id;
    654                 """,
    655                 baseClass = "foo.bar.Base",
    656                 attributes = hashMapOf("inheritSuperIndices" to "true"),
    657                 jfos = listOf(parent)) { entity, _ ->
    658             assertThat(entity.indices.size, `is`(1))
    659             assertThat(entity.indices.first(),
    660                     `is`(Index(name = "index_MyEntity_name",
    661                             unique = false,
    662                             fields = fieldsByName(entity, "name"))))
    663         }.compilesWithoutError().withWarningCount(0)
    664     }
    665 
    666     @Test
    667     fun index_droppedGrandParentEntityIndex() {
    668         val grandParent = JavaFileObjects.forSourceLines("foo.bar.Base",
    669                 """
    670                 package foo.bar;
    671                 import androidx.room.*;
    672                 @Entity(indices = @Index({"name", "lastName"}))
    673                 public class Base {
    674                     @PrimaryKey
    675                     long baseId;
    676                     String name, lastName;
    677                 }
    678                 """)
    679         val parent = JavaFileObjects.forSourceLines("foo.bar.Parent",
    680                 """
    681                 package foo.bar;
    682                 import androidx.room.*;
    683 
    684                 public class Parent extends Base {
    685                     String iHaveAField;
    686                 }
    687                 """)
    688         singleEntity(
    689                 """
    690                 @PrimaryKey
    691                 public int id;
    692                 """, baseClass = "foo.bar.Parent", jfos = listOf(parent, grandParent)) {
    693             entity, _ ->
    694             assertThat(entity.indices.isEmpty(), `is`(true))
    695         }.compilesWithoutError()
    696                 .withWarningContaining(
    697                         ProcessorErrors.droppedSuperClassIndex(
    698                                 childEntity = "foo.bar.MyEntity",
    699                                 superEntity = "foo.bar.Base")
    700                 )
    701     }
    702 
    703     @Test
    704     fun index_droppedParentEntityIndex() {
    705         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    706                 """
    707                 package foo.bar;
    708                 import androidx.room.*;
    709                 @Entity(indices = @Index({"name", "lastName"}))
    710                 public class Base {
    711                     @PrimaryKey
    712                     long baseId;
    713                     String name, lastName;
    714                 }
    715                 """)
    716         singleEntity(
    717                 """
    718                 @PrimaryKey
    719                 public int id;
    720                 """, baseClass = "foo.bar.Base", jfos = listOf(parent)) { entity, _ ->
    721             assertThat(entity.indices.isEmpty(), `is`(true))
    722         }.compilesWithoutError()
    723                 .withWarningContaining(
    724                         ProcessorErrors.droppedSuperClassIndex(
    725                                 childEntity = "foo.bar.MyEntity",
    726                                 superEntity = "foo.bar.Base")
    727                 )
    728     }
    729 
    730     @Test
    731     fun index_droppedEmbeddedEntityIndex() {
    732         singleEntity(
    733                 """
    734                 @PrimaryKey
    735                 public int id;
    736                 @Embedded
    737                 public Foo foo;
    738                 @Entity(indices = {@Index("a")})
    739                 static class Foo {
    740                     @PrimaryKey
    741                     @ColumnInfo(name = "foo_id")
    742                     int id;
    743                     @ColumnInfo(index = true)
    744                     public int a;
    745                 }
    746                 """) { entity, _ ->
    747             assertThat(entity.indices.isEmpty(), `is`(true))
    748         }.compilesWithoutError()
    749                 .withWarningContaining(
    750                         ProcessorErrors.droppedEmbeddedIndex(
    751                                 entityName = "foo.bar.MyEntity.Foo",
    752                                 fieldPath = "foo",
    753                                 grandParent = "foo.bar.MyEntity")
    754                 )
    755     }
    756 
    757     @Test
    758     fun index_onEmbeddedField() {
    759         singleEntity(
    760                 """
    761                 @PrimaryKey
    762                 public int id;
    763                 @Embedded
    764                 @ColumnInfo(index = true)
    765                 public Foo foo;
    766                 static class Foo {
    767                     @ColumnInfo(index = true)
    768                     public int a;
    769                 }
    770                 """) { entity, _ ->
    771             assertThat(entity.indices.isEmpty(), `is`(true))
    772         }.failsToCompile().withErrorContaining(
    773                 ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION
    774         )
    775     }
    776 
    777     @Test
    778     fun index_droppedEmbeddedFieldIndex() {
    779         singleEntity(
    780                 """
    781                 @PrimaryKey
    782                 public int id;
    783                 @Embedded
    784                 public Foo foo;
    785                 static class Foo {
    786                     @ColumnInfo(index = true)
    787                     public int a;
    788                 }
    789                 """) { entity, _ ->
    790             assertThat(entity.indices.isEmpty(), `is`(true))
    791         }.compilesWithoutError()
    792                 .withWarningContaining(
    793                         ProcessorErrors.droppedEmbeddedFieldIndex("foo > a", "foo.bar.MyEntity")
    794                 )
    795     }
    796 
    797     @Test
    798     fun index_referenceEmbeddedField() {
    799         singleEntity(
    800                 """
    801                 @PrimaryKey
    802                 public int id;
    803                 @Embedded
    804                 public Foo foo;
    805                 static class Foo {
    806                     public int a;
    807                 }
    808                 """, attributes = mapOf("indices" to "@Index(\"a\")")) { entity, _ ->
    809             assertThat(entity.indices.size, `is`(1))
    810             assertThat(entity.indices.first(), `is`(
    811                     Index(
    812                             name = "index_MyEntity_a",
    813                             unique = false,
    814                             fields = fieldsByName(entity, "a")
    815                     )
    816             ))
    817         }.compilesWithoutError()
    818     }
    819 
    820     @Test
    821     fun primaryKey_definedInBothWays() {
    822         singleEntity(
    823                 """
    824                 public int id;
    825                 @PrimaryKey
    826                 public String foo;
    827                 """,
    828                 attributes = mapOf("primaryKeys" to "\"id\"")) { _, _ ->
    829         }.failsToCompile().withErrorContaining(
    830                 ProcessorErrors.multiplePrimaryKeyAnnotations(
    831                         listOf("PrimaryKey[id]", "PrimaryKey[foo]")
    832                 ))
    833     }
    834 
    835     @Test
    836     fun primaryKey_badColumnName() {
    837         singleEntity(
    838                 """
    839                 public int id;
    840                 """,
    841                 attributes = mapOf("primaryKeys" to "\"foo\"")) { _, _ ->
    842         }.failsToCompile().withErrorContaining(
    843                 ProcessorErrors.primaryKeyColumnDoesNotExist("foo", listOf("id")))
    844     }
    845 
    846     @Test
    847     fun primaryKey_multipleAnnotations() {
    848         singleEntity("""
    849                 @PrimaryKey
    850                 int x;
    851                 @PrimaryKey
    852                 int y;
    853                 """) { entity, _ ->
    854             assertThat(entity.primaryKey.fields.isEmpty(), `is`(true))
    855         }.failsToCompile()
    856                 .withErrorContaining(
    857                         ProcessorErrors.multiplePrimaryKeyAnnotations(
    858                                 listOf("PrimaryKey[x]", "PrimaryKey[y]")))
    859     }
    860 
    861     @Test
    862     fun primaryKey_fromParentField() {
    863         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    864                 """
    865                 package foo.bar;
    866                 import androidx.room.*;
    867                 public class Base {
    868                     @PrimaryKey
    869                     long baseId;
    870                     String name, lastName;
    871                 }
    872                 """)
    873         singleEntity(
    874                 """
    875                 public int id;
    876                 """,
    877                 baseClass = "foo.bar.Base",
    878                 jfos = listOf(parent)) { entity, _ ->
    879             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("baseId"))
    880         }.compilesWithoutError().withWarningCount(0)
    881     }
    882 
    883     @Test
    884     fun primaryKey_fromParentEntity() {
    885         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    886                 """
    887                 package foo.bar;
    888                 import androidx.room.*;
    889                 @Entity(primaryKeys = "baseId")
    890                 public class Base {
    891                     long baseId;
    892                     String name, lastName;
    893                 }
    894                 """)
    895         singleEntity(
    896                 """
    897                 public int id;
    898                 """,
    899                 baseClass = "foo.bar.Base",
    900                 jfos = listOf(parent)) { entity, _ ->
    901             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("baseId"))
    902         }.compilesWithoutError().withWarningCount(0)
    903     }
    904 
    905     @Test
    906     fun primaryKey_overrideFromParentField() {
    907         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    908                 """
    909                 package foo.bar;
    910                 import androidx.room.*;
    911                 public class Base {
    912                     @PrimaryKey
    913                     long baseId;
    914                     String name, lastName;
    915                 }
    916                 """)
    917         singleEntity(
    918                 """
    919                 @PrimaryKey
    920                 public int id;
    921                 """,
    922                 baseClass = "foo.bar.Base",
    923                 jfos = listOf(parent)) { entity, _ ->
    924             assertThat(entity.primaryKey.fields.size, `is`(1))
    925             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
    926             assertThat(entity.primaryKey.autoGenerateId, `is`(false))
    927         }.compilesWithoutError().withNoteContaining(
    928                 "PrimaryKey[baseId] is overridden by PrimaryKey[id]"
    929         )
    930     }
    931 
    932     @Test
    933     fun primaryKey_overrideFromParentEntityViaField() {
    934         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    935                 """
    936                 package foo.bar;
    937                 import androidx.room.*;
    938                 @Entity(primaryKeys = "baseId")
    939                 public class Base {
    940                     long baseId;
    941                     String name, lastName;
    942                 }
    943                 """)
    944         singleEntity(
    945                 """
    946                 @PrimaryKey
    947                 public int id;
    948                 """,
    949                 baseClass = "foo.bar.Base",
    950                 jfos = listOf(parent)) { entity, _ ->
    951             assertThat(entity.primaryKey.fields.size, `is`(1))
    952             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
    953         }.compilesWithoutError().withNoteContaining(
    954                 "PrimaryKey[baseId] is overridden by PrimaryKey[id]"
    955         )
    956     }
    957 
    958     @Test
    959     fun primaryKey_overrideFromParentEntityViaEntity() {
    960         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
    961                 """
    962                 package foo.bar;
    963                 import androidx.room.*;
    964                 @Entity(primaryKeys = "baseId")
    965                 public class Base {
    966                     long baseId;
    967                     String name, lastName;
    968                 }
    969                 """)
    970         singleEntity(
    971                 """
    972                 public int id;
    973                 """,
    974                 baseClass = "foo.bar.Base",
    975                 jfos = listOf(parent),
    976                 attributes = mapOf("primaryKeys" to "\"id\"")) { entity, _ ->
    977             assertThat(entity.primaryKey.fields.size, `is`(1))
    978             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
    979             assertThat(entity.primaryKey.autoGenerateId, `is`(false))
    980         }.compilesWithoutError().withNoteContaining(
    981                 "PrimaryKey[baseId] is overridden by PrimaryKey[id]"
    982         )
    983     }
    984 
    985     @Test
    986     fun primaryKey_autoGenerate() {
    987         listOf("long", "Long", "Integer", "int").forEach { type ->
    988             singleEntity(
    989                     """
    990                 @PrimaryKey(autoGenerate = true)
    991                 public $type id;
    992                 """) { entity, _ ->
    993                 assertThat(entity.primaryKey.fields.size, `is`(1))
    994                 assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
    995                 assertThat(entity.primaryKey.autoGenerateId, `is`(true))
    996             }.compilesWithoutError()
    997         }
    998     }
    999 
   1000     @Test
   1001     fun primaryKey_nonNull_notNeeded() {
   1002         listOf("long", "Long", "Integer", "int").forEach { type ->
   1003             singleEntity(
   1004                     """
   1005                 @PrimaryKey
   1006                 public $type id;
   1007                 """) { entity, _ ->
   1008                 assertThat(entity.primaryKey.fields.size, `is`(1))
   1009                 assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
   1010                 assertThat(entity.primaryKey.autoGenerateId, `is`(false))
   1011             }.compilesWithoutError()
   1012         }
   1013     }
   1014 
   1015     @Test
   1016     fun primaryKey_autoGenerateBadType() {
   1017         listOf("String", "float", "Float", "Double", "double").forEach { type ->
   1018             singleEntity(
   1019                     """
   1020                 @PrimaryKey(autoGenerate = true)
   1021                 public $type id;
   1022                 """) { entity, _ ->
   1023                 assertThat(entity.primaryKey.fields.size, `is`(1))
   1024                 assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
   1025                 assertThat(entity.primaryKey.autoGenerateId, `is`(true))
   1026             }.failsToCompile().withErrorContaining(
   1027                     ProcessorErrors.AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT)
   1028         }
   1029     }
   1030 
   1031     @Test
   1032     fun primaryKey_embedded() {
   1033         singleEntity(
   1034                 """
   1035                 public int id;
   1036 
   1037                 @Embedded(prefix = "bar_")
   1038                 @PrimaryKey
   1039                 @NonNull
   1040                 public Foo foo;
   1041 
   1042                 static class Foo {
   1043                     public int a;
   1044                     public int b;
   1045                 }
   1046                 """) { entity, _ ->
   1047             assertThat(entity.primaryKey.fields.map { it.columnName },
   1048                     `is`(listOf("bar_a", "bar_b")))
   1049         }.compilesWithoutError().withWarningCount(0)
   1050     }
   1051 
   1052     @Test
   1053     fun primaryKey_embeddedInherited() {
   1054         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1055                 """
   1056                 package foo.bar;
   1057                 import androidx.annotation.NonNull;
   1058                 import androidx.room.*;
   1059 
   1060                 public class Base {
   1061                     long baseId;
   1062                     String name, lastName;
   1063                     @Embedded(prefix = "bar_")
   1064                     @PrimaryKey
   1065                     @NonNull
   1066                     public Foo foo;
   1067 
   1068                     static class Foo {
   1069                         public int a;
   1070                         public int b;
   1071                     }
   1072                 }
   1073                 """)
   1074         singleEntity(
   1075                 """
   1076                 public int id;
   1077                 """,
   1078                 baseClass = "foo.bar.Base",
   1079                 jfos = listOf(parent)) { entity, _ ->
   1080             assertThat(entity.primaryKey.fields.map { it.columnName },
   1081                     `is`(listOf("bar_a", "bar_b")))
   1082         }.compilesWithoutError().withWarningCount(0)
   1083     }
   1084 
   1085     @Test
   1086     fun primaryKey_overrideViaEmbedded() {
   1087         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1088                 """
   1089                 package foo.bar;
   1090                 import androidx.room.*;
   1091 
   1092                 @Entity(primaryKeys = "baseId")
   1093                 public class Base {
   1094                     long baseId;
   1095                     String name, lastName;
   1096                 }
   1097                 """)
   1098         singleEntity(
   1099                 """
   1100                 public int id;
   1101                 @Embedded(prefix = "bar_")
   1102                 @PrimaryKey
   1103                 @NonNull
   1104                 public Foo foo;
   1105 
   1106                 static class Foo {
   1107                     public int a;
   1108                     public int b;
   1109                 }
   1110                 """,
   1111                 baseClass = "foo.bar.Base",
   1112                 jfos = listOf(parent)) { entity, _ ->
   1113             assertThat(entity.primaryKey.fields.map { it.columnName },
   1114                     `is`(listOf("bar_a", "bar_b")))
   1115         }.compilesWithoutError().withNoteContaining(
   1116                 "PrimaryKey[baseId] is overridden by PrimaryKey[foo > a, foo > b]")
   1117     }
   1118 
   1119     @Test
   1120     fun primaryKey_overrideEmbedded() {
   1121         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1122                 """
   1123                 package foo.bar;
   1124                 import androidx.annotation.NonNull;
   1125                 import androidx.room.*;
   1126 
   1127                 public class Base {
   1128                     long baseId;
   1129                     String name, lastName;
   1130                     @Embedded(prefix = "bar_")
   1131                     @PrimaryKey
   1132                     @NonNull
   1133                     public Foo foo;
   1134 
   1135                     static class Foo {
   1136                         public int a;
   1137                         public int b;
   1138                     }
   1139                 }
   1140                 """)
   1141         singleEntity(
   1142                 """
   1143                 @PrimaryKey
   1144                 public int id;
   1145                 """,
   1146                 baseClass = "foo.bar.Base",
   1147                 jfos = listOf(parent)) { entity, _ ->
   1148             assertThat(entity.primaryKey.fields.map { it.columnName },
   1149                     `is`(listOf("id")))
   1150         }.compilesWithoutError().withNoteContaining(
   1151                 "PrimaryKey[foo > a, foo > b] is overridden by PrimaryKey[id]")
   1152     }
   1153 
   1154     @Test
   1155     fun primaryKey_NonNull() {
   1156         singleEntity(
   1157                 """
   1158             @PrimaryKey
   1159             @NonNull
   1160             public String id;
   1161             """) { entity, _ ->
   1162             assertThat(entity.primaryKey.fields.size, `is`(1))
   1163             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
   1164         }.compilesWithoutError()
   1165     }
   1166 
   1167     @Test
   1168     fun primaryKey_Nullable() {
   1169         singleEntity(
   1170                 """
   1171             @PrimaryKey
   1172             public String id;
   1173             """) { entity, _ ->
   1174             assertThat(entity.primaryKey.fields.size, `is`(1))
   1175             assertThat(entity.primaryKey.fields.firstOrNull()?.name, `is`("id"))
   1176         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("id"))
   1177     }
   1178 
   1179     @Test
   1180     fun primaryKey_MultipleNullable() {
   1181         singleEntity(
   1182                 """
   1183             @PrimaryKey
   1184             public String id;
   1185             @PrimaryKey
   1186             public String anotherId;
   1187             """) { _, _ ->
   1188         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("id"))
   1189                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("anotherId"))
   1190     }
   1191 
   1192     @Test
   1193     fun primaryKey_MultipleNullableAndNonNullable() {
   1194         singleEntity(
   1195                 """
   1196             @PrimaryKey
   1197             @NonNull
   1198             public String id;
   1199             @PrimaryKey
   1200             public String anotherId;
   1201             """) { _, _ ->
   1202         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("anotherId"))
   1203     }
   1204 
   1205     @Test
   1206     fun primaryKey_definedAsAttributesNullable() {
   1207         singleEntity(
   1208                 """
   1209                 public int id;
   1210                 public String foo;
   1211                 """,
   1212                 attributes = mapOf("primaryKeys" to "{\"id\", \"foo\"}")) { _, _ ->
   1213         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1214     }
   1215 
   1216     @Test
   1217     fun primaryKey_definedAsAttributesNonNull() {
   1218         singleEntity(
   1219                 """
   1220                 public int id;
   1221                 @NonNull
   1222                 public String foo;
   1223                 """,
   1224                 attributes = mapOf("primaryKeys" to "{\"id\", \"foo\"}")) { entity, _ ->
   1225             assertThat(entity.primaryKey.fields.map { it.name }, `is`(listOf("id", "foo")))
   1226         }.compilesWithoutError()
   1227     }
   1228 
   1229     @Test
   1230     fun primaryKey_nullableEmbedded() {
   1231         singleEntity(
   1232                 """
   1233                 public int id;
   1234 
   1235                 @Embedded(prefix = "bar_")
   1236                 @PrimaryKey
   1237                 public Foo foo;
   1238 
   1239                 static class Foo {
   1240                     public int a;
   1241                     public int b;
   1242                 }
   1243                 """) { _, _ ->
   1244         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1245     }
   1246 
   1247     @Test
   1248     fun primaryKey_nullableEmbeddedObject() {
   1249         singleEntity(
   1250                 """
   1251                 public int id;
   1252 
   1253                 @Embedded(prefix = "bar_")
   1254                 @PrimaryKey
   1255                 public Foo foo;
   1256 
   1257                 static class Foo {
   1258                     public String a;
   1259                     public String b;
   1260                 }
   1261                 """) { _, _ ->
   1262         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a"))
   1263                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > b"))
   1264                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1265                 .and().withErrorCount(3)
   1266     }
   1267 
   1268     @Test
   1269     fun primaryKey_nullableEmbeddedObject_multipleParents() {
   1270         singleEntity(
   1271                 """
   1272                 public int id;
   1273 
   1274                 @Embedded(prefix = "bar_")
   1275                 @PrimaryKey
   1276                 public Foo foo;
   1277 
   1278                 static class Foo {
   1279                 @Embedded(prefix = "baz_")
   1280                 public Baz a;
   1281                 public String b;
   1282 
   1283                 static class Baz {
   1284                     public Integer bb;
   1285                 }
   1286             }
   1287                 """) { _, _ ->
   1288         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a"))
   1289                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > b"))
   1290                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1291                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a > bb"))
   1292                 .and().withErrorCount(4)
   1293     }
   1294 
   1295     @Test
   1296     fun primaryKey_nullableEmbeddedInherited() {
   1297         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1298                 """
   1299                 package foo.bar;
   1300                 import androidx.annotation.NonNull;
   1301                 import androidx.room.*;
   1302 
   1303                 public class Base {
   1304                     long baseId;
   1305                     String name, lastName;
   1306                     @Embedded(prefix = "bar_")
   1307                     @PrimaryKey
   1308                     public Foo foo;
   1309 
   1310                     static class Foo {
   1311                         public int a;
   1312                         public int b;
   1313                     }
   1314                 }
   1315                 """)
   1316         singleEntity(
   1317                 """
   1318                 public int id;
   1319                 """,
   1320                 baseClass = "foo.bar.Base",
   1321                 jfos = listOf(parent)) { _, _ ->
   1322         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1323                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a"))
   1324                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > b"))
   1325                 .and().withErrorCount(3)
   1326     }
   1327 
   1328     @Test
   1329     fun primaryKey_nullableOverrideViaEmbedded() {
   1330         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1331                 """
   1332                 package foo.bar;
   1333                 import androidx.room.*;
   1334 
   1335                 @Entity(primaryKeys = "baseId")
   1336                 public class Base {
   1337                     long baseId;
   1338                     String name, lastName;
   1339                 }
   1340                 """)
   1341         singleEntity(
   1342                 """
   1343                 public int id;
   1344                 @Embedded(prefix = "bar_")
   1345                 @PrimaryKey
   1346                 public Foo foo;
   1347 
   1348                 static class Foo {
   1349                     public int a;
   1350                     public int b;
   1351                 }
   1352                 """,
   1353                 baseClass = "foo.bar.Base",
   1354                 jfos = listOf(parent)) { _, _ ->
   1355         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1356                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a"))
   1357                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > b"))
   1358                 .and().withNoteContaining(
   1359                 "PrimaryKey[baseId] is overridden by PrimaryKey[foo > a, foo > b]")
   1360                 .and().withErrorCount(3)
   1361     }
   1362 
   1363     @Test
   1364     fun primaryKey_nullableOverrideEmbedded() {
   1365         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1366                 """
   1367                 package foo.bar;
   1368                 import androidx.annotation.NonNull;
   1369                 import androidx.room.*;
   1370 
   1371                 public class Base {
   1372                     long baseId;
   1373                     String name, lastName;
   1374                     @Embedded(prefix = "bar_")
   1375                     @PrimaryKey
   1376                     public Foo foo;
   1377 
   1378                     static class Foo {
   1379                         public int a;
   1380                         public int b;
   1381                     }
   1382                 }
   1383                 """)
   1384         singleEntity(
   1385                 """
   1386                 @PrimaryKey
   1387                 public int id;
   1388                 """,
   1389                 baseClass = "foo.bar.Base",
   1390                 jfos = listOf(parent)) { _, _ ->
   1391         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1392                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a"))
   1393                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > b"))
   1394                 .and().withNoteContaining(
   1395                 "PrimaryKey[foo > a, foo > b] is overridden by PrimaryKey[id]")
   1396                 .and().withErrorCount(3)
   1397     }
   1398 
   1399     @Test
   1400     fun primaryKey_integerOverrideEmbedded() {
   1401         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1402                 """
   1403                 package foo.bar;
   1404                 import androidx.annotation.NonNull;
   1405                 import androidx.room.*;
   1406 
   1407                 public class Base {
   1408                     long baseId;
   1409                     String name, lastName;
   1410                     @Embedded(prefix = "bar_")
   1411                     @PrimaryKey
   1412                     public Foo foo;
   1413 
   1414                     static class Foo {
   1415                         public Integer a;
   1416                     }
   1417                 }
   1418                 """)
   1419         singleEntity(
   1420                 """
   1421                 @PrimaryKey
   1422                 public int id;
   1423                 """,
   1424                 baseClass = "foo.bar.Base",
   1425                 jfos = listOf(parent)) { _, _ ->
   1426         }.compilesWithoutError().withNoteContaining(
   1427                 "PrimaryKey[foo > a] is overridden by PrimaryKey[id]")
   1428     }
   1429 
   1430     @Test
   1431     fun primaryKey_singleStringPrimaryKeyOverrideEmbedded() {
   1432         val parent = JavaFileObjects.forSourceLines("foo.bar.Base",
   1433                 """
   1434                 package foo.bar;
   1435                 import androidx.annotation.NonNull;
   1436                 import androidx.room.*;
   1437 
   1438                 public class Base {
   1439                     long baseId;
   1440                     String name, lastName;
   1441                     @Embedded(prefix = "bar_")
   1442                     @PrimaryKey
   1443                     public Foo foo;
   1444 
   1445                     static class Foo {
   1446                         public String a;
   1447                     }
   1448                 }
   1449                 """)
   1450         singleEntity(
   1451                 """
   1452                 @PrimaryKey
   1453                 public int id;
   1454                 """,
   1455                 baseClass = "foo.bar.Base",
   1456                 jfos = listOf(parent)) { _, _ ->
   1457         }.failsToCompile().withErrorContaining(ProcessorErrors.primaryKeyNull("foo"))
   1458                 .and().withErrorContaining(ProcessorErrors.primaryKeyNull("foo > a"))
   1459                 .and().withNoteContaining(
   1460                 "PrimaryKey[foo > a] is overridden by PrimaryKey[id]")
   1461                 .and().withErrorCount(2)
   1462     }
   1463 
   1464     @Test
   1465     fun relationInEntity() {
   1466         singleEntity(
   1467                 """
   1468                 @PrimaryKey
   1469                 int id;
   1470                 @Relation(parentColumn = "id", entityColumn = "uid")
   1471                 java.util.List<User> users;
   1472                 """, jfos = listOf(COMMON.USER)
   1473         ) { _, _ ->
   1474         }.failsToCompile().withErrorContaining(RELATION_IN_ENTITY)
   1475     }
   1476 
   1477     @Test
   1478     fun foreignKey_invalidAction() {
   1479         val annotation = mapOf(
   1480                 "foreignKeys" to """{@ForeignKey(
   1481                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1482                     parentColumns = "lastName",
   1483                     childColumns = "name",
   1484                     onDelete = 101
   1485                 )}""".trimIndent()
   1486         )
   1487         singleEntity(
   1488                 """
   1489                 @PrimaryKey
   1490                 int id;
   1491                 String name;
   1492                 """,
   1493                 attributes = annotation, jfos = listOf(COMMON.USER)
   1494         ) { _, _ ->
   1495         }.failsToCompile().withErrorContaining(ProcessorErrors.INVALID_FOREIGN_KEY_ACTION)
   1496     }
   1497 
   1498     @Test
   1499     fun foreignKey_badEntity() {
   1500         val annotation = mapOf(
   1501                 "foreignKeys" to """{@ForeignKey(
   1502                     entity = dsa.class,
   1503                     parentColumns = "lastName",
   1504                     childColumns = "name"
   1505                 )}""".trimIndent()
   1506         )
   1507         singleEntity(
   1508                 """
   1509                 @PrimaryKey
   1510                 int id;
   1511                 String name;
   1512                 """,
   1513                 attributes = annotation, jfos = listOf(COMMON.USER)
   1514         ) { _, _ ->
   1515         }.failsToCompile().withErrorContaining("cannot find symbol")
   1516     }
   1517 
   1518     @Test
   1519     fun foreignKey_notAnEntity() {
   1520         val annotation = mapOf(
   1521                 "foreignKeys" to """{@ForeignKey(
   1522                     entity = ${COMMON.NOT_AN_ENTITY_TYPE_NAME}.class,
   1523                     parentColumns = "lastName",
   1524                     childColumns = "name"
   1525                 )}""".trimIndent()
   1526         )
   1527         singleEntity(
   1528                 """
   1529                 @PrimaryKey
   1530                 int id;
   1531                 String name;
   1532                 """,
   1533                 attributes = annotation, jfos = listOf(COMMON.NOT_AN_ENTITY)
   1534         ) { _, _ ->
   1535         }.failsToCompile().withErrorContaining(ProcessorErrors.foreignKeyNotAnEntity(
   1536                 COMMON.NOT_AN_ENTITY_TYPE_NAME.toString()))
   1537     }
   1538 
   1539     @Test
   1540     fun foreignKey_invalidChildColumn() {
   1541         val annotation = mapOf(
   1542                 "foreignKeys" to """{@ForeignKey(
   1543                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1544                     parentColumns = "lastName",
   1545                     childColumns = "namex"
   1546                 )}""".trimIndent()
   1547         )
   1548         singleEntity(
   1549                 """
   1550                 @PrimaryKey
   1551                 int id;
   1552                 String name;
   1553                 """,
   1554                 attributes = annotation, jfos = listOf(COMMON.USER)
   1555         ) { _, _ ->
   1556         }.failsToCompile().withErrorContaining(ProcessorErrors.foreignKeyChildColumnDoesNotExist(
   1557                 "namex", listOf("id", "name")))
   1558     }
   1559 
   1560     @Test
   1561     fun foreignKey_columnCountMismatch() {
   1562         val annotation = mapOf(
   1563                 "foreignKeys" to """{@ForeignKey(
   1564                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1565                     parentColumns = "lastName",
   1566                     childColumns = {"name", "id"}
   1567                 )}""".trimIndent()
   1568         )
   1569         singleEntity(
   1570                 """
   1571                 @PrimaryKey
   1572                 int id;
   1573                 String name;
   1574                 """,
   1575                 attributes = annotation, jfos = listOf(COMMON.USER)
   1576         ) { _, _ ->
   1577         }.failsToCompile().withErrorContaining(ProcessorErrors.foreignKeyColumnNumberMismatch(
   1578                 listOf("name", "id"), listOf("lastName")))
   1579     }
   1580 
   1581     @Test
   1582     fun foreignKey_emptyChildColumns() {
   1583         val annotation = mapOf(
   1584                 "foreignKeys" to """{@ForeignKey(
   1585                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1586                     parentColumns = "lastName",
   1587                     childColumns = {}
   1588                 )}""".trimIndent()
   1589         )
   1590         singleEntity(
   1591                 """
   1592                 @PrimaryKey
   1593                 int id;
   1594                 String name;
   1595                 """,
   1596                 attributes = annotation, jfos = listOf(COMMON.USER)
   1597         ) { _, _ ->
   1598         }.failsToCompile().withErrorContaining(ProcessorErrors.FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST)
   1599     }
   1600 
   1601     @Test
   1602     fun foreignKey_emptyParentColumns() {
   1603         val annotation = mapOf(
   1604                 "foreignKeys" to """{@ForeignKey(
   1605                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1606                     parentColumns = {},
   1607                     childColumns = {"name"}
   1608                 )}""".trimIndent()
   1609         )
   1610         singleEntity(
   1611                 """
   1612                 @PrimaryKey
   1613                 int id;
   1614                 String name;
   1615                 """,
   1616                 attributes = annotation, jfos = listOf(COMMON.USER)
   1617         ) { _, _ ->
   1618         }.failsToCompile().withErrorContaining(ProcessorErrors.FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST)
   1619     }
   1620 
   1621     @Test
   1622     fun foreignKey_simple() {
   1623         val annotation = mapOf(
   1624                 "foreignKeys" to """{@ForeignKey(
   1625                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1626                     parentColumns = "lastName",
   1627                     childColumns = "name",
   1628                     onDelete = ForeignKey.SET_NULL,
   1629                     onUpdate = ForeignKey.CASCADE,
   1630                     deferred = true
   1631                 )}""".trimIndent()
   1632         )
   1633         singleEntity(
   1634                 """
   1635                 @PrimaryKey
   1636                 int id;
   1637                 String name;
   1638                 """,
   1639                 attributes = annotation, jfos = listOf(COMMON.USER)
   1640         ) { entity, _ ->
   1641             assertThat(entity.foreignKeys.size, `is`(1))
   1642             val fKey = entity.foreignKeys.first()
   1643             assertThat(fKey.parentTable, `is`("User"))
   1644             assertThat(fKey.parentColumns, `is`(listOf("lastName")))
   1645             assertThat(fKey.deferred, `is`(true))
   1646             assertThat(fKey.childFields.size, `is`(1))
   1647             val field = fKey.childFields.first()
   1648             assertThat(field.name, `is`("name"))
   1649         }.compilesWithoutError()
   1650     }
   1651 
   1652     @Test
   1653     fun foreignKey_dontDuplicationChildIndex_SingleColumn() {
   1654         val annotation = mapOf(
   1655                 "foreignKeys" to """{@ForeignKey(
   1656                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1657                     parentColumns = "lastName",
   1658                     childColumns = "name",
   1659                     onDelete = ForeignKey.SET_NULL,
   1660                     onUpdate = ForeignKey.CASCADE,
   1661                     deferred = true
   1662                 )}""".trimIndent(),
   1663                 "indices" to """@Index("name")"""
   1664         )
   1665         singleEntity(
   1666                 """
   1667                 @PrimaryKey
   1668                 int id;
   1669                 String name;
   1670                 """,
   1671                 attributes = annotation, jfos = listOf(COMMON.USER)
   1672         ) { _, _ ->
   1673         }.compilesWithoutWarnings()
   1674     }
   1675 
   1676     @Test
   1677     fun foreignKey_dontDuplicationChildIndex_MultipleColumns() {
   1678         val annotation = mapOf(
   1679                 "foreignKeys" to """{@ForeignKey(
   1680                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1681                     parentColumns = {"lastName", "name"},
   1682                     childColumns = {"lName", "name"},
   1683                     onDelete = ForeignKey.SET_NULL,
   1684                     onUpdate = ForeignKey.CASCADE,
   1685                     deferred = true
   1686                 )}""".trimIndent(),
   1687                 "indices" to """@Index({"lName", "name"})"""
   1688         )
   1689         singleEntity(
   1690                 """
   1691                 @PrimaryKey
   1692                 int id;
   1693                 String name;
   1694                 String lName;
   1695                 """,
   1696                 attributes = annotation, jfos = listOf(COMMON.USER)
   1697         ) { entity, _ ->
   1698             assertThat(entity.indices.size, `is`(1))
   1699         }.compilesWithoutWarnings()
   1700     }
   1701 
   1702     @Test
   1703     fun foreignKey_dontDuplicationChildIndex_WhenCovered() {
   1704         val annotation = mapOf(
   1705                 "foreignKeys" to """{@ForeignKey(
   1706                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1707                     parentColumns = {"lastName"},
   1708                     childColumns = {"name"},
   1709                     onDelete = ForeignKey.SET_NULL,
   1710                     onUpdate = ForeignKey.CASCADE,
   1711                     deferred = true
   1712                 )}""".trimIndent(),
   1713                 "indices" to """@Index({"name", "lName"})"""
   1714         )
   1715         singleEntity(
   1716                 """
   1717                 @PrimaryKey
   1718                 int id;
   1719                 String name;
   1720                 String lName;
   1721                 """,
   1722                 attributes = annotation, jfos = listOf(COMMON.USER)
   1723         ) { entity, _ ->
   1724             assertThat(entity.indices.size, `is`(1))
   1725         }.compilesWithoutWarnings()
   1726     }
   1727 
   1728     @Test
   1729     fun foreignKey_warnMissingChildIndex() {
   1730         val annotation = mapOf(
   1731                 "foreignKeys" to """{@ForeignKey(
   1732                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1733                     parentColumns = "lastName",
   1734                     childColumns = "name",
   1735                     onDelete = ForeignKey.SET_NULL,
   1736                     onUpdate = ForeignKey.CASCADE,
   1737                     deferred = true
   1738                 )}""".trimIndent()
   1739         )
   1740         singleEntity(
   1741                 """
   1742                 @PrimaryKey
   1743                 int id;
   1744                 String name;
   1745                 """,
   1746                 attributes = annotation, jfos = listOf(COMMON.USER)
   1747         ) { entity, _ ->
   1748             assertThat(entity.indices, `is`(emptyList()))
   1749         }.compilesWithoutError().withWarningContaining(
   1750                 ProcessorErrors.foreignKeyMissingIndexInChildColumn("name"))
   1751     }
   1752 
   1753     @Test
   1754     fun foreignKey_warnMissingChildrenIndex() {
   1755         val annotation = mapOf(
   1756                 "foreignKeys" to """{@ForeignKey(
   1757                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1758                     parentColumns = {"lastName", "name"},
   1759                     childColumns = {"lName", "name"}
   1760                 )}""".trimIndent()
   1761         )
   1762         singleEntity(
   1763                 """
   1764                 @PrimaryKey
   1765                 int id;
   1766                 String name;
   1767                 String lName;
   1768                 """,
   1769                 attributes = annotation, jfos = listOf(COMMON.USER)
   1770         ) { entity, _ ->
   1771             assertThat(entity.indices, `is`(emptyList()))
   1772         }.compilesWithoutError().withWarningContaining(
   1773                 ProcessorErrors.foreignKeyMissingIndexInChildColumns(listOf("lName", "name")))
   1774     }
   1775 
   1776     @Test
   1777     fun foreignKey_dontIndexIfAlreadyPrimaryKey() {
   1778         val annotation = mapOf(
   1779                 "foreignKeys" to """{@ForeignKey(
   1780                     entity = ${COMMON.USER_TYPE_NAME}.class,
   1781                     parentColumns = "lastName",
   1782                     childColumns = "id",
   1783                     onDelete = ForeignKey.SET_NULL,
   1784                     onUpdate = ForeignKey.CASCADE,
   1785                     deferred = true
   1786                 )}""".trimIndent()
   1787         )
   1788         singleEntity(
   1789                 """
   1790                 @PrimaryKey
   1791                 int id;
   1792                 String name;
   1793                 """,
   1794                 attributes = annotation, jfos = listOf(COMMON.USER)
   1795         ) { entity, _ ->
   1796             assertThat(entity.indices, `is`(emptyList()))
   1797         }.compilesWithoutWarnings()
   1798     }
   1799 
   1800     @Test
   1801     fun recursion_1Level() {
   1802         singleEntity(
   1803                 """
   1804                 @Embedded
   1805                 MyEntity myEntity;
   1806                 """) { _, _ ->
   1807         }.failsToCompile().withErrorContaining(
   1808                 ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format(
   1809                         "foo.bar.MyEntity -> foo.bar.MyEntity"))
   1810     }
   1811 
   1812     @Test
   1813     fun recursion_2Levels_embedToRelation() {
   1814         singleEntity(
   1815                 """
   1816                 int pojoId;
   1817                 @Embedded
   1818                 A a;
   1819 
   1820                 static class A {
   1821                     int entityId;
   1822                     @Relation(parentColumn = "pojoId", entityColumn = "entityId")
   1823                     MyEntity myEntity;
   1824                 }
   1825                 """) { _, _ ->
   1826         }.failsToCompile().withErrorContaining(
   1827                 ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format(
   1828                         "foo.bar.MyEntity -> foo.bar.MyEntity.A -> foo.bar.MyEntity"))
   1829     }
   1830 
   1831     @Test
   1832     fun recursion_2Levels_onlyEmbeds_entityToPojo() {
   1833         singleEntity(
   1834                 """
   1835                 @Embedded
   1836                 A a;
   1837 
   1838                 static class A {
   1839                     @Embedded
   1840                     MyEntity myEntity;
   1841                 }
   1842                 """) { _, _ ->
   1843         }.failsToCompile().withErrorContaining(
   1844                 ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format(
   1845                         "foo.bar.MyEntity -> foo.bar.MyEntity.A -> foo.bar.MyEntity"))
   1846     }
   1847 
   1848     @Test
   1849     fun recursion_2Levels_onlyEmbeds_onlyEntities() {
   1850         singleEntity(
   1851                 """
   1852                 @Embedded
   1853                 A a;
   1854 
   1855                 @Entity
   1856                 static class A {
   1857                     @Embedded
   1858                     MyEntity myEntity;
   1859                 }
   1860                 """) { _, _ ->
   1861         }.failsToCompile().withErrorContaining(
   1862                 ProcessorErrors.RECURSIVE_REFERENCE_DETECTED.format(
   1863                         "foo.bar.MyEntity -> foo.bar.MyEntity.A -> foo.bar.MyEntity"))
   1864     }
   1865 
   1866     @Test
   1867     fun okTableName() {
   1868         val annotation = mapOf("tableName" to "\"foo bar\"")
   1869         singleEntity(
   1870                 """
   1871                 @PrimaryKey
   1872                 int id;
   1873                 String name;
   1874                 """,
   1875                 attributes = annotation, jfos = listOf(COMMON.USER)
   1876         ) { _, _ ->
   1877         }.compilesWithoutError()
   1878     }
   1879 
   1880     @Test
   1881     fun badTableName() {
   1882         val annotation = mapOf("tableName" to """ "foo`bar" """)
   1883         singleEntity(
   1884                 """
   1885                 @PrimaryKey
   1886                 int id;
   1887                 String name;
   1888                 """,
   1889                 attributes = annotation, jfos = listOf(COMMON.USER)
   1890         ) { _, _ ->
   1891         }.failsToCompile().withErrorContaining(ProcessorErrors.INVALID_TABLE_NAME)
   1892     }
   1893 
   1894     @Test
   1895     fun badColumnName() {
   1896         singleEntity(
   1897                 """
   1898                 @PrimaryKey
   1899                 int id;
   1900                 @ColumnInfo(name = "\"foo bar\"")
   1901                 String name;
   1902                 """,
   1903                 jfos = listOf(COMMON.USER)
   1904         ) { _, _ ->
   1905         }.failsToCompile().withErrorContaining(ProcessorErrors.INVALID_COLUMN_NAME)
   1906     }
   1907 }
   1908