1 /* 2 * Copyright (C) 2017 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 com.android.tools.metalava.compatibility 18 19 import com.android.tools.lint.checks.infrastructure.TestFiles.base64gzip 20 import com.android.tools.metalava.ARG_SHOW_ANNOTATION 21 import com.android.tools.metalava.ARG_SHOW_UNANNOTATED 22 import com.android.tools.metalava.DriverTest 23 import com.android.tools.metalava.androidxNonNullSource 24 import com.android.tools.metalava.androidxNullableSource 25 import com.android.tools.metalava.cli.common.ARG_ERROR_CATEGORY 26 import com.android.tools.metalava.cli.common.ARG_HIDE 27 import com.android.tools.metalava.model.ANDROID_SYSTEM_API 28 import com.android.tools.metalava.model.provider.Capability 29 import com.android.tools.metalava.model.testing.RequiresCapabilities 30 import com.android.tools.metalava.model.text.ApiClassResolution 31 import com.android.tools.metalava.model.text.FileFormat 32 import com.android.tools.metalava.nonNullSource 33 import com.android.tools.metalava.reporter.Issues 34 import com.android.tools.metalava.restrictToSource 35 import com.android.tools.metalava.suppressLintSource 36 import com.android.tools.metalava.systemApiSource 37 import com.android.tools.metalava.testApiSource 38 import com.android.tools.metalava.testing.KnownSourceFiles 39 import com.android.tools.metalava.testing.java 40 import com.android.tools.metalava.testing.kotlin 41 import org.junit.Test 42 43 class CompatibilityCheckTest : DriverTest() { 44 @Test Change between class and interfacenull45 fun `Change between class and interface`() { 46 check( 47 expectedIssues = 48 """ 49 load-api.txt:3: error: Class test.pkg.MyTest1 changed class/interface declaration [ChangedClass] 50 load-api.txt:5: error: Class test.pkg.MyTest2 changed class/interface declaration [ChangedClass] 51 """, 52 checkCompatibilityApiReleased = 53 """ 54 package test.pkg { 55 public class MyTest1 { 56 } 57 public interface MyTest2 { 58 } 59 public class MyTest3 { 60 } 61 public interface MyTest4 { 62 } 63 } 64 """, 65 // MyTest1 and MyTest2 reversed from class to interface or vice versa, MyTest3 and 66 // MyTest4 unchanged 67 signatureSource = 68 """ 69 package test.pkg { 70 public interface MyTest1 { 71 } 72 public class MyTest2 { 73 } 74 public class MyTest3 { 75 } 76 public interface MyTest4 { 77 } 78 } 79 """ 80 ) 81 } 82 83 @Test Interfaces should not be droppednull84 fun `Interfaces should not be dropped`() { 85 check( 86 expectedIssues = 87 """ 88 load-api.txt:3: error: Class test.pkg.MyTest1 changed class/interface declaration [ChangedClass] 89 load-api.txt:5: error: Class test.pkg.MyTest2 changed class/interface declaration [ChangedClass] 90 """, 91 checkCompatibilityApiReleased = 92 """ 93 package test.pkg { 94 public class MyTest1 { 95 } 96 public interface MyTest2 { 97 } 98 public class MyTest3 { 99 } 100 public interface MyTest4 { 101 } 102 } 103 """, 104 // MyTest1 and MyTest2 reversed from class to interface or vice versa, MyTest3 and 105 // MyTest4 unchanged 106 signatureSource = 107 """ 108 package test.pkg { 109 public interface MyTest1 { 110 } 111 public class MyTest2 { 112 } 113 public class MyTest3 { 114 } 115 public interface MyTest4 { 116 } 117 } 118 """ 119 ) 120 } 121 122 @Test Ensure warnings for removed APIsnull123 fun `Ensure warnings for removed APIs`() { 124 check( 125 expectedIssues = 126 """ 127 released-api.txt:4: error: Removed method test.pkg.MyTest1.method(Float) [RemovedMethod] 128 released-api.txt:5: error: Removed field test.pkg.MyTest1.field [RemovedField] 129 released-api.txt:7: error: Removed class test.pkg.MyTest2 [RemovedClass] 130 """, 131 checkCompatibilityApiReleased = 132 """ 133 package test.pkg { 134 public class MyTest1 { 135 method public Double method(Float); 136 field public Double field; 137 } 138 public class MyTest2 { 139 method public Double method(Float); 140 field public Double field; 141 } 142 } 143 package test.pkg.other { 144 } 145 """, 146 signatureSource = 147 """ 148 package test.pkg { 149 public class MyTest1 { 150 } 151 } 152 """ 153 ) 154 } 155 156 @Test Kotlin Coroutinesnull157 fun `Kotlin Coroutines`() { 158 check( 159 expectedIssues = "", 160 format = FileFormat.V2, 161 checkCompatibilityApiReleased = 162 """ 163 // Signature format: 3.0 164 package test.pkg { 165 public final class TestKt { 166 ctor public TestKt(); 167 method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit>); 168 } 169 } 170 """, 171 signatureSource = 172 """ 173 // Signature format: 3.0 174 package test.pkg { 175 public final class TestKt { 176 ctor public TestKt(); 177 method public static suspend inline Object hello(@NonNull kotlin.coroutines.Continuation<? super kotlin.Unit> p); 178 } 179 } 180 """ 181 ) 182 } 183 184 @RequiresCapabilities(Capability.KOTLIN) 185 @Test Remove operatornull186 fun `Remove operator`() { 187 check( 188 expectedIssues = 189 """ 190 src/test/pkg/Foo.kt:4: error: Cannot remove `operator` modifier from method test.pkg.Foo.plus(String): Incompatible change [OperatorRemoval] 191 """, 192 checkCompatibilityApiReleased = 193 """ 194 // Signature format: 5.0 195 package test.pkg { 196 public final class Foo { 197 ctor public Foo(); 198 method public final operator void plus(String s); 199 } 200 } 201 """, 202 sourceFiles = 203 arrayOf( 204 kotlin( 205 """ 206 package test.pkg 207 208 class Foo { 209 fun plus(s: String) { } 210 } 211 """ 212 ) 213 ) 214 ) 215 } 216 217 @RequiresCapabilities(Capability.KOTLIN) 218 @Test Remove varargnull219 fun `Remove vararg`() { 220 check( 221 expectedIssues = 222 """ 223 src/test/pkg/test.kt:3: error: Changing from varargs to array is an incompatible change: parameter x in test.pkg.TestKt.method2(int[] x) [VarargRemoval] 224 """, 225 checkCompatibilityApiReleased = 226 """ 227 // Signature format: 5.0 228 package test.pkg { 229 public final class TestKt { 230 method public static final void method1(int[] x); 231 method public static final void method2(int... x); 232 } 233 } 234 """, 235 sourceFiles = 236 arrayOf( 237 kotlin( 238 """ 239 package test.pkg 240 fun method1(vararg x: Int) { } 241 fun method2(x: IntArray) { } 242 """ 243 ) 244 ) 245 ) 246 } 247 248 @Test Removed method from classpathnull249 fun `Removed method from classpath`() { 250 check( 251 apiClassResolution = ApiClassResolution.API_CLASSPATH, 252 classpath = 253 arrayOf( 254 /* The following source file, compiled, then ran 255 assertEquals("", toBase64gzip(File("path/to/lib1.jar"))) 256 257 package test.pkg; 258 259 public interface FacetProvider { 260 Object getFacet(Class<?> facetClass); 261 } 262 */ 263 base64gzip( 264 "libs/lib1.jar", 265 "" + 266 "H4sIAAAAAAAA/wvwZmYRYeDg4GDwKMgPZ0ACnAwsDL6uIY66nn5u+v9OMTAw" + 267 "MwR4s3OApJigSgJwahYBYrhmX0c/TzfX4BA9X7fPvmdO+3jr6l3k9dbVOnfm" + 268 "/OYggyvGD54W6Xn56nj6XixdxcI147XkC0nNjB/iqmrPl2hZPBcXfSKuOo1B" + 269 "NPtT0cciRrAbRCZqCTgBbXBCcYMamhtkgLgktbhEvyA7Xd8tMTm1JKAovywz" + 270 "JbVILzknsbjY+mv+dTs2NrZotrwyNjU3to2TrjwS+tv0pqR2+5EnVyY1LPqz" + 271 "6cyUK0plbGJubI1rjmxy+TvnyJ6S2v9L1lx5IuTG1vflitAGJze2UF75lj3F" + 272 "fkmFG7cu49/Fl+3Gdu7BmS97jky6tCjEjY2Xx9YsquzG1kLW59PFVJfvSn3G" + 273 "8JVzoYUf/5I5vRMbJzbOZGSRaMxLTU1g/nSz0UaNjU+hW/jMIyawN6/4uhXN" + 274 "BXriHdibjEwiDKiBDYsGUEyhApR4Q9eKHHoiKNpsccQasgmgUEZ2mAyKCQcJ" + 275 "hHmANysbSB0zEB4H0isYQTwAofA0RIUCAAA=" 276 ), 277 /* The following source file, compiled, then ran 278 assertEquals("", toBase64gzip(File("path/to/lib2.jar"))) 279 280 package test.pkg; 281 282 public interface FacetProviderAdapter { 283 FacetProvider getFacetProvider(int type); 284 } 285 */ 286 base64gzip( 287 "libs/lib2.jar", 288 "" + 289 "H4sIAAAAAAAA/wvwZmYRYeDg4GDwK8gPZ0ACnAwsDL6uIY66nn5u+v9OMTAw" + 290 "MwR4s3OApJigSgJwahYBYrhmX0c/TzfX4BA9X7fPvmdO+3jr6l3k9dbVOnfm" + 291 "/OYggyvGD54W6Xn56nj6XixdxcI147XkC0nNjB/iqmrPl2hZPBcXfSKuOo1B" + 292 "NPtT0cciRrAbRCZqCTgBbXBCcYMamhuUgbgktbhEvyA7Xd8tMTm1JKAovywz" + 293 "JbXIMSWxoCS1SC85J7G42Ppr/nU7Nja2aDa/MjY1N7abk648Evrb9KakdvuR" + 294 "J1cmNSz6s+nMlCtKx6ccaZp0RamMTcyNrXHNkU0uf+cc2VNS+3/JmitPhBYE" + 295 "VGVxdlW5sWXy+vvKv2mLNDYqYH0+XUx1+a7UZ0uMjDySNnvxp3BLKzMrMxsz" + 296 "cxgw5aalJjBvlLjRqCLMzA72E+ufzUeagS7eDfYTI5MIA2rIwsIcFC2oACWS" + 297 "0LUiB5UIijZbHFGEbAIoSJEdpoxiwkHiAjjAm5UNpJwZCM8B6amMIB4AmZLm" + 298 "53kCAAA=" 299 ), 300 ), 301 sourceFiles = 302 arrayOf( 303 java( 304 """ 305 package test.pkg; 306 307 public class FacetProviderAdapterImpl implements FacetProviderAdapter { 308 private FacetProvider mProvider; 309 @Override 310 public FacetProvider getFacetProvider(int type) { 311 return mProvider; 312 } 313 314 public static class FacetProviderImpl implements FacetProvider { 315 private Object mItem; 316 @Override 317 public Object getFacet(Class<?> facetClass) { 318 return mItem; 319 } 320 } 321 } 322 """ 323 ) 324 ), 325 format = FileFormat.V4, 326 checkCompatibilityApiReleased = 327 """ 328 // Signature format: 4.0 329 package test.pkg { 330 public interface FacetProvider { 331 method public Object! getFacet(Class<?>!); 332 } 333 public interface FacetProviderAdapter { 334 method public test.pkg.FacetProvider! getFacetProvider(int); 335 } 336 public class FacetProviderAdapterImpl implements test.pkg.FacetProviderAdapter { 337 method public test.pkg.FacetProvider? getFacetProvider(int); 338 } 339 public class FacetProviderAdapterImpl.FacetProviderImpl implements test.pkg.FacetProvider { 340 method public Object? getFacet(Class<? extends Object!>?); 341 } 342 } 343 """, 344 expectedIssues = 345 """ 346 released-api.txt:3: error: Removed class test.pkg.FacetProvider [RemovedInterface] 347 released-api.txt:6: error: Removed class test.pkg.FacetProviderAdapter [RemovedInterface] 348 src/test/pkg/FacetProviderAdapterImpl.java:6: error: Attempted to remove nullability from test.pkg.FacetProvider (was NULLABLE) in method test.pkg.FacetProviderAdapterImpl.getFacetProvider(int) [InvalidNullConversion] 349 src/test/pkg/FacetProviderAdapterImpl.java:13: error: Attempted to remove nullability from java.lang.Class<?> (was NULLABLE) in parameter facetClass in test.pkg.FacetProviderAdapterImpl.FacetProviderImpl.getFacet(Class<?> facetClass) [InvalidNullConversion] 350 src/test/pkg/FacetProviderAdapterImpl.java:13: error: Attempted to remove nullability from java.lang.Object (was NULLABLE) in method test.pkg.FacetProviderAdapterImpl.FacetProviderImpl.getFacet(Class<?>) [InvalidNullConversion] 351 """ 352 ) 353 } 354 355 @RequiresCapabilities(Capability.KOTLIN) 356 @Test Add final to class that can be extendednull357 fun `Add final to class that can be extended`() { 358 // Adding final on a class is incompatible. 359 check( 360 // Make AddedFinalInstantiable an error, so it is reported as an issue. 361 extraArguments = arrayOf("--error", Issues.ADDED_FINAL_UNINSTANTIABLE.name), 362 expectedIssues = 363 """ 364 src/test/pkg/Java.java:2: error: Class test.pkg.Java added 'final' qualifier [AddedFinal] 365 src/test/pkg/Java.java:4: error: Method test.pkg.Java.method has added 'final' qualifier [AddedFinal] 366 src/test/pkg/Kotlin.kt:3: error: Class test.pkg.Kotlin added 'final' qualifier [AddedFinal] 367 src/test/pkg/Kotlin.kt:4: error: Method test.pkg.Kotlin.method has added 'final' qualifier [AddedFinal] 368 """, 369 checkCompatibilityApiReleased = 370 """ 371 // Signature format: 5.0 372 package test.pkg { 373 public class Java { 374 ctor public Java(); 375 method public void method(int); 376 } 377 public class Kotlin { 378 ctor public Kotlin(); 379 method public void method(String s); 380 } 381 } 382 """, 383 sourceFiles = 384 arrayOf( 385 kotlin( 386 """ 387 package test.pkg 388 389 class Kotlin { 390 fun method(s: String) { } 391 } 392 """ 393 ), 394 java( 395 """ 396 package test.pkg; 397 public final class Java { 398 public Java() { } 399 public void method(int parameter) { } 400 } 401 """ 402 ) 403 ) 404 ) 405 } 406 407 @RequiresCapabilities(Capability.KOTLIN) 408 @Test Add final to class that cannot be extendednull409 fun `Add final to class that cannot be extended`() { 410 // Adding final on a class is incompatible unless the class could not be extended. 411 check( 412 // Make AddedFinalInstantiable an error, so it is reported as an issue. 413 extraArguments = arrayOf("--error", Issues.ADDED_FINAL_UNINSTANTIABLE.name), 414 expectedIssues = 415 """ 416 src/test/pkg/Java.java:2: error: Class test.pkg.Java added 'final' qualifier but was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 417 src/test/pkg/Java.java:4: error: Method test.pkg.Java.method added 'final' qualifier but containing class test.pkg.Java was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 418 src/test/pkg/Kotlin.kt:3: error: Class test.pkg.Kotlin added 'final' qualifier but was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 419 src/test/pkg/Kotlin.kt:5: error: Method test.pkg.Kotlin.method added 'final' qualifier but containing class test.pkg.Kotlin was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 420 """, 421 checkCompatibilityApiReleased = 422 """ 423 // Signature format: 5.0 424 package test.pkg { 425 public class Java { 426 method public void method(int); 427 } 428 public class Kotlin { 429 method public void method(String s); 430 } 431 } 432 """, 433 sourceFiles = 434 arrayOf( 435 kotlin( 436 """ 437 package test.pkg 438 439 class Kotlin 440 private constructor() { 441 fun method(s: String) { } 442 } 443 """ 444 ), 445 java( 446 """ 447 package test.pkg; 448 public final class Java { 449 private Java() { } 450 public void method(int parameter) { } 451 } 452 """ 453 ) 454 ) 455 ) 456 } 457 458 @RequiresCapabilities(Capability.KOTLIN) 459 @Test Add final to method of class that can be extendednull460 fun `Add final to method of class that can be extended`() { 461 // Adding final on a method is incompatible. 462 check( 463 // Make AddedFinalInstantiable an error, so it is reported as an issue. 464 extraArguments = arrayOf("--error", Issues.ADDED_FINAL_UNINSTANTIABLE.name), 465 expectedIssues = 466 """ 467 src/test/pkg/Java.java:4: error: Method test.pkg.Java.method has added 'final' qualifier [AddedFinal] 468 src/test/pkg/Kotlin.kt:4: error: Method test.pkg.Kotlin.method has added 'final' qualifier [AddedFinal] 469 """, 470 checkCompatibilityApiReleased = 471 """ 472 // Signature format: 5.0 473 package test.pkg { 474 public class Java { 475 ctor public Java(); 476 method public void method(int); 477 } 478 public class Kotlin { 479 ctor public Kotlin(); 480 method public void method(String s); 481 } 482 } 483 """, 484 sourceFiles = 485 arrayOf( 486 kotlin( 487 """ 488 package test.pkg 489 490 open class Kotlin { 491 fun method(s: String) { } 492 } 493 """ 494 ), 495 java( 496 """ 497 package test.pkg; 498 public class Java { 499 public Java() { } 500 public final void method(final int parameter) { } 501 } 502 """ 503 ) 504 ) 505 ) 506 } 507 508 @RequiresCapabilities(Capability.KOTLIN) 509 @Test Add final to method of class that cannot be extendednull510 fun `Add final to method of class that cannot be extended`() { 511 // Adding final on a method is incompatible unless the containing class could not be 512 // extended. 513 check( 514 // Make AddedFinalInstantiable an error, so it is reported as an issue. 515 extraArguments = arrayOf("--error", Issues.ADDED_FINAL_UNINSTANTIABLE.name), 516 expectedIssues = 517 """ 518 src/test/pkg/Java.java:4: error: Method test.pkg.Java.method added 'final' qualifier but containing class test.pkg.Java was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 519 src/test/pkg/Kotlin.kt:5: error: Method test.pkg.Kotlin.method added 'final' qualifier but containing class test.pkg.Kotlin was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 520 """ 521 .trimIndent(), 522 checkCompatibilityApiReleased = 523 """ 524 // Signature format: 5.0 525 package test.pkg { 526 public class Java { 527 method public void method(int); 528 } 529 public class Kotlin { 530 method public void method(String s); 531 } 532 } 533 """, 534 sourceFiles = 535 arrayOf( 536 kotlin( 537 """ 538 package test.pkg 539 540 open class Kotlin 541 private constructor() { 542 fun method(s: String) { } 543 } 544 """ 545 ), 546 java( 547 """ 548 package test.pkg; 549 public class Java { 550 private Java() { } 551 public final void method(final int parameter) { } 552 } 553 """ 554 ) 555 ) 556 ) 557 } 558 559 @Test Add final to method parameternull560 fun `Add final to method parameter`() { 561 // Adding final on a method parameter is fine. 562 check( 563 checkCompatibilityApiReleased = 564 """ 565 package test.pkg { 566 public class Java { 567 ctor public Java(); 568 method public void method(int); 569 } 570 } 571 """, 572 sourceFiles = 573 arrayOf( 574 java( 575 """ 576 package test.pkg; 577 public class Java { 578 public Java() { } 579 public void method(final int parameter) { } 580 } 581 """ 582 ) 583 ) 584 ) 585 } 586 587 @Test Inherited finalnull588 fun `Inherited final`() { 589 // Make sure that we correctly compare effectively final (inherited from surrounding class) 590 // between the signature file codebase and the real codebase 591 check( 592 expectedIssues = """ 593 """, 594 checkCompatibilityApiReleased = 595 """ 596 package test.pkg { 597 public final class Cls extends test.pkg.Parent { 598 } 599 public class Parent { 600 method public void method(int); 601 } 602 } 603 """, 604 sourceFiles = 605 arrayOf( 606 java( 607 """ 608 package test.pkg; 609 public final class Cls extends Parent { 610 private Cls() { } 611 @Override public void method(final int parameter) { } 612 } 613 """ 614 ), 615 java( 616 """ 617 package test.pkg; 618 public class Parent { 619 private Parent() { } 620 public void method(final int parameter) { } 621 } 622 """ 623 ) 624 ) 625 ) 626 } 627 628 @Test Implicit concretenull629 fun `Implicit concrete`() { 630 // Doclava signature files sometimes leave out overridden methods of 631 // abstract methods. We don't want to list these as having changed 632 // their abstractness. 633 check( 634 expectedIssues = """ 635 """, 636 checkCompatibilityApiReleased = 637 """ 638 package test.pkg { 639 public final class Cls extends test.pkg.Parent { 640 } 641 public class Parent { 642 method public abstract void method(int); 643 } 644 } 645 """, 646 sourceFiles = 647 arrayOf( 648 java( 649 """ 650 package test.pkg; 651 public final class Cls extends Parent { 652 private Cls() { } 653 @Override public void method(final int parameter) { } 654 } 655 """ 656 ), 657 java( 658 """ 659 package test.pkg; 660 public class Parent { 661 private Parent() { } 662 public abstract void method(final int parameter); 663 } 664 """ 665 ) 666 ) 667 ) 668 } 669 670 @Test Implicit modifiers from inherited super classesnull671 fun `Implicit modifiers from inherited super classes`() { 672 check( 673 expectedIssues = """ 674 """, 675 checkCompatibilityApiReleased = 676 """ 677 package test.pkg { 678 public final class Cls implements test.pkg.Interface { 679 method public void method(int); 680 method public final void method2(int); 681 } 682 public interface Interface { 683 method public void method2(int); 684 } 685 } 686 """, 687 sourceFiles = 688 arrayOf( 689 java( 690 """ 691 package test.pkg; 692 public final class Cls extends HiddenParent implements Interface { 693 private Cls() { } 694 @Override public void method(final int parameter) { } 695 } 696 """ 697 ), 698 java( 699 """ 700 package test.pkg; 701 class HiddenParent { 702 private HiddenParent() { } 703 public abstract void method(final int parameter) { } 704 public final void method2(final int parameter) { } 705 } 706 """ 707 ), 708 java( 709 """ 710 package test.pkg; 711 public interface Interface { 712 void method2(final int parameter) { } 713 } 714 """ 715 ) 716 ) 717 ) 718 } 719 720 @Test Wildcard comparisonsnull721 fun `Wildcard comparisons`() { 722 // Doclava signature files sometimes leave out overridden methods of 723 // abstract methods. We don't want to list these as having changed 724 // their abstractness. 725 check( 726 expectedIssues = """ 727 """, 728 checkCompatibilityApiReleased = 729 """ 730 package test.pkg { 731 public abstract class AbstractMap<K, V> implements java.util.Map { 732 method public java.util.Set<K> keySet(); 733 method public V put(K, V); 734 method public void putAll(java.util.Map<? extends K, ? extends V>); 735 } 736 public abstract class EnumMap<K extends java.lang.Enum<K>, V> extends test.pkg.AbstractMap { 737 } 738 } 739 """, 740 sourceFiles = 741 arrayOf( 742 java( 743 """ 744 package test.pkg; 745 @SuppressWarnings({"ConstantConditions", "NullableProblems"}) 746 public abstract class AbstractMap<K, V> implements java.util.Map { 747 private AbstractMap() { } 748 public V put(K k, V v) { return null; } 749 public java.util.Set<K> keySet() { return null; } 750 public void putAll(java.util.Map<? extends K, ? extends V> x) { } 751 } 752 """ 753 ), 754 java( 755 """ 756 package test.pkg; 757 public abstract class EnumMap<K extends java.lang.Enum<K>, V> extends test.pkg.AbstractMap { 758 private EnumMap() { } 759 public V put(K k, V v) { return null; } 760 } 761 """ 762 ) 763 ) 764 ) 765 } 766 767 @Test Added constructornull768 fun `Added constructor`() { 769 // Regression test for issue 116619591 770 check( 771 expectedIssues = "", 772 checkCompatibilityApiReleased = 773 """ 774 package test.pkg { 775 public abstract class AbstractMap<K, V> implements java.util.Map { 776 } 777 } 778 """, 779 sourceFiles = 780 arrayOf( 781 java( 782 """ 783 package test.pkg; 784 @SuppressWarnings({"ConstantConditions", "NullableProblems"}) 785 public abstract class AbstractMap<K, V> implements java.util.Map { 786 } 787 """ 788 ) 789 ) 790 ) 791 } 792 793 @RequiresCapabilities(Capability.KOTLIN) 794 @Test Remove infixnull795 fun `Remove infix`() { 796 check( 797 expectedIssues = 798 """ 799 src/test/pkg/Foo.kt:5: error: Cannot remove `infix` modifier from method test.pkg.Foo.add2(String): Incompatible change [InfixRemoval] 800 """, 801 checkCompatibilityApiReleased = 802 """ 803 // Signature format: 5.0 804 package test.pkg { 805 public final class Foo { 806 ctor public Foo(); 807 method public final void add1(String s); 808 method public final infix void add2(String s); 809 method public final infix void add3(String s); 810 } 811 } 812 """, 813 sourceFiles = 814 arrayOf( 815 kotlin( 816 """ 817 package test.pkg 818 819 class Foo { 820 infix fun add1(s: String) { } 821 fun add2(s: String) { } 822 infix fun add3(s: String) { } 823 } 824 """ 825 ) 826 ) 827 ) 828 } 829 830 @RequiresCapabilities(Capability.KOTLIN) 831 @Test Add sealnull832 fun `Add seal`() { 833 check( 834 expectedIssues = 835 """ 836 src/test/pkg/Foo.kt:2: error: Cannot add 'sealed' modifier to class test.pkg.Foo: Incompatible change [AddSealed] 837 """, 838 checkCompatibilityApiReleased = 839 """ 840 package test.pkg { 841 public class Foo { 842 } 843 } 844 """, 845 sourceFiles = 846 arrayOf( 847 kotlin( 848 """ 849 package test.pkg 850 sealed class Foo 851 """ 852 ) 853 ) 854 ) 855 } 856 857 @RequiresCapabilities(Capability.KOTLIN) 858 @Test Remove default parameternull859 fun `Remove default parameter`() { 860 check( 861 expectedIssues = 862 """ 863 src/test/pkg/Foo.kt:3: error: Attempted to remove default value from parameter s1 in test.pkg.Foo [DefaultValueChange] 864 src/test/pkg/Foo.kt:7: error: Attempted to remove default value from parameter s1 in test.pkg.Foo.method4 [DefaultValueChange] 865 866 """, 867 checkCompatibilityApiReleased = 868 """ 869 // Signature format: 3.0 870 package test.pkg { 871 public final class Foo { 872 ctor public Foo(String? s1 = null); 873 method public final void method1(boolean b, String? s1); 874 method public final void method2(boolean b, String? s1); 875 method public final void method3(boolean b, String? s1 = "null"); 876 method public final void method4(boolean b, String? s1 = "null"); 877 } 878 } 879 """, 880 sourceFiles = 881 arrayOf( 882 kotlin( 883 """ 884 package test.pkg 885 886 class Foo(s1: String?) { 887 fun method1(b: Boolean, s1: String?) { } // No change 888 fun method2(b: Boolean, s1: String? = null) { } // Adding: OK 889 fun method3(b: Boolean, s1: String? = null) { } // No change 890 fun method4(b: Boolean, s1: String?) { } // Removed 891 } 892 """ 893 ) 894 ) 895 ) 896 } 897 898 @RequiresCapabilities(Capability.KOTLIN) 899 @Test Remove optional parameternull900 fun `Remove optional parameter`() { 901 check( 902 expectedIssues = 903 """ 904 src/test/pkg/Foo.kt:3: error: Attempted to remove default value from parameter s1 in test.pkg.Foo [DefaultValueChange] 905 src/test/pkg/Foo.kt:7: error: Attempted to remove default value from parameter s1 in test.pkg.Foo.method4 [DefaultValueChange] 906 """, 907 format = FileFormat.V4, 908 checkCompatibilityApiReleased = 909 """ 910 // Signature format: 3.0 911 package test.pkg { 912 public final class Foo { 913 ctor public Foo(optional String? s1); 914 method public final void method1(boolean b, String? s1); 915 method public final void method2(boolean b, String? s1); 916 method public final void method3(boolean b, optional String? s1); 917 method public final void method4(boolean b, optional String? s1); 918 } 919 } 920 """, 921 sourceFiles = 922 arrayOf( 923 kotlin( 924 """ 925 package test.pkg 926 927 class Foo(s1: String?) { // Removed 928 fun method1(b: Boolean, s1: String?) { } // No change 929 fun method2(b: Boolean, s1: String? = null) { } // Adding: OK 930 fun method3(b: Boolean, s1: String? = null) { } // No change 931 fun method4(b: Boolean, s1: String?) { } // Removed 932 } 933 """ 934 ) 935 ) 936 ) 937 } 938 939 @Test Removing method or field when still available via inheritance is OKnull940 fun `Removing method or field when still available via inheritance is OK`() { 941 check( 942 expectedIssues = """ 943 """, 944 checkCompatibilityApiReleased = 945 """ 946 package test.pkg { 947 public class Child extends test.pkg.Parent { 948 ctor public Child(); 949 field public int field1; 950 method public void method1(); 951 } 952 public class Parent { 953 ctor public Parent(); 954 field public int field1; 955 field public int field2; 956 method public void method1(); 957 method public void method2(); 958 } 959 } 960 """, 961 sourceFiles = 962 arrayOf( 963 java( 964 """ 965 package test.pkg; 966 967 public class Parent { 968 public int field1 = 0; 969 public int field2 = 0; 970 public void method1() { } 971 public void method2() { } 972 } 973 """ 974 ), 975 java( 976 """ 977 package test.pkg; 978 979 public class Child extends Parent { 980 public int field1 = 0; 981 @Override public void method1() { } // NO CHANGE 982 //@Override public void method2() { } // REMOVED OK: Still inherited 983 } 984 """ 985 ) 986 ) 987 ) 988 } 989 990 @Test Change field constant value, change field typenull991 fun `Change field constant value, change field type`() { 992 check( 993 expectedIssues = 994 """ 995 src/test/pkg/Parent.java:5: error: Field test.pkg.Parent.field2 has changed value from 2 to 42 [ChangedValue] 996 src/test/pkg/Parent.java:6: error: Field test.pkg.Parent.field3 has changed type from int to char [ChangedType] 997 src/test/pkg/Parent.java:7: error: Field test.pkg.Parent.field4 has added 'final' qualifier [AddedFinal] 998 src/test/pkg/Parent.java:8: error: Field test.pkg.Parent.field5 has changed 'static' qualifier [ChangedStatic] 999 src/test/pkg/Parent.java:10: error: Field test.pkg.Parent.field7 has changed 'volatile' qualifier [ChangedVolatile] 1000 src/test/pkg/Parent.java:20: error: Field test.pkg.Parent.field94 has changed value from 1 to 42 [ChangedValue] 1001 """, 1002 checkCompatibilityApiReleased = 1003 """ 1004 package test.pkg { 1005 public class Parent { 1006 ctor public Parent(); 1007 field public static final int field1 = 1; // 0x1 1008 field public static final int field2 = 2; // 0x2 1009 field public int field3; 1010 field public int field4 = 4; // 0x4 1011 field public int field5; 1012 field public int field6; 1013 field public int field7; 1014 field public deprecated int field8; 1015 field public int field9; 1016 field public static final int field91 = 1; // 0x1 1017 field public static final int field92 = 1; // 0x1 1018 field public static final int field93 = 1; // 0x1 1019 field public static final int field94 = 1; // 0x1 1020 } 1021 } 1022 """, 1023 sourceFiles = 1024 arrayOf( 1025 java( 1026 """ 1027 package test.pkg; 1028 import android.annotation.SuppressLint; 1029 public class Parent { 1030 public static final int field1 = 1; // UNCHANGED 1031 public static final int field2 = 42; // CHANGED VALUE 1032 public char field3 = 3; // CHANGED TYPE 1033 public final int field4 = 4; // ADDED FINAL 1034 public static int field5 = 5; // ADDED STATIC 1035 public transient int field6 = 6; // ADDED TRANSIENT 1036 public volatile int field7 = 7; // ADDED VOLATILE 1037 public int field8 = 8; // REMOVED DEPRECATED 1038 /** @deprecated */ @Deprecated public int field9 = 8; // ADDED DEPRECATED 1039 @SuppressLint("ChangedValue") 1040 public static final int field91 = 42;// CHANGED VALUE: Suppressed 1041 @SuppressLint("ChangedValue:Field test.pkg.Parent.field92 has changed value from 1 to 42") 1042 public static final int field92 = 42;// CHANGED VALUE: Suppressed with same message 1043 @SuppressLint("ChangedValue: Field test.pkg.Parent.field93 has changed value from 1 to 42") 1044 public static final int field93 = 42;// CHANGED VALUE: Suppressed with same message 1045 @SuppressLint("ChangedValue:Field test.pkg.Parent.field94 has changed value from 10 to 1") 1046 public static final int field94 = 42;// CHANGED VALUE: Suppressed but with different message 1047 } 1048 """ 1049 ), 1050 suppressLintSource, 1051 // Hide android.annotation classes. 1052 KnownSourceFiles.androidAnnotationHide, 1053 ), 1054 ) 1055 } 1056 1057 @Test Change annotation default method value changenull1058 fun `Change annotation default method value change`() { 1059 check( 1060 expectedIssues = 1061 """ 1062 src/test/pkg/ExportedProperty.java:13: error: Method test.pkg.ExportedProperty.prefix has changed value from "" to "hello" [ChangedValue] 1063 src/test/pkg/ExportedProperty.java:14: error: Method test.pkg.ExportedProperty.floating has changed value from 1.0f to 1.1f [ChangedValue] 1064 src/test/pkg/ExportedProperty.java:15: error: Method test.pkg.ExportedProperty.category has changed value from "" to nothing [ChangedValue] 1065 """, 1066 checkCompatibilityApiReleased = 1067 """ 1068 // Signature format: 3.0 1069 package test.pkg { 1070 public @interface ExportedProperty { 1071 method public abstract boolean resolveId() default false; 1072 method public abstract float floating() default 1.0f; 1073 method public abstract String! prefix() default ""; 1074 method public abstract String! category() default ""; 1075 method public abstract boolean formatToHexString(); 1076 } 1077 } 1078 """, 1079 sourceFiles = 1080 arrayOf( 1081 java( 1082 """ 1083 package test.pkg; 1084 1085 import java.lang.annotation.ElementType; 1086 import java.lang.annotation.Retention; 1087 import java.lang.annotation.RetentionPolicy; 1088 import java.lang.annotation.Target; 1089 import static java.lang.annotation.RetentionPolicy.SOURCE; 1090 1091 @Target({ElementType.FIELD, ElementType.METHOD}) 1092 @Retention(RetentionPolicy.RUNTIME) 1093 public @interface ExportedProperty { 1094 boolean resolveId() default false; // UNCHANGED 1095 String prefix() default "hello"; // CHANGED VALUE 1096 float floating() default 1.1f; // CHANGED VALUE 1097 String category(); // REMOVED VALUE 1098 boolean formatToHexString() default false; // ADDED VALUE 1099 } 1100 """ 1101 ) 1102 ) 1103 ) 1104 } 1105 1106 @Test Incompatible class change -- class to interfacenull1107 fun `Incompatible class change -- class to interface`() { 1108 check( 1109 expectedIssues = 1110 """ 1111 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent changed class/interface declaration [ChangedClass] 1112 """, 1113 checkCompatibilityApiReleased = 1114 """ 1115 package test.pkg { 1116 public class Parent { 1117 } 1118 } 1119 """, 1120 sourceFiles = 1121 arrayOf( 1122 java( 1123 """ 1124 package test.pkg; 1125 1126 public interface Parent { 1127 } 1128 """ 1129 ) 1130 ) 1131 ) 1132 } 1133 1134 @Test Incompatible class change -- change implemented interfacesnull1135 fun `Incompatible class change -- change implemented interfaces`() { 1136 check( 1137 expectedIssues = 1138 """ 1139 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent no longer implements java.io.Closeable [RemovedInterface] 1140 """, 1141 checkCompatibilityApiReleased = 1142 """ 1143 package test.pkg { 1144 public abstract class Parent implements java.io.Closeable, java.util.Map { 1145 } 1146 } 1147 """, 1148 sourceFiles = 1149 arrayOf( 1150 java( 1151 """ 1152 package test.pkg; 1153 1154 public abstract class Parent implements java.util.Map, java.util.List { 1155 private Parent() {} 1156 } 1157 """ 1158 ) 1159 ) 1160 ) 1161 } 1162 1163 @Test Incompatible class change -- change qualifiersnull1164 fun `Incompatible class change -- change qualifiers`() { 1165 check( 1166 expectedIssues = 1167 """ 1168 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent changed 'abstract' qualifier [ChangedAbstract] 1169 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent changed 'static' qualifier [ChangedStatic] 1170 """, 1171 checkCompatibilityApiReleased = 1172 """ 1173 package test.pkg { 1174 public class Parent { 1175 } 1176 } 1177 """, 1178 sourceFiles = 1179 arrayOf( 1180 java( 1181 """ 1182 package test.pkg; 1183 1184 public abstract static class Parent { 1185 private Parent() {} 1186 } 1187 """ 1188 ) 1189 ) 1190 ) 1191 } 1192 1193 @Test Incompatible class change -- finalnull1194 fun `Incompatible class change -- final`() { 1195 check( 1196 expectedIssues = 1197 """ 1198 released-api.txt:4: error: Removed constructor test.pkg.Class1() [RemovedMethod] 1199 src/test/pkg/Class1.java:3: error: Class test.pkg.Class1 added 'final' qualifier [AddedFinal] 1200 """, 1201 checkCompatibilityApiReleased = 1202 """ 1203 package test.pkg { 1204 public class Class1 { 1205 ctor public Class1(); 1206 } 1207 public class Class2 { 1208 } 1209 public final class Class3 { 1210 } 1211 } 1212 """, 1213 sourceFiles = 1214 arrayOf( 1215 java( 1216 """ 1217 package test.pkg; 1218 1219 public final class Class1 { 1220 private Class1() {} 1221 } 1222 """ 1223 ), 1224 java( 1225 """ 1226 package test.pkg; 1227 1228 public final class Class2 { 1229 private Class2() {} 1230 } 1231 """ 1232 ), 1233 java( 1234 """ 1235 package test.pkg; 1236 1237 public class Class3 { 1238 private Class3() {} 1239 } 1240 """ 1241 ) 1242 ) 1243 ) 1244 } 1245 1246 @Test Incompatible class change -- visibilitynull1247 fun `Incompatible class change -- visibility`() { 1248 check( 1249 expectedIssues = 1250 """ 1251 src/test/pkg/Class1.java:3: error: Class test.pkg.Class1 changed visibility from protected to public [ChangedScope] 1252 src/test/pkg/Class2.java:3: error: Class test.pkg.Class2 changed visibility from public to protected [ChangedScope] 1253 """, 1254 checkCompatibilityApiReleased = 1255 """ 1256 package test.pkg { 1257 protected class Class1 { 1258 } 1259 public class Class2 { 1260 } 1261 } 1262 """, 1263 sourceFiles = 1264 arrayOf( 1265 java( 1266 """ 1267 package test.pkg; 1268 1269 public class Class1 { 1270 private Class1() {} 1271 } 1272 """ 1273 ), 1274 java( 1275 """ 1276 package test.pkg; 1277 1278 protected class Class2 { 1279 private Class2() {} 1280 } 1281 """ 1282 ) 1283 ) 1284 ) 1285 } 1286 1287 @Test Incompatible class change -- superclassnull1288 fun `Incompatible class change -- superclass`() { 1289 check( 1290 expectedIssues = 1291 """ 1292 src/test/pkg/Class3.java:3: error: Class test.pkg.Class3 superclass changed from java.lang.Char to java.lang.Number [ChangedSuperclass] 1293 """, 1294 checkCompatibilityApiReleased = 1295 """ 1296 package test.pkg { 1297 public abstract class Class1 { 1298 } 1299 public abstract class Class2 extends java.lang.Number { 1300 } 1301 public abstract class Class3 extends java.lang.Char { 1302 } 1303 } 1304 """, 1305 sourceFiles = 1306 arrayOf( 1307 java( 1308 """ 1309 package test.pkg; 1310 1311 public abstract class Class1 extends java.lang.Short { 1312 private Class1() {} 1313 } 1314 """ 1315 ), 1316 java( 1317 """ 1318 package test.pkg; 1319 1320 public abstract class Class2 extends java.lang.Float { 1321 private Class2() {} 1322 } 1323 """ 1324 ), 1325 java( 1326 """ 1327 package test.pkg; 1328 1329 public abstract class Class3 extends java.lang.Number { 1330 private Class3() {} 1331 } 1332 """ 1333 ) 1334 ) 1335 ) 1336 } 1337 1338 @Test allow adding first type parameternull1339 fun `allow adding first type parameter`() { 1340 check( 1341 checkCompatibilityApiReleased = 1342 """ 1343 package test.pkg { 1344 public class Foo { 1345 } 1346 } 1347 """, 1348 signatureSource = 1349 """ 1350 package test.pkg { 1351 public class Foo<T> { 1352 } 1353 } 1354 """ 1355 ) 1356 } 1357 1358 @Test disallow removing type parameternull1359 fun `disallow removing type parameter`() { 1360 check( 1361 expectedIssues = 1362 """ 1363 load-api.txt:3: error: Class test.pkg.Foo changed number of type parameters from 1 to 0 [ChangedType] 1364 """, 1365 checkCompatibilityApiReleased = 1366 """ 1367 package test.pkg { 1368 public class Foo<T> { 1369 } 1370 } 1371 """, 1372 signatureSource = 1373 """ 1374 package test.pkg { 1375 public class Foo { 1376 } 1377 } 1378 """ 1379 ) 1380 } 1381 1382 @Test disallow changing number of type parametersnull1383 fun `disallow changing number of type parameters`() { 1384 check( 1385 expectedIssues = 1386 """ 1387 load-api.txt:3: error: Class test.pkg.Foo changed number of type parameters from 1 to 2 [ChangedType] 1388 """, 1389 checkCompatibilityApiReleased = 1390 """ 1391 package test.pkg { 1392 public class Foo<A> { 1393 } 1394 } 1395 """, 1396 signatureSource = 1397 """ 1398 package test.pkg { 1399 public class Foo<A,B> { 1400 } 1401 } 1402 """ 1403 ) 1404 } 1405 1406 @Test Incompatible method change -- modifiersnull1407 fun `Incompatible method change -- modifiers`() { 1408 check( 1409 expectedIssues = 1410 """ 1411 src/test/pkg/MyClass.java:5: error: Method test.pkg.MyClass.myMethod2 has changed 'abstract' qualifier [ChangedAbstract] 1412 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.myMethod3 has changed 'static' qualifier [ChangedStatic] 1413 """, 1414 checkCompatibilityApiReleased = 1415 """ 1416 package test.pkg { 1417 public abstract class MyClass { 1418 method public void myMethod2(); 1419 method public void myMethod3(); 1420 method deprecated public void myMethod4(); 1421 } 1422 } 1423 """, 1424 sourceFiles = 1425 arrayOf( 1426 java( 1427 """ 1428 package test.pkg; 1429 1430 public abstract class MyClass { 1431 private MyClass() {} 1432 public native abstract void myMethod2(); // Note that Errors.CHANGE_NATIVE is hidden by default 1433 public static void myMethod3() {} 1434 public void myMethod4() {} 1435 } 1436 """ 1437 ) 1438 ) 1439 ) 1440 } 1441 1442 @Test Incompatible method change -- finalnull1443 fun `Incompatible method change -- final`() { 1444 check( 1445 expectedIssues = 1446 """ 1447 src/test/pkg/Outer.java:7: error: Method test.pkg.Outer.Class1.method1 has added 'final' qualifier [AddedFinal] 1448 src/test/pkg/Outer.java:19: error: Method test.pkg.Outer.Class4.method4 has removed 'final' qualifier [RemovedFinalStrict] 1449 """, 1450 checkCompatibilityApiReleased = 1451 """ 1452 package test.pkg { 1453 public abstract class Outer { 1454 } 1455 public class Outer.Class1 { 1456 ctor public Class1(); 1457 method public void method1(); 1458 } 1459 public final class Outer.Class2 { 1460 method public void method2(); 1461 } 1462 public final class Outer.Class3 { 1463 method public void method3(); 1464 } 1465 public class Outer.Class4 { 1466 method public final void method4(); 1467 } 1468 } 1469 """, 1470 sourceFiles = 1471 arrayOf( 1472 java( 1473 """ 1474 package test.pkg; 1475 1476 public abstract class Outer { 1477 private Outer() {} 1478 public class Class1 { 1479 public Class1() {} 1480 public final void method1() { } // Added final 1481 } 1482 public final class Class2 { 1483 private Class2() {} 1484 public final void method2() { } // Added final but class is effectively final so no change 1485 } 1486 public final class Class3 { 1487 private Class3() {} 1488 public void method3() { } // Removed final but is still effectively final 1489 } 1490 public class Class4 { 1491 private Class4() {} 1492 public void method4() { } // Removed final 1493 } 1494 } 1495 """ 1496 ) 1497 ) 1498 ) 1499 } 1500 1501 @Test Incompatible method change -- visibilitynull1502 fun `Incompatible method change -- visibility`() { 1503 check( 1504 expectedIssues = 1505 """ 1506 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.myMethod2 changed visibility from public to protected [ChangedScope] 1507 """, 1508 checkCompatibilityApiReleased = 1509 """ 1510 package test.pkg { 1511 public abstract class MyClass { 1512 method protected void myMethod1(); 1513 method public void myMethod2(); 1514 } 1515 } 1516 """, 1517 sourceFiles = 1518 arrayOf( 1519 java( 1520 """ 1521 package test.pkg; 1522 1523 public abstract class MyClass { 1524 private MyClass() {} 1525 public void myMethod1() {} 1526 protected void myMethod2() {} 1527 } 1528 """ 1529 ) 1530 ) 1531 ) 1532 } 1533 1534 @Test Incompatible method change -- return typesnull1535 fun `Incompatible method change -- return types`() { 1536 check( 1537 expectedIssues = 1538 """ 1539 src/test/pkg/MyClass.java:5: error: Method test.pkg.MyClass.method1 has changed return type from float to int [ChangedType] 1540 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.method2 has changed return type from java.util.List<java.lang.Number> to java.util.List<java.lang.Integer> [ChangedType] 1541 src/test/pkg/MyClass.java:7: error: Method test.pkg.MyClass.method3 has changed return type from java.util.List<java.lang.Integer> to java.util.List<java.lang.Number> [ChangedType] 1542 src/test/pkg/MyClass.java:8: error: Method test.pkg.MyClass.method4 has changed return type from java.lang.String to java.lang.String[] [ChangedType] 1543 src/test/pkg/MyClass.java:9: error: Method test.pkg.MyClass.method5 has changed return type from java.lang.String[] to java.lang.String[][] [ChangedType] 1544 src/test/pkg/MyClass.java:11: error: Method test.pkg.MyClass.method7 has changed return type from T (extends java.lang.Number) to java.lang.Number [ChangedType] 1545 src/test/pkg/MyClass.java:13: error: Method test.pkg.MyClass.method9 has changed return type from X (extends java.lang.Throwable) to U (extends java.lang.Number) [ChangedType] 1546 """, 1547 checkCompatibilityApiReleased = 1548 """ 1549 package test.pkg { 1550 public abstract class MyClass<T extends Number> { 1551 method public float method1(); 1552 method public java.util.List<java.lang.Number> method2(); 1553 method public java.util.List<java.lang.Integer> method3(); 1554 method public String method4(); 1555 method public String[] method5(); 1556 method public <X extends java.lang.Throwable> T method6(java.util.function.Supplier<? extends X>); 1557 method public <X extends java.lang.Throwable> T method7(java.util.function.Supplier<? extends X>); 1558 method public <X extends java.lang.Throwable> Number method8(java.util.function.Supplier<? extends X>); 1559 method public <X extends java.lang.Throwable> X method9(java.util.function.Supplier<? extends X>); 1560 } 1561 } 1562 """, 1563 sourceFiles = 1564 arrayOf( 1565 java( 1566 """ 1567 package test.pkg; 1568 1569 public abstract class MyClass<U extends Number> { // Changing type variable name is fine/compatible 1570 private MyClass() {} 1571 public int method1() { return 0; } 1572 public java.util.List<Integer> method2() { return null; } 1573 public java.util.List<Number> method3() { return null; } 1574 public String[] method4() { return null; } 1575 public String[][] method5() { return null; } 1576 public <X extends java.lang.Throwable> U method6(java.util.function.Supplier<? extends X> arg) { return null; } 1577 public <X extends java.lang.Throwable> Number method7(java.util.function.Supplier<? extends X> arg) { return null; } 1578 public <X extends java.lang.Throwable> U method8(java.util.function.Supplier<? extends X> arg) { return null; } 1579 public <X extends java.lang.Throwable> U method9(java.util.function.Supplier<? extends X> arg) { return null; } 1580 } 1581 """ 1582 ) 1583 ) 1584 ) 1585 } 1586 1587 @Test Incompatible field change -- visibility and removing finalnull1588 fun `Incompatible field change -- visibility and removing final`() { 1589 check( 1590 expectedIssues = 1591 """ 1592 src/test/pkg/MyClass.java:6: error: Field test.pkg.MyClass.myField2 changed visibility from public to protected [ChangedScope] 1593 """, 1594 checkCompatibilityApiReleased = 1595 """ 1596 package test.pkg { 1597 public abstract class MyClass { 1598 field protected int myField1; 1599 field public int myField2; 1600 field public final int myField3; 1601 } 1602 } 1603 """, 1604 sourceFiles = 1605 arrayOf( 1606 java( 1607 """ 1608 package test.pkg; 1609 1610 public abstract class MyClass { 1611 private MyClass() {} 1612 public int myField1 = 1; 1613 protected int myField2 = 1; 1614 public int myField3 = 1; 1615 } 1616 """ 1617 ) 1618 ) 1619 ) 1620 } 1621 1622 @Test Adding classes, interfaces and packages, and removing thesenull1623 fun `Adding classes, interfaces and packages, and removing these`() { 1624 check( 1625 expectedIssues = 1626 """ 1627 released-api.txt:3: error: Removed class test.pkg.MyOldClass [RemovedClass] 1628 released-api.txt:6: error: Removed package test.pkg3 [RemovedPackage] 1629 """, 1630 checkCompatibilityApiReleased = 1631 """ 1632 package test.pkg { 1633 public abstract class MyOldClass { 1634 } 1635 } 1636 package test.pkg3 { 1637 public abstract class MyOldClass { 1638 } 1639 } 1640 """, 1641 sourceFiles = 1642 arrayOf( 1643 java( 1644 """ 1645 package test.pkg; 1646 1647 public abstract class MyClass { 1648 private MyClass() {} 1649 } 1650 """ 1651 ), 1652 java( 1653 """ 1654 package test.pkg; 1655 1656 public interface MyInterface { 1657 } 1658 """ 1659 ), 1660 java( 1661 """ 1662 package test.pkg2; 1663 1664 public abstract class MyClass2 { 1665 private MyClass2() {} 1666 } 1667 """ 1668 ) 1669 ) 1670 ) 1671 } 1672 1673 @Test Test removing public constructornull1674 fun `Test removing public constructor`() { 1675 check( 1676 expectedIssues = 1677 """ 1678 released-api.txt:4: error: Removed constructor test.pkg.MyClass() [RemovedMethod] 1679 """, 1680 checkCompatibilityApiReleased = 1681 """ 1682 package test.pkg { 1683 public abstract class MyClass { 1684 ctor public MyClass(); 1685 } 1686 } 1687 """, 1688 sourceFiles = 1689 arrayOf( 1690 java( 1691 """ 1692 package test.pkg; 1693 1694 public abstract class MyClass { 1695 private MyClass() {} 1696 } 1697 """ 1698 ) 1699 ) 1700 ) 1701 } 1702 1703 @Test Test type variables from text signature filesnull1704 fun `Test type variables from text signature files`() { 1705 check( 1706 expectedIssues = 1707 """ 1708 src/test/pkg/MyClass.java:8: error: Method test.pkg.MyClass.myMethod4 has changed return type from S (extends java.lang.Object) to S (extends java.lang.Float) [ChangedType] 1709 """, 1710 checkCompatibilityApiReleased = 1711 """ 1712 package test.pkg { 1713 public abstract class MyClass<T extends test.pkg.Number,T_SPLITR> { 1714 method public T myMethod1(); 1715 method public <S extends test.pkg.Number> S myMethod2(); 1716 method public <S> S myMethod3(); 1717 method public <S> S myMethod4(); 1718 method public java.util.List<byte[]> myMethod5(); 1719 method public T_SPLITR[] myMethod6(); 1720 method public String myMethod7(); 1721 } 1722 public class Number { 1723 ctor public Number(); 1724 } 1725 } 1726 """, 1727 sourceFiles = 1728 arrayOf( 1729 java( 1730 """ 1731 package test.pkg; 1732 1733 public abstract class MyClass<T extends Number,T_SPLITR> { 1734 private MyClass() {} 1735 public T myMethod1() { return null; } 1736 public <S extends Number> S myMethod2() { return null; } 1737 public <S> S myMethod3() { return null; } 1738 public <S extends Float> S myMethod4() { return null; } 1739 public java.util.List<byte[]> myMethod5() { return null; } 1740 public T_SPLITR[] myMethod6() { return null; } 1741 public <S extends String> S myMethod7() { return null; } 1742 } 1743 """ 1744 ), 1745 java( 1746 """ 1747 package test.pkg; 1748 public class Number { 1749 } 1750 """ 1751 ) 1752 ) 1753 ) 1754 } 1755 1756 @Test Test fields with type variable types are correctly parsed as type variablesnull1757 fun `Test fields with type variable types are correctly parsed as type variables`() { 1758 check( 1759 expectedIssues = 1760 """ 1761 src/test/pkg/MyClass.java:5: error: Field test.pkg.MyClass.myField has changed type from String to java.lang.String [ChangedType] 1762 """, 1763 // If MyClass did not have a type parameter named String, myField would be parsed as 1764 // type java.lang.String 1765 checkCompatibilityApiReleased = 1766 """ 1767 package test.pkg { 1768 public abstract class MyClass<String> { 1769 field public String myField; 1770 } 1771 } 1772 """, 1773 sourceFiles = 1774 arrayOf( 1775 java( 1776 """ 1777 package test.pkg; 1778 1779 public abstract class MyClass<String> { 1780 private MyClass() {} 1781 public java.lang.String myField; 1782 } 1783 """ 1784 ) 1785 ) 1786 ) 1787 } 1788 1789 @RequiresCapabilities(Capability.KOTLIN) 1790 @Test Test Kotlin extensionsnull1791 fun `Test Kotlin extensions`() { 1792 check( 1793 format = FileFormat.V2, 1794 expectedIssues = "", 1795 checkCompatibilityApiReleased = 1796 """ 1797 // Signature format: 3.0 1798 package androidx.content { 1799 public final class ContentValuesKt { 1800 method public static android.content.ContentValues contentValuesOf(kotlin.Pair<String,Object?>... pairs); 1801 } 1802 } 1803 """, 1804 sourceFiles = 1805 arrayOf( 1806 kotlin( 1807 "src/androidx/content/ContentValues.kt", 1808 """ 1809 package androidx.content 1810 1811 import android.content.ContentValues 1812 1813 fun contentValuesOf(vararg pairs: Pair<String, Any?>) = ContentValues(pairs.size).apply { 1814 for ((key, value) in pairs) { 1815 when (value) { 1816 null -> putNull(key) 1817 is String -> put(key, value) 1818 is Int -> put(key, value) 1819 is Long -> put(key, value) 1820 is Boolean -> put(key, value) 1821 is Float -> put(key, value) 1822 is Double -> put(key, value) 1823 is ByteArray -> put(key, value) 1824 is Byte -> put(key, value) 1825 is Short -> put(key, value) 1826 else -> { 1827 val valueType = value.javaClass.canonicalName 1828 throw IllegalArgumentException("Illegal value type") 1829 } 1830 } 1831 } 1832 } 1833 """ 1834 ) 1835 ) 1836 ) 1837 } 1838 1839 @RequiresCapabilities(Capability.KOTLIN) 1840 @Test Test Kotlin type boundsnull1841 fun `Test Kotlin type bounds`() { 1842 check( 1843 format = FileFormat.V2, 1844 expectedIssues = "", 1845 checkCompatibilityApiReleased = 1846 """ 1847 // Signature format: 3.0 1848 package androidx.navigation { 1849 public final class NavDestination { 1850 ctor public NavDestination(); 1851 } 1852 public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> { 1853 ctor public NavDestinationBuilder(int id); 1854 method public D build(); 1855 } 1856 } 1857 """, 1858 sourceFiles = 1859 arrayOf( 1860 kotlin( 1861 """ 1862 package androidx.navigation 1863 1864 open class NavDestinationBuilder<out D : NavDestination>( 1865 id: Int 1866 ) { 1867 open fun build(): D { 1868 TODO() 1869 } 1870 } 1871 1872 class NavDestination 1873 """ 1874 ) 1875 ) 1876 ) 1877 } 1878 1879 @Test Test inherited methodsnull1880 fun `Test inherited methods`() { 1881 check( 1882 expectedIssues = """ 1883 """, 1884 checkCompatibilityApiReleased = 1885 """ 1886 package test.pkg { 1887 public class Child1 extends test.pkg.Parent { 1888 } 1889 public class Child2 extends test.pkg.Parent { 1890 method public void method0(java.lang.String, int); 1891 method public void method4(java.lang.String, int); 1892 } 1893 public class Child3 extends test.pkg.Parent { 1894 method public void method1(java.lang.String, int); 1895 method public void method2(java.lang.String, int); 1896 } 1897 public class Parent { 1898 method public void method1(java.lang.String, int); 1899 method public void method2(java.lang.String, int); 1900 method public void method3(java.lang.String, int); 1901 } 1902 } 1903 """, 1904 sourceFiles = 1905 arrayOf( 1906 java( 1907 """ 1908 package test.pkg; 1909 1910 public class Child1 extends Parent { 1911 private Child1() {} 1912 @Override 1913 public void method1(String first, int second) { 1914 } 1915 @Override 1916 public void method2(String first, int second) { 1917 } 1918 @Override 1919 public void method3(String first, int second) { 1920 } 1921 } 1922 """ 1923 ), 1924 java( 1925 """ 1926 package test.pkg; 1927 1928 public class Child2 extends Parent { 1929 private Child2() {} 1930 @Override 1931 public void method0(String first, int second) { 1932 } 1933 @Override 1934 public void method1(String first, int second) { 1935 } 1936 @Override 1937 public void method2(String first, int second) { 1938 } 1939 @Override 1940 public void method3(String first, int second) { 1941 } 1942 @Override 1943 public void method4(String first, int second) { 1944 } 1945 } 1946 """ 1947 ), 1948 java( 1949 """ 1950 package test.pkg; 1951 1952 public class Child3 extends Parent { 1953 private Child3() {} 1954 @Override 1955 public void method1(String first, int second) { 1956 } 1957 } 1958 """ 1959 ), 1960 java( 1961 """ 1962 package test.pkg; 1963 public class Parent { 1964 private Parent() { } 1965 public void method1(String first, int second) { 1966 } 1967 public void method2(String first, int second) { 1968 } 1969 public void method3(String first, int second) { 1970 } 1971 } 1972 """ 1973 ) 1974 ) 1975 ) 1976 } 1977 1978 @Test Partial text file which references inner classes not listed elsewherenull1979 fun `Partial text file which references inner classes not listed elsewhere`() { 1980 // This happens in system and test files where we only include APIs that differ 1981 // from the base API. When parsing these code bases we need to gracefully handle 1982 // references to inner classes. 1983 check( 1984 includeSystemApiAnnotations = true, 1985 expectedIssues = 1986 """ 1987 released-api.txt:5: error: Removed method test.pkg.Bar.Inner1.Inner2.removedMethod() [RemovedMethod] 1988 """, 1989 sourceFiles = 1990 arrayOf( 1991 java( 1992 """ 1993 package other.pkg; 1994 1995 public class MyClass { 1996 public class MyInterface { 1997 public void test() { } 1998 } 1999 } 2000 """ 2001 ) 2002 .indented(), 2003 java( 2004 """ 2005 package test.pkg; 2006 import android.annotation.SystemApi; 2007 2008 public class Bar { 2009 public class Inner1 { 2010 private Inner1() { } 2011 @SuppressWarnings("JavaDoc") 2012 public class Inner2 { 2013 private Inner2() { } 2014 2015 /** 2016 * @hide 2017 */ 2018 @SystemApi 2019 public void method() { } 2020 2021 /** 2022 * @hide 2023 */ 2024 @SystemApi 2025 public void addedMethod() { } 2026 } 2027 } 2028 } 2029 """ 2030 ), 2031 systemApiSource, 2032 // Hide android.annotation classes. 2033 KnownSourceFiles.androidAnnotationHide, 2034 ), 2035 extraArguments = 2036 arrayOf( 2037 ARG_SHOW_ANNOTATION, 2038 "android.annotation.SystemApi", 2039 ), 2040 checkCompatibilityApiReleased = 2041 """ 2042 package test.pkg { 2043 public class Bar.Inner1.Inner2 { 2044 method public void method(); 2045 method public void removedMethod(); 2046 } 2047 } 2048 """ 2049 ) 2050 } 2051 2052 @Test Incompatible Changes in Released System APInull2053 fun `Incompatible Changes in Released System API `() { 2054 // Incompatible changes to a released System API should be detected 2055 // In this case removing final and changing value of constant 2056 check( 2057 includeSystemApiAnnotations = true, 2058 expectedIssues = 2059 """ 2060 src/android/rolecontrollerservice/RoleControllerService.java:8: error: Method android.rolecontrollerservice.RoleControllerService.sendNetworkScore has removed 'final' qualifier [RemovedFinalStrict] 2061 src/android/rolecontrollerservice/RoleControllerService.java:9: error: Field android.rolecontrollerservice.RoleControllerService.APP_RETURN_UNWANTED has changed value from 1 to 0 [ChangedValue] 2062 """, 2063 sourceFiles = 2064 arrayOf( 2065 java( 2066 """ 2067 package android.rolecontrollerservice; 2068 import android.annotation.SystemApi; 2069 2070 /** @hide */ 2071 @SystemApi 2072 public abstract class RoleControllerService { 2073 public abstract void onGrantDefaultRoles(); 2074 public void sendNetworkScore(); 2075 public static final int APP_RETURN_UNWANTED = 0; 2076 } 2077 """ 2078 ), 2079 systemApiSource, 2080 // Hide android.annotation classes. 2081 KnownSourceFiles.androidAnnotationHide, 2082 ), 2083 extraArguments = 2084 arrayOf( 2085 ARG_SHOW_ANNOTATION, 2086 "android.annotation.TestApi", 2087 ), 2088 checkCompatibilityApiReleased = 2089 """ 2090 package android.rolecontrollerservice { 2091 public abstract class RoleControllerService { 2092 ctor public RoleControllerService(); 2093 method public abstract void onGrantDefaultRoles(); 2094 method public final void sendNetworkScore(); 2095 field public static final int APP_RETURN_UNWANTED = 1; 2096 } 2097 } 2098 """ 2099 ) 2100 } 2101 2102 @Test Regression test for bug 120847535null2103 fun `Regression test for bug 120847535`() { 2104 // Regression test for 2105 // 120847535: check-api doesn't fail on method that is in current.txt, but marked @hide 2106 // @TestApi 2107 check( 2108 expectedIssues = 2109 """ 2110 released-api.txt:7: error: Removed method test.view.ViewTreeObserver.registerFrameCommitCallback(Runnable) [RemovedMethod] 2111 """, 2112 sourceFiles = 2113 arrayOf( 2114 java( 2115 """ 2116 package test.view; 2117 import android.annotation.TestApi; 2118 public final class ViewTreeObserver { 2119 /** 2120 * @hide 2121 */ 2122 @TestApi 2123 public void registerFrameCommitCallback(Runnable callback) { 2124 } 2125 } 2126 """ 2127 ) 2128 .indented(), 2129 java( 2130 """ 2131 package test.view; 2132 public final class View { 2133 private View() { } 2134 } 2135 """ 2136 ) 2137 .indented(), 2138 testApiSource, 2139 // Hide android.annotation classes. 2140 KnownSourceFiles.androidAnnotationHide, 2141 ), 2142 api = 2143 """ 2144 package test.view { 2145 public final class View { 2146 } 2147 public final class ViewTreeObserver { 2148 ctor public ViewTreeObserver(); 2149 } 2150 } 2151 """, 2152 checkCompatibilityApiReleased = 2153 """ 2154 package test.view { 2155 public final class View { 2156 } 2157 public final class ViewTreeObserver { 2158 ctor public ViewTreeObserver(); 2159 method public void registerFrameCommitCallback(java.lang.Runnable); 2160 } 2161 } 2162 """ 2163 ) 2164 } 2165 2166 @Test Test release compatibility checkingnull2167 fun `Test release compatibility checking`() { 2168 // Different checks are enforced for current vs release API comparisons: 2169 // we don't flag AddedClasses etc. Removed classes *are* enforced. 2170 check( 2171 expectedIssues = 2172 """ 2173 released-api.txt:4: error: Removed constructor test.pkg.Class1() [RemovedMethod] 2174 released-api.txt:15: error: Removed class test.pkg.MyOldClass [RemovedClass] 2175 released-api.txt:18: error: Removed package test.pkg3 [RemovedPackage] 2176 src/test/pkg/Class1.java:3: error: Class test.pkg.Class1 added 'final' qualifier [AddedFinal] 2177 src/test/pkg/MyClass.java:5: error: Method test.pkg.MyClass.myMethod2 has changed 'abstract' qualifier [ChangedAbstract] 2178 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.myMethod3 has changed 'static' qualifier [ChangedStatic] 2179 """, 2180 checkCompatibilityApiReleased = 2181 """ 2182 package test.pkg { 2183 public class Class1 { 2184 ctor public Class1(); 2185 } 2186 public class Class2 { 2187 } 2188 public final class Class3 { 2189 } 2190 public abstract class MyClass { 2191 method public void myMethod2(); 2192 method public void myMethod3(); 2193 method deprecated public void myMethod4(); 2194 } 2195 public abstract class MyOldClass { 2196 } 2197 } 2198 package test.pkg3 { 2199 public abstract class MyOldClass { 2200 } 2201 } 2202 """, 2203 sourceFiles = 2204 arrayOf( 2205 java( 2206 """ 2207 package test.pkg; 2208 2209 public final class Class1 { 2210 private Class1() {} 2211 } 2212 """ 2213 ), 2214 java( 2215 """ 2216 package test.pkg; 2217 2218 public final class Class2 { 2219 private Class2() {} 2220 } 2221 """ 2222 ), 2223 java( 2224 """ 2225 package test.pkg; 2226 2227 public class Class3 { 2228 private Class3() {} 2229 } 2230 """ 2231 ), 2232 java( 2233 """ 2234 package test.pkg; 2235 2236 public abstract class MyNewClass { 2237 private MyNewClass() {} 2238 } 2239 """ 2240 ), 2241 java( 2242 """ 2243 package test.pkg; 2244 2245 public abstract class MyClass { 2246 private MyClass() {} 2247 public native abstract void myMethod2(); // Note that Errors.CHANGE_NATIVE is hidden by default 2248 public static void myMethod3() {} 2249 public void myMethod4() {} 2250 } 2251 """ 2252 ) 2253 ) 2254 ) 2255 } 2256 2257 @Test Test remove deprecated API is an errornull2258 fun `Test remove deprecated API is an error`() { 2259 // Regression test for b/145745855 2260 check( 2261 expectedIssues = 2262 """ 2263 released-api.txt:4: error: Removed deprecated constructor test.pkg.SomeClass() [RemovedDeprecatedMethod] 2264 released-api.txt:5: error: Removed deprecated method test.pkg.SomeClass.deprecatedMethod() [RemovedDeprecatedMethod] 2265 released-api.txt:7: error: Removed deprecated class test.pkg.DeprecatedClass [RemovedDeprecatedClass] 2266 """, 2267 checkCompatibilityApiReleased = 2268 """ 2269 package test.pkg { 2270 public class SomeClass { 2271 ctor deprecated public SomeClass(); 2272 method deprecated public void deprecatedMethod(); 2273 } 2274 deprecated public class DeprecatedClass { 2275 ctor deprecated public DeprecatedClass(); 2276 method deprecated public void deprecatedMethod(); 2277 } 2278 } 2279 """, 2280 sourceFiles = 2281 arrayOf( 2282 java( 2283 """ 2284 package test.pkg; 2285 2286 public class SomeClass { 2287 private SomeClass() {} 2288 } 2289 """ 2290 ) 2291 ) 2292 ) 2293 } 2294 2295 @RequiresCapabilities(Capability.KOTLIN) 2296 @Test Implicit nullnessnull2297 fun `Implicit nullness`() { 2298 check( 2299 checkCompatibilityApiReleased = 2300 """ 2301 // Signature format: 5.0 2302 package androidx.annotation { 2303 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RestrictTo { 2304 method public abstract androidx.annotation.RestrictTo.Scope[] value(); 2305 } 2306 2307 public enum RestrictTo.Scope { 2308 enum_constant @Deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID; 2309 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY; 2310 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP; 2311 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP_PREFIX; 2312 enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES; 2313 enum_constant public static final androidx.annotation.RestrictTo.Scope TESTS; 2314 } 2315 } 2316 """, 2317 sourceFiles = arrayOf(restrictToSource) 2318 ) 2319 } 2320 2321 @Test Java String constantsnull2322 fun `Java String constants`() { 2323 check( 2324 checkCompatibilityApiReleased = 2325 """ 2326 // Signature format: 2.0 2327 package androidx.browser.browseractions { 2328 public class BrowserActionsIntent { 2329 field public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID"; 2330 } 2331 } 2332 """, 2333 sourceFiles = 2334 arrayOf( 2335 java( 2336 """ 2337 package androidx.browser.browseractions; 2338 public class BrowserActionsIntent { 2339 private BrowserActionsIntent() { } 2340 public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID"; 2341 2342 } 2343 """ 2344 ) 2345 .indented() 2346 ) 2347 ) 2348 } 2349 2350 @Test Classes with mapsnull2351 fun `Classes with maps`() { 2352 check( 2353 checkCompatibilityApiReleased = 2354 """ 2355 // Signature format: 2.0 2356 package androidx.collection { 2357 public class SimpleArrayMap<K, V> { 2358 } 2359 } 2360 """, 2361 sourceFiles = 2362 arrayOf( 2363 java( 2364 """ 2365 package androidx.collection; 2366 2367 public class SimpleArrayMap<K, V> { 2368 private SimpleArrayMap() { } 2369 } 2370 """ 2371 ) 2372 .indented() 2373 ) 2374 ) 2375 } 2376 2377 @Test Referencing type parameters in typesnull2378 fun `Referencing type parameters in types`() { 2379 check( 2380 checkCompatibilityApiReleased = 2381 """ 2382 // Signature format: 3.0 2383 package androidx.collection { 2384 public class MyMap<Key, Value> { 2385 ctor public MyMap(); 2386 field public Key! myField; 2387 method public Key! getReplacement(Key!); 2388 } 2389 } 2390 """, 2391 sourceFiles = 2392 arrayOf( 2393 java( 2394 """ 2395 package androidx.collection; 2396 2397 public class MyMap<Key, Value> { 2398 public Key getReplacement(Key key) { return null; } 2399 public Key myField = null; 2400 } 2401 """ 2402 ) 2403 .indented() 2404 ) 2405 ) 2406 } 2407 2408 @Test Insignificant type formatting differencesnull2409 fun `Insignificant type formatting differences`() { 2410 check( 2411 checkCompatibilityApiReleased = 2412 """ 2413 package test.pkg { 2414 public final class UsageStatsManager { 2415 method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets(); 2416 method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>); 2417 field public java.util.Map<java.lang.String, java.lang.Integer> map; 2418 } 2419 } 2420 """, 2421 signatureSource = 2422 """ 2423 package test.pkg { 2424 public final class UsageStatsManager { 2425 method public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets(); 2426 method public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>); 2427 field public java.util.Map<java.lang.String,java.lang.Integer> map; 2428 } 2429 } 2430 """ 2431 ) 2432 } 2433 2434 @RequiresCapabilities(Capability.KOTLIN) 2435 @Test Adding and removing reifiednull2436 fun `Adding and removing reified`() { 2437 check( 2438 expectedIssues = 2439 """ 2440 src/test/pkg/test.kt:5: error: Method test.pkg.TestKt.add made type variable T reified: incompatible change [AddedReified] 2441 src/test/pkg/test.kt:8: error: Method test.pkg.TestKt.two made type variable S reified: incompatible change [AddedReified] 2442 """, 2443 checkCompatibilityApiReleased = 2444 """ 2445 // Signature format: 3.0 2446 package test.pkg { 2447 public final class TestKt { 2448 method public static inline <T> void add(T t); 2449 method public static inline <reified T> void remove(T t); 2450 method public static inline <reified T> void unchanged(T t); 2451 method public static inline <S, reified T> void two(S s, T t); 2452 } 2453 } 2454 """, 2455 sourceFiles = 2456 arrayOf( 2457 kotlin( 2458 """ 2459 @file:Suppress("NOTHING_TO_INLINE", "RedundantVisibilityModifier", "unused") 2460 2461 package test.pkg 2462 2463 inline fun <reified T> add(t: T) { } 2464 inline fun <T> remove(t: T) { } 2465 inline fun <reified T> unchanged(t: T) { } 2466 inline fun <reified S, T> two(s: S, t: T) { } 2467 """ 2468 ) 2469 .indented() 2470 ) 2471 ) 2472 } 2473 2474 @Test Empty prev api with @hide and --show-annotationnull2475 fun `Empty prev api with @hide and --show-annotation`() { 2476 check( 2477 checkCompatibilityApiReleased = """ 2478 """, 2479 sourceFiles = 2480 arrayOf( 2481 java( 2482 """ 2483 package android.media; 2484 2485 /** 2486 * @hide 2487 */ 2488 public class SubtitleController { 2489 public interface Listener { 2490 void onSubtitleTrackSelected() { } 2491 } 2492 } 2493 """ 2494 ), 2495 java( 2496 """ 2497 package android.media; 2498 import android.annotation.SystemApi; 2499 2500 /** 2501 * @hide 2502 */ 2503 @SystemApi 2504 @SuppressWarnings("HiddenSuperclass") 2505 public class MediaPlayer implements SubtitleController.Listener { 2506 } 2507 """ 2508 ), 2509 systemApiSource, 2510 // Hide android.annotation classes. 2511 KnownSourceFiles.androidAnnotationHide, 2512 ), 2513 extraArguments = 2514 arrayOf( 2515 ARG_SHOW_ANNOTATION, 2516 "android.annotation.SystemApi", 2517 ), 2518 expectedIssues = "" 2519 ) 2520 } 2521 2522 @Test Inherited systemApi method in an inner classnull2523 fun `Inherited systemApi method in an inner class`() { 2524 check( 2525 checkCompatibilityApiReleased = 2526 """ 2527 package android.telephony { 2528 public class MmTelFeature.Capabilities { 2529 method public boolean isCapable(int); 2530 } 2531 } 2532 """, 2533 sourceFiles = 2534 arrayOf( 2535 java( 2536 """ 2537 package android.telephony; 2538 2539 /** 2540 * @hide 2541 */ 2542 @android.annotation.SystemApi 2543 public class MmTelFeature { 2544 public static class Capabilities extends ParentCapabilities { 2545 @Override 2546 boolean isCapable(int argument) { return true; } 2547 } 2548 } 2549 """ 2550 ), 2551 java( 2552 """ 2553 package android.telephony; 2554 2555 /** 2556 * @hide 2557 */ 2558 @android.annotation.SystemApi 2559 public class Parent { 2560 public static class ParentCapabilities { 2561 public boolean isCapable(int argument) { return false; } 2562 } 2563 } 2564 """ 2565 ), 2566 systemApiSource, 2567 // Hide android.annotation classes. 2568 KnownSourceFiles.androidAnnotationHide, 2569 ), 2570 extraArguments = 2571 arrayOf( 2572 ARG_SHOW_ANNOTATION, 2573 "android.annotation.SystemApi", 2574 ), 2575 expectedIssues = "" 2576 ) 2577 } 2578 2579 @Test Moving removed api back to public apinull2580 fun `Moving removed api back to public api`() { 2581 check( 2582 checkCompatibilityRemovedApiReleased = 2583 """ 2584 package android.content { 2585 public class ContextWrapper { 2586 method public void createContextForSplit(); 2587 } 2588 } 2589 """, 2590 sourceFiles = 2591 arrayOf( 2592 java( 2593 """ 2594 package android.content; 2595 2596 public class ContextWrapper extends Parent { 2597 /** @removed */ 2598 @Override 2599 public void getSharedPreferences() { } 2600 2601 /** @hide */ 2602 @Override 2603 public void createContextForSplit() { } 2604 } 2605 """ 2606 ), 2607 java( 2608 """ 2609 package android.content; 2610 2611 public abstract class Parent { 2612 /** @hide */ 2613 @Override 2614 public void getSharedPreferences() { } 2615 2616 public abstract void createContextForSplit() { } 2617 } 2618 """ 2619 ) 2620 ), 2621 expectedIssues = "" 2622 ) 2623 } 2624 2625 @Test Inherited nullability annotationsnull2626 fun `Inherited nullability annotations`() { 2627 check( 2628 checkCompatibilityApiReleased = 2629 """ 2630 package test.pkg { 2631 public final class SAXException extends test.pkg.Parent { 2632 } 2633 public final class Parent extends test.pkg.Grandparent { 2634 } 2635 public final class Grandparent { 2636 method @Nullable public String getMessage(); 2637 } 2638 } 2639 """, 2640 sourceFiles = 2641 arrayOf( 2642 java( 2643 """ 2644 package test.pkg; 2645 2646 public final class SAXException extends Parent { 2647 @Override public String getMessage() { 2648 return "sample"; 2649 } 2650 } 2651 """ 2652 ), 2653 java( 2654 """ 2655 package test.pkg; 2656 2657 public final class Parent extends Grandparent { 2658 } 2659 """ 2660 ), 2661 java( 2662 """ 2663 package test.pkg; 2664 2665 public final class Grandparent { 2666 public String getMessage() { 2667 return "sample"; 2668 } 2669 } 2670 """ 2671 ) 2672 ), 2673 mergeJavaStubAnnotations = 2674 """ 2675 package test.pkg; 2676 2677 public class Grandparent implements java.io.Serializable { 2678 @libcore.util.Nullable public test.pkg.String getMessage() { throw new RuntimeException("Stub!"); } 2679 } 2680 """, 2681 expectedIssues = """ 2682 """ 2683 ) 2684 } 2685 2686 @Test Inherited @removed fieldsnull2687 fun `Inherited @removed fields`() { 2688 check( 2689 checkCompatibilityRemovedApiReleased = 2690 """ 2691 package android.provider { 2692 2693 public static final class StreamItems implements android.provider.BaseColumns { 2694 field public static final String _COUNT = "_count"; 2695 field public static final String _ID = "_id"; 2696 } 2697 } 2698 """, 2699 sourceFiles = 2700 arrayOf( 2701 java( 2702 """ 2703 package android.provider; 2704 2705 /** 2706 * @removed 2707 */ 2708 public static final class StreamItems implements BaseColumns { 2709 } 2710 """ 2711 ), 2712 java( 2713 """ 2714 package android.provider; 2715 2716 public interface BaseColumns { 2717 public static final String _ID = "_id"; 2718 public static final String _COUNT = "_count"; 2719 } 2720 """ 2721 ) 2722 ), 2723 expectedIssues = """ 2724 """ 2725 ) 2726 } 2727 2728 @Test Inherited deprecated protected @removed methodnull2729 fun `Inherited deprecated protected @removed method`() { 2730 check( 2731 checkCompatibilityApiReleased = 2732 """ 2733 package android.icu.util { 2734 public class SpecificCalendar { 2735 method @Deprecated protected void validateField(); 2736 } 2737 } 2738 """, 2739 sourceFiles = 2740 arrayOf( 2741 java( 2742 """ 2743 package android.icu.util; 2744 import java.text.Format; 2745 2746 public class SpecificCalendar extends Calendar { 2747 /** 2748 * @deprecated for this test 2749 * @hide 2750 */ 2751 @Override 2752 @Deprecated 2753 protected void validateField() { 2754 } 2755 } 2756 """ 2757 ), 2758 java( 2759 """ 2760 package android.icu.util; 2761 2762 public class Calendar { 2763 protected void validateField() { 2764 } 2765 } 2766 """ 2767 ) 2768 ), 2769 expectedIssues = """ 2770 """ 2771 ) 2772 } 2773 2774 @Test Move class from SystemApi to public and then remove a methodnull2775 fun `Move class from SystemApi to public and then remove a method`() { 2776 check( 2777 checkCompatibilityApiReleased = 2778 """ 2779 package android.hardware.lights { 2780 public static final class LightsRequest.Builder { 2781 ctor public LightsRequest.Builder(); 2782 method public void clearLight(); 2783 method public void setLight(); 2784 } 2785 2786 public final class LightsManager { 2787 } 2788 } 2789 """, 2790 sourceFiles = 2791 arrayOf( 2792 java( 2793 """ 2794 package android.hardware.lights; 2795 2796 import android.annotation.SystemApi; 2797 2798 public class LightsRequest { 2799 public static final class Builder { 2800 void clearLight() { } 2801 } 2802 } 2803 """ 2804 ), 2805 java( 2806 """ 2807 package android.hardware.lights; 2808 2809 import android.annotation.SystemApi; 2810 2811 /** 2812 * @hide 2813 */ 2814 @SystemApi 2815 public class LightsManager { 2816 } 2817 """ 2818 ), 2819 systemApiSource, 2820 // Hide android.annotation classes. 2821 KnownSourceFiles.androidAnnotationHide, 2822 ), 2823 extraArguments = 2824 arrayOf( 2825 ARG_SHOW_ANNOTATION, 2826 "android.annotation.SystemApi", 2827 ), 2828 expectedIssues = 2829 """ 2830 released-api.txt:6: error: Removed method android.hardware.lights.LightsRequest.Builder.setLight() [RemovedMethod] 2831 """ 2832 ) 2833 } 2834 2835 @Test Change item in nested SystemApinull2836 fun `Change item in nested SystemApi`() { 2837 check( 2838 checkCompatibilityApiReleased = 2839 """ 2840 package android.foobar { 2841 public static class Foo.Nested { 2842 ctor public Foo.Nested(); 2843 method public void existing(); 2844 } 2845 } 2846 """, 2847 sourceFiles = 2848 arrayOf( 2849 java( 2850 """ 2851 package android.foobar; 2852 2853 import android.annotation.SystemApi; 2854 2855 public class Foo { 2856 /** @hide */ 2857 @SystemApi 2858 public static final class Nested { 2859 public final int existing(); 2860 } 2861 } 2862 """ 2863 ), 2864 systemApiSource 2865 ), 2866 showAnnotations = arrayOf(ANDROID_SYSTEM_API), 2867 expectedIssues = 2868 """ 2869 src/android/foobar/Foo.java:8: error: Class android.foobar.Foo.Nested added 'final' qualifier [AddedFinal] 2870 src/android/foobar/Foo.java:9: error: Method android.foobar.Foo.Nested.existing has added 'final' qualifier [AddedFinal] 2871 src/android/foobar/Foo.java:9: error: Method android.foobar.Foo.Nested.existing has changed return type from void to int [ChangedType] 2872 """ 2873 ) 2874 } 2875 2876 @Test Moving a field from SystemApi to publicnull2877 fun `Moving a field from SystemApi to public`() { 2878 check( 2879 checkCompatibilityApiReleased = 2880 """ 2881 package android.content { 2882 public class Context { 2883 field public static final String BUGREPORT_SERVICE = "bugreport"; 2884 method public java.io.File getPreloadsFileCache(); 2885 } 2886 } 2887 """, 2888 sourceFiles = 2889 arrayOf( 2890 java( 2891 """ 2892 package android.content; 2893 2894 import android.annotation.SystemApi; 2895 import java.io.File; 2896 2897 public class Context { 2898 public static final String BUGREPORT_SERVICE = "bugreport"; 2899 2900 /** 2901 * @hide 2902 */ 2903 @SystemApi 2904 public File getPreloadsFileCache() { return null; } 2905 } 2906 """ 2907 ), 2908 systemApiSource, 2909 // Hide android.annotation classes. 2910 KnownSourceFiles.androidAnnotationHide, 2911 ), 2912 extraArguments = 2913 arrayOf( 2914 ARG_SHOW_ANNOTATION, 2915 "android.annotation.SystemApi", 2916 ), 2917 expectedIssues = """ 2918 """ 2919 ) 2920 } 2921 2922 @Test Compare interfaces when Object is redefinednull2923 fun `Compare interfaces when Object is redefined`() { 2924 check( 2925 checkCompatibilityApiReleased = 2926 """ 2927 package java.lang { 2928 public class Object { 2929 method public final void wait(); 2930 } 2931 } 2932 package test.pkg { 2933 public interface SomeInterface { 2934 } 2935 } 2936 """, 2937 sourceFiles = 2938 arrayOf( 2939 java( 2940 """ 2941 package test.pkg; 2942 2943 public interface SomeInterface { 2944 } 2945 """ 2946 ) 2947 ), 2948 // it's not quite right to say that java.lang was removed, but it's better than also 2949 // saying that SomeInterface no longer implements wait() 2950 expectedIssues = 2951 """ 2952 released-api.txt:2: error: Removed package java.lang [RemovedPackage] 2953 """ 2954 ) 2955 } 2956 2957 @Test Overriding method without redeclaring nullabilitynull2958 fun `Overriding method without redeclaring nullability`() { 2959 check( 2960 checkCompatibilityApiReleased = 2961 """ 2962 package test.pkg { 2963 public class Child extends test.pkg.Parent { 2964 } 2965 public class Parent { 2966 method public void sample(@Nullable String); 2967 } 2968 } 2969 """, 2970 sourceFiles = 2971 arrayOf( 2972 java( 2973 """ 2974 package test.pkg; 2975 2976 public class Child extends Parent { 2977 public void sample(String arg) { 2978 } 2979 } 2980 """ 2981 ), 2982 java( 2983 """ 2984 package test.pkg; 2985 import androidx.annotation.Nullable; 2986 public class Parent { 2987 public void sample(@Nullable String arg) { 2988 } 2989 } 2990 """ 2991 ), 2992 androidxNullableSource 2993 ), 2994 // The correct behavior would be for this test to fail, because of the removal of 2995 // nullability annotations on the child class. However, when we generate signature 2996 // files, 2997 // we omit methods having the same signature as super methods, so if we were to generate 2998 // a signature file for this source, we would generate the given signature file. So, 2999 // we temporarily allow (and expect) this to pass without errors 3000 // expectedIssues = "src/test/pkg/Child.java:4: error: Attempted to remove @Nullable 3001 // annotation from parameter arg in test.pkg.Child.sample(String arg) 3002 // [InvalidNullConversion]" 3003 expectedIssues = "" 3004 ) 3005 } 3006 3007 @Test Final class inherits a methodnull3008 fun `Final class inherits a method`() { 3009 check( 3010 checkCompatibilityApiReleased = 3011 """ 3012 package java.security { 3013 public abstract class BasicPermission extends java.security.Permission { 3014 method public boolean implies(java.security.Permission); 3015 } 3016 public abstract class Permission { 3017 method public abstract boolean implies(java.security.Permission); 3018 } 3019 } 3020 package javax.security.auth { 3021 public final class AuthPermission extends java.security.BasicPermission { 3022 } 3023 } 3024 """, 3025 sourceFiles = 3026 arrayOf( 3027 java( 3028 """ 3029 package javax.security.auth; 3030 3031 public final class AuthPermission extends java.security.BasicPermission { 3032 } 3033 """ 3034 ), 3035 java( 3036 """ 3037 package java.security; 3038 3039 public abstract class BasicPermission extends Permission { 3040 public boolean implies(Permission p) { 3041 return true; 3042 } 3043 } 3044 """ 3045 ), 3046 java( 3047 """ 3048 package java.security; 3049 public abstract class Permission { 3050 public abstract boolean implies(Permission permission); 3051 } 3052 """ 3053 ) 3054 ), 3055 expectedIssues = "" 3056 ) 3057 } 3058 3059 @Test Implementing undefined interfacenull3060 fun `Implementing undefined interface`() { 3061 check( 3062 checkCompatibilityApiReleased = 3063 """ 3064 package org.apache.http.conn.scheme { 3065 @Deprecated public final class PlainSocketFactory implements org.apache.http.conn.scheme.SocketFactory { 3066 } 3067 } 3068 """, 3069 sourceFiles = 3070 arrayOf( 3071 java( 3072 """ 3073 package org.apache.http.conn.scheme; 3074 3075 /** @deprecated */ 3076 @Deprecated 3077 public final class PlainSocketFactory implements SocketFactory { 3078 } 3079 """ 3080 ) 3081 ), 3082 expectedIssues = "" 3083 ) 3084 } 3085 3086 @Test Inherited abstract methodnull3087 fun `Inherited abstract method`() { 3088 check( 3089 checkCompatibilityApiReleased = 3090 """ 3091 package test.pkg { 3092 public class MeasureFormat { 3093 method public test.pkg.MeasureFormat parse(); 3094 } 3095 } 3096 """, 3097 sourceFiles = 3098 arrayOf( 3099 java( 3100 """ 3101 package test.pkg; 3102 3103 public class MeasureFormat extends UFormat { 3104 private MeasureFormat() { } 3105 /** @hide */ 3106 public MeasureFormat parse(); 3107 } 3108 """ 3109 ), 3110 java( 3111 """ 3112 package test.pkg; 3113 import android.annotation.SystemApi; 3114 3115 public abstract class UFormat { 3116 public abstract UFormat parse() { 3117 } 3118 } 3119 """ 3120 ), 3121 systemApiSource 3122 ), 3123 expectedIssues = "" 3124 ) 3125 } 3126 3127 @Test Ignore hidden referencesnull3128 fun `Ignore hidden references`() { 3129 check( 3130 expectedIssues = """ 3131 """, 3132 checkCompatibilityApiReleased = 3133 """ 3134 package test.pkg { 3135 public class MyClass { 3136 ctor public MyClass(); 3137 method public void method1(test.pkg.Hidden); 3138 } 3139 } 3140 """, 3141 sourceFiles = 3142 arrayOf( 3143 java( 3144 """ 3145 package test.pkg; 3146 3147 public class MyClass { 3148 public void method1(Hidden hidden) { } 3149 } 3150 """ 3151 ), 3152 java( 3153 """ 3154 package test.pkg; 3155 /** @hide */ 3156 public class Hidden { 3157 } 3158 """ 3159 ) 3160 ), 3161 extraArguments = 3162 arrayOf( 3163 ARG_HIDE, 3164 "ReferencesHidden", 3165 ARG_HIDE, 3166 "UnavailableSymbol", 3167 ARG_HIDE, 3168 "HiddenTypeParameter" 3169 ) 3170 ) 3171 } 3172 3173 @Test Empty bundle filesnull3174 fun `Empty bundle files`() { 3175 // Regression test for 124333557 3176 // Makes sure we properly handle conflicting definitions of a java file in separate source 3177 // roots 3178 check( 3179 expectedIssues = "", 3180 checkCompatibilityApiReleased = 3181 """ 3182 // Signature format: 3.0 3183 package com.android.location.provider { 3184 public class LocationProviderBase1 { 3185 ctor public LocationProviderBase1(); 3186 method public void onGetStatus(android.os.Bundle!); 3187 } 3188 public class LocationProviderBase2 { 3189 ctor public LocationProviderBase2(); 3190 method public void onGetStatus(android.os.Bundle!); 3191 } 3192 } 3193 """, 3194 sourceFiles = 3195 arrayOf( 3196 java( 3197 "src2/com/android/location/provider/LocationProviderBase1.java", 3198 """ 3199 /** Something */ 3200 package com.android.location.provider; 3201 """ 3202 ), 3203 java( 3204 "src/com/android/location/provider/LocationProviderBase1.java", 3205 """ 3206 package com.android.location.provider; 3207 import android.os.Bundle; 3208 3209 public class LocationProviderBase1 { 3210 public void onGetStatus(Bundle bundle) { } 3211 } 3212 """ 3213 ), 3214 // Try both combinations (empty java file both first on the source path 3215 // and second on the source path) 3216 java( 3217 "src/com/android/location/provider/LocationProviderBase2.java", 3218 """ 3219 /** Something */ 3220 package com.android.location.provider; 3221 """ 3222 ), 3223 java( 3224 "src/com/android/location/provider/LocationProviderBase2.java", 3225 """ 3226 package com.android.location.provider; 3227 import android.os.Bundle; 3228 3229 public class LocationProviderBase2 { 3230 public void onGetStatus(Bundle bundle) { } 3231 } 3232 """ 3233 ) 3234 ) 3235 ) 3236 } 3237 3238 @Test Check parameterized return type nullabilitynull3239 fun `Check parameterized return type nullability`() { 3240 // Regression test for 130567941 3241 check( 3242 expectedIssues = "", 3243 checkCompatibilityApiReleased = 3244 """ 3245 // Signature format: 3.0 3246 package androidx.coordinatorlayout.widget { 3247 public class CoordinatorLayout { 3248 ctor public CoordinatorLayout(); 3249 method public java.util.List<android.view.View!> getDependencies(); 3250 } 3251 } 3252 """, 3253 sourceFiles = 3254 arrayOf( 3255 java( 3256 """ 3257 package androidx.coordinatorlayout.widget; 3258 3259 import java.util.List; 3260 import androidx.annotation.NonNull; 3261 import android.view.View; 3262 3263 public class CoordinatorLayout { 3264 @NonNull 3265 public List<View> getDependencies() { 3266 throw Exception("Not implemented"); 3267 } 3268 } 3269 """ 3270 ), 3271 androidxNonNullSource, 3272 // Hide androidx.annotation classes. 3273 KnownSourceFiles.androidxAnnotationHide, 3274 ), 3275 ) 3276 } 3277 3278 @Test Check return type changing packagenull3279 fun `Check return type changing package`() { 3280 // Regression test for 130567941 3281 check( 3282 expectedIssues = 3283 """ 3284 load-api.txt:7: error: Method test.pkg.sample.SampleClass.convert1 has changed return type from Number (extends java.lang.Object) to java.lang.Number [ChangedType] 3285 """, 3286 checkCompatibilityApiReleased = 3287 """ 3288 // Signature format: 3.0 3289 package test.pkg.sample { 3290 public abstract class SampleClass { 3291 method public <Number> Number! convert(Number); 3292 method public <Number> Number! convert1(Number); 3293 } 3294 } 3295 """, 3296 signatureSource = 3297 """ 3298 // Signature format: 3.0 3299 package test.pkg.sample { 3300 public abstract class SampleClass { 3301 // Here the generic type parameter applies to both the function argument and the function return type 3302 method public <Number> Number! convert(Number); 3303 // Here the generic type parameter applies to the function argument but not the function return type 3304 method public <Number> java.lang.Number! convert1(Number); 3305 } 3306 } 3307 """ 3308 ) 3309 } 3310 3311 @Test Check generic type argument when showUnannotated is explicitly enablednull3312 fun `Check generic type argument when showUnannotated is explicitly enabled`() { 3313 // Regression test for 130567941 3314 check( 3315 expectedIssues = """ 3316 """, 3317 checkCompatibilityApiReleased = 3318 """ 3319 // Signature format: 3.0 3320 package androidx.versionedparcelable { 3321 public abstract class VersionedParcel { 3322 method public <T> T![]! readArray(); 3323 } 3324 } 3325 """, 3326 sourceFiles = 3327 arrayOf( 3328 java( 3329 """ 3330 package androidx.versionedparcelable; 3331 3332 public abstract class VersionedParcel { 3333 private VersionedParcel() { } 3334 3335 public <T> T[] readArray() { return null; } 3336 } 3337 """ 3338 ) 3339 ), 3340 extraArguments = 3341 arrayOf(ARG_SHOW_UNANNOTATED, ARG_SHOW_ANNOTATION, "androidx.annotation.RestrictTo") 3342 ) 3343 } 3344 3345 @Test Check using parameterized arrays as type parametersnull3346 fun `Check using parameterized arrays as type parameters`() { 3347 check( 3348 format = FileFormat.V3, 3349 sourceFiles = 3350 arrayOf( 3351 java( 3352 """ 3353 package test.pkg; 3354 import java.util.ArrayList; 3355 import java.lang.Exception; 3356 3357 public class SampleArray<D extends ArrayList> extends ArrayList<D[]> { 3358 public D[] get(int index) { 3359 throw Exception("Not implemented"); 3360 } 3361 } 3362 """ 3363 ) 3364 ), 3365 checkCompatibilityApiReleased = 3366 """ 3367 // Signature format: 3.0 3368 package test.pkg { 3369 public class SampleArray<D extends java.util.ArrayList> extends java.util.ArrayList<D[]> { 3370 ctor public SampleArray(); 3371 method public D![]! get(int); 3372 } 3373 } 3374 """ 3375 ) 3376 } 3377 3378 @Test New default method on annotationnull3379 fun `New default method on annotation`() { 3380 // Regression test for 134754815 3381 check( 3382 expectedIssues = 3383 """ 3384 src/androidx/room/Relation.java:5: error: Added method androidx.room.Relation.IHaveNoDefault() [AddedAbstractMethod] 3385 """, 3386 checkCompatibilityApiReleased = 3387 """ 3388 // Signature format: 3.0 3389 package androidx.room { 3390 public @interface Relation { 3391 } 3392 } 3393 """, 3394 sourceFiles = 3395 arrayOf( 3396 java( 3397 """ 3398 package androidx.room; 3399 3400 public @interface Relation { 3401 String IHaveADefault() default ""; 3402 String IHaveNoDefault(); 3403 } 3404 """ 3405 ) 3406 ) 3407 ) 3408 } 3409 3410 @Test Changing static qualifier on inner classes with no public constructorsnull3411 fun `Changing static qualifier on inner classes with no public constructors`() { 3412 check( 3413 expectedIssues = 3414 """ 3415 load-api.txt:9: error: Class test.pkg.ParentClass.BadInnerClass changed 'static' qualifier [ChangedStatic] 3416 load-api.txt:12: error: Class test.pkg.ParentClass.AnotherBadInnerClass changed 'static' qualifier [ChangedStatic] 3417 """, 3418 checkCompatibilityApiReleased = 3419 """ 3420 package test.pkg { 3421 public class ParentClass { 3422 } 3423 public static class ParentClass.OkInnerClass { 3424 } 3425 public class ParentClass.AnotherOkInnerClass { 3426 } 3427 public static class ParentClass.BadInnerClass { 3428 ctor public BadInnerClass(); 3429 } 3430 public class ParentClass.AnotherBadInnerClass { 3431 ctor public AnotherBadInnerClass(); 3432 } 3433 } 3434 """, 3435 signatureSource = 3436 """ 3437 package test.pkg { 3438 public class ParentClass { 3439 } 3440 public class ParentClass.OkInnerClass { 3441 } 3442 public static class ParentClass.AnotherOkInnerClass { 3443 } 3444 public class ParentClass.BadInnerClass { 3445 ctor public BadInnerClass(); 3446 } 3447 public static class ParentClass.AnotherBadInnerClass { 3448 ctor public AnotherBadInnerClass(); 3449 } 3450 } 3451 """ 3452 ) 3453 } 3454 3455 @RequiresCapabilities(Capability.KOTLIN) 3456 @Test Remove fun modifier from interfacenull3457 fun `Remove fun modifier from interface`() { 3458 check( 3459 expectedIssues = 3460 """ 3461 src/test/pkg/FunctionalInterface.kt:3: error: Cannot remove 'fun' modifier from class test.pkg.FunctionalInterface: source incompatible change [FunRemoval] 3462 """, 3463 format = FileFormat.V4, 3464 checkCompatibilityApiReleased = 3465 """ 3466 // Signature format: 4.0 3467 package test.pkg { 3468 public fun interface FunctionalInterface { 3469 method public boolean methodOne(int number); 3470 } 3471 } 3472 """, 3473 sourceFiles = 3474 arrayOf( 3475 kotlin( 3476 """ 3477 package test.pkg 3478 3479 interface FunctionalInterface { 3480 fun methodOne(number: Int): Boolean 3481 } 3482 """ 3483 ) 3484 ) 3485 ) 3486 } 3487 3488 @Test Remove fun modifier from interface signature filesnull3489 fun `Remove fun modifier from interface signature files`() { 3490 check( 3491 expectedIssues = 3492 """ 3493 load-api.txt:3: error: Cannot remove 'fun' modifier from class test.pkg.FunctionalInterface: source incompatible change [FunRemoval] 3494 """, 3495 format = FileFormat.V4, 3496 checkCompatibilityApiReleased = 3497 """ 3498 // Signature format: 4.0 3499 package test.pkg { 3500 public fun interface FunctionalInterface { 3501 method public boolean methodOne(int number); 3502 } 3503 } 3504 """, 3505 signatureSource = 3506 """ 3507 // Signature format: 4.0 3508 package test.pkg { 3509 public interface FunctionalInterface { 3510 method public boolean methodOne(int number); 3511 } 3512 } 3513 """ 3514 .trimIndent() 3515 ) 3516 } 3517 3518 @Test Adding default value to annotation parameternull3519 fun `Adding default value to annotation parameter`() { 3520 check( 3521 expectedIssues = "", 3522 format = FileFormat.V4, 3523 checkCompatibilityApiReleased = 3524 """ 3525 // Signature format: 4.0 3526 package androidx.annotation.experimental { 3527 public @interface UseExperimental { 3528 method public abstract Class<? extends java.lang.Object!> markerClass(); 3529 } 3530 } 3531 """, 3532 sourceFiles = 3533 arrayOf( 3534 java( 3535 """ 3536 package androidx.annotation.experimental; 3537 public @interface UseExperimental { 3538 Class<?> markerClass() default void.class; 3539 } 3540 """ 3541 ) 3542 ) 3543 ) 3544 } 3545 3546 @RequiresCapabilities(Capability.KOTLIN) 3547 @Test adding methods to interfacesnull3548 fun `adding methods to interfaces`() { 3549 check( 3550 expectedIssues = 3551 """ 3552 src/test/pkg/JavaInterface.java:4: error: Added method test.pkg.JavaInterface.noDefault() [AddedAbstractMethod] 3553 src/test/pkg/KotlinInterface.kt:4: error: Added method test.pkg.KotlinInterface.noDefault() [AddedAbstractMethod] 3554 src/test/pkg/KotlinInterface.kt:5: error: Added method test.pkg.KotlinInterface.hasDefault() [AddedAbstractMethod] 3555 """, 3556 checkCompatibilityApiReleased = 3557 """ 3558 // Signature format: 3.0 3559 package test.pkg { 3560 public interface JavaInterface { 3561 } 3562 public interface KotlinInterface { 3563 } 3564 } 3565 """, 3566 sourceFiles = 3567 arrayOf( 3568 java( 3569 """ 3570 package test.pkg; 3571 3572 public interface JavaInterface { 3573 void noDefault(); 3574 default boolean hasDefault() { 3575 return true; 3576 } 3577 static void newStatic(); 3578 } 3579 """ 3580 ), 3581 kotlin( 3582 """ 3583 package test.pkg 3584 3585 interface KotlinInterface { 3586 fun noDefault() 3587 fun hasDefault(): Boolean = true 3588 } 3589 """ 3590 ) 3591 ) 3592 ) 3593 } 3594 3595 @Test Changing visibility from public to privatenull3596 fun `Changing visibility from public to private`() { 3597 check( 3598 expectedIssues = 3599 """ 3600 load-api.txt:3: error: Class test.pkg.Foo changed visibility from public to private [ChangedScope] 3601 """ 3602 .trimIndent(), 3603 signatureSource = 3604 """ 3605 package test.pkg { 3606 private class Foo {} 3607 } 3608 """ 3609 .trimIndent(), 3610 format = FileFormat.V4, 3611 checkCompatibilityApiReleased = 3612 """ 3613 package test.pkg { 3614 public class Foo {} 3615 } 3616 """ 3617 .trimIndent() 3618 ) 3619 } 3620 3621 @Test Changing class kindnull3622 fun `Changing class kind`() { 3623 check( 3624 expectedIssues = 3625 """ 3626 load-api.txt:3: error: Class test.pkg.ClassToEnum changed class/interface declaration [ChangedClass] 3627 load-api.txt:4: error: Class test.pkg.ClassToInterface changed class/interface declaration [ChangedClass] 3628 load-api.txt:5: error: Class test.pkg.ClassToAnnotation changed class/interface declaration [ChangedClass] 3629 load-api.txt:6: error: Class test.pkg.EnumToClass changed class/interface declaration [ChangedClass] 3630 load-api.txt:7: error: Class test.pkg.EnumToInterface changed class/interface declaration [ChangedClass] 3631 load-api.txt:8: error: Class test.pkg.EnumToAnnotation changed class/interface declaration [ChangedClass] 3632 load-api.txt:9: error: Class test.pkg.InterfaceToClass changed class/interface declaration [ChangedClass] 3633 load-api.txt:10: error: Class test.pkg.InterfaceToEnum changed class/interface declaration [ChangedClass] 3634 load-api.txt:11: error: Class test.pkg.InterfaceToAnnotation changed class/interface declaration [ChangedClass] 3635 load-api.txt:12: error: Class test.pkg.AnnotationToClass changed class/interface declaration [ChangedClass] 3636 load-api.txt:13: error: Class test.pkg.AnnotationToInterface changed class/interface declaration [ChangedClass] 3637 load-api.txt:14: error: Class test.pkg.AnnotationToEnum changed class/interface declaration [ChangedClass] 3638 """ 3639 .trimIndent(), 3640 signatureSource = 3641 """ 3642 package test.pkg { 3643 public enum ClassToEnum {} 3644 public interface ClassToInterface {} 3645 public @interface ClassToAnnotation {} 3646 public class EnumToClass {} 3647 public interface EnumToInterface {} 3648 public @interface EnumToAnnotation {} 3649 public class InterfaceToClass {} 3650 public enum InterfaceToEnum {} 3651 public @interface InterfaceToAnnotation {} 3652 public class AnnotationToClass {} 3653 public interface AnnotationToInterface {} 3654 public enum AnnotationToEnum {} 3655 } 3656 """ 3657 .trimIndent(), 3658 format = FileFormat.V4, 3659 checkCompatibilityApiReleased = 3660 """ 3661 package test.pkg { 3662 public class ClassToEnum {} 3663 public class ClassToInterface {} 3664 public class ClassToAnnotation {} 3665 public enum EnumToClass {} 3666 public enum EnumToInterface {} 3667 public enum EnumToAnnotation {} 3668 public interface InterfaceToClass {} 3669 public interface InterfaceToEnum {} 3670 public interface InterfaceToAnnotation {} 3671 public @interface AnnotationToClass {} 3672 public @interface AnnotationToInterface {} 3673 public @interface AnnotationToEnum {} 3674 } 3675 """ 3676 .trimIndent() 3677 ) 3678 } 3679 3680 @Test Allow increased field access for classesnull3681 fun `Allow increased field access for classes`() { 3682 check( 3683 signatureSource = 3684 """ 3685 package test.pkg { 3686 public class Foo { 3687 field public int bar; 3688 field protected int baz; 3689 field protected int spam; 3690 } 3691 } 3692 """, 3693 checkCompatibilityApiReleased = 3694 """ 3695 package test.pkg { 3696 public class Foo { 3697 field protected int bar; 3698 field private int baz; 3699 field internal int spam; 3700 } 3701 } 3702 """ 3703 ) 3704 } 3705 3706 @Test Block decreased field access in classesnull3707 fun `Block decreased field access in classes`() { 3708 check( 3709 expectedIssues = 3710 """ 3711 load-api.txt:4: error: Field test.pkg.Foo.bar changed visibility from public to protected [ChangedScope] 3712 load-api.txt:5: error: Field test.pkg.Foo.baz changed visibility from protected to private [ChangedScope] 3713 load-api.txt:6: error: Field test.pkg.Foo.spam changed visibility from protected to internal [ChangedScope] 3714 """, 3715 signatureSource = 3716 """ 3717 package test.pkg { 3718 public class Foo { 3719 field protected int bar; 3720 field private int baz; 3721 field internal int spam; 3722 } 3723 } 3724 """, 3725 checkCompatibilityApiReleased = 3726 """ 3727 package test.pkg { 3728 public class Foo { 3729 field public int bar; 3730 field protected int baz; 3731 field protected int spam; 3732 } 3733 } 3734 """ 3735 ) 3736 } 3737 3738 @Test Allow increased accessnull3739 fun `Allow increased access`() { 3740 check( 3741 signatureSource = 3742 """ 3743 package test.pkg { 3744 public class Foo { 3745 method public void bar(); 3746 method protected void baz(); 3747 method protected void spam(); 3748 } 3749 } 3750 """, 3751 format = FileFormat.V4, 3752 checkCompatibilityApiReleased = 3753 """ 3754 package test.pkg { 3755 public class Foo { 3756 method protected void bar(); 3757 method private void baz(); 3758 method internal void spam(); 3759 } 3760 } 3761 """ 3762 ) 3763 } 3764 3765 @Test Block decreased accessnull3766 fun `Block decreased access`() { 3767 check( 3768 expectedIssues = 3769 """ 3770 load-api.txt:4: error: Method test.pkg.Foo.bar changed visibility from public to protected [ChangedScope] 3771 load-api.txt:5: error: Method test.pkg.Foo.baz changed visibility from protected to private [ChangedScope] 3772 load-api.txt:6: error: Method test.pkg.Foo.spam changed visibility from protected to internal [ChangedScope] 3773 """, 3774 signatureSource = 3775 """ 3776 package test.pkg { 3777 public class Foo { 3778 method protected void bar(); 3779 method private void baz(); 3780 method internal void spam(); 3781 } 3782 } 3783 """, 3784 format = FileFormat.V4, 3785 checkCompatibilityApiReleased = 3786 """ 3787 package test.pkg { 3788 public class Foo { 3789 method public void bar(); 3790 method protected void baz(); 3791 method protected void spam(); 3792 } 3793 } 3794 """ 3795 ) 3796 } 3797 3798 @Test configuring issue severitynull3799 fun `configuring issue severity`() { 3800 check( 3801 extraArguments = arrayOf(ARG_HIDE, Issues.REMOVED_METHOD.name), 3802 signatureSource = 3803 """ 3804 package test.pkg { 3805 public class Foo { 3806 } 3807 } 3808 """, 3809 checkCompatibilityApiReleased = 3810 """ 3811 package test.pkg { 3812 public class Foo { 3813 ctor public Foo(); 3814 method public void bar(); 3815 } 3816 } 3817 """ 3818 ) 3819 } 3820 3821 @Test block changing open to abstractnull3822 fun `block changing open to abstract`() { 3823 check( 3824 expectedIssues = 3825 """ 3826 load-api.txt:3: error: Class test.pkg.Foo changed 'abstract' qualifier [ChangedAbstract] 3827 load-api.txt:5: error: Method test.pkg.Foo.bar has changed 'abstract' qualifier [ChangedAbstract] 3828 """, 3829 signatureSource = 3830 """ 3831 package test.pkg { 3832 public abstract class Foo { 3833 ctor public Foo(); 3834 method public abstract void bar(); 3835 } 3836 } 3837 """, 3838 checkCompatibilityApiReleased = 3839 """ 3840 package test.pkg { 3841 public class Foo { 3842 ctor public Foo(); 3843 method public void bar(); 3844 } 3845 } 3846 """ 3847 ) 3848 } 3849 3850 @Test allow changing abstract to opennull3851 fun `allow changing abstract to open`() { 3852 check( 3853 signatureSource = 3854 """ 3855 package test.pkg { 3856 public class Foo { 3857 ctor public Foo(); 3858 method public void bar(); 3859 } 3860 } 3861 """, 3862 checkCompatibilityApiReleased = 3863 """ 3864 package test.pkg { 3865 public abstract class Foo { 3866 ctor public Foo(); 3867 method public abstract void bar(); 3868 } 3869 } 3870 """ 3871 ) 3872 } 3873 3874 @Test Change default to abstractnull3875 fun `Change default to abstract`() { 3876 check( 3877 expectedIssues = 3878 """ 3879 load-api.txt:4: error: Method test.pkg.Foo.bar has changed 'default' qualifier [ChangedDefault] 3880 """, 3881 signatureSource = 3882 """ 3883 package test.pkg { 3884 public interface Foo { 3885 method abstract public void bar(Int); 3886 } 3887 } 3888 """, 3889 checkCompatibilityApiReleased = 3890 """ 3891 package test.pkg { 3892 public interface Foo { 3893 method default public void bar(Int); 3894 } 3895 } 3896 """ 3897 ) 3898 } 3899 3900 @Test Allow change from non-final to final in sealed classnull3901 fun `Allow change from non-final to final in sealed class`() { 3902 check( 3903 signatureSource = 3904 """ 3905 package test.pkg { 3906 sealed class Foo { 3907 method final public void bar(Int); 3908 } 3909 } 3910 """, 3911 format = FileFormat.V4, 3912 checkCompatibilityApiReleased = 3913 """ 3914 package test.pkg { 3915 sealed class Foo { 3916 method public void bar(Int); 3917 } 3918 } 3919 """ 3920 ) 3921 } 3922 3923 @Test unchanged self-referencing type parameter is compatiblenull3924 fun `unchanged self-referencing type parameter is compatible`() { 3925 check( 3926 checkCompatibilityApiReleased = 3927 """ 3928 // Signature format: 5.0 3929 package test.pkg { 3930 public abstract class Foo<T extends test.pkg.Foo<T>> { 3931 method public static <T extends test.pkg.Foo<T>> T valueOf(Class<T!>, String); 3932 } 3933 } 3934 """, 3935 sourceFiles = 3936 arrayOf( 3937 java( 3938 """ 3939 package test.pkg; 3940 import android.annotation.NonNull; 3941 public abstract class Foo<T extends Foo<T>> { 3942 @NonNull 3943 public static <T extends Foo<T>> T valueOf(@NonNull Class<T> fooType, @NonNull String name) {} 3944 } 3945 """ 3946 ), 3947 nonNullSource 3948 ) 3949 ) 3950 } 3951 3952 @Test adding a method to an abstract class with hidden constructornull3953 fun `adding a method to an abstract class with hidden constructor`() { 3954 check( 3955 checkCompatibilityApiReleased = 3956 """ 3957 package test.pkg { 3958 public abstract class Foo { 3959 } 3960 } 3961 """, 3962 sourceFiles = 3963 arrayOf( 3964 java( 3965 """ 3966 package test.pkg; 3967 public abstract class Foo { 3968 /** 3969 * @hide 3970 */ 3971 public Foo() {} 3972 public abstract void newAbstractMethod(); 3973 } 3974 """ 3975 ), 3976 ) 3977 ) 3978 } 3979 3980 @Test Allow incompatible changes to unchecked APIsnull3981 fun `Allow incompatible changes to unchecked APIs`() { 3982 check( 3983 checkCompatibilityApiReleased = 3984 """ 3985 package test.pkg { 3986 @test.pkg.MetaAnnotatedDoNotCheckCompat 3987 public class MyTest1 { 3988 method public Double method(Float); 3989 field public Double field; 3990 } 3991 @test.pkg.MetaAnnotatedDoNotCheckCompat 3992 public class MyTest2 { 3993 } 3994 @test.pkg.MetaDoNotCheckCompat public @interface MetaAnnotatedDoNotCheckCompat { 3995 } 3996 @test.pkg.MetaDoNotCheckCompat public @interface MetaDoNotCheckCompat { 3997 } 3998 } 3999 """, 4000 signatureSource = 4001 """ 4002 package test.pkg { 4003 public class MyTest1 { 4004 } 4005 } 4006 """, 4007 suppressCompatibilityMetaAnnotations = arrayOf("test.pkg.MetaDoNotCheckCompat") 4008 ) 4009 } 4010 4011 @Test Allow changing API from unchecked to checkednull4012 fun `Allow changing API from unchecked to checked`() { 4013 check( 4014 checkCompatibilityApiReleased = 4015 """ 4016 package test.pkg { 4017 @test.pkg.MetaAnnotatedDoNotCheckCompat 4018 public class MyTest1 { 4019 } 4020 @test.pkg.MetaAnnotatedDoNotCheckCompat 4021 public class MyTest2 { 4022 } 4023 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaAnnotatedDoNotCheckCompat { 4024 } 4025 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaDoNotCheckCompat { 4026 } 4027 } 4028 """, 4029 signatureSource = 4030 """ 4031 package test.pkg { 4032 public class MyTest1 { 4033 } 4034 @test.pkg.MetaAnnotatedDoNotCheckCompat 4035 public class MyTest2 { 4036 } 4037 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaAnnotatedDoNotCheckCompat { 4038 } 4039 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaDoNotCheckCompat { 4040 } 4041 } 4042 """, 4043 suppressCompatibilityMetaAnnotations = arrayOf("test.pkg.MetaDoNotCheckCompat") 4044 ) 4045 } 4046 4047 @Test Doesn't crash when checking annotations with BINARY retentionnull4048 fun `Doesn't crash when checking annotations with BINARY retention`() { 4049 check( 4050 expectedIssues = "", 4051 checkCompatibilityApiReleased = 4052 """ 4053 package androidx.wear.watchface { 4054 @androidx.wear.watchface.complications.data.ComplicationExperimental public final class BoundingArc { 4055 ctor public BoundingArc(float startAngle, float totalAngle, @Px float thickness); 4056 method public float getStartAngle(); 4057 method public float getThickness(); 4058 method public float getTotalAngle(); 4059 method public boolean hitTest(android.graphics.Rect rect, @Px float x, @Px float y); 4060 method public void setStartAngle(float); 4061 method public void setThickness(float); 4062 method public void setTotalAngle(float); 4063 property public final float startAngle; 4064 property public final float thickness; 4065 property public final float totalAngle; 4066 } 4067 } 4068 package androidx.wear.watchface.complications.data { 4069 @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ComplicationExperimental { 4070 } 4071 } 4072 """, 4073 signatureSource = 4074 """ 4075 package androidx.wear.watchface { 4076 @androidx.wear.watchface.complications.data.ComplicationExperimental public final class BoundingArc { 4077 ctor public BoundingArc(float startAngle, float totalAngle, @Px float thickness); 4078 method public float getStartAngle(); 4079 method public float getThickness(); 4080 method public float getTotalAngle(); 4081 method public boolean hitTest(android.graphics.Rect rect, @Px float x, @Px float y); 4082 property public final float startAngle; 4083 property public final float thickness; 4084 property public final float totalAngle; 4085 } 4086 } 4087 package androidx.wear.watchface.complications.data { 4088 @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ComplicationExperimental { 4089 } 4090 } 4091 """, 4092 suppressCompatibilityMetaAnnotations = arrayOf("kotlin.RequiresOptIn") 4093 ) 4094 } 4095 4096 @Test 4097 fun `Fail when changing API from checked to unchecked`() { 4098 check( 4099 expectedIssues = 4100 """ 4101 released-api.txt:3: error: Removed class test.pkg.MyTest1 from compatibility checked API surface [BecameUnchecked] 4102 """, 4103 checkCompatibilityApiReleased = 4104 """ 4105 package test.pkg { 4106 public class MyTest1 { 4107 } 4108 @test.pkg.MetaAnnotatedDoNotCheckCompat 4109 public class MyTest2 { 4110 } 4111 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaAnnotatedDoNotCheckCompat { 4112 } 4113 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaDoNotCheckCompat { 4114 } 4115 } 4116 """, 4117 signatureSource = 4118 """ 4119 package test.pkg { 4120 @test.pkg.MetaAnnotatedDoNotCheckCompat 4121 public class MyTest1 { 4122 } 4123 @test.pkg.MetaAnnotatedDoNotCheckCompat 4124 public class MyTest2 { 4125 } 4126 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaAnnotatedDoNotCheckCompat { 4127 } 4128 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.MetaDoNotCheckCompat public @interface MetaDoNotCheckCompat { 4129 } 4130 } 4131 """, 4132 suppressCompatibilityMetaAnnotations = arrayOf("test.pkg.MetaDoNotCheckCompat") 4133 ) 4134 } 4135 4136 @Test 4137 fun `Conversion from AutoCloseable to Closeable is not API-breaking`() { 4138 // Closeable implements AutoCloseable 4139 check( 4140 apiClassResolution = ApiClassResolution.API_CLASSPATH, 4141 expectedIssues = "", 4142 checkCompatibilityApiReleased = 4143 """ 4144 // Signature format: 4.0 4145 package test.pkg { 4146 public class Foo implements java.lang.AutoCloseable { 4147 method public void close(); 4148 } 4149 } 4150 """, 4151 signatureSource = 4152 """ 4153 // Signature format: 4.0 4154 package test.pkg { 4155 public class Foo implements java.io.Closeable { 4156 method public void close(); 4157 } 4158 } 4159 """ 4160 ) 4161 } 4162 4163 @Test 4164 fun `Conversion from Closeable to AutoCloseable is API-breaking`() { 4165 // AutoCloseable does not implement Closeable 4166 check( 4167 expectedIssues = 4168 """ 4169 load-api.txt:3: error: Class test.pkg.Foo no longer implements java.io.Closeable [RemovedInterface] 4170 """ 4171 .trimIndent(), 4172 checkCompatibilityApiReleased = 4173 """ 4174 // Signature format: 4.0 4175 package test.pkg { 4176 public class Foo implements java.io.Closeable { 4177 method public void close(); 4178 } 4179 } 4180 """, 4181 signatureSource = 4182 """ 4183 // Signature format: 4.0 4184 package test.pkg { 4185 public class Foo implements java.lang.AutoCloseable { 4186 method public void close(); 4187 } 4188 } 4189 """ 4190 ) 4191 } 4192 4193 @Test 4194 fun `Conversion from MutableCollection to AbstractMutableCollection is not API-breaking`() { 4195 check( 4196 apiClassResolution = ApiClassResolution.API_CLASSPATH, 4197 expectedIssues = "", 4198 checkCompatibilityApiReleased = 4199 """ 4200 // Signature format: 4.0 4201 package test.pkg { 4202 public class MyCollection<E> implements java.util.Collection<E> { 4203 ctor public MyCollection(); 4204 method public boolean add(E! e); 4205 method public boolean addAll(java.util.Collection<? extends E> c); 4206 method public void clear(); 4207 method public boolean contains(Object! o); 4208 method public boolean containsAll(java.util.Collection<?> c); 4209 method public boolean isEmpty(); 4210 method public java.util.Iterator<E> iterator(); 4211 method public boolean remove(Object! o); 4212 method public boolean removeAll(java.util.Collection<?> c); 4213 method public boolean retainAll(java.util.Collection<?> c); 4214 method public int size(); 4215 method public Object![] toArray(); 4216 method public <T> T![] toArray(T[] a); 4217 } 4218 } 4219 """, 4220 signatureSource = 4221 """ 4222 // Signature format: 4.0 4223 package test.pkg { 4224 public class MyCollection<E> extends java.util.AbstractCollection<E> { 4225 ctor public MyCollection(); 4226 method public java.util.Iterator<E> iterator(); 4227 method public int size(); 4228 } 4229 } 4230 """ 4231 ) 4232 } 4233 4234 @Test 4235 fun `Expected API changes converting collections to Kotlin`() { 4236 check( 4237 apiClassResolution = ApiClassResolution.API_CLASSPATH, 4238 // The parameter names are different between java.util.Collection and 4239 // kotlin.collections.Collection 4240 // Methods not defined in kotlin.collections.Collection appear abstract as they are not 4241 // listed in the API file 4242 expectedIssues = 4243 """ 4244 error: Method test.pkg.MyCollection.add has changed 'abstract' qualifier [ChangedAbstract] 4245 error: Method test.pkg.MyCollection.addAll has changed 'abstract' qualifier [ChangedAbstract] 4246 error: Method test.pkg.MyCollection.clear has changed 'abstract' qualifier [ChangedAbstract] 4247 error: Method test.pkg.MyCollection.remove has changed 'abstract' qualifier [ChangedAbstract] 4248 error: Method test.pkg.MyCollection.removeAll has changed 'abstract' qualifier [ChangedAbstract] 4249 error: Method test.pkg.MyCollection.retainAll has changed 'abstract' qualifier [ChangedAbstract] 4250 error: Method test.pkg.MyCollection.size has changed 'abstract' qualifier [ChangedAbstract] 4251 error: Method test.pkg.MyCollection.toArray has changed 'abstract' qualifier [ChangedAbstract] 4252 error: Method test.pkg.MyCollection.toArray has changed 'abstract' qualifier [ChangedAbstract] 4253 error: Attempted to remove parameter name from parameter p in test.pkg.MyCollection.add [ParameterNameChange] 4254 error: Attempted to remove parameter name from parameter p in test.pkg.MyCollection.addAll [ParameterNameChange] 4255 error: Attempted to remove parameter name from parameter p in test.pkg.MyCollection.remove [ParameterNameChange] 4256 error: Attempted to remove parameter name from parameter p in test.pkg.MyCollection.removeAll [ParameterNameChange] 4257 error: Attempted to remove parameter name from parameter p in test.pkg.MyCollection.retainAll [ParameterNameChange] 4258 error: Attempted to remove parameter name from parameter p in test.pkg.MyCollection.toArray [ParameterNameChange] 4259 load-api.txt:5: error: Attempted to change parameter name from o to element in method test.pkg.MyCollection.contains [ParameterNameChange] 4260 load-api.txt:5: error: Attempted to change parameter name from o to element in method test.pkg.MyCollection.contains [ParameterNameChange] 4261 load-api.txt:6: error: Attempted to change parameter name from c to elements in method test.pkg.MyCollection.containsAll [ParameterNameChange] 4262 load-api.txt:6: error: Attempted to change parameter name from c to elements in method test.pkg.MyCollection.containsAll [ParameterNameChange] 4263 """, 4264 checkCompatibilityApiReleased = 4265 """ 4266 // Signature format: 4.0 4267 package test.pkg { 4268 public class MyCollection<E> implements java.util.Collection<E> { 4269 ctor public MyCollection(); 4270 method public boolean add(E! e); 4271 method public boolean addAll(java.util.Collection<? extends E> c); 4272 method public void clear(); 4273 method public boolean contains(Object! o); 4274 method public boolean containsAll(java.util.Collection<?> c); 4275 method public boolean isEmpty(); 4276 method public java.util.Iterator<E> iterator(); 4277 method public boolean remove(Object! o); 4278 method public boolean removeAll(java.util.Collection<?> c); 4279 method public boolean retainAll(java.util.Collection<?> c); 4280 method public int size(); 4281 method public Object![] toArray(); 4282 method public <T> T![] toArray(T[] a); 4283 } 4284 } 4285 """, 4286 signatureSource = 4287 """ 4288 // Signature format: 4.0 4289 package test.pkg { 4290 public class MyCollection<E> implements java.util.Collection<E> kotlin.jvm.internal.markers.KMappedMarker { 4291 ctor public MyCollection(); 4292 method public boolean contains(E element); 4293 method public boolean containsAll(java.util.Collection<E!> elements); 4294 method public int getSize(); 4295 method public boolean isEmpty(); 4296 method public java.util.Iterator<E> iterator(); 4297 property public int size; 4298 } 4299 } 4300 """ 4301 ) 4302 } 4303 4304 @Test 4305 fun `No issues using the same classpath class twice`() { 4306 check( 4307 apiClassResolution = ApiClassResolution.API_CLASSPATH, 4308 expectedIssues = "", 4309 checkCompatibilityApiReleased = 4310 """ 4311 // Signature format: 4.0 4312 package test.pkg { 4313 public class String1 extends java.lang.String { 4314 method public boolean isEmpty(); 4315 } 4316 public class String2 extends java.lang.String { 4317 method public boolean isEmpty(); 4318 } 4319 } 4320 """, 4321 signatureSource = 4322 """ 4323 // Signature format: 4.0 4324 package test.pkg { 4325 public class String1 extends java.lang.String {} 4326 public class String2 extends java.lang.String {} 4327 } 4328 """ 4329 ) 4330 } 4331 4332 @Test 4333 fun `Avoid stack overflow for self-referential and cyclical annotation usage`() { 4334 val signature = 4335 """ 4336 package test.pkg { 4337 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.SelfReferenceAnnotation public @interface SelfReferenceAnnotation {} 4338 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.CyclicalReferenceAnnotationB public @interface CyclicalReferenceAnnotationA {} 4339 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @test.pkg.CyclicalReferenceAnnotationA public @interface CyclicalReferenceAnnotationB {} 4340 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface MetaSuppressCompatibility {} 4341 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface MetaHide {} 4342 } 4343 """ 4344 check( 4345 checkCompatibilityApiReleased = signature, 4346 signatureSource = signature, 4347 suppressCompatibilityMetaAnnotations = arrayOf("test.pkg.MetaSuppressCompatibility"), 4348 ) 4349 } 4350 4351 @Test 4352 fun `Set all issues in the Compatibility category to error-level`() { 4353 check( 4354 expectedIssues = 4355 """ 4356 load-api.txt:2: error: Added package test.pkg [AddedPackage] 4357 """ 4358 .trimIndent(), 4359 checkCompatibilityApiReleased = 4360 """ 4361 // Signature format: 4.0 4362 """, 4363 signatureSource = 4364 """ 4365 // Signature format: 4.0 4366 package test.pkg { 4367 public class String1 extends java.lang.String {} 4368 } 4369 """, 4370 extraArguments = arrayOf(ARG_ERROR_CATEGORY, "Compatibility") 4371 ) 4372 } 4373 4374 @Test 4375 fun `Synthetic suppress compatibility annotation allows incompatible changes`() { 4376 check( 4377 checkCompatibilityApiReleased = 4378 """ 4379 package androidx.benchmark.macro.junit4 { 4380 @RequiresApi(28) @SuppressCompatibility @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule { 4381 ctor public BaselineProfileRule(); 4382 method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description); 4383 method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock); 4384 } 4385 } 4386 """, 4387 signatureSource = 4388 """ 4389 package androidx.benchmark.macro.junit4 { 4390 @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule { 4391 ctor public BaselineProfileRule(); 4392 method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description); 4393 method public void collect(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock); 4394 } 4395 } 4396 package androidx.benchmark.macro { 4397 @kotlin.RequiresOptIn(message="The Baseline profile generation API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalBaselineProfilesApi { 4398 } 4399 } 4400 """, 4401 suppressCompatibilityMetaAnnotations = arrayOf("kotlin.RequiresOptIn") 4402 ) 4403 } 4404 4405 @Test 4406 fun `Removing @JvmDefaultWithCompatibility is an incompatible change`() { 4407 check( 4408 expectedIssues = 4409 "load-api.txt:3: error: Cannot remove @kotlin.jvm.JvmDefaultWithCompatibility annotation from class test.pkg.AnnotationRemoved: Incompatible change [RemovedJvmDefaultWithCompatibility]", 4410 checkCompatibilityApiReleased = 4411 """ 4412 // Signature format: 4.0 4413 package test.pkg { 4414 @kotlin.jvm.JvmDefaultWithCompatibility public interface AnnotationRemoved { 4415 method public default void foo(); 4416 } 4417 @kotlin.jvm.JvmDefaultWithCompatibility public interface AnnotationStays { 4418 method public default void foo(); 4419 } 4420 } 4421 """, 4422 signatureSource = 4423 """ 4424 // Signature format: 4.0 4425 package test.pkg { 4426 public interface AnnotationRemoved { 4427 method public default void foo(); 4428 } 4429 @kotlin.jvm.JvmDefaultWithCompatibility public interface AnnotationStays { 4430 method public default void foo(); 4431 } 4432 } 4433 """ 4434 ) 4435 } 4436 4437 @RequiresCapabilities(Capability.KOTLIN) 4438 @Test 4439 fun `@JvmDefaultWithCompatibility check works with source files`() { 4440 check( 4441 expectedIssues = 4442 "src/test/pkg/AnnotationRemoved.kt:3: error: Cannot remove @kotlin.jvm.JvmDefaultWithCompatibility annotation from class test.pkg.AnnotationRemoved: Incompatible change [RemovedJvmDefaultWithCompatibility]", 4443 checkCompatibilityApiReleased = 4444 """ 4445 // Signature format: 4.0 4446 package test.pkg { 4447 @kotlin.jvm.JvmDefaultWithCompatibility public interface AnnotationRemoved { 4448 method public default void foo(); 4449 } 4450 @kotlin.jvm.JvmDefaultWithCompatibility public interface AnnotationStays { 4451 method public default void foo(); 4452 } 4453 } 4454 """, 4455 sourceFiles = 4456 arrayOf( 4457 kotlin( 4458 """ 4459 package test.pkg 4460 4461 interface AnnotationRemoved { 4462 fun foo() {} 4463 } 4464 4465 @JvmDefaultWithCompatibility 4466 interface AnnotationStays { 4467 fun foo() {} 4468 } 4469 """ 4470 ) 4471 ) 4472 ) 4473 } 4474 4475 @Test 4476 fun `Changing return type to variable with equal bounds is compatible`() { 4477 check( 4478 expectedIssues = "", 4479 checkCompatibilityApiReleased = 4480 """ 4481 // Signature format: 2.0 4482 package test.pkg { 4483 public final class Foo { 4484 method public <A extends java.lang.annotation.Annotation> A getAnnotation(); 4485 method public <A extends java.lang.annotation.Annotation> A[] getAnnotationArray(); 4486 } 4487 } 4488 """, 4489 sourceFiles = 4490 arrayOf( 4491 java( 4492 """ 4493 package test.pkg; 4494 4495 public final class Foo { 4496 public <T extends java.lang.annotation.Annotation> T getAnnotation() { return null; } 4497 public <T extends java.lang.annotation.Annotation> T[] getAnnotationArray() { return null; } 4498 } 4499 """ 4500 ) 4501 ), 4502 ) 4503 } 4504 4505 @Test 4506 fun `Changing return type to variable with unequal bounds is incompatible`() { 4507 check( 4508 expectedIssues = 4509 """ 4510 src/test/pkg/Foo.java:4: error: Method test.pkg.Foo.getAnnotation has changed return type from A (extends java.lang.annotation.Annotation) to A (extends java.lang.String) [ChangedType] 4511 src/test/pkg/Foo.java:5: error: Method test.pkg.Foo.getAnnotationArray has changed return type from A (extends java.lang.annotation.Annotation)[] to A (extends java.lang.String)[] [ChangedType] 4512 """, 4513 checkCompatibilityApiReleased = 4514 """ 4515 // Signature format: 2.0 4516 package test.pkg { 4517 public final class Foo { 4518 method public <A extends java.lang.annotation.Annotation> A getAnnotation(); 4519 method public <A extends java.lang.annotation.Annotation> A[] getAnnotationArray(); 4520 } 4521 } 4522 """, 4523 sourceFiles = 4524 arrayOf( 4525 java( 4526 """ 4527 package test.pkg; 4528 4529 public final class Foo { 4530 public <A extends java.lang.String> A getAnnotation() { return null; } 4531 public <A extends java.lang.String> A[] getAnnotationArray() { return null; } 4532 } 4533 """ 4534 ) 4535 ), 4536 ) 4537 } 4538 4539 @Test 4540 fun `Check compatibility against overridden method with type variable substitution`() { 4541 check( 4542 // The remove method isn't listed in this file, but it exists on Properties with return 4543 // type `java.lang.Object`. 4544 checkCompatibilityApiReleased = 4545 """ 4546 package test.pkg { 4547 public class Properties extends test.pkg.Hashtable<java.lang.Object,java.lang.Object> { 4548 } 4549 4550 public class Hashtable<K, V> { 4551 method public V remove(Object); 4552 } 4553 } 4554 """, 4555 signatureSource = 4556 """ 4557 package test.pkg { 4558 public class Properties extends test.pkg.Hashtable<java.lang.Object,java.lang.Object> { 4559 method public Object remove(Object); 4560 } 4561 4562 public class Hashtable<K, V> extends java.util.Dictionary<K,V> implements java.lang.Cloneable java.util.Map<K,V> java.io.Serializable { 4563 method public V remove(Object); 4564 } 4565 } 4566 """, 4567 ) 4568 } 4569 4570 @RequiresCapabilities(Capability.KOTLIN) 4571 @Test 4572 fun `Test adding method with same name as method with type parameter`() { 4573 check( 4574 checkCompatibilityApiReleased = 4575 """ 4576 // Signature format: 5.0 4577 package test.pkg { 4578 public final class TestKt { 4579 method public static <T> T foo(T target); 4580 } 4581 } 4582 """, 4583 sourceFiles = 4584 arrayOf( 4585 kotlin( 4586 """ 4587 package test.pkg 4588 fun <T> foo(target: T) = target 4589 fun foo(target: String) = target 4590 """ 4591 ) 4592 ), 4593 ) 4594 } 4595 4596 @Test Test that parent method with type parameter matches child overridenull4597 fun `Test that parent method with type parameter matches child override`() { 4598 check( 4599 checkCompatibilityApiReleased = 4600 """ 4601 // Signature format: 5.0 4602 package test.pkg { 4603 public final class Child extends test.pkg.Parent<java.lang.Integer> { 4604 method public void foo(Integer t); 4605 } 4606 public class Parent<T> { 4607 method public void foo(T! t); 4608 } 4609 } 4610 """, 4611 signatureSource = 4612 """ 4613 // Signature format: 5.0 4614 package test.pkg { 4615 public final class Child extends test.pkg.Parent<java.lang.Integer> { 4616 } 4617 public class Parent<T> { 4618 method public void foo(T! t); 4619 } 4620 } 4621 """ 4622 ) 4623 } 4624 4625 @RequiresCapabilities(Capability.KOTLIN) 4626 @Test Test removal of implicit no-args constructor is flaggednull4627 fun `Test removal of implicit no-args constructor is flagged`() { 4628 check( 4629 expectedIssues = 4630 "released-api.txt:8: error: Removed constructor test.pkg.RemoveNoArgsCtor() [RemovedMethod]", 4631 checkCompatibilityApiReleased = 4632 """ 4633 package test.pkg { 4634 public final class KeepNoArgsCtor { 4635 ctor public KeepNoArgsCtor(); 4636 ctor public KeepNoArgsCtor(optional int one, optional int two); 4637 } 4638 public final class RemoveNoArgsCtor { 4639 ctor public RemoveNoArgsCtor(); 4640 ctor public RemoveNoArgsCtor(optional int one, optional int two); 4641 ctor public RemoveNoArgsCtor(String str); 4642 } 4643 } 4644 """, 4645 sourceFiles = 4646 arrayOf( 4647 kotlin( 4648 """ 4649 package test.pkg 4650 // The kotlin compiler generates a no-args constructor because all 4651 // parameters to the primary constructor have default values 4652 class KeepNoArgsCtor(one: Int = 1, two: Int = 2) 4653 class RemoveNoArgsCtor(str: String) { 4654 // This is a secondary constructor, so a no-args constructor isn't 4655 // generated even though all parameters have default values 4656 constructor(one: Int = 1, two: Int = 2): this("") 4657 } 4658 """ 4659 ) 4660 ), 4661 api = 4662 """ 4663 package test.pkg { 4664 public final class KeepNoArgsCtor { 4665 ctor public KeepNoArgsCtor(); 4666 ctor public KeepNoArgsCtor(optional int one, optional int two); 4667 } 4668 public final class RemoveNoArgsCtor { 4669 ctor public RemoveNoArgsCtor(optional int one, optional int two); 4670 ctor public RemoveNoArgsCtor(String str); 4671 } 4672 } 4673 """ 4674 ) 4675 } 4676 4677 // TODO: Check method signatures changing incompatibly (look especially out for adding new 4678 // overloaded methods and comparator getting confused!) 4679 // ..equals on the method items should actually be very useful! 4680 } 4681