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 @file:Suppress("ALL")
18 
19 package com.android.tools.metalava
20 
21 import com.android.tools.lint.checks.infrastructure.TestFiles.base64gzip
22 import org.junit.Test
23 
24 class ApiFileTest : DriverTest() {
25 /*
26    Conditions to test:
27    - test all the error scenarios found in the notStrippable case!
28    - split up test into many individual test cases
29    - try referencing a class from an annotation!
30    - test having a throws list where some exceptions are hidden but extend
31      public exceptions: do we map over to the referenced ones?
32 
33    - test type reference from all the possible places -- in type signatures - interfaces,
34      extends, throws, type bounds, etc.
35    - method which overrides @hide method: should appear in subclass (test chain
36      of two nested too)
37    - BluetoothGattCharacteristic.java#describeContents: Was marked @hide,
38      but is unhidden because it extends a public interface method
39    - package javadoc (also make sure merging both!, e.g. try having @hide in each)
40    - StopWatchMap -- inner class with @hide marks allh top levels!
41    - Test field inlining: should I include fields from an interface, if that
42      inteface was implemented by the parent class (and therefore appears there too?)
43      What if the superclass is abstract?
44    - Exposing package private classes. Test that I only do this for package private
45      classes, NOT Those marked @hide (is that, having @hide on a used type, illegal?)
46    - Test error handling (invalid @hide combinations))
47    - Consider what happens if we promote a package private class (because it's
48      extended by a public class), and then we restore its public members; the
49      override logic there isn't quite right. We've duplicated the significant-override
50      code to not skip private members, but that could change semantics. This isn't
51      ideal; instead we should now mark this class as public, and re-run the analysis
52      again (with the new hidden state for this class).
53    - compilation unit sorting - top level classes out of order
54    - Massive classes such as android.R.java? Maybe do synthetic test.
55    - HttpResponseCache implemented a public OkHttp interface, but the sole implementation
56      method was marked @hide, so the method doesn't show up. Is that some other rule --
57      that we skip interfaces if their implementation methods are marked @hide?
58    - Test recursive package filtering.
59  */
60 
61     @Test
Basic class signature extractionnull62     fun `Basic class signature extraction`() {
63         // Basic class; also checks that default constructor is made explicit
64         check(
65             sourceFiles = arrayOf(
66                 java(
67                     """
68                     package test.pkg;
69                     public class Foo {
70                     }
71                     """
72                 )
73             ),
74             api = """
75                     package test.pkg {
76                       public class Foo {
77                         ctor public Foo();
78                       }
79                     }
80                     """
81         )
82     }
83 
84     @Test
Parameter Names in Javanull85     fun `Parameter Names in Java`() {
86         // Java code which explicitly specifies parameter names
87         check(
88             compatibilityMode = false, // parameter names only in v2
89             sourceFiles = arrayOf(
90                 java(
91                     """
92                     package test.pkg;
93                     import androidx.annotation.ParameterName;
94 
95                     public class Foo {
96                         public void foo(int javaParameter1, @ParameterName("publicParameterName") int javaParameter2) {
97                         }
98                     }
99                     """
100                 ),
101                 supportParameterName
102             ),
103             api = """
104                     package test.pkg {
105                       public class Foo {
106                         ctor public Foo();
107                         method public void foo(int, int publicParameterName);
108                       }
109                     }
110                  """,
111             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
112         )
113     }
114 
115     @Test
Default Values Names in Javanull116     fun `Default Values Names in Java`() {
117         // Java code which explicitly specifies parameter names
118         check(
119             format = FileFormat.V3,
120             sourceFiles = arrayOf(
121                 java(
122                     """
123                     package test.pkg;
124                     import androidx.annotation.DefaultValue;
125 
126                     public class Foo {
127                         public void foo(
128                             @DefaultValue("null") String prefix,
129                             @DefaultValue("\"Hello World\"") String greeting,
130                             @DefaultValue("42") int meaning) {
131                         }
132                     }
133                     """
134                 ),
135                 supportDefaultValue
136             ),
137             api = """
138                 // Signature format: 3.0
139                 package test.pkg {
140                   public class Foo {
141                     ctor public Foo();
142                     method public void foo(String! = null, String! = "Hello World", int = 42);
143                   }
144                 }
145                  """,
146             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
147         )
148     }
149 
150     @Test
Default Values and Names in Kotlinnull151     fun `Default Values and Names in Kotlin`() {
152         // Kotlin code which explicitly specifies parameter names
153         check(
154             format = FileFormat.V3,
155             compatibilityMode = false,
156             sourceFiles = arrayOf(
157                 kotlin(
158                     """
159                     package test.pkg
160                     import some.other.pkg.Constants.Misc.SIZE
161                     import android.graphics.Bitmap
162                     import android.view.View
163 
164                     class Foo {
165                         fun method1(int: Int = 42,
166                             int2: Int? = null,
167                             byte: Int = 2 * 21,
168                             str: String = "hello " + "world",
169                             vararg args: String) { }
170 
171                         fun method2(int: Int, int2: Int = (2*int) * SIZE) { }
172 
173                         fun method3(str: String, int: Int, int2: Int = double(int) + str.length) { }
174 
175                         fun emptyLambda(sizeOf: () -> Unit = {  }) {}
176 
177                         fun View.drawToBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap? = null
178 
179                         companion object {
180                             fun double(int: Int) = 2 * int
181                             fun print(foo: Foo = Foo()) { println(foo) }
182                         }
183                     }
184                     """
185                 ),
186                 java(
187                     """
188                     package some.other.pkg;
189                     public class Constants {
190                         public static class Misc {
191                             public static final int SIZE = 5;
192                         }
193                     }
194                     """
195                 )
196             ),
197             api = """
198                 // Signature format: 3.0
199                 package test.pkg {
200                   public final class Foo {
201                     ctor public Foo();
202                     method public android.graphics.Bitmap? drawToBitmap(android.view.View, android.graphics.Bitmap.Config config = android.graphics.Bitmap.Config.ARGB_8888);
203                     method public void emptyLambda(kotlin.jvm.functions.Function0<kotlin.Unit> sizeOf = {});
204                     method public void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args);
205                     method public void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE);
206                     method public void method3(String str, int p, int int2 = double(int) + str.length);
207                     field public static final test.pkg.Foo.Companion Companion;
208                   }
209                   public static final class Foo.Companion {
210                     method public int double(int p);
211                     method public void print(test.pkg.Foo foo = test.pkg.Foo());
212                   }
213                 }
214                 """,
215             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation", ARG_HIDE_PACKAGE, "some.other.pkg"),
216             includeSignatureVersion = true
217         )
218     }
219 
220     @Test
Default Values in Kotlin for expressionsnull221     fun `Default Values in Kotlin for expressions`() {
222         // Testing trickier default values; regression test for problem
223         // observed in androidx.core.util with LruCache
224         check(
225             format = FileFormat.V3,
226             sourceFiles = arrayOf(
227                 kotlin(
228                     """
229                     package androidx.core.util
230 
231                     import android.util.LruCache
232 
233                     inline fun <K : Any, V : Any> lruCache(
234                         maxSize: Int,
235                         crossinline sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
236                         @Suppress("USELESS_CAST") // https://youtrack.jetbrains.com/issue/KT-21946
237                         crossinline create: (key: K) -> V? = { null as V? },
238                         crossinline onEntryRemoved: (evicted: Boolean, key: K, oldValue: V, newValue: V?) -> Unit =
239                             { _, _, _, _ -> }
240                     ): LruCache<K, V> {
241                         return object : LruCache<K, V>(maxSize) {
242                             override fun sizeOf(key: K, value: V) = sizeOf(key, value)
243                             override fun create(key: K) = create(key)
244                             override fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {
245                                 onEntryRemoved(evicted, key, oldValue, newValue)
246                             }
247                         }
248                     }
249                     """
250                 ),
251                 java(
252                     """
253                     package androidx.collection;
254 
255                     import androidx.annotation.NonNull;
256                     import androidx.annotation.Nullable;
257 
258                     import java.util.LinkedHashMap;
259                     import java.util.Locale;
260                     import java.util.Map;
261 
262                     public class LruCache<K, V> {
263                         @Nullable
264                         protected V create(@NonNull K key) {
265                             return null;
266                         }
267 
268                         protected int sizeOf(@NonNull K key, @NonNull V value) {
269                             return 1;
270                         }
271 
272                         protected void entryRemoved(boolean evicted, @NonNull K key, @NonNull V oldValue,
273                                 @Nullable V newValue) {
274                         }
275                     }
276                     """
277                 ),
278                 androidxNullableSource,
279                 androidxNonNullSource
280             ),
281             api = """
282                 // Signature format: 3.0
283                 package androidx.core.util {
284                   public final class TestKt {
285                     method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> return 1 }, kotlin.jvm.functions.Function1<? super K,? extends V> create = { return null as V }, kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = { _, _, _, _ ->  });
286                   }
287                 }
288                 """,
289             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation", ARG_HIDE_PACKAGE, "androidx.collection"),
290             includeSignatureVersion = true
291         )
292     }
293 
294     @Test
Basic Kotlin classnull295     fun `Basic Kotlin class`() {
296         check(
297             format = FileFormat.V1,
298             extraArguments = arrayOf("--parameter-names=true"),
299             sourceFiles = arrayOf(
300                 kotlin(
301                     """
302                     package test.pkg
303                     class Kotlin(val property1: String = "Default Value", arg2: Int) : Parent() {
304                         override fun method() = "Hello World"
305                         fun otherMethod(ok: Boolean, times: Int) {
306                         }
307 
308                         var property2: String? = null
309 
310                         private var someField = 42
311                         @JvmField
312                         var someField2 = 42
313 
314                         internal var myHiddenVar = false
315                         internal fun myHiddenMethod() { }
316                         internal data class myHiddenClass(): Unit
317 
318                         companion object {
319                             const val MY_CONST = 42
320                         }
321                     }
322 
323                     //@get:RequiresApi(26)
324                     inline val @receiver:String Long.isSrgb get() = true
325                     inline val /*@receiver:ColorInt*/ Int.red get() = 0
326                     inline operator fun String.component1() = ""
327 
328                     open class Parent {
329                         open fun method(): String? = null
330                         open fun method2(value: Boolean, value: Boolean?): String? = null
331                         open fun method3(value: Int?, value2: Int): Int = null
332                     }
333                     """
334                 )
335             ),
336             api = """
337                 package test.pkg {
338                   public final class Kotlin extends test.pkg.Parent {
339                     ctor public Kotlin(java.lang.String property1, int arg2);
340                     method public java.lang.String getProperty1();
341                     method public java.lang.String getProperty2();
342                     method public void otherMethod(boolean ok, int times);
343                     method public void setProperty2(java.lang.String p);
344                     property public final java.lang.String property2;
345                     field public static final test.pkg.Kotlin.Companion Companion;
346                     field public static final int MY_CONST = 42; // 0x2a
347                     field public int someField2;
348                   }
349                   public static final class Kotlin.Companion {
350                   }
351                   public final class KotlinKt {
352                     method public static inline operator java.lang.String component1(java.lang.String);
353                     method public static inline int getRed(int);
354                     method public static inline boolean isSrgb(long);
355                   }
356                   public class Parent {
357                     ctor public Parent();
358                     method public java.lang.String method();
359                     method public java.lang.String method2(boolean value, java.lang.Boolean value);
360                     method public int method3(java.lang.Integer value, int value2);
361                   }
362                 }
363                 """
364         )
365     }
366 
367     @Test
Kotlin Reified Methodsnull368     fun `Kotlin Reified Methods`() {
369         check(
370             sourceFiles = arrayOf(
371                 java(
372                     """
373                     package test.pkg;
374 
375                     public class Context {
376                         @SuppressWarnings("unchecked")
377                         public final <T> T getSystemService(Class<T> serviceClass) {
378                             return null;
379                         }
380                     }
381                     """
382                 ),
383                 kotlin(
384                     """
385                     package test.pkg
386 
387                     inline fun <reified T> Context.systemService1() = getSystemService(T::class.java)
388                     inline fun Context.systemService2() = getSystemService(String::class.java)
389                     """
390                 )
391             ),
392             api = """
393                 package test.pkg {
394                   public class Context {
395                     ctor public Context();
396                     method public final <T> T getSystemService(java.lang.Class<T>);
397                   }
398                   public final class _java_Kt {
399                     method public static inline <reified T> T systemService1(test.pkg.Context);
400                     method public static inline java.lang.String systemService2(test.pkg.Context);
401                   }
402                 }
403                 """
404         )
405     }
406 
407     @Test
Kotlin Reified Methods 2null408     fun `Kotlin Reified Methods 2`() {
409         check(
410             compatibilityMode = false,
411             sourceFiles = arrayOf(
412                 kotlin(
413                     """
414                     @file:Suppress("NOTHING_TO_INLINE", "RedundantVisibilityModifier", "unused")
415 
416                     package test.pkg
417 
418                     inline fun <T> a(t: T) { }
419                     inline fun <reified T> b(t: T) { }
420                     private inline fun <reified T> c(t: T) { } // hide
421                     internal inline fun <reified T> d(t: T) { } // hide
422                     public inline fun <reified T> e(t: T) { }
423                     inline fun <reified T> T.f(t: T) { }
424                     """
425                 )
426             ),
427             api = """
428                 package test.pkg {
429                   public final class TestKt {
430                     method public static inline <T> void a(@Nullable T t);
431                     method public static inline <reified T> void b(@Nullable T t);
432                     method public static inline <reified T> void e(@Nullable T t);
433                     method public static inline <reified T> void f(@Nullable T, @Nullable T t);
434                   }
435                 }
436                 """
437         )
438     }
439 
440     @Test
Suspend functionsnull441     fun `Suspend functions`() {
442         check(
443             compatibilityMode = false,
444             sourceFiles = arrayOf(
445                 kotlin(
446                     """
447                     package test.pkg
448                     suspend inline fun hello() { }
449                     internal suspend fun internalHello() { }
450                     private suspend fun privateHello() { }
451                     """
452                 )
453             ),
454             api = """
455                 package test.pkg {
456                   public final class TestKt {
457                     method @Nullable public static suspend inline Object hello(@NonNull kotlin.coroutines.Continuation<? super kotlin.Unit> p);
458                   }
459                 }
460                 """
461         )
462     }
463 
464     @Test
Var properties with private settersnull465     fun `Var properties with private setters`() {
466         check(
467             format = FileFormat.V3,
468             sourceFiles = arrayOf(
469                 kotlin(
470                     """
471                     package test.pkg
472                     class MyClass {
473                         // This property should have no public setter
474                         var readOnlyVar = false
475                             internal set
476                     }
477                     """
478                 )
479             ),
480             api = """
481                 // Signature format: 3.0
482                 package test.pkg {
483                   public final class MyClass {
484                     ctor public MyClass();
485                     method public boolean getReadOnlyVar();
486                     property public final boolean readOnlyVar;
487                   }
488                 }
489                 """
490         )
491     }
492 
493     @Test
Kotlin Genericsnull494     fun `Kotlin Generics`() {
495         check(
496             format = FileFormat.V3,
497             sourceFiles = arrayOf(
498                 kotlin(
499                     """
500                     package test.pkg
501                     class Bar
502                     class Type<in T> {
503                         fun foo(param: Type<Bar>) {
504                         }
505                     }
506                     """
507                 )
508             ),
509             compatibilityMode = false,
510             api = """
511                 // Signature format: 3.0
512                 package test.pkg {
513                   public final class Bar {
514                     ctor public Bar();
515                   }
516                   public final class Type<T> {
517                     ctor public Type();
518                     method public void foo(test.pkg.Type<? super test.pkg.Bar> param);
519                   }
520                 }
521                 """
522         )
523     }
524 
525     @Test
Nullness in reified signaturesnull526     fun `Nullness in reified signatures`() {
527         check(
528             compatibilityMode = false,
529             sourceFiles = arrayOf(
530                 kotlin(
531                     "src/test/pkg/test.kt",
532                     """
533                     package test.pkg
534 
535                     import androidx.annotation.UiThread
536                     import test.pkg2.NavArgs
537                     import test.pkg2.NavArgsLazy
538                     import test.pkg2.Fragment
539                     import test.pkg2.Bundle
540 
541                     @UiThread
542                     inline fun <reified Args : NavArgs> Fragment.navArgs() = NavArgsLazy(Args::class) {
543                         throw IllegalStateException("Fragment $this has null arguments")
544                     }
545                     """
546                 ),
547                 kotlin(
548                     """
549                     package test.pkg2
550 
551                     import kotlin.reflect.KClass
552 
553                     interface NavArgs
554                     class Fragment
555                     class Bundle
556                     class NavArgsLazy<Args : NavArgs>(
557                         private val navArgsClass: KClass<Args>,
558                         private val argumentProducer: () -> Bundle
559                     )
560                     """
561                 ),
562                 uiThreadSource
563             ),
564             api = """
565                 // Signature format: 3.0
566                 package test.pkg {
567                   public final class TestKt {
568                     method @UiThread public static inline <reified Args extends test.pkg2.NavArgs> test.pkg2.NavArgsLazy<Args>! navArgs(test.pkg2.Fragment);
569                   }
570                 }
571                 """,
572 //            Actual expected API is below. However, due to KT-39209 the nullability information is
573 //              missing
574 //            api = """
575 //                // Signature format: 3.0
576 //                package test.pkg {
577 //                  public final class TestKt {
578 //                    method @UiThread public static inline <reified Args extends test.pkg2.NavArgs> test.pkg2.NavArgsLazy<Args> navArgs(test.pkg2.Fragment);
579 //                  }
580 //                }
581 //                """,
582             format = FileFormat.V3,
583             extraArguments = arrayOf(
584                 ARG_HIDE_PACKAGE, "androidx.annotation",
585                 ARG_HIDE_PACKAGE, "test.pkg2",
586                 ARG_HIDE, "ReferencesHidden",
587                 ARG_HIDE, "UnavailableSymbol",
588                 ARG_HIDE, "HiddenTypeParameter"
589             )
590         )
591     }
592 
593     @Test
Nullness in varargsnull594     fun `Nullness in varargs`() {
595         check(
596             compatibilityMode = false,
597             sourceFiles = arrayOf(
598                 java(
599                     """
600                     package androidx.collection;
601 
602                     import java.util.Collection;
603                     import java.util.HashMap;
604                     import java.util.Map;
605 
606                     public class ArrayMap<K, V> extends HashMap<K, V> implements Map<K, V> {
607                         public ArrayMap() {
608                         }
609                     }
610                     """
611                 ),
612                 java(
613                     """
614                     package androidx.core.app;
615 
616                     import java.util.ArrayList;
617                     import java.util.List;
618 
619                     import androidx.annotation.NonNull;
620                     import androidx.annotation.Nullable;
621 
622                     public class ActivityOptionsCompat {
623                         private ActivityOptionsCompat() {
624                         }
625                         @NonNull
626                         public static List<String> javaListOf(String... sharedElements) {
627                             return new ArrayList<String>();
628                         }
629                         @Nullable
630                         public static List<String> javaListOfNullable(String... sharedElements) {
631                             return null;
632                         }
633 
634                     }
635                     """
636                 ),
637                 kotlin(
638                     "src/main/java/androidx/collection/ArrayMap.kt",
639                     """
640                     package androidx.collection
641 
642                     inline fun <K, V> arrayMapOf(): ArrayMap<K, V> = ArrayMap()
643 
644                     fun <K, V> arrayMapOf(vararg pairs: Pair<K, V>): ArrayMap<K, V> {
645                         val map = ArrayMap<K, V>(pairs.size)
646                         for (pair in pairs) {
647                             map[pair.first] = pair.second
648                         }
649                         return map
650                     }
651                     fun <K, V> arrayMapOfNullable(vararg pairs: Pair<K, V>?): ArrayMap<K, V>? {
652                         return null
653                     }
654                     """
655                 ),
656                 androidxNonNullSource,
657                 androidxNullableSource
658             ),
659             api = """
660                 // Signature format: 3.0
661                 package androidx.collection {
662                   public class ArrayMap<K, V> extends java.util.HashMap<K,V> implements java.util.Map<K,V> {
663                     ctor public ArrayMap();
664                   }
665                   public final class ArrayMapKt {
666                     method public static inline <K, V> androidx.collection.ArrayMap<K,V> arrayMapOf();
667                     method public static <K, V> androidx.collection.ArrayMap<K,V> arrayMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
668                     method public static <K, V> androidx.collection.ArrayMap<K,V>? arrayMapOfNullable(kotlin.Pair<? extends K,? extends V>?... pairs);
669                   }
670                 }
671                 package androidx.core.app {
672                   public class ActivityOptionsCompat {
673                     method public static java.util.List<java.lang.String!> javaListOf(java.lang.String!...);
674                     method public static java.util.List<java.lang.String!>? javaListOfNullable(java.lang.String!...);
675                   }
676                 }
677                 """,
678             format = FileFormat.V3,
679             extraArguments = arrayOf(
680                 ARG_HIDE_PACKAGE, "androidx.annotation",
681                 ARG_HIDE, "ReferencesHidden",
682                 ARG_HIDE, "UnavailableSymbol",
683                 ARG_HIDE, "HiddenTypeParameter"
684             )
685         )
686     }
687 
688     @Test
Propagate Platform types in Kotlinnull689     fun `Propagate Platform types in Kotlin`() {
690         check(
691             compatibilityMode = false,
692             format = FileFormat.V3,
693             sourceFiles = arrayOf(
694                 kotlin(
695                     """
696                     // Nullable Pair in Kotlin
697                     package androidx.util
698 
699                     class NullableKotlinPair<out F, out S>(val first: F?, val second: S?)
700                     """
701                 ),
702                 kotlin(
703                     """
704                     // Non-nullable Pair in Kotlin
705                     package androidx.util
706                     class NonNullableKotlinPair<out F: Any, out S: Any>(val first: F, val second: S)
707                     """
708                 ),
709                 java(
710                     """
711                     // Platform nullability Pair in Java
712                     package androidx.util;
713 
714                     @SuppressWarnings("WeakerAccess")
715                     public class PlatformJavaPair<F, S> {
716                         public final F first;
717                         public final S second;
718 
719                         public PlatformJavaPair(F first, S second) {
720                             this.first = first;
721                             this.second = second;
722                         }
723                     }
724                 """
725                 ),
726                 java(
727                     """
728                     // Platform nullability Pair in Java
729                     package androidx.util;
730                     import androidx.annotation.NonNull;
731                     import androidx.annotation.Nullable;
732 
733                     @SuppressWarnings("WeakerAccess")
734                     public class NullableJavaPair<F, S> {
735                         public final @Nullable F first;
736                         public final @Nullable S second;
737 
738                         public NullableJavaPair(@Nullable F first, @Nullable S second) {
739                             this.first = first;
740                             this.second = second;
741                         }
742                     }
743                     """
744                 ),
745                 java(
746                     """
747                     // Platform nullability Pair in Java
748                     package androidx.util;
749 
750                     import androidx.annotation.NonNull;
751 
752                     @SuppressWarnings("WeakerAccess")
753                     public class NonNullableJavaPair<F, S> {
754                         public final @NonNull F first;
755                         public final @NonNull S second;
756 
757                         public NonNullableJavaPair(@NonNull F first, @NonNull S second) {
758                             this.first = first;
759                             this.second = second;
760                         }
761                     }
762                     """
763                 ),
764                 kotlin(
765                     """
766                     package androidx.util
767 
768                     @Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
769                     inline operator fun <F, S> PlatformJavaPair<F, S>.component1() = first
770                     """
771                 ),
772                 androidxNonNullSource,
773                 androidxNullableSource
774             ),
775             api = """
776                 // Signature format: 3.0
777                 package androidx.util {
778                   public class NonNullableJavaPair<F, S> {
779                     ctor public NonNullableJavaPair(F, S);
780                     field public final F first;
781                     field public final S second;
782                   }
783                   public final class NonNullableKotlinPair<F, S> {
784                     ctor public NonNullableKotlinPair(F first, S second);
785                     method public F getFirst();
786                     method public S getSecond();
787                   }
788                   public class NullableJavaPair<F, S> {
789                     ctor public NullableJavaPair(F?, S?);
790                     field public final F? first;
791                     field public final S? second;
792                   }
793                   public final class NullableKotlinPair<F, S> {
794                     ctor public NullableKotlinPair(F? first, S? second);
795                     method public F? getFirst();
796                     method public S? getSecond();
797                   }
798                   public class PlatformJavaPair<F, S> {
799                     ctor public PlatformJavaPair(F!, S!);
800                     field public final F! first;
801                     field public final S! second;
802                   }
803                   public final class TestKt {
804                     method public static inline operator <F, S> F! component1(androidx.util.PlatformJavaPair<F,S>);
805                   }
806                 }
807                 """,
808             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
809         )
810     }
811 
812     @Test
Known nullnessnull813     fun `Known nullness`() {
814         // Don't emit platform types for some unannotated elements that we know the
815         // nullness for: annotation type members, equals-parameters, initialized constants, etc.
816         check(
817             compatibilityMode = false,
818             outputKotlinStyleNulls = true,
819             sourceFiles = arrayOf(
820                 java(
821                     """
822                     // Platform nullability Pair in Java
823                     package test;
824 
825                     import androidx.annotation.NonNull;
826 
827                     public class MyClass {
828                         public static final String MY_CONSTANT1 = "constant"; // Not nullable
829                         public final String MY_CONSTANT2 = "constant"; // Not nullable
830                         public String MY_CONSTANT3 = "constant"; // Unknown
831 
832                         /** @deprecated */
833                         @Deprecated
834                         @Override
835                         public boolean equals(
836                             Object parameter  // nullable
837                         ) {
838                             return super.equals(parameter);
839                         }
840 
841                         /** @deprecated */
842                         @Deprecated
843                         @Override // Not nullable
844                         public String toString() {
845                             return super.toString();
846                         }
847                     }
848                     """
849                 ),
850                 java(
851                     """
852                     package test.pkg;
853 
854                     import static java.lang.annotation.ElementType.*;
855                     import java.lang.annotation.*;
856                     public @interface MyAnnotation {
857                         String[] value(); // Not nullable
858                     }
859                     """
860                 ).indented(),
861                 java(
862                     """
863                     package test.pkg;
864                     @SuppressWarnings("ALL")
865                     public enum Foo {
866                         A, B;
867                     }
868                     """
869                 ),
870                 kotlin(
871                     """
872                     package test.pkg
873                     enum class Language {
874                         KOTLIN,
875                         JAVA
876                     }
877                     """
878                 ).indented(),
879                 kotlin(
880                     """
881                     package test.pkg
882                     class Issue {
883                         fun setAndroidSpecific(value: Boolean): Issue { return this }
884                         companion object {
885                             @JvmStatic
886                             fun create(
887                                 id: String,
888                                 briefDescription: String,
889                                 explanation: String
890                             ): Issue {
891                                 return Issue()
892                             }
893                         }
894                     }
895                     """
896                 ).indented(),
897                 kotlin(
898                     """
899                     package test.pkg
900                     object MySingleton {
901                     }
902                     """
903                 ).indented(),
904                 java(
905                     """
906                     package test.pkg;
907                     public class WrongCallDetector {
908                         public static final Issue ISSUE =
909                                 Issue.create(
910                                                 "WrongCall",
911                                                 "Using wrong draw/layout method",
912                                                 "Custom views typically need to call `measure()`)
913                                         .setAndroidSpecific(true));
914                     }
915                     """
916                 ).indented(),
917                 androidxNonNullSource,
918                 androidxNullableSource
919             ),
920             api = """
921                 // Signature format: 3.0
922                 package test {
923                   public class MyClass {
924                     ctor public MyClass();
925                     method @Deprecated public boolean equals(Object?);
926                     method @Deprecated public String toString();
927                     field public static final String MY_CONSTANT1 = "constant";
928                     field public final String MY_CONSTANT2 = "constant";
929                     field public String! MY_CONSTANT3;
930                   }
931                 }
932                 package test.pkg {
933                   public enum Foo {
934                     enum_constant public static final test.pkg.Foo A;
935                     enum_constant public static final test.pkg.Foo B;
936                   }
937                   public final class Issue {
938                     ctor public Issue();
939                     method public static test.pkg.Issue create(String id, String briefDescription, String explanation);
940                     method public test.pkg.Issue setAndroidSpecific(boolean value);
941                     field public static final test.pkg.Issue.Companion Companion;
942                   }
943                   public static final class Issue.Companion {
944                     method public test.pkg.Issue create(String id, String briefDescription, String explanation);
945                   }
946                   public enum Language {
947                     method public static test.pkg.Language valueOf(String name) throws java.lang.IllegalArgumentException;
948                     method public static test.pkg.Language[] values();
949                     enum_constant public static final test.pkg.Language JAVA;
950                     enum_constant public static final test.pkg.Language KOTLIN;
951                   }
952                   @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface MyAnnotation {
953                     method public abstract String[] value();
954                   }
955                   public final class MySingleton {
956                     field public static final test.pkg.MySingleton INSTANCE;
957                   }
958                   public class WrongCallDetector {
959                     ctor public WrongCallDetector();
960                     field public static final test.pkg.Issue ISSUE;
961                   }
962                 }
963                 """,
964             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
965         )
966     }
967 
968     @Test
JvmOverloadsnull969     fun JvmOverloads() {
970         // Regression test for https://github.com/android/android-ktx/issues/366
971         check(
972             format = FileFormat.V3,
973             compatibilityMode = false,
974             sourceFiles = arrayOf(
975                 kotlin(
976                     """
977                         package androidx.content
978 
979                         import android.annotation.SuppressLint
980                         import android.content.SharedPreferences
981 
982                         @SuppressLint("ApplySharedPref")
983                         @JvmOverloads
984                         inline fun SharedPreferences.edit(
985                             commit: Boolean = false,
986                             action: SharedPreferences.Editor.() -> Unit
987                         ) {
988                             val editor = edit()
989                             action(editor)
990                             if (commit) {
991                                 editor.commit()
992                             } else {
993                                 editor.apply()
994                             }
995                         }
996 
997                         @JvmOverloads
998                         fun String.blahblahblah(firstArg: String = "hello", secondArg: Int = 42, thirdArg: String = "world") {
999                         }
1000                     """
1001                 )
1002             ),
1003             api = """
1004                 // Signature format: 3.0
1005                 package androidx.content {
1006                   public final class TestKt {
1007                     method public static void blahblahblah(String, String firstArg = "hello", int secondArg = 42, String thirdArg = "world");
1008                     method public static void blahblahblah(String, String firstArg = "hello", int secondArg = 42);
1009                     method public static void blahblahblah(String, String firstArg = "hello");
1010                     method public static void blahblahblah(String);
1011                     method public static inline void edit(android.content.SharedPreferences, boolean commit = false, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
1012                     method public static inline void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
1013                   }
1014                 }
1015                 """,
1016             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
1017         )
1018     }
1019 
1020     @Test
Test JvmStaticnull1021     fun `Test JvmStatic`() {
1022         check(
1023             sourceFiles = arrayOf(
1024                 kotlin(
1025                     """
1026                     package test.pkg
1027 
1028                     class SimpleClass {
1029                         companion object {
1030                             @JvmStatic
1031                             fun jvmStaticMethod() {}
1032                             fun nonJvmStaticMethod() {}
1033                         }
1034                     }
1035                 """
1036                 )
1037             ),
1038             format = FileFormat.V3,
1039             api = """
1040                 // Signature format: 3.0
1041                 package test.pkg {
1042                   public final class SimpleClass {
1043                     ctor public SimpleClass();
1044                     method public static void jvmStaticMethod();
1045                     field public static final test.pkg.SimpleClass.Companion Companion;
1046                   }
1047                   public static final class SimpleClass.Companion {
1048                     method public void jvmStaticMethod();
1049                     method public void nonJvmStaticMethod();
1050                   }
1051                 }
1052             """
1053         )
1054     }
1055 
1056     @Test
Test JvmFieldnull1057     fun `Test JvmField`() {
1058         check(
1059             sourceFiles = arrayOf(
1060                 kotlin(
1061                     """
1062                     package test.pkg
1063 
1064                     class SimpleClass {
1065                         @JvmField
1066                         var jvmField = -1
1067 
1068                         var nonJvmField = -2
1069                     }
1070                 """
1071                 )
1072             ),
1073             format = FileFormat.V3,
1074             api = """
1075                 // Signature format: 3.0
1076                 package test.pkg {
1077                   public final class SimpleClass {
1078                     ctor public SimpleClass();
1079                     method public int getNonJvmField();
1080                     method public void setNonJvmField(int p);
1081                     property public final int nonJvmField;
1082                     field public int jvmField;
1083                   }
1084                 }
1085             """
1086         )
1087     }
1088 
1089     @Test
Test JvmNamenull1090     fun `Test JvmName`() {
1091         check(
1092             sourceFiles = arrayOf(
1093                 kotlin(
1094                     """
1095                     package test.pkg
1096 
1097                     class SimpleClass {
1098                         @get:JvmName("myPropertyJvmGetter")
1099                         var myProperty = -1
1100 
1101                         var anotherProperty = -1
1102                     }
1103                 """
1104                 )
1105             ),
1106             format = FileFormat.V3,
1107             api = """
1108                 // Signature format: 3.0
1109                 package test.pkg {
1110                   public final class SimpleClass {
1111                     ctor public SimpleClass();
1112                     method public int getAnotherProperty();
1113                     method public int myPropertyJvmGetter();
1114                     method public void setAnotherProperty(int p);
1115                     method public void setMyProperty(int p);
1116                     property public final int anotherProperty;
1117                     property public final int myProperty;
1118                   }
1119                 }
1120             """
1121         )
1122     }
1123 
1124     @Test
Test RequiresOptIn and OptInnull1125     fun `Test RequiresOptIn and OptIn`() {
1126         check(
1127             sourceFiles = arrayOf(
1128                 kotlin(
1129                     """
1130                     package test.pkg
1131 
1132                     @RequiresOptIn
1133                     @Retention(AnnotationRetention.BINARY)
1134                     @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
1135                     annotation class ExperimentalBar
1136 
1137                     @ExperimentalBar
1138                     class FancyBar
1139 
1140                     @OptIn(FancyBar::class) // @OptIn should not be tracked as it is not API
1141                     class SimpleClass {
1142                         fun methodUsingFancyBar() {
1143                             val fancyBar = FancyBar()
1144                         }
1145                     }
1146                 """
1147                 )
1148             ),
1149             format = FileFormat.V3,
1150             api = """
1151                 // Signature format: 3.0
1152                 package test.pkg {
1153                   @kotlin.RequiresOptIn @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION}) public @interface ExperimentalBar {
1154                   }
1155                   @test.pkg.ExperimentalBar public final class FancyBar {
1156                     ctor public FancyBar();
1157                   }
1158                   public final class SimpleClass {
1159                     ctor public SimpleClass();
1160                     method public void methodUsingFancyBar();
1161                   }
1162                 }
1163             """
1164         )
1165     }
1166 
1167     @Test
Test Experimental and UseExperimentalnull1168     fun `Test Experimental and UseExperimental`() {
1169         check(
1170             sourceFiles = arrayOf(
1171                 kotlin(
1172                     """
1173                     package test.pkg
1174 
1175                     @Experimental
1176                     @Retention(AnnotationRetention.BINARY)
1177                     @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
1178                     annotation class ExperimentalBar
1179 
1180                     @ExperimentalBar
1181                     class FancyBar
1182 
1183                     @UseExperimental(FancyBar::class) // @UseExperimental should not be tracked as it is not API
1184                     class SimpleClass {
1185                         fun methodUsingFancyBar() {
1186                             val fancyBar = FancyBar()
1187                         }
1188                     }
1189 
1190                     @androidx.annotation.experimental.UseExperimental(FancyBar::class) // @UseExperimental should not be tracked as it is not API
1191                     class AnotherSimpleClass {
1192                         fun methodUsingFancyBar() {
1193                             val fancyBar = FancyBar()
1194                         }
1195                     }
1196                 """
1197                 ),
1198                 kotlin("""
1199                     package androidx.annotation.experimental
1200 
1201                     import kotlin.annotation.Retention
1202                     import kotlin.annotation.Target
1203                     import kotlin.reflect.KClass
1204 
1205                     @Retention(AnnotationRetention.BINARY)
1206                     @Target(
1207                         AnnotationTarget.CLASS,
1208                         AnnotationTarget.PROPERTY,
1209                         AnnotationTarget.LOCAL_VARIABLE,
1210                         AnnotationTarget.VALUE_PARAMETER,
1211                         AnnotationTarget.CONSTRUCTOR,
1212                         AnnotationTarget.FUNCTION,
1213                         AnnotationTarget.PROPERTY_GETTER,
1214                         AnnotationTarget.PROPERTY_SETTER,
1215                         AnnotationTarget.FILE,
1216                         AnnotationTarget.TYPEALIAS
1217                     )
1218                     annotation class UseExperimental(
1219                         /**
1220                          * Defines the experimental API(s) whose usage this annotation allows.
1221                          */
1222                         vararg val markerClass: KClass<out Annotation>
1223                     )
1224                 """)
1225             ),
1226             format = FileFormat.V3,
1227             api = """
1228                 // Signature format: 3.0
1229                 package androidx.annotation.experimental {
1230                   @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE, AnnotationTarget.TYPEALIAS}) public @interface UseExperimental {
1231                     method public abstract Class<? extends java.lang.annotation.Annotation>[] markerClass();
1232                   }
1233                 }
1234                 package test.pkg {
1235                   public final class AnotherSimpleClass {
1236                     ctor public AnotherSimpleClass();
1237                     method public void methodUsingFancyBar();
1238                   }
1239                   @kotlin.Experimental @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION}) public @interface ExperimentalBar {
1240                   }
1241                   @test.pkg.ExperimentalBar public final class FancyBar {
1242                     ctor public FancyBar();
1243                   }
1244                   public final class SimpleClass {
1245                     ctor public SimpleClass();
1246                     method public void methodUsingFancyBar();
1247                   }
1248                 }
1249             """
1250         )
1251     }
1252 
1253     @Test
Extract class with genericsnull1254     fun `Extract class with generics`() {
1255         // Basic interface with generics; makes sure <T extends Object> is written as just <T>
1256         // Also include some more complex generics expressions to make sure they're serialized
1257         // correctly (in particular, using fully qualified names instead of what appears in
1258         // the source code.)
1259         check(
1260             sourceFiles = arrayOf(
1261                 java(
1262                     """
1263                     package test.pkg;
1264                     @SuppressWarnings("ALL")
1265                     public interface MyInterface<T extends Object>
1266                             extends MyBaseInterface {
1267                     }
1268                     """
1269                 ), java(
1270                     """
1271                     package a.b.c;
1272                     @SuppressWarnings("ALL")
1273                     public interface MyStream<T, S extends MyStream<T, S>> extends test.pkg.AutoCloseable {
1274                     }
1275                     """
1276                 ), java(
1277                     """
1278                     package test.pkg;
1279                     @SuppressWarnings("ALL")
1280                     public interface MyInterface2<T extends Number>
1281                             extends MyBaseInterface {
1282                         class TtsSpan<C extends MyInterface<?>> { }
1283                         abstract class Range<T extends Comparable<? super T>> {
1284                             protected String myString;
1285                         }
1286                     }
1287                     """
1288                 ),
1289                 java(
1290                     """
1291                     package test.pkg;
1292                     public interface MyBaseInterface {
1293                         void fun(int a, String b);
1294                     }
1295                     """
1296                 ),
1297                 java(
1298                     """
1299                     package test.pkg;
1300                     public interface MyOtherInterface extends MyBaseInterface, AutoCloseable {
1301                         void fun(int a, String b);
1302                     }
1303                     """
1304                 ),
1305                 java(
1306                     """
1307                     package test.pkg;
1308                     public interface AutoCloseable {
1309                     }
1310                     """
1311                 )
1312             ),
1313             api = """
1314                     package a.b.c {
1315                       public abstract interface MyStream<T, S extends a.b.c.MyStream<T, S>> implements test.pkg.AutoCloseable {
1316                       }
1317                     }
1318                     package test.pkg {
1319                       public abstract interface AutoCloseable {
1320                       }
1321                       public abstract interface MyBaseInterface {
1322                         method public abstract void fun(int, java.lang.String);
1323                       }
1324                       public abstract interface MyInterface<T> implements test.pkg.MyBaseInterface {
1325                       }
1326                       public abstract interface MyInterface2<T extends java.lang.Number> implements test.pkg.MyBaseInterface {
1327                       }
1328                       public static abstract class MyInterface2.Range<T extends java.lang.Comparable<? super T>> {
1329                         ctor public MyInterface2.Range();
1330                         field protected java.lang.String myString;
1331                       }
1332                       public static class MyInterface2.TtsSpan<C extends test.pkg.MyInterface<?>> {
1333                         ctor public MyInterface2.TtsSpan();
1334                       }
1335                       public abstract interface MyOtherInterface implements test.pkg.AutoCloseable test.pkg.MyBaseInterface {
1336                       }
1337                     }
1338                 """,
1339             extraArguments = arrayOf(ARG_HIDE, "KotlinKeyword")
1340         )
1341     }
1342 
1343     @Test
Basic class without default constructor, has constructors with argsnull1344     fun `Basic class without default constructor, has constructors with args`() {
1345         // Class without private constructors (shouldn't insert default constructor)
1346         check(
1347             sourceFiles = arrayOf(
1348                 java(
1349                     """
1350                     package test.pkg;
1351                     public class Foo {
1352                         public Foo(int i) {
1353 
1354                         }
1355                         public Foo(int i, int j) {
1356                         }
1357                     }
1358                     """
1359                 )
1360             ),
1361             api = """
1362                 package test.pkg {
1363                   public class Foo {
1364                     ctor public Foo(int);
1365                     ctor public Foo(int, int);
1366                   }
1367                 }
1368                 """
1369         )
1370     }
1371 
1372     @Test
Basic class without default constructor, has private constructornull1373     fun `Basic class without default constructor, has private constructor`() {
1374         // Class without private constructors; no default constructor should be inserted
1375         check(
1376             sourceFiles = arrayOf(
1377                 java(
1378                     """
1379                     package test.pkg;
1380                     @SuppressWarnings("ALL")
1381                     public class Foo {
1382                         private Foo() {
1383                         }
1384                     }
1385                     """
1386                 )
1387             ),
1388             api = """
1389                 package test.pkg {
1390                   public class Foo {
1391                   }
1392                 }
1393                 """
1394         )
1395     }
1396 
1397     @Test
Interface class extractionnull1398     fun `Interface class extraction`() {
1399         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
1400         // in the interface are taken to be public etc)
1401         check(
1402             sourceFiles = arrayOf(
1403                 java(
1404                     """
1405                     package test.pkg;
1406                     @SuppressWarnings("ALL")
1407                     public interface Foo {
1408                         void foo();
1409                     }
1410                     """
1411                 )
1412             ),
1413             api = """
1414                 package test.pkg {
1415                   public abstract interface Foo {
1416                     method public abstract void foo();
1417                   }
1418                 }
1419                 """
1420         )
1421     }
1422 
1423     @Test
Enum class extractionnull1424     fun `Enum class extraction`() {
1425         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
1426         // in the interface are taken to be public etc)
1427         check(
1428             sourceFiles = arrayOf(
1429                 java(
1430                     """
1431                     package test.pkg;
1432                     @SuppressWarnings("ALL")
1433                     public enum Foo {
1434                         A, B;
1435                     }
1436                     """
1437                 )
1438             ),
1439             api = """
1440                 package test.pkg {
1441                   public final class Foo extends java.lang.Enum {
1442                     method public static test.pkg.Foo valueOf(java.lang.String) throws java.lang.IllegalArgumentException;
1443                     method public static final test.pkg.Foo[] values();
1444                     enum_constant public static final test.pkg.Foo A;
1445                     enum_constant public static final test.pkg.Foo B;
1446                   }
1447                 }
1448                 """
1449         )
1450     }
1451 
1452     @Test
Enum class, non-compat modenull1453     fun `Enum class, non-compat mode`() {
1454         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
1455         // in the interface are taken to be public etc)
1456         check(
1457             sourceFiles = arrayOf(
1458                 java(
1459                     """
1460                     package test.pkg;
1461                     @SuppressWarnings("ALL")
1462                     public enum Foo {
1463                         A, B;
1464                     }
1465                     """
1466                 )
1467             ),
1468             compatibilityMode = false,
1469             api = """
1470                 package test.pkg {
1471                   public enum Foo {
1472                     enum_constant public static final test.pkg.Foo A;
1473                     enum_constant public static final test.pkg.Foo B;
1474                   }
1475                 }
1476                 """
1477         )
1478     }
1479 
1480     @Test
Annotation class extractionnull1481     fun `Annotation class extraction`() {
1482         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
1483         // in the interface are taken to be public etc)
1484         check(
1485             sourceFiles = arrayOf(
1486                 java(
1487                     """
1488                     package test.pkg;
1489                     @SuppressWarnings("ALL")
1490                     public @interface Foo {
1491                         String value();
1492                     }
1493                     """
1494                 ),
1495                 java(
1496                     """
1497                     package android.annotation;
1498                     import static java.lang.annotation.ElementType.*;
1499                     import java.lang.annotation.*;
1500                     @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
1501                     @Retention(RetentionPolicy.CLASS)
1502                     @SuppressWarnings("ALL")
1503                     public @interface SuppressLint {
1504                         String[] value();
1505                     }
1506                 """
1507                 )
1508             ),
1509             api = """
1510                 package android.annotation {
1511                   public abstract class SuppressLint implements java.lang.annotation.Annotation {
1512                   }
1513                 }
1514                 package test.pkg {
1515                   public abstract class Foo implements java.lang.annotation.Annotation {
1516                   }
1517                 }
1518                 """
1519         )
1520     }
1521 
1522     @Test
Do not include inherited public methods from private parents in compat modenull1523     fun `Do not include inherited public methods from private parents in compat mode`() {
1524         // Real life example: StringBuilder.setLength, in compat mode
1525         check(
1526             compatibilityMode = true,
1527             sourceFiles = arrayOf(
1528                 java(
1529                     """
1530                     package test.pkg;
1531                     public class MyStringBuilder extends AbstractMyStringBuilder {
1532                     }
1533                     """
1534                 ),
1535                 java(
1536                     """
1537                     package test.pkg;
1538                     class AbstractMyStringBuilder {
1539                         public void setLength(int length) {
1540                         }
1541                     }
1542                     """
1543                 )
1544             ),
1545             api = """
1546                 package test.pkg {
1547                   public class MyStringBuilder {
1548                     ctor public MyStringBuilder();
1549                   }
1550                 }
1551                 """
1552         )
1553     }
1554 
1555     @Test
Include inherited public methods from private parentsnull1556     fun `Include inherited public methods from private parents`() {
1557         // In non-compat mode, include public methods from hidden parents too.
1558         // Real life example: StringBuilder.setLength
1559         // This is just like the above test, but with compat mode disabled.
1560         check(
1561             compatibilityMode = false,
1562             sourceFiles = arrayOf(
1563                 java(
1564                     """
1565                     package test.pkg;
1566                     public class MyStringBuilder extends AbstractMyStringBuilder {
1567                     }
1568                     """
1569                 ),
1570                 java(
1571                     """
1572                     package test.pkg;
1573                     class AbstractMyStringBuilder {
1574                         public void setLength(int length) {
1575                         }
1576                     }
1577                     """
1578                 )
1579             ),
1580             api = """
1581                 package test.pkg {
1582                   public class MyStringBuilder {
1583                     ctor public MyStringBuilder();
1584                     method public void setLength(int);
1585                   }
1586                 }
1587                 """
1588         )
1589     }
1590 
1591     @Test
Skip inherited package private methods from private parentsnull1592     fun `Skip inherited package private methods from private parents`() {
1593         // In non-compat mode, include public methods from hidden parents too.
1594         // Real life example: StringBuilder.setLength
1595         // This is just like the above test, but with compat mode disabled.
1596         check(
1597             compatibilityMode = false,
1598             sourceFiles = arrayOf(
1599                 java(
1600                     """
1601                     package test.pkg;
1602                     public class MyStringBuilder<A,B> extends AbstractMyStringBuilder<A,B> {
1603                     }
1604                     """
1605                 ),
1606                 java(
1607                     """
1608                     package test.pkg;
1609                     class AbstractMyStringBuilder<C,D> extends PublicSuper<C,D> {
1610                         public void setLength(int length) {
1611                         }
1612                         @Override boolean isContiguous() {
1613                             return true;
1614                         }
1615                         @Override boolean concrete() {
1616                             return false;
1617                         }
1618                     }
1619                     """
1620                 ),
1621                 java(
1622                     """
1623                     package test.pkg;
1624                     public class PublicSuper<E,F> {
1625                         abstract boolean isContiguous();
1626                         boolean concrete() {
1627                             return false;
1628                         }
1629                     }
1630                     """
1631                 )
1632             ),
1633             api = """
1634                 package test.pkg {
1635                   public class MyStringBuilder<A, B> extends test.pkg.PublicSuper<A,B> {
1636                     ctor public MyStringBuilder();
1637                     method public void setLength(int);
1638                   }
1639                   public class PublicSuper<E, F> {
1640                     ctor public PublicSuper();
1641                   }
1642                 }
1643                 """
1644         )
1645     }
1646 
1647     @Test
Annotation class extraction, non-compat modenull1648     fun `Annotation class extraction, non-compat mode`() {
1649         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
1650         // in the interface are taken to be public etc)
1651         check(
1652             sourceFiles = arrayOf(
1653                 java(
1654                     """
1655                     package test.pkg;
1656                     public @interface Foo {
1657                         String value();
1658                     }
1659                     """
1660                 ),
1661                 java(
1662                     """
1663                     package android.annotation;
1664                     import static java.lang.annotation.ElementType.*;
1665                     import java.lang.annotation.*;
1666                     @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
1667                     @Retention(RetentionPolicy.CLASS)
1668                     @SuppressWarnings("ALL")
1669                     public @interface SuppressLint {
1670                         String[] value();
1671                     }
1672                     """
1673                 )
1674             ),
1675             compatibilityMode = false,
1676             api = """
1677                 package android.annotation {
1678                   @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface SuppressLint {
1679                     method public abstract String[] value();
1680                   }
1681                 }
1682                 package test.pkg {
1683                   @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface Foo {
1684                     method public abstract String value();
1685                   }
1686                 }
1687                 """
1688         )
1689     }
1690 
1691     @Test
Annotation retentionnull1692     fun `Annotation retention`() {
1693         // For annotations where the java.lang.annotation classes themselves are not
1694         // part of the source tree, ensure that we compute the right retention (runtime, meaning
1695         // it should show up in the stubs file.).
1696         check(
1697             extraArguments = arrayOf(ARG_EXCLUDE_ANNOTATIONS),
1698             sourceFiles = arrayOf(
1699                 java(
1700                     """
1701                     package test.pkg;
1702                     public @interface Foo {
1703                         String value();
1704                     }
1705                     """
1706                 ),
1707                 java(
1708                     """
1709                     package android.annotation;
1710                     import static java.lang.annotation.ElementType.*;
1711                     import java.lang.annotation.*;
1712                     @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
1713                     @Retention(RetentionPolicy.CLASS)
1714                     @SuppressWarnings("ALL")
1715                     public @interface SuppressLint {
1716                         String[] value();
1717                     }
1718                     """
1719                 ),
1720                 kotlin(
1721                     """
1722                     package test.pkg
1723 
1724                     @DslMarker
1725                     annotation class ImplicitRuntimeRetention
1726 
1727                     @Retention(AnnotationRetention.RUNTIME)
1728                     annotation class ExplicitRuntimeRetention {
1729                     }
1730                     """.trimIndent()
1731                 )
1732             ),
1733             format = FileFormat.V3,
1734             api = """
1735             // Signature format: 3.0
1736             package android.annotation {
1737               @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface SuppressLint {
1738                 method public abstract String[] value();
1739               }
1740             }
1741             package test.pkg {
1742               @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) public @interface ExplicitRuntimeRetention {
1743               }
1744               @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface Foo {
1745                 method public abstract String value();
1746               }
1747               @kotlin.DslMarker public @interface ImplicitRuntimeRetention {
1748               }
1749             }
1750             """.trimIndent(),
1751             compatibilityMode = true,
1752             stubs = arrayOf(
1753                 // For annotations where the java.lang.annotation classes themselves are not
1754                 // part of the source tree, ensure that we compute the right retention (runtime, meaning
1755                 // it should show up in the stubs file.).
1756                 """
1757                 package test.pkg;
1758                 @SuppressWarnings({"unchecked", "deprecation", "all"})
1759                 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
1760                 public @interface Foo {
1761                 public java.lang.String value();
1762                 }
1763                 """,
1764                 """
1765                 package android.annotation;
1766                 @SuppressWarnings({"unchecked", "deprecation", "all"})
1767                 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
1768                 @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE})
1769                 public @interface SuppressLint {
1770                 public java.lang.String[] value();
1771                 }
1772                 """
1773             )
1774         )
1775     }
1776 
1777     @Test
Superclass signature extractionnull1778     fun `Superclass signature extraction`() {
1779         // Make sure superclass statement is correct; inherited method from parent that has same
1780         // signature isn't included in the child
1781         check(
1782             sourceFiles = arrayOf(
1783                 java(
1784                     """
1785                     package test.pkg;
1786                     @SuppressWarnings("ALL")
1787                     public class Foo extends Super {
1788                         @Override public void base() { }
1789                         public void child() { }
1790                     }
1791                     """
1792                 ),
1793                 java(
1794                     """
1795                     package test.pkg;
1796                     @SuppressWarnings("ALL")
1797                     public class Super {
1798                         public void base() { }
1799                     }
1800                     """
1801                 )
1802             ),
1803             api = """
1804                 package test.pkg {
1805                   public class Foo extends test.pkg.Super {
1806                     ctor public Foo();
1807                     method public void child();
1808                   }
1809                   public class Super {
1810                     ctor public Super();
1811                     method public void base();
1812                   }
1813                 }
1814                 """
1815         )
1816     }
1817 
1818     @Test
Extract fields with types and initial valuesnull1819     fun `Extract fields with types and initial values`() {
1820         check(
1821             sourceFiles = arrayOf(
1822                 java(
1823                     """
1824                     package test.pkg;
1825                     @SuppressWarnings("ALL")
1826                     public class Foo {
1827                         private int hidden = 1;
1828                         int hidden2 = 2;
1829                         /** @hide */
1830                         int hidden3 = 3;
1831 
1832                         protected int field00; // No value
1833                         public static final boolean field01 = true;
1834                         public static final int field02 = 42;
1835                         public static final long field03 = 42L;
1836                         public static final short field04 = 5;
1837                         public static final byte field05 = 5;
1838                         public static final char field06 = 'c';
1839                         public static final float field07 = 98.5f;
1840                         public static final double field08 = 98.5;
1841                         public static final String field09 = "String with \"escapes\" and \u00a9...";
1842                         public static final double field10 = Double.NaN;
1843                         public static final double field11 = Double.POSITIVE_INFINITY;
1844 
1845                         public static final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
1846                         public static final char HEX_INPUT = 61184;
1847                     }
1848                     """
1849                 )
1850             ),
1851             api = """
1852                 package test.pkg {
1853                   public class Foo {
1854                     ctor public Foo();
1855                     field public static final java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
1856                     field public static final char HEX_INPUT = 61184; // 0xef00 '\uef00'
1857                     field protected int field00;
1858                     field public static final boolean field01 = true;
1859                     field public static final int field02 = 42; // 0x2a
1860                     field public static final long field03 = 42L; // 0x2aL
1861                     field public static final short field04 = 5; // 0x5
1862                     field public static final byte field05 = 5; // 0x5
1863                     field public static final char field06 = 99; // 0x0063 'c'
1864                     field public static final float field07 = 98.5f;
1865                     field public static final double field08 = 98.5;
1866                     field public static final java.lang.String field09 = "String with \"escapes\" and \u00a9...";
1867                     field public static final double field10 = (0.0/0.0);
1868                     field public static final double field11 = (1.0/0.0);
1869                   }
1870                 }
1871                 """
1872         )
1873     }
1874 
1875     @Test
Check all modifiersnull1876     fun `Check all modifiers`() {
1877         // Include as many modifiers as possible to see which ones are included
1878         // in the signature files, and the expected sorting order.
1879         // Note that the signature files treat "deprecated" as a fake modifier.
1880         // Note also how the "protected" modifier on the interface method gets
1881         // promoted to public.
1882         check(
1883             sourceFiles = arrayOf(
1884                 java(
1885                     """
1886                     package test.pkg;
1887 
1888                     @SuppressWarnings("ALL")
1889                     public abstract class Foo {
1890                         @Deprecated private static final long field1 = 5;
1891                         @Deprecated private static volatile long field2 = 5;
1892                         @Deprecated public static strictfp final synchronized void method1() { }
1893                         @Deprecated public static final synchronized native void method2();
1894                         @Deprecated protected static final class Inner1 { }
1895                         @Deprecated protected static abstract  class Inner2 { }
1896                         @Deprecated protected interface Inner3 {
1897                             default void method3() { }
1898                             static void method4(final int arg) { }
1899                         }
1900                     }
1901                     """
1902                 )
1903             ),
1904 
1905             expectedIssues = """
1906                 src/test/pkg/Foo.java:7: error: Method test.pkg.Foo.method1(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
1907                 src/test/pkg/Foo.java:8: error: Method test.pkg.Foo.method2(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
1908                 src/test/pkg/Foo.java:9: error: Class test.pkg.Foo.Inner1: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
1909                 src/test/pkg/Foo.java:10: error: Class test.pkg.Foo.Inner2: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
1910                 src/test/pkg/Foo.java:11: error: Class test.pkg.Foo.Inner3: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
1911                 """,
1912 
1913             api = """
1914                     package test.pkg {
1915                       public abstract class Foo {
1916                         ctor public Foo();
1917                         method public static final deprecated synchronized void method1();
1918                         method public static final deprecated synchronized void method2();
1919                       }
1920                       protected static final deprecated class Foo.Inner1 {
1921                         ctor protected Foo.Inner1();
1922                       }
1923                       protected static abstract deprecated class Foo.Inner2 {
1924                         ctor protected Foo.Inner2();
1925                       }
1926                       protected static abstract deprecated interface Foo.Inner3 {
1927                         method public default void method3();
1928                         method public static void method4(int);
1929                       }
1930                     }
1931                 """
1932         )
1933     }
1934 
1935     @Test
Warn about findViewByIdnull1936     fun `Warn about findViewById`() {
1937         // Include as many modifiers as possible to see which ones are included
1938         // in the signature files, and the expected sorting order.
1939         // Note that the signature files treat "deprecated" as a fake modifier.
1940         // Note also how the "protected" modifier on the interface method gets
1941         // promoted to public.
1942         check(
1943             compatibilityMode = false,
1944             outputKotlinStyleNulls = false,
1945             sourceFiles = arrayOf(
1946                 java(
1947                     """
1948                     package test.pkg;
1949                     import android.annotation.Nullable;
1950 
1951                     @SuppressWarnings("ALL")
1952                     public abstract class Foo {
1953                         @Nullable public String findViewById(int id) { return ""; }
1954                     }
1955                     """
1956                 ),
1957                 nullableSource
1958             ),
1959 
1960             expectedIssues = """
1961                 src/test/pkg/Foo.java:6: warning: method test.pkg.Foo.findViewById(int) should not be annotated @Nullable; it should be left unspecified to make it a platform type [ExpectedPlatformType]
1962                 """,
1963             extraArguments = arrayOf(ARG_WARNING, "ExpectedPlatformType"),
1964             api = """
1965                 package test.pkg {
1966                   public abstract class Foo {
1967                     ctor public Foo();
1968                     method public String findViewById(int);
1969                   }
1970                 }
1971                 """
1972         )
1973     }
1974 
1975     @Test
Check all modifiers, non-compat modenull1976     fun `Check all modifiers, non-compat mode`() {
1977         // Like testModifiers but turns off compat mode, such that we have
1978         // a modifier order more in line with standard code conventions
1979         check(
1980             compatibilityMode = false,
1981             sourceFiles = arrayOf(
1982                 java(
1983                     """
1984                     package test.pkg;
1985 
1986                     @SuppressWarnings("ALL")
1987                     public abstract class Foo {
1988                         @Deprecated private static final long field1 = 5;
1989                         @Deprecated private static volatile long field2 = 5;
1990                         /** @deprecated */ @Deprecated public static strictfp final synchronized void method1() { }
1991                         /** @deprecated */ @Deprecated public static final synchronized native void method2();
1992                         /** @deprecated */ @Deprecated protected static final class Inner1 { }
1993                         /** @deprecated */ @Deprecated protected static abstract class Inner2 { }
1994                         /** @deprecated */ @Deprecated protected interface Inner3 {
1995                             protected default void method3() { }
1996                             static void method4(final int arg) { }
1997                         }
1998                     }
1999                     """
2000                 )
2001             ),
2002             api = """
2003                 package test.pkg {
2004                   public abstract class Foo {
2005                     ctor public Foo();
2006                     method @Deprecated public static final void method1();
2007                     method @Deprecated public static final void method2();
2008                   }
2009                   @Deprecated protected static final class Foo.Inner1 {
2010                     ctor @Deprecated protected Foo.Inner1();
2011                   }
2012                   @Deprecated protected abstract static class Foo.Inner2 {
2013                     ctor @Deprecated protected Foo.Inner2();
2014                   }
2015                   @Deprecated protected static interface Foo.Inner3 {
2016                     method @Deprecated public default void method3();
2017                     method @Deprecated public static void method4(int);
2018                   }
2019                 }
2020                 """
2021         )
2022     }
2023 
2024     @Test
Package with only hidden classes should be removed from signature filesnull2025     fun `Package with only hidden classes should be removed from signature files`() {
2026         // Checks that if we have packages that are hidden, or contain only hidden or doconly
2027         // classes, the entire package is omitted from the signature file. Note how the test.pkg1.sub
2028         // package is not marked @hide, but doclava now treats subpackages of a hidden package
2029         // as also hidden.
2030         check(
2031             sourceFiles = arrayOf(
2032                 java(
2033                     """
2034                     ${"/** @hide hidden package */" /* avoid dangling javadoc warning */}
2035                     package test.pkg1;
2036                     """
2037                 ),
2038                 java(
2039                     """
2040                     package test.pkg1;
2041                     @SuppressWarnings("ALL")
2042                     public class Foo {
2043                         // Hidden by package hide
2044                     }
2045                     """
2046                 ),
2047                 java(
2048                     """
2049                     package test.pkg2;
2050                     /** @hide hidden class in this package */
2051                     @SuppressWarnings("ALL")
2052                     public class Bar {
2053                     }
2054                     """
2055                 ),
2056                 java(
2057                     """
2058                     package test.pkg2;
2059                     /** @doconly hidden class in this package */
2060                     @SuppressWarnings("ALL")
2061                     public class Baz {
2062                     }
2063                     """
2064                 ),
2065                 java(
2066                     """
2067                     package test.pkg1.sub;
2068                     // Hidden by @hide in package above
2069                     @SuppressWarnings("ALL")
2070                     public class Test {
2071                     }
2072                     """
2073                 ),
2074                 java(
2075                     """
2076                     package test.pkg3;
2077                     // The only really visible class
2078                     @SuppressWarnings("ALL")
2079                     public class Boo {
2080                     }
2081                     """
2082                 )
2083             ),
2084             api = """
2085                 package test.pkg3 {
2086                   public class Boo {
2087                     ctor public Boo();
2088                   }
2089                 }
2090                 """
2091         )
2092     }
2093 
2094     @Test
Enums can be abstractnull2095     fun `Enums can be abstract`() {
2096         // As per https://bugs.openjdk.java.net/browse/JDK-6287639
2097         // abstract methods in enums should not be listed as abstract,
2098         // but doclava1 does, so replicate this.
2099         // Also checks that we handle both enum fields and regular fields
2100         // and that they are listed separately.
2101 
2102         check(
2103             sourceFiles = arrayOf(
2104                 java(
2105                     """
2106                     package test.pkg;
2107 
2108                     @SuppressWarnings("ALL")
2109                     public enum FooBar {
2110                         ABC {
2111                             @Override
2112                             protected void foo() { }
2113                         }, DEF {
2114                             @Override
2115                             protected void foo() { }
2116                         };
2117 
2118                         protected abstract void foo();
2119                         public static int field1 = 1;
2120                         public int field2 = 2;
2121                     }
2122                     """
2123                 )
2124             ),
2125             api = """
2126                 package test.pkg {
2127                   public class FooBar extends java.lang.Enum {
2128                     method protected abstract void foo();
2129                     method public static test.pkg.FooBar valueOf(java.lang.String) throws java.lang.IllegalArgumentException;
2130                     method public static final test.pkg.FooBar[] values();
2131                     enum_constant public static final test.pkg.FooBar ABC;
2132                     enum_constant public static final test.pkg.FooBar DEF;
2133                     field public static int field1;
2134                     field public int field2;
2135                   }
2136                 }
2137             """
2138         )
2139     }
2140 
2141     @Test
Check erasure in throws-listnull2142     fun `Check erasure in throws-list`() {
2143         // Makes sure that when we have a generic signature in the throws list we take
2144         // the erasure instead (in compat mode); "Throwable" instead of "X" in the below
2145         // test. Real world example: Optional.orElseThrow.
2146         check(
2147             compatibilityMode = true,
2148             sourceFiles = arrayOf(
2149                 java(
2150                     """
2151                     package test.pkg;
2152 
2153                     import java.util.function.Supplier;
2154 
2155                     @SuppressWarnings("ALL")
2156                     public final class Test<T> {
2157                         public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
2158                             return null;
2159                         }
2160                     }
2161                     """
2162                 )
2163             ),
2164             api = """
2165                 package test.pkg {
2166                   public final class Test<T> {
2167                     ctor public Test();
2168                     method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws java.lang.Throwable;
2169                   }
2170                 }
2171                 """
2172         )
2173     }
2174 
2175     @Test
Check various generics signature subtletiesnull2176     fun `Check various generics signature subtleties`() {
2177         // Some additional declarations where PSI default type handling diffs from doclava1
2178         check(
2179             sourceFiles = arrayOf(
2180                 java(
2181                     """
2182                     package test.pkg;
2183 
2184                     @SuppressWarnings("ALL")
2185                     public abstract class Collections {
2186                         public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T> collection) {
2187                             return null;
2188                         }
2189                         public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T t);
2190                         public final class Range<T extends java.lang.Comparable<? super T>> { }
2191                     }
2192                     """
2193                 ),
2194                 java(
2195                     """
2196                     package test.pkg;
2197 
2198                     import java.util.Set;
2199 
2200                     @SuppressWarnings("ALL")
2201                     public class MoreAsserts {
2202                         public static void assertEquals(String arg0, Set<? extends Object> arg1, Set<? extends Object> arg2) { }
2203                         public static void assertEquals(Set<? extends Object> arg1, Set<? extends Object> arg2) { }
2204                     }
2205 
2206                     """
2207                 )
2208             ),
2209 
2210             // This is the output from doclava1; I'm not quite matching this yet (sorting order differs,
2211             // and my heuristic to remove "extends java.lang.Object" is somehow preserved here. I'm
2212             // not clear on when they do it and when they don't.
2213             /*
2214             api = """
2215             package test.pkg {
2216               public abstract class Collections {
2217                 ctor public Collections();
2218                 method public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T);
2219                 method public static <T & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
2220               }
2221               public final class Collections.Range<T extends java.lang.Comparable<? super T>> {
2222                 ctor public Collections.Range();
2223               }
2224               public class MoreAsserts {
2225                 ctor public MoreAsserts();
2226                 method public static void assertEquals(java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
2227                 method public static void assertEquals(java.lang.String, java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
2228               }
2229             }
2230             """,
2231             */
2232             api = """
2233                 package test.pkg {
2234                   public abstract class Collections {
2235                     ctor public Collections();
2236                     method public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T);
2237                     method public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
2238                   }
2239                   public final class Collections.Range<T extends java.lang.Comparable<? super T>> {
2240                     ctor public Collections.Range();
2241                   }
2242                   public class MoreAsserts {
2243                     ctor public MoreAsserts();
2244                     method public static void assertEquals(java.lang.String, java.util.Set<?>, java.util.Set<?>);
2245                     method public static void assertEquals(java.util.Set<?>, java.util.Set<?>);
2246                   }
2247                 }
2248                 """
2249         )
2250     }
2251 
2252     @Test
Check instance methods in enumsnull2253     fun `Check instance methods in enums`() {
2254         // Make sure that when we have instance methods in an enum they're handled
2255         // correctly (there's some special casing around enums to insert extra methods
2256         // that was broken, as exposed by ChronoUnit#toString)
2257         check(
2258             sourceFiles = arrayOf(
2259                 java(
2260                     """
2261                     package test.pkg;
2262 
2263                     @SuppressWarnings("ALL")
2264                     public interface TempUnit {
2265                         @Override
2266                         String toString();
2267                     }
2268                      """
2269                 ),
2270                 java(
2271                     """
2272                     package test.pkg;
2273 
2274                     @SuppressWarnings("ALL")
2275                     public enum ChronUnit implements TempUnit {
2276                         C, B, A;
2277 
2278                         public String valueOf(int x) {
2279                             return Integer.toString(x + 5);
2280                         }
2281 
2282                         public String values(String separator) {
2283                             return null;
2284                         }
2285 
2286                         @Override
2287                         public String toString() {
2288                             return name();
2289                         }
2290                     }
2291                 """
2292                 )
2293             ),
2294             importedPackages = emptyList(),
2295             api = """
2296                 package test.pkg {
2297                   public final class ChronUnit extends java.lang.Enum implements test.pkg.TempUnit {
2298                     method public java.lang.String valueOf(int);
2299                     method public static test.pkg.ChronUnit valueOf(java.lang.String) throws java.lang.IllegalArgumentException;
2300                     method public final java.lang.String values(java.lang.String);
2301                     method public static final test.pkg.ChronUnit[] values();
2302                     enum_constant public static final test.pkg.ChronUnit A;
2303                     enum_constant public static final test.pkg.ChronUnit B;
2304                     enum_constant public static final test.pkg.ChronUnit C;
2305                   }
2306                   public abstract interface TempUnit {
2307                     method public abstract java.lang.String toString();
2308                   }
2309                 }
2310                 """
2311         )
2312     }
2313 
2314     @Test
Mixing enums and fieldsnull2315     fun `Mixing enums and fields`() {
2316         // Checks sorting order of enum constant values
2317         val source = """
2318             package java.nio.file.attribute {
2319               public final class AclEntryPermission extends java.lang.Enum {
2320                 method public static java.nio.file.attribute.AclEntryPermission valueOf(java.lang.String);
2321                 method public static final java.nio.file.attribute.AclEntryPermission[] values();
2322                 enum_constant public static final java.nio.file.attribute.AclEntryPermission APPEND_DATA;
2323                 enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE;
2324                 enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE_CHILD;
2325                 enum_constant public static final java.nio.file.attribute.AclEntryPermission EXECUTE;
2326                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ACL;
2327                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ATTRIBUTES;
2328                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_DATA;
2329                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_NAMED_ATTRS;
2330                 enum_constant public static final java.nio.file.attribute.AclEntryPermission SYNCHRONIZE;
2331                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ACL;
2332                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ATTRIBUTES;
2333                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_DATA;
2334                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_NAMED_ATTRS;
2335                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_OWNER;
2336                 field public static final java.nio.file.attribute.AclEntryPermission ADD_FILE;
2337                 field public static final java.nio.file.attribute.AclEntryPermission ADD_SUBDIRECTORY;
2338                 field public static final java.nio.file.attribute.AclEntryPermission LIST_DIRECTORY;
2339               }
2340             }
2341                     """
2342         check(
2343             signatureSource = source,
2344             api = source
2345         )
2346     }
2347 
2348     @Test
Superclass filtering, should skip intermediate hidden classesnull2349     fun `Superclass filtering, should skip intermediate hidden classes`() {
2350         check(
2351             sourceFiles = arrayOf(
2352                 java(
2353                     """
2354                     package test.pkg;
2355                     @SuppressWarnings("ALL")
2356                     public class MyClass extends HiddenParent {
2357                         public void method4() { }
2358                     }
2359                     """
2360                 ),
2361                 java(
2362                     """
2363                     package test.pkg;
2364                     /** @hide */
2365                     @SuppressWarnings("ALL")
2366                     public class HiddenParent extends HiddenParent2 {
2367                         public static final String CONSTANT = "MyConstant";
2368                         protected int mContext;
2369                         public void method3() { }
2370                     }
2371                     """
2372                 ),
2373                 java(
2374                     """
2375                     package test.pkg;
2376                     /** @hide */
2377                     @SuppressWarnings("ALL")
2378                     public class HiddenParent2 extends PublicParent {
2379                         public void method2() { }
2380                     }
2381                     """
2382                 ),
2383                 java(
2384                     """
2385                     package test.pkg;
2386                     @SuppressWarnings("ALL")
2387                     public class PublicParent {
2388                         public void method1() { }
2389                     }
2390                     """
2391                 )
2392             ),
2393             // Notice how the intermediate methods (method2, method3) have been removed
2394             includeStrippedSuperclassWarnings = true,
2395             expectedIssues = "src/test/pkg/MyClass.java:2: warning: Public class test.pkg.MyClass stripped of unavailable superclass test.pkg.HiddenParent [HiddenSuperclass]",
2396             api = """
2397                 package test.pkg {
2398                   public class MyClass extends test.pkg.PublicParent {
2399                     ctor public MyClass();
2400                     method public void method4();
2401                   }
2402                   public class PublicParent {
2403                     ctor public PublicParent();
2404                     method public void method1();
2405                   }
2406                 }
2407                 """
2408         )
2409     }
2410 
2411     @Test
Inheriting from package private classes, package private class should be includednull2412     fun `Inheriting from package private classes, package private class should be included`() {
2413         check(
2414             compatibilityMode = false,
2415             sourceFiles = arrayOf(
2416                 java(
2417                     """
2418                     package test.pkg;
2419                     @SuppressWarnings("ALL")
2420                     public class MyClass extends HiddenParent {
2421                         public void method1() { }
2422                     }
2423                     """
2424                 ),
2425                 java(
2426                     """
2427                     package test.pkg;
2428                     @SuppressWarnings("ALL")
2429                     class HiddenParent {
2430                         public static final String CONSTANT = "MyConstant";
2431                         protected int mContext;
2432                         public void method2() { }
2433                     }
2434                     """
2435                 )
2436             ),
2437             expectedIssues = "",
2438             api = """
2439                     package test.pkg {
2440                       public class MyClass {
2441                         ctor public MyClass();
2442                         method public void method1();
2443                         method public void method2();
2444                         field public static final String CONSTANT = "MyConstant";
2445                       }
2446                     }
2447             """
2448         )
2449     }
2450 
2451     @Test
Inheriting generic method from package private classnull2452     fun `Inheriting generic method from package private class`() {
2453         check(
2454             compatibilityMode = false,
2455             sourceFiles = arrayOf(
2456                 java(
2457                     """
2458                     package test.pkg;
2459                     @SuppressWarnings("ALL")
2460                     public class MyClass extends HiddenParent {
2461                         public void method1() { }
2462                     }
2463                     """
2464                 ),
2465                 java(
2466                     """
2467                     package test.pkg;
2468                     @SuppressWarnings("ALL")
2469                     class HiddenParent {
2470                         public <T> T method2(T t) { }
2471                         public String method3(String s) { }
2472                     }
2473                     """
2474                 )
2475             ),
2476             expectedIssues = "",
2477             api = """
2478                     package test.pkg {
2479                       public class MyClass {
2480                         ctor public MyClass();
2481                         method public void method1();
2482                         method public <T> T method2(T);
2483                         method public String method3(String);
2484                       }
2485                     }
2486             """
2487         )
2488     }
2489 
2490     @Test
Type substitution for generic method referencing parent type parameternull2491     fun `Type substitution for generic method referencing parent type parameter`() {
2492         // Type parameters from parent classes need to be replaced with their bounds in the child.
2493         check(
2494             compatibilityMode = false,
2495             sourceFiles = arrayOf(
2496                 java(
2497                     """
2498                     package test.pkg;
2499                     @SuppressWarnings("ALL")
2500                     public class MyClass extends HiddenParent<String> {
2501                         public void method1() { }
2502                     }
2503                     """
2504                 ),
2505                 java(
2506                     """
2507                     package test.pkg;
2508                     @SuppressWarnings("ALL")
2509                     class HiddenParent<T> {
2510                         public T method2(T t) { }
2511                     }
2512                     """
2513                 )
2514             ),
2515             expectedIssues = "",
2516             api = """
2517                     package test.pkg {
2518                       public class MyClass {
2519                         ctor public MyClass();
2520                         method public void method1();
2521                         method public String method2(String);
2522                       }
2523                     }
2524             """
2525         )
2526     }
2527 
2528     @Test
Using compatibility flag manuallynull2529     fun `Using compatibility flag manually`() {
2530         // Like previous test, but using compatibility mode and explicitly turning on
2531         // the hidden super class compatibility flag. This test is mostly intended
2532         // to test the flag handling for individual compatibility flags.
2533         check(
2534             compatibilityMode = true,
2535             extraArguments = arrayOf("--skip-inherited-methods=false"),
2536             sourceFiles = arrayOf(
2537                 java(
2538                     """
2539                     package test.pkg;
2540                     @SuppressWarnings("ALL")
2541                     public class MyClass extends HiddenParent {
2542                         public void method1() { }
2543                     }
2544                     """
2545                 ),
2546                 java(
2547                     """
2548                     package test.pkg;
2549                     @SuppressWarnings("ALL")
2550                     class HiddenParent {
2551                         public static final String CONSTANT = "MyConstant";
2552                         protected int mContext;
2553                         public void method2() { }
2554                     }
2555                     """
2556                 )
2557             ),
2558             expectedIssues = "",
2559             api = """
2560                     package test.pkg {
2561                       public class MyClass {
2562                         ctor public MyClass();
2563                         method public void method1();
2564                         method public void method2();
2565                       }
2566                     }
2567             """
2568         )
2569     }
2570 
2571     @Test
When implementing rather than extending package private class, inline members insteadnull2572     fun `When implementing rather than extending package private class, inline members instead`() {
2573         // If you implement a package private interface, we just remove it and inline the members into
2574         // the subclass
2575         check(
2576             compatibilityMode = true,
2577             sourceFiles = arrayOf(
2578                 java(
2579                     """
2580                     package test.pkg;
2581                     public class MyClass implements HiddenInterface {
2582                         @Override public void method() { }
2583                         @Override public void other() { }
2584                     }
2585                     """
2586                 ),
2587                 java(
2588                     """
2589                     package test.pkg;
2590                     public interface OtherInterface {
2591                         void other();
2592                     }
2593                     """
2594                 ),
2595                 java(
2596                     """
2597                     package test.pkg;
2598                     interface HiddenInterface extends OtherInterface {
2599                         void method() { }
2600                         String CONSTANT = "MyConstant";
2601                     }
2602                     """
2603                 )
2604             ),
2605             api = """
2606                 package test.pkg {
2607                   public class MyClass implements test.pkg.OtherInterface {
2608                     ctor public MyClass();
2609                     method public void method();
2610                     method public void other();
2611                     field public static final java.lang.String CONSTANT = "MyConstant";
2612                   }
2613                   public abstract interface OtherInterface {
2614                     method public abstract void other();
2615                   }
2616                 }
2617                 """
2618         )
2619     }
2620 
2621     @Test
Implementing package private class, non-compat modenull2622     fun `Implementing package private class, non-compat mode`() {
2623         // Like the previous test, but in non compat mode we correctly
2624         // include all the non-hidden public interfaces into the signature
2625 
2626         // BUG: Note that we need to implement the parent
2627         check(
2628             compatibilityMode = false,
2629             sourceFiles = arrayOf(
2630                 java(
2631                     """
2632                     package test.pkg;
2633                     public class MyClass implements HiddenInterface {
2634                         @Override public void method() { }
2635                         @Override public void other() { }
2636                     }
2637                     """
2638                 ),
2639                 java(
2640                     """
2641                     package test.pkg;
2642                     public interface OtherInterface {
2643                         void other();
2644                     }
2645                     """
2646                 ),
2647                 java(
2648                     """
2649                     package test.pkg;
2650                     interface HiddenInterface extends OtherInterface {
2651                         void method() { }
2652                         String CONSTANT = "MyConstant";
2653                     }
2654                     """
2655                 )
2656             ),
2657             api = """
2658                 package test.pkg {
2659                   public class MyClass implements test.pkg.OtherInterface {
2660                     ctor public MyClass();
2661                     method public void method();
2662                     method public void other();
2663                     field public static final String CONSTANT = "MyConstant";
2664                   }
2665                   public interface OtherInterface {
2666                     method public void other();
2667                   }
2668                 }
2669                 """
2670         )
2671     }
2672 
2673     @Test
Default modifiers should be omittednull2674     fun `Default modifiers should be omitted`() {
2675         // If signatures vary only by the "default" modifier in the interface, don't show it on the implementing
2676         // class
2677         check(
2678             sourceFiles = arrayOf(
2679                 java(
2680                     """
2681                     package test.pkg;
2682 
2683                     public class MyClass implements SuperInterface {
2684                         @Override public void method() {  }
2685                         @Override public void method2() { }
2686                     }
2687                     """
2688                 ),
2689                 java(
2690                     """
2691                     package test.pkg;
2692 
2693                     public interface SuperInterface {
2694                         void method();
2695                         default void method2() {
2696                         }
2697                     }
2698                     """
2699                 )
2700             ),
2701             api = """
2702                 package test.pkg {
2703                   public class MyClass implements test.pkg.SuperInterface {
2704                     ctor public MyClass();
2705                     method public void method();
2706                   }
2707                   public abstract interface SuperInterface {
2708                     method public abstract void method();
2709                     method public default void method2();
2710                   }
2711                 }
2712             """
2713         )
2714     }
2715 
2716     @Test
Override via different throws list should be includednull2717     fun `Override via different throws list should be included`() {
2718         // If a method overrides another but changes the throws list, the overriding
2719         // method must be listed in the subclass. This is observed for example in
2720         // AbstractCursor#finalize, which omits the throws clause from Object's finalize.
2721         check(
2722             sourceFiles = arrayOf(
2723                 java(
2724                     """
2725                     package test.pkg;
2726 
2727                     public abstract class AbstractCursor extends Parent {
2728                         @Override protected void finalize2() {  } // note: not throws Throwable!
2729                     }
2730                     """
2731                 ),
2732                 java(
2733                     """
2734                     package test.pkg;
2735 
2736                     @SuppressWarnings("RedundantThrows")
2737                     public class Parent {
2738                         protected void finalize2() throws Throwable {
2739                         }
2740                     }
2741                     """
2742                 )
2743             ),
2744             api = """
2745                 package test.pkg {
2746                   public abstract class AbstractCursor extends test.pkg.Parent {
2747                     ctor public AbstractCursor();
2748                     method protected void finalize2();
2749                   }
2750                   public class Parent {
2751                     ctor public Parent();
2752                     method protected void finalize2() throws java.lang.Throwable;
2753                   }
2754                 }
2755             """
2756         )
2757     }
2758 
2759     @Test
Implementing interface methodnull2760     fun `Implementing interface method`() {
2761         // If you have a public method that implements an interface method,
2762         // they'll vary in the "abstract" modifier, but it shouldn't be listed on the
2763         // class. This is an issue for example for the ZonedDateTime#getLong method
2764         // implementing the TemporalAccessor#getLong method
2765         check(
2766             sourceFiles = arrayOf(
2767                 java(
2768                     """
2769                     package test.pkg;
2770                     public interface SomeInterface2 {
2771                         @Override default long getLong() {
2772                             return 42;
2773                         }
2774                     }
2775                     """
2776                 ),
2777                 java(
2778                     """
2779                     package test.pkg;
2780                     public class Foo implements SomeInterface2 {
2781                         @Override
2782                         public long getLong() { return 0L; }
2783                     }
2784                     """
2785                 )
2786             ),
2787             api = """
2788             package test.pkg {
2789               public class Foo implements test.pkg.SomeInterface2 {
2790                 ctor public Foo();
2791               }
2792               public abstract interface SomeInterface2 {
2793                 method public default long getLong();
2794               }
2795             }
2796         """
2797         )
2798     }
2799 
2800     @Test
Implementing interface method 2null2801     fun `Implementing interface method 2`() {
2802         check(
2803             sourceFiles = arrayOf(
2804                 java(
2805                     """
2806                     package test.pkg;
2807                     public interface SomeInterface {
2808                         long getLong();
2809                     }
2810                     """
2811                 ),
2812                 java(
2813                     """
2814                     package test.pkg;
2815                     public interface SomeInterface2 {
2816                         @Override default long getLong() {
2817                             return 42;
2818                         }
2819                     }
2820                     """
2821                 ),
2822                 java(
2823                     """
2824                     package test.pkg;
2825                     public class Foo implements SomeInterface, SomeInterface2 {
2826                         @Override
2827                         public long getLong() { return 0L; }
2828                     }
2829                     """
2830                 )
2831             ),
2832             api = """
2833                 package test.pkg {
2834                   public class Foo implements test.pkg.SomeInterface test.pkg.SomeInterface2 {
2835                     ctor public Foo();
2836                   }
2837                   public abstract interface SomeInterface {
2838                     method public abstract long getLong();
2839                   }
2840                   public abstract interface SomeInterface2 {
2841                     method public default long getLong();
2842                   }
2843                 }
2844                 """
2845         )
2846     }
2847 
2848     @Test
Check basic @remove scenariosnull2849     fun `Check basic @remove scenarios`() {
2850         // Test basic @remove handling for methods and fields
2851         check(
2852             sourceFiles = arrayOf(
2853                 java(
2854                     """
2855                     package test.pkg;
2856                     @SuppressWarnings("JavaDoc")
2857                     public class Bar {
2858                         /** @removed */
2859                         public Bar() { }
2860                         public int field;
2861                         public void test() { }
2862                         /** @removed */
2863                         public int removedField;
2864                         /** @removed */
2865                         public void removedMethod() { }
2866                         /** @removed and @hide - should not be listed */
2867                         public int hiddenField;
2868 
2869                         /** @removed */
2870                         public class Inner { }
2871 
2872                         public class Inner2 {
2873                             public class Inner3 {
2874                                 /** @removed */
2875                                 public class Inner4 { }
2876                             }
2877                         }
2878 
2879                         public class Inner5 {
2880                             public class Inner6 {
2881                                 public class Inner7 {
2882                                     /** @removed */
2883                                     public int removed;
2884                                 }
2885                             }
2886                         }
2887                     }
2888                     """
2889                 )
2890             ),
2891             removedApi = """
2892                 package test.pkg {
2893                   public class Bar {
2894                     ctor public Bar();
2895                     method public void removedMethod();
2896                     field public int removedField;
2897                   }
2898                   public class Bar.Inner {
2899                     ctor public Bar.Inner();
2900                   }
2901                   public class Bar.Inner2.Inner3.Inner4 {
2902                     ctor public Bar.Inner2.Inner3.Inner4();
2903                   }
2904                   public class Bar.Inner5.Inner6.Inner7 {
2905                     field public int removed;
2906                   }
2907                 }
2908                 """,
2909             removedDexApi = "" +
2910                 "Ltest/pkg/Bar;-><init>()V\n" +
2911                 "Ltest/pkg/Bar;->removedMethod()V\n" +
2912                 "Ltest/pkg/Bar;->removedField:I\n" +
2913                 "Ltest/pkg/Bar\$Inner;\n" +
2914                 "Ltest/pkg/Bar\$Inner;-><init>()V\n" +
2915                 "Ltest/pkg/Bar\$Inner2\$Inner3\$Inner4;\n" +
2916                 "Ltest/pkg/Bar\$Inner2\$Inner3\$Inner4;-><init>()V\n" +
2917                 "Ltest/pkg/Bar\$Inner5\$Inner6\$Inner7;->removed:I"
2918         )
2919     }
2920 
2921     @Test
Check @remove classnull2922     fun `Check @remove class`() {
2923         // Test removing classes
2924         check(
2925             sourceFiles = arrayOf(
2926                 java(
2927                     """
2928                     package test.pkg;
2929                     /** @removed */
2930                     @SuppressWarnings("JavaDoc")
2931                     public class Foo {
2932                         public void foo() { }
2933                         public class Inner {
2934                         }
2935                     }
2936                     """
2937                 ),
2938                 java(
2939                     """
2940                     package test.pkg;
2941                     @SuppressWarnings("JavaDoc")
2942                     public class Bar implements Parcelable {
2943                         public int field;
2944                         public void method();
2945 
2946                         /** @removed */
2947                         public int removedField;
2948                         /** @removed */
2949                         public void removedMethod() { }
2950 
2951                         public class Inner1 {
2952                         }
2953                         /** @removed */
2954                         public class Inner2 {
2955                         }
2956                     }
2957                     """
2958                 ),
2959                 java(
2960                     """
2961                     package test.pkg;
2962                     @SuppressWarnings("ALL")
2963                     public interface Parcelable {
2964                         void method();
2965                     }
2966                     """
2967                 )
2968             ),
2969             /*
2970             I expected this: but doclava1 doesn't do that (and we now match its behavior)
2971             package test.pkg {
2972               public class Bar {
2973                 method public void removedMethod();
2974                 field public int removedField;
2975               }
2976               public class Bar.Inner2 {
2977               }
2978               public class Foo {
2979                 method public void foo();
2980               }
2981             }
2982              */
2983             removedApi = """
2984                     package test.pkg {
2985                       public class Bar implements test.pkg.Parcelable {
2986                         method public void removedMethod();
2987                         field public int removedField;
2988                       }
2989                       public class Bar.Inner2 {
2990                         ctor public Bar.Inner2();
2991                       }
2992                       public class Foo {
2993                         ctor public Foo();
2994                         method public void foo();
2995                       }
2996                       public class Foo.Inner {
2997                         ctor public Foo.Inner();
2998                       }
2999                     }
3000                 """
3001         )
3002     }
3003 
3004     @Test
Test include overridden @Deprecated even if annotated with @hidenull3005     fun `Test include overridden @Deprecated even if annotated with @hide`() {
3006         check(
3007             sourceFiles = arrayOf(
3008                 java(
3009                     """
3010                     package test.pkg;
3011                     @SuppressWarnings("JavaDoc")
3012                     public class Child extends Parent {
3013                         /**
3014                         * @deprecated
3015                         * @hide
3016                         */
3017                         @Deprecated @Override
3018                         public String toString() {
3019                             return "Child";
3020                         }
3021 
3022                         /**
3023                          * @hide
3024                          */
3025                         public void hiddenApi() {
3026                         }
3027                     }
3028                     """
3029                 ),
3030                 java(
3031                     """
3032                     package test.pkg;
3033                     public class Parent {
3034                         public String toString() {
3035                             return "Parent";
3036                         }
3037                     }
3038                     """
3039                 )
3040             ),
3041             api = """
3042                     package test.pkg {
3043                       public class Child extends test.pkg.Parent {
3044                         ctor public Child();
3045                         method public deprecated java.lang.String toString();
3046                       }
3047                       public class Parent {
3048                         ctor public Parent();
3049                       }
3050                     }
3051                     """
3052         )
3053     }
3054 
3055     @Test
Test invalid class namenull3056     fun `Test invalid class name`() {
3057         // Regression test for b/73018978
3058         check(
3059             sourceFiles = arrayOf(
3060                 kotlin(
3061                     "src/test/pkg/Foo.kt",
3062                     """
3063                     @file:JvmName("-Foo")
3064 
3065                     package test.pkg
3066 
3067                     @Suppress("unused")
3068                     inline fun String.printHelloWorld() { println("Hello World") }
3069                     """
3070                 )
3071             ),
3072             api = """
3073                 package test.pkg {
3074                   public final class -Foo {
3075                     method public static inline void printHelloWorld(java.lang.String);
3076                   }
3077                 }
3078                 """
3079         )
3080     }
3081 
3082     @Test
Indirect Field Includes from Interfacesnull3083     fun `Indirect Field Includes from Interfaces`() {
3084         // Real-world example: include ZipConstants into ZipFile and JarFile
3085         check(
3086             sourceFiles = arrayOf(
3087                 java(
3088                     """
3089                     package test.pkg1;
3090                     interface MyConstants {
3091                         long CONSTANT1 = 12345;
3092                         long CONSTANT2 = 67890;
3093                         long CONSTANT3 = 42;
3094                     }
3095                     """
3096                 ),
3097                 java(
3098                     """
3099                     package test.pkg1;
3100                     import java.io.Closeable;
3101                     @SuppressWarnings("WeakerAccess")
3102                     public class MyParent implements MyConstants, Closeable {
3103                     }
3104                     """
3105                 ),
3106                 java(
3107                     """
3108                     package test.pkg2;
3109 
3110                     import test.pkg1.MyParent;
3111                     public class MyChild extends MyParent {
3112                     }
3113                     """
3114                 )
3115 
3116             ),
3117             api = """
3118                     package test.pkg1 {
3119                       public class MyParent implements java.io.Closeable {
3120                         ctor public MyParent();
3121                         field public static final long CONSTANT1 = 12345L; // 0x3039L
3122                         field public static final long CONSTANT2 = 67890L; // 0x10932L
3123                         field public static final long CONSTANT3 = 42L; // 0x2aL
3124                       }
3125                     }
3126                     package test.pkg2 {
3127                       public class MyChild extends test.pkg1.MyParent {
3128                         ctor public MyChild();
3129                         field public static final long CONSTANT1 = 12345L; // 0x3039L
3130                         field public static final long CONSTANT2 = 67890L; // 0x10932L
3131                         field public static final long CONSTANT3 = 42L; // 0x2aL
3132                       }
3133                     }
3134                 """
3135         )
3136     }
3137 
3138     @Test
Skip interfaces from packages explicitly hidden via argumentsnull3139     fun `Skip interfaces from packages explicitly hidden via arguments`() {
3140         // Real-world example: HttpResponseCache implements OkCacheContainer but hides the only inherited method
3141         check(
3142             extraArguments = arrayOf(
3143                 ARG_HIDE_PACKAGE, "com.squareup.okhttp"
3144             ),
3145             sourceFiles = arrayOf(
3146                 java(
3147                     """
3148                     package android.net.http;
3149                     import com.squareup.okhttp.Cache;
3150                     import com.squareup.okhttp.OkCacheContainer;
3151                     import java.io.Closeable;
3152                     import java.net.ResponseCache;
3153                     @SuppressWarnings("JavaDoc")
3154                     public final class HttpResponseCache implements Closeable, OkCacheContainer {
3155                         /** @hide Needed for OkHttp integration. */
3156                         @Override
3157                         public Cache getCache() {
3158                             return delegate.getCache();
3159                         }
3160                     }
3161                     """
3162                 ),
3163                 java(
3164                     """
3165                     package com.squareup.okhttp;
3166                     public interface OkCacheContainer {
3167                       Cache getCache();
3168                     }
3169                     """
3170                 ),
3171                 java(
3172                     """
3173                     package com.squareup.okhttp;
3174                     public class Cache {
3175                     }
3176                     """
3177                 )
3178             ),
3179             api = """
3180                 package android.net.http {
3181                   public final class HttpResponseCache implements java.io.Closeable {
3182                     ctor public HttpResponseCache();
3183                   }
3184                 }
3185                 """
3186         )
3187     }
3188 
3189     @Test
Extend from multiple interfacesnull3190     fun `Extend from multiple interfaces`() {
3191         // Real-world example: XmlResourceParser
3192         check(
3193             checkCompilation = true,
3194             sourceFiles = arrayOf(
3195                 java(
3196                     """
3197                     package android.content.res;
3198                     import android.util.AttributeSet;
3199                     import org.xmlpull.v1.XmlPullParser;
3200                     import my.AutoCloseable;
3201 
3202                     @SuppressWarnings("UnnecessaryInterfaceModifier")
3203                     public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
3204                         public void close();
3205                     }
3206                     """
3207                 ),
3208                 java(
3209                     """
3210                     package android.util;
3211                     @SuppressWarnings("WeakerAccess")
3212                     public interface AttributeSet {
3213                     }
3214                     """
3215                 ),
3216                 java(
3217                     """
3218                     package my;
3219                     public interface AutoCloseable {
3220                     }
3221                     """
3222                 ),
3223                 java(
3224                     """
3225                     package org.xmlpull.v1;
3226                     @SuppressWarnings("WeakerAccess")
3227                     public interface XmlPullParser {
3228                     }
3229                     """
3230                 )
3231             ),
3232             api = """
3233                 package android.content.res {
3234                   public abstract interface XmlResourceParser implements android.util.AttributeSet my.AutoCloseable org.xmlpull.v1.XmlPullParser {
3235                     method public abstract void close();
3236                   }
3237                 }
3238                 package android.util {
3239                   public abstract interface AttributeSet {
3240                   }
3241                 }
3242                 package my {
3243                   public abstract interface AutoCloseable {
3244                   }
3245                 }
3246                 package org.xmlpull.v1 {
3247                   public abstract interface XmlPullParser {
3248                   }
3249                 }
3250                 """
3251         )
3252     }
3253 
3254     @Test
Test KDoc suppressnull3255     fun `Test KDoc suppress`() {
3256         // Basic class; also checks that default constructor is made explicit
3257         check(
3258             sourceFiles = arrayOf(
3259                 java(
3260                     """
3261                     package test.pkg;
3262                     public class Foo {
3263                         private Foo() { }
3264                         /** @suppress */
3265                         public void hidden() {
3266                         }
3267                     }
3268                     """
3269                 ),
3270                 java(
3271                     """
3272                     package test.pkg;
3273                     /**
3274                     * Some comment.
3275                     * @suppress
3276                     */
3277                     public class Hidden {
3278                         private Hidden() { }
3279                         public void hidden() {
3280                         }
3281                         public class Inner {
3282                         }
3283                     }
3284                     """
3285                 )
3286             ),
3287             api = """
3288                     package test.pkg {
3289                       public class Foo {
3290                       }
3291                     }
3292                 """
3293         )
3294     }
3295 
3296     @Test
Check skipping implicit final or deprecated overridenull3297     fun `Check skipping implicit final or deprecated override`() {
3298         // Regression test for 122358225
3299         check(
3300             compatibilityMode = false,
3301             sourceFiles = arrayOf(
3302                 java(
3303                     """
3304                     package test.pkg;
3305 
3306                     public class Parent {
3307                         public void foo1() { }
3308                         public void foo2() { }
3309                         public void foo3() { }
3310                         public void foo4() { }
3311                     }
3312                     """
3313                 ),
3314                 java(
3315                     """
3316                     package test.pkg;
3317 
3318                     public final class Child1 extends Parent {
3319                         private Child1() { }
3320                         public final void foo1() { }
3321                         public void foo2() { }
3322                     }
3323                     """
3324                 ),
3325                 java(
3326                     """
3327                     package test.pkg;
3328 
3329                     /** @deprecated */
3330                     @Deprecated
3331                     public final class Child2 extends Parent {
3332                         private Child2() { }
3333                         /** @deprecated */
3334                         @Deprecated
3335                         public void foo3() { }
3336                         public void foo4() { }
3337                     }
3338                     """
3339                 ),
3340                 java(
3341                     """
3342                     package test.pkg;
3343 
3344                     /** @deprecated */
3345                     @Deprecated
3346                     public final class Child3 extends Parent {
3347                         private Child3() { }
3348                         public final void foo1() { }
3349                         public void foo2() { }
3350                         /** @deprecated */
3351                         @Deprecated
3352                         public void foo3() { }
3353                         /** @deprecated */
3354                         @Deprecated
3355                         public final void foo4() { }
3356                     }
3357                     """
3358                 )
3359             ),
3360             api = """
3361                 package test.pkg {
3362                   public final class Child1 extends test.pkg.Parent {
3363                   }
3364                   @Deprecated public final class Child2 extends test.pkg.Parent {
3365                   }
3366                   @Deprecated public final class Child3 extends test.pkg.Parent {
3367                   }
3368                   public class Parent {
3369                     ctor public Parent();
3370                     method public void foo1();
3371                     method public void foo2();
3372                     method public void foo3();
3373                     method public void foo4();
3374                   }
3375                 }
3376                 """
3377         )
3378     }
3379 
3380     @Test
Ignore synchronized differencesnull3381     fun `Ignore synchronized differences`() {
3382         check(
3383             compatibilityMode = false,
3384             sourceFiles = arrayOf(
3385                 java(
3386                     """
3387                     package test.pkg2;
3388 
3389                     public class Parent {
3390                         public void foo1() { }
3391                         public synchronized void foo2() { }
3392                     }
3393                     """
3394                 ),
3395                 java(
3396                     """
3397                     package test.pkg2;
3398 
3399                     public class Child1 extends Parent {
3400                         private Child1() { }
3401                         public synchronized void foo1() { }
3402                         public void foo2() { }
3403                     }
3404                     """
3405                 )
3406             ),
3407             api = """
3408                 package test.pkg2 {
3409                   public class Child1 extends test.pkg2.Parent {
3410                   }
3411                   public class Parent {
3412                     ctor public Parent();
3413                     method public void foo1();
3414                     method public void foo2();
3415                   }
3416                 }
3417                 """
3418         )
3419     }
3420 
3421     @Test
Skip incorrect inheritnull3422     fun `Skip incorrect inherit`() {
3423         check(
3424             // Simulate test-mock scenario for getIContentProvider
3425             extraArguments = arrayOf("--stub-packages", "android.test.mock"),
3426             compatibilityMode = false,
3427             expectedIssues = "src/android/test/mock/MockContentProvider.java:6: warning: Public class android.test.mock.MockContentProvider stripped of unavailable superclass android.content.ContentProvider [HiddenSuperclass]",
3428             sourceFiles = arrayOf(
3429                 java(
3430                     """
3431                     package android.test.mock;
3432 
3433                     import android.content.ContentProvider;
3434                     import android.content.IContentProvider;
3435 
3436                     public abstract class MockContentProvider extends ContentProvider {
3437                         /**
3438                          * Returns IContentProvider which calls back same methods in this class.
3439                          * By overriding this class, we avoid the mechanism hidden behind ContentProvider
3440                          * (IPC, etc.)
3441                          *
3442                          * @hide
3443                          */
3444                         @Override
3445                         public final IContentProvider getIContentProvider() {
3446                             return mIContentProvider;
3447                         }
3448                     }
3449                     """
3450                 ),
3451                 java(
3452                     """
3453                     package android.content;
3454 
3455                     /** @hide */
3456                     public abstract class ContentProvider {
3457                         protected boolean isTemporary() {
3458                             return false;
3459                         }
3460 
3461                         // This is supposed to be @hide, but in turbine-combined/framework.jar included
3462                         // by java_sdk_library like test-mock, it's not; this is what the special
3463                         // flag is used to test
3464                         public IContentProvider getIContentProvider() {
3465                             return null;
3466                         }
3467                     }
3468                     """
3469                 ),
3470                 java(
3471                     """
3472                     package android.content;
3473                     import android.os.IInterface;
3474 
3475                     /**
3476                      * The ipc interface to talk to a content provider.
3477                      * @hide
3478                      */
3479                     public interface IContentProvider extends IInterface {
3480                     }
3481                     """
3482                 ),
3483                 java(
3484                     """
3485                     package android.content;
3486 
3487                     // Not hidden. Here to make sure that we respect stub-packages
3488                     // and exclude it from everything, including signatures.
3489                     public class ClipData {
3490                     }
3491                     """
3492                 )
3493             ),
3494             api = """
3495                 package android.test.mock {
3496                   public abstract class MockContentProvider {
3497                     ctor public MockContentProvider();
3498                   }
3499                 }
3500                 """
3501         )
3502     }
3503 
3504     @Test
Test Visible For Testingnull3505     fun `Test Visible For Testing`() {
3506         // Use the otherwise= visibility in signatures
3507         // Regression test for issue 118763806
3508         check(
3509             sourceFiles = arrayOf(
3510                 java(
3511                     """
3512                     package test.pkg;
3513                     import androidx.annotation.VisibleForTesting;
3514 
3515                     @SuppressWarnings({"ClassNameDiffersFromFileName", "WeakerAccess"})
3516                     public class ProductionCodeJava {
3517                         private ProductionCodeJava() { }
3518 
3519                         @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
3520                         public void shouldBeProtected() {
3521                         }
3522 
3523                         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
3524                         protected void shouldBePrivate1() {
3525                         }
3526 
3527                         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
3528                         public void shouldBePrivate2() {
3529                         }
3530 
3531                         @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
3532                         public void shouldBePackagePrivate() {
3533                         }
3534 
3535                         @VisibleForTesting(otherwise = VisibleForTesting.NONE)
3536                         public void shouldBeHidden() {
3537                         }
3538                     }
3539                     """
3540                 ).indented(),
3541                 kotlin(
3542                     """
3543                     package test.pkg
3544                     import androidx.annotation.VisibleForTesting
3545 
3546                     open class ProductionCodeKotlin private constructor() {
3547 
3548                         @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
3549                         fun shouldBeProtected() {
3550                         }
3551 
3552                         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
3553                         protected fun shouldBePrivate1() {
3554                         }
3555 
3556                         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
3557                         fun shouldBePrivate2() {
3558                         }
3559 
3560                         @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
3561                         fun shouldBePackagePrivate() {
3562                         }
3563 
3564                         @VisibleForTesting(otherwise = VisibleForTesting.NONE)
3565                         fun shouldBeHidden() {
3566                         }
3567                     }
3568                     """
3569                 ).indented(),
3570                 visibleForTestingSource
3571             ),
3572             api = """
3573                 package test.pkg {
3574                   public class ProductionCodeJava {
3575                     method protected void shouldBeProtected();
3576                   }
3577                   public class ProductionCodeKotlin {
3578                     method protected final void shouldBeProtected();
3579                   }
3580                 }
3581                 """,
3582             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
3583         )
3584     }
3585 
3586     @Test
References Deprecatednull3587     fun `References Deprecated`() {
3588         check(
3589             extraArguments = arrayOf(
3590                 ARG_ERROR, "ReferencesDeprecated",
3591                 ARG_ERROR, "ExtendsDeprecated"
3592             ),
3593             expectedIssues = """
3594             src/test/pkg/MyClass.java:3: error: Parameter of deprecated type test.pkg.DeprecatedClass in test.pkg.MyClass.method1(): this method should also be deprecated [ReferencesDeprecated]
3595             src/test/pkg/MyClass.java:4: error: Return type of deprecated type test.pkg.DeprecatedInterface in test.pkg.MyClass.method2(): this method should also be deprecated [ReferencesDeprecated]
3596             src/test/pkg/MyClass.java:4: error: Returning deprecated type test.pkg.DeprecatedInterface from test.pkg.MyClass.method2(): this method should also be deprecated [ReferencesDeprecated]
3597             src/test/pkg/MyClass.java:2: error: Extending deprecated super class class test.pkg.DeprecatedClass from test.pkg.MyClass: this class should also be deprecated [ExtendsDeprecated]
3598             src/test/pkg/MyClass.java:2: error: Implementing interface of deprecated type test.pkg.DeprecatedInterface in test.pkg.MyClass: this class should also be deprecated [ExtendsDeprecated]
3599             """,
3600             sourceFiles = arrayOf(
3601                 java(
3602                     """
3603                     package test.pkg;
3604                     /** @deprecated */
3605                     @Deprecated
3606                     public class DeprecatedClass {
3607                     }
3608                     """
3609                 ),
3610                 java(
3611                     """
3612                     package test.pkg;
3613                     /** @deprecated */
3614                     @Deprecated
3615                     public interface DeprecatedInterface {
3616                     }
3617                     """
3618                 ),
3619                 java(
3620                     """
3621                     package test.pkg;
3622                     public class MyClass extends DeprecatedClass implements DeprecatedInterface {
3623                         public void method1(DeprecatedClass p, int i) { }
3624                         public DeprecatedInterface method2(int i) { return null; }
3625 
3626                         /** @deprecated */
3627                         @Deprecated
3628                         public void method3(DeprecatedClass p, int i) { }
3629                     }
3630                     """
3631                 )
3632             )
3633         )
3634     }
3635 
3636     @Test
v3 format for qualified references in typesnull3637     fun `v3 format for qualified references in types`() {
3638         check(
3639             format = FileFormat.V3,
3640             sourceFiles = arrayOf(
3641                 java(
3642                     """
3643                     package androidx.appcompat.app;
3644                     import android.view.View;
3645                     import android.view.View.OnClickListener;
3646 
3647                     public class ActionBarDrawerToggle {
3648                         private ActionBarDrawerToggle() { }
3649                         public View.OnClickListener getToolbarNavigationClickListener1() {
3650                             return null;
3651                         }
3652                         public OnClickListener getToolbarNavigationClickListener2() {
3653                             return null;
3654                         }
3655                         public android.view.View.OnClickListener getToolbarNavigationClickListener3() {
3656                             return null;
3657                         }
3658                     }
3659                     """
3660                 )
3661             ),
3662             api = """
3663                 // Signature format: 3.0
3664                 package androidx.appcompat.app {
3665                   public class ActionBarDrawerToggle {
3666                     method public android.view.View.OnClickListener! getToolbarNavigationClickListener1();
3667                     method public android.view.View.OnClickListener! getToolbarNavigationClickListener2();
3668                     method public android.view.View.OnClickListener! getToolbarNavigationClickListener3();
3669                   }
3670                 }
3671                 """
3672         )
3673     }
3674 
3675     @Test
FooKt class constructors are not publicnull3676     fun `FooKt class constructors are not public`() {
3677         check(
3678             format = FileFormat.V3,
3679             sourceFiles = arrayOf(
3680                 kotlin("src/main/java/test/pkg/Foo.kt",
3681                     """
3682                     package test.pkg
3683                     fun myCall() : Boolean = false
3684                     class Bar
3685                     """
3686                 )
3687             ),
3688             api = """
3689                 // Signature format: 3.0
3690                 package test.pkg {
3691                   public final class Bar {
3692                     ctor public Bar();
3693                   }
3694                   public final class FooKt {
3695                     method public static boolean myCall();
3696                   }
3697                 }
3698                 """
3699         )
3700     }
3701 
3702     @Test
Test inherited hidden methods for descendant classes - Package privatenull3703     fun `Test inherited hidden methods for descendant classes - Package private`() {
3704         check(
3705             compatibilityMode = false,
3706             sourceFiles = arrayOf(
3707                 java(
3708                     """
3709                     package test.pkg;
3710                     public class Class4 extends Class3 {
3711                         public void method4() { }
3712                     }
3713                     """
3714                 ),
3715                 java(
3716                     """
3717                     package test.pkg;
3718                     public class Class3 extends Class2 {
3719                         public void method3() { }
3720                     }
3721                     """
3722                 ),
3723                 java(
3724                     """
3725                     package test.pkg;
3726                     class Class2 extends Class1 {
3727                         public void method2() { }
3728                     }
3729                     """
3730                 ),
3731                 java(
3732                     """
3733                     package test.pkg;
3734                     public class Class1 {
3735                         public void method1() { }
3736                     }
3737                     """
3738                 )
3739             ),
3740             expectedIssues = "",
3741             api =
3742             """
3743                 package test.pkg {
3744                   public class Class1 {
3745                     ctor public Class1();
3746                     method public void method1();
3747                   }
3748                   public class Class3 extends test.pkg.Class1 {
3749                     ctor public Class3();
3750                     method public void method2();
3751                     method public void method3();
3752                   }
3753                   public class Class4 extends test.pkg.Class3 {
3754                     ctor public Class4();
3755                     method public void method4();
3756                   }
3757                 }
3758                 """
3759         )
3760     }
3761 
3762     @Test
Test inherited hidden methods for descendant classes - Hidden annotationnull3763     fun `Test inherited hidden methods for descendant classes - Hidden annotation`() {
3764         check(
3765             compatibilityMode = false,
3766             sourceFiles = arrayOf(
3767                 java(
3768                     """
3769                     package test.pkg;
3770                     public class Class4 extends Class3 {
3771                         public void method4() { }
3772                     }
3773                     """
3774                 ),
3775                 java(
3776                     """
3777                     package test.pkg;
3778                     public class Class3 extends Class2 {
3779                         public void method3() { }
3780                     }
3781                     """
3782                 ),
3783                 java(
3784                     """
3785                     package test.pkg;
3786                     /** @hide */
3787                     public class Class2 extends Class1 {
3788                         public void method2() { }
3789                     }
3790                     """
3791                 ),
3792                 java(
3793                     """
3794                     package test.pkg;
3795                     public class Class1 {
3796                         public void method1() { }
3797                     }
3798                     """
3799                 )
3800             ),
3801             expectedIssues = "src/test/pkg/Class3.java:2: warning: Public class test.pkg.Class3 stripped of unavailable superclass test.pkg.Class2 [HiddenSuperclass]",
3802             api =
3803             """
3804                 package test.pkg {
3805                   public class Class1 {
3806                     ctor public Class1();
3807                     method public void method1();
3808                   }
3809                   public class Class3 extends test.pkg.Class1 {
3810                     ctor public Class3();
3811                     method public void method2();
3812                     method public void method3();
3813                   }
3814                   public class Class4 extends test.pkg.Class3 {
3815                     ctor public Class4();
3816                     method public void method4();
3817                   }
3818                 }
3819                 """
3820 
3821         )
3822     }
3823 
3824     @Test
Test inherited methods that use genericsnull3825     fun `Test inherited methods that use generics`() {
3826         check(
3827             compatibilityMode = false,
3828             sourceFiles = arrayOf(
3829                 java(
3830                     """
3831                     package test.pkg;
3832                     import androidx.annotation.NonNull;
3833                     public class Class2 extends Class1<String> {
3834                         @Override
3835                         public void method1(String input) { }
3836                         @Override
3837                         public void method2(@NonNull String input) { }
3838                     }
3839                     """
3840                 ),
3841                 java(
3842                     """
3843                     package test.pkg;
3844                     import androidx.annotation.NonNull;
3845                     class Class1<T> {
3846                         public void method1(T input) { }
3847                         public void method2(T input) { }
3848                         public void method3(T input) { }
3849                         @NonNull
3850                         public String method4(T input) { return ""; }
3851                         public T method5(@NonNull String input) { return null; }
3852                     }
3853                     """
3854                 ),
3855                 androidxNonNullSource
3856             ),
3857             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation"),
3858             expectedIssues = "",
3859             api =
3860                 """
3861                 package test.pkg {
3862                   public class Class2 {
3863                     ctor public Class2();
3864                     method public void method1(String);
3865                     method public void method2(@NonNull String);
3866                     method public void method3(String);
3867                     method @NonNull public String method4(String);
3868                     method public String method5(@NonNull String);
3869                   }
3870                 }
3871                 """
3872 
3873         )
3874     }
3875 
3876     @Test
Test merging API signature filesnull3877     fun `Test merging API signature files`() {
3878         val source1 = """
3879             package Test.pkg {
3880               public final class Class1 {
3881                 method public void method1();
3882               }
3883             }
3884             package Test.pkg1 {
3885               public final class Class1 {
3886                 method public void method1();
3887               }
3888             }
3889                     """
3890         val source2 = """
3891             package Test.pkg {
3892               public final class Class2 {
3893                 method public void method1(String);
3894               }
3895             }
3896             package Test.pkg2 {
3897               public final class Class1 {
3898                 method public void method1(String, String);
3899               }
3900             }
3901                     """
3902         val expected = """
3903             package Test.pkg {
3904               public final class Class1 {
3905                 method public void method1();
3906               }
3907               public final class Class2 {
3908                 method public void method1(java.lang.String);
3909               }
3910             }
3911             package Test.pkg1 {
3912               public final class Class1 {
3913                 method public void method1();
3914               }
3915             }
3916             package Test.pkg2 {
3917               public final class Class1 {
3918                 method public void method1(java.lang.String, java.lang.String);
3919               }
3920             }
3921                     """
3922         check(
3923             signatureSources = arrayOf(source1, source2),
3924             api = expected
3925         )
3926     }
3927 
3928     val MERGE_TEST_SOURCE_1 = """
3929             package test.pkg {
3930               public final class BaseClass {
3931                 method public void method1();
3932               }
3933             }
3934                     """
3935     val MERGE_TEST_SOURCE_2 = """
3936             package test.pkg {
3937               public final class SubClass extends test.pkg.BaseClass {
3938               }
3939             }
3940                     """
3941     val MERGE_TEST_EXPECTED = """
3942             package test.pkg {
3943               public final class BaseClass {
3944                 method public void method1();
3945               }
3946               public final class SubClass extends test.pkg.BaseClass {
3947               }
3948             }
3949             """
3950 
3951     @Test
Test merging API signature files, one refer to anothernull3952     fun `Test merging API signature files, one refer to another`() {
3953         check(
3954             signatureSources = arrayOf(MERGE_TEST_SOURCE_1, MERGE_TEST_SOURCE_2),
3955             api = MERGE_TEST_EXPECTED
3956         )
3957     }
3958 
3959     @Test
Test merging API signature files, one refer to another, in reverse ordernull3960     fun `Test merging API signature files, one refer to another, in reverse order`() {
3961         // Exactly the same as the previous test, but read them in the reverse order
3962         check(
3963             signatureSources = arrayOf(MERGE_TEST_SOURCE_2, MERGE_TEST_SOURCE_1),
3964             api = MERGE_TEST_EXPECTED
3965         )
3966     }
3967 
3968     @Test
Test merging API signature files with reverse dependencynull3969     fun `Test merging API signature files with reverse dependency`() {
3970         val source1 = """
3971             package test.pkg {
3972               public final class Class1 {
3973                 method public void method1(test.pkg.Class2 arg);
3974               }
3975             }
3976                     """
3977         val source2 = """
3978             package test.pkg {
3979               public final class Class2 {
3980               }
3981             }
3982                     """
3983         val expected = """
3984             package test.pkg {
3985               public final class Class1 {
3986                 method public void method1(test.pkg.Class2);
3987               }
3988               public final class Class2 {
3989               }
3990             }
3991                     """
3992         check(
3993             signatureSources = arrayOf(source1, source2),
3994             api = expected
3995         )
3996     }
3997 
3998     @Test
Test merging 3 API signature filesnull3999     fun `Test merging 3 API signature files`() {
4000         val source1 = """
4001             package test.pkg1 {
4002               public final class BaseClass1 {
4003                 method public void method1();
4004               }
4005 
4006               public final class AnotherSubClass extends test.pkg2.AnotherBase {
4007                 method public void method1();
4008               }
4009             }
4010                     """
4011         val source2 = """
4012             package test.pkg2 {
4013               public final class SubClass1 extends test.pkg1.BaseClass1 {
4014               }
4015             }
4016                     """
4017         val source3 = """
4018             package test.pkg2 {
4019               public final class SubClass2 extends test.pkg2.SubClass1 {
4020                 method public void bar();
4021               }
4022 
4023               public final class AnotherBase {
4024                 method public void baz();
4025               }
4026             }
4027                     """
4028         val expected = """
4029             package test.pkg1 {
4030               public final class AnotherSubClass extends test.pkg2.AnotherBase {
4031                 method public void method1();
4032               }
4033               public final class BaseClass1 {
4034                 method public void method1();
4035               }
4036             }
4037             package test.pkg2 {
4038               public final class AnotherBase {
4039                 method public void baz();
4040               }
4041               public final class SubClass1 extends test.pkg1.BaseClass1 {
4042               }
4043               public final class SubClass2 extends test.pkg2.SubClass1 {
4044                 method public void bar();
4045               }
4046             }
4047                     """
4048         check(
4049             signatureSources = arrayOf(source1, source2, source3),
4050             api = expected
4051         )
4052     }
4053 
4054     @Test
Test cannot merging API signature files with duplicate classnull4055     fun `Test cannot merging API signature files with duplicate class`() {
4056         val source1 = """
4057             package Test.pkg {
4058               public final class Class1 {
4059                 method public void method1();
4060               }
4061             }
4062                     """
4063         val source2 = """
4064             package Test.pkg {
4065               public final class Class1 {
4066                 method public void method1();
4067               }
4068             }
4069                     """
4070         check(
4071             signatureSources = arrayOf(source1, source2),
4072             expectedFail = "Aborting: Unable to parse signature file: TESTROOT/project/load-api2.txt:2: Duplicate class found: Test.pkg.Class1"
4073         )
4074     }
4075 
4076     @Test
Test cannot merging API signature files with different file formatsnull4077     fun `Test cannot merging API signature files with different file formats`() {
4078         val source1 = """
4079             // Signature format: 2.0
4080             package Test.pkg {
4081             }
4082                     """
4083         val source2 = """
4084             // Signature format: 3.0
4085             package Test.pkg {
4086             }
4087                     """
4088         check(
4089             signatureSources = arrayOf(source1, source2),
4090             expectedFail = "Aborting: Unable to parse signature file: Cannot merge different formats of signature files. " +
4091                 "First file format=V2, current file format=V3: file=TESTROOT/project/load-api2.txt"
4092         )
4093     }
4094 
4095     @Test
Test tracking of @Composable annotation from classpathnull4096     fun `Test tracking of @Composable annotation from classpath`() {
4097         check(
4098             format = FileFormat.V3,
4099             classpath = arrayOf(
4100                 /* The following source file, compiled, and root folder jar'ed and stored as base64 gzip:
4101                     package test.pkg
4102                     @MustBeDocumented
4103                     @Retention(AnnotationRetention.BINARY)
4104                     @Target(
4105                         AnnotationTarget.CLASS,
4106                         AnnotationTarget.FUNCTION,
4107                         AnnotationTarget.TYPE,
4108                         AnnotationTarget.TYPE_PARAMETER,
4109                         AnnotationTarget.PROPERTY
4110                     )
4111                     annotation class Composable
4112                  */
4113                 base64gzip(
4114                     "test.jar", "" +
4115                         "UEsDBAoAAAgIAKx6s1AAAAAAAgAAAAAAAAAJAAAATUVUQS1JTkYvAwBQSwMECgAACAgAZ3qzULJ/" +
4116                         "Au4bAAAAGQAAABQAAABNRVRBLUlORi9NQU5JRkVTVC5NRvNNzMtMSy0u0Q1LLSrOzM+zUjDUM+Dl" +
4117                         "4uUCAFBLAwQKAAAICABnerNQDArdZgwAAAAQAAAAGwAAAE1FVEEtSU5GL3RlbXAua290bGluX21v" +
4118                         "ZHVsZWNgYGBmYGBghGIBAFBLAwQKAAAICABnerNQAAAAAAIAAAAAAAAABQAAAHRlc3QvAwBQSwME" +
4119                         "CgAACAgAZ3qzUAAAAAACAAAAAAAAAAkAAAB0ZXN0L3BrZy8DAFBLAwQKAAAICABnerNQbrgjGPQB" +
4120                         "AACVAwAAGQAAAHRlc3QvcGtnL0NvbXBvc2FibGUuY2xhc3OFUk1v2kAQfWtioG6TkKRpSdI0H01I" +
4121                         "P6S65doTEEdF4kvGrRRxqBZYIQdjo+xClRu3Xvsz+ht6qFCO/VFVZ4kCVLJU2Xo7O/PGM/M8v//8" +
4122                         "/AUgjzcMW0pIZQ/7PbsUDYaR5O1ApMAYMld8zO2Ahz273r4SHZVCguFg4eVhGCmu/Ci0C3MzBZPh" +
4123                         "pNKPVOCHy5TqSKqiOI86o4EIleh+YNiPoblCUZgsiptjHowEw1kMb1FxOSNZLNcK7iXDbkyKx697" +
4124                         "QhFrjQdB9FV07xwyvt9FgXmeWaoUmk2G9MWnWskr12sMK95lw6Ev6uNLo+AWqo7nuERpuPWG43rU" +
4125                         "ylElVrJ/lDiM5yyPlvsPpREFfudmpmoscT7FcXzcCYRux7sZCi0kzfGxfs6wcS9NVSje5YpT0BiM" +
4126                         "E7Q+TEOGru3ZFRpoQ1ifXN33NNR0YllG1rCMzJ41naRvvxnZ6SRvvGPF6eT2R9LQvDzDdiVmBakM" +
4127                         "SF4lBkOG1YX/bV8xWM1odN0RF35A27HjjkiAgfjsS58Ii/8mc1QAK/SZpG6P7FczfInXdH5Hih4g" +
4128                         "TfEHAhYe4hGZqy2YAmtY15DRsKFhU8MWHlPC9l3CE6zjqTZbMASympbFDnZhYq+FRBnPZu8+nt/f" +
4129                         "Dso4xBGZOG6BSbzACYUkTiVyEmd/AVBLAQIUAwoAAAgIAKx6s1AAAAAAAgAAAAAAAAAJAAAAAAAA" +
4130                         "AAAAEADtQQAAAABNRVRBLUlORi9QSwECFAMKAAAICABnerNQsn8C7hsAAAAZAAAAFAAAAAAAAAAA" +
4131                         "AAAApIEpAAAATUVUQS1JTkYvTUFOSUZFU1QuTUZQSwECFAMKAAAICABnerNQDArdZgwAAAAQAAAA" +
4132                         "GwAAAAAAAAAAAAAAoIF2AAAATUVUQS1JTkYvdGVtcC5rb3RsaW5fbW9kdWxlUEsBAhQDCgAACAgA" +
4133                         "Z3qzUAAAAAACAAAAAAAAAAUAAAAAAAAAAAAQAOhBuwAAAHRlc3QvUEsBAhQDCgAACAgAZ3qzUAAA" +
4134                         "AAACAAAAAAAAAAkAAAAAAAAAAAAQAOhB4AAAAHRlc3QvcGtnL1BLAQIUAwoAAAgIAGd6s1BuuCMY" +
4135                         "9AEAAJUDAAAZAAAAAAAAAAAAAACggQkBAAB0ZXN0L3BrZy9Db21wb3NhYmxlLmNsYXNzUEsFBgAA" +
4136                         "AAAGAAYAcwEAADQDAAAAAA=="
4137                 )
4138             ),
4139             sourceFiles = arrayOf(
4140                 kotlin(
4141                     """
4142                     package test.pkg
4143                     class RadioGroupScope() {
4144                         @Composable
4145                         fun RadioGroupItem(
4146                             selected: Boolean,
4147                             onSelect: () -> Unit,
4148                             content: @Composable () -> Unit
4149                         ) { }
4150                     }
4151                 """
4152                 )
4153             ),
4154             expectedIssues = "",
4155             api =
4156             """
4157                 // Signature format: 3.0
4158                 package test.pkg {
4159                   public final class RadioGroupScope {
4160                     ctor public RadioGroupScope();
4161                     method @test.pkg.Composable public void RadioGroupItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelect, kotlin.jvm.functions.Function0<kotlin.Unit> content);
4162                   }
4163                 }
4164             """
4165         )
4166     }
4167 }
4168