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