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 org.junit.Test
22 
23 class ApiFileTest : DriverTest() {
24 /*
25    Conditions to test:
26    - test all the error scenarios found in the notStrippable case!
27    - split up test into many individual test cases
28    - try referencing a class from an annotation!
29    - test having a throws list where some exceptions are hidden but extend
30      public exceptions: do we map over to the referenced ones?
31 
32    - test type reference from all the possible places -- in type signatures - interfaces,
33      extends, throws, type bounds, etc.
34    - method which overrides @hide method: should appear in subclass (test chain
35      of two nested too)
36    - BluetoothGattCharacteristic.java#describeContents: Was marked @hide,
37      but is unhidden because it extends a public interface method
38    - package javadoc (also make sure merging both!, e.g. try having @hide in each)
39    - StopWatchMap -- inner class with @hide marks allh top levels!
40    - Test field inlining: should I include fields from an interface, if that
41      inteface was implemented by the parent class (and therefore appears there too?)
42      What if the superclass is abstract?
43    - Exposing package private classes. Test that I only do this for package private
44      classes, NOT Those marked @hide (is that, having @hide on a used type, illegal?)
45    - Test error handling (invalid @hide combinations))
46    - Consider what happens if we promote a package private class (because it's
47      extended by a public class), and then we restore its public members; the
48      override logic there isn't quite right. We've duplicated the significant-override
49      code to not skip private members, but that could change semantics. This isn't
50      ideal; instead we should now mark this class as public, and re-run the analysis
51      again (with the new hidden state for this class).
52    - compilation unit sorting - top level classes out of order
53    - Massive classes such as android.R.java? Maybe do synthetic test.
54    - HttpResponseCache implemented a public OkHttp interface, but the sole implementation
55      method was marked @hide, so the method doesn't show up. Is that some other rule --
56      that we skip interfaces if their implementation methods are marked @hide?
57    - Test recursive package filtering.
58  */
59 
60     @Test
Basic class signature extractionnull61     fun `Basic class signature extraction`() {
62         // Basic class; also checks that default constructor is made explicit
63         check(
64             sourceFiles = *arrayOf(
65                 java(
66                     """
67                     package test.pkg;
68                     public class Foo {
69                     }
70                     """
71                 )
72             ),
73             api = """
74                     package test.pkg {
75                       public class Foo {
76                         ctor public Foo();
77                       }
78                     }
79                     """
80         )
81     }
82 
83     @Test
Parameter Names in Javanull84     fun `Parameter Names in Java`() {
85         // Java code which explicitly specifies parameter names
86         check(
87             sourceFiles = *arrayOf(
88                 java(
89                     """
90                     package test.pkg;
91                     import androidx.annotation.ParameterName;
92 
93                     public class Foo {
94                         public void foo(int javaParameter1, @ParameterName("publicParameterName") int javaParameter2) {
95                         }
96                     }
97                     """
98                 ),
99                 supportParameterName
100             ),
101             api = """
102                     package test.pkg {
103                       public class Foo {
104                         ctor public Foo();
105                         method public void foo(int, int publicParameterName);
106                       }
107                     }
108                  """,
109             extraArguments = arrayOf("--hide-package", "androidx.annotation"),
110             checkDoclava1 = false /* doesn't support parameter names */
111         )
112     }
113 
114     @Test
Default Values Names in Javanull115     fun `Default Values Names in Java`() {
116         // Java code which explicitly specifies parameter names
117         check(
118             compatibilityMode = false,
119             sourceFiles = *arrayOf(
120                 java(
121                     """
122                     package test.pkg;
123                     import androidx.annotation.DefaultValue;
124 
125                     public class Foo {
126                         public void foo(
127                             @DefaultValue("null") String prefix,
128                             @DefaultValue("\"Hello World\"") String greeting,
129                             @DefaultValue("42") int meaning) {
130                         }
131                     }
132                     """
133                 ),
134                 supportDefaultValue
135             ),
136             api = """
137                 package test.pkg {
138                   public class Foo {
139                     ctor public Foo();
140                     method public void foo(String! = "null", String! = "\"Hello World\"", int = "42");
141                   }
142                 }
143                  """,
144             extraArguments = arrayOf("--hide-package", "androidx.annotation"),
145             checkDoclava1 = false /* doesn't support default Values */
146         )
147     }
148 
149     @Test
Default Values and Names in Kotlinnull150     fun `Default Values and Names in Kotlin`() {
151         // Kotlin code which explicitly specifies parameter names
152         check(
153             compatibilityMode = false,
154             sourceFiles = *arrayOf(
155                 kotlin(
156                     """
157                     package test.pkg
158 
159                     class Foo {
160                         fun error(int: Int = 42, int2: Int? = null, byte: Int = 42) { }
161                     }
162                     """
163                 )
164             ),
165             api = """
166                 package test.pkg {
167                   public final class Foo {
168                     ctor public Foo();
169                     method public void error(int p = "42", Integer? int2 = "null", int p1 = "42");
170                   }
171                 }
172                 """,
173             extraArguments = arrayOf("--hide-package", "androidx.annotation"),
174             checkDoclava1 = false /* doesn't support default Values */
175         )
176     }
177 
178     @Test
Basic Kotlin classnull179     fun `Basic Kotlin class`() {
180         check(
181             sourceFiles = *arrayOf(
182                 kotlin(
183                     """
184                     package test.pkg
185                     class Kotlin(val property1: String = "Default Value", arg2: Int) : Parent() {
186                         override fun method() = "Hello World"
187                         fun otherMethod(ok: Boolean, times: Int) {
188                         }
189 
190                         var property2: String? = null
191 
192                         private var someField = 42
193                         @JvmField
194                         var someField2 = 42
195 
196                         internal var myHiddenVar = false
197                         internal fun myHiddenMethod(): Unit { }
198                         internal data class myHiddenClass(): Unit { }
199 
200                         companion object {
201                             const val MY_CONST = 42
202                         }
203                     }
204 
205                     open class Parent {
206                         open fun method(): String? = null
207                         open fun method2(value: Boolean, value: Boolean?): String? = null
208                         open fun method3(value: Int?, value2: Int): Int = null
209                     }
210                     """
211                 )
212             ),
213             api = """
214                 package test.pkg {
215                   public final class Kotlin extends test.pkg.Parent {
216                     ctor public Kotlin(java.lang.String property1, int arg2);
217                     method public java.lang.String getProperty1();
218                     method public java.lang.String getProperty2();
219                     method public void otherMethod(boolean ok, int times);
220                     method public void setProperty2(java.lang.String p);
221                     field public static final test.pkg.Kotlin.Companion Companion;
222                     field public static final int MY_CONST = 42; // 0x2a
223                     field public int someField2;
224                   }
225                   public static final class Kotlin.Companion {
226                   }
227                   public class Parent {
228                     ctor public Parent();
229                     method public java.lang.String method();
230                     method public java.lang.String method2(boolean value, java.lang.Boolean value);
231                     method public int method3(java.lang.Integer value, int value2);
232                   }
233                 }
234                 """,
235             privateApi = """
236                 package test.pkg {
237                   public final class Kotlin extends test.pkg.Parent {
238                     method internal boolean getMyHiddenVar${"$"}lintWithKotlin();
239                     method internal void myHiddenMethod${"$"}lintWithKotlin();
240                     method internal void setMyHiddenVar${"$"}lintWithKotlin(boolean p);
241                     field internal boolean myHiddenVar;
242                     field private final java.lang.String property1;
243                     field private java.lang.String property2;
244                     field private int someField;
245                   }
246                   public static final class Kotlin.Companion {
247                     ctor private Kotlin.Companion();
248                   }
249                   internal static final class Kotlin.myHiddenClass extends kotlin.Unit {
250                     ctor public Kotlin.myHiddenClass();
251                     method internal test.pkg.Kotlin.myHiddenClass copy();
252                   }
253                 }
254                 """,
255             checkDoclava1 = false /* doesn't support Kotlin... */
256         )
257     }
258 
259     @Test
Kotlin Reified Methodsnull260     fun `Kotlin Reified Methods`() {
261         check(
262             sourceFiles = *arrayOf(
263                 java(
264                     """
265                     package test.pkg;
266 
267                     public class Context {
268                         @SuppressWarnings("unchecked")
269                         public final <T> T getSystemService(Class<T> serviceClass) {
270                             return null;
271                         }
272                     }
273                     """
274                 ),
275                 kotlin(
276                     """
277                     package test.pkg
278 
279                     inline fun <reified T> Context.systemService1() = getSystemService(T::class.java)
280                     inline fun Context.systemService2() = getSystemService(String::class.java)
281                     """
282                 )
283             ),
284             api = """
285                 package test.pkg {
286                   public class Context {
287                     ctor public Context();
288                     method public final <T> T getSystemService(java.lang.Class<T>);
289                   }
290                   public final class _java_Kt {
291                     ctor public _java_Kt();
292                     method public static java.lang.String systemService2(test.pkg.Context);
293                   }
294                 }
295                 """,
296             checkDoclava1 = false /* doesn't support Kotlin... */
297         )
298     }
299 
300     @Test
Propagate Platform types in Kotlinnull301     fun `Propagate Platform types in Kotlin`() {
302         check(
303             compatibilityMode = false,
304             outputKotlinStyleNulls = true,
305             sourceFiles = *arrayOf(
306                 kotlin(
307                     """
308                     // Nullable Pair in Kotlin
309                     package androidx.util
310 
311                     class NullableKotlinPair<out F, out S>(val first: F?, val second: S?)
312                     """
313                 ),
314                 kotlin(
315                     """
316                     // Non-nullable Pair in Kotlin
317                     package androidx.util
318                     class NonNullableKotlinPair<out F: Any, out S: Any>(val first: F, val second: S)
319                     """
320                 ),
321                 java(
322                     """
323                     // Platform nullability Pair in Java
324                     package androidx.util;
325 
326                     @SuppressWarnings("WeakerAccess")
327                     public class PlatformJavaPair<F, S> {
328                         public final F first;
329                         public final S second;
330 
331                         public PlatformJavaPair(F first, S second) {
332                             this.first = first;
333                             this.second = second;
334                         }
335                     }
336                 """
337                 ),
338                 java(
339                     """
340                     // Platform nullability Pair in Java
341                     package androidx.util;
342                     import androidx.annotation.NonNull;
343                     import androidx.annotation.Nullable;
344 
345                     @SuppressWarnings("WeakerAccess")
346                     public class NullableJavaPair<F, S> {
347                         public final @Nullable F first;
348                         public final @Nullable S second;
349 
350                         public NullableJavaPair(@Nullable F first, @Nullable S second) {
351                             this.first = first;
352                             this.second = second;
353                         }
354                     }
355                     """
356                 ),
357                 java(
358                     """
359                     // Platform nullability Pair in Java
360                     package androidx.util;
361 
362                     import androidx.annotation.NonNull;
363 
364                     @SuppressWarnings("WeakerAccess")
365                     public class NonNullableJavaPair<F, S> {
366                         public final @NonNull F first;
367                         public final @NonNull S second;
368 
369                         public NonNullableJavaPair(@NonNull F first, @NonNull S second) {
370                             this.first = first;
371                             this.second = second;
372                         }
373                     }
374                     """
375                 ),
376                 kotlin(
377                     """
378                     package androidx.util
379 
380                     @Suppress("HasPlatformType") // Intentionally propagating platform type with unknown nullability.
381                     inline operator fun <F, S> PlatformJavaPair<F, S>.component1() = first
382                     """
383                 ),
384                 supportNonNullSource,
385                 supportNullableSource
386             ),
387             api = """
388                 package androidx.util {
389                   public class NonNullableJavaPair<F, S> {
390                     ctor public NonNullableJavaPair(F, S);
391                     field public final F first;
392                     field public final S second;
393                   }
394                   public final class NonNullableKotlinPair<F, S> {
395                     ctor public NonNullableKotlinPair(F first, S second);
396                     method public F getFirst();
397                     method public S getSecond();
398                   }
399                   public class NullableJavaPair<F, S> {
400                     ctor public NullableJavaPair(F?, S?);
401                     field public final F? first;
402                     field public final S? second;
403                   }
404                   public final class NullableKotlinPair<F, S> {
405                     ctor public NullableKotlinPair(F? first, S? second);
406                     method public F? getFirst();
407                     method public S? getSecond();
408                   }
409                   public class PlatformJavaPair<F, S> {
410                     ctor public PlatformJavaPair(F!, S!);
411                     field public final F! first;
412                     field public final S! second;
413                   }
414                   public final class TestKt {
415                     ctor public TestKt();
416                     method public static operator <F, S> F! component1(androidx.util.PlatformJavaPair<F,S>);
417                   }
418                 }
419                 """,
420             extraArguments = arrayOf("--hide-package", "androidx.annotation"),
421             checkDoclava1 = false /* doesn't support Kotlin... */
422         )
423     }
424 
425     @Test
JvmOverloadsnull426     fun `JvmOverloads`() {
427         // Regression test for https://github.com/android/android-ktx/issues/366
428         check(
429             compatibilityMode = false,
430             sourceFiles = *arrayOf(
431                 kotlin(
432                     """
433                         package androidx.content
434 
435                         import android.annotation.SuppressLint
436                         import android.content.SharedPreferences
437 
438                         @SuppressLint("ApplySharedPref")
439                         @JvmOverloads
440                         inline fun SharedPreferences.edit(
441                             commit: Boolean = false,
442                             action: SharedPreferences.Editor.() -> Unit
443                         ) {
444                             val editor = edit()
445                             action(editor)
446                             if (commit) {
447                                 editor.commit()
448                             } else {
449                                 editor.apply()
450                             }
451                         }
452 
453                         @JvmOverloads
454                         fun String.blahblahblah(firstArg: String = "hello", secondArg: Int = "42", thirdArg: String = "world") {
455                         }
456                     """
457                 )
458             ),
459             api = """
460                 package androidx.content {
461                   public final class TestKt {
462                     ctor public TestKt();
463                     method public static void blahblahblah(String, String firstArg = "\"hello\"", int secondArg = "\"42\"", String thirdArg = "\"world\"");
464                     method public static void blahblahblah(String, String firstArg = "\"hello\"", int secondArg = "\"42\"");
465                     method public static void blahblahblah(String, String firstArg = "\"hello\"");
466                     method public static void blahblahblah(String);
467                     method public static void edit(android.content.SharedPreferences, boolean commit = "false", kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
468                     method public static void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
469                   }
470                 }
471                 """,
472             extraArguments = arrayOf("--hide-package", "androidx.annotation"),
473             checkDoclava1 = false /* doesn't support default Values */
474         )
475     }
476 
477     @Test
Extract class with genericsnull478     fun `Extract class with generics`() {
479         // Basic interface with generics; makes sure <T extends Object> is written as just <T>
480         // Also include some more complex generics expressions to make sure they're serialized
481         // correctly (in particular, using fully qualified names instead of what appears in
482         // the source code.)
483         check(
484             checkDoclava1 = true,
485             sourceFiles = *arrayOf(
486                 java(
487                     """
488                     package test.pkg;
489                     @SuppressWarnings("ALL")
490                     public interface MyInterface<T extends Object>
491                             extends MyBaseInterface {
492                     }
493                     """
494                 ), java(
495                     """
496                     package a.b.c;
497                     @SuppressWarnings("ALL")
498                     public interface MyStream<T, S extends MyStream<T, S>> extends test.pkg.AutoCloseable {
499                     }
500                     """
501                 ), java(
502                     """
503                     package test.pkg;
504                     @SuppressWarnings("ALL")
505                     public interface MyInterface2<T extends Number>
506                             extends MyBaseInterface {
507                         class TtsSpan<C extends MyInterface<?>> { }
508                         abstract class Range<T extends Comparable<? super T>> {
509                             protected String myString;
510                         }
511                     }
512                     """
513                 ),
514                 java(
515                     """
516                     package test.pkg;
517                     public interface MyBaseInterface {
518                         void fun(int a, String b);
519                     }
520                     """
521                 ),
522                 java(
523                     """
524                     package test.pkg;
525                     public interface MyOtherInterface extends MyBaseInterface, AutoCloseable {
526                         void fun(int a, String b);
527                     }
528                     """
529                 ),
530                 java(
531                     """
532                     package test.pkg;
533                     public interface AutoCloseable {
534                     }
535                     """
536                 )
537             ),
538             api = """
539                     package a.b.c {
540                       public abstract interface MyStream<T, S extends a.b.c.MyStream<T, S>> implements test.pkg.AutoCloseable {
541                       }
542                     }
543                     package test.pkg {
544                       public abstract interface AutoCloseable {
545                       }
546                       public abstract interface MyBaseInterface {
547                         method public abstract void fun(int, java.lang.String);
548                       }
549                       public abstract interface MyInterface<T> implements test.pkg.MyBaseInterface {
550                       }
551                       public abstract interface MyInterface2<T extends java.lang.Number> implements test.pkg.MyBaseInterface {
552                       }
553                       public static abstract class MyInterface2.Range<T extends java.lang.Comparable<? super T>> {
554                         ctor public MyInterface2.Range();
555                         field protected java.lang.String myString;
556                       }
557                       public static class MyInterface2.TtsSpan<C extends test.pkg.MyInterface<?>> {
558                         ctor public MyInterface2.TtsSpan();
559                       }
560                       public abstract interface MyOtherInterface implements test.pkg.AutoCloseable test.pkg.MyBaseInterface {
561                       }
562                     }
563                 """,
564             extraArguments = arrayOf("--hide", "KotlinKeyword")
565         )
566     }
567 
568     @Test
Basic class without default constructor, has constructors with argsnull569     fun `Basic class without default constructor, has constructors with args`() {
570         // Class without private constructors (shouldn't insert default constructor)
571         check(
572             sourceFiles = *arrayOf(
573                 java(
574                     """
575                     package test.pkg;
576                     public class Foo {
577                         public Foo(int i) {
578 
579                         }
580                         public Foo(int i, int j) {
581                         }
582                     }
583                     """
584                 )
585             ),
586             api = """
587                 package test.pkg {
588                   public class Foo {
589                     ctor public Foo(int);
590                     ctor public Foo(int, int);
591                   }
592                 }
593                 """
594         )
595     }
596 
597     @Test
Basic class without default constructor, has private constructornull598     fun `Basic class without default constructor, has private constructor`() {
599         // Class without private constructors; no default constructor should be inserted
600         check(
601             sourceFiles = *arrayOf(
602                 java(
603                     """
604                     package test.pkg;
605                     @SuppressWarnings("ALL")
606                     public class Foo {
607                         private Foo() {
608                         }
609                     }
610                     """
611                 )
612             ),
613             api = """
614                 package test.pkg {
615                   public class Foo {
616                   }
617                 }
618                 """
619         )
620     }
621 
622     @Test
Interface class extractionnull623     fun `Interface class extraction`() {
624         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
625         // in the interface are taken to be public etc)
626         check(
627             sourceFiles = *arrayOf(
628                 java(
629                     """
630                     package test.pkg;
631                     @SuppressWarnings("ALL")
632                     public interface Foo {
633                         void foo();
634                     }
635                     """
636                 )
637             ),
638             api = """
639                 package test.pkg {
640                   public abstract interface Foo {
641                     method public abstract void foo();
642                   }
643                 }
644                 """
645         )
646     }
647 
648     @Test
Enum class extractionnull649     fun `Enum class extraction`() {
650         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
651         // in the interface are taken to be public etc)
652         check(
653             sourceFiles = *arrayOf(
654                 java(
655                     """
656                     package test.pkg;
657                     @SuppressWarnings("ALL")
658                     public enum Foo {
659                         A, B;
660                     }
661                     """
662                 )
663             ),
664             api = """
665                 package test.pkg {
666                   public final class Foo extends java.lang.Enum {
667                     method public static test.pkg.Foo valueOf(java.lang.String);
668                     method public static final test.pkg.Foo[] values();
669                     enum_constant public static final test.pkg.Foo A;
670                     enum_constant public static final test.pkg.Foo B;
671                   }
672                 }
673                 """
674         )
675     }
676 
677     @Test
Enum class, non-compat modenull678     fun `Enum class, non-compat mode`() {
679         @Suppress("ConstantConditionIf")
680         if (SKIP_NON_COMPAT) {
681             println("Skipping test for non-compatibility mode which isn't fully done yet")
682             return
683         }
684 
685         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
686         // in the interface are taken to be public etc)
687         check(
688             sourceFiles = *arrayOf(
689                 java(
690                     """
691                     package test.pkg;
692                     @SuppressWarnings("ALL")
693                     public enum Foo {
694                         A, B;
695                     }
696                     """
697                 )
698             ),
699             compatibilityMode = false,
700             api = """
701                 package test.pkg {
702                   public enum Foo {
703                     enum_constant public static final test.pkg.Foo! A;
704                     enum_constant public static final test.pkg.Foo! B;
705                   }
706                 }
707                 """
708         )
709     }
710 
711     @Test
Annotation class extractionnull712     fun `Annotation class extraction`() {
713         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
714         // in the interface are taken to be public etc)
715         check(
716             // For unknown reasons, doclava1 behaves differently here than when invoked on the
717             // whole platform
718             checkDoclava1 = false,
719             sourceFiles = *arrayOf(
720                 java(
721                     """
722                     package test.pkg;
723                     @SuppressWarnings("ALL")
724                     public @interface Foo {
725                         String value();
726                     }
727                     """
728                 ),
729                 java(
730                     """
731                     package android.annotation;
732                     import static java.lang.annotation.ElementType.*;
733                     import java.lang.annotation.*;
734                     @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
735                     @Retention(RetentionPolicy.CLASS)
736                     @SuppressWarnings("ALL")
737                     public @interface SuppressLint {
738                         String[] value();
739                     }
740                 """
741                 )
742             ),
743             api = """
744                 package android.annotation {
745                   public abstract class SuppressLint implements java.lang.annotation.Annotation {
746                   }
747                 }
748                 package test.pkg {
749                   public abstract class Foo implements java.lang.annotation.Annotation {
750                   }
751                 }
752                 """
753         )
754     }
755 
756     @Test
Do not include inherited public methods from private parents in compat modenull757     fun `Do not include inherited public methods from private parents in compat mode`() {
758         // Real life example: StringBuilder.setLength, in compat mode
759         check(
760             compatibilityMode = true,
761             sourceFiles = *arrayOf(
762                 java(
763                     """
764                     package test.pkg;
765                     public class MyStringBuilder extends AbstractMyStringBuilder {
766                     }
767                     """
768                 ),
769                 java(
770                     """
771                     package test.pkg;
772                     class AbstractMyStringBuilder {
773                         public void setLength(int length) {
774                         }
775                     }
776                     """
777                 )
778             ),
779             api = """
780                 package test.pkg {
781                   public class MyStringBuilder {
782                     ctor public MyStringBuilder();
783                   }
784                 }
785                 """
786         )
787     }
788 
789     @Test
Include inherited public methods from private parentsnull790     fun `Include inherited public methods from private parents`() {
791         // In non-compat mode, include public methods from hidden parents too.
792         // Real life example: StringBuilder.setLength
793         // This is just like the above test, but with compat mode disabled.
794         check(
795             compatibilityMode = false,
796             sourceFiles = *arrayOf(
797                 java(
798                     """
799                     package test.pkg;
800                     public class MyStringBuilder extends AbstractMyStringBuilder {
801                     }
802                     """
803                 ),
804                 java(
805                     """
806                     package test.pkg;
807                     class AbstractMyStringBuilder {
808                         public void setLength(int length) {
809                         }
810                     }
811                     """
812                 )
813             ),
814             api = """
815                 package test.pkg {
816                   public class MyStringBuilder {
817                     ctor public MyStringBuilder();
818                     method public void setLength(int);
819                   }
820                 }
821                 """
822         )
823     }
824 
825     @Test
Annotation class extraction, non-compat modenull826     fun `Annotation class extraction, non-compat mode`() {
827         @Suppress("ConstantConditionIf")
828         if (SKIP_NON_COMPAT) {
829             println("Skipping test for non-compatibility mode which isn't fully done yet")
830             return
831         }
832 
833         // Interface: makes sure the right modifiers etc are shown (and that "package private" methods
834         // in the interface are taken to be public etc)
835         check(
836             sourceFiles = *arrayOf(
837                 java(
838                     """
839                     package test.pkg;
840                     public @interface Foo {
841                         String value();
842                     }
843                     """
844                 ),
845                 java(
846                     """
847                     package android.annotation;
848                     import static java.lang.annotation.ElementType.*;
849                     import java.lang.annotation.*;
850                     @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
851                     @Retention(RetentionPolicy.CLASS)
852                     @SuppressWarnings("ALL")
853                     public @interface SuppressLint {
854                         String[] value();
855                     }
856                     """
857                 )
858             ),
859             compatibilityMode = false,
860             api = """
861                 package android.annotation {
862                   public @interface SuppressLint {
863                     method public abstract String[]! value();
864                   }
865                 }
866                 package test.pkg {
867                   public @interface Foo {
868                     method public abstract String! value();
869                   }
870                 }
871                 """
872         )
873     }
874 
875     @Test
Superclass signature extractionnull876     fun `Superclass signature extraction`() {
877         // Make sure superclass statement is correct; inherited method from parent that has same
878         // signature isn't included in the child
879         check(
880             sourceFiles = *arrayOf(
881                 java(
882                     """
883                     package test.pkg;
884                     @SuppressWarnings("ALL")
885                     public class Foo extends Super {
886                         @Override public void base() { }
887                         public void child() { }
888                     }
889                     """
890                 ),
891                 java(
892                     """
893                     package test.pkg;
894                     @SuppressWarnings("ALL")
895                     public class Super {
896                         public void base() { }
897                     }
898                     """
899                 )
900             ),
901             api = """
902                 package test.pkg {
903                   public class Foo extends test.pkg.Super {
904                     ctor public Foo();
905                     method public void child();
906                   }
907                   public class Super {
908                     ctor public Super();
909                     method public void base();
910                   }
911                 }
912                 """
913         )
914     }
915 
916     @Test
Extract fields with types and initial valuesnull917     fun `Extract fields with types and initial values`() {
918         check(
919             sourceFiles = *arrayOf(
920                 java(
921                     """
922                     package test.pkg;
923                     @SuppressWarnings("ALL")
924                     public class Foo {
925                         private int hidden = 1;
926                         int hidden2 = 2;
927                         /** @hide */
928                         int hidden3 = 3;
929 
930                         protected int field00; // No value
931                         public static final boolean field01 = true;
932                         public static final int field02 = 42;
933                         public static final long field03 = 42L;
934                         public static final short field04 = 5;
935                         public static final byte field05 = 5;
936                         public static final char field06 = 'c';
937                         public static final float field07 = 98.5f;
938                         public static final double field08 = 98.5;
939                         public static final String field09 = "String with \"escapes\" and \u00a9...";
940                         public static final double field10 = Double.NaN;
941                         public static final double field11 = Double.POSITIVE_INFINITY;
942 
943                         public static final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
944                         public static final char HEX_INPUT = 61184;
945                     }
946                     """
947                 )
948             ),
949             api = """
950                 package test.pkg {
951                   public class Foo {
952                     ctor public Foo();
953                     field public static final java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
954                     field public static final char HEX_INPUT = 61184; // 0xef00 '\uef00'
955                     field protected int field00;
956                     field public static final boolean field01 = true;
957                     field public static final int field02 = 42; // 0x2a
958                     field public static final long field03 = 42L; // 0x2aL
959                     field public static final short field04 = 5; // 0x5
960                     field public static final byte field05 = 5; // 0x5
961                     field public static final char field06 = 99; // 0x0063 'c'
962                     field public static final float field07 = 98.5f;
963                     field public static final double field08 = 98.5;
964                     field public static final java.lang.String field09 = "String with \"escapes\" and \u00a9...";
965                     field public static final double field10 = (0.0/0.0);
966                     field public static final double field11 = (1.0/0.0);
967                   }
968                 }
969                 """
970         )
971     }
972 
973     @Test
Check all modifiersnull974     fun `Check all modifiers`() {
975         // Include as many modifiers as possible to see which ones are included
976         // in the signature files, and the expected sorting order.
977         // Note that the signature files treat "deprecated" as a fake modifier.
978         // Note also how the "protected" modifier on the interface method gets
979         // promoted to public.
980         check(
981             checkDoclava1 = true,
982             sourceFiles = *arrayOf(
983                 java(
984                     """
985                     package test.pkg;
986 
987                     @SuppressWarnings("ALL")
988                     public abstract class Foo {
989                         @Deprecated private static final long field1 = 5;
990                         @Deprecated private static volatile long field2 = 5;
991                         @Deprecated public static strictfp final synchronized void method1() { }
992                         @Deprecated public static final synchronized native void method2();
993                         @Deprecated protected static final class Inner1 { }
994                         @Deprecated protected static abstract  class Inner2 { }
995                         @Deprecated protected interface Inner3 {
996                             default void method3() { }
997                             static void method4(final int arg) { }
998                         }
999                     }
1000                     """
1001                 )
1002             ),
1003 
1004             warnings = """
1005                     src/test/pkg/Foo.java:7: warning: Method test.pkg.Foo.method1(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch:113]
1006                     src/test/pkg/Foo.java:8: warning: Method test.pkg.Foo.method2(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch:113]
1007                     src/test/pkg/Foo.java:9: warning: Class test.pkg.Foo.Inner1: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch:113]
1008                     src/test/pkg/Foo.java:10: warning: Class test.pkg.Foo.Inner2: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch:113]
1009                     src/test/pkg/Foo.java:11: warning: Class test.pkg.Foo.Inner3: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch:113]
1010                         """,
1011 
1012             api = """
1013                     package test.pkg {
1014                       public abstract class Foo {
1015                         ctor public Foo();
1016                         method public static final deprecated synchronized void method1();
1017                         method public static final deprecated synchronized void method2();
1018                       }
1019                       protected static final deprecated class Foo.Inner1 {
1020                         ctor protected Foo.Inner1();
1021                       }
1022                       protected static abstract deprecated class Foo.Inner2 {
1023                         ctor protected Foo.Inner2();
1024                       }
1025                       protected static abstract deprecated interface Foo.Inner3 {
1026                         method public default void method3();
1027                         method public static void method4(int);
1028                       }
1029                     }
1030                 """
1031         )
1032     }
1033 
1034     @Test
Check all modifiers, non-compat modenull1035     fun `Check all modifiers, non-compat mode`() {
1036         @Suppress("ConstantConditionIf")
1037         if (SKIP_NON_COMPAT) {
1038             @Suppress("ConstantConditionIf")
1039             println("Skipping test for non-compatibility mode which isn't fully done yet")
1040             return
1041         }
1042 
1043         // Like testModifiers but turns off compat mode, such that we have
1044         // a modifier order more in line with standard code conventions
1045         check(
1046             compatibilityMode = false,
1047             sourceFiles = *arrayOf(
1048                 java(
1049                     """
1050                     package test.pkg;
1051 
1052                     @SuppressWarnings("ALL")
1053                     public abstract class Foo {
1054                         @Deprecated private static final long field1 = 5;
1055                         @Deprecated private static volatile long field2 = 5;
1056                         /** @deprecated */ @Deprecated public static strictfp final synchronized void method1() { }
1057                         /** @deprecated */ @Deprecated public static final synchronized native void method2();
1058                         /** @deprecated */ @Deprecated protected static final class Inner1 { }
1059                         /** @deprecated */ @Deprecated protected static abstract class Inner2 { }
1060                         /** @deprecated */ @Deprecated protected interface Inner3 {
1061                             protected default void method3() { }
1062                             static void method4(final int arg) { }
1063                         }
1064                     }
1065                     """
1066                 )
1067             ),
1068             api = """
1069                 package test.pkg {
1070                   public abstract class Foo {
1071                     ctor public Foo();
1072                     method deprecated public static final synchronized strictfp void method1();
1073                     method deprecated public static final synchronized native void method2();
1074                   }
1075                   deprecated protected static final class Foo.Inner1 {
1076                     ctor protected Foo.Inner1();
1077                   }
1078                   deprecated protected abstract static class Foo.Inner2 {
1079                     ctor protected Foo.Inner2();
1080                   }
1081                   deprecated protected static interface Foo.Inner3 {
1082                     method public default void method3();
1083                     method public static void method4(int);
1084                   }
1085                 }
1086                 """
1087         )
1088     }
1089 
1090     @Test
Package with only hidden classes should be removed from signature filesnull1091     fun `Package with only hidden classes should be removed from signature files`() {
1092         // Checks that if we have packages that are hidden, or contain only hidden or doconly
1093         // classes, the entire package is omitted from the signature file. Note how the test.pkg1.sub
1094         // package is not marked @hide, but doclava now treats subpackages of a hidden package
1095         // as also hidden.
1096         check(
1097             sourceFiles = *arrayOf(
1098                 java(
1099                     """
1100                     ${"/** @hide hidden package */" /* avoid dangling javadoc warning */}
1101                     package test.pkg1;
1102                     """
1103                 ),
1104                 java(
1105                     """
1106                     package test.pkg1;
1107                     @SuppressWarnings("ALL")
1108                     public class Foo {
1109                         // Hidden by package hide
1110                     }
1111                     """
1112                 ),
1113                 java(
1114                     """
1115                     package test.pkg2;
1116                     /** @hide hidden class in this package */
1117                     @SuppressWarnings("ALL")
1118                     public class Bar {
1119                     }
1120                     """
1121                 ),
1122                 java(
1123                     """
1124                     package test.pkg2;
1125                     /** @doconly hidden class in this package */
1126                     @SuppressWarnings("ALL")
1127                     public class Baz {
1128                     }
1129                     """
1130                 ),
1131                 java(
1132                     """
1133                     package test.pkg1.sub;
1134                     // Hidden by @hide in package above
1135                     @SuppressWarnings("ALL")
1136                     public class Test {
1137                     }
1138                     """
1139                 ),
1140                 java(
1141                     """
1142                     package test.pkg3;
1143                     // The only really visible class
1144                     @SuppressWarnings("ALL")
1145                     public class Boo {
1146                     }
1147                     """
1148                 )
1149             ),
1150             api = """
1151                 package test.pkg3 {
1152                   public class Boo {
1153                     ctor public Boo();
1154                   }
1155                 }
1156                 """
1157         )
1158     }
1159 
1160     @Test
Enums can be abstractnull1161     fun `Enums can be abstract`() {
1162         // As per https://bugs.openjdk.java.net/browse/JDK-6287639
1163         // abstract methods in enums should not be listed as abstract,
1164         // but doclava1 does, so replicate this.
1165         // Also checks that we handle both enum fields and regular fields
1166         // and that they are listed separately.
1167 
1168         check(
1169             sourceFiles = *arrayOf(
1170                 java(
1171                     """
1172                     package test.pkg;
1173 
1174                     @SuppressWarnings("ALL")
1175                     public enum FooBar {
1176                         ABC {
1177                             @Override
1178                             protected void foo() { }
1179                         }, DEF {
1180                             @Override
1181                             protected void foo() { }
1182                         };
1183 
1184                         protected abstract void foo();
1185                         public static int field1 = 1;
1186                         public int field2 = 2;
1187                     }
1188                     """
1189                 )
1190             ),
1191             api = """
1192                 package test.pkg {
1193                   public class FooBar extends java.lang.Enum {
1194                     method protected abstract void foo();
1195                     method public static test.pkg.FooBar valueOf(java.lang.String);
1196                     method public static final test.pkg.FooBar[] values();
1197                     enum_constant public static final test.pkg.FooBar ABC;
1198                     enum_constant public static final test.pkg.FooBar DEF;
1199                     field public static int field1;
1200                     field public int field2;
1201                   }
1202                 }
1203             """
1204         )
1205     }
1206 
1207     @Test
Check erasure in throws-listnull1208     fun `Check erasure in throws-list`() {
1209         // Makes sure that when we have a generic signature in the throws list we take
1210         // the erasure instead (in compat mode); "Throwable" instead of "X" in the below
1211         // test. Real world example: Optional.orElseThrow.
1212         check(
1213             compatibilityMode = true,
1214             sourceFiles = *arrayOf(
1215                 java(
1216                     """
1217                     package test.pkg;
1218 
1219                     import java.util.function.Supplier;
1220 
1221                     @SuppressWarnings("ALL")
1222                     public final class Test<T> {
1223                         public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
1224                             return null;
1225                         }
1226                     }
1227                     """
1228                 )
1229             ),
1230             api = """
1231                 package test.pkg {
1232                   public final class Test<T> {
1233                     ctor public Test();
1234                     method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws java.lang.Throwable;
1235                   }
1236                 }
1237                 """
1238         )
1239     }
1240 
1241     @Test
Check various generics signature subtletiesnull1242     fun `Check various generics signature subtleties`() {
1243         // Some additional declarations where PSI default type handling diffs from doclava1
1244         check(
1245             sourceFiles = *arrayOf(
1246                 java(
1247                     """
1248                     package test.pkg;
1249 
1250                     @SuppressWarnings("ALL")
1251                     public abstract class Collections {
1252                         public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T> collection) {
1253                             return null;
1254                         }
1255                         public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T t);
1256                         public final class Range<T extends java.lang.Comparable<? super T>> { }
1257                     }
1258                     """
1259                 ), java(
1260                     """
1261                     package test.pkg;
1262 
1263                     import java.util.Set;
1264 
1265                     @SuppressWarnings("ALL")
1266                     public class MoreAsserts {
1267                         public static void assertEquals(String arg0, Set<? extends Object> arg1, Set<? extends Object> arg2) { }
1268                         public static void assertEquals(Set<? extends Object> arg1, Set<? extends Object> arg2) { }
1269                     }
1270 
1271                     """
1272                 )
1273             ),
1274 
1275             // This is the output from doclava1; I'm not quite matching this yet (sorting order differs,
1276             // and my heuristic to remove "extends java.lang.Object" is somehow preserved here. I'm
1277             // not clear on when they do it and when they don't.
1278             /*
1279             api = """
1280             package test.pkg {
1281               public abstract class Collections {
1282                 ctor public Collections();
1283                 method public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T);
1284                 method public static <T & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
1285               }
1286               public final class Collections.Range<T extends java.lang.Comparable<? super T>> {
1287                 ctor public Collections.Range();
1288               }
1289               public class MoreAsserts {
1290                 ctor public MoreAsserts();
1291                 method public static void assertEquals(java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
1292                 method public static void assertEquals(java.lang.String, java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
1293               }
1294             }
1295             """,
1296             */
1297             api = """
1298                 package test.pkg {
1299                   public abstract class Collections {
1300                     ctor public Collections();
1301                     method public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T);
1302                     method public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
1303                   }
1304                   public final class Collections.Range<T extends java.lang.Comparable<? super T>> {
1305                     ctor public Collections.Range();
1306                   }
1307                   public class MoreAsserts {
1308                     ctor public MoreAsserts();
1309                     method public static void assertEquals(java.lang.String, java.util.Set<?>, java.util.Set<?>);
1310                     method public static void assertEquals(java.util.Set<?>, java.util.Set<?>);
1311                   }
1312                 }
1313                 """,
1314 
1315             // Can't check doclava1 on this: its output doesn't match javac, e.g. for the above declaration
1316             // of max, javap shows this signature:
1317             //   public static <T extends java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
1318             // which matches metalava's output:
1319             //   method public static <T & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
1320             // and not doclava1:
1321             //   method public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
1322 
1323             checkDoclava1 = false
1324         )
1325     }
1326 
1327     @Test
Check instance methods in enumsnull1328     fun `Check instance methods in enums`() {
1329         // Make sure that when we have instance methods in an enum they're handled
1330         // correctly (there's some special casing around enums to insert extra methods
1331         // that was broken, as exposed by ChronoUnit#toString)
1332         check(
1333             sourceFiles = *arrayOf(
1334                 java(
1335                     """
1336                     package test.pkg;
1337 
1338                     @SuppressWarnings("ALL")
1339                     public interface TempUnit {
1340                         @Override
1341                         String toString();
1342                     }
1343                      """
1344                 ),
1345                 java(
1346                     """
1347                     package test.pkg;
1348 
1349                     @SuppressWarnings("ALL")
1350                     public enum ChronUnit implements TempUnit {
1351                         C, B, A;
1352 
1353                         public String valueOf(int x) {
1354                             return Integer.toString(x + 5);
1355                         }
1356 
1357                         public String values(String separator) {
1358                             return null;
1359                         }
1360 
1361                         @Override
1362                         public String toString() {
1363                             return name();
1364                         }
1365                     }
1366                 """
1367                 )
1368             ),
1369             importedPackages = emptyList(),
1370             api = """
1371                 package test.pkg {
1372                   public final class ChronUnit extends java.lang.Enum implements test.pkg.TempUnit {
1373                     method public static test.pkg.ChronUnit valueOf(java.lang.String);
1374                     method public java.lang.String valueOf(int);
1375                     method public static final test.pkg.ChronUnit[] values();
1376                     method public final java.lang.String values(java.lang.String);
1377                     enum_constant public static final test.pkg.ChronUnit A;
1378                     enum_constant public static final test.pkg.ChronUnit B;
1379                     enum_constant public static final test.pkg.ChronUnit C;
1380                   }
1381                   public abstract interface TempUnit {
1382                     method public abstract java.lang.String toString();
1383                   }
1384                 }
1385                 """
1386         )
1387     }
1388 
1389     @Test
Mixing enums and fieldsnull1390     fun `Mixing enums and fields`() {
1391         // Checks sorting order of enum constant values
1392         val source = """
1393             package java.nio.file.attribute {
1394               public final class AclEntryPermission extends java.lang.Enum {
1395                 method public static java.nio.file.attribute.AclEntryPermission valueOf(java.lang.String);
1396                 method public static final java.nio.file.attribute.AclEntryPermission[] values();
1397                 enum_constant public static final java.nio.file.attribute.AclEntryPermission APPEND_DATA;
1398                 enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE;
1399                 enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE_CHILD;
1400                 enum_constant public static final java.nio.file.attribute.AclEntryPermission EXECUTE;
1401                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ACL;
1402                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ATTRIBUTES;
1403                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_DATA;
1404                 enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_NAMED_ATTRS;
1405                 enum_constant public static final java.nio.file.attribute.AclEntryPermission SYNCHRONIZE;
1406                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ACL;
1407                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ATTRIBUTES;
1408                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_DATA;
1409                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_NAMED_ATTRS;
1410                 enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_OWNER;
1411                 field public static final java.nio.file.attribute.AclEntryPermission ADD_FILE;
1412                 field public static final java.nio.file.attribute.AclEntryPermission ADD_SUBDIRECTORY;
1413                 field public static final java.nio.file.attribute.AclEntryPermission LIST_DIRECTORY;
1414               }
1415             }
1416                     """
1417         check(
1418             signatureSource = source,
1419             api = source
1420         )
1421     }
1422 
1423     @Test
Superclass filtering, should skip intermediate hidden classesnull1424     fun `Superclass filtering, should skip intermediate hidden classes`() {
1425         check(
1426             sourceFiles = *arrayOf(
1427                 java(
1428                     """
1429                     package test.pkg;
1430                     @SuppressWarnings("ALL")
1431                     public class MyClass extends HiddenParent {
1432                         public void method4() { }
1433                     }
1434                     """
1435                 ),
1436                 java(
1437                     """
1438                     package test.pkg;
1439                     /** @hide */
1440                     @SuppressWarnings("ALL")
1441                     public class HiddenParent extends HiddenParent2 {
1442                         public static final String CONSTANT = "MyConstant";
1443                         protected int mContext;
1444                         public void method3() { }
1445                     }
1446                     """
1447                 ),
1448                 java(
1449                     """
1450                     package test.pkg;
1451                     /** @hide */
1452                     @SuppressWarnings("ALL")
1453                     public class HiddenParent2 extends PublicParent {
1454                         public void method2() { }
1455                     }
1456                     """
1457                 ),
1458                 java(
1459                     """
1460                     package test.pkg;
1461                     @SuppressWarnings("ALL")
1462                     public class PublicParent {
1463                         public void method1() { }
1464                     }
1465                     """
1466                 )
1467             ),
1468             // Notice how the intermediate methods (method2, method3) have been removed
1469             includeStrippedSuperclassWarnings = true,
1470             warnings = "src/test/pkg/MyClass.java:2: warning: Public class test.pkg.MyClass stripped of unavailable superclass test.pkg.HiddenParent [HiddenSuperclass:111]",
1471             api = """
1472                 package test.pkg {
1473                   public class MyClass extends test.pkg.PublicParent {
1474                     ctor public MyClass();
1475                     method public void method4();
1476                   }
1477                   public class PublicParent {
1478                     ctor public PublicParent();
1479                     method public void method1();
1480                   }
1481                 }
1482                 """
1483         )
1484     }
1485 
1486     @Test
Inheriting from package private classes, package private class should be includednull1487     fun `Inheriting from package private classes, package private class should be included`() {
1488         check(
1489             checkDoclava1 = false, // doclava1 does not include method2, which it should
1490             compatibilityMode = false,
1491             sourceFiles =
1492             *arrayOf(
1493                 java(
1494                     """
1495                     package test.pkg;
1496                     @SuppressWarnings("ALL")
1497                     public class MyClass extends HiddenParent {
1498                         public void method1() { }
1499                     }
1500                     """
1501                 ),
1502                 java(
1503                     """
1504                     package test.pkg;
1505                     @SuppressWarnings("ALL")
1506                     class HiddenParent {
1507                         public static final String CONSTANT = "MyConstant";
1508                         protected int mContext;
1509                         public void method2() { }
1510                     }
1511                     """
1512                 )
1513             ),
1514             warnings = "",
1515             api = """
1516                     package test.pkg {
1517                       public class MyClass {
1518                         ctor public MyClass();
1519                         method public void method1();
1520                         method public void method2();
1521                       }
1522                     }
1523             """
1524         )
1525     }
1526 
1527     @Test
Using compatibility flag manuallynull1528     fun `Using compatibility flag manually`() {
1529         // Like previous test, but using compatibility mode and explicitly turning on
1530         // the hidden super class compatibility flag. This test is mostly intended
1531         // to test the flag handling for individual compatibility flags.
1532         check(
1533             checkDoclava1 = false, // doclava1 does not include method2, which it should
1534             compatibilityMode = true,
1535             extraArguments = arrayOf("--skip-inherited-methods=false"),
1536             sourceFiles =
1537             *arrayOf(
1538                 java(
1539                     """
1540                     package test.pkg;
1541                     @SuppressWarnings("ALL")
1542                     public class MyClass extends HiddenParent {
1543                         public void method1() { }
1544                     }
1545                     """
1546                 ),
1547                 java(
1548                     """
1549                     package test.pkg;
1550                     @SuppressWarnings("ALL")
1551                     class HiddenParent {
1552                         public static final String CONSTANT = "MyConstant";
1553                         protected int mContext;
1554                         public void method2() { }
1555                     }
1556                     """
1557                 )
1558             ),
1559             warnings = "",
1560             api = """
1561                     package test.pkg {
1562                       public class MyClass {
1563                         ctor public MyClass();
1564                         method public void method1();
1565                         method public void method2();
1566                       }
1567                     }
1568             """
1569         )
1570     }
1571 
1572     @Test
When implementing rather than extending package private class, inline members insteadnull1573     fun `When implementing rather than extending package private class, inline members instead`() {
1574         // If you implement a package private interface, we just remove it and inline the members into
1575         // the subclass
1576         check(
1577             compatibilityMode = true,
1578             sourceFiles = *arrayOf(
1579                 java(
1580                     """
1581                     package test.pkg;
1582                     public class MyClass implements HiddenInterface {
1583                         @Override public void method() { }
1584                         @Override public void other() { }
1585                     }
1586                     """
1587                 ),
1588                 java(
1589                     """
1590                     package test.pkg;
1591                     public interface OtherInterface {
1592                         void other();
1593                     }
1594                     """
1595                 ),
1596                 java(
1597                     """
1598                     package test.pkg;
1599                     interface HiddenInterface extends OtherInterface {
1600                         void method() { }
1601                         String CONSTANT = "MyConstant";
1602                     }
1603                     """
1604                 )
1605             ),
1606             api = """
1607                 package test.pkg {
1608                   public class MyClass implements test.pkg.OtherInterface {
1609                     ctor public MyClass();
1610                     method public void method();
1611                     method public void other();
1612                     field public static final java.lang.String CONSTANT = "MyConstant";
1613                   }
1614                   public abstract interface OtherInterface {
1615                     method public abstract void other();
1616                   }
1617                 }
1618                 """
1619         )
1620     }
1621 
1622     @Test
Implementing package private class, non-compat modenull1623     fun `Implementing package private class, non-compat mode`() {
1624         @Suppress("ConstantConditionIf")
1625         if (SKIP_NON_COMPAT) {
1626             println("Skipping test for non-compatibility mode which isn't fully done yet")
1627             return
1628         }
1629 
1630         // Like the previous test, but in non compat mode we correctly
1631         // include all the non-hidden public interfaces into the signature
1632 
1633         // BUG: Note that we need to implement the parent
1634         check(
1635             compatibilityMode = false,
1636             sourceFiles = *arrayOf(
1637                 java(
1638                     """
1639                     package test.pkg;
1640                     public class MyClass implements HiddenInterface {
1641                         @Override public void method() { }
1642                         @Override public void other() { }
1643                     }
1644                     """
1645                 ),
1646                 java(
1647                     """
1648                     package test.pkg;
1649                     public interface OtherInterface {
1650                         void other();
1651                     }
1652                     """
1653                 ),
1654                 java(
1655                     """
1656                     package test.pkg;
1657                     interface HiddenInterface extends OtherInterface {
1658                         void method() { }
1659                         String CONSTANT = "MyConstant";
1660                     }
1661                     """
1662                 )
1663             ),
1664             api = """
1665                 package test.pkg {
1666                   public class MyClass implements test.pkg.OtherInterface {
1667                     ctor public MyClass();
1668                     method public void method();
1669                     method public void other();
1670                     field public static final String! CONSTANT = "MyConstant";
1671                   }
1672                   public interface OtherInterface {
1673                     method public void other();
1674                   }
1675                 }
1676                 """
1677         )
1678     }
1679 
1680     @Test
Default modifiers should be omittednull1681     fun `Default modifiers should be omitted`() {
1682         // If signatures vary only by the "default" modifier in the interface, don't show it on the implementing
1683         // class
1684         check(
1685             sourceFiles =
1686             *arrayOf(
1687                 java(
1688                     """
1689                     package test.pkg;
1690 
1691                     public class MyClass implements SuperInterface {
1692                         @Override public void method() {  }
1693                         @Override public void method2() { }
1694                     }
1695                     """
1696                 ),
1697                 java(
1698                     """
1699                     package test.pkg;
1700 
1701                     public interface SuperInterface {
1702                         void method();
1703                         default void method2() {
1704                         }
1705                     }
1706                     """
1707                 )
1708             ),
1709             api = """
1710                 package test.pkg {
1711                   public class MyClass implements test.pkg.SuperInterface {
1712                     ctor public MyClass();
1713                     method public void method();
1714                   }
1715                   public abstract interface SuperInterface {
1716                     method public abstract void method();
1717                     method public default void method2();
1718                   }
1719                 }
1720             """
1721         )
1722     }
1723 
1724     @Test
Override via different throws list should be includednull1725     fun `Override via different throws list should be included`() {
1726         // If a method overrides another but changes the throws list, the overriding
1727         // method must be listed in the subclass. This is observed for example in
1728         // AbstractCursor#finalize, which omits the throws clause from Object's finalize.
1729         check(
1730             sourceFiles =
1731             *arrayOf(
1732                 java(
1733                     """
1734                     package test.pkg;
1735 
1736                     public abstract class AbstractCursor extends Parent {
1737                         @Override protected void finalize2() {  } // note: not throws Throwable!
1738                     }
1739                     """
1740                 ),
1741                 java(
1742                     """
1743                     package test.pkg;
1744 
1745                     @SuppressWarnings("RedundantThrows")
1746                     public class Parent {
1747                         protected void finalize2() throws Throwable {
1748                         }
1749                     }
1750                     """
1751                 )
1752             ),
1753             api = """
1754                 package test.pkg {
1755                   public abstract class AbstractCursor extends test.pkg.Parent {
1756                     ctor public AbstractCursor();
1757                     method protected void finalize2();
1758                   }
1759                   public class Parent {
1760                     ctor public Parent();
1761                     method protected void finalize2() throws java.lang.Throwable;
1762                   }
1763                 }
1764             """
1765         )
1766     }
1767 
1768     @Test
Implementing interface methodnull1769     fun `Implementing interface method`() {
1770         // If you have a public method that implements an interface method,
1771         // they'll vary in the "abstract" modifier, but it shouldn't be listed on the
1772         // class. This is an issue for example for the ZonedDateTime#getLong method
1773         // implementing the TemporalAccessor#getLong method
1774         check(
1775             sourceFiles = *arrayOf(
1776 //                java(
1777 //                    """
1778 //                    package test.pkg;
1779 //                    public interface SomeInterface {
1780 //                        long getLong();
1781 //                    }
1782 //                    """
1783 //                ),
1784                 java(
1785                     """
1786                     package test.pkg;
1787                     public interface SomeInterface2 {
1788                         @Override default long getLong() {
1789                             return 42;
1790                         }
1791                     }
1792                     """
1793                 ),
1794                 java(
1795                     """
1796                     package test.pkg;
1797                     public class Foo implements /*SomeInterface,*/ SomeInterface2 {
1798                         @Override
1799                         public long getLong() { return 0L; }
1800                     }
1801                     """
1802                 )
1803             ),
1804 //            api = """
1805 //                package test.pkg {
1806 //                  public class Foo implements test.pkg.SomeInterface test.pkg.SomeInterface2 {
1807 //                    ctor public Foo();
1808 //                  }
1809 //                  public abstract interface SomeInterface {
1810 //                    method public abstract long getLong();
1811 //                  }
1812 //                  public abstract interface SomeInterface2 {
1813 //                    method public default long getLong();
1814 //                  }
1815 //                }
1816 //                """
1817             api = """
1818             package test.pkg {
1819               public class Foo implements test.pkg.SomeInterface2 {
1820                 ctor public Foo();
1821               }
1822               public abstract interface SomeInterface2 {
1823                 method public default long getLong();
1824               }
1825             }
1826         """
1827         )
1828     }
1829 
1830     @Test
Check basic @remove scenariosnull1831     fun `Check basic @remove scenarios`() {
1832         // Test basic @remove handling for methods and fields
1833         check(
1834             checkDoclava1 = true,
1835             sourceFiles = *arrayOf(
1836                 java(
1837                     """
1838                     package test.pkg;
1839                     @SuppressWarnings("JavaDoc")
1840                     public class Bar {
1841                         /** @removed */
1842                         public Bar() { }
1843                         public int field;
1844                         public void test() { }
1845                         /** @removed */
1846                         public int removedField;
1847                         /** @removed */
1848                         public void removedMethod() { }
1849                         /** @removed and @hide - should not be listed */
1850                         public int hiddenField;
1851 
1852                         /** @removed */
1853                         public class Inner { }
1854 
1855                         public class Inner2 {
1856                             public class Inner3 {
1857                                 /** @removed */
1858                                 public class Inner4 { }
1859                             }
1860                         }
1861 
1862                         public class Inner5 {
1863                             public class Inner6 {
1864                                 public class Inner7 {
1865                                     /** @removed */
1866                                     public int removed;
1867                                 }
1868                             }
1869                         }
1870                     }
1871                     """
1872                 )
1873             ),
1874             removedApi = """
1875                 package test.pkg {
1876                   public class Bar {
1877                     ctor public Bar();
1878                     method public void removedMethod();
1879                     field public int removedField;
1880                   }
1881                   public class Bar.Inner {
1882                     ctor public Bar.Inner();
1883                   }
1884                   public class Bar.Inner2.Inner3.Inner4 {
1885                     ctor public Bar.Inner2.Inner3.Inner4();
1886                   }
1887                   public class Bar.Inner5.Inner6.Inner7 {
1888                     field public int removed;
1889                   }
1890                 }
1891                 """,
1892             removedDexApi = "" +
1893                 "Ltest/pkg/Bar;-><init>()V\n" +
1894                 "Ltest/pkg/Bar;->removedMethod()V\n" +
1895                 "Ltest/pkg/Bar;->removedField:I\n" +
1896                 "Ltest/pkg/Bar\$Inner;\n" +
1897                 "Ltest/pkg/Bar\$Inner;-><init>()V\n" +
1898                 "Ltest/pkg/Bar\$Inner2\$Inner3\$Inner4;\n" +
1899                 "Ltest/pkg/Bar\$Inner2\$Inner3\$Inner4;-><init>()V\n" +
1900                 "Ltest/pkg/Bar\$Inner5\$Inner6\$Inner7;->removed:I"
1901         )
1902     }
1903 
1904     @Test
Check @remove classnull1905     fun `Check @remove class`() {
1906         // Test removing classes
1907         check(
1908             checkDoclava1 = true,
1909             sourceFiles = *arrayOf(
1910                 java(
1911                     """
1912                     package test.pkg;
1913                     /** @removed */
1914                     @SuppressWarnings("JavaDoc")
1915                     public class Foo {
1916                         public void foo() { }
1917                         public class Inner {
1918                         }
1919                     }
1920                     """
1921                 ),
1922                 java(
1923                     """
1924                     package test.pkg;
1925                     @SuppressWarnings("JavaDoc")
1926                     public class Bar implements Parcelable {
1927                         public int field;
1928                         public void method();
1929 
1930                         /** @removed */
1931                         public int removedField;
1932                         /** @removed */
1933                         public void removedMethod() { }
1934 
1935                         public class Inner1 {
1936                         }
1937                         /** @removed */
1938                         public class Inner2 {
1939                         }
1940                     }
1941                     """
1942                 ),
1943                 java(
1944                     """
1945                     package test.pkg;
1946                     @SuppressWarnings("ALL")
1947                     public interface Parcelable {
1948                         void method();
1949                     }
1950                     """
1951                 )
1952             ),
1953             /*
1954             I expected this: but doclava1 doesn't do that (and we now match its behavior)
1955             package test.pkg {
1956               public class Bar {
1957                 method public void removedMethod();
1958                 field public int removedField;
1959               }
1960               public class Bar.Inner2 {
1961               }
1962               public class Foo {
1963                 method public void foo();
1964               }
1965             }
1966              */
1967             removedApi = """
1968                     package test.pkg {
1969                       public class Bar implements test.pkg.Parcelable {
1970                         method public void removedMethod();
1971                         field public int removedField;
1972                       }
1973                       public class Bar.Inner2 {
1974                         ctor public Bar.Inner2();
1975                       }
1976                       public class Foo {
1977                         ctor public Foo();
1978                         method public void foo();
1979                       }
1980                       public class Foo.Inner {
1981                         ctor public Foo.Inner();
1982                       }
1983                     }
1984                 """
1985         )
1986     }
1987 
1988     @Test
Test include overridden @Deprecated even if annotated with @hidenull1989     fun `Test include overridden @Deprecated even if annotated with @hide`() {
1990         check(
1991             checkDoclava1 = true,
1992             sourceFiles = *arrayOf(
1993                 java(
1994                     """
1995                     package test.pkg;
1996                     @SuppressWarnings("JavaDoc")
1997                     public class Child extends Parent {
1998                         /**
1999                         * @deprecated
2000                         * @hide
2001                         */
2002                         @Deprecated @Override
2003                         public String toString() {
2004                             return "Child";
2005                         }
2006                     }
2007                     """
2008                 ),
2009                 java(
2010                     """
2011                     package test.pkg;
2012                     public class Parent {
2013                         public String toString() {
2014                             return "Parent";
2015                         }
2016                     }
2017                     """
2018                 )
2019             ),
2020             api = """
2021                     package test.pkg {
2022                       public class Child extends test.pkg.Parent {
2023                         ctor public Child();
2024                         method public deprecated java.lang.String toString();
2025                       }
2026                       public class Parent {
2027                         ctor public Parent();
2028                       }
2029                     }
2030                     """,
2031             dexApi = """
2032                 Ltest/pkg/Child;
2033                 Ltest/pkg/Child;-><init>()V
2034                 Ltest/pkg/Child;->toString()Ljava/lang/String;
2035                 Ltest/pkg/Parent;
2036                 Ltest/pkg/Parent;-><init>()V
2037                 Ltest/pkg/Parent;->toString()Ljava/lang/String;
2038             """
2039         )
2040     }
2041 
2042     @Test
Test invalid class namenull2043     fun `Test invalid class name`() {
2044         // Regression test for b/73018978
2045         check(
2046             checkDoclava1 = false,
2047             sourceFiles = *arrayOf(
2048                 kotlin(
2049                     "src/test/pkg/Foo.kt",
2050                     """
2051                     @file:JvmName("-Foo")
2052 
2053                     package test.pkg
2054 
2055                     @Suppress("unused")
2056                     inline fun String.printHelloWorld() { println("Hello World") }
2057                     """
2058                 )
2059             ),
2060             api = """
2061                 package test.pkg {
2062                   public final class -Foo {
2063                     method public static void printHelloWorld(java.lang.String);
2064                   }
2065                 }
2066                 """
2067         )
2068     }
2069 
2070     @Test
Indirect Field Includes from Interfacesnull2071     fun `Indirect Field Includes from Interfaces`() {
2072         // Real-world example: include ZipConstants into ZipFile and JarFile
2073         check(
2074             checkDoclava1 = true,
2075             sourceFiles = *arrayOf(
2076                 java(
2077                     """
2078                     package test.pkg1;
2079                     interface MyConstants {
2080                         long CONSTANT1 = 12345;
2081                         long CONSTANT2 = 67890;
2082                         long CONSTANT3 = 42;
2083                     }
2084                     """
2085                 ),
2086                 java(
2087                     """
2088                     package test.pkg1;
2089                     import java.io.Closeable;
2090                     @SuppressWarnings("WeakerAccess")
2091                     public class MyParent implements MyConstants, Closeable {
2092                     }
2093                     """
2094                 ),
2095                 java(
2096                     """
2097                     package test.pkg2;
2098 
2099                     import test.pkg1.MyParent;
2100                     public class MyChild extends MyParent {
2101                     }
2102                     """
2103                 )
2104 
2105             ),
2106             api = """
2107                     package test.pkg1 {
2108                       public class MyParent implements java.io.Closeable {
2109                         ctor public MyParent();
2110                         field public static final long CONSTANT1 = 12345L; // 0x3039L
2111                         field public static final long CONSTANT2 = 67890L; // 0x10932L
2112                         field public static final long CONSTANT3 = 42L; // 0x2aL
2113                       }
2114                     }
2115                     package test.pkg2 {
2116                       public class MyChild extends test.pkg1.MyParent {
2117                         ctor public MyChild();
2118                         field public static final long CONSTANT1 = 12345L; // 0x3039L
2119                         field public static final long CONSTANT2 = 67890L; // 0x10932L
2120                         field public static final long CONSTANT3 = 42L; // 0x2aL
2121                       }
2122                     }
2123                 """
2124         )
2125     }
2126 
2127     @Test
Skip interfaces from packages explicitly hidden via argumentsnull2128     fun `Skip interfaces from packages explicitly hidden via arguments`() {
2129         // Real-world example: HttpResponseCache implements OkCacheContainer but hides the only inherited method
2130         check(
2131             checkDoclava1 = true,
2132             extraArguments = arrayOf(
2133                 "--hide-package", "com.squareup.okhttp"
2134             ),
2135             sourceFiles = *arrayOf(
2136                 java(
2137                     """
2138                     package android.net.http;
2139                     import com.squareup.okhttp.Cache;
2140                     import com.squareup.okhttp.OkCacheContainer;
2141                     import java.io.Closeable;
2142                     import java.net.ResponseCache;
2143                     @SuppressWarnings("JavaDoc")
2144                     public final class HttpResponseCache implements Closeable, OkCacheContainer {
2145                         /** @hide Needed for OkHttp integration. */
2146                         @Override
2147                         public Cache getCache() {
2148                             return delegate.getCache();
2149                         }
2150                     }
2151                     """
2152                 ),
2153                 java(
2154                     """
2155                     package com.squareup.okhttp;
2156                     public interface OkCacheContainer {
2157                       Cache getCache();
2158                     }
2159                     """
2160                 )
2161             ),
2162             api = """
2163                 package android.net.http {
2164                   public final class HttpResponseCache implements java.io.Closeable {
2165                     ctor public HttpResponseCache();
2166                   }
2167                 }
2168                 """
2169         )
2170     }
2171 
2172     @Test
Private API signaturesnull2173     fun `Private API signatures`() {
2174         check(
2175             checkDoclava1 = false, // doclava1 doesn't have the same behavior: see
2176             // https://android-review.googlesource.com/c/platform/external/doclava/+/589515
2177             sourceFiles = *arrayOf(
2178                 java(
2179                     """
2180                         package test.pkg;
2181                         public class Class1 implements MyInterface {
2182                             Class1(int arg) { }
2183                             /** @hide */
2184                             public void method1() { }
2185                             void method2() { }
2186                             private void method3() { }
2187                             public int field1 = 1;
2188                             protected int field2 = 2;
2189                             int field3 = 3;
2190                             float[][] field4 = 3;
2191                             long[] field5 = null;
2192                             private int field6 = 4;
2193                             void myVarargsMethod(int x, String... args) { }
2194 
2195                             public class Inner { // Fully public, should not be included
2196                                  public void publicMethod() { }
2197                             }
2198                         }
2199                     """
2200                 ),
2201 
2202                 java(
2203                     """
2204                         package test.pkg;
2205                         class Class2 {
2206                             public void method4() { }
2207 
2208                             private class Class3 {
2209                                 public void method5() { }
2210                             }
2211                         }
2212                     """
2213                 ),
2214 
2215                 java(
2216                     """
2217                         package test.pkg;
2218                         /** @doconly */
2219                         class Class4 {
2220                             public void method5() { }
2221                         }
2222                     """
2223                 ),
2224 
2225                 java(
2226                     """
2227                         package test.pkg;
2228                         /** @hide */
2229                         @SuppressWarnings("UnnecessaryInterfaceModifier")
2230                         public interface MyInterface {
2231                             public static final String MY_CONSTANT = "5";
2232                         }
2233                     """
2234                 )
2235             ),
2236             privateApi = """
2237                 package test.pkg {
2238                   public class Class1 implements test.pkg.MyInterface {
2239                     ctor  Class1(int);
2240                     method public void method1();
2241                     method  void method2();
2242                     method private void method3();
2243                     method  void myVarargsMethod(int, java.lang.String...);
2244                     field  int field3;
2245                     field  float[][] field4;
2246                     field  long[] field5;
2247                     field private int field6;
2248                   }
2249                    class Class2 {
2250                     ctor  Class2();
2251                     method public void method4();
2252                   }
2253                   private class Class2.Class3 {
2254                     ctor private Class2.Class3();
2255                     method public void method5();
2256                   }
2257                    class Class4 {
2258                     ctor  Class4();
2259                     method public void method5();
2260                   }
2261                   public abstract interface MyInterface {
2262                     field public static final java.lang.String MY_CONSTANT = "5";
2263                   }
2264                 }
2265                 """,
2266             privateDexApi = """
2267                 Ltest/pkg/Class1;-><init>(I)V
2268                 Ltest/pkg/Class1;->method1()V
2269                 Ltest/pkg/Class1;->method2()V
2270                 Ltest/pkg/Class1;->method3()V
2271                 Ltest/pkg/Class1;->myVarargsMethod(I[Ljava/lang/String;)V
2272                 Ltest/pkg/Class1;->field3:I
2273                 Ltest/pkg/Class1;->field4:[[F
2274                 Ltest/pkg/Class1;->field5:[J
2275                 Ltest/pkg/Class1;->field6:I
2276                 Ltest/pkg/Class2;
2277                 Ltest/pkg/Class2;-><init>()V
2278                 Ltest/pkg/Class2;->method4()V
2279                 Ltest/pkg/Class2${"$"}Class3;
2280                 Ltest/pkg/Class2${"$"}Class3;-><init>()V
2281                 Ltest/pkg/Class2${"$"}Class3;->method5()V
2282                 Ltest/pkg/Class4;
2283                 Ltest/pkg/Class4;-><init>()V
2284                 Ltest/pkg/Class4;->method5()V
2285                 Ltest/pkg/MyInterface;
2286                 Ltest/pkg/MyInterface;->MY_CONSTANT:Ljava/lang/String;
2287                 """
2288         )
2289     }
2290 
2291     @Test
Private API signature corner casesnull2292     fun `Private API signature corner cases`() {
2293         // Some corner case scenarios exposed by differences in output from doclava and metalava
2294         check(
2295             checkDoclava1 = false,
2296             sourceFiles = *arrayOf(
2297                 java(
2298                     """
2299                         package test.pkg;
2300                         import android.os.Parcel;
2301                         import android.os.Parcelable;
2302                         import java.util.concurrent.FutureTask;
2303 
2304                         public class Class1 extends PrivateParent implements MyInterface {
2305                             Class1(int arg) { }
2306 
2307                             @Override public String toString() {
2308                                 return "Class1";
2309                             }
2310 
2311                             private abstract class AmsTask extends FutureTask<String> {
2312                                 @Override
2313                                 protected void set(String bundle) {
2314                                     super.set(bundle);
2315                                 }
2316                             }
2317 
2318                             /** @hide */
2319                             public abstract static class TouchPoint implements Parcelable {
2320                             }
2321                         }
2322                     """
2323                 ),
2324 
2325                 java(
2326                     """
2327                         package test.pkg;
2328                         class PrivateParent {
2329                             final String getValue() {
2330                                 return "";
2331                             }
2332                         }
2333                     """
2334                 ),
2335 
2336                 java(
2337                     """
2338                         package test.pkg;
2339                         /** @hide */
2340                         public enum MyEnum {
2341                             FOO, BAR
2342                         }
2343                     """
2344                 ),
2345 
2346                 java(
2347                     """
2348                         package test.pkg;
2349                         @SuppressWarnings("UnnecessaryInterfaceModifier")
2350                         public interface MyInterface {
2351                             public static final String MY_CONSTANT = "5";
2352                         }
2353                     """
2354                 )
2355             ),
2356             privateApi = """
2357                 package test.pkg {
2358                   public class Class1 extends test.pkg.PrivateParent implements test.pkg.MyInterface {
2359                     ctor  Class1(int);
2360                   }
2361                   private abstract class Class1.AmsTask extends java.util.concurrent.FutureTask {
2362                   }
2363                   public static abstract class Class1.TouchPoint implements android.os.Parcelable {
2364                     ctor public Class1.TouchPoint();
2365                   }
2366                   public final class MyEnum extends java.lang.Enum {
2367                     ctor private MyEnum();
2368                     enum_constant public static final test.pkg.MyEnum BAR;
2369                     enum_constant public static final test.pkg.MyEnum FOO;
2370                   }
2371                    class PrivateParent {
2372                     ctor  PrivateParent();
2373                     method  final java.lang.String getValue();
2374                   }
2375                 }
2376                 """,
2377             privateDexApi = """
2378                 Ltest/pkg/Class1;-><init>(I)V
2379                 Ltest/pkg/Class1${"$"}AmsTask;
2380                 Ltest/pkg/Class1${"$"}TouchPoint;
2381                 Ltest/pkg/Class1${"$"}TouchPoint;-><init>()V
2382                 Ltest/pkg/MyEnum;
2383                 Ltest/pkg/MyEnum;-><init>()V
2384                 Ltest/pkg/MyEnum;->valueOf(Ljava/lang/String;)Ltest/pkg/MyEnum;
2385                 Ltest/pkg/MyEnum;->values()[Ltest/pkg/MyEnum;
2386                 Ltest/pkg/MyEnum;->BAR:Ltest/pkg/MyEnum;
2387                 Ltest/pkg/MyEnum;->FOO:Ltest/pkg/MyEnum;
2388                 Ltest/pkg/PrivateParent;
2389                 Ltest/pkg/PrivateParent;-><init>()V
2390                 Ltest/pkg/PrivateParent;->getValue()Ljava/lang/String;
2391                 """
2392         )
2393     }
2394 
2395     @Test
Extend from multiple interfacesnull2396     fun `Extend from multiple interfaces`() {
2397         // Real-world example: XmlResourceParser
2398         check(
2399             checkDoclava1 = true,
2400             checkCompilation = true,
2401             sourceFiles = *arrayOf(
2402                 java(
2403                     """
2404                     package android.content.res;
2405                     import android.util.AttributeSet;
2406                     import org.xmlpull.v1.XmlPullParser;
2407                     import my.AutoCloseable;
2408 
2409                     @SuppressWarnings("UnnecessaryInterfaceModifier")
2410                     public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
2411                         public void close();
2412                     }
2413                     """
2414                 ),
2415                 java(
2416                     """
2417                     package android.util;
2418                     @SuppressWarnings("WeakerAccess")
2419                     public interface AttributeSet {
2420                     }
2421                     """
2422                 ),
2423                 java(
2424                     """
2425                     package my;
2426                     public interface AutoCloseable {
2427                     }
2428                     """
2429                 ),
2430                 java(
2431                     """
2432                     package org.xmlpull.v1;
2433                     @SuppressWarnings("WeakerAccess")
2434                     public interface XmlPullParser {
2435                     }
2436                     """
2437                 )
2438             ),
2439             api = """
2440                 package android.content.res {
2441                   public abstract interface XmlResourceParser implements android.util.AttributeSet my.AutoCloseable org.xmlpull.v1.XmlPullParser {
2442                     method public abstract void close();
2443                   }
2444                 }
2445                 package android.util {
2446                   public abstract interface AttributeSet {
2447                   }
2448                 }
2449                 package my {
2450                   public abstract interface AutoCloseable {
2451                   }
2452                 }
2453                 package org.xmlpull.v1 {
2454                   public abstract interface XmlPullParser {
2455                   }
2456                 }
2457                 """
2458         )
2459     }
2460 
2461     @Test
Including private interfaces from typesnull2462     fun `Including private interfaces from types`() {
2463         check(
2464             checkDoclava1 = true,
2465             sourceFiles = *arrayOf(
2466                 java("""package test.pkg1; interface Interface1 { }"""),
2467                 java("""package test.pkg1; abstract class Class1 { }"""),
2468                 java("""package test.pkg1; abstract class Class2 { }"""),
2469                 java("""package test.pkg1; abstract class Class3 { }"""),
2470                 java("""package test.pkg1; abstract class Class4 { }"""),
2471                 java("""package test.pkg1; abstract class Class5 { }"""),
2472                 java("""package test.pkg1; abstract class Class6 { }"""),
2473                 java("""package test.pkg1; abstract class Class7 { }"""),
2474                 java("""package test.pkg1; abstract class Class8 { }"""),
2475                 java("""package test.pkg1; abstract class Class9 { }"""),
2476                 java(
2477                     """
2478                     package test.pkg1;
2479 
2480                     import java.util.List;
2481                     import java.util.Map;
2482                     public abstract class Usage implements List<Class1> {
2483                        <T extends java.lang.Comparable<? super T>> void sort(java.util.List<T> list) {}
2484                        public Class3 myClass1 = null;
2485                        public List<? extends Class4> myClass2 = null;
2486                        public Map<String, ? extends Class5> myClass3 = null;
2487                        public <T extends Class6> void mySort(List<Class7> list, T element) {}
2488                        public void ellipsisType(Class8... myargs);
2489                        public void arrayType(Class9[] myargs);
2490                     }
2491                     """
2492                 )
2493             ),
2494 
2495             // TODO: Test annotations! (values, annotation classes, etc.)
2496             warnings = """
2497                     src/test/pkg1/Usage.java:12: warning: Parameter myargs references hidden type test.pkg1.Class9[]. [HiddenTypeParameter:121]
2498                     src/test/pkg1/Usage.java:11: warning: Parameter myargs references hidden type test.pkg1.Class8.... [HiddenTypeParameter:121]
2499                     src/test/pkg1/Usage.java:10: warning: Parameter list references hidden type class test.pkg1.Class7. [HiddenTypeParameter:121]
2500                     src/test/pkg1/Usage.java:7: warning: Field Usage.myClass1 references hidden type test.pkg1.Class3. [HiddenTypeParameter:121]
2501                     src/test/pkg1/Usage.java:8: warning: Field Usage.myClass2 references hidden type class test.pkg1.Class4. [HiddenTypeParameter:121]
2502                     src/test/pkg1/Usage.java:9: warning: Field Usage.myClass3 references hidden type class test.pkg1.Class5. [HiddenTypeParameter:121]
2503                     """,
2504             api = """
2505                     package test.pkg1 {
2506                       public abstract class Usage implements java.util.List {
2507                         ctor public Usage();
2508                         method public void arrayType(test.pkg1.Class9[]);
2509                         method public void ellipsisType(test.pkg1.Class8...);
2510                         method public <T extends test.pkg1.Class6> void mySort(java.util.List<test.pkg1.Class7>, T);
2511                         field public test.pkg1.Class3 myClass1;
2512                         field public java.util.List<? extends test.pkg1.Class4> myClass2;
2513                         field public java.util.Map<java.lang.String, ? extends test.pkg1.Class5> myClass3;
2514                       }
2515                     }
2516                 """
2517         )
2518     }
2519 }