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