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 17package androidx.room.processor 18 19import COMMON 20import androidx.room.RoomProcessor 21import androidx.room.solver.query.result.EntityRowAdapter 22import androidx.room.solver.query.result.PojoRowAdapter 23import androidx.room.testing.TestInvocation 24import androidx.room.testing.TestProcessor 25import androidx.room.vo.Database 26import androidx.room.vo.Warning 27import com.google.auto.common.MoreElements 28import com.google.common.truth.Truth 29import com.google.testing.compile.CompileTester 30import com.google.testing.compile.JavaFileObjects 31import com.google.testing.compile.JavaSourcesSubjectFactory 32import com.squareup.javapoet.ClassName 33import org.hamcrest.CoreMatchers.`is` 34import org.hamcrest.CoreMatchers.instanceOf 35import org.hamcrest.CoreMatchers.not 36import org.hamcrest.CoreMatchers.notNullValue 37import org.hamcrest.CoreMatchers.sameInstance 38import org.hamcrest.MatcherAssert.assertThat 39import org.junit.Test 40import org.junit.runner.RunWith 41import org.junit.runners.JUnit4 42import javax.tools.JavaFileObject 43import javax.tools.StandardLocation 44 45@RunWith(JUnit4::class) 46class DatabaseProcessorTest { 47 companion object { 48 const val DATABASE_PREFIX = """ 49 package foo.bar; 50 import androidx.room.*; 51 """ 52 val DB1: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Db1", 53 """ 54 $DATABASE_PREFIX 55 @Database(entities = {Book.class}, version = 42) 56 public abstract class Db1 extends RoomDatabase { 57 abstract BookDao bookDao(); 58 } 59 """) 60 val DB2: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Db2", 61 """ 62 $DATABASE_PREFIX 63 @Database(entities = {Book.class}, version = 42) 64 public abstract class Db2 extends RoomDatabase { 65 abstract BookDao bookDao(); 66 } 67 """) 68 val DB3: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Db3", 69 """ 70 $DATABASE_PREFIX 71 @Database(entities = {Book.class}, version = 42) 72 public abstract class Db3 extends RoomDatabase { 73 } 74 """) 75 val USER: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.User", 76 """ 77 package foo.bar; 78 import androidx.room.*; 79 @Entity 80 public class User { 81 @PrimaryKey 82 int uid; 83 } 84 """) 85 val USER_DAO: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.UserDao", 86 """ 87 package foo.bar; 88 import androidx.room.*; 89 @Dao 90 public interface UserDao { 91 @Query("SELECT * FROM user") 92 public java.util.List<User> loadAll(); 93 94 @Insert 95 public void insert(User... users); 96 97 @Query("SELECT * FROM user where uid = :uid") 98 public User loadOne(int uid); 99 100 @TypeConverters(Converter.class) 101 @Query("SELECT * FROM user where uid = :uid") 102 public User loadWithConverter(int uid); 103 104 @Query("SELECT * FROM user where uid = :uid") 105 public Pojo loadOnePojo(int uid); 106 107 @Query("SELECT * FROM user") 108 public java.util.List<Pojo> loadAllPojos(); 109 110 @TypeConverters(Converter.class) 111 @Query("SELECT * FROM user where uid = :uid") 112 public Pojo loadPojoWithConverter(int uid); 113 114 public static class Converter { 115 @TypeConverter 116 public static java.util.Date foo(Long input) {return null;} 117 } 118 119 public static class Pojo { 120 public int uid; 121 } 122 } 123 """) 124 val BOOK: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.Book", 125 """ 126 package foo.bar; 127 import androidx.room.*; 128 @Entity 129 public class Book { 130 @PrimaryKey 131 int bookId; 132 } 133 """) 134 val BOOK_DAO: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.BookDao", 135 """ 136 package foo.bar; 137 import androidx.room.*; 138 @Dao 139 public interface BookDao { 140 @Query("SELECT * FROM book") 141 public java.util.List<Book> loadAllBooks(); 142 @Insert 143 public void insert(Book book); 144 } 145 """) 146 } 147 148 @Test 149 fun simple() { 150 singleDb(""" 151 @Database(entities = {User.class}, version = 42) 152 public abstract class MyDb extends RoomDatabase { 153 abstract UserDao userDao(); 154 } 155 """, USER, USER_DAO) { db, _ -> 156 assertThat(db.daoMethods.size, `is`(1)) 157 assertThat(db.entities.size, `is`(1)) 158 }.compilesWithoutError() 159 } 160 161 @Test 162 fun multiple() { 163 singleDb(""" 164 @Database(entities = {User.class, Book.class}, version = 42) 165 public abstract class MyDb extends RoomDatabase { 166 abstract UserDao userDao(); 167 abstract BookDao bookDao(); 168 } 169 """, USER, USER_DAO, BOOK, BOOK_DAO) { db, _ -> 170 assertThat(db.daoMethods.size, `is`(2)) 171 assertThat(db.entities.size, `is`(2)) 172 assertThat(db.daoMethods.map { it.name }, `is`(listOf("userDao", "bookDao"))) 173 assertThat(db.entities.map { it.type.toString() }, 174 `is`(listOf("foo.bar.User", "foo.bar.Book"))) 175 }.compilesWithoutError() 176 } 177 178 @Test 179 fun detectMissingBaseClass() { 180 singleDb(""" 181 @Database(entities = {User.class, Book.class}, version = 42) 182 public abstract class MyDb { 183 } 184 """, USER, BOOK) { _, _ -> 185 }.failsToCompile().withErrorContaining(ProcessorErrors.DB_MUST_EXTEND_ROOM_DB) 186 } 187 188 @Test 189 fun detectMissingTable() { 190 singleDb( 191 """ 192 @Database(entities = {Book.class}, version = 42) 193 public abstract class MyDb extends RoomDatabase { 194 abstract BookDao bookDao(); 195 } 196 """, BOOK, JavaFileObjects.forSourceString("foo.bar.BookDao", 197 """ 198 package foo.bar; 199 import androidx.room.*; 200 @Dao 201 public interface BookDao { 202 @Query("SELECT * FROM nonExistentTable") 203 public java.util.List<Book> loadAllBooks(); 204 } 205 """)) { _, _ -> 206 }.failsToCompile().withErrorContaining("no such table: nonExistentTable") 207 } 208 209 @Test 210 fun detectDuplicateTableNames() { 211 singleDb(""" 212 @Database(entities = {User.class, AnotherClass.class}, version = 42) 213 public abstract class MyDb extends RoomDatabase { 214 abstract UserDao userDao(); 215 } 216 """, USER, USER_DAO, JavaFileObjects.forSourceString("foo.bar.AnotherClass", 217 """ 218 package foo.bar; 219 import androidx.room.*; 220 @Entity(tableName="user") 221 public class AnotherClass { 222 @PrimaryKey 223 int uid; 224 } 225 """)) { _, _ -> 226 }.failsToCompile().withErrorContaining( 227 ProcessorErrors.duplicateTableNames("user", 228 listOf("foo.bar.User", "foo.bar.AnotherClass")) 229 ) 230 } 231 232 @Test 233 fun skipBadQueryVerification() { 234 singleDb( 235 """ 236 @SkipQueryVerification 237 @Database(entities = {Book.class}, version = 42) 238 public abstract class MyDb extends RoomDatabase { 239 abstract BookDao bookDao(); 240 } 241 """, BOOK, JavaFileObjects.forSourceString("foo.bar.BookDao", 242 """ 243 package foo.bar; 244 import androidx.room.*; 245 @Dao 246 public interface BookDao { 247 @Query("SELECT nonExistingField FROM Book") 248 public java.util.List<Book> loadAllBooks(); 249 } 250 """)) { _, _ -> 251 }.compilesWithoutError() 252 } 253 254 @Test 255 fun multipleDatabases() { 256 val db1_2 = JavaFileObjects.forSourceString("foo.barx.Db1", 257 """ 258 package foo.barx; 259 import androidx.room.*; 260 import foo.bar.*; 261 @Database(entities = {Book.class}, version = 42) 262 public abstract class Db1 extends RoomDatabase { 263 abstract BookDao bookDao(); 264 } 265 """) 266 Truth.assertAbout(JavaSourcesSubjectFactory.javaSources()) 267 .that(listOf(BOOK, BOOK_DAO, DB1, DB2, db1_2)) 268 .processedWith(RoomProcessor()) 269 .compilesWithoutError() 270 .and() 271 .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", "Db1_Impl.class") 272 .and() 273 .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", "Db2_Impl.class") 274 .and() 275 .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.barx", "Db1_Impl.class") 276 .and() 277 .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", 278 "BookDao_Db1_0_Impl.class") 279 .and() 280 .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", 281 "BookDao_Db1_1_Impl.class") 282 .and() 283 .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", 284 "BookDao_Db2_Impl.class") 285 } 286 287 @Test 288 fun twoDaoMethodsForTheSameDao() { 289 singleDb( 290 """ 291 @Database(entities = {User.class}, version = 42) 292 public abstract class MyDb extends RoomDatabase { 293 abstract UserDao userDao(); 294 abstract UserDao userDao2(); 295 } 296 """, USER, USER_DAO) { _, _ -> } 297 .failsToCompile() 298 .withErrorContaining(ProcessorErrors.DAO_METHOD_CONFLICTS_WITH_OTHERS) 299 .and() 300 .withErrorContaining(ProcessorErrors.duplicateDao( 301 ClassName.get("foo.bar", "UserDao"), listOf("userDao", "userDao2") 302 )) 303 } 304 305 @Test 306 fun suppressedWarnings() { 307 singleDb( 308 """ 309 @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH) 310 @Database(entities = {User.class}, version = 42) 311 public abstract class MyDb extends RoomDatabase { 312 abstract UserDao userDao(); 313 } 314 """, USER, USER_DAO) { db, invocation -> 315 assertThat(DatabaseProcessor(invocation.context, db.element) 316 .context.logger.suppressedWarnings, `is`(setOf(Warning.CURSOR_MISMATCH))) 317 }.compilesWithoutError() 318 } 319 320 @Test 321 fun duplicateIndexNames() { 322 val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1", 323 """ 324 package foo.bar; 325 import androidx.room.*; 326 @Entity(indices = {@Index(name ="index_name", value = {"name"})}) 327 public class Entity1 { 328 @PrimaryKey 329 int uid; 330 String name; 331 } 332 """) 333 334 val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2", 335 """ 336 package foo.bar; 337 import androidx.room.*; 338 @Entity(indices = {@Index(name ="index_name", value = {"anotherName"})}) 339 public class Entity2 { 340 @PrimaryKey 341 int uid; 342 String anotherName; 343 } 344 """) 345 singleDb(""" 346 @Database(entities = {Entity1.class, Entity2.class}, version = 42) 347 public abstract class MyDb extends RoomDatabase { 348 } 349 """, entity1, entity2) { _, _ -> 350 }.failsToCompile().withErrorContaining( 351 ProcessorErrors.duplicateIndexInDatabase("index_name", 352 listOf("foo.bar.Entity1 > index_name", "foo.bar.Entity2 > index_name")) 353 ) 354 } 355 356 @Test 357 fun foreignKey_missingParent() { 358 val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1", 359 """ 360 package foo.bar; 361 import androidx.room.*; 362 @Entity(foreignKeys = @ForeignKey(entity = ${COMMON.USER_TYPE_NAME}.class, 363 parentColumns = "lastName", 364 childColumns = "name")) 365 public class Entity1 { 366 @PrimaryKey 367 int uid; 368 String name; 369 } 370 """) 371 singleDb(""" 372 @Database(entities = {Entity1.class}, version = 42) 373 public abstract class MyDb extends RoomDatabase { 374 } 375 """, entity1, COMMON.USER) { _, _ -> 376 }.failsToCompile().withErrorContaining( 377 ProcessorErrors.foreignKeyMissingParentEntityInDatabase("User", "foo.bar.Entity1") 378 ) 379 } 380 381 @Test 382 fun foreignKey_missingParentIndex() { 383 val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1", 384 """ 385 package foo.bar; 386 import androidx.room.*; 387 @Entity(foreignKeys = @ForeignKey(entity = ${COMMON.USER_TYPE_NAME}.class, 388 parentColumns = "lastName", 389 childColumns = "name")) 390 public class Entity1 { 391 @PrimaryKey 392 int uid; 393 String name; 394 } 395 """) 396 singleDb(""" 397 @Database(entities = {Entity1.class, User.class}, version = 42) 398 public abstract class MyDb extends RoomDatabase { 399 } 400 """, entity1, COMMON.USER) { _, _ -> 401 }.failsToCompile().withErrorContaining( 402 ProcessorErrors.foreignKeyMissingIndexInParent( 403 parentEntity = COMMON.USER_TYPE_NAME.toString(), 404 parentColumns = listOf("lastName"), 405 childEntity = "foo.bar.Entity1", 406 childColumns = listOf("name") 407 ) 408 ) 409 } 410 411 @Test 412 fun foreignKey_goodWithPrimaryKey() { 413 val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1", 414 """ 415 package foo.bar; 416 import androidx.room.*; 417 @Entity(foreignKeys = @ForeignKey(entity = Entity2.class, 418 parentColumns = "uid", 419 childColumns = "parentId")) 420 public class Entity1 { 421 @PrimaryKey 422 int uid; 423 int parentId; 424 String name; 425 } 426 """) 427 428 val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2", 429 """ 430 package foo.bar; 431 import androidx.room.*; 432 @Entity 433 public class Entity2 { 434 @PrimaryKey 435 int uid; 436 String anotherName; 437 } 438 """) 439 singleDb(""" 440 @Database(entities = {Entity1.class, Entity2.class}, version = 42) 441 public abstract class MyDb extends RoomDatabase { 442 } 443 """, entity1, entity2) { _, _ -> 444 }.compilesWithoutError() 445 } 446 447 @Test 448 fun foreignKey_missingParentColumn() { 449 val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1", 450 """ 451 package foo.bar; 452 import androidx.room.*; 453 @Entity(foreignKeys = @ForeignKey(entity = Entity2.class, 454 parentColumns = {"anotherName", "anotherName2"}, 455 childColumns = {"name", "name2"})) 456 public class Entity1 { 457 @PrimaryKey 458 int uid; 459 String name; 460 String name2; 461 } 462 """) 463 464 val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2", 465 """ 466 package foo.bar; 467 import androidx.room.*; 468 @Entity 469 public class Entity2 { 470 @PrimaryKey 471 int uid; 472 String anotherName; 473 } 474 """) 475 singleDb(""" 476 @Database(entities = {Entity1.class, Entity2.class}, version = 42) 477 public abstract class MyDb extends RoomDatabase { 478 } 479 """, entity1, entity2) { _, _ -> 480 }.failsToCompile().withErrorContaining( 481 ProcessorErrors.foreignKeyParentColumnDoesNotExist("foo.bar.Entity2", 482 "anotherName2", listOf("uid", "anotherName")) 483 ) 484 } 485 486 @Test 487 fun foreignKey_goodWithIndex() { 488 val entity1 = JavaFileObjects.forSourceString("foo.bar.Entity1", 489 """ 490 package foo.bar; 491 import androidx.room.*; 492 @Entity(foreignKeys = @ForeignKey(entity = Entity2.class, 493 parentColumns = {"anotherName", "anotherName2"}, 494 childColumns = {"name", "name2"})) 495 public class Entity1 { 496 @PrimaryKey 497 int uid; 498 String name; 499 String name2; 500 } 501 """) 502 503 val entity2 = JavaFileObjects.forSourceString("foo.bar.Entity2", 504 """ 505 package foo.bar; 506 import androidx.room.*; 507 @Entity(indices = @Index(value = {"anotherName2", "anotherName"}, unique = true)) 508 public class Entity2 { 509 @PrimaryKey 510 int uid; 511 String anotherName; 512 String anotherName2; 513 } 514 """) 515 singleDb(""" 516 @Database(entities = {Entity1.class, Entity2.class}, version = 42) 517 public abstract class MyDb extends RoomDatabase { 518 } 519 """, entity1, entity2) { _, _ -> 520 }.compilesWithoutError() 521 } 522 523 @Test 524 fun insertNotAReferencedEntity() { 525 singleDb(""" 526 @Database(entities = {User.class}, version = 42) 527 public abstract class MyDb extends RoomDatabase { 528 abstract BookDao bookDao(); 529 } 530 """, USER, USER_DAO, BOOK, BOOK_DAO) { _, _ -> 531 }.failsToCompile().withErrorContaining( 532 ProcessorErrors.shortcutEntityIsNotInDatabase( 533 database = "foo.bar.MyDb", 534 dao = "foo.bar.BookDao", 535 entity = "foo.bar.Book" 536 ) 537 ) 538 } 539 540 @Test 541 fun cache_entity() { 542 singleDb(""" 543 @Database(entities = {User.class}, version = 42) 544 @SkipQueryVerification 545 public abstract class MyDb extends RoomDatabase { 546 public abstract MyUserDao userDao(); 547 @Dao 548 interface MyUserDao { 549 @Insert 550 public void insert(User... users); 551 552 @Query("SELECT * FROM user where uid = :uid") 553 public User loadOne(int uid); 554 555 @TypeConverters(Converter.class) 556 @Query("SELECT * FROM user where uid = :uid") 557 public User loadWithConverter(int uid); 558 } 559 public static class Converter { 560 @TypeConverter 561 public static java.util.Date foo(Long input) {return null;} 562 } 563 } 564 """, USER, USER_DAO) { db, _ -> 565 val userDao = db.daoMethods.first().dao 566 val insertionMethod = userDao.insertionMethods.find { it.name == "insert" } 567 assertThat(insertionMethod, notNullValue()) 568 val loadOne = userDao.queryMethods.find { it.name == "loadOne" } 569 assertThat(loadOne, notNullValue()) 570 val adapter = loadOne?.queryResultBinder?.adapter?.rowAdapter 571 assertThat("test sanity", adapter, instanceOf(EntityRowAdapter::class.java)) 572 val adapterEntity = (adapter as EntityRowAdapter).entity 573 assertThat(insertionMethod?.entities?.values?.first(), sameInstance(adapterEntity)) 574 575 val withConverter = userDao.queryMethods.find { it.name == "loadWithConverter" } 576 assertThat(withConverter, notNullValue()) 577 val convAdapter = withConverter?.queryResultBinder?.adapter?.rowAdapter 578 assertThat("test sanity", adapter, instanceOf(EntityRowAdapter::class.java)) 579 val convAdapterEntity = (convAdapter as EntityRowAdapter).entity 580 assertThat(insertionMethod?.entities?.values?.first(), 581 not(sameInstance(convAdapterEntity))) 582 583 assertThat(convAdapterEntity, notNullValue()) 584 assertThat(adapterEntity, notNullValue()) 585 }.compilesWithoutError() 586 } 587 588 @Test 589 fun cache_pojo() { 590 singleDb(""" 591 @Database(entities = {User.class}, version = 42) 592 public abstract class MyDb extends RoomDatabase { 593 public abstract UserDao userDao(); 594 } 595 """, USER, USER_DAO) { db, _ -> 596 val userDao = db.daoMethods.first().dao 597 val loadOne = userDao.queryMethods.find { it.name == "loadOnePojo" } 598 assertThat(loadOne, notNullValue()) 599 val adapter = loadOne?.queryResultBinder?.adapter?.rowAdapter 600 assertThat("test sanity", adapter, instanceOf(PojoRowAdapter::class.java)) 601 val adapterPojo = (adapter as PojoRowAdapter).pojo 602 603 val loadAll = userDao.queryMethods.find { it.name == "loadAllPojos" } 604 assertThat(loadAll, notNullValue()) 605 val loadAllAdapter = loadAll?.queryResultBinder?.adapter?.rowAdapter 606 assertThat("test sanity", loadAllAdapter, instanceOf(PojoRowAdapter::class.java)) 607 val loadAllPojo = (loadAllAdapter as PojoRowAdapter).pojo 608 assertThat(adapter, not(sameInstance(loadAllAdapter))) 609 assertThat(adapterPojo, sameInstance(loadAllPojo)) 610 611 val withConverter = userDao.queryMethods.find { it.name == "loadPojoWithConverter" } 612 assertThat(withConverter, notNullValue()) 613 val convAdapter = withConverter?.queryResultBinder?.adapter?.rowAdapter 614 assertThat("test sanity", adapter, instanceOf(PojoRowAdapter::class.java)) 615 val convAdapterPojo = (convAdapter as PojoRowAdapter).pojo 616 assertThat(convAdapterPojo, notNullValue()) 617 assertThat(convAdapterPojo, not(sameInstance(adapterPojo))) 618 }.compilesWithoutError() 619 } 620 621 @Test 622 fun daoConstructor_RoomDatabase() { 623 assertConstructor(listOf(DB1), "BookDao(RoomDatabase db) {}") 624 .compilesWithoutError() 625 } 626 627 @Test 628 fun daoConstructor_specificDatabase() { 629 assertConstructor(listOf(DB1), "BookDao(Db1 db) {}") 630 .compilesWithoutError() 631 } 632 633 @Test 634 fun daoConstructor_wrongDatabase() { 635 assertConstructor(listOf(DB1, DB3), "BookDao(Db3 db) {}") 636 .failsToCompile() 637 .withErrorContaining(ProcessorErrors 638 .daoMustHaveMatchingConstructor("foo.bar.BookDao", "foo.bar.Db1")) 639 } 640 641 @Test 642 fun daoConstructor_multipleDatabases_RoomDatabase() { 643 assertConstructor(listOf(DB1, DB2), "BookDao(RoomDatabase db) {}") 644 .compilesWithoutError() 645 } 646 647 @Test 648 fun daoConstructor_multipleDatabases_specificDatabases() { 649 assertConstructor(listOf(DB1, DB2), """ 650 BookDao(Db1 db) {} 651 BookDao(Db2 db) {} 652 """) 653 .compilesWithoutError() 654 } 655 656 @Test 657 fun daoConstructor_multipleDatabases_empty() { 658 assertConstructor(listOf(DB1, DB2), """ 659 BookDao(Db1 db) {} 660 BookDao() {} // Db2 uses this 661 """) 662 .compilesWithoutError() 663 } 664 665 @Test 666 fun daoConstructor_multipleDatabases_noMatch() { 667 assertConstructor(listOf(DB1, DB2), """ 668 BookDao(Db1 db) {} 669 """) 670 .failsToCompile() 671 .withErrorContaining(ProcessorErrors 672 .daoMustHaveMatchingConstructor("foo.bar.BookDao", "foo.bar.Db2")) 673 } 674 675 fun assertConstructor(dbs: List<JavaFileObject>, constructor: String): CompileTester { 676 val bookDao = JavaFileObjects.forSourceString("foo.bar.BookDao", 677 """ 678 package foo.bar; 679 import androidx.room.*; 680 @Dao 681 public abstract class BookDao { 682 $constructor 683 } 684 """) 685 return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources()) 686 .that(listOf(BOOK, bookDao) + dbs) 687 .processedWith(RoomProcessor()) 688 } 689 690 fun singleDb(input: String, vararg otherFiles: JavaFileObject, 691 handler: (Database, TestInvocation) -> Unit): CompileTester { 692 return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources()) 693 .that(otherFiles.toMutableList() 694 + JavaFileObjects.forSourceString("foo.bar.MyDb", 695 DATABASE_PREFIX + input 696 )) 697 .processedWith(TestProcessor.builder() 698 .forAnnotations(androidx.room.Database::class) 699 .nextRunHandler { invocation -> 700 val entity = invocation.roundEnv 701 .getElementsAnnotatedWith( 702 androidx.room.Database::class.java) 703 .first() 704 val parser = DatabaseProcessor(invocation.context, 705 MoreElements.asType(entity)) 706 val parsedDb = parser.process() 707 handler(parsedDb, invocation) 708 true 709 } 710 .build()) 711 } 712} 713