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