1 /* <lambda>null2 * Copyright 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 androidx.baselineprofile.gradle.consumer 18 19 import androidx.baselineprofile.gradle.utils.ANDROID_APPLICATION_PLUGIN 20 import androidx.baselineprofile.gradle.utils.ANDROID_LIBRARY_PLUGIN 21 import androidx.baselineprofile.gradle.utils.ANDROID_TEST_PLUGIN 22 import androidx.baselineprofile.gradle.utils.BaselineProfileProjectSetupRule 23 import androidx.baselineprofile.gradle.utils.EXPECTED_PROFILE_FOLDER 24 import androidx.baselineprofile.gradle.utils.Fixtures 25 import androidx.baselineprofile.gradle.utils.TestAgpVersion 26 import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_8_1_1 27 import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_8_3_1 28 import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_CURRENT 29 import androidx.baselineprofile.gradle.utils.VariantProfile 30 import androidx.baselineprofile.gradle.utils.build 31 import androidx.baselineprofile.gradle.utils.buildAndAssertThatOutput 32 import androidx.baselineprofile.gradle.utils.buildAndFailAndAssertThatOutput 33 import androidx.baselineprofile.gradle.utils.camelCase 34 import androidx.baselineprofile.gradle.utils.require 35 import androidx.baselineprofile.gradle.utils.requireInOrder 36 import androidx.baselineprofile.gradle.utils.toUri 37 import com.google.common.truth.Truth.assertThat 38 import com.google.common.truth.Truth.assertWithMessage 39 import java.io.File 40 import org.junit.Rule 41 import org.junit.Test 42 import org.junit.runner.RunWith 43 import org.junit.runners.Parameterized 44 45 @RunWith(Parameterized::class) 46 class BaselineProfileConsumerPluginTest(private val agpVersion: TestAgpVersion) { 47 48 companion object { 49 @Parameterized.Parameters(name = "agpVersion={0}") 50 @JvmStatic 51 fun parameters() = TestAgpVersion.all() 52 } 53 54 @get:Rule 55 val projectSetup = BaselineProfileProjectSetupRule(forceAgpVersion = agpVersion.versionString) 56 57 private val gradleRunner by lazy { projectSetup.consumer.gradleRunner } 58 59 private fun baselineProfileFile(variantName: String) = 60 projectSetup.baselineProfileFile(variantName) 61 62 private fun startupProfileFile(variantName: String) = 63 projectSetup.startupProfileFile(variantName) 64 65 private fun mergedArtProfile(variantName: String) = projectSetup.mergedArtProfile(variantName) 66 67 private fun readBaselineProfileFileContent(variantName: String) = 68 projectSetup.readBaselineProfileFileContent(variantName) 69 70 private fun readStartupProfileFileContent(variantName: String) = 71 projectSetup.readStartupProfileFileContent(variantName) 72 73 @Test 74 fun testGenerateTaskWithNoFlavorsForLibrary() { 75 projectSetup.consumer.setup(androidPlugin = ANDROID_LIBRARY_PLUGIN) 76 projectSetup.producer.setupWithoutFlavors( 77 releaseProfileLines = 78 listOf( 79 Fixtures.CLASS_1_METHOD_1, 80 Fixtures.CLASS_1, 81 Fixtures.CLASS_2_METHOD_1, 82 Fixtures.CLASS_2 83 ), 84 releaseStartupProfileLines = 85 listOf( 86 Fixtures.CLASS_3_METHOD_1, 87 Fixtures.CLASS_3, 88 Fixtures.CLASS_4_METHOD_1, 89 Fixtures.CLASS_4 90 ) 91 ) 92 93 gradleRunner.build("generateBaselineProfile") { 94 val notFound = 95 it.lines() 96 .requireInOrder( 97 "A baseline profile was generated for the variant `release`:", 98 "${baselineProfileFile("main").toUri()}" 99 ) 100 assertThat(notFound).isEmpty() 101 } 102 103 assertThat(readBaselineProfileFileContent("main")) 104 .containsExactly( 105 Fixtures.CLASS_1, 106 Fixtures.CLASS_1_METHOD_1, 107 Fixtures.CLASS_2, 108 Fixtures.CLASS_2_METHOD_1, 109 Fixtures.CLASS_3_METHOD_1, 110 Fixtures.CLASS_3, 111 Fixtures.CLASS_4_METHOD_1, 112 Fixtures.CLASS_4 113 ) 114 115 assertThat(startupProfileFile("main").exists()).isFalse() 116 } 117 118 @Test 119 fun testGenerateTaskWithNoFlavorsForApplication() { 120 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 121 projectSetup.producer.setupWithoutFlavors( 122 releaseProfileLines = 123 listOf( 124 Fixtures.CLASS_1_METHOD_1, 125 Fixtures.CLASS_1, 126 Fixtures.CLASS_2_METHOD_1, 127 Fixtures.CLASS_2 128 ), 129 releaseStartupProfileLines = 130 listOf( 131 Fixtures.CLASS_3_METHOD_1, 132 Fixtures.CLASS_3, 133 Fixtures.CLASS_4_METHOD_1, 134 Fixtures.CLASS_4 135 ) 136 ) 137 138 gradleRunner.build("generateBaselineProfile") { 139 val notFound = 140 it.lines() 141 .requireInOrder( 142 "A baseline profile was generated for the variant `release`:", 143 "${baselineProfileFile("release").toUri()}", 144 "A startup profile was generated for the variant `release`:", 145 "${startupProfileFile("release").toUri()}" 146 ) 147 assertThat(notFound).isEmpty() 148 } 149 150 assertThat(readBaselineProfileFileContent("release")) 151 .containsExactly( 152 Fixtures.CLASS_1, 153 Fixtures.CLASS_1_METHOD_1, 154 Fixtures.CLASS_2, 155 Fixtures.CLASS_2_METHOD_1, 156 Fixtures.CLASS_3, 157 Fixtures.CLASS_3_METHOD_1, 158 Fixtures.CLASS_4, 159 Fixtures.CLASS_4_METHOD_1, 160 ) 161 162 assertThat(readStartupProfileFileContent("release")) 163 .containsExactly( 164 Fixtures.CLASS_3, 165 Fixtures.CLASS_3_METHOD_1, 166 Fixtures.CLASS_4, 167 Fixtures.CLASS_4_METHOD_1, 168 ) 169 } 170 171 @Test 172 fun testGenerateTaskWithNoFlavorsForApplicationAndNoStartupProfile() { 173 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 174 projectSetup.producer.setupWithoutFlavors( 175 releaseProfileLines = 176 listOf( 177 Fixtures.CLASS_1_METHOD_1, 178 Fixtures.CLASS_1, 179 ), 180 releaseStartupProfileLines = listOf() 181 ) 182 183 gradleRunner.withArguments("generateBaselineProfile", "--stacktrace").build() 184 185 assertThat(readBaselineProfileFileContent("release")) 186 .containsExactly( 187 Fixtures.CLASS_1, 188 Fixtures.CLASS_1_METHOD_1, 189 ) 190 191 assertThat(startupProfileFile("release").exists()).isFalse() 192 } 193 194 @Test 195 fun testGenerateTaskWithFlavorsAndDefaultMerge() { 196 projectSetup.consumer.setup( 197 androidPlugin = ANDROID_APPLICATION_PLUGIN, 198 flavors = true, 199 dependencyOnProducerProject = true 200 ) 201 projectSetup.producer.setupWithFreeAndPaidFlavors( 202 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 203 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 204 freeReleaseStartupProfileLines = listOf(Fixtures.CLASS_3_METHOD_1, Fixtures.CLASS_3), 205 paidReleaseStartupProfileLines = listOf(Fixtures.CLASS_4_METHOD_1, Fixtures.CLASS_4), 206 ) 207 208 // Asserts that all per-variant, per-flavor and per-build type tasks are being generated. 209 gradleRunner.buildAndAssertThatOutput("tasks") { 210 contains("generateReleaseBaselineProfile - ") 211 contains("generateFreeReleaseBaselineProfile - ") 212 contains("generatePaidReleaseBaselineProfile - ") 213 } 214 215 gradleRunner.build("generateReleaseBaselineProfile") { 216 arrayOf("freeRelease", "paidRelease").forEach { variantName -> 217 val notFound = 218 it.lines() 219 .requireInOrder( 220 "A baseline profile was generated for the variant `$variantName`:", 221 "${baselineProfileFile(variantName).toUri()}", 222 "A startup profile was generated for the variant `$variantName`:", 223 "${startupProfileFile(variantName).toUri()}" 224 ) 225 226 assertWithMessage( 227 """ 228 |The following lines in gradle output were not found: 229 |${notFound.joinToString("\n")} 230 | 231 |Full gradle output: 232 |$it 233 """ 234 .trimMargin() 235 ) 236 .that(notFound) 237 .isEmpty() 238 } 239 } 240 241 assertThat(readBaselineProfileFileContent("freeRelease")) 242 .containsExactly( 243 Fixtures.CLASS_1, 244 Fixtures.CLASS_1_METHOD_1, 245 Fixtures.CLASS_3, 246 Fixtures.CLASS_3_METHOD_1, 247 ) 248 249 assertThat(readBaselineProfileFileContent("paidRelease")) 250 .containsExactly( 251 Fixtures.CLASS_2, 252 Fixtures.CLASS_2_METHOD_1, 253 Fixtures.CLASS_4, 254 Fixtures.CLASS_4_METHOD_1, 255 ) 256 } 257 258 @Test 259 fun testPluginAppliedToLibraryModule() { 260 projectSetup.producer.setup() 261 projectSetup.consumer.setup( 262 androidPlugin = ANDROID_LIBRARY_PLUGIN, 263 addAppTargetPlugin = false, 264 dependencyOnProducerProject = true 265 ) 266 gradleRunner.withArguments("generateBaselineProfile", "--stacktrace").build() 267 // This should not fail. 268 } 269 270 @Test 271 fun testPluginAppliedToNonApplicationAndNonLibraryModule() { 272 projectSetup.producer.setup() 273 projectSetup.consumer.setup( 274 androidPlugin = ANDROID_TEST_PLUGIN, 275 addAppTargetPlugin = false, 276 dependencyOnProducerProject = true 277 ) 278 279 gradleRunner.withArguments("generateReleaseBaselineProfile", "--stacktrace").buildAndFail() 280 } 281 282 @Test 283 fun testSrcSetAreAddedToVariantsForApplications() { 284 projectSetup.producer.setupWithFreeAndPaidFlavors() 285 projectSetup.consumer.setup( 286 androidPlugin = ANDROID_APPLICATION_PLUGIN, 287 flavors = true, 288 additionalGradleCodeBlock = 289 """ 290 androidComponents { 291 onVariants(selector()) { variant -> 292 tasks.register(variant.name + "Sources", DisplaySourceSets) { t -> 293 t.srcs.set(variant.sources.baselineProfiles.all) 294 } 295 } 296 } 297 """ 298 .trimIndent() 299 ) 300 301 data class VariantExpectedSrcSets(val variantName: String, val expectedDirs: List<String>) 302 303 fun variantBaselineProfileSrcSetDir(variantName: String): Array<String> { 304 return (listOf("src/$variantName/baselineProfiles")).toTypedArray() 305 } 306 307 arrayOf( 308 VariantExpectedSrcSets( 309 variantName = "freeRelease", 310 expectedDirs = 311 listOf( 312 "src/main/baselineProfiles", 313 "src/free/baselineProfiles", 314 "src/release/baselineProfiles", 315 *variantBaselineProfileSrcSetDir("freeRelease"), 316 "src/freeRelease/generated/baselineProfiles", 317 ) 318 ), 319 VariantExpectedSrcSets( 320 variantName = "paidRelease", 321 expectedDirs = 322 listOf( 323 "src/main/baselineProfiles", 324 "src/paid/baselineProfiles", 325 "src/release/baselineProfiles", 326 *variantBaselineProfileSrcSetDir("paidRelease"), 327 "src/paidRelease/generated/baselineProfiles", 328 ) 329 ), 330 *(listOf( 331 VariantExpectedSrcSets( 332 variantName = "freeBenchmarkRelease", 333 expectedDirs = 334 listOf( 335 "src/main/baselineProfiles", 336 "src/free/baselineProfiles", 337 "src/benchmarkRelease/baselineProfiles", 338 "src/freeBenchmarkRelease/baselineProfiles", 339 "src/freeRelease/generated/baselineProfiles", 340 ) 341 ), 342 VariantExpectedSrcSets( 343 variantName = "paidBenchmarkRelease", 344 expectedDirs = 345 listOf( 346 "src/main/baselineProfiles", 347 "src/paid/baselineProfiles", 348 "src/benchmarkRelease/baselineProfiles", 349 "src/paidBenchmarkRelease/baselineProfiles", 350 "src/paidRelease/generated/baselineProfiles", 351 ) 352 ) 353 )) 354 .toTypedArray() 355 ) 356 .forEach { 357 val expected = 358 it.expectedDirs 359 .map { dir -> File(projectSetup.consumer.rootDir, dir) } 360 .onEach { f -> 361 // Expected src set location. Note that src sets are not added if the 362 // folder does 363 // not exist so we need to create it. 364 f.mkdirs() 365 f.deleteOnExit() 366 } 367 368 gradleRunner.buildAndAssertThatOutput("${it.variantName}Sources") { 369 expected.forEach { e -> contains(e.absolutePath) } 370 } 371 } 372 } 373 374 @Test 375 fun testWhenPluginIsAppliedAndNoDependencyIsSetShouldFailWithErrorMsg() { 376 projectSetup.consumer.setup( 377 androidPlugin = ANDROID_APPLICATION_PLUGIN, 378 flavors = false, 379 dependencyOnProducerProject = false 380 ) 381 gradleRunner.build("generateReleaseBaselineProfile", "--stacktrace") { 382 assertThat(it.replace("\n", " ")) 383 .contains( 384 "The baseline profile consumer plugin is applied to this module but no " + 385 "dependency has been set for variant `release`" 386 ) 387 } 388 } 389 390 @Test 391 fun testExperimentalPropertiesNotSet() { 392 projectSetup.producer.setupWithFreeAndPaidFlavors( 393 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 394 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 395 ) 396 projectSetup.consumer.setup( 397 androidPlugin = ANDROID_LIBRARY_PLUGIN, 398 dependencyOnProducerProject = true, 399 flavors = true, 400 buildTypeAnotherRelease = true 401 ) 402 403 arrayOf( 404 "printExperimentalPropertiesForVariantFreeRelease", 405 "printExperimentalPropertiesForVariantPaidRelease", 406 "printExperimentalPropertiesForVariantFreeAnotherRelease", 407 "printExperimentalPropertiesForVariantPaidAnotherRelease", 408 ) 409 .forEach { 410 gradleRunner.buildAndAssertThatOutput(it) { 411 doesNotContain("android.experimental.art-profile-r8-rewriting=") 412 doesNotContain("android.experimental.r8.dex-startup-optimization=") 413 } 414 } 415 } 416 417 @Test 418 fun testFilterAndSortAndMerge() { 419 projectSetup.consumer.setup( 420 androidPlugin = ANDROID_LIBRARY_PLUGIN, 421 flavors = true, 422 baselineProfileBlock = 423 """ 424 filter { 425 include("com.sample.Utils") 426 } 427 """ 428 .trimIndent() 429 ) 430 projectSetup.producer.setupWithFreeAndPaidFlavors( 431 freeReleaseProfileLines = 432 listOf( 433 Fixtures.CLASS_1_METHOD_1, 434 Fixtures.CLASS_1_METHOD_2, 435 Fixtures.CLASS_1, 436 ), 437 paidReleaseProfileLines = 438 listOf( 439 Fixtures.CLASS_2_METHOD_1, 440 Fixtures.CLASS_2_METHOD_2, 441 Fixtures.CLASS_2_METHOD_3, 442 Fixtures.CLASS_2_METHOD_4, 443 Fixtures.CLASS_2_METHOD_5, 444 Fixtures.CLASS_2, 445 ) 446 ) 447 448 gradleRunner.withArguments("generateBaselineProfile", "--stacktrace").build() 449 450 // In the final output there should be : 451 // - one single file in src/main/generated/baselineProfiles (because this is a library). 452 // - There should be only the Utils class [CLASS_2] because of the include filter. 453 // - The method `someOtherMethod` [CLASS_2_METHOD_3] should be included only once 454 // (despite being included multiple times with different flags). 455 assertThat(readBaselineProfileFileContent("main")) 456 .containsExactly( 457 Fixtures.CLASS_2, 458 Fixtures.CLASS_2_METHOD_1, 459 Fixtures.CLASS_2_METHOD_2, 460 Fixtures.CLASS_2_METHOD_3, 461 ) 462 } 463 464 @Test 465 fun testFilterPerVariant() { 466 projectSetup.consumer.setup( 467 androidPlugin = ANDROID_APPLICATION_PLUGIN, 468 flavors = true, 469 baselineProfileBlock = 470 """ 471 filter { 472 include("com.sample.Activity") 473 } 474 variants { 475 freeRelease { 476 filter { include("com.sample.Utils") } 477 } 478 paidRelease { 479 filter { include("com.sample.Fragment") } 480 } 481 } 482 """ 483 .trimIndent() 484 ) 485 486 val commonProfile = 487 listOf( 488 Fixtures.CLASS_1, 489 Fixtures.CLASS_1_METHOD_1, 490 Fixtures.CLASS_1_METHOD_2, 491 Fixtures.CLASS_2, 492 Fixtures.CLASS_2_METHOD_1, 493 Fixtures.CLASS_2_METHOD_2, 494 Fixtures.CLASS_2_METHOD_3, 495 Fixtures.CLASS_3, 496 Fixtures.CLASS_3_METHOD_1, 497 ) 498 projectSetup.producer.setupWithFreeAndPaidFlavors( 499 freeReleaseProfileLines = commonProfile, 500 paidReleaseProfileLines = commonProfile, 501 ) 502 503 gradleRunner.withArguments("generateBaselineProfile", "--stacktrace").build() 504 505 assertThat(readBaselineProfileFileContent("freeRelease")) 506 .containsExactly( 507 Fixtures.CLASS_1, 508 Fixtures.CLASS_1_METHOD_1, 509 Fixtures.CLASS_1_METHOD_2, 510 Fixtures.CLASS_2, 511 Fixtures.CLASS_2_METHOD_1, 512 Fixtures.CLASS_2_METHOD_2, 513 Fixtures.CLASS_2_METHOD_3, 514 ) 515 assertThat(readBaselineProfileFileContent("paidRelease")) 516 .containsExactly( 517 Fixtures.CLASS_1, 518 Fixtures.CLASS_1_METHOD_1, 519 Fixtures.CLASS_1_METHOD_2, 520 Fixtures.CLASS_3, 521 Fixtures.CLASS_3_METHOD_1, 522 ) 523 } 524 525 @Test 526 fun testSaveInSrcTrueAndAutomaticGenerationDuringBuildTrue() { 527 projectSetup.consumer.setup( 528 androidPlugin = ANDROID_APPLICATION_PLUGIN, 529 flavors = true, 530 baselineProfileBlock = 531 """ 532 saveInSrc = true 533 automaticGenerationDuringBuild = true 534 """ 535 .trimIndent() 536 ) 537 projectSetup.producer.setupWithFreeAndPaidFlavors( 538 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 539 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 540 ) 541 542 // Asserts that assembling release triggers generation of profile 543 gradleRunner.build("assembleFreeRelease", "--dry-run") { 544 val notFound = 545 it.lines() 546 .requireInOrder( 547 ":${projectSetup.consumer.name}:mergeFreeReleaseBaselineProfile", 548 ":${projectSetup.consumer.name}:copyFreeReleaseBaselineProfileIntoSrc", 549 ":${projectSetup.consumer.name}:mergeFreeReleaseArtProfile", 550 ":${projectSetup.consumer.name}:compileFreeReleaseArtProfile", 551 ":${projectSetup.consumer.name}:assembleFreeRelease" 552 ) 553 assertThat(notFound).isEmpty() 554 } 555 556 // Asserts that the profile is generated in the src folder 557 gradleRunner.build("generateFreeReleaseBaselineProfile") { 558 assertThat(readBaselineProfileFileContent("freeRelease")) 559 .containsExactly( 560 Fixtures.CLASS_1, 561 Fixtures.CLASS_1_METHOD_1, 562 ) 563 } 564 } 565 566 @Test 567 fun testSaveInSrcTrueAndAutomaticGenerationDuringBuildFalse() { 568 projectSetup.consumer.setup( 569 androidPlugin = ANDROID_APPLICATION_PLUGIN, 570 flavors = true, 571 baselineProfileBlock = 572 """ 573 saveInSrc = true 574 automaticGenerationDuringBuild = false 575 """ 576 .trimIndent() 577 ) 578 projectSetup.producer.setupWithFreeAndPaidFlavors( 579 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 580 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 581 ) 582 583 // Asserts that assembling release does not trigger generation of profile 584 gradleRunner.buildAndAssertThatOutput("assembleFreeRelease", "--dry-run") { 585 arrayOf("mergeFreeReleaseBaselineProfile", "copyFreeReleaseBaselineProfileIntoSrc") 586 .forEach { doesNotContain(":${projectSetup.consumer.name}:$it") } 587 arrayOf( 588 "mergeFreeReleaseArtProfile", 589 "compileFreeReleaseArtProfile", 590 "assembleFreeRelease" 591 ) 592 .forEach { contains(":${projectSetup.consumer.name}:$it") } 593 } 594 595 // Asserts that the profile is generated in the src folder 596 gradleRunner.build("generateFreeReleaseBaselineProfile") { 597 assertThat(readBaselineProfileFileContent("freeRelease")) 598 .containsExactly( 599 Fixtures.CLASS_1, 600 Fixtures.CLASS_1_METHOD_1, 601 ) 602 } 603 } 604 605 @Test 606 fun testSaveInSrcFalseAndAutomaticGenerationDuringBuildTrue() { 607 projectSetup.consumer.setup( 608 androidPlugin = ANDROID_APPLICATION_PLUGIN, 609 flavors = true, 610 baselineProfileBlock = 611 """ 612 saveInSrc = false 613 automaticGenerationDuringBuild = true 614 """ 615 .trimIndent() 616 ) 617 projectSetup.producer.setupWithFreeAndPaidFlavors( 618 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 619 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 620 freeReleaseStartupProfileLines = listOf(Fixtures.CLASS_3_METHOD_1, Fixtures.CLASS_3), 621 paidReleaseStartupProfileLines = listOf(Fixtures.CLASS_4_METHOD_1, Fixtures.CLASS_4), 622 ) 623 624 // Asserts that assembling release triggers generation of profile 625 gradleRunner.build("assembleFreeRelease", "--dry-run") { 626 627 // Assert sequence of tasks is found 628 val notFound = 629 it.lines() 630 .requireInOrder( 631 ":${projectSetup.consumer.name}:mergeFreeReleaseBaselineProfile", 632 ":${projectSetup.consumer.name}:mergeFreeReleaseArtProfile", 633 ":${projectSetup.consumer.name}:compileFreeReleaseArtProfile", 634 ":${projectSetup.consumer.name}:assembleFreeRelease" 635 ) 636 assertThat(notFound).isEmpty() 637 638 // Asserts that the copy task is disabled, because of `saveInSrc` set to false. 639 assertThat(it) 640 .doesNotContain( 641 ":${projectSetup.consumer.name}:copyFreeReleaseBaselineProfileIntoSrc" 642 ) 643 } 644 645 // Asserts that the profile is not generated in the src folder 646 gradleRunner.build("generateFreeReleaseBaselineProfile") { 647 // Note that here the profiles are generated in the intermediates so the output does 648 // not matter. 649 val notFound = 650 it.lines() 651 .requireInOrder( 652 "A baseline profile was generated for the variant `freeRelease`:", 653 "A startup profile was generated for the variant `freeRelease`:", 654 ) 655 assertThat(notFound).isEmpty() 656 } 657 658 assertThat(baselineProfileFile("freeRelease").exists()).isFalse() 659 } 660 661 @Test 662 fun testSaveInSrcFalseAndAutomaticGenerationDuringBuildFalse() { 663 projectSetup.producer.setup() 664 projectSetup.consumer.setup( 665 androidPlugin = ANDROID_APPLICATION_PLUGIN, 666 baselineProfileBlock = 667 """ 668 saveInSrc = false 669 automaticGenerationDuringBuild = false 670 """ 671 .trimIndent() 672 ) 673 gradleRunner 674 .withArguments("generateReleaseBaselineProfile", "--stacktrace") 675 .buildAndFail() 676 .output 677 .replace(System.lineSeparator(), " ") 678 .also { 679 assertThat(it) 680 .contains( 681 "The current configuration of flags `saveInSrc` and " + 682 "`automaticGenerationDuringBuild` is not supported" 683 ) 684 } 685 } 686 687 @Test 688 fun testWhenFiltersFilterOutAllTheProfileRules() { 689 projectSetup.consumer.setup( 690 androidPlugin = ANDROID_LIBRARY_PLUGIN, 691 baselineProfileBlock = 692 """ 693 filter { include("nothing.**") } 694 """ 695 .trimIndent() 696 ) 697 projectSetup.producer.setupWithoutFlavors( 698 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 699 ) 700 701 gradleRunner 702 .withArguments("generateBaselineProfile", "--stacktrace") 703 .buildAndFail() 704 .output 705 .replace(System.lineSeparator(), " ") 706 .also { 707 assertThat(it) 708 .contains( 709 "The baseline profile consumer plugin is configured with filters that " + 710 "exclude all the profile rules" 711 ) 712 } 713 } 714 715 @Test 716 fun testWhenProfileProducerProducesEmptyProfile() { 717 projectSetup.consumer.setup(androidPlugin = ANDROID_LIBRARY_PLUGIN) 718 projectSetup.producer.setupWithoutFlavors(releaseProfileLines = listOf()) 719 gradleRunner.buildAndAssertThatOutput("generateBaselineProfile") { 720 contains("No baseline profile rules were generated") 721 } 722 } 723 724 @Test 725 fun testVariantConfigurationOverrideForFlavors() { 726 projectSetup.producer.setupWithFreeAndPaidFlavors( 727 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 728 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 729 ) 730 projectSetup.consumer.setup( 731 androidPlugin = ANDROID_LIBRARY_PLUGIN, 732 flavors = true, 733 baselineProfileBlock = 734 """ 735 736 // Global configuration 737 saveInSrc = true 738 automaticGenerationDuringBuild = false 739 baselineProfileOutputDir = "generated/baselineProfiles" 740 mergeIntoMain = true 741 742 // Per variant configuration overrides global configuration. 743 variants { 744 free { 745 saveInSrc = false 746 automaticGenerationDuringBuild = true 747 baselineProfileOutputDir = "somefolder" 748 mergeIntoMain = false 749 } 750 paidRelease { 751 saveInSrc = false 752 automaticGenerationDuringBuild = true 753 baselineProfileOutputDir = "someOtherfolder" 754 mergeIntoMain = false 755 } 756 } 757 758 """ 759 .trimIndent() 760 ) 761 762 gradleRunner.buildAndAssertThatOutput( 763 "printBaselineProfileExtensionForVariantFreeRelease" 764 ) { 765 contains("saveInSrc=`false`") 766 contains("automaticGenerationDuringBuild=`true`") 767 contains("baselineProfileOutputDir=`somefolder`") 768 contains("mergeIntoMain=`false`") 769 } 770 771 gradleRunner.buildAndAssertThatOutput( 772 "printBaselineProfileExtensionForVariantPaidRelease" 773 ) { 774 contains("saveInSrc=`false`") 775 contains("automaticGenerationDuringBuild=`true`") 776 contains("baselineProfileOutputDir=`someOtherfolder`") 777 contains("mergeIntoMain=`false`") 778 } 779 } 780 781 @Test 782 fun testVariantConfigurationOverrideForBuildTypes() { 783 projectSetup.producer.setupWithFreeAndPaidFlavors( 784 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 785 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 786 ) 787 projectSetup.consumer.setup( 788 androidPlugin = ANDROID_APPLICATION_PLUGIN, 789 flavors = true, 790 baselineProfileBlock = 791 """ 792 793 // Global configuration 794 saveInSrc = true 795 automaticGenerationDuringBuild = false 796 baselineProfileOutputDir = "generated/baselineProfiles" 797 mergeIntoMain = true 798 799 // Per variant configuration overrides global configuration. 800 variants { 801 release { 802 saveInSrc = false 803 automaticGenerationDuringBuild = true 804 baselineProfileOutputDir = "myReleaseFolder" 805 mergeIntoMain = false 806 } 807 paidRelease { 808 saveInSrc = false 809 automaticGenerationDuringBuild = true 810 baselineProfileOutputDir = "someOtherfolder" 811 mergeIntoMain = false 812 } 813 } 814 815 """ 816 .trimIndent() 817 ) 818 819 gradleRunner.buildAndAssertThatOutput( 820 "printBaselineProfileExtensionForVariantFreeRelease" 821 ) { 822 contains("saveInSrc=`false`") 823 contains("automaticGenerationDuringBuild=`true`") 824 contains("baselineProfileOutputDir=`myReleaseFolder`") 825 contains("mergeIntoMain=`false`") 826 } 827 828 gradleRunner.buildAndAssertThatOutput( 829 "printBaselineProfileExtensionForVariantPaidRelease" 830 ) { 831 contains("saveInSrc=`false`") 832 contains("automaticGenerationDuringBuild=`true`") 833 contains("baselineProfileOutputDir=`someOtherfolder`") 834 contains("mergeIntoMain=`false`") 835 } 836 } 837 838 @Test 839 fun testVariantConfigurationOverrideForFlavorsAndBuildType() { 840 projectSetup.producer.setupWithFreeAndPaidFlavors( 841 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 842 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 843 ) 844 projectSetup.consumer.setup( 845 androidPlugin = ANDROID_LIBRARY_PLUGIN, 846 flavors = true, 847 baselineProfileBlock = 848 """ 849 variants { 850 free { 851 saveInSrc = true 852 } 853 release { 854 saveInSrc = false 855 } 856 } 857 858 """ 859 .trimIndent() 860 ) 861 gradleRunner 862 .withArguments("printBaselineProfileExtensionForVariantFreeRelease", "--stacktrace") 863 .buildAndFail() 864 .output 865 .let { 866 assertThat(it) 867 .contains("The per-variant configuration for baseline profiles is ambiguous") 868 } 869 } 870 871 @Test 872 fun testVariantDependenciesWithFlavors() { 873 projectSetup.producer.setupWithFreeAndPaidFlavors( 874 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 875 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 876 ) 877 878 // In this setup no dependency is being added through the dependency block. 879 // Instead dependencies are being added through per-variant configuration block. 880 projectSetup.consumer.setup( 881 androidPlugin = ANDROID_APPLICATION_PLUGIN, 882 flavors = true, 883 dependencyOnProducerProject = false, 884 baselineProfileBlock = 885 """ 886 variants { 887 free { 888 from(project(":${projectSetup.producer.name}")) 889 } 890 paid { 891 from(project(":${projectSetup.producer.name}")) 892 } 893 } 894 895 """ 896 .trimIndent() 897 ) 898 gradleRunner.withArguments("generateReleaseBaselineProfile", "--stacktrace").build() 899 900 assertThat(readBaselineProfileFileContent("freeRelease")) 901 .containsExactly( 902 Fixtures.CLASS_1, 903 Fixtures.CLASS_1_METHOD_1, 904 ) 905 assertThat(readBaselineProfileFileContent("paidRelease")) 906 .containsExactly( 907 Fixtures.CLASS_2, 908 Fixtures.CLASS_2_METHOD_1, 909 ) 910 } 911 912 @Test 913 fun testPartialResults() { 914 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 915 916 // Function to setup the producer, run the generate profile command and assert output 917 fun setupProducerGenerateAndAssert( 918 partial: Boolean, 919 generatedProfiles: Map<String, List<String>>, 920 actualProfile: List<String> 921 ) { 922 projectSetup.producer.setup( 923 variantProfiles = 924 listOf( 925 VariantProfile( 926 flavor = null, 927 buildType = "release", 928 profileFileLines = generatedProfiles 929 ) 930 ) 931 ) 932 933 val args = 934 listOfNotNull( 935 "generateBaselineProfile", 936 if (partial) "-Pandroid.testInstrumentationRunnerArguments.class=someClass" 937 else null 938 ) 939 940 projectSetup.consumer.gradleRunner.build(*args.toTypedArray()) {} 941 942 assertThat(readBaselineProfileFileContent("release")) 943 .containsExactly(*actualProfile.toTypedArray()) 944 } 945 946 // Full generation, 2 new tests. 947 setupProducerGenerateAndAssert( 948 partial = false, 949 generatedProfiles = 950 mapOf( 951 "myTest1" to listOf(Fixtures.CLASS_1, Fixtures.CLASS_1_METHOD_1), 952 "myTest2" to listOf(Fixtures.CLASS_2, Fixtures.CLASS_2_METHOD_1) 953 ), 954 actualProfile = 955 listOf( 956 Fixtures.CLASS_1, 957 Fixtures.CLASS_1_METHOD_1, 958 Fixtures.CLASS_2, 959 Fixtures.CLASS_2_METHOD_1 960 ) 961 ) 962 963 // Partial generation, modify 1 test. 964 setupProducerGenerateAndAssert( 965 partial = true, 966 generatedProfiles = 967 mapOf("myTest1" to listOf(Fixtures.CLASS_3, Fixtures.CLASS_3_METHOD_1)), 968 actualProfile = 969 listOf( 970 Fixtures.CLASS_3, 971 Fixtures.CLASS_3_METHOD_1, 972 Fixtures.CLASS_2, 973 Fixtures.CLASS_2_METHOD_1 974 ) 975 ) 976 977 // Partial generation, add 1 test. 978 setupProducerGenerateAndAssert( 979 partial = true, 980 generatedProfiles = 981 mapOf("myTest3" to listOf(Fixtures.CLASS_4, Fixtures.CLASS_4_METHOD_1)), 982 actualProfile = 983 listOf( 984 Fixtures.CLASS_3, 985 Fixtures.CLASS_3_METHOD_1, 986 Fixtures.CLASS_4, 987 Fixtures.CLASS_4_METHOD_1, 988 Fixtures.CLASS_2, 989 Fixtures.CLASS_2_METHOD_1 990 ) 991 ) 992 993 // Full generation, 2 new tests. 994 setupProducerGenerateAndAssert( 995 partial = false, 996 generatedProfiles = 997 mapOf( 998 "myTest1-new" to listOf(Fixtures.CLASS_1, Fixtures.CLASS_1_METHOD_1), 999 "myTest2-new" to listOf(Fixtures.CLASS_2, Fixtures.CLASS_2_METHOD_1) 1000 ), 1001 actualProfile = 1002 listOf( 1003 Fixtures.CLASS_1, 1004 Fixtures.CLASS_1_METHOD_1, 1005 Fixtures.CLASS_2, 1006 Fixtures.CLASS_2_METHOD_1 1007 ) 1008 ) 1009 } 1010 1011 @Test 1012 fun testBaselineProfileIsInMergeArtProfileIntermediate() { 1013 projectSetup.consumer.setup( 1014 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1015 flavors = true, 1016 baselineProfileBlock = 1017 """ 1018 saveInSrc = true 1019 automaticGenerationDuringBuild = true 1020 """ 1021 .trimIndent() 1022 ) 1023 1024 data class VariantAndProfile(val variantName: String, val profile: List<String>) 1025 1026 val freeRelease = 1027 VariantAndProfile( 1028 variantName = "freeRelease", 1029 profile = 1030 listOf( 1031 Fixtures.CLASS_1, 1032 Fixtures.CLASS_1_METHOD_1, 1033 Fixtures.CLASS_1_METHOD_2, 1034 Fixtures.CLASS_3, 1035 Fixtures.CLASS_3_METHOD_1, 1036 ) 1037 ) 1038 val paidRelease = 1039 VariantAndProfile( 1040 variantName = "paidRelease", 1041 profile = 1042 listOf( 1043 Fixtures.CLASS_1, 1044 Fixtures.CLASS_1_METHOD_1, 1045 Fixtures.CLASS_1_METHOD_2, 1046 Fixtures.CLASS_2, 1047 Fixtures.CLASS_2_METHOD_1, 1048 Fixtures.CLASS_2_METHOD_2, 1049 Fixtures.CLASS_2_METHOD_3, 1050 ) 1051 ) 1052 projectSetup.producer.setupWithFreeAndPaidFlavors( 1053 freeReleaseProfileLines = freeRelease.profile, 1054 paidReleaseProfileLines = paidRelease.profile, 1055 ) 1056 1057 val variants = arrayOf(freeRelease, paidRelease) 1058 val tasks = variants.map { camelCase("merge", it.variantName, "ArtProfile") } 1059 gradleRunner.build(*(tasks.toTypedArray())) {} 1060 1061 variants.forEach { 1062 val notFound = 1063 mergedArtProfile(it.variantName).readLines().require(*(it.profile).toTypedArray()) 1064 assertThat(notFound).isEmpty() 1065 } 1066 } 1067 1068 @Test 1069 fun testMultidimensionalFlavorsAndMatchingFallbacks() { 1070 projectSetup.consumer.setupWithBlocks( 1071 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1072 flavorsBlock = 1073 """ 1074 flavorDimensions = ["tier", "color"] 1075 free { dimension "tier" } 1076 red { dimension "color" } 1077 paid { 1078 dimension "tier" 1079 matchingFallbacks += "free" 1080 } 1081 blue { 1082 dimension "color" 1083 matchingFallbacks += "red" 1084 } 1085 """ 1086 .trimIndent(), 1087 buildTypesBlock = "", 1088 dependencyOnProducerProject = false, 1089 dependenciesBlock = 1090 """ 1091 implementation(project(":${projectSetup.dependency.name}")) 1092 """ 1093 .trimIndent(), 1094 baselineProfileBlock = 1095 """ 1096 variants { 1097 free { from(project(":${projectSetup.producer.name}")) } 1098 red { from(project(":${projectSetup.producer.name}")) } 1099 paid { from(project(":${projectSetup.producer.name}")) } 1100 // blue is already covered by the intersection of the other dimensions so no 1101 // need to specify it. 1102 } 1103 1104 """ 1105 .trimIndent() 1106 ) 1107 projectSetup.producer.setup( 1108 variantProfiles = 1109 listOf( 1110 VariantProfile( 1111 flavorDimensions = 1112 mapOf( 1113 "tier" to "free", 1114 "color" to "red", 1115 ), 1116 buildType = "release", 1117 profileFileLines = 1118 mapOf( 1119 "some-test-output" to 1120 listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1121 ), 1122 startupFileLines = 1123 mapOf( 1124 "some-startup-test-output" to 1125 listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1126 ), 1127 ) 1128 ) 1129 ) 1130 1131 arrayOf("freeRedRelease", "freeBlueRelease", "paidRedRelease", "paidBlueRelease").forEach { 1132 variantName -> 1133 gradleRunner.build(camelCase("generate", variantName, "baselineProfile")) { 1134 assertThat(readBaselineProfileFileContent(variantName)) 1135 .containsExactly(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1136 } 1137 } 1138 } 1139 1140 @Test 1141 fun testSkipGeneration() { 1142 projectSetup.consumer.setup(ANDROID_APPLICATION_PLUGIN) 1143 projectSetup.producer.setupWithoutFlavors( 1144 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1145 ) 1146 1147 gradleRunner.build("generateBaselineProfile", "-Pandroidx.baselineprofile.skipgeneration") { 1148 assertThat(baselineProfileFile("release").exists()).isFalse() 1149 } 1150 } 1151 1152 @Test 1153 fun testSkipGenerationWithPreviousResults() { 1154 projectSetup.consumer.setup(ANDROID_APPLICATION_PLUGIN) 1155 projectSetup.producer.setupWithoutFlavors( 1156 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1157 ) 1158 1159 gradleRunner.build("generateBaselineProfile") { 1160 assertThat(readBaselineProfileFileContent("release")) 1161 .containsExactly(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1162 } 1163 1164 projectSetup.producer.setupWithoutFlavors( 1165 releaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 1166 ) 1167 1168 gradleRunner.build("generateBaselineProfile", "-Pandroidx.baselineprofile.skipgeneration") { 1169 1170 // Note that the baseline profile should still contain the previous profile rules 1171 // and not the updated ones, as running with `skipgeneration` will disable the 1172 // generation tasks. 1173 assertThat(readBaselineProfileFileContent("release")) 1174 .containsExactly(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1175 } 1176 } 1177 1178 @Test 1179 fun testVariantSpecificDependencies() { 1180 projectSetup.producer.setupWithoutFlavors( 1181 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1182 ) 1183 projectSetup.consumer.setup( 1184 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1185 dependenciesBlock = 1186 """ 1187 releaseImplementation(project(":${projectSetup.dependency.name}")) 1188 """ 1189 .trimIndent() 1190 ) 1191 gradleRunner.build("generateReleaseBaselineProfile", "--stacktrace") { 1192 assertThat(readBaselineProfileFileContent("release")) 1193 .containsExactly(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1194 } 1195 } 1196 1197 @Test 1198 fun testVariantSpecificDependenciesWithFlavorsAndMultipleBuildTypes() { 1199 projectSetup.consumer.setupWithBlocks( 1200 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1201 flavorsBlock = 1202 """ 1203 flavorDimensions = ["tier"] 1204 free { dimension "tier" } 1205 paid { dimension "tier" } 1206 """ 1207 .trimIndent(), 1208 buildTypesBlock = 1209 """ 1210 anotherRelease { initWith(release) } 1211 """ 1212 .trimIndent(), 1213 dependencyOnProducerProject = true, 1214 dependenciesBlock = 1215 """ 1216 releaseImplementation(project(":${projectSetup.dependency.name}")) 1217 anotherReleaseImplementation(project(":${projectSetup.dependency.name}")) 1218 """ 1219 .trimIndent(), 1220 ) 1221 projectSetup.producer.setup( 1222 variantProfiles = 1223 listOf( 1224 VariantProfile( 1225 flavorDimensions = mapOf("tier" to "free"), 1226 buildType = "release", 1227 profileFileLines = 1228 mapOf( 1229 "test-output-baseline-free-release" to 1230 listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1231 ), 1232 startupFileLines = mapOf() 1233 ), 1234 VariantProfile( 1235 flavorDimensions = mapOf("tier" to "paid"), 1236 buildType = "release", 1237 profileFileLines = 1238 mapOf( 1239 "test-output-baseline-paid-release" to 1240 listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 1241 ), 1242 startupFileLines = mapOf() 1243 ), 1244 VariantProfile( 1245 flavorDimensions = mapOf("tier" to "free"), 1246 buildType = "anotherRelease", 1247 profileFileLines = 1248 mapOf( 1249 "test-output-baseline-free-anotherRelease" to 1250 listOf(Fixtures.CLASS_3_METHOD_1, Fixtures.CLASS_3) 1251 ), 1252 startupFileLines = mapOf() 1253 ), 1254 VariantProfile( 1255 flavorDimensions = mapOf("tier" to "paid"), 1256 buildType = "anotherRelease", 1257 profileFileLines = 1258 mapOf( 1259 "test-output-baseline-paid-anotherRelease" to 1260 listOf(Fixtures.CLASS_4_METHOD_1, Fixtures.CLASS_4) 1261 ), 1262 startupFileLines = mapOf() 1263 ), 1264 ) 1265 ) 1266 1267 data class Expected(val variantName: String, val profileLines: List<String>) 1268 arrayOf( 1269 Expected( 1270 variantName = "freeRelease", 1271 profileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1) 1272 ), 1273 Expected( 1274 variantName = "paidRelease", 1275 profileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 1276 ), 1277 Expected( 1278 variantName = "freeAnotherRelease", 1279 profileLines = listOf(Fixtures.CLASS_3_METHOD_1, Fixtures.CLASS_3) 1280 ), 1281 Expected( 1282 variantName = "paidAnotherRelease", 1283 profileLines = listOf(Fixtures.CLASS_4_METHOD_1, Fixtures.CLASS_4) 1284 ), 1285 ) 1286 .forEach { expected -> 1287 gradleRunner.build(camelCase("generate", expected.variantName, "baselineProfile")) { 1288 assertThat(readBaselineProfileFileContent(expected.variantName)) 1289 .containsExactlyElementsIn(expected.profileLines) 1290 } 1291 } 1292 } 1293 1294 @Test 1295 fun whenBenchmarkVariantsAreDisabledShouldNotify() { 1296 projectSetup.consumer.setup( 1297 dependencyOnProducerProject = true, 1298 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1299 additionalGradleCodeBlock = 1300 """ 1301 androidComponents { 1302 beforeVariants(selector()) { variant -> 1303 variant.enable = variant.buildType != "benchmarkRelease" 1304 } 1305 } 1306 """ 1307 .trimIndent() 1308 ) 1309 projectSetup.producer.setupWithoutFlavors( 1310 releaseProfileLines = 1311 listOf( 1312 Fixtures.CLASS_1_METHOD_1, 1313 Fixtures.CLASS_1, 1314 Fixtures.CLASS_2_METHOD_1, 1315 Fixtures.CLASS_2 1316 ), 1317 releaseStartupProfileLines = 1318 listOf( 1319 Fixtures.CLASS_3_METHOD_1, 1320 Fixtures.CLASS_3, 1321 Fixtures.CLASS_4_METHOD_1, 1322 Fixtures.CLASS_4 1323 ) 1324 ) 1325 1326 gradleRunner.buildAndAssertThatOutput("tasks", "--info") { 1327 contains("Variant `benchmarkRelease` is disabled.") 1328 } 1329 } 1330 1331 @Test 1332 fun testProfileStats() { 1333 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 1334 1335 // Test no previous execution 1336 projectSetup.producer.setupWithoutFlavors( 1337 releaseProfileLines = 1338 listOf( 1339 Fixtures.CLASS_1_METHOD_1, 1340 Fixtures.CLASS_1, 1341 ), 1342 releaseStartupProfileLines = 1343 listOf( 1344 Fixtures.CLASS_1_METHOD_1, 1345 Fixtures.CLASS_1, 1346 ) 1347 ) 1348 gradleRunner.build("generateBaselineProfile") { 1349 val notFound = 1350 it.lines() 1351 .requireInOrder( 1352 "Comparison with previous baseline profile:", 1353 "Comparison with previous startup profile:", 1354 ) 1355 assertThat(notFound.size).isEqualTo(2) 1356 } 1357 1358 // Test unchanged 1359 gradleRunner.build("generateBaselineProfile", "--rerun-tasks") { 1360 println(it) 1361 val notFound = 1362 it.lines() 1363 .requireInOrder( 1364 "Comparison with previous baseline profile:", 1365 " 2 Old rules", 1366 " 2 New rules", 1367 " 0 Added rules (0.00%)", 1368 " 0 Removed rules (0.00%)", 1369 " 2 Unmodified rules (100.00%)", 1370 "Comparison with previous startup profile:", 1371 " 2 Old rules", 1372 " 2 New rules", 1373 " 0 Added rules (0.00%)", 1374 " 0 Removed rules (0.00%)", 1375 " 2 Unmodified rules (100.00%)", 1376 ) 1377 assertThat(notFound).isEmpty() 1378 } 1379 1380 // Test added 1381 projectSetup.producer.setupWithoutFlavors( 1382 releaseProfileLines = 1383 listOf( 1384 Fixtures.CLASS_1_METHOD_1, 1385 Fixtures.CLASS_1, 1386 Fixtures.CLASS_2_METHOD_2, 1387 Fixtures.CLASS_2, 1388 ), 1389 releaseStartupProfileLines = 1390 listOf( 1391 Fixtures.CLASS_1_METHOD_1, 1392 Fixtures.CLASS_1, 1393 Fixtures.CLASS_2_METHOD_2, 1394 Fixtures.CLASS_2, 1395 ) 1396 ) 1397 gradleRunner.build("generateBaselineProfile", "--rerun-tasks") { 1398 println(it) 1399 val notFound = 1400 it.lines() 1401 .requireInOrder( 1402 "Comparison with previous baseline profile:", 1403 " 2 Old rules", 1404 " 4 New rules", 1405 " 2 Added rules (50.00%)", 1406 " 0 Removed rules (0.00%)", 1407 " 2 Unmodified rules (50.00%)", 1408 "Comparison with previous startup profile:", 1409 " 2 Old rules", 1410 " 4 New rules", 1411 " 2 Added rules (50.00%)", 1412 " 0 Removed rules (0.00%)", 1413 " 2 Unmodified rules (50.00%)", 1414 ) 1415 assertThat(notFound).isEmpty() 1416 } 1417 1418 // Test removed 1419 projectSetup.producer.setupWithoutFlavors( 1420 releaseProfileLines = 1421 listOf( 1422 Fixtures.CLASS_2_METHOD_2, 1423 Fixtures.CLASS_2, 1424 ), 1425 releaseStartupProfileLines = 1426 listOf( 1427 Fixtures.CLASS_2_METHOD_2, 1428 Fixtures.CLASS_2, 1429 ) 1430 ) 1431 gradleRunner.build("generateBaselineProfile", "--rerun-tasks") { 1432 println(it) 1433 val notFound = 1434 it.lines() 1435 .requireInOrder( 1436 "Comparison with previous baseline profile:", 1437 " 4 Old rules", 1438 " 2 New rules", 1439 " 0 Added rules (0.00%)", 1440 " 2 Removed rules (50.00%)", 1441 " 2 Unmodified rules (50.00%)", 1442 "Comparison with previous startup profile:", 1443 " 4 Old rules", 1444 " 2 New rules", 1445 " 0 Added rules (0.00%)", 1446 " 2 Removed rules (50.00%)", 1447 " 2 Unmodified rules (50.00%)", 1448 ) 1449 assertThat(notFound).isEmpty() 1450 } 1451 } 1452 1453 @Test 1454 fun testSuppressWarningMaxAgpVersion() { 1455 val requiredLines = 1456 listOf( 1457 "This version of the Baseline Profile Gradle Plugin was tested with versions below", 1458 // We skip the lines in between because they may contain changing version numbers. 1459 "baselineProfile {", 1460 " warnings {", 1461 " maxAgpVersion = false", 1462 " }", 1463 "}" 1464 ) 1465 projectSetup.producer.setupWithoutFlavors( 1466 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1467 ) 1468 1469 // Setup with default warnings 1470 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 1471 projectSetup.consumer.gradleRunner.build( 1472 "generateBaselineProfile", 1473 "-Pandroidx.benchmark.test.maxagpversion=1.0.0" 1474 ) { 1475 val notFound = it.lines().requireInOrder(*requiredLines.toTypedArray()) 1476 assertThat(notFound).isEmpty() 1477 } 1478 1479 // Setup turning off warning 1480 projectSetup.consumer.setup( 1481 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1482 baselineProfileBlock = 1483 """ 1484 warnings { 1485 maxAgpVersion = false 1486 } 1487 """ 1488 .trimIndent() 1489 ) 1490 projectSetup.consumer.gradleRunner.build( 1491 "generateBaselineProfile", 1492 "-Pandroidx.benchmark.test.maxagpversion=1.0.0" 1493 ) { 1494 val notFound = it.lines().requireInOrder(*requiredLines.toTypedArray()) 1495 assertThat(notFound).isEqualTo(requiredLines) 1496 } 1497 } 1498 1499 @Test 1500 fun testSuppressWarningWithProperty() { 1501 val requiredLines = 1502 listOf( 1503 "This version of the Baseline Profile Gradle Plugin was tested with versions below", 1504 // We skip the lines in between because they may contain changing version numbers. 1505 "baselineProfile {", 1506 " warnings {", 1507 " maxAgpVersion = false", 1508 " }", 1509 "}" 1510 ) 1511 1512 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 1513 projectSetup.producer.setupWithoutFlavors( 1514 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1515 ) 1516 1517 val gradleCmds = 1518 arrayOf( 1519 "generateBaselineProfile", 1520 "-Pandroidx.benchmark.test.maxagpversion=1.0.0", 1521 ) 1522 1523 // Run with no suppress warnings property 1524 projectSetup.consumer.gradleRunner.build(*gradleCmds) { 1525 val notFound = it.lines().requireInOrder(*requiredLines.toTypedArray()) 1526 assertThat(notFound).isEmpty() 1527 } 1528 1529 // Run with suppress warnings property 1530 projectSetup.consumer.gradleRunner.build( 1531 *gradleCmds, 1532 "-Pandroidx.baselineprofile.suppresswarnings" 1533 ) { 1534 val notFound = it.lines().requireInOrder(*requiredLines.toTypedArray()) 1535 assertThat(notFound).isEqualTo(requiredLines) 1536 } 1537 } 1538 1539 @Test 1540 fun testMergeArtAndStartupProfilesShouldDependOnProfileGeneration() { 1541 projectSetup.producer.setupWithFreeAndPaidFlavors( 1542 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1543 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 1544 ) 1545 1546 arrayOf( 1547 Pair(true, true), 1548 Pair(true, false), 1549 Pair(false, true), 1550 ) 1551 .forEach { (saveInSrc, automaticGenerationDuringBuild) -> 1552 projectSetup.consumer.setup( 1553 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1554 flavors = true, 1555 baselineProfileBlock = 1556 """ 1557 saveInSrc = $saveInSrc 1558 automaticGenerationDuringBuild = $automaticGenerationDuringBuild 1559 """ 1560 .trimIndent() 1561 ) 1562 gradleRunner.build("generateFreeReleaseBaselineProfile", "assembleFreeRelease") {} 1563 } 1564 } 1565 } 1566 1567 @RunWith(Parameterized::class) 1568 class BaselineProfileConsumerPluginTestWithAgp81(private val agpVersion: TestAgpVersion) { 1569 1570 companion object { 1571 @Parameterized.Parameters(name = "agpVersion={0}") 1572 @JvmStatic parametersnull1573 fun parameters() = TestAgpVersion.atLeast(TEST_AGP_VERSION_8_1_1) 1574 } 1575 1576 @get:Rule 1577 val projectSetup = BaselineProfileProjectSetupRule(forceAgpVersion = agpVersion.versionString) 1578 1579 @Test 1580 fun verifyGenerateTasks() { 1581 projectSetup.producer.setupWithFreeAndPaidFlavors( 1582 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1583 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 1584 freeAnotherReleaseProfileLines = listOf(Fixtures.CLASS_3_METHOD_1, Fixtures.CLASS_3), 1585 paidAnotherReleaseProfileLines = listOf(Fixtures.CLASS_4_METHOD_1, Fixtures.CLASS_4), 1586 ) 1587 projectSetup.consumer.setup( 1588 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1589 dependencyOnProducerProject = true, 1590 flavors = true, 1591 buildTypeAnotherRelease = true 1592 ) 1593 projectSetup.consumer.gradleRunner.build("tasks") { 1594 val notFound = 1595 it.lines() 1596 .require( 1597 "generateBaselineProfile - ", 1598 "generateReleaseBaselineProfile - ", 1599 "generateAnotherReleaseBaselineProfile - ", 1600 "generateFreeBaselineProfile - ", 1601 "generatePaidBaselineProfile - ", 1602 "generateFreeReleaseBaselineProfile - ", 1603 "generatePaidReleaseBaselineProfile - ", 1604 "generateFreeAnotherReleaseBaselineProfile - ", 1605 "generatePaidAnotherReleaseBaselineProfile - ", 1606 ) 1607 assertThat(notFound).isEmpty() 1608 } 1609 1610 val name = projectSetup.consumer.name 1611 1612 projectSetup.consumer.gradleRunner.build("generateBaselineProfile", "--dry-run") { 1613 val notFound = 1614 it.lines() 1615 .require( 1616 ":$name:copyFreeReleaseBaselineProfileIntoSrc", 1617 ":$name:copyPaidReleaseBaselineProfileIntoSrc", 1618 ":$name:copyFreeAnotherReleaseBaselineProfileIntoSrc", 1619 ":$name:copyPaidAnotherReleaseBaselineProfileIntoSrc", 1620 ) 1621 assertThat(notFound).isEmpty() 1622 } 1623 1624 projectSetup.consumer.gradleRunner.build("generateReleaseBaselineProfile", "--dry-run") { 1625 val notFound = 1626 it.lines() 1627 .require( 1628 ":$name:copyFreeReleaseBaselineProfileIntoSrc", 1629 ":$name:copyPaidReleaseBaselineProfileIntoSrc", 1630 ) 1631 assertThat(notFound).isEmpty() 1632 } 1633 1634 projectSetup.consumer.gradleRunner.build( 1635 "generateAnotherReleaseBaselineProfile", 1636 "--dry-run" 1637 ) { 1638 val notFound = 1639 it.lines() 1640 .require( 1641 ":$name:copyFreeAnotherReleaseBaselineProfileIntoSrc", 1642 ":$name:copyPaidAnotherReleaseBaselineProfileIntoSrc", 1643 ) 1644 assertThat(notFound).isEmpty() 1645 } 1646 1647 projectSetup.consumer.gradleRunner.build("generateFreeBaselineProfile", "--dry-run") { 1648 val notFound = 1649 it.lines() 1650 .require( 1651 ":$name:copyFreeReleaseBaselineProfileIntoSrc", 1652 ":$name:copyFreeAnotherReleaseBaselineProfileIntoSrc", 1653 ) 1654 assertThat(notFound).isEmpty() 1655 } 1656 1657 projectSetup.consumer.gradleRunner.build("generatePaidBaselineProfile", "--dry-run") { 1658 val notFound = 1659 it.lines() 1660 .require( 1661 ":$name:copyPaidReleaseBaselineProfileIntoSrc", 1662 ":$name:copyPaidAnotherReleaseBaselineProfileIntoSrc", 1663 ) 1664 assertThat(notFound).isEmpty() 1665 } 1666 } 1667 1668 @Test verifyTasksWithAndroidTestPluginnull1669 fun verifyTasksWithAndroidTestPlugin() { 1670 projectSetup.consumer.setup( 1671 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1672 flavors = true, 1673 baselineProfileBlock = 1674 """ 1675 saveInSrc = true 1676 automaticGenerationDuringBuild = true 1677 """ 1678 .trimIndent(), 1679 additionalGradleCodeBlock = 1680 """ 1681 androidComponents { 1682 onVariants(selector()) { variant -> 1683 tasks.register(variant.name + "BaselineProfileSrcSet", PrintTask) { t -> 1684 t.text.set(variant.sources.baselineProfiles.directories.toString()) 1685 } 1686 } 1687 } 1688 """ 1689 .trimIndent() 1690 ) 1691 projectSetup.producer.setupWithFreeAndPaidFlavors( 1692 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1693 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2), 1694 ) 1695 1696 // Asserts that running connected checks on a benchmark variants also triggers 1697 // baseline profile generation (due to `automaticGenerationDuringBuild` true`). 1698 projectSetup.producer.gradleRunner.build( 1699 "connectedFreeBenchmarkReleaseAndroidTest", 1700 "--dry-run" 1701 ) { text -> 1702 val consumerName = projectSetup.consumer.name 1703 val producerName = projectSetup.producer.name 1704 1705 val notFound = 1706 text 1707 .lines() 1708 .requireInOrder( 1709 ":$consumerName:packageFreeNonMinifiedRelease", 1710 ":$producerName:connectedFreeNonMinifiedReleaseAndroidTest", 1711 ":$producerName:collectFreeNonMinifiedReleaseBaselineProfile", 1712 ":$consumerName:mergeFreeReleaseBaselineProfile", 1713 ":$consumerName:copyFreeReleaseBaselineProfileIntoSrc", 1714 ":$consumerName:mergeFreeBenchmarkReleaseArtProfile", 1715 ":$consumerName:compileFreeBenchmarkReleaseArtProfile", 1716 ":$consumerName:packageFreeBenchmarkRelease", 1717 ":$consumerName:createFreeBenchmarkReleaseApkListingFileRedirect", 1718 ":$producerName:connectedFreeBenchmarkReleaseAndroidTest" 1719 ) 1720 1721 assertThat(notFound).isEmpty() 1722 } 1723 } 1724 1725 @Test automaticGenerationDuringBuildNotCompatibleWithLibraryModulenull1726 fun automaticGenerationDuringBuildNotCompatibleWithLibraryModule() { 1727 projectSetup.consumer.setup( 1728 androidPlugin = ANDROID_LIBRARY_PLUGIN, 1729 baselineProfileBlock = 1730 """ 1731 saveInSrc = true 1732 automaticGenerationDuringBuild = true 1733 """ 1734 .trimIndent() 1735 ) 1736 projectSetup.producer.setupWithoutFlavors( 1737 releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1738 ) 1739 1740 // Asserts that running connected checks on a benchmark variants also triggers 1741 // baseline profile generation (due to `automaticGenerationDuringBuild` true`). 1742 projectSetup.consumer.gradleRunner.buildAndFailAndAssertThatOutput( 1743 "generateBaselineProfile", 1744 "--dry-run" 1745 ) { 1746 contains( 1747 "The flag `automaticGenerationDuringBuild` is not compatible with library " + 1748 "modules. Please remove the flag `automaticGenerationDuringBuild` " + 1749 "in your com.android.library module" 1750 ) 1751 } 1752 } 1753 1754 @Test testExperimentalPropertiesSetnull1755 fun testExperimentalPropertiesSet() { 1756 projectSetup.producer.setupWithFreeAndPaidFlavors( 1757 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1758 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 1759 ) 1760 projectSetup.consumer.setup( 1761 androidPlugin = ANDROID_LIBRARY_PLUGIN, 1762 dependencyOnProducerProject = true, 1763 flavors = true, 1764 buildTypeAnotherRelease = true, 1765 baselineProfileBlock = 1766 """ 1767 baselineProfileRulesRewrite = true 1768 dexLayoutOptimization = true 1769 """ 1770 .trimIndent() 1771 ) 1772 1773 arrayOf( 1774 "printExperimentalPropertiesForVariantFreeRelease", 1775 "printExperimentalPropertiesForVariantPaidRelease", 1776 "printExperimentalPropertiesForVariantFreeAnotherRelease", 1777 "printExperimentalPropertiesForVariantPaidAnotherRelease", 1778 ) 1779 .forEach { 1780 projectSetup.consumer.gradleRunner.buildAndAssertThatOutput(it) { 1781 // These properties are ignored in agp 8.0 1782 contains("android.experimental.art-profile-r8-rewriting=true") 1783 contains("android.experimental.r8.dex-startup-optimization=true") 1784 } 1785 } 1786 } 1787 1788 @Test testGenerateTaskWithFlavorsAndMergeAllnull1789 fun testGenerateTaskWithFlavorsAndMergeAll() { 1790 projectSetup.consumer.setup( 1791 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1792 flavors = true, 1793 dependencyOnProducerProject = true, 1794 baselineProfileBlock = 1795 """ 1796 mergeIntoMain = true 1797 """ 1798 .trimIndent() 1799 ) 1800 projectSetup.producer.setupWithFreeAndPaidFlavors( 1801 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1802 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 1803 ) 1804 1805 // Asserts that all per-variant, per-flavor and per-build type tasks are being generated. 1806 projectSetup.consumer.gradleRunner.buildAndAssertThatOutput("tasks") { 1807 contains("generateBaselineProfile - ") 1808 doesNotContain("generateReleaseBaselineProfile - ") 1809 doesNotContain("generateFreeReleaseBaselineProfile - ") 1810 doesNotContain("generatePaidReleaseBaselineProfile - ") 1811 } 1812 1813 projectSetup.consumer.gradleRunner 1814 .withArguments("generateBaselineProfile", "--stacktrace") 1815 .build() 1816 1817 val lines = 1818 File( 1819 projectSetup.consumer.rootDir, 1820 "src/main/$EXPECTED_PROFILE_FOLDER/baseline-prof.txt" 1821 ) 1822 .readLines() 1823 assertThat(lines) 1824 .containsExactly( 1825 Fixtures.CLASS_1, 1826 Fixtures.CLASS_1_METHOD_1, 1827 Fixtures.CLASS_2, 1828 Fixtures.CLASS_2_METHOD_1, 1829 ) 1830 } 1831 1832 @Test testExperimentalPropertyHideVariantInAndroidStudionull1833 fun testExperimentalPropertyHideVariantInAndroidStudio() { 1834 projectSetup.producer.setupWithFreeAndPaidFlavors( 1835 freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1), 1836 paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2) 1837 ) 1838 1839 val taskList = 1840 listOf( 1841 "printExperimentalPropertiesForVariantFreeNonMinifiedRelease", 1842 "printExperimentalPropertiesForVariantFreeBenchmarkRelease", 1843 "printExperimentalPropertiesForVariantPaidNonMinifiedRelease", 1844 "printExperimentalPropertiesForVariantPaidBenchmarkRelease", 1845 "printExperimentalPropertiesForVariantFreeNonMinifiedAnotherRelease", 1846 "printExperimentalPropertiesForVariantFreeBenchmarkAnotherRelease", 1847 "printExperimentalPropertiesForVariantPaidNonMinifiedAnotherRelease", 1848 "printExperimentalPropertiesForVariantPaidBenchmarkAnotherRelease", 1849 ) 1850 1851 // Setup consumer module with DEFAULT configuration 1852 projectSetup.consumer.setup( 1853 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1854 dependencyOnProducerProject = true, 1855 flavors = true, 1856 buildTypeAnotherRelease = true, 1857 ) 1858 taskList.forEach { 1859 projectSetup.consumer.gradleRunner.buildAndAssertThatOutput(it) { 1860 contains("androidx.baselineProfile.hideInStudio=true") 1861 } 1862 } 1863 1864 // Setup consumer module NOT HIDING the build types 1865 projectSetup.consumer.setup( 1866 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1867 dependencyOnProducerProject = true, 1868 flavors = true, 1869 buildTypeAnotherRelease = true, 1870 baselineProfileBlock = 1871 """ 1872 hideSyntheticBuildTypesInAndroidStudio = false 1873 """ 1874 .trimIndent() 1875 ) 1876 taskList.forEach { 1877 projectSetup.consumer.gradleRunner.buildAndAssertThatOutput(it) { 1878 doesNotContain("androidx.baselineProfile.hideInStudio=") 1879 } 1880 } 1881 1882 // Setup consumer module HIDING the build types 1883 projectSetup.consumer.setup( 1884 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1885 dependencyOnProducerProject = true, 1886 flavors = true, 1887 buildTypeAnotherRelease = true, 1888 baselineProfileBlock = 1889 """ 1890 hideSyntheticBuildTypesInAndroidStudio = true 1891 """ 1892 .trimIndent() 1893 ) 1894 taskList.forEach { 1895 projectSetup.consumer.gradleRunner.buildAndAssertThatOutput(it) { 1896 contains("androidx.baselineProfile.hideInStudio=true") 1897 } 1898 } 1899 } 1900 } 1901 1902 @RunWith(Parameterized::class) 1903 class BaselineProfileConsumerPluginTestWithAgp83(private val agpVersion: TestAgpVersion) { 1904 1905 companion object { 1906 @Parameterized.Parameters(name = "agpVersion={0}") 1907 @JvmStatic parametersnull1908 fun parameters() = TestAgpVersion.atLeast(TEST_AGP_VERSION_8_3_1) 1909 } 1910 1911 @get:Rule 1912 val projectSetup = BaselineProfileProjectSetupRule(forceAgpVersion = agpVersion.versionString) 1913 1914 private val gradleRunner by lazy { projectSetup.consumer.gradleRunner } 1915 1916 @Test testSrcSetAreAddedToVariantsForLibrariesnull1917 fun testSrcSetAreAddedToVariantsForLibraries() { 1918 projectSetup.producer.setupWithoutFlavors() 1919 projectSetup.consumer.setup( 1920 androidPlugin = ANDROID_LIBRARY_PLUGIN, 1921 additionalGradleCodeBlock = 1922 """ 1923 androidComponents { 1924 onVariants(selector()) { variant -> 1925 tasks.register(variant.name + "Sources", DisplaySourceSets) { t -> 1926 t.srcs.set(variant.sources.baselineProfiles.all) 1927 } 1928 } 1929 } 1930 """ 1931 .trimIndent() 1932 ) 1933 1934 val expected = 1935 listOf( 1936 "src/main/baselineProfiles", 1937 "src/main/generated/baselineProfiles", 1938 "src/release/baselineProfiles", 1939 ) 1940 .map { dir -> File(projectSetup.consumer.rootDir, dir) } 1941 .onEach { f -> 1942 // Expected src set location. Note that src sets are not added if the folder 1943 // does 1944 // not exist so we need to create it. 1945 f.mkdirs() 1946 f.deleteOnExit() 1947 } 1948 1949 gradleRunner.buildAndAssertThatOutput("releaseSources") { 1950 expected.forEach { e -> contains(e.absolutePath) } 1951 } 1952 } 1953 } 1954 1955 @RunWith(Parameterized::class) 1956 class BaselineProfileConsumerPluginTestWithKmp(agpVersion: TestAgpVersion) { 1957 1958 companion object { 1959 @Parameterized.Parameters(name = "agpVersion={0}") 1960 @JvmStatic parametersnull1961 fun parameters() = TestAgpVersion.atLeast(TEST_AGP_VERSION_8_3_1) 1962 } 1963 1964 @get:Rule 1965 val projectSetup = 1966 BaselineProfileProjectSetupRule( 1967 forceAgpVersion = agpVersion.versionString, 1968 addKotlinGradlePluginToClasspath = true 1969 ) 1970 1971 private val gradleRunner by lazy { projectSetup.consumer.gradleRunner } 1972 1973 @Test testSrcSetAreAddedToVariantsForApplicationsWithKmpnull1974 fun testSrcSetAreAddedToVariantsForApplicationsWithKmp() { 1975 projectSetup.producer.setupWithoutFlavors(releaseProfileLines = listOf()) 1976 projectSetup.consumer.setupWithBlocks( 1977 androidPlugin = ANDROID_APPLICATION_PLUGIN, 1978 otherPluginsBlock = 1979 """ 1980 id("org.jetbrains.kotlin.multiplatform") 1981 """ 1982 .trimIndent(), 1983 dependenciesBlock = 1984 """ 1985 implementation(project(":${projectSetup.dependency.name}")) 1986 """ 1987 .trimIndent(), 1988 additionalGradleCodeBlock = 1989 """ 1990 kotlin { 1991 jvm { } 1992 androidTarget { } 1993 sourceSets { 1994 androidMain { } 1995 } 1996 } 1997 1998 androidComponents { 1999 onVariants(selector()) { variant -> 2000 tasks.register(variant.name + "Sources", DisplaySourceSets) { t -> 2001 t.srcs.set(variant.sources.baselineProfiles.all) 2002 } 2003 } 2004 } 2005 """ 2006 .trimIndent() 2007 ) 2008 2009 val expected = 2010 listOf( 2011 "src/main/baselineProfiles", 2012 "src/release/baselineProfiles", 2013 "src/androidRelease/generated/baselineProfiles", 2014 ) 2015 .map { dir -> File(projectSetup.consumer.rootDir, dir) } 2016 .onEach { f -> 2017 // Expected src set location. Note that src sets are not added if the folder 2018 // does 2019 // not exist so we need to create it. 2020 f.mkdirs() 2021 f.deleteOnExit() 2022 } 2023 2024 gradleRunner.buildAndAssertThatOutput("releaseSources") { 2025 expected.forEach { e -> contains(e.absolutePath) } 2026 } 2027 } 2028 2029 @Test testSrcSetAreAddedToVariantsForLibrariesWithKmpnull2030 fun testSrcSetAreAddedToVariantsForLibrariesWithKmp() { 2031 projectSetup.producer.setupWithoutFlavors(releaseProfileLines = listOf()) 2032 projectSetup.consumer.setupWithBlocks( 2033 androidPlugin = ANDROID_LIBRARY_PLUGIN, 2034 otherPluginsBlock = 2035 """ 2036 id("org.jetbrains.kotlin.multiplatform") 2037 """ 2038 .trimIndent(), 2039 dependenciesBlock = 2040 """ 2041 implementation(project(":${projectSetup.dependency.name}")) 2042 """ 2043 .trimIndent(), 2044 additionalGradleCodeBlock = 2045 """ 2046 kotlin { 2047 jvm { } 2048 androidTarget("androidTargetCustom") { } 2049 } 2050 2051 androidComponents { 2052 onVariants(selector()) { variant -> 2053 tasks.register(variant.name + "Sources", DisplaySourceSets) { t -> 2054 t.srcs.set(variant.sources.baselineProfiles.all) 2055 } 2056 } 2057 } 2058 """ 2059 .trimIndent() 2060 ) 2061 2062 val expected = 2063 listOf( 2064 "src/main/baselineProfiles", 2065 "src/release/baselineProfiles", 2066 "src/androidTargetCustomMain/generated/baselineProfiles", 2067 ) 2068 .map { dir -> File(projectSetup.consumer.rootDir, dir) } 2069 .onEach { f -> 2070 // Expected src set location. Note that src sets are not added if the folder 2071 // does 2072 // not exist so we need to create it. 2073 f.mkdirs() 2074 f.deleteOnExit() 2075 } 2076 2077 gradleRunner.buildAndAssertThatOutput("releaseSources") { 2078 expected.forEach { e -> contains(e.absolutePath) } 2079 } 2080 } 2081 } 2082 2083 @RunWith(Parameterized::class) 2084 class BaselineProfileConsumerPluginTestWithFtl(agpVersion: TestAgpVersion) { 2085 2086 companion object { 2087 @Parameterized.Parameters(name = "agpVersion={0}") 2088 @JvmStatic parametersnull2089 fun parameters() = TestAgpVersion.atLeast(TEST_AGP_VERSION_CURRENT) 2090 } 2091 2092 @get:Rule 2093 val projectSetup = BaselineProfileProjectSetupRule(forceAgpVersion = agpVersion.versionString) 2094 2095 @Test 2096 fun testGenerateBaselineProfileWithFtlArtifact() { 2097 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 2098 2099 // The difference with FTL is that artifacts are added as global artifacts instead of 2100 // per test result. This different setup can be specified in the `VariantProfile`. 2101 projectSetup.producer.setup( 2102 variantProfiles = 2103 VariantProfile.release( 2104 ftlFileLines = listOf(Fixtures.CLASS_2_METHOD_1), 2105 useGsSchema = false, 2106 ) 2107 ) 2108 2109 projectSetup.consumer.gradleRunner.build("generateBaselineProfile", "--info") { 2110 println(it) 2111 val notFound = 2112 it.lines() 2113 .requireInOrder( 2114 "A baseline profile was generated for the variant `release`:", 2115 "${projectSetup.baselineProfileFile("release").toUri()}", 2116 ) 2117 assertThat(notFound).isEmpty() 2118 } 2119 2120 assertThat(projectSetup.readBaselineProfileFileContent("release")) 2121 .containsExactly(Fixtures.CLASS_2_METHOD_1) 2122 } 2123 2124 @Test testGenerateBaselineProfileWithFtlArtifactInGoogleStorageShouldNotCrashnull2125 fun testGenerateBaselineProfileWithFtlArtifactInGoogleStorageShouldNotCrash() { 2126 projectSetup.consumer.setup(androidPlugin = ANDROID_APPLICATION_PLUGIN) 2127 2128 // The difference with FTL is that artifacts are added as global artifacts instead of 2129 // per test result. This different setup can be specified in the `VariantProfile`. 2130 projectSetup.producer.setup( 2131 variantProfiles = 2132 VariantProfile.release( 2133 ftlFileLines = listOf(Fixtures.CLASS_2_METHOD_1), 2134 useGsSchema = true, 2135 ) 2136 ) 2137 2138 projectSetup.consumer.gradleRunner.build("generateBaselineProfile", "--info") { 2139 println(it) 2140 val notFound = 2141 it.lines() 2142 .requireInOrder( 2143 "No baseline profile rules were generated for the variant `release`", 2144 "No startup profile rules were generated for the variant `release`" 2145 ) 2146 assertThat(notFound).isEmpty() 2147 } 2148 2149 assertThat(projectSetup.baselineProfileFile("release").exists()).isFalse() 2150 assertThat(projectSetup.startupProfileFile("release").exists()).isFalse() 2151 } 2152 } 2153