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