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