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