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