1 /* 2 * Copyright (C) 2023 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 android.adservices.lint.test 18 19 import android.adservices.lint.prod.RoomDatabaseMigrationDetector 20 import com.android.tools.lint.checks.infrastructure.LintDetectorTest 21 import com.android.tools.lint.checks.infrastructure.TestFile 22 import com.android.tools.lint.checks.infrastructure.TestLintTask 23 import com.android.tools.lint.detector.api.Detector 24 import com.android.tools.lint.detector.api.Issue 25 import org.junit.Test 26 import org.junit.runner.RunWith 27 import org.junit.runners.JUnit4 28 29 @RunWith(JUnit4::class) 30 class RoomDatabaseMigrationDetectorTest : LintDetectorTest() { getDetectornull31 override fun getDetector(): Detector = RoomDatabaseMigrationDetector() 32 33 override fun getIssues(): List<Issue> = 34 listOf( 35 RoomDatabaseMigrationDetector.ISSUE_ERROR, 36 RoomDatabaseMigrationDetector.ISSUE_WARNING 37 ) 38 39 override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) 40 41 @Test 42 fun testMigrationPath_happyCase() { 43 lint() 44 .files( 45 java( 46 """ 47 package com.android.adservices.service.common.fake.packagename; 48 49 import androidx.room.AutoMigration; 50 import androidx.room.Database; 51 import androidx.room.RoomDatabase; 52 53 @Database( 54 entities = {}, 55 autoMigrations = { 56 @AutoMigration(from = 1, to = 2), 57 }, 58 version = FakeDatabase.DATABASE_VERSION) 59 public class FakeDatabase extends RoomDatabase { 60 public static final int DATABASE_VERSION = 2; 61 public static final String DATABASE_NAME = "fake.db"; 62 63 // Singleton and dao declaration. 64 } 65 """ 66 ), 67 java( 68 """ 69 package com.android.adservices.data; 70 71 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 72 73 class RoomDatabaseRegistration { 74 FakeDatabase mFakeDatabase; 75 } 76 """ 77 ), 78 *stubs 79 ) 80 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 81 .run() 82 .expectClean() 83 } 84 85 @Test testMigrationPath_missingDatabaseAnnotationErrornull86 fun testMigrationPath_missingDatabaseAnnotationError() { 87 lint() 88 .files( 89 java( 90 """ 91 package com.android.adservices.service.common.fake.packagename; 92 93 import androidx.room.AutoMigration; 94 import androidx.room.Database; 95 import androidx.room.RoomDatabase; 96 97 public class FakeDatabase extends RoomDatabase { 98 public static final String DATABASE_NAME = "fake.db"; 99 100 // Singleton and dao declaration. 101 } 102 """ 103 ), 104 java( 105 """ 106 package com.android.adservices.data; 107 108 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 109 110 class RoomDatabaseRegistration { 111 FakeDatabase mFakeDatabase; 112 } 113 """ 114 ), 115 *stubs 116 ) 117 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 118 .run() 119 .expectContains(RoomDatabaseMigrationDetector.MISSING_DATABASE_ANNOTATION_ERROR) 120 .expectContains(createErrorCountString(1, 0)) 121 } 122 123 @Test testMigrationPath_missingMigrationPathAttributenull124 fun testMigrationPath_missingMigrationPathAttribute() { 125 lint() 126 .files( 127 java( 128 """ 129 package com.android.adservices.service.common.fake.packagename; 130 131 import androidx.room.AutoMigration; 132 import androidx.room.Database; 133 import androidx.room.RoomDatabase; 134 135 @Database( 136 entities = {}, 137 version = FakeDatabase.DATABASE_VERSION) 138 public class FakeDatabase extends RoomDatabase { 139 public static final int DATABASE_VERSION = 2; 140 public static final String DATABASE_NAME = "fake.db"; 141 142 // Singleton and dao declaration. 143 } 144 """ 145 ), 146 java( 147 """ 148 package com.android.adservices.data; 149 150 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 151 152 class RoomDatabaseRegistration { 153 FakeDatabase mFakeDatabase; 154 } 155 """ 156 ), 157 *stubs 158 ) 159 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 160 .run() 161 .expectContains(RoomDatabaseMigrationDetector.MISSING_AUTO_MIGRATION_ATTRIBUTE_ERROR) 162 .expectContains(createErrorCountString(1, 0)) 163 } 164 @Test testMigrationPath_incompleteMigrationPathnull165 fun testMigrationPath_incompleteMigrationPath() { 166 lint() 167 .files( 168 java( 169 """ 170 package com.android.adservices.service.common.fake.packagename; 171 172 import androidx.room.AutoMigration; 173 import androidx.room.Database; 174 import androidx.room.RoomDatabase; 175 176 @Database( 177 entities = {}, 178 autoMigrations = { 179 @AutoMigration(from = 1, to = 2), 180 }, 181 version = FakeDatabase.DATABASE_VERSION) 182 public class FakeDatabase extends RoomDatabase { 183 public static final int DATABASE_VERSION = 3; 184 public static final String DATABASE_NAME = "fake.db"; 185 186 // Singleton and dao declaration. 187 } 188 """ 189 ), 190 java( 191 """ 192 package com.android.adservices.data; 193 194 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 195 196 class RoomDatabaseRegistration { 197 FakeDatabase mFakeDatabase; 198 } 199 """ 200 ), 201 *stubs 202 ) 203 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 204 .run() 205 .expectContains(RoomDatabaseMigrationDetector.INCOMPLETE_MIGRATION_PATH_ERROR) 206 .expectContains(createErrorCountString(0, 1)) 207 } 208 209 @Test testMigrationPath_missingDatabaseVersionFieldInClassnull210 fun testMigrationPath_missingDatabaseVersionFieldInClass() { 211 lint() 212 .files( 213 java( 214 """ 215 package com.android.adservices.service.common.fake.packagename; 216 217 import androidx.room.AutoMigration; 218 import androidx.room.Database; 219 import androidx.room.RoomDatabase; 220 221 @Database( 222 entities = {}, 223 autoMigrations = { 224 @AutoMigration(from = 1, to = 2), 225 }, 226 version = 2) 227 public class FakeDatabase extends RoomDatabase { 228 public static final String DATABASE_NAME = "fake.db"; 229 230 // Singleton and dao declaration. 231 } 232 """ 233 ), 234 java( 235 """ 236 package com.android.adservices.data; 237 238 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 239 240 class RoomDatabaseRegistration { 241 FakeDatabase mFakeDatabase; 242 } 243 """ 244 ), 245 *stubs 246 ) 247 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 248 .run() 249 .expectContains(RoomDatabaseMigrationDetector.MISSING_DATABASE_VERSION_FIELD_ERROR) 250 .expectContains(createErrorCountString(1, 0)) 251 } 252 253 @Test testMigrationPath_missingDatabaseVersionAnnotationFieldnull254 fun testMigrationPath_missingDatabaseVersionAnnotationField() { 255 lint() 256 .files( 257 java( 258 """ 259 package com.android.adservices.service.common.fake.packagename; 260 261 import androidx.room.AutoMigration; 262 import androidx.room.Database; 263 import androidx.room.RoomDatabase; 264 265 @Database( 266 entities = {}, 267 autoMigrations = { 268 @AutoMigration(from = 1, to = 2), 269 }) 270 public class FakeDatabase extends RoomDatabase { 271 public static final String DATABASE_NAME = "fake.db"; 272 273 // Singleton and dao declaration. 274 } 275 """ 276 ), 277 java( 278 """ 279 package com.android.adservices.data; 280 281 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 282 283 class RoomDatabaseRegistration { 284 FakeDatabase mFakeDatabase; 285 } 286 """ 287 ), 288 *stubs 289 ) 290 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 291 .run() 292 .expectContains( 293 RoomDatabaseMigrationDetector.MISSING_DATABASE_VERSION_ANNOTATION_ATTRIBUTE_ERROR 294 ) 295 .expectContains(RoomDatabaseMigrationDetector.MISSING_DATABASE_VERSION_FIELD_ERROR) 296 .expectContains(createErrorCountString(2, 0)) 297 } 298 299 @Test testMigrationPath_failedToReferenceDatabaseVersionErrornull300 fun testMigrationPath_failedToReferenceDatabaseVersionError() { 301 lint() 302 .files( 303 java( 304 """ 305 package com.android.adservices.service.common.fake.packagename; 306 307 import androidx.room.AutoMigration; 308 import androidx.room.Database; 309 import androidx.room.RoomDatabase; 310 311 @Database( 312 entities = {}, 313 autoMigrations = { 314 @AutoMigration(from = 1, to = 2), 315 }, 316 version = 2) 317 public class FakeDatabase extends RoomDatabase { 318 public static final int DATABASE_VERSION = 2; 319 public static final String DATABASE_NAME = "fake.db"; 320 321 // Singleton and dao declaration. 322 } 323 """ 324 ), 325 java( 326 """ 327 package com.android.adservices.data; 328 329 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 330 331 class RoomDatabaseRegistration { 332 FakeDatabase mFakeDatabase; 333 } 334 """ 335 ), 336 *stubs 337 ) 338 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 339 .run() 340 .expectContains( 341 RoomDatabaseMigrationDetector.FAILED_REF_VERSION_FIELD_IN_ANNOTATION_ERROR 342 ) 343 .expectContains(createErrorCountString(1, 0)) 344 } 345 346 @Test testMigrationPath_schemaExportFalseErrornull347 fun testMigrationPath_schemaExportFalseError() { 348 lint() 349 .files( 350 java( 351 """ 352 package com.android.adservices.service.common.fake.packagename; 353 354 import androidx.room.AutoMigration; 355 import androidx.room.Database; 356 import androidx.room.RoomDatabase; 357 358 @Database( 359 entities = {}, 360 autoMigrations = { 361 @AutoMigration(from = 1, to = 2), 362 }, 363 version = FakeDatabase.DATABASE_VERSION, 364 exportSchema = false) 365 public class FakeDatabase extends RoomDatabase { 366 public static final int DATABASE_VERSION = 2; 367 public static final String DATABASE_NAME = "fake.db"; 368 369 // Singleton and dao declaration. 370 } 371 """ 372 ), 373 java( 374 """ 375 package com.android.adservices.data; 376 377 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 378 379 class RoomDatabaseRegistration { 380 FakeDatabase mFakeDatabase; 381 } 382 """ 383 ), 384 *stubs 385 ) 386 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 387 .run() 388 .expectContains(RoomDatabaseMigrationDetector.SCHEMA_EXPORT_FALSE_ERROR) 389 .expectContains(createErrorCountString(1, 0)) 390 } 391 392 @Test testMigrationPath_schemaExportTrue_happyCasenull393 fun testMigrationPath_schemaExportTrue_happyCase() { 394 lint() 395 .files( 396 java( 397 """ 398 package com.android.adservices.service.common.fake.packagename; 399 400 import androidx.room.AutoMigration; 401 import androidx.room.Database; 402 import androidx.room.RoomDatabase; 403 404 @Database( 405 entities = {}, 406 autoMigrations = { 407 @AutoMigration(from = 1, to = 2), 408 }, 409 version = FakeDatabase.DATABASE_VERSION, 410 exportSchema = true) 411 public class FakeDatabase extends RoomDatabase { 412 public static final int DATABASE_VERSION = 2; 413 public static final String DATABASE_NAME = "fake.db"; 414 415 // Singleton and dao declaration. 416 } 417 """ 418 ), 419 java( 420 """ 421 package com.android.adservices.data; 422 423 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 424 425 class RoomDatabaseRegistration { 426 FakeDatabase mFakeDatabase; 427 } 428 """ 429 ), 430 *stubs 431 ) 432 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 433 .run() 434 .expectClean() 435 } 436 437 @Test testMigrationPath_databaseNotRegisterednull438 fun testMigrationPath_databaseNotRegistered() { 439 lint() 440 .files( 441 java( 442 """ 443 package com.android.adservices.service.common.fake.packagename; 444 445 import androidx.room.AutoMigration; 446 import androidx.room.Database; 447 import androidx.room.RoomDatabase; 448 449 @Database( 450 entities = {}, 451 autoMigrations = { 452 @AutoMigration(from = 1, to = 2), 453 }, 454 version = FakeDatabase.DATABASE_VERSION) 455 public class FakeDatabase extends RoomDatabase { 456 public static final int DATABASE_VERSION = 2; 457 public static final String DATABASE_NAME = "fake.db"; 458 459 // Singleton and dao declaration. 460 } 461 """ 462 ), 463 java( 464 """ 465 package com.android.adservices.data; 466 467 import com.android.adservices.service.common.fake.packagename.FakeDatabase; 468 469 class RoomDatabaseRegistration { 470 } 471 """ 472 ), 473 *stubs 474 ) 475 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 476 .run() 477 .expectContains( 478 RoomDatabaseMigrationDetector.DATABASE_NOT_REGISTERED_ERROR.format( 479 "com.android.adservices.service.common.fake.packagename.FakeDatabase" 480 ) 481 ) 482 .expectContains(createErrorCountString(1, 0)) 483 } 484 485 @Test testMigrationPath_databaseRegisterClassMissingnull486 fun testMigrationPath_databaseRegisterClassMissing() { 487 lint() 488 .files( 489 java( 490 """ 491 package com.android.adservices.service.common.fake.packagename; 492 493 import androidx.room.AutoMigration; 494 import androidx.room.Database; 495 import androidx.room.RoomDatabase; 496 497 @Database( 498 entities = {}, 499 autoMigrations = { 500 @AutoMigration(from = 1, to = 2), 501 }, 502 version = FakeDatabase.DATABASE_VERSION) 503 public class FakeDatabase extends RoomDatabase { 504 public static final int DATABASE_VERSION = 2; 505 public static final String DATABASE_NAME = "fake.db"; 506 507 // Singleton and dao declaration. 508 } 509 """ 510 ), 511 *stubs 512 ) 513 .issues(RoomDatabaseMigrationDetector.ISSUE_ERROR) 514 .run() 515 .expectContains(RoomDatabaseMigrationDetector.DATABASE_REGISTRATION_CLASS_MISSING_ERROR) 516 .expectContains(createErrorCountString(1, 0)) 517 } 518 519 @Test testMigrationPath_noDatabaseClass_happyCasenull520 fun testMigrationPath_noDatabaseClass_happyCase() { 521 lint().files(*stubs).issues(RoomDatabaseMigrationDetector.ISSUE_ERROR).run().expectClean() 522 } 523 524 private val database: TestFile = 525 kotlin( 526 """ 527 package androidx.room 528 529 @kotlin.annotation.Target @kotlin.annotation.Retention public final annotation class Database public constructor(entities: kotlin.Array<kotlin.reflect.KClass<*>> /* = compiled code */, views: kotlin.Array<kotlin.reflect.KClass<*>> /* = compiled code */, version: kotlin.Int, exportSchema: kotlin.Boolean /* = compiled code */, autoMigrations: kotlin.Array<androidx.room.AutoMigration> /* = compiled code */) : kotlin.Annotation { 530 public final val autoMigrations: kotlin.Array<androidx.room.AutoMigration> /* compiled code */ 531 532 public final val entities: kotlin.Array<kotlin.reflect.KClass<*>> /* compiled code */ 533 534 public final val exportSchema: kotlin.Boolean /* compiled code */ 535 536 public final val version: kotlin.Int /* compiled code */ 537 538 public final val views: kotlin.Array<kotlin.reflect.KClass<*>> /* compiled code */ 539 } 540 """ 541 ) 542 543 private val autoMigration: TestFile = 544 kotlin( 545 """ 546 package androidx.room 547 548 @kotlin.annotation.Target @kotlin.annotation.Retention public final annotation class AutoMigration public constructor(from: kotlin.Int, to: kotlin.Int, spec: kotlin.reflect.KClass<*> /* = compiled code */) : kotlin.Annotation { 549 public final val from: kotlin.Int /* compiled code */ 550 551 public final val spec: kotlin.reflect.KClass<*> /* compiled code */ 552 553 public final val to: kotlin.Int /* compiled code */ 554 }""" 555 ) 556 557 private val roomDatabase: TestFile = 558 kotlin( 559 """ 560 package androidx.room 561 562 abstract class RoomDatabase 563 """ 564 .trimIndent() 565 ) 566 567 private val stubs = arrayOf(database, roomDatabase, autoMigration) 568 createErrorCountStringnull569 private fun createErrorCountString(errors: Int, warnings: Int): String { 570 return "%d errors, %d warnings".format(errors, warnings) 571 } 572 } 573