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