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 18 19 import org.junit.Ignore 20 import org.junit.Test 21 import java.io.File 22 import kotlin.text.Charsets.UTF_8 23 24 class 25 CompatibilityCheckTest : DriverTest() { 26 @Test Change between class and interfacenull27 fun `Change between class and interface`() { 28 check( 29 expectedIssues = """ 30 TESTROOT/load-api.txt:2: error: Class test.pkg.MyTest1 changed class/interface declaration [ChangedClass] 31 TESTROOT/load-api.txt:4: error: Class test.pkg.MyTest2 changed class/interface declaration [ChangedClass] 32 """, 33 checkCompatibilityApiReleased = """ 34 package test.pkg { 35 public class MyTest1 { 36 } 37 public interface MyTest2 { 38 } 39 public class MyTest3 { 40 } 41 public interface MyTest4 { 42 } 43 } 44 """, 45 // MyTest1 and MyTest2 reversed from class to interface or vice versa, MyTest3 and MyTest4 unchanged 46 signatureSource = """ 47 package test.pkg { 48 public interface MyTest1 { 49 } 50 public class MyTest2 { 51 } 52 public class MyTest3 { 53 } 54 public interface MyTest4 { 55 } 56 } 57 """ 58 ) 59 } 60 61 @Test Interfaces should not be droppednull62 fun `Interfaces should not be dropped`() { 63 check( 64 expectedIssues = """ 65 TESTROOT/load-api.txt:2: error: Class test.pkg.MyTest1 changed class/interface declaration [ChangedClass] 66 TESTROOT/load-api.txt:4: error: Class test.pkg.MyTest2 changed class/interface declaration [ChangedClass] 67 """, 68 checkCompatibilityApiReleased = """ 69 package test.pkg { 70 public class MyTest1 { 71 } 72 public interface MyTest2 { 73 } 74 public class MyTest3 { 75 } 76 public interface MyTest4 { 77 } 78 } 79 """, 80 // MyTest1 and MyTest2 reversed from class to interface or vice versa, MyTest3 and MyTest4 unchanged 81 signatureSource = """ 82 package test.pkg { 83 public interface MyTest1 { 84 } 85 public class MyTest2 { 86 } 87 public class MyTest3 { 88 } 89 public interface MyTest4 { 90 } 91 } 92 """ 93 ) 94 } 95 96 @Test Ensure warnings for removed APIsnull97 fun `Ensure warnings for removed APIs`() { 98 check( 99 expectedIssues = """ 100 TESTROOT/released-api.txt:3: error: Removed method test.pkg.MyTest1.method(Float) [RemovedMethod] 101 TESTROOT/released-api.txt:4: error: Removed field test.pkg.MyTest1.field [RemovedField] 102 TESTROOT/released-api.txt:6: error: Removed class test.pkg.MyTest2 [RemovedClass] 103 """, 104 checkCompatibilityApiReleased = """ 105 package test.pkg { 106 public class MyTest1 { 107 method public Double method(Float); 108 field public Double field; 109 } 110 public class MyTest2 { 111 method public Double method(Float); 112 field public Double field; 113 } 114 } 115 package test.pkg.other { 116 } 117 """, 118 signatureSource = """ 119 package test.pkg { 120 public class MyTest1 { 121 } 122 } 123 """ 124 ) 125 } 126 127 @Test Flag invalid nullness changesnull128 fun `Flag invalid nullness changes`() { 129 check( 130 expectedIssues = """ 131 TESTROOT/load-api.txt:5: error: Attempted to remove @Nullable annotation from method test.pkg.MyTest.convert3(Float) [InvalidNullConversion] 132 TESTROOT/load-api.txt:5: error: Attempted to remove @Nullable annotation from parameter arg1 in test.pkg.MyTest.convert3(Float arg1) [InvalidNullConversion] 133 TESTROOT/load-api.txt:6: error: Attempted to remove @NonNull annotation from method test.pkg.MyTest.convert4(Float) [InvalidNullConversion] 134 TESTROOT/load-api.txt:6: error: Attempted to remove @NonNull annotation from parameter arg1 in test.pkg.MyTest.convert4(Float arg1) [InvalidNullConversion] 135 TESTROOT/load-api.txt:7: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter arg1 in test.pkg.MyTest.convert5(Float arg1) [InvalidNullConversion] 136 TESTROOT/load-api.txt:8: error: Attempted to change method return from @NonNull to @Nullable: incompatible change for method test.pkg.MyTest.convert6(Float) [InvalidNullConversion] 137 """, 138 outputKotlinStyleNulls = false, 139 checkCompatibilityApiReleased = """ 140 package test.pkg { 141 public class MyTest { 142 method public Double convert1(Float); 143 method public Double convert2(Float); 144 method @Nullable public Double convert3(@Nullable Float); 145 method @NonNull public Double convert4(@NonNull Float); 146 method @Nullable public Double convert5(@Nullable Float); 147 method @NonNull public Double convert6(@NonNull Float); 148 // booleans cannot reasonably be annotated with @Nullable/@NonNull but 149 // the compiler accepts it and we had a few of these accidentally annotated 150 // that way in API 28, such as Boolean.getBoolean. Make sure we don't flag 151 // these as incompatible changes when they're dropped. 152 method public void convert7(@NonNull boolean); 153 } 154 } 155 """, 156 // Changes: +nullness, -nullness, nullable->nonnull, nonnull->nullable 157 signatureSource = """ 158 package test.pkg { 159 public class MyTest { 160 method @Nullable public Double convert1(@Nullable Float); 161 method @NonNull public Double convert2(@NonNull Float); 162 method public Double convert3(Float); 163 method public Double convert4(Float); 164 method @NonNull public Double convert5(@NonNull Float); 165 method @Nullable public Double convert6(@Nullable Float); 166 method public void convert7(boolean); 167 } 168 } 169 """ 170 ) 171 } 172 173 @Test Kotlin Nullnessnull174 fun `Kotlin Nullness`() { 175 check( 176 expectedIssues = """ 177 src/test/pkg/Outer.kt:5: error: Attempted to change method return from @NonNull to @Nullable: incompatible change for method test.pkg.Outer.method2(String,String) [InvalidNullConversion] 178 src/test/pkg/Outer.kt:5: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter string in test.pkg.Outer.method2(String string, String maybeString) [InvalidNullConversion] 179 src/test/pkg/Outer.kt:6: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter string in test.pkg.Outer.method3(String maybeString, String string) [InvalidNullConversion] 180 src/test/pkg/Outer.kt:8: error: Attempted to change method return from @NonNull to @Nullable: incompatible change for method test.pkg.Outer.Inner.method2(String,String) [InvalidNullConversion] 181 src/test/pkg/Outer.kt:8: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter string in test.pkg.Outer.Inner.method2(String string, String maybeString) [InvalidNullConversion] 182 src/test/pkg/Outer.kt:9: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter string in test.pkg.Outer.Inner.method3(String maybeString, String string) [InvalidNullConversion] 183 """, 184 inputKotlinStyleNulls = true, 185 outputKotlinStyleNulls = true, 186 checkCompatibilityApiReleased = """ 187 package test.pkg { 188 public final class Outer { 189 ctor public Outer(); 190 method public final String? method1(String, String?); 191 method public final String method2(String?, String); 192 method public final String? method3(String, String?); 193 } 194 public static final class Outer.Inner { 195 ctor public Outer.Inner(); 196 method public final String method2(String?, String); 197 method public final String? method3(String, String?); 198 } 199 } 200 """, 201 sourceFiles = arrayOf( 202 kotlin( 203 """ 204 package test.pkg 205 206 class Outer { 207 fun method1(string: String, maybeString: String?): String? = null 208 fun method2(string: String, maybeString: String?): String? = null 209 fun method3(maybeString: String?, string : String): String = "" 210 class Inner { 211 fun method2(string: String, maybeString: String?): String? = null 212 fun method3(maybeString: String?, string : String): String = "" 213 } 214 } 215 """ 216 ) 217 ) 218 ) 219 } 220 221 @Test Java Parameter Name Changenull222 fun `Java Parameter Name Change`() { 223 check( 224 expectedIssues = """ 225 src/test/pkg/JavaClass.java:6: error: Attempted to remove parameter name from parameter newName in test.pkg.JavaClass.method1 [ParameterNameChange] 226 src/test/pkg/JavaClass.java:7: error: Attempted to change parameter name from secondParameter to newName in method test.pkg.JavaClass.method2 [ParameterNameChange] 227 """, 228 checkCompatibilityApiReleased = """ 229 package test.pkg { 230 public class JavaClass { 231 ctor public JavaClass(); 232 method public String method1(String parameterName); 233 method public String method2(String firstParameter, String secondParameter); 234 } 235 } 236 """, 237 sourceFiles = arrayOf( 238 java( 239 """ 240 @Suppress("all") 241 package test.pkg; 242 import androidx.annotation.ParameterName; 243 244 public class JavaClass { 245 public String method1(String newName) { return null; } 246 public String method2(@ParameterName("firstParameter") String s, @ParameterName("newName") String prevName) { return null; } 247 } 248 """ 249 ), 250 supportParameterName 251 ), 252 extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation") 253 ) 254 } 255 256 @Test Kotlin Parameter Name Changenull257 fun `Kotlin Parameter Name Change`() { 258 check( 259 expectedIssues = """ 260 src/test/pkg/KotlinClass.kt:4: error: Attempted to change parameter name from prevName to newName in method test.pkg.KotlinClass.method1 [ParameterNameChange] 261 """, 262 inputKotlinStyleNulls = true, 263 outputKotlinStyleNulls = true, 264 checkCompatibilityApiReleased = """ 265 package test.pkg { 266 public final class KotlinClass { 267 ctor public KotlinClass(); 268 method public final String? method1(String prevName); 269 } 270 } 271 """, 272 sourceFiles = arrayOf( 273 kotlin( 274 """ 275 package test.pkg 276 277 class KotlinClass { 278 fun method1(newName: String): String? = null 279 } 280 """ 281 ) 282 ) 283 ) 284 } 285 286 @Test Kotlin Coroutinesnull287 fun `Kotlin Coroutines`() { 288 check( 289 expectedIssues = "", 290 inputKotlinStyleNulls = true, 291 outputKotlinStyleNulls = true, 292 checkCompatibilityApiReleased = """ 293 package test.pkg { 294 public final class TestKt { 295 ctor public TestKt(); 296 method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit>); 297 } 298 } 299 """, 300 signatureSource = """ 301 package test.pkg { 302 public final class TestKt { 303 ctor public TestKt(); 304 method public static suspend inline Object hello(@NonNull kotlin.coroutines.Continuation<? super kotlin.Unit> p); 305 } 306 } 307 """ 308 ) 309 } 310 311 @Test Remove operatornull312 fun `Remove operator`() { 313 check( 314 expectedIssues = """ 315 src/test/pkg/Foo.kt:4: error: Cannot remove `operator` modifier from method test.pkg.Foo.plus(String): Incompatible change [OperatorRemoval] 316 """, 317 checkCompatibilityApiReleased = """ 318 package test.pkg { 319 public final class Foo { 320 ctor public Foo(); 321 method public final operator void plus(String s); 322 } 323 } 324 """, 325 sourceFiles = arrayOf( 326 kotlin( 327 """ 328 package test.pkg 329 330 class Foo { 331 fun plus(s: String) { } 332 } 333 """ 334 ) 335 ) 336 ) 337 } 338 339 @Test Remove varargnull340 fun `Remove vararg`() { 341 check( 342 expectedIssues = """ 343 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] 344 """, 345 checkCompatibilityApiReleased = """ 346 package test.pkg { 347 public final class TestKt { 348 method public static final void method1(int[] x); 349 method public static final void method2(int... x); 350 } 351 } 352 """, 353 sourceFiles = arrayOf( 354 kotlin( 355 """ 356 package test.pkg 357 fun method1(vararg x: Int) { } 358 fun method2(x: IntArray) { } 359 """ 360 ) 361 ) 362 ) 363 } 364 365 @Test Add finalnull366 fun `Add final`() { 367 // Adding final on class or method is incompatible; adding it on a parameter is fine. 368 // Field is iffy. 369 check( 370 expectedIssues = """ 371 src/test/pkg/Java.java:4: error: Method test.pkg.Java.method has added 'final' qualifier [AddedFinal] 372 src/test/pkg/Kotlin.kt:4: error: Method test.pkg.Kotlin.method has added 'final' qualifier [AddedFinal] 373 """, 374 checkCompatibilityApiReleased = """ 375 package test.pkg { 376 public class 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 = arrayOf( 386 kotlin( 387 """ 388 package test.pkg 389 390 open class Kotlin { 391 fun method(s: String) { } 392 } 393 """ 394 ), 395 java( 396 """ 397 package test.pkg; 398 public class Java { 399 private Java() { } 400 public final void method(final int parameter) { } 401 } 402 """ 403 ) 404 ) 405 ) 406 } 407 408 @Test Inherited finalnull409 fun `Inherited final`() { 410 // Make sure that we correctly compare effectively final (inherited from surrounding class) 411 // between the signature file codebase and the real codebase 412 check( 413 expectedIssues = """ 414 """, 415 checkCompatibilityApiReleased = """ 416 package test.pkg { 417 public final class Cls extends test.pkg.Parent { 418 } 419 public class Parent { 420 method public void method(int); 421 } 422 } 423 """, 424 sourceFiles = arrayOf( 425 java( 426 """ 427 package test.pkg; 428 public final class Cls extends Parent { 429 private Cls() { } 430 @Override public void method(final int parameter) { } 431 } 432 """ 433 ), 434 java( 435 """ 436 package test.pkg; 437 public class Parent { 438 private Parent() { } 439 public void method(final int parameter) { } 440 } 441 """ 442 ) 443 ) 444 ) 445 } 446 447 @Test Implicit concretenull448 fun `Implicit concrete`() { 449 // Doclava signature files sometimes leave out overridden methods of 450 // abstract methods. We don't want to list these as having changed 451 // their abstractness. 452 check( 453 expectedIssues = """ 454 """, 455 checkCompatibilityApiReleased = """ 456 package test.pkg { 457 public final class Cls extends test.pkg.Parent { 458 } 459 public class Parent { 460 method public abstract void method(int); 461 } 462 } 463 """, 464 sourceFiles = arrayOf( 465 java( 466 """ 467 package test.pkg; 468 public final class Cls extends Parent { 469 private Cls() { } 470 @Override public void method(final int parameter) { } 471 } 472 """ 473 ), 474 java( 475 """ 476 package test.pkg; 477 public class Parent { 478 private Parent() { } 479 public abstract void method(final int parameter); 480 } 481 """ 482 ) 483 ) 484 ) 485 } 486 487 @Test Implicit modifiers from inherited super classesnull488 fun `Implicit modifiers from inherited super classes`() { 489 check( 490 expectedIssues = """ 491 """, 492 checkCompatibilityApiReleased = """ 493 package test.pkg { 494 public final class Cls implements test.pkg.Interface { 495 method public void method(int); 496 method public final void method2(int); 497 } 498 public interface Interface { 499 method public void method2(int); 500 } 501 } 502 """, 503 sourceFiles = arrayOf( 504 java( 505 """ 506 package test.pkg; 507 public final class Cls extends HiddenParent implements Interface { 508 private Cls() { } 509 @Override public void method(final int parameter) { } 510 } 511 """ 512 ), 513 java( 514 """ 515 package test.pkg; 516 class HiddenParent { 517 private HiddenParent() { } 518 public abstract void method(final int parameter) { } 519 public final void method2(final int parameter) { } 520 } 521 """ 522 ), 523 java( 524 """ 525 package test.pkg; 526 public interface Interface { 527 void method2(final int parameter) { } 528 } 529 """ 530 ) 531 ) 532 ) 533 } 534 535 @Test Wildcard comparisonsnull536 fun `Wildcard comparisons`() { 537 // Doclava signature files sometimes leave out overridden methods of 538 // abstract methods. We don't want to list these as having changed 539 // their abstractness. 540 check( 541 expectedIssues = """ 542 """, 543 checkCompatibilityApiReleased = """ 544 package test.pkg { 545 public abstract class AbstractMap<K, V> implements java.util.Map { 546 method public java.util.Set<K> keySet(); 547 method public V put(K, V); 548 method public void putAll(java.util.Map<? extends K, ? extends V>); 549 } 550 public abstract class EnumMap<K extends java.lang.Enum<K>, V> extends test.pkg.AbstractMap { 551 } 552 } 553 """, 554 sourceFiles = arrayOf( 555 java( 556 """ 557 package test.pkg; 558 @SuppressWarnings({"ConstantConditions", "NullableProblems"}) 559 public abstract class AbstractMap<K, V> implements java.util.Map { 560 private AbstractMap() { } 561 public V put(K k, V v) { return null; } 562 public java.util.Set<K> keySet() { return null; } 563 public void putAll(java.util.Map<? extends K, ? extends V> x) { } 564 } 565 """ 566 ), 567 java( 568 """ 569 package test.pkg; 570 public abstract class EnumMap<K extends java.lang.Enum<K>, V> extends test.pkg.AbstractMap { 571 private EnumMap() { } 572 public V put(K k, V v) { return null; } 573 } 574 """ 575 ) 576 ) 577 ) 578 } 579 580 @Test Added constructornull581 fun `Added constructor`() { 582 // Regression test for issue 116619591 583 check( 584 expectedIssues = "", 585 checkCompatibilityApiReleased = """ 586 package test.pkg { 587 public abstract class AbstractMap<K, V> implements java.util.Map { 588 } 589 } 590 """, 591 sourceFiles = arrayOf( 592 java( 593 """ 594 package test.pkg; 595 @SuppressWarnings({"ConstantConditions", "NullableProblems"}) 596 public abstract class AbstractMap<K, V> implements java.util.Map { 597 } 598 """ 599 ) 600 ) 601 ) 602 } 603 604 @Test Remove infixnull605 fun `Remove infix`() { 606 check( 607 expectedIssues = """ 608 src/test/pkg/Foo.kt:5: error: Cannot remove `infix` modifier from method test.pkg.Foo.add2(String): Incompatible change [InfixRemoval] 609 """, 610 checkCompatibilityApiReleased = """ 611 package test.pkg { 612 public final class Foo { 613 ctor public Foo(); 614 method public final void add1(String s); 615 method public final infix void add2(String s); 616 method public final infix void add3(String s); 617 } 618 } 619 """, 620 sourceFiles = arrayOf( 621 kotlin( 622 """ 623 package test.pkg 624 625 class Foo { 626 infix fun add1(s: String) { } 627 fun add2(s: String) { } 628 infix fun add3(s: String) { } 629 } 630 """ 631 ) 632 ) 633 ) 634 } 635 636 @Test Add sealnull637 fun `Add seal`() { 638 check( 639 expectedIssues = """ 640 src/test/pkg/Foo.kt:2: error: Cannot add 'sealed' modifier to class test.pkg.Foo: Incompatible change [AddSealed] 641 """, 642 checkCompatibilityApiReleased = """ 643 package test.pkg { 644 public class Foo { 645 } 646 } 647 """, 648 sourceFiles = arrayOf( 649 kotlin( 650 """ 651 package test.pkg 652 sealed class Foo 653 """ 654 ) 655 ) 656 ) 657 } 658 659 @Test Remove default parameternull660 fun `Remove default parameter`() { 661 check( 662 expectedIssues = """ 663 src/test/pkg/Foo.kt:3: error: Attempted to remove default value from parameter s1 in test.pkg.Foo [DefaultValueChange] [See https://s.android.com/api-guidelines#default-value-removal] 664 src/test/pkg/Foo.kt:7: error: Attempted to remove default value from parameter s1 in test.pkg.Foo.method4 [DefaultValueChange] [See https://s.android.com/api-guidelines#default-value-removal] 665 666 """, 667 inputKotlinStyleNulls = true, 668 checkCompatibilityApiReleased = """ 669 package test.pkg { 670 public final class Foo { 671 ctor public Foo(String? s1 = null); 672 method public final void method1(boolean b, String? s1); 673 method public final void method2(boolean b, String? s1); 674 method public final void method3(boolean b, String? s1 = "null"); 675 method public final void method4(boolean b, String? s1 = "null"); 676 } 677 } 678 """, 679 sourceFiles = arrayOf( 680 kotlin( 681 """ 682 package test.pkg 683 684 class Foo(s1: String?) { 685 fun method1(b: Boolean, s1: String?) { } // No change 686 fun method2(b: Boolean, s1: String? = null) { } // Adding: OK 687 fun method3(b: Boolean, s1: String? = null) { } // No change 688 fun method4(b: Boolean, s1: String?) { } // Removed 689 } 690 """ 691 ) 692 ) 693 ) 694 } 695 696 @Test Remove optional parameternull697 fun `Remove optional parameter`() { 698 check( 699 expectedIssues = """ 700 src/test/pkg/Foo.kt:3: error: Attempted to remove default value from parameter s1 in test.pkg.Foo [DefaultValueChange] [See https://s.android.com/api-guidelines#default-value-removal] 701 src/test/pkg/Foo.kt:7: error: Attempted to remove default value from parameter s1 in test.pkg.Foo.method4 [DefaultValueChange] [See https://s.android.com/api-guidelines#default-value-removal] 702 """, 703 inputKotlinStyleNulls = true, 704 format = FileFormat.V4, 705 checkCompatibilityApiReleased = """ 706 package test.pkg { 707 public final class Foo { 708 ctor public Foo(optional String? s1); 709 method public final void method1(boolean b, String? s1); 710 method public final void method2(boolean b, String? s1); 711 method public final void method3(boolean b, optional String? s1); 712 method public final void method4(boolean b, optional String? s1); 713 } 714 } 715 """, 716 sourceFiles = arrayOf( 717 kotlin( 718 """ 719 package test.pkg 720 721 class Foo(s1: String?) { // Removed 722 fun method1(b: Boolean, s1: String?) { } // No change 723 fun method2(b: Boolean, s1: String? = null) { } // Adding: OK 724 fun method3(b: Boolean, s1: String? = null) { } // No change 725 fun method4(b: Boolean, s1: String?) { } // Removed 726 } 727 """ 728 ) 729 ) 730 ) 731 } 732 733 @Test Removing method or field when still available via inheritance is OKnull734 fun `Removing method or field when still available via inheritance is OK`() { 735 check( 736 expectedIssues = """ 737 """, 738 checkCompatibilityApiReleased = """ 739 package test.pkg { 740 public class Child extends test.pkg.Parent { 741 ctor public Child(); 742 field public int field1; 743 method public void method1(); 744 } 745 public class Parent { 746 ctor public Parent(); 747 field public int field1; 748 field public int field2; 749 method public void method1(); 750 method public void method2(); 751 } 752 } 753 """, 754 sourceFiles = arrayOf( 755 java( 756 """ 757 package test.pkg; 758 759 public class Parent { 760 public int field1 = 0; 761 public int field2 = 0; 762 public void method1() { } 763 public void method2() { } 764 } 765 """ 766 ), 767 java( 768 """ 769 package test.pkg; 770 771 public class Child extends Parent { 772 public int field1 = 0; 773 @Override public void method1() { } // NO CHANGE 774 //@Override public void method2() { } // REMOVED OK: Still inherited 775 } 776 """ 777 ) 778 ) 779 ) 780 } 781 782 @Test Change field constant value, change field typenull783 fun `Change field constant value, change field type`() { 784 check( 785 expectedIssues = """ 786 src/test/pkg/Parent.java:5: error: Field test.pkg.Parent.field2 has changed value from 2 to 42 [ChangedValue] 787 src/test/pkg/Parent.java:6: error: Field test.pkg.Parent.field3 has changed type from int to char [ChangedType] 788 src/test/pkg/Parent.java:7: error: Field test.pkg.Parent.field4 has added 'final' qualifier [AddedFinal] 789 src/test/pkg/Parent.java:8: error: Field test.pkg.Parent.field5 has changed 'static' qualifier [ChangedStatic] 790 src/test/pkg/Parent.java:10: error: Field test.pkg.Parent.field7 has changed 'volatile' qualifier [ChangedVolatile] 791 src/test/pkg/Parent.java:20: error: Field test.pkg.Parent.field94 has changed value from 1 to 42 [ChangedValue] 792 """, 793 checkCompatibilityApiReleased = """ 794 package test.pkg { 795 public class Parent { 796 ctor public Parent(); 797 field public static final int field1 = 1; // 0x1 798 field public static final int field2 = 2; // 0x2 799 field public int field3; 800 field public int field4 = 4; // 0x4 801 field public int field5; 802 field public int field6; 803 field public int field7; 804 field public deprecated int field8; 805 field public int field9; 806 field public static final int field91 = 1; // 0x1 807 field public static final int field92 = 1; // 0x1 808 field public static final int field93 = 1; // 0x1 809 field public static final int field94 = 1; // 0x1 810 } 811 } 812 """, 813 sourceFiles = arrayOf( 814 java( 815 """ 816 package test.pkg; 817 import android.annotation.SuppressLint; 818 public class Parent { 819 public static final int field1 = 1; // UNCHANGED 820 public static final int field2 = 42; // CHANGED VALUE 821 public char field3 = 3; // CHANGED TYPE 822 public final int field4 = 4; // ADDED FINAL 823 public static int field5 = 5; // ADDED STATIC 824 public transient int field6 = 6; // ADDED TRANSIENT 825 public volatile int field7 = 7; // ADDED VOLATILE 826 public int field8 = 8; // REMOVED DEPRECATED 827 /** @deprecated */ @Deprecated public int field9 = 8; // ADDED DEPRECATED 828 @SuppressLint("ChangedValue") 829 public static final int field91 = 42;// CHANGED VALUE: Suppressed 830 @SuppressLint("ChangedValue:Field test.pkg.Parent.field92 has changed value from 1 to 42") 831 public static final int field92 = 42;// CHANGED VALUE: Suppressed with same message 832 @SuppressLint("ChangedValue: Field test.pkg.Parent.field93 has changed value from 1 to 42") 833 public static final int field93 = 42;// CHANGED VALUE: Suppressed with same message 834 @SuppressLint("ChangedValue:Field test.pkg.Parent.field94 has changed value from 10 to 1") 835 public static final int field94 = 42;// CHANGED VALUE: Suppressed but with different message 836 } 837 """ 838 ), 839 suppressLintSource 840 ), 841 extraArguments = arrayOf(ARG_HIDE_PACKAGE, "android.annotation") 842 ) 843 } 844 845 @Test Change annotation default method value changenull846 fun `Change annotation default method value change`() { 847 check( 848 inputKotlinStyleNulls = true, 849 expectedIssues = """ 850 src/test/pkg/ExportedProperty.java:15: error: Method test.pkg.ExportedProperty.category has changed value from "" to nothing [ChangedValue] 851 src/test/pkg/ExportedProperty.java:14: error: Method test.pkg.ExportedProperty.floating has changed value from 1.0f to 1.1f [ChangedValue] 852 src/test/pkg/ExportedProperty.java:13: error: Method test.pkg.ExportedProperty.prefix has changed value from "" to "hello" [ChangedValue] 853 """, 854 checkCompatibilityApiReleased = """ 855 package test.pkg { 856 public @interface ExportedProperty { 857 method public abstract boolean resolveId() default false; 858 method public abstract float floating() default 1.0f; 859 method public abstract String! prefix() default ""; 860 method public abstract String! category() default ""; 861 method public abstract boolean formatToHexString(); 862 } 863 } 864 """, 865 sourceFiles = arrayOf( 866 java( 867 """ 868 package test.pkg; 869 870 import java.lang.annotation.ElementType; 871 import java.lang.annotation.Retention; 872 import java.lang.annotation.RetentionPolicy; 873 import java.lang.annotation.Target; 874 import static java.lang.annotation.RetentionPolicy.SOURCE; 875 876 @Target({ElementType.FIELD, ElementType.METHOD}) 877 @Retention(RetentionPolicy.RUNTIME) 878 public @interface ExportedProperty { 879 boolean resolveId() default false; // UNCHANGED 880 String prefix() default "hello"; // CHANGED VALUE 881 float floating() default 1.1f; // CHANGED VALUE 882 String category(); // REMOVED VALUE 883 boolean formatToHexString() default false; // ADDED VALUE 884 } 885 """ 886 ) 887 ) 888 ) 889 } 890 891 @Test Incompatible class change -- class to interfacenull892 fun `Incompatible class change -- class to interface`() { 893 check( 894 expectedIssues = """ 895 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent changed class/interface declaration [ChangedClass] 896 """, 897 checkCompatibilityApiReleased = """ 898 package test.pkg { 899 public class Parent { 900 } 901 } 902 """, 903 sourceFiles = arrayOf( 904 java( 905 """ 906 package test.pkg; 907 908 public interface Parent { 909 } 910 """ 911 ) 912 ) 913 ) 914 } 915 916 @Test Incompatible class change -- change implemented interfacesnull917 fun `Incompatible class change -- change implemented interfaces`() { 918 check( 919 expectedIssues = """ 920 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent no longer implements java.io.Closeable [RemovedInterface] 921 """, 922 checkCompatibilityApiReleased = """ 923 package test.pkg { 924 public abstract class Parent implements java.io.Closeable, java.util.Map { 925 } 926 } 927 """, 928 sourceFiles = arrayOf( 929 java( 930 """ 931 package test.pkg; 932 933 public abstract class Parent implements java.util.Map, java.util.List { 934 private Parent() {} 935 } 936 """ 937 ) 938 ) 939 ) 940 } 941 942 @Test Incompatible class change -- change qualifiersnull943 fun `Incompatible class change -- change qualifiers`() { 944 check( 945 expectedIssues = """ 946 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent changed 'abstract' qualifier [ChangedAbstract] 947 src/test/pkg/Parent.java:3: error: Class test.pkg.Parent changed 'static' qualifier [ChangedStatic] 948 """, 949 checkCompatibilityApiReleased = """ 950 package test.pkg { 951 public class Parent { 952 } 953 } 954 """, 955 sourceFiles = arrayOf( 956 java( 957 """ 958 package test.pkg; 959 960 public abstract static class Parent { 961 private Parent() {} 962 } 963 """ 964 ) 965 ) 966 ) 967 } 968 969 @Test Incompatible class change -- finalnull970 fun `Incompatible class change -- final`() { 971 check( 972 expectedIssues = """ 973 src/test/pkg/Class1.java:3: error: Class test.pkg.Class1 added 'final' qualifier [AddedFinal] 974 TESTROOT/released-api.txt:3: error: Removed constructor test.pkg.Class1() [RemovedMethod] 975 """, 976 checkCompatibilityApiReleased = """ 977 package test.pkg { 978 public class Class1 { 979 ctor public Class1(); 980 } 981 public class Class2 { 982 } 983 public final class Class3 { 984 } 985 } 986 """, 987 sourceFiles = arrayOf( 988 java( 989 """ 990 package test.pkg; 991 992 public final class Class1 { 993 private Class1() {} 994 } 995 """ 996 ), 997 java( 998 """ 999 package test.pkg; 1000 1001 public final class Class2 { 1002 private Class2() {} 1003 } 1004 """ 1005 ), 1006 java( 1007 """ 1008 package test.pkg; 1009 1010 public class Class3 { 1011 private Class3() {} 1012 } 1013 """ 1014 ) 1015 ) 1016 ) 1017 } 1018 1019 @Test Incompatible class change -- visibilitynull1020 fun `Incompatible class change -- visibility`() { 1021 check( 1022 expectedIssues = """ 1023 src/test/pkg/Class1.java:3: error: Class test.pkg.Class1 changed visibility from protected to public [ChangedScope] 1024 src/test/pkg/Class2.java:3: error: Class test.pkg.Class2 changed visibility from public to protected [ChangedScope] 1025 """, 1026 checkCompatibilityApiReleased = """ 1027 package test.pkg { 1028 protected class Class1 { 1029 } 1030 public class Class2 { 1031 } 1032 } 1033 """, 1034 sourceFiles = arrayOf( 1035 java( 1036 """ 1037 package test.pkg; 1038 1039 public class Class1 { 1040 private Class1() {} 1041 } 1042 """ 1043 ), 1044 java( 1045 """ 1046 package test.pkg; 1047 1048 protected class Class2 { 1049 private Class2() {} 1050 } 1051 """ 1052 ) 1053 ) 1054 ) 1055 } 1056 1057 @Test Incompatible class change -- superclassnull1058 fun `Incompatible class change -- superclass`() { 1059 check( 1060 expectedIssues = """ 1061 src/test/pkg/Class3.java:3: error: Class test.pkg.Class3 superclass changed from java.lang.Char to java.lang.Number [ChangedSuperclass] 1062 """, 1063 checkCompatibilityApiReleased = """ 1064 package test.pkg { 1065 public abstract class Class1 { 1066 } 1067 public abstract class Class2 extends java.lang.Number { 1068 } 1069 public abstract class Class3 extends java.lang.Char { 1070 } 1071 } 1072 """, 1073 sourceFiles = arrayOf( 1074 java( 1075 """ 1076 package test.pkg; 1077 1078 public abstract class Class1 extends java.lang.Short { 1079 private Class1() {} 1080 } 1081 """ 1082 ), 1083 java( 1084 """ 1085 package test.pkg; 1086 1087 public abstract class Class2 extends java.lang.Float { 1088 private Class2() {} 1089 } 1090 """ 1091 ), 1092 java( 1093 """ 1094 package test.pkg; 1095 1096 public abstract class Class3 extends java.lang.Number { 1097 private Class3() {} 1098 } 1099 """ 1100 ) 1101 ) 1102 ) 1103 } 1104 1105 @Test allow adding first type parameternull1106 fun `allow adding first type parameter`() { 1107 check( 1108 checkCompatibilityApiReleased = """ 1109 package test.pkg { 1110 public class Foo { 1111 } 1112 } 1113 """, 1114 signatureSource = """ 1115 package test.pkg { 1116 public class Foo<T> { 1117 } 1118 } 1119 """ 1120 ) 1121 } 1122 1123 @Test disallow removing type parameternull1124 fun `disallow removing type parameter`() { 1125 check( 1126 expectedIssues = """ 1127 TESTROOT/load-api.txt:2: error: Class test.pkg.Foo changed number of type parameters from 1 to 0 [ChangedType] 1128 """, 1129 checkCompatibilityApiReleased = """ 1130 package test.pkg { 1131 public class Foo<T> { 1132 } 1133 } 1134 """, 1135 signatureSource = """ 1136 package test.pkg { 1137 public class Foo { 1138 } 1139 } 1140 """ 1141 ) 1142 } 1143 1144 @Test disallow changing number of type parametersnull1145 fun `disallow changing number of type parameters`() { 1146 check( 1147 expectedIssues = """ 1148 TESTROOT/load-api.txt:2: error: Class test.pkg.Foo changed number of type parameters from 1 to 2 [ChangedType] 1149 """, 1150 checkCompatibilityApiReleased = """ 1151 package test.pkg { 1152 public class Foo<A> { 1153 } 1154 } 1155 """, 1156 signatureSource = """ 1157 package test.pkg { 1158 public class Foo<A,B> { 1159 } 1160 } 1161 """ 1162 ) 1163 } 1164 1165 @Test Incompatible method change -- modifiersnull1166 fun `Incompatible method change -- modifiers`() { 1167 check( 1168 expectedIssues = """ 1169 src/test/pkg/MyClass.java:5: error: Method test.pkg.MyClass.myMethod2 has changed 'abstract' qualifier [ChangedAbstract] 1170 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.myMethod3 has changed 'static' qualifier [ChangedStatic] 1171 """, 1172 checkCompatibilityApiReleased = """ 1173 package test.pkg { 1174 public abstract class MyClass { 1175 method public void myMethod2(); 1176 method public void myMethod3(); 1177 method deprecated public void myMethod4(); 1178 } 1179 } 1180 """, 1181 sourceFiles = arrayOf( 1182 java( 1183 """ 1184 package test.pkg; 1185 1186 public abstract class MyClass { 1187 private MyClass() {} 1188 public native abstract void myMethod2(); // Note that Errors.CHANGE_NATIVE is hidden by default 1189 public static void myMethod3() {} 1190 public void myMethod4() {} 1191 } 1192 """ 1193 ) 1194 ) 1195 ) 1196 } 1197 1198 @Test Incompatible method change -- finalnull1199 fun `Incompatible method change -- final`() { 1200 check( 1201 expectedIssues = """ 1202 src/test/pkg/Outer.java:7: error: Method test.pkg.Outer.Class1.method1 has added 'final' qualifier [AddedFinal] 1203 src/test/pkg/Outer.java:19: error: Method test.pkg.Outer.Class4.method4 has removed 'final' qualifier [RemovedFinal] 1204 """, 1205 checkCompatibilityApiReleased = """ 1206 package test.pkg { 1207 public abstract class Outer { 1208 } 1209 public class Outer.Class1 { 1210 method public void method1(); 1211 } 1212 public final class Outer.Class2 { 1213 method public void method2(); 1214 } 1215 public final class Outer.Class3 { 1216 method public void method3(); 1217 } 1218 public class Outer.Class4 { 1219 method public final void method4(); 1220 } 1221 } 1222 """, 1223 sourceFiles = arrayOf( 1224 java( 1225 """ 1226 package test.pkg; 1227 1228 public abstract class Outer { 1229 private Outer() {} 1230 public class Class1 { 1231 private Class1() {} 1232 public final void method1() { } // Added final 1233 } 1234 public final class Class2 { 1235 private Class2() {} 1236 public final void method2() { } // Added final but class is effectively final so no change 1237 } 1238 public final class Class3 { 1239 private Class3() {} 1240 public void method3() { } // Removed final but is still effectively final 1241 } 1242 public class Class4 { 1243 private Class4() {} 1244 public void method4() { } // Removed final 1245 } 1246 } 1247 """ 1248 ) 1249 ) 1250 ) 1251 } 1252 1253 @Test Incompatible method change -- visibilitynull1254 fun `Incompatible method change -- visibility`() { 1255 check( 1256 expectedIssues = """ 1257 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.myMethod2 changed visibility from public to protected [ChangedScope] 1258 """, 1259 checkCompatibilityApiReleased = """ 1260 package test.pkg { 1261 public abstract class MyClass { 1262 method protected void myMethod1(); 1263 method public void myMethod2(); 1264 } 1265 } 1266 """, 1267 sourceFiles = arrayOf( 1268 java( 1269 """ 1270 package test.pkg; 1271 1272 public abstract class MyClass { 1273 private MyClass() {} 1274 public void myMethod1() {} 1275 protected void myMethod2() {} 1276 } 1277 """ 1278 ) 1279 ) 1280 ) 1281 } 1282 1283 @Ignore("TODO(aurimas) reenable once this is default on") 1284 @Test Incompatible method change -- throws listnull1285 fun `Incompatible method change -- throws list`() { 1286 check( 1287 expectedIssues = """ 1288 src/test/pkg/MyClass.java:7: error: Method test.pkg.MyClass.method1 added thrown exception java.io.IOException [ChangedThrows] 1289 src/test/pkg/MyClass.java:8: error: Method test.pkg.MyClass.method2 no longer throws exception java.io.IOException [ChangedThrows] 1290 src/test/pkg/MyClass.java:9: error: Method test.pkg.MyClass.method3 no longer throws exception java.io.IOException [ChangedThrows] 1291 src/test/pkg/MyClass.java:9: error: Method test.pkg.MyClass.method3 no longer throws exception java.lang.NumberFormatException [ChangedThrows] 1292 src/test/pkg/MyClass.java:9: error: Method test.pkg.MyClass.method3 added thrown exception java.lang.UnsupportedOperationException [ChangedThrows] 1293 """, 1294 checkCompatibilityApiReleased = """ 1295 package test.pkg { 1296 public abstract class MyClass { 1297 method public void finalize() throws java.lang.Throwable; 1298 method public void method1(); 1299 method public void method2() throws java.io.IOException; 1300 method public void method3() throws java.io.IOException, java.lang.NumberFormatException; 1301 } 1302 } 1303 """, 1304 sourceFiles = arrayOf( 1305 java( 1306 """ 1307 package test.pkg; 1308 1309 @SuppressWarnings("RedundantThrows") 1310 public abstract class MyClass { 1311 private MyClass() {} 1312 public void finalize() {} 1313 public void method1() throws java.io.IOException {} 1314 public void method2() {} 1315 public void method3() throws java.lang.UnsupportedOperationException {} 1316 } 1317 """ 1318 ) 1319 ) 1320 ) 1321 } 1322 1323 @Test Incompatible method change -- return typesnull1324 fun `Incompatible method change -- return types`() { 1325 check( 1326 expectedIssues = """ 1327 src/test/pkg/MyClass.java:5: error: Method test.pkg.MyClass.method1 has changed return type from float to int [ChangedType] 1328 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.method2 has changed return type from java.util.List<Number> to java.util.List<java.lang.Integer> [ChangedType] 1329 src/test/pkg/MyClass.java:7: error: Method test.pkg.MyClass.method3 has changed return type from java.util.List<Integer> to java.util.List<java.lang.Number> [ChangedType] 1330 src/test/pkg/MyClass.java:8: error: Method test.pkg.MyClass.method4 has changed return type from String to String[] [ChangedType] 1331 src/test/pkg/MyClass.java:9: error: Method test.pkg.MyClass.method5 has changed return type from String[] to String[][] [ChangedType] 1332 src/test/pkg/MyClass.java:11: error: Method test.pkg.MyClass.method7 has changed return type from T to Number [ChangedType] 1333 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] 1334 """, 1335 checkCompatibilityApiReleased = """ 1336 package test.pkg { 1337 public abstract class MyClass<T extends Number> { 1338 method public float method1(); 1339 method public java.util.List<Number> method2(); 1340 method public java.util.List<Integer> method3(); 1341 method public String method4(); 1342 method public String[] method5(); 1343 method public <X extends java.lang.Throwable> T method6(java.util.function.Supplier<? extends X>); 1344 method public <X extends java.lang.Throwable> T method7(java.util.function.Supplier<? extends X>); 1345 method public <X extends java.lang.Throwable> Number method8(java.util.function.Supplier<? extends X>); 1346 method public <X extends java.lang.Throwable> X method9(java.util.function.Supplier<? extends X>); 1347 } 1348 } 1349 """, 1350 sourceFiles = arrayOf( 1351 java( 1352 """ 1353 package test.pkg; 1354 1355 public abstract class MyClass<U extends Number> { // Changing type variable name is fine/compatible 1356 private MyClass() {} 1357 public int method1() { return 0; } 1358 public java.util.List<Integer> method2() { return null; } 1359 public java.util.List<Number> method3() { return null; } 1360 public String[] method4() { return null; } 1361 public String[][] method5() { return null; } 1362 public <X extends java.lang.Throwable> U method6(java.util.function.Supplier<? extends X> arg) { return null; } 1363 public <X extends java.lang.Throwable> Number method7(java.util.function.Supplier<? extends X> arg) { return null; } 1364 public <X extends java.lang.Throwable> U method8(java.util.function.Supplier<? extends X> arg) { return null; } 1365 public <X extends java.lang.Throwable> U method9(java.util.function.Supplier<? extends X> arg) { return null; } 1366 } 1367 """ 1368 ) 1369 ) 1370 ) 1371 } 1372 1373 @Test Incompatible field change -- visibility and removing finalnull1374 fun `Incompatible field change -- visibility and removing final`() { 1375 check( 1376 expectedIssues = """ 1377 src/test/pkg/MyClass.java:6: error: Field test.pkg.MyClass.myField2 changed visibility from public to protected [ChangedScope] 1378 """, 1379 checkCompatibilityApiReleased = """ 1380 package test.pkg { 1381 public abstract class MyClass { 1382 field protected int myField1; 1383 field public int myField2; 1384 field public final int myField3; 1385 } 1386 } 1387 """, 1388 sourceFiles = arrayOf( 1389 java( 1390 """ 1391 package test.pkg; 1392 1393 public abstract class MyClass { 1394 private MyClass() {} 1395 public int myField1 = 1; 1396 protected int myField2 = 1; 1397 public int myField3 = 1; 1398 } 1399 """ 1400 ) 1401 ) 1402 ) 1403 } 1404 1405 @Test Adding classes, interfaces and packages, and removing thesenull1406 fun `Adding classes, interfaces and packages, and removing these`() { 1407 check( 1408 expectedIssues = """ 1409 TESTROOT/released-api.txt:2: error: Removed class test.pkg.MyOldClass [RemovedClass] 1410 TESTROOT/released-api.txt:5: error: Removed package test.pkg3 [RemovedPackage] 1411 """, 1412 checkCompatibilityApiReleased = """ 1413 package test.pkg { 1414 public abstract class MyOldClass { 1415 } 1416 } 1417 package test.pkg3 { 1418 public abstract class MyOldClass { 1419 } 1420 } 1421 """, 1422 sourceFiles = arrayOf( 1423 java( 1424 """ 1425 package test.pkg; 1426 1427 public abstract class MyClass { 1428 private MyClass() {} 1429 } 1430 """ 1431 ), 1432 java( 1433 """ 1434 package test.pkg; 1435 1436 public interface MyInterface { 1437 } 1438 """ 1439 ), 1440 java( 1441 """ 1442 package test.pkg2; 1443 1444 public abstract class MyClass2 { 1445 private MyClass2() {} 1446 } 1447 """ 1448 ) 1449 ) 1450 ) 1451 } 1452 1453 @Test Test removing public constructornull1454 fun `Test removing public constructor`() { 1455 check( 1456 expectedIssues = """ 1457 TESTROOT/released-api.txt:3: error: Removed constructor test.pkg.MyClass() [RemovedMethod] 1458 """, 1459 checkCompatibilityApiReleased = """ 1460 package test.pkg { 1461 public abstract class MyClass { 1462 ctor public MyClass(); 1463 } 1464 } 1465 """, 1466 sourceFiles = arrayOf( 1467 java( 1468 """ 1469 package test.pkg; 1470 1471 public abstract class MyClass { 1472 private MyClass() {} 1473 } 1474 """ 1475 ) 1476 ) 1477 ) 1478 } 1479 1480 @Test Test type variables from text signature filesnull1481 fun `Test type variables from text signature files`() { 1482 check( 1483 expectedIssues = """ 1484 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] 1485 """, 1486 checkCompatibilityApiReleased = """ 1487 package test.pkg { 1488 public abstract class MyClass<T extends test.pkg.Number,T_SPLITR> { 1489 method public T myMethod1(); 1490 method public <S extends test.pkg.Number> S myMethod2(); 1491 method public <S> S myMethod3(); 1492 method public <S> S myMethod4(); 1493 method public java.util.List<byte[]> myMethod5(); 1494 method public T_SPLITR[] myMethod6(); 1495 } 1496 public class Number { 1497 ctor public Number(); 1498 } 1499 } 1500 """, 1501 sourceFiles = arrayOf( 1502 java( 1503 """ 1504 package test.pkg; 1505 1506 public abstract class MyClass<T extends Number,T_SPLITR> { 1507 private MyClass() {} 1508 public T myMethod1() { return null; } 1509 public <S extends Number> S myMethod2() { return null; } 1510 public <S> S myMethod3() { return null; } 1511 public <S extends Float> S myMethod4() { return null; } 1512 public java.util.List<byte[]> myMethod5() { return null; } 1513 public T_SPLITR[] myMethod6() { return null; } 1514 } 1515 """ 1516 ), 1517 java( 1518 """ 1519 package test.pkg; 1520 public class Number { 1521 } 1522 """ 1523 ) 1524 ) 1525 ) 1526 } 1527 1528 @Test Test Kotlin extensionsnull1529 fun `Test Kotlin extensions`() { 1530 check( 1531 inputKotlinStyleNulls = true, 1532 outputKotlinStyleNulls = true, 1533 expectedIssues = "", 1534 checkCompatibilityApiReleased = """ 1535 package androidx.content { 1536 public final class ContentValuesKt { 1537 method public static android.content.ContentValues contentValuesOf(kotlin.Pair<String,?>... pairs); 1538 } 1539 } 1540 """, 1541 sourceFiles = arrayOf( 1542 kotlin( 1543 "src/androidx/content/ContentValues.kt", 1544 """ 1545 package androidx.content 1546 1547 import android.content.ContentValues 1548 1549 fun contentValuesOf(vararg pairs: Pair<String, Any?>) = ContentValues(pairs.size).apply { 1550 for ((key, value) in pairs) { 1551 when (value) { 1552 null -> putNull(key) 1553 is String -> put(key, value) 1554 is Int -> put(key, value) 1555 is Long -> put(key, value) 1556 is Boolean -> put(key, value) 1557 is Float -> put(key, value) 1558 is Double -> put(key, value) 1559 is ByteArray -> put(key, value) 1560 is Byte -> put(key, value) 1561 is Short -> put(key, value) 1562 else -> { 1563 val valueType = value.javaClass.canonicalName 1564 throw IllegalArgumentException("Illegal value type") 1565 } 1566 } 1567 } 1568 } 1569 """ 1570 ) 1571 ) 1572 ) 1573 } 1574 1575 @Test Test Kotlin type boundsnull1576 fun `Test Kotlin type bounds`() { 1577 check( 1578 inputKotlinStyleNulls = false, 1579 outputKotlinStyleNulls = true, 1580 expectedIssues = "", 1581 checkCompatibilityApiReleased = """ 1582 package androidx.navigation { 1583 public final class NavDestination { 1584 ctor public NavDestination(); 1585 } 1586 public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> { 1587 ctor public NavDestinationBuilder(int id); 1588 method public D build(); 1589 } 1590 } 1591 """, 1592 sourceFiles = arrayOf( 1593 kotlin( 1594 """ 1595 package androidx.navigation 1596 1597 open class NavDestinationBuilder<out D : NavDestination>( 1598 id: Int 1599 ) { 1600 open fun build(): D { 1601 TODO() 1602 } 1603 } 1604 1605 class NavDestination 1606 """ 1607 ) 1608 ) 1609 ) 1610 } 1611 1612 @Test Test inherited methodsnull1613 fun `Test inherited methods`() { 1614 check( 1615 expectedIssues = """ 1616 """, 1617 checkCompatibilityApiReleased = """ 1618 package test.pkg { 1619 public class Child1 extends test.pkg.Parent { 1620 } 1621 public class Child2 extends test.pkg.Parent { 1622 method public void method0(java.lang.String, int); 1623 method public void method4(java.lang.String, int); 1624 } 1625 public class Child3 extends test.pkg.Parent { 1626 method public void method1(java.lang.String, int); 1627 method public void method2(java.lang.String, int); 1628 } 1629 public class Parent { 1630 method public void method1(java.lang.String, int); 1631 method public void method2(java.lang.String, int); 1632 method public void method3(java.lang.String, int); 1633 } 1634 } 1635 """, 1636 sourceFiles = arrayOf( 1637 java( 1638 """ 1639 package test.pkg; 1640 1641 public class Child1 extends Parent { 1642 private Child1() {} 1643 @Override 1644 public void method1(String first, int second) { 1645 } 1646 @Override 1647 public void method2(String first, int second) { 1648 } 1649 @Override 1650 public void method3(String first, int second) { 1651 } 1652 } 1653 """ 1654 ), 1655 java( 1656 """ 1657 package test.pkg; 1658 1659 public class Child2 extends Parent { 1660 private Child2() {} 1661 @Override 1662 public void method0(String first, int second) { 1663 } 1664 @Override 1665 public void method1(String first, int second) { 1666 } 1667 @Override 1668 public void method2(String first, int second) { 1669 } 1670 @Override 1671 public void method3(String first, int second) { 1672 } 1673 @Override 1674 public void method4(String first, int second) { 1675 } 1676 } 1677 """ 1678 ), 1679 java( 1680 """ 1681 package test.pkg; 1682 1683 public class Child3 extends Parent { 1684 private Child3() {} 1685 @Override 1686 public void method1(String first, int second) { 1687 } 1688 } 1689 """ 1690 ), 1691 java( 1692 """ 1693 package test.pkg; 1694 public class Parent { 1695 private Parent() { } 1696 public void method1(String first, int second) { 1697 } 1698 public void method2(String first, int second) { 1699 } 1700 public void method3(String first, int second) { 1701 } 1702 } 1703 """ 1704 ) 1705 ) 1706 ) 1707 } 1708 1709 @Test Partial text file which references inner classes not listed elsewherenull1710 fun `Partial text file which references inner classes not listed elsewhere`() { 1711 // This happens in system and test files where we only include APIs that differ 1712 // from the base API. When parsing these code bases we need to gracefully handle 1713 // references to inner classes. 1714 check( 1715 includeSystemApiAnnotations = true, 1716 expectedIssues = """ 1717 TESTROOT/released-api.txt:4: error: Removed method test.pkg.Bar.Inner1.Inner2.removedMethod() [RemovedMethod] 1718 """, 1719 sourceFiles = arrayOf( 1720 java( 1721 """ 1722 package other.pkg; 1723 1724 public class MyClass { 1725 public class MyInterface { 1726 public void test() { } 1727 } 1728 } 1729 """ 1730 ).indented(), 1731 java( 1732 """ 1733 package test.pkg; 1734 import android.annotation.SystemApi; 1735 1736 public class Bar { 1737 public class Inner1 { 1738 private Inner1() { } 1739 @SuppressWarnings("JavaDoc") 1740 public class Inner2 { 1741 private Inner2() { } 1742 1743 /** 1744 * @hide 1745 */ 1746 @SystemApi 1747 public void method() { } 1748 1749 /** 1750 * @hide 1751 */ 1752 @SystemApi 1753 public void addedMethod() { } 1754 } 1755 } 1756 } 1757 """ 1758 ), 1759 systemApiSource 1760 ), 1761 1762 extraArguments = arrayOf( 1763 ARG_SHOW_ANNOTATION, "android.annotation.SystemApi", 1764 ARG_HIDE_PACKAGE, "android.annotation", 1765 ), 1766 1767 checkCompatibilityApiReleased = 1768 """ 1769 package test.pkg { 1770 public class Bar.Inner1.Inner2 { 1771 method public void method(); 1772 method public void removedMethod(); 1773 } 1774 } 1775 """ 1776 ) 1777 } 1778 1779 @Test Incompatible Changes in Released System APInull1780 fun `Incompatible Changes in Released System API `() { 1781 // Incompatible changes to a released System API should be detected 1782 // In this case removing final and changing value of constant 1783 check( 1784 includeSystemApiAnnotations = true, 1785 expectedIssues = """ 1786 src/android/rolecontrollerservice/RoleControllerService.java:8: error: Method android.rolecontrollerservice.RoleControllerService.sendNetworkScore has removed 'final' qualifier [RemovedFinal] 1787 src/android/rolecontrollerservice/RoleControllerService.java:9: error: Field android.rolecontrollerservice.RoleControllerService.APP_RETURN_UNWANTED has changed value from 1 to 0 [ChangedValue] 1788 """, 1789 sourceFiles = arrayOf( 1790 java( 1791 """ 1792 package android.rolecontrollerservice; 1793 import android.annotation.SystemApi; 1794 1795 /** @hide */ 1796 @SystemApi 1797 public abstract class RoleControllerService { 1798 public abstract void onGrantDefaultRoles(); 1799 public void sendNetworkScore(); 1800 public static final int APP_RETURN_UNWANTED = 0; 1801 } 1802 """ 1803 ), 1804 systemApiSource 1805 ), 1806 1807 extraArguments = arrayOf( 1808 ARG_SHOW_ANNOTATION, "android.annotation.TestApi", 1809 ARG_HIDE_PACKAGE, "android.annotation", 1810 ), 1811 1812 checkCompatibilityApiReleased = 1813 """ 1814 package android.rolecontrollerservice { 1815 public abstract class RoleControllerService { 1816 ctor public RoleControllerService(); 1817 method public abstract void onGrantDefaultRoles(); 1818 method public final void sendNetworkScore(); 1819 field public static final int APP_RETURN_UNWANTED = 1; 1820 } 1821 } 1822 """ 1823 ) 1824 } 1825 1826 @Test Incompatible changes to released API signature codebasenull1827 fun `Incompatible changes to released API signature codebase`() { 1828 // Incompatible changes to a released System API should be detected 1829 // in case of partial files 1830 check( 1831 expectedIssues = """ 1832 TESTROOT/released-api.txt:5: error: Removed method test.pkg.Foo.method2() [RemovedMethod] 1833 """, 1834 signatureSource = """ 1835 // Signature format: 3.0 1836 package test.pkg { 1837 public final class Foo { 1838 ctor public Foo(); 1839 method public void method1(); 1840 } 1841 } 1842 """, 1843 1844 checkCompatibilityApiReleased = 1845 """ 1846 package test.pkg { 1847 public final class Foo { 1848 ctor public Foo(); 1849 method public void method1(); 1850 method public void method2(); 1851 method public void method3(); 1852 } 1853 } 1854 """, 1855 checkCompatibilityBaseApi = 1856 """ 1857 package test.pkg { 1858 public final class Foo { 1859 ctor public Foo(); 1860 method public void method3(); 1861 } 1862 } 1863 """, 1864 ) 1865 } 1866 1867 @Test Partial text file which adds methods to show-annotation APInull1868 fun `Partial text file which adds methods to show-annotation API`() { 1869 // This happens in system and test files where we only include APIs that differ 1870 // from the base IDE. When parsing these code bases we need to gracefully handle 1871 // references to inner classes. 1872 check( 1873 includeSystemApiAnnotations = true, 1874 expectedIssues = """ 1875 TESTROOT/released-api.txt:4: error: Removed method android.rolecontrollerservice.RoleControllerService.onClearRoleHolders() [RemovedMethod] 1876 """, 1877 sourceFiles = arrayOf( 1878 java( 1879 """ 1880 package android.rolecontrollerservice; 1881 1882 public class Service { 1883 } 1884 """ 1885 ).indented(), 1886 java( 1887 """ 1888 package android.rolecontrollerservice; 1889 import android.annotation.SystemApi; 1890 1891 /** @hide */ 1892 @SystemApi 1893 public abstract class RoleControllerService extends Service { 1894 public abstract void onGrantDefaultRoles(); 1895 } 1896 """ 1897 ), 1898 systemApiSource 1899 ), 1900 1901 extraArguments = arrayOf( 1902 ARG_SHOW_ANNOTATION, "android.annotation.TestApi", 1903 ARG_HIDE_PACKAGE, "android.annotation", 1904 ), 1905 1906 checkCompatibilityApiReleased = 1907 """ 1908 package android.rolecontrollerservice { 1909 public abstract class RoleControllerService extends android.rolecontrollerservice.Service { 1910 ctor public RoleControllerService(); 1911 method public abstract void onClearRoleHolders(); 1912 } 1913 } 1914 """ 1915 ) 1916 } 1917 1918 @Test Partial text file where type previously did not existnull1919 fun `Partial text file where type previously did not exist`() { 1920 check( 1921 expectedIssues = """ 1922 """, 1923 sourceFiles = arrayOf( 1924 java( 1925 """ 1926 package test.pkg; 1927 import android.annotation.SystemApi; 1928 1929 /** 1930 * @hide 1931 */ 1932 @SystemApi 1933 public class SampleException1 extends java.lang.Exception { 1934 } 1935 """ 1936 ).indented(), 1937 java( 1938 """ 1939 package test.pkg; 1940 import android.annotation.SystemApi; 1941 1942 /** 1943 * @hide 1944 */ 1945 @SystemApi 1946 public class SampleException2 extends java.lang.Throwable { 1947 } 1948 """ 1949 ).indented(), 1950 java( 1951 """ 1952 package test.pkg; 1953 import android.annotation.SystemApi; 1954 1955 /** 1956 * @hide 1957 */ 1958 @SystemApi 1959 public class Utils { 1960 public void method1() throws SampleException1 { } 1961 public void method2() throws SampleException2 { } 1962 } 1963 """ 1964 ), 1965 systemApiSource 1966 ), 1967 1968 extraArguments = arrayOf( 1969 ARG_SHOW_ANNOTATION, "android.annotation.SystemApi", 1970 ARG_HIDE_PACKAGE, "android.annotation", 1971 ), 1972 1973 checkCompatibilityApiReleased = 1974 """ 1975 package test.pkg { 1976 public class Utils { 1977 ctor public Utils(); 1978 // We don't define SampleException1 or SampleException in this file, 1979 // in this partial signature, so we don't need to validate that they 1980 // have not been changed 1981 method public void method1() throws test.pkg.SampleException1; 1982 method public void method2() throws test.pkg.SampleException2; 1983 } 1984 } 1985 """ 1986 ) 1987 } 1988 1989 @Test Regression test for bug 120847535null1990 fun `Regression test for bug 120847535`() { 1991 // Regression test for 1992 // 120847535: check-api doesn't fail on method that is in current.txt, but marked @hide @TestApi 1993 check( 1994 expectedIssues = """ 1995 TESTROOT/released-api.txt:6: error: Removed method test.view.ViewTreeObserver.registerFrameCommitCallback(Runnable) [RemovedMethod] 1996 """, 1997 sourceFiles = arrayOf( 1998 java( 1999 """ 2000 package test.view; 2001 import android.annotation.TestApi; 2002 public final class ViewTreeObserver { 2003 /** 2004 * @hide 2005 */ 2006 @TestApi 2007 public void registerFrameCommitCallback(Runnable callback) { 2008 } 2009 } 2010 """ 2011 ).indented(), 2012 java( 2013 """ 2014 package test.view; 2015 public final class View { 2016 private View() { } 2017 } 2018 """ 2019 ).indented(), 2020 testApiSource 2021 ), 2022 2023 api = """ 2024 package test.view { 2025 public final class View { 2026 } 2027 public final class ViewTreeObserver { 2028 ctor public ViewTreeObserver(); 2029 } 2030 } 2031 """, 2032 extraArguments = arrayOf( 2033 ARG_HIDE_PACKAGE, "android.annotation", 2034 ), 2035 2036 checkCompatibilityApiReleased = """ 2037 package test.view { 2038 public final class View { 2039 } 2040 public final class ViewTreeObserver { 2041 ctor public ViewTreeObserver(); 2042 method public void registerFrameCommitCallback(java.lang.Runnable); 2043 } 2044 } 2045 """ 2046 ) 2047 } 2048 2049 @Test Test release compatibility checkingnull2050 fun `Test release compatibility checking`() { 2051 // Different checks are enforced for current vs release API comparisons: 2052 // we don't flag AddedClasses etc. Removed classes *are* enforced. 2053 check( 2054 expectedIssues = """ 2055 src/test/pkg/Class1.java:3: error: Class test.pkg.Class1 added 'final' qualifier [AddedFinal] 2056 TESTROOT/released-api.txt:3: error: Removed constructor test.pkg.Class1() [RemovedMethod] 2057 src/test/pkg/MyClass.java:5: error: Method test.pkg.MyClass.myMethod2 has changed 'abstract' qualifier [ChangedAbstract] 2058 src/test/pkg/MyClass.java:6: error: Method test.pkg.MyClass.myMethod3 has changed 'static' qualifier [ChangedStatic] 2059 TESTROOT/released-api.txt:14: error: Removed class test.pkg.MyOldClass [RemovedClass] 2060 TESTROOT/released-api.txt:17: error: Removed package test.pkg3 [RemovedPackage] 2061 """, 2062 checkCompatibilityApiReleased = """ 2063 package test.pkg { 2064 public class Class1 { 2065 ctor public Class1(); 2066 } 2067 public class Class2 { 2068 } 2069 public final class Class3 { 2070 } 2071 public abstract class MyClass { 2072 method public void myMethod2(); 2073 method public void myMethod3(); 2074 method deprecated public void myMethod4(); 2075 } 2076 public abstract class MyOldClass { 2077 } 2078 } 2079 package test.pkg3 { 2080 public abstract class MyOldClass { 2081 } 2082 } 2083 """, 2084 sourceFiles = arrayOf( 2085 java( 2086 """ 2087 package test.pkg; 2088 2089 public final class Class1 { 2090 private Class1() {} 2091 } 2092 """ 2093 ), 2094 java( 2095 """ 2096 package test.pkg; 2097 2098 public final class Class2 { 2099 private Class2() {} 2100 } 2101 """ 2102 ), 2103 java( 2104 """ 2105 package test.pkg; 2106 2107 public class Class3 { 2108 private Class3() {} 2109 } 2110 """ 2111 ), 2112 java( 2113 """ 2114 package test.pkg; 2115 2116 public abstract class MyNewClass { 2117 private MyNewClass() {} 2118 } 2119 """ 2120 ), 2121 java( 2122 """ 2123 package test.pkg; 2124 2125 public abstract class MyClass { 2126 private MyClass() {} 2127 public native abstract void myMethod2(); // Note that Errors.CHANGE_NATIVE is hidden by default 2128 public static void myMethod3() {} 2129 public void myMethod4() {} 2130 } 2131 """ 2132 ) 2133 ) 2134 ) 2135 } 2136 2137 @Test Test remove deprecated API is an errornull2138 fun `Test remove deprecated API is an error`() { 2139 // Regression test for b/145745855 2140 check( 2141 expectedIssues = """ 2142 TESTROOT/released-api.txt:6: error: Removed deprecated class test.pkg.DeprecatedClass [RemovedDeprecatedClass] 2143 TESTROOT/released-api.txt:3: error: Removed deprecated constructor test.pkg.SomeClass() [RemovedDeprecatedMethod] 2144 TESTROOT/released-api.txt:4: error: Removed deprecated method test.pkg.SomeClass.deprecatedMethod() [RemovedDeprecatedMethod] 2145 """, 2146 checkCompatibilityApiReleased = """ 2147 package test.pkg { 2148 public class SomeClass { 2149 ctor deprecated public SomeClass(); 2150 method deprecated public void deprecatedMethod(); 2151 } 2152 deprecated public class DeprecatedClass { 2153 ctor deprecated public DeprecatedClass(); 2154 method deprecated public void deprecatedMethod(); 2155 } 2156 } 2157 """, 2158 sourceFiles = arrayOf( 2159 java( 2160 """ 2161 package test.pkg; 2162 2163 public class SomeClass { 2164 private SomeClass() {} 2165 } 2166 """ 2167 ) 2168 ) 2169 ) 2170 } 2171 2172 @Test Test check release with base apinull2173 fun `Test check release with base api`() { 2174 check( 2175 expectedIssues = "", 2176 checkCompatibilityApiReleased = """ 2177 package test.pkg { 2178 public class SomeClass { 2179 method public static void publicMethodA(); 2180 method public static void publicMethodB(); 2181 } 2182 } 2183 """, 2184 sourceFiles = arrayOf( 2185 java( 2186 """ 2187 package test.pkg; 2188 2189 public class SomeClass { 2190 public static void publicMethodA(); 2191 } 2192 """ 2193 ) 2194 ), 2195 checkCompatibilityBaseApi = """ 2196 package test.pkg { 2197 public class SomeClass { 2198 method public static void publicMethodB(); 2199 } 2200 } 2201 """ 2202 ) 2203 } 2204 2205 @Test Test check a class moving from the released api to the base apinull2206 fun `Test check a class moving from the released api to the base api`() { 2207 check( 2208 checkCompatibilityApiReleased = """ 2209 package test.pkg { 2210 public class SomeClass1 { 2211 method public void method1(); 2212 } 2213 public class SomeClass2 { 2214 method public void oldMethod(); 2215 } 2216 } 2217 """, 2218 checkCompatibilityBaseApi = """ 2219 package test.pkg { 2220 public class SomeClass2 { 2221 method public void newMethod(); 2222 } 2223 } 2224 """, 2225 sourceFiles = arrayOf( 2226 java( 2227 """ 2228 package test.pkg; 2229 2230 public class SomeClass1 { 2231 public void method1(); 2232 } 2233 """ 2234 ) 2235 ), 2236 expectedIssues = """ 2237 TESTROOT/released-api.txt:6: error: Removed method test.pkg.SomeClass2.oldMethod() [RemovedMethod] 2238 """.trimIndent() 2239 ) 2240 } 2241 2242 @Test Implicit nullnessnull2243 fun `Implicit nullness`() { 2244 check( 2245 inputKotlinStyleNulls = true, 2246 checkCompatibilityApiReleased = """ 2247 // Signature format: 2.0 2248 package androidx.annotation { 2249 @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 { 2250 method public abstract androidx.annotation.RestrictTo.Scope[] value(); 2251 } 2252 2253 public enum RestrictTo.Scope { 2254 enum_constant @Deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID; 2255 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY; 2256 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP; 2257 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP_PREFIX; 2258 enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES; 2259 enum_constant public static final androidx.annotation.RestrictTo.Scope TESTS; 2260 } 2261 } 2262 """, 2263 2264 sourceFiles = arrayOf( 2265 restrictToSource 2266 ) 2267 ) 2268 } 2269 2270 @Test Java String constantsnull2271 fun `Java String constants`() { 2272 check( 2273 inputKotlinStyleNulls = true, 2274 checkCompatibilityApiReleased = """ 2275 package androidx.browser.browseractions { 2276 public class BrowserActionsIntent { 2277 field public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID"; 2278 } 2279 } 2280 """, 2281 2282 sourceFiles = arrayOf( 2283 java( 2284 """ 2285 package androidx.browser.browseractions; 2286 public class BrowserActionsIntent { 2287 private BrowserActionsIntent() { } 2288 public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID"; 2289 2290 } 2291 """ 2292 ).indented() 2293 ) 2294 ) 2295 } 2296 2297 @Test Classes with mapsnull2298 fun `Classes with maps`() { 2299 check( 2300 inputKotlinStyleNulls = true, 2301 checkCompatibilityApiReleased = """ 2302 // Signature format: 2.0 2303 package androidx.collection { 2304 public class SimpleArrayMap<K, V> { 2305 } 2306 } 2307 """, 2308 2309 sourceFiles = arrayOf( 2310 java( 2311 """ 2312 package androidx.collection; 2313 2314 public class SimpleArrayMap<K, V> { 2315 private SimpleArrayMap() { } 2316 } 2317 """ 2318 ).indented() 2319 ) 2320 ) 2321 } 2322 2323 @Test Referencing type parameters in typesnull2324 fun `Referencing type parameters in types`() { 2325 check( 2326 inputKotlinStyleNulls = true, 2327 checkCompatibilityApiReleased = """ 2328 // Signature format: 2.0 2329 package androidx.collection { 2330 public class MyMap<Key, Value> { 2331 ctor public MyMap(); 2332 field public Key! myField; 2333 method public Key! getReplacement(Key!); 2334 } 2335 } 2336 """, 2337 2338 sourceFiles = arrayOf( 2339 java( 2340 """ 2341 package androidx.collection; 2342 2343 public class MyMap<Key, Value> { 2344 public Key getReplacement(Key key) { return null; } 2345 public Key myField = null; 2346 } 2347 """ 2348 ).indented() 2349 ) 2350 ) 2351 } 2352 2353 @Test Comparing annotations with methods with v1 signature filesnull2354 fun `Comparing annotations with methods with v1 signature files`() { 2355 check( 2356 checkCompatibilityApiReleased = """ 2357 package androidx.annotation { 2358 public abstract class RestrictTo implements java.lang.annotation.Annotation { 2359 } 2360 public static final class RestrictTo.Scope extends java.lang.Enum { 2361 enum_constant public static final deprecated androidx.annotation.RestrictTo.Scope GROUP_ID; 2362 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY; 2363 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP; 2364 enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP_PREFIX; 2365 enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES; 2366 enum_constant public static final androidx.annotation.RestrictTo.Scope TESTS; 2367 } 2368 } 2369 """, 2370 2371 sourceFiles = arrayOf( 2372 restrictToSource 2373 ) 2374 ) 2375 } 2376 2377 @Test Insignificant type formatting differencesnull2378 fun `Insignificant type formatting differences`() { 2379 check( 2380 checkCompatibilityApiReleased = """ 2381 package test.pkg { 2382 public final class UsageStatsManager { 2383 method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets(); 2384 method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>); 2385 field public java.util.Map<java.lang.String, java.lang.Integer> map; 2386 } 2387 } 2388 """, 2389 signatureSource = """ 2390 package test.pkg { 2391 public final class UsageStatsManager { 2392 method public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets(); 2393 method public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>); 2394 field public java.util.Map<java.lang.String,java.lang.Integer> map; 2395 } 2396 } 2397 """ 2398 ) 2399 } 2400 2401 @Test Compare signatures with Kotlin nullability from signaturenull2402 fun `Compare signatures with Kotlin nullability from signature`() { 2403 check( 2404 expectedIssues = """ 2405 TESTROOT/load-api.txt:5: error: Attempted to remove @NonNull annotation from parameter str in test.pkg.Foo.method1(int p, Integer int2, int p1, String str, java.lang.String... args) [InvalidNullConversion] 2406 TESTROOT/load-api.txt:7: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter str in test.pkg.Foo.method3(String str, int p, int int2) [InvalidNullConversion] 2407 """.trimIndent(), 2408 format = FileFormat.V3, 2409 checkCompatibilityApiReleased = """ 2410 // Signature format: 3.0 2411 package test.pkg { 2412 public final class Foo { 2413 ctor public Foo(); 2414 method public void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args); 2415 method public void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE); 2416 method public void method3(String? str, int p, int int2 = double(int) + str.length); 2417 field public static final test.pkg.Foo.Companion! Companion; 2418 } 2419 } 2420 """, 2421 signatureSource = """ 2422 // Signature format: 3.0 2423 package test.pkg { 2424 public final class Foo { 2425 ctor public Foo(); 2426 method public void method1(int p = 42, Integer? int2 = null, int p1 = 42, String! str = "hello world", java.lang.String... args); 2427 method public void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE); 2428 method public void method3(String str, int p, int int2 = double(int) + str.length); 2429 field public static final test.pkg.Foo.Companion! Companion; 2430 } 2431 } 2432 """ 2433 ) 2434 } 2435 2436 @Test Compare signatures with Kotlin nullability from sourcenull2437 fun `Compare signatures with Kotlin nullability from source`() { 2438 check( 2439 expectedIssues = """ 2440 src/test/pkg/test.kt:4: error: Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter str1 in test.pkg.TestKt.fun1(String str1, String str2, java.util.List<java.lang.String> list) [InvalidNullConversion] 2441 """.trimIndent(), 2442 format = FileFormat.V3, 2443 checkCompatibilityApiReleased = """ 2444 // Signature format: 3.0 2445 package test.pkg { 2446 public final class TestKt { 2447 method public static void fun1(String? str1, String str2, java.util.List<java.lang.String!> list); 2448 } 2449 } 2450 """, 2451 sourceFiles = arrayOf( 2452 kotlin( 2453 """ 2454 package test.pkg 2455 import java.util.List 2456 2457 fun fun1(str1: String, str2: String?, list: List<String?>) { } 2458 2459 """.trimIndent() 2460 ) 2461 ) 2462 ) 2463 } 2464 2465 @Test Adding and removing reifiednull2466 fun `Adding and removing reified`() { 2467 check( 2468 inputKotlinStyleNulls = true, 2469 expectedIssues = """ 2470 src/test/pkg/test.kt:5: error: Method test.pkg.TestKt.add made type variable T reified: incompatible change [AddedReified] 2471 src/test/pkg/test.kt:8: error: Method test.pkg.TestKt.two made type variable S reified: incompatible change [AddedReified] 2472 """, 2473 checkCompatibilityApiReleased = """ 2474 package test.pkg { 2475 public final class TestKt { 2476 method public static inline <T> void add(T! t); 2477 method public static inline <reified T> void remove(T! t); 2478 method public static inline <reified T> void unchanged(T! t); 2479 method public static inline <S, reified T> void two(S! s, T! t); 2480 } 2481 } 2482 """, 2483 2484 sourceFiles = arrayOf( 2485 kotlin( 2486 """ 2487 @file:Suppress("NOTHING_TO_INLINE", "RedundantVisibilityModifier", "unused") 2488 2489 package test.pkg 2490 2491 inline fun <reified T> add(t: T) { } 2492 inline fun <T> remove(t: T) { } 2493 inline fun <reified T> unchanged(t: T) { } 2494 inline fun <reified S, T> two(s: S, t: T) { } 2495 """ 2496 ).indented() 2497 ) 2498 ) 2499 } 2500 2501 @Test Empty prev api with @hide and --show-annotationnull2502 fun `Empty prev api with @hide and --show-annotation`() { 2503 check( 2504 checkCompatibilityApiReleased = """ 2505 """, 2506 sourceFiles = arrayOf( 2507 java( 2508 """ 2509 package android.media; 2510 2511 /** 2512 * @hide 2513 */ 2514 public class SubtitleController { 2515 public interface Listener { 2516 void onSubtitleTrackSelected() { } 2517 } 2518 } 2519 """ 2520 ), 2521 java( 2522 """ 2523 package android.media; 2524 import android.annotation.SystemApi; 2525 2526 /** 2527 * @hide 2528 */ 2529 @SystemApi 2530 @SuppressWarnings("HiddenSuperclass") 2531 public class MediaPlayer implements SubtitleController.Listener { 2532 } 2533 """ 2534 ), 2535 systemApiSource 2536 ), 2537 extraArguments = arrayOf( 2538 ARG_SHOW_ANNOTATION, "android.annotation.SystemApi", 2539 ARG_HIDE_PACKAGE, "android.annotation", 2540 ), 2541 expectedIssues = "" 2542 2543 ) 2544 } 2545 2546 @Test Inherited systemApi method in an inner classnull2547 fun `Inherited systemApi method in an inner class`() { 2548 check( 2549 checkCompatibilityApiReleased = """ 2550 package android.telephony { 2551 public class MmTelFeature.Capabilities { 2552 method public boolean isCapable(int); 2553 } 2554 } 2555 """, 2556 sourceFiles = arrayOf( 2557 java( 2558 """ 2559 package android.telephony; 2560 2561 /** 2562 * @hide 2563 */ 2564 @android.annotation.SystemApi 2565 public class MmTelFeature { 2566 public static class Capabilities extends ParentCapabilities { 2567 @Override 2568 boolean isCapable(int argument) { return true; } 2569 } 2570 } 2571 """ 2572 ), 2573 java( 2574 """ 2575 package android.telephony; 2576 2577 /** 2578 * @hide 2579 */ 2580 @android.annotation.SystemApi 2581 public class Parent { 2582 public static class ParentCapabilities { 2583 public boolean isCapable(int argument) { return false; } 2584 } 2585 } 2586 """ 2587 ), 2588 systemApiSource 2589 ), 2590 extraArguments = arrayOf( 2591 ARG_SHOW_ANNOTATION, "android.annotation.SystemApi", 2592 ARG_HIDE_PACKAGE, "android.annotation", 2593 ), 2594 expectedIssues = "" 2595 ) 2596 } 2597 2598 @Test Moving removed api back to public apinull2599 fun `Moving removed api back to public api`() { 2600 check( 2601 checkCompatibilityRemovedApiReleased = """ 2602 package android.content { 2603 public class ContextWrapper { 2604 method public void createContextForSplit(); 2605 } 2606 } 2607 """, 2608 sourceFiles = arrayOf( 2609 java( 2610 """ 2611 package android.content; 2612 2613 public class ContextWrapper extends Parent { 2614 /** @removed */ 2615 @Override 2616 public void getSharedPreferences() { } 2617 2618 /** @hide */ 2619 @Override 2620 public void createContextForSplit() { } 2621 } 2622 """ 2623 ), 2624 java( 2625 """ 2626 package android.content; 2627 2628 public abstract class Parent { 2629 /** @hide */ 2630 @Override 2631 public void getSharedPreferences() { } 2632 2633 public abstract void createContextForSplit() { } 2634 } 2635 """ 2636 ) 2637 ), 2638 expectedIssues = "" 2639 ) 2640 } 2641 2642 @Test Inherited nullability annotationsnull2643 fun `Inherited nullability annotations`() { 2644 check( 2645 checkCompatibilityApiReleased = """ 2646 package test.pkg { 2647 public final class SAXException extends test.pkg.Parent { 2648 } 2649 public final class Parent extends test.pkg.Grandparent { 2650 } 2651 public final class Grandparent { 2652 method @Nullable public String getMessage(); 2653 } 2654 } 2655 """, 2656 sourceFiles = arrayOf( 2657 java( 2658 """ 2659 package test.pkg; 2660 2661 public final class SAXException extends Parent { 2662 @Override public String getMessage() { 2663 return "sample"; 2664 } 2665 } 2666 """ 2667 ), 2668 java( 2669 """ 2670 package test.pkg; 2671 2672 public final class Parent extends Grandparent { 2673 } 2674 """ 2675 ), 2676 java( 2677 """ 2678 package test.pkg; 2679 2680 public final class Grandparent { 2681 public String getMessage() { 2682 return "sample"; 2683 } 2684 } 2685 """ 2686 ) 2687 ), 2688 mergeJavaStubAnnotations = """ 2689 package test.pkg; 2690 2691 public class Grandparent implements java.io.Serializable { 2692 @libcore.util.Nullable public test.pkg.String getMessage() { throw new RuntimeException("Stub!"); } 2693 } 2694 """, 2695 expectedIssues = """ 2696 """ 2697 ) 2698 } 2699 2700 @Test Inherited @removed fieldsnull2701 fun `Inherited @removed fields`() { 2702 check( 2703 checkCompatibilityRemovedApiReleased = """ 2704 package android.provider { 2705 2706 public static final class StreamItems implements android.provider.BaseColumns { 2707 field public static final String _COUNT = "_count"; 2708 field public static final String _ID = "_id"; 2709 } 2710 } 2711 """, 2712 sourceFiles = arrayOf( 2713 java( 2714 """ 2715 package android.provider; 2716 2717 /** 2718 * @removed 2719 */ 2720 public static final class StreamItems implements BaseColumns { 2721 } 2722 """ 2723 ), 2724 java( 2725 """ 2726 package android.provider; 2727 2728 public interface BaseColumns { 2729 public static final String _ID = "_id"; 2730 public static final String _COUNT = "_count"; 2731 } 2732 """ 2733 ) 2734 ), 2735 expectedIssues = """ 2736 """ 2737 ) 2738 } 2739 2740 @Test Inherited deprecated protected @removed methodnull2741 fun `Inherited deprecated protected @removed method`() { 2742 check( 2743 checkCompatibilityApiReleased = """ 2744 package android.icu.util { 2745 public class SpecificCalendar { 2746 method @Deprecated protected void validateField(); 2747 } 2748 } 2749 """, 2750 sourceFiles = arrayOf( 2751 java( 2752 """ 2753 package android.icu.util; 2754 import java.text.Format; 2755 2756 public class SpecificCalendar extends Calendar { 2757 /** 2758 * @deprecated for this test 2759 * @hide 2760 */ 2761 @Override 2762 @Deprecated 2763 protected void validateField() { 2764 } 2765 } 2766 """ 2767 ), 2768 java( 2769 """ 2770 package android.icu.util; 2771 2772 public class Calendar { 2773 protected void validateField() { 2774 } 2775 } 2776 """ 2777 ) 2778 ), 2779 expectedIssues = """ 2780 """ 2781 ) 2782 } 2783 2784 @Test Move class from SystemApi to public and then remove a methodnull2785 fun `Move class from SystemApi to public and then remove a method`() { 2786 check( 2787 checkCompatibilityApiReleased = """ 2788 package android.hardware.lights { 2789 public static final class LightsRequest.Builder { 2790 ctor public LightsRequest.Builder(); 2791 method public void clearLight(); 2792 method public void setLight(); 2793 } 2794 2795 public final class LightsManager { 2796 } 2797 } 2798 """, 2799 sourceFiles = arrayOf( 2800 java( 2801 """ 2802 package android.hardware.lights; 2803 2804 import android.annotation.SystemApi; 2805 2806 public class LightsRequest { 2807 public static class Builder { 2808 void clearLight() { } 2809 } 2810 } 2811 """ 2812 ), 2813 java( 2814 """ 2815 package android.hardware.lights; 2816 2817 import android.annotation.SystemApi; 2818 2819 /** 2820 * @hide 2821 */ 2822 @SystemApi 2823 public class LightsManager { 2824 } 2825 """ 2826 ), 2827 systemApiSource 2828 ), 2829 extraArguments = arrayOf( 2830 ARG_SHOW_ANNOTATION, "android.annotation.SystemApi", 2831 ARG_HIDE_PACKAGE, "android.annotation", 2832 ), 2833 2834 expectedIssues = """ 2835 TESTROOT/released-api.txt:5: error: Removed method android.hardware.lights.LightsRequest.Builder.setLight() [RemovedMethod] 2836 """ 2837 ) 2838 } 2839 2840 @Test Moving a field from SystemApi to publicnull2841 fun `Moving a field from SystemApi to public`() { 2842 check( 2843 checkCompatibilityApiReleased = """ 2844 package android.content { 2845 public class Context { 2846 field public static final String BUGREPORT_SERVICE = "bugreport"; 2847 method public File getPreloadsFileCache(); 2848 } 2849 } 2850 """, 2851 sourceFiles = arrayOf( 2852 java( 2853 """ 2854 package android.content; 2855 2856 import android.annotation.SystemApi; 2857 2858 public class Context { 2859 public static final String BUGREPORT_SERVICE = "bugreport"; 2860 2861 /** 2862 * @hide 2863 */ 2864 @SystemApi 2865 public File getPreloadsFileCache() { return null; } 2866 } 2867 """ 2868 ), 2869 systemApiSource 2870 ), 2871 extraArguments = arrayOf( 2872 ARG_SHOW_ANNOTATION, "android.annotation.SystemApi", 2873 ARG_HIDE_PACKAGE, "android.annotation", 2874 ), 2875 2876 expectedIssues = """ 2877 """ 2878 ) 2879 } 2880 2881 @Test Compare interfaces when Object is redefinednull2882 fun `Compare interfaces when Object is redefined`() { 2883 check( 2884 checkCompatibilityApiReleased = """ 2885 package java.lang { 2886 public class Object { 2887 method public final void wait(); 2888 } 2889 } 2890 package test.pkg { 2891 public interface SomeInterface { 2892 } 2893 } 2894 """, 2895 sourceFiles = arrayOf( 2896 java( 2897 """ 2898 package test.pkg; 2899 2900 public interface SomeInterface { 2901 } 2902 """ 2903 ) 2904 ), 2905 // it's not quite right to say that java.lang was removed, but it's better than also 2906 // saying that SomeInterface no longer implements wait() 2907 expectedIssues = """ 2908 TESTROOT/released-api.txt:1: error: Removed package java.lang [RemovedPackage] 2909 """ 2910 ) 2911 } 2912 2913 @Test Overriding method without redeclaring nullabilitynull2914 fun `Overriding method without redeclaring nullability`() { 2915 check( 2916 checkCompatibilityApiReleased = """ 2917 package test.pkg { 2918 public class Child extends test.pkg.Parent { 2919 } 2920 public class Parent { 2921 method public void sample(@Nullable String); 2922 } 2923 } 2924 """, 2925 sourceFiles = arrayOf( 2926 java( 2927 """ 2928 package test.pkg; 2929 2930 public class Child extends Parent { 2931 public void sample(String arg) { 2932 } 2933 } 2934 """ 2935 ), 2936 java( 2937 """ 2938 package test.pkg; 2939 2940 public class Parent { 2941 public void sample(@Nullable String arg) { 2942 } 2943 } 2944 """ 2945 ) 2946 ), 2947 // The correct behavior would be for this test to fail, because of the removal of 2948 // nullability annotations on the child class. However, when we generate signature files, 2949 // we omit methods having the same signature as super methods, so if we were to generate 2950 // a signature file for this source, we would generate the given signature file. So, 2951 // we temporarily allow (and expect) this to pass without errors 2952 // expectedIssues = "src/test/pkg/Child.java:4: error: Attempted to remove @Nullable annotation from parameter arg in test.pkg.Child.sample(String arg) [InvalidNullConversion]" 2953 expectedIssues = "" 2954 ) 2955 } 2956 2957 @Test Final class inherits a methodnull2958 fun `Final class inherits a method`() { 2959 check( 2960 checkCompatibilityApiReleased = """ 2961 package java.security { 2962 public abstract class BasicPermission extends java.security.Permission { 2963 method public boolean implies(java.security.Permission); 2964 } 2965 public abstract class Permission { 2966 method public abstract boolean implies(java.security.Permission); 2967 } 2968 } 2969 package javax.security.auth { 2970 public final class AuthPermission extends java.security.BasicPermission { 2971 } 2972 } 2973 """, 2974 sourceFiles = arrayOf( 2975 java( 2976 """ 2977 package javax.security.auth; 2978 2979 public final class AuthPermission extends java.security.BasicPermission { 2980 } 2981 """ 2982 ), 2983 java( 2984 """ 2985 package java.security; 2986 2987 public abstract class BasicPermission extends Permission { 2988 public boolean implies(Permission p) { 2989 return true; 2990 } 2991 } 2992 """ 2993 ), 2994 java( 2995 """ 2996 package java.security; 2997 public abstract class Permission { 2998 public abstract boolean implies(Permission permission); 2999 } 3000 } 3001 """ 3002 ) 3003 ), 3004 expectedIssues = "" 3005 ) 3006 } 3007 3008 @Test Implementing undefined interfacenull3009 fun `Implementing undefined interface`() { 3010 check( 3011 checkCompatibilityApiReleased = """ 3012 package org.apache.http.conn.scheme { 3013 @Deprecated public final class PlainSocketFactory implements org.apache.http.conn.scheme.SocketFactory { 3014 } 3015 } 3016 """, 3017 sourceFiles = arrayOf( 3018 java( 3019 """ 3020 package org.apache.http.conn.scheme; 3021 3022 /** @deprecated */ 3023 @Deprecated 3024 public final class PlainSocketFactory implements SocketFactory { 3025 } 3026 """ 3027 ) 3028 ), 3029 expectedIssues = "" 3030 ) 3031 } 3032 3033 @Test Inherited abstract methodnull3034 fun `Inherited abstract method`() { 3035 check( 3036 checkCompatibilityApiReleased = """ 3037 package test.pkg { 3038 public class MeasureFormat { 3039 method public test.pkg.MeasureFormat parse(); 3040 } 3041 } 3042 """, 3043 sourceFiles = arrayOf( 3044 java( 3045 """ 3046 package test.pkg; 3047 3048 public class MeasureFormat extends UFormat { 3049 private MeasureFormat() { } 3050 /** @hide */ 3051 public MeasureFormat parse(); 3052 } 3053 """ 3054 ), 3055 java( 3056 """ 3057 package test.pkg; 3058 import android.annotation.SystemApi; 3059 3060 public abstract class UFormat { 3061 public abstract UFormat parse() { 3062 } 3063 } 3064 """ 3065 ), 3066 systemApiSource 3067 ), 3068 expectedIssues = "" 3069 ) 3070 } 3071 3072 @Ignore("Not currently working: we're getting the wrong PSI results; I suspect caching across the two codebases") 3073 @Test Test All Android API levelsnull3074 fun `Test All Android API levels`() { 3075 // Checks API across Android SDK versions and makes sure the results are 3076 // intentional (to help shake out bugs in the API compatibility checker) 3077 3078 // Expected migration warnings (the map value) when migrating to the target key level from the previous level 3079 val expected = mapOf( 3080 5 to "warning: Method android.view.Surface.lockCanvas added thrown exception java.lang.IllegalArgumentException [ChangedThrows]", 3081 6 to """ 3082 warning: Method android.accounts.AbstractAccountAuthenticator.confirmCredentials added thrown exception android.accounts.NetworkErrorException [ChangedThrows] 3083 warning: Method android.accounts.AbstractAccountAuthenticator.updateCredentials added thrown exception android.accounts.NetworkErrorException [ChangedThrows] 3084 warning: Field android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL has changed value from 2008 to 2014 [ChangedValue] 3085 """, 3086 7 to """ 3087 error: Removed field android.view.ViewGroup.FLAG_USE_CHILD_DRAWING_ORDER [RemovedField] 3088 """, 3089 3090 // setOption getting removed here is wrong! Seems to be a PSI loading bug. 3091 8 to """ 3092 warning: Constructor android.net.SSLCertificateSocketFactory no longer throws exception java.security.KeyManagementException [ChangedThrows] 3093 warning: Constructor android.net.SSLCertificateSocketFactory no longer throws exception java.security.NoSuchAlgorithmException [ChangedThrows] 3094 error: Removed method java.net.DatagramSocketImpl.getOption(int) [RemovedMethod] 3095 error: Removed method java.net.DatagramSocketImpl.setOption(int,Object) [RemovedMethod] 3096 warning: Constructor java.nio.charset.Charset no longer throws exception java.nio.charset.IllegalCharsetNameException [ChangedThrows] 3097 warning: Method java.nio.charset.Charset.forName no longer throws exception java.nio.charset.IllegalCharsetNameException [ChangedThrows] 3098 warning: Method java.nio.charset.Charset.forName no longer throws exception java.nio.charset.UnsupportedCharsetException [ChangedThrows] 3099 warning: Method java.nio.charset.Charset.isSupported no longer throws exception java.nio.charset.IllegalCharsetNameException [ChangedThrows] 3100 warning: Method java.util.regex.Matcher.appendReplacement no longer throws exception java.lang.IllegalStateException [ChangedThrows] 3101 warning: Method java.util.regex.Matcher.start no longer throws exception java.lang.IllegalStateException [ChangedThrows] 3102 warning: Method java.util.regex.Pattern.compile no longer throws exception java.util.regex.PatternSyntaxException [ChangedThrows] 3103 warning: Class javax.xml.XMLConstants added final qualifier [AddedFinal] 3104 error: Removed constructor javax.xml.XMLConstants() [RemovedMethod] 3105 warning: Method javax.xml.parsers.DocumentBuilder.isXIncludeAware no longer throws exception java.lang.UnsupportedOperationException [ChangedThrows] 3106 warning: Method javax.xml.parsers.DocumentBuilderFactory.newInstance no longer throws exception javax.xml.parsers.FactoryConfigurationError [ChangedThrows] 3107 warning: Method javax.xml.parsers.SAXParser.isXIncludeAware no longer throws exception java.lang.UnsupportedOperationException [ChangedThrows] 3108 warning: Method javax.xml.parsers.SAXParserFactory.newInstance no longer throws exception javax.xml.parsers.FactoryConfigurationError [ChangedThrows] 3109 warning: Method org.w3c.dom.Element.getAttributeNS added thrown exception org.w3c.dom.DOMException [ChangedThrows] 3110 warning: Method org.w3c.dom.Element.getAttributeNodeNS added thrown exception org.w3c.dom.DOMException [ChangedThrows] 3111 warning: Method org.w3c.dom.Element.getElementsByTagNameNS added thrown exception org.w3c.dom.DOMException [ChangedThrows] 3112 warning: Method org.w3c.dom.Element.hasAttributeNS added thrown exception org.w3c.dom.DOMException [ChangedThrows] 3113 warning: Method org.w3c.dom.NamedNodeMap.getNamedItemNS added thrown exception org.w3c.dom.DOMException [ChangedThrows] 3114 """, 3115 3116 18 to """ 3117 warning: Class android.os.Looper added final qualifier but was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 3118 warning: Class android.os.MessageQueue added final qualifier but was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable] 3119 error: Removed field android.os.Process.BLUETOOTH_GID [RemovedField] 3120 error: Removed class android.renderscript.Program [RemovedClass] 3121 error: Removed class android.renderscript.ProgramStore [RemovedClass] 3122 """, 3123 19 to """ 3124 warning: Method android.app.Notification.Style.build has changed 'abstract' qualifier [ChangedAbstract] 3125 error: Removed method android.os.Debug.MemoryInfo.getOtherLabel(int) [RemovedMethod] 3126 error: Removed method android.os.Debug.MemoryInfo.getOtherPrivateDirty(int) [RemovedMethod] 3127 error: Removed method android.os.Debug.MemoryInfo.getOtherPss(int) [RemovedMethod] 3128 error: Removed method android.os.Debug.MemoryInfo.getOtherSharedDirty(int) [RemovedMethod] 3129 warning: Field android.view.animation.Transformation.TYPE_ALPHA has changed value from nothing/not constant to 1 [ChangedValue] 3130 warning: Field android.view.animation.Transformation.TYPE_ALPHA has added 'final' qualifier [AddedFinal] 3131 warning: Field android.view.animation.Transformation.TYPE_BOTH has changed value from nothing/not constant to 3 [ChangedValue] 3132 warning: Field android.view.animation.Transformation.TYPE_BOTH has added 'final' qualifier [AddedFinal] 3133 warning: Field android.view.animation.Transformation.TYPE_IDENTITY has changed value from nothing/not constant to 0 [ChangedValue] 3134 warning: Field android.view.animation.Transformation.TYPE_IDENTITY has added 'final' qualifier [AddedFinal] 3135 warning: Field android.view.animation.Transformation.TYPE_MATRIX has changed value from nothing/not constant to 2 [ChangedValue] 3136 warning: Field android.view.animation.Transformation.TYPE_MATRIX has added 'final' qualifier [AddedFinal] 3137 warning: Method java.nio.CharBuffer.subSequence has changed return type from CharSequence to java.nio.CharBuffer [ChangedType] 3138 """, // The last warning above is not right; seems to be a PSI jar loading bug. It returns the wrong return type! 3139 3140 20 to """ 3141 error: Removed method android.util.TypedValue.complexToDimensionNoisy(int,android.util.DisplayMetrics) [RemovedMethod] 3142 warning: Method org.json.JSONObject.keys has changed return type from java.util.Iterator to java.util.Iterator<java.lang.String> [ChangedType] 3143 warning: Field org.xmlpull.v1.XmlPullParserFactory.features has changed type from java.util.HashMap to java.util.HashMap<java.lang.String, java.lang.Boolean> [ChangedType] 3144 """, 3145 26 to """ 3146 warning: Field android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE has changed value from 130 to 230 [ChangedValue] 3147 warning: Field android.content.pm.PermissionInfo.PROTECTION_MASK_FLAGS has changed value from 4080 to 65520 [ChangedValue] 3148 """, 3149 27 to "" 3150 ) 3151 3152 val suppressLevels = mapOf( 3153 1 to "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,ChangedDeprecated", 3154 7 to "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,ChangedDeprecated", 3155 18 to "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,RemovedMethod,ChangedDeprecated,ChangedThrows,AddedFinal,ChangedType,RemovedDeprecatedClass", 3156 26 to "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,RemovedMethod,ChangedDeprecated,ChangedThrows,AddedFinal,RemovedClass,RemovedDeprecatedClass", 3157 27 to "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,RemovedMethod,ChangedDeprecated,ChangedThrows,AddedFinal" 3158 ) 3159 3160 val loadPrevAsSignature = false 3161 3162 for (apiLevel in 5..27) { 3163 if (!expected.containsKey(apiLevel)) { 3164 continue 3165 } 3166 println("Checking compatibility from API level ${apiLevel - 1} to $apiLevel...") 3167 val current = getAndroidJar(apiLevel) 3168 val previous = getAndroidJar(apiLevel - 1) 3169 val previousApi = previous.path 3170 3171 // PSI based check 3172 3173 check( 3174 extraArguments = arrayOf( 3175 "--omit-locations", 3176 ARG_HIDE, 3177 suppressLevels[apiLevel] 3178 ?: "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,ChangedDeprecated,RemovedField,RemovedClass,RemovedDeprecatedClass" + 3179 (if ((apiLevel == 19 || apiLevel == 20) && loadPrevAsSignature) ",ChangedType" else "") 3180 3181 ), 3182 expectedIssues = expected[apiLevel]?.trimIndent() ?: "", 3183 checkCompatibilityApiReleased = previousApi, 3184 apiJar = current 3185 ) 3186 3187 // Signature based check 3188 if (apiLevel >= 21) { 3189 // Check signature file checks. We have .txt files for API level 14 and up, but there are a 3190 // BUNCH of problems in older signature files that make the comparisons not work -- 3191 // missing type variables in class declarations, missing generics in method signatures, etc. 3192 val signatureFile = File("../../prebuilts/sdk/${apiLevel - 1}/public/api/android.txt") 3193 if (!(signatureFile.isFile)) { 3194 println("Couldn't find $signatureFile: Check that pwd for test is correct. Skipping this test.") 3195 return 3196 } 3197 val previousSignatureApi = signatureFile.readText(UTF_8) 3198 3199 check( 3200 extraArguments = arrayOf( 3201 "--omit-locations", 3202 ARG_HIDE, 3203 suppressLevels[apiLevel] 3204 ?: "AddedPackage,AddedClass,AddedMethod,AddedInterface,AddedField,ChangedDeprecated,RemovedField,RemovedClass,RemovedDeprecatedClass" 3205 ), 3206 expectedIssues = expected[apiLevel]?.trimIndent() ?: "", 3207 checkCompatibilityApiReleased = previousSignatureApi, 3208 apiJar = current 3209 ) 3210 } 3211 } 3212 } 3213 3214 @Test Ignore hidden referencesnull3215 fun `Ignore hidden references`() { 3216 check( 3217 expectedIssues = """ 3218 """, 3219 checkCompatibilityApiReleased = """ 3220 package test.pkg { 3221 public class MyClass { 3222 ctor public MyClass(); 3223 method public void method1(test.pkg.Hidden); 3224 } 3225 } 3226 """, 3227 sourceFiles = arrayOf( 3228 java( 3229 """ 3230 package test.pkg; 3231 3232 public class MyClass { 3233 public void method1(Hidden hidden) { } 3234 } 3235 """ 3236 ), 3237 java( 3238 """ 3239 package test.pkg; 3240 /** @hide */ 3241 public class Hidden { 3242 } 3243 """ 3244 ) 3245 ), 3246 extraArguments = arrayOf( 3247 ARG_HIDE, "ReferencesHidden", 3248 ARG_HIDE, "UnavailableSymbol", 3249 ARG_HIDE, "HiddenTypeParameter" 3250 ) 3251 ) 3252 } 3253 3254 @Test Empty bundle filesnull3255 fun `Empty bundle files`() { 3256 // Regression test for 124333557 3257 // Makes sure we properly handle conflicting definitions of a java file in separate source roots 3258 check( 3259 expectedIssues = "", 3260 checkCompatibilityApiReleased = """ 3261 // Signature format: 3.0 3262 package com.android.location.provider { 3263 public class LocationProviderBase1 { 3264 ctor public LocationProviderBase1(); 3265 method public void onGetStatus(android.os.Bundle!); 3266 } 3267 public class LocationProviderBase2 { 3268 ctor public LocationProviderBase2(); 3269 method public void onGetStatus(android.os.Bundle!); 3270 } 3271 } 3272 """, 3273 sourceFiles = arrayOf( 3274 java( 3275 "src2/com/android/location/provider/LocationProviderBase1.java", 3276 """ 3277 /** Something */ 3278 package com.android.location.provider; 3279 """ 3280 ), 3281 java( 3282 "src/com/android/location/provider/LocationProviderBase1.java", 3283 """ 3284 package com.android.location.provider; 3285 import android.os.Bundle; 3286 3287 public class LocationProviderBase1 { 3288 public void onGetStatus(Bundle bundle) { } 3289 } 3290 """ 3291 ), 3292 // Try both combinations (empty java file both first on the source path 3293 // and second on the source path) 3294 java( 3295 "src/com/android/location/provider/LocationProviderBase2.java", 3296 """ 3297 /** Something */ 3298 package com.android.location.provider; 3299 """ 3300 ), 3301 java( 3302 "src/com/android/location/provider/LocationProviderBase2.java", 3303 """ 3304 package com.android.location.provider; 3305 import android.os.Bundle; 3306 3307 public class LocationProviderBase2 { 3308 public void onGetStatus(Bundle bundle) { } 3309 } 3310 """ 3311 ) 3312 ) 3313 ) 3314 } 3315 3316 @Test Check parameterized return type nullabilitynull3317 fun `Check parameterized return type nullability`() { 3318 // Regression test for 130567941 3319 check( 3320 expectedIssues = "", 3321 checkCompatibilityApiReleased = """ 3322 // Signature format: 3.0 3323 package androidx.coordinatorlayout.widget { 3324 public class CoordinatorLayout { 3325 ctor public CoordinatorLayout(); 3326 method public java.util.List<android.view.View!> getDependencies(); 3327 } 3328 } 3329 """, 3330 sourceFiles = arrayOf( 3331 java( 3332 """ 3333 package androidx.coordinatorlayout.widget; 3334 3335 import java.util.List; 3336 import androidx.annotation.NonNull; 3337 import android.view.View; 3338 3339 public class CoordinatorLayout { 3340 @NonNull 3341 public List<View> getDependencies() { 3342 throw Exception("Not implemented"); 3343 } 3344 } 3345 """ 3346 ), 3347 androidxNonNullSource 3348 ), 3349 extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation") 3350 ) 3351 } 3352 3353 @Test Check return type changing packagenull3354 fun `Check return type changing package`() { 3355 // Regression test for 130567941 3356 check( 3357 expectedIssues = """ 3358 TESTROOT/load-api.txt:7: error: Method test.pkg.sample.SampleClass.convert1 has changed return type from Number to java.lang.Number [ChangedType] 3359 """, 3360 inputKotlinStyleNulls = true, 3361 outputKotlinStyleNulls = true, 3362 checkCompatibilityApiReleased = """ 3363 // Signature format: 3.0 3364 package test.pkg.sample { 3365 public abstract class SampleClass { 3366 method public <Number> Number! convert(Number); 3367 method public <Number> Number! convert1(Number); 3368 } 3369 } 3370 """, 3371 signatureSource = """ 3372 // Signature format: 3.0 3373 package test.pkg.sample { 3374 public abstract class SampleClass { 3375 // Here the generic type parameter applies to both the function argument and the function return type 3376 method public <Number> Number! convert(Number); 3377 // Here the generic type parameter applies to the function argument but not the function return type 3378 method public <Number> java.lang.Number! convert1(Number); 3379 } 3380 } 3381 """ 3382 ) 3383 } 3384 3385 @Test Check generic type argument when showUnannotated is explicitly enablednull3386 fun `Check generic type argument when showUnannotated is explicitly enabled`() { 3387 // Regression test for 130567941 3388 check( 3389 expectedIssues = """ 3390 """, 3391 inputKotlinStyleNulls = true, 3392 outputKotlinStyleNulls = true, 3393 checkCompatibilityApiReleased = """ 3394 // Signature format: 3.0 3395 package androidx.versionedparcelable { 3396 public abstract class VersionedParcel { 3397 method public <T> T![]! readArray(); 3398 } 3399 } 3400 """, 3401 sourceFiles = arrayOf( 3402 java( 3403 """ 3404 package androidx.versionedparcelable; 3405 3406 public abstract class VersionedParcel { 3407 private VersionedParcel() { } 3408 3409 public <T> T[] readArray() { return null; } 3410 } 3411 """ 3412 ) 3413 ), 3414 extraArguments = arrayOf(ARG_SHOW_UNANNOTATED, ARG_SHOW_ANNOTATION, "androidx.annotation.RestrictTo") 3415 ) 3416 } 3417 3418 @Test Check using parameterized arrays as type parametersnull3419 fun `Check using parameterized arrays as type parameters`() { 3420 check( 3421 format = FileFormat.V3, 3422 sourceFiles = arrayOf( 3423 java( 3424 """ 3425 package test.pkg; 3426 import java.util.ArrayList; 3427 import java.lang.Exception; 3428 3429 public class SampleArray<D extends ArrayList> extends ArrayList<D[]> { 3430 public D[] get(int index) { 3431 throw Exception("Not implemented"); 3432 } 3433 } 3434 """ 3435 ) 3436 ), 3437 3438 checkCompatibilityApiReleased = """ 3439 // Signature format: 3.0 3440 package test.pkg { 3441 public class SampleArray<D extends java.util.ArrayList> extends java.util.ArrayList<D[]> { 3442 ctor public SampleArray(); 3443 method public D![]! get(int); 3444 } 3445 } 3446 """ 3447 ) 3448 } 3449 3450 @Test New default method on annotationnull3451 fun `New default method on annotation`() { 3452 // Regression test for 134754815 3453 check( 3454 expectedIssues = """ 3455 src/androidx/room/Relation.java:5: error: Added method androidx.room.Relation.IHaveNoDefault() [AddedAbstractMethod] 3456 """, 3457 inputKotlinStyleNulls = true, 3458 outputKotlinStyleNulls = true, 3459 checkCompatibilityApiReleased = """ 3460 // Signature format: 3.0 3461 package androidx.room { 3462 public @interface Relation { 3463 } 3464 } 3465 """, 3466 sourceFiles = arrayOf( 3467 java( 3468 """ 3469 package androidx.room; 3470 3471 public @interface Relation { 3472 String IHaveADefault() default ""; 3473 String IHaveNoDefault(); 3474 } 3475 """ 3476 ) 3477 ) 3478 ) 3479 } 3480 3481 @Test Changing static qualifier on inner classes with no public constructorsnull3482 fun `Changing static qualifier on inner classes with no public constructors`() { 3483 check( 3484 expectedIssues = """ 3485 TESTROOT/load-api.txt:11: error: Class test.pkg.ParentClass.AnotherBadInnerClass changed 'static' qualifier [ChangedStatic] 3486 TESTROOT/load-api.txt:8: error: Class test.pkg.ParentClass.BadInnerClass changed 'static' qualifier [ChangedStatic] 3487 """, 3488 checkCompatibilityApiReleased = """ 3489 package test.pkg { 3490 public class ParentClass { 3491 } 3492 public static class ParentClass.OkInnerClass { 3493 } 3494 public class ParentClass.AnotherOkInnerClass { 3495 } 3496 public static class ParentClass.BadInnerClass { 3497 ctor public BadInnerClass(); 3498 } 3499 public class ParentClass.AnotherBadInnerClass { 3500 ctor public AnotherBadInnerClass(); 3501 } 3502 } 3503 """, 3504 signatureSource = """ 3505 package test.pkg { 3506 public class ParentClass { 3507 } 3508 public class ParentClass.OkInnerClass { 3509 } 3510 public static class ParentClass.AnotherOkInnerClass { 3511 } 3512 public class ParentClass.BadInnerClass { 3513 ctor public BadInnerClass(); 3514 } 3515 public static class ParentClass.AnotherBadInnerClass { 3516 ctor public AnotherBadInnerClass(); 3517 } 3518 } 3519 """ 3520 ) 3521 } 3522 3523 @Test Remove fun modifier from interfacenull3524 fun `Remove fun modifier from interface`() { 3525 check( 3526 expectedIssues = """ 3527 src/test/pkg/FunctionalInterface.kt:3: error: Cannot remove 'fun' modifier from class test.pkg.FunctionalInterface: source incompatible change [FunRemoval] 3528 """, 3529 format = FileFormat.V4, 3530 checkCompatibilityApiReleased = """ 3531 // Signature format: 4.0 3532 package test.pkg { 3533 public fun interface FunctionalInterface { 3534 method public boolean methodOne(int number); 3535 } 3536 } 3537 """, 3538 sourceFiles = arrayOf( 3539 kotlin( 3540 """ 3541 package test.pkg 3542 3543 interface FunctionalInterface { 3544 fun methodOne(number: Int): Boolean 3545 } 3546 """ 3547 ) 3548 ) 3549 ) 3550 } 3551 3552 @Test Remove fun modifier from interface signature filesnull3553 fun `Remove fun modifier from interface signature files`() { 3554 check( 3555 expectedIssues = """ 3556 TESTROOT/load-api.txt:3: error: Cannot remove 'fun' modifier from class test.pkg.FunctionalInterface: source incompatible change [FunRemoval] 3557 """, 3558 format = FileFormat.V4, 3559 checkCompatibilityApiReleased = """ 3560 // Signature format: 4.0 3561 package test.pkg { 3562 public fun interface FunctionalInterface { 3563 method public boolean methodOne(int number); 3564 } 3565 } 3566 """, 3567 signatureSource = """ 3568 // Signature format: 4.0 3569 package test.pkg { 3570 public interface FunctionalInterface { 3571 method public boolean methodOne(int number); 3572 } 3573 } 3574 """.trimIndent() 3575 ) 3576 } 3577 3578 @Test Adding default value to annotation parameternull3579 fun `Adding default value to annotation parameter`() { 3580 check( 3581 expectedIssues = "", 3582 format = FileFormat.V4, 3583 checkCompatibilityApiReleased = """ 3584 // Signature format: 4.0 3585 package androidx.annotation.experimental { 3586 public @interface UseExperimental { 3587 method public abstract Class<?> markerClass(); 3588 } 3589 } 3590 """, 3591 sourceFiles = arrayOf( 3592 java( 3593 """ 3594 package androidx.annotation.experimental; 3595 public @interface UseExperimental { 3596 Class<?> markerClass() default void.class; 3597 } 3598 """ 3599 ) 3600 ) 3601 ) 3602 } 3603 3604 @Test adding methods to interfacesnull3605 fun `adding methods to interfaces`() { 3606 check( 3607 expectedIssues = """ 3608 src/test/pkg/JavaInterface.java:4: error: Added method test.pkg.JavaInterface.noDefault() [AddedAbstractMethod] 3609 src/test/pkg/KotlinInterface.kt:5: error: Added method test.pkg.KotlinInterface.hasDefault() [AddedAbstractMethod] 3610 src/test/pkg/KotlinInterface.kt:4: error: Added method test.pkg.KotlinInterface.noDefault() [AddedAbstractMethod] 3611 """, 3612 checkCompatibilityApiReleased = """ 3613 // Signature format: 3.0 3614 package test.pkg { 3615 public interface JavaInterface { 3616 } 3617 public interface KotlinInterface { 3618 } 3619 } 3620 """, 3621 sourceFiles = arrayOf( 3622 java( 3623 """ 3624 package test.pkg; 3625 3626 public interface JavaInterface { 3627 void noDefault(); 3628 default boolean hasDefault() { 3629 return true; 3630 } 3631 static void newStatic(); 3632 } 3633 """ 3634 ), 3635 kotlin( 3636 """ 3637 package test.pkg 3638 3639 interface KotlinInterface { 3640 fun noDefault() 3641 fun hasDefault(): Boolean = true 3642 } 3643 """ 3644 ) 3645 ) 3646 ) 3647 } 3648 3649 @Test Changing visibility from public to privatenull3650 fun `Changing visibility from public to private`() { 3651 check( 3652 expectedIssues = """ 3653 TESTROOT/load-api.txt:2: error: Class test.pkg.Foo changed visibility from public to private [ChangedScope] 3654 """.trimIndent(), 3655 signatureSource = """ 3656 package test.pkg { 3657 private class Foo {} 3658 } 3659 """.trimIndent(), 3660 format = FileFormat.V4, 3661 checkCompatibilityApiReleased = """ 3662 package test.pkg { 3663 public class Foo {} 3664 } 3665 """.trimIndent() 3666 ) 3667 } 3668 3669 @Test Changing class kindnull3670 fun `Changing class kind`() { 3671 check( 3672 expectedIssues = """ 3673 TESTROOT/load-api.txt:11: error: Class test.pkg.AnnotationToClass changed class/interface declaration [ChangedClass] 3674 TESTROOT/load-api.txt:13: error: Class test.pkg.AnnotationToEnum changed class/interface declaration [ChangedClass] 3675 TESTROOT/load-api.txt:12: error: Class test.pkg.AnnotationToInterface changed class/interface declaration [ChangedClass] 3676 TESTROOT/load-api.txt:4: error: Class test.pkg.ClassToAnnotation changed class/interface declaration [ChangedClass] 3677 TESTROOT/load-api.txt:2: error: Class test.pkg.ClassToEnum changed class/interface declaration [ChangedClass] 3678 TESTROOT/load-api.txt:3: error: Class test.pkg.ClassToInterface changed class/interface declaration [ChangedClass] 3679 TESTROOT/load-api.txt:7: error: Class test.pkg.EnumToAnnotation changed class/interface declaration [ChangedClass] 3680 TESTROOT/load-api.txt:5: error: Class test.pkg.EnumToClass changed class/interface declaration [ChangedClass] 3681 TESTROOT/load-api.txt:6: error: Class test.pkg.EnumToInterface changed class/interface declaration [ChangedClass] 3682 TESTROOT/load-api.txt:10: error: Class test.pkg.InterfaceToAnnotation changed class/interface declaration [ChangedClass] 3683 TESTROOT/load-api.txt:8: error: Class test.pkg.InterfaceToClass changed class/interface declaration [ChangedClass] 3684 TESTROOT/load-api.txt:9: error: Class test.pkg.InterfaceToEnum changed class/interface declaration [ChangedClass] 3685 """.trimIndent(), 3686 signatureSource = """ 3687 package test.pkg { 3688 public enum ClassToEnum {} 3689 public interface ClassToInterface {} 3690 public @interface ClassToAnnotation {} 3691 public class EnumToClass {} 3692 public interface EnumToInterface {} 3693 public @interface EnumToAnnotation {} 3694 public class InterfaceToClass {} 3695 public enum InterfaceToEnum {} 3696 public @interface InterfaceToAnnotation {} 3697 public class AnnotationToClass {} 3698 public interface AnnotationToInterface {} 3699 public enum AnnotationToEnum {} 3700 } 3701 """.trimIndent(), 3702 format = FileFormat.V4, 3703 checkCompatibilityApiReleased = """ 3704 package test.pkg { 3705 public class ClassToEnum {} 3706 public class ClassToInterface {} 3707 public class ClassToAnnotation {} 3708 public enum EnumToClass {} 3709 public enum EnumToInterface {} 3710 public enum EnumToAnnotation {} 3711 public interface InterfaceToClass {} 3712 public interface InterfaceToEnum {} 3713 public interface InterfaceToAnnotation {} 3714 public @interface AnnotationToClass {} 3715 public @interface AnnotationToInterface {} 3716 public @interface AnnotationToEnum {} 3717 } 3718 """.trimIndent() 3719 ) 3720 } 3721 3722 @Test Allow increased field access for classesnull3723 fun `Allow increased field access for classes`() { 3724 check( 3725 signatureSource = """ 3726 package test.pkg { 3727 class Foo { 3728 field public int bar; 3729 field protected int baz; 3730 field protected int spam; 3731 } 3732 } 3733 """, 3734 checkCompatibilityApiReleased = """ 3735 package test.pkg { 3736 class Foo { 3737 field protected int bar; 3738 field private int baz; 3739 field internal int spam; 3740 } 3741 } 3742 """ 3743 ) 3744 } 3745 3746 @Test Block decreased field access in classesnull3747 fun `Block decreased field access in classes`() { 3748 check( 3749 expectedIssues = """ 3750 TESTROOT/load-api.txt:3: error: Field test.pkg.Foo.bar changed visibility from public to protected [ChangedScope] 3751 TESTROOT/load-api.txt:4: error: Field test.pkg.Foo.baz changed visibility from protected to private [ChangedScope] 3752 TESTROOT/load-api.txt:5: error: Field test.pkg.Foo.spam changed visibility from protected to internal [ChangedScope] 3753 """, 3754 signatureSource = """ 3755 package test.pkg { 3756 class Foo { 3757 field protected int bar; 3758 field private int baz; 3759 field internal int spam; 3760 } 3761 } 3762 """, 3763 checkCompatibilityApiReleased = """ 3764 package test.pkg { 3765 class Foo { 3766 field public int bar; 3767 field protected int baz; 3768 field protected int spam; 3769 } 3770 } 3771 """ 3772 ) 3773 } 3774 3775 @Test Allow increased accessnull3776 fun `Allow increased access`() { 3777 check( 3778 signatureSource = """ 3779 package test.pkg { 3780 class Foo { 3781 method public void bar(); 3782 method protected void baz(); 3783 method protected void spam(); 3784 } 3785 } 3786 """, 3787 format = FileFormat.V4, 3788 checkCompatibilityApiReleased = """ 3789 package test.pkg { 3790 class Foo { 3791 method protected void bar(); 3792 method private void baz(); 3793 method internal void spam(); 3794 } 3795 } 3796 """ 3797 ) 3798 } 3799 3800 @Test Block decreased accessnull3801 fun `Block decreased access`() { 3802 check( 3803 expectedIssues = """ 3804 TESTROOT/load-api.txt:3: error: Method test.pkg.Foo.bar changed visibility from public to protected [ChangedScope] 3805 TESTROOT/load-api.txt:4: error: Method test.pkg.Foo.baz changed visibility from protected to private [ChangedScope] 3806 TESTROOT/load-api.txt:5: error: Method test.pkg.Foo.spam changed visibility from protected to internal [ChangedScope] 3807 """, 3808 signatureSource = """ 3809 package test.pkg { 3810 class Foo { 3811 method protected void bar(); 3812 method private void baz(); 3813 method internal void spam(); 3814 } 3815 } 3816 """, 3817 format = FileFormat.V4, 3818 checkCompatibilityApiReleased = """ 3819 package test.pkg { 3820 class Foo { 3821 method public void bar(); 3822 method protected void baz(); 3823 method protected void spam(); 3824 } 3825 } 3826 """ 3827 ) 3828 } 3829 3830 @Test configuring issue severitynull3831 fun `configuring issue severity`() { 3832 check( 3833 extraArguments = arrayOf(ARG_HIDE, Issues.REMOVED_METHOD.name), 3834 signatureSource = """ 3835 package test.pkg { 3836 public class Foo { 3837 } 3838 } 3839 """, 3840 checkCompatibilityApiReleased = """ 3841 package test.pkg { 3842 public class Foo { 3843 ctor public Foo(); 3844 method public void bar(); 3845 } 3846 } 3847 """ 3848 ) 3849 } 3850 3851 @Test block changing open to abstractnull3852 fun `block changing open to abstract`() { 3853 check( 3854 expectedIssues = """ 3855 TESTROOT/load-api.txt:2: error: Class test.pkg.Foo changed 'abstract' qualifier [ChangedAbstract] 3856 TESTROOT/load-api.txt:4: error: Method test.pkg.Foo.bar has changed 'abstract' qualifier [ChangedAbstract] 3857 """, 3858 signatureSource = """ 3859 package test.pkg { 3860 public abstract class Foo { 3861 ctor public Foo(); 3862 method public abstract void bar(); 3863 } 3864 } 3865 """, 3866 checkCompatibilityApiReleased = """ 3867 package test.pkg { 3868 public class Foo { 3869 ctor public Foo(); 3870 method public void bar(); 3871 } 3872 } 3873 """ 3874 ) 3875 } 3876 3877 @Test allow changing abstract to opennull3878 fun `allow changing abstract to open`() { 3879 check( 3880 signatureSource = """ 3881 package test.pkg { 3882 public class Foo { 3883 ctor public Foo(); 3884 method public void bar(); 3885 } 3886 } 3887 """, 3888 checkCompatibilityApiReleased = """ 3889 package test.pkg { 3890 public abstract class Foo { 3891 ctor public Foo(); 3892 method public abstract void bar(); 3893 } 3894 } 3895 """ 3896 ) 3897 } 3898 3899 @Test Change default to abstractnull3900 fun `Change default to abstract`() { 3901 check( 3902 expectedIssues = """ 3903 TESTROOT/load-api.txt:3: error: Method test.pkg.Foo.bar has changed 'default' qualifier [ChangedDefault] 3904 """, 3905 signatureSource = """ 3906 package test.pkg { 3907 interface Foo { 3908 method abstract public void bar(Int); 3909 } 3910 } 3911 """, 3912 checkCompatibilityApiReleased = """ 3913 package test.pkg { 3914 interface Foo { 3915 method default public void bar(Int); 3916 } 3917 } 3918 """ 3919 ) 3920 } 3921 3922 @Test Allow change from non-final to final in sealed classnull3923 fun `Allow change from non-final to final in sealed class`() { 3924 check( 3925 signatureSource = """ 3926 package test.pkg { 3927 sealed class Foo { 3928 method final public void bar(Int); 3929 } 3930 } 3931 """, 3932 format = FileFormat.V4, 3933 checkCompatibilityApiReleased = """ 3934 package test.pkg { 3935 sealed class Foo { 3936 method public void bar(Int); 3937 } 3938 } 3939 """ 3940 ) 3941 } 3942 3943 @Test unchanged self-referencing type parameter is compatiblenull3944 fun `unchanged self-referencing type parameter is compatible`() { 3945 check( 3946 checkCompatibilityApiReleased = """ 3947 package test.pkg { 3948 public abstract class Foo<T extends test.pkg.Foo<T>> { 3949 method public static <T extends test.pkg.Foo<T>> T valueOf(Class<T>, String); 3950 } 3951 } 3952 """, 3953 sourceFiles = arrayOf( 3954 java( 3955 """ 3956 package test.pkg; 3957 import android.annotation.NonNull; 3958 public abstract class Foo<T extends Foo<T>> { 3959 @NonNull 3960 public static <T extends Foo<T>> T valueOf(@NonNull Class<T> fooType, @NonNull String name) {} 3961 } 3962 """ 3963 ), 3964 nonNullSource 3965 ) 3966 ) 3967 } 3968 3969 // TODO: Check method signatures changing incompatibly (look especially out for adding new overloaded 3970 // methods and comparator getting confused!) 3971 // ..equals on the method items should actually be very useful! 3972 } 3973