xref: /aosp_15_r20/tools/metalava/metalava/src/test/java/com/android/tools/metalava/stub/StubsInterfaceTest.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.stub
18 
19 import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
20 import com.android.tools.metalava.model.text.FileFormat
21 import com.android.tools.metalava.testing.java
22 import org.junit.Test
23 
24 @SuppressWarnings("ALL")
25 class StubsInterfaceTest : AbstractStubsTest() {
26     @Test
Generate stubs for interface classnull27     fun `Generate stubs for interface class`() {
28         // Interface: makes sure the right modifiers etc are shown (and that "package private"
29         // methods
30         // in the interface are taken to be public etc)
31         checkStubs(
32             sourceFiles =
33                 arrayOf(
34                     java(
35                         """
36                     package test.pkg;
37                     public interface Foo {
38                         void foo();
39                     }
40                     """
41                     )
42                 ),
43             source =
44                 """
45                 package test.pkg;
46                 @SuppressWarnings({"unchecked", "deprecation", "all"})
47                 public interface Foo {
48                 public void foo();
49                 }
50                 """,
51             checkTextStubEquivalence = true
52         )
53     }
54 
55     @Test
Check implementing a package private interfacenull56     fun `Check implementing a package private interface`() {
57         // If you implement a package private interface, we just remove it and inline the members
58         // into
59         // the subclass
60 
61         // BUG: Note that we need to implement the parent
62         checkStubs(
63             sourceFiles =
64                 arrayOf(
65                     java(
66                         """
67                     package test.pkg;
68                     public class MyClass implements HiddenInterface {
69                         @Override public void method() { }
70                         @Override public void other() { }
71                     }
72                     """
73                     ),
74                     java(
75                         """
76                     package test.pkg;
77                     public interface OtherInterface {
78                         void other();
79                     }
80                     """
81                     ),
82                     java(
83                         """
84                     package test.pkg;
85                     interface HiddenInterface extends OtherInterface {
86                         void method() { }
87                         String CONSTANT = "MyConstant";
88                     }
89                     """
90                     )
91                 ),
92             source =
93                 """
94                 package test.pkg;
95                 @SuppressWarnings({"unchecked", "deprecation", "all"})
96                 public class MyClass implements test.pkg.OtherInterface {
97                 public MyClass() { throw new RuntimeException("Stub!"); }
98                 public void method() { throw new RuntimeException("Stub!"); }
99                 public void other() { throw new RuntimeException("Stub!"); }
100                 public static final java.lang.String CONSTANT = "MyConstant";
101                 }
102                 """,
103             checkTextStubEquivalence = true
104         )
105     }
106 
107     @Test
Check generating constants in interface without inline-able initializersnull108     fun `Check generating constants in interface without inline-able initializers`() {
109         checkStubs(
110             sourceFiles =
111                 arrayOf(
112                     java(
113                         """
114                     package test.pkg;
115                     public interface MyClass {
116                         String[] CONSTANT1 = {"MyConstant","MyConstant2"};
117                         boolean CONSTANT2 = Boolean.getBoolean(System.getenv("VAR1"));
118                         int CONSTANT3 = Integer.parseInt(System.getenv("VAR2"));
119                         String CONSTANT4 = null;
120                     }
121                     """
122                     )
123                 ),
124             warnings = "",
125             source =
126                 """
127                 package test.pkg;
128                 @SuppressWarnings({"unchecked", "deprecation", "all"})
129                 public interface MyClass {
130                 public static final java.lang.String[] CONSTANT1 = null;
131                 public static final boolean CONSTANT2 = false;
132                 public static final int CONSTANT3 = 0; // 0x0
133                 public static final java.lang.String CONSTANT4 = null;
134                 }
135                 """,
136             checkTextStubEquivalence = true
137         )
138     }
139 
140     @Test
Check generating type parameters in interface listnull141     fun `Check generating type parameters in interface list`() {
142         checkStubs(
143             format = FileFormat.V2,
144             sourceFiles =
145                 arrayOf(
146                     java(
147                         """
148                     package test.pkg;
149 
150                     @SuppressWarnings("NullableProblems")
151                     public class GenericsInInterfaces<T> implements Comparable<GenericsInInterfaces> {
152                         @Override
153                         public int compareTo(GenericsInInterfaces o) {
154                             return 0;
155                         }
156 
157                         void foo(T bar) {
158                         }
159                     }
160                     """
161                     )
162                 ),
163             api =
164                 """
165                 package test.pkg {
166                   public class GenericsInInterfaces<T> implements java.lang.Comparable<test.pkg.GenericsInInterfaces> {
167                     ctor public GenericsInInterfaces();
168                     method public int compareTo(test.pkg.GenericsInInterfaces);
169                   }
170                 }
171                 """,
172             source =
173                 """
174                 package test.pkg;
175                 @SuppressWarnings({"unchecked", "deprecation", "all"})
176                 public class GenericsInInterfaces<T> implements java.lang.Comparable<test.pkg.GenericsInInterfaces> {
177                 public GenericsInInterfaces() { throw new RuntimeException("Stub!"); }
178                 public int compareTo(test.pkg.GenericsInInterfaces o) { throw new RuntimeException("Stub!"); }
179                 }
180                 """
181         )
182     }
183 
184     @Test
Check generating required stubs from hidden super classes and interfacesnull185     fun `Check generating required stubs from hidden super classes and interfaces`() {
186         checkStubs(
187             sourceFiles =
188                 arrayOf(
189                     java(
190                         """
191                     package test.pkg;
192                     public class MyClass extends HiddenSuperClass implements HiddenInterface, PublicInterface2 {
193                         public void myMethod() { }
194                         @Override public void publicInterfaceMethod2() { }
195                     }
196                     """
197                     ),
198                     java(
199                         """
200                     package test.pkg;
201                     class HiddenSuperClass extends PublicSuperParent {
202                         @Override public void inheritedMethod2() { }
203                         @Override public void publicInterfaceMethod() { }
204                         @Override public void publicMethod() {}
205                         @Override public void publicMethod2() {}
206                     }
207                     """
208                     ),
209                     java(
210                         """
211                     package test.pkg;
212                     public abstract class PublicSuperParent {
213                         public void inheritedMethod1() {}
214                         public void inheritedMethod2() {}
215                         public abstract void publicMethod() {}
216                     }
217                     """
218                     ),
219                     java(
220                         """
221                     package test.pkg;
222                     interface HiddenInterface extends PublicInterface {
223                         int MY_CONSTANT = 5;
224                         void hiddenInterfaceMethod();
225                     }
226                     """
227                     ),
228                     java(
229                         """
230                     package test.pkg;
231                     public interface PublicInterface {
232                         void publicInterfaceMethod();
233                     }
234                     """
235                     ),
236                     java(
237                         """
238                     package test.pkg;
239                     public interface PublicInterface2 {
240                         void publicInterfaceMethod2();
241                     }
242                     """
243                     )
244                 ),
245             warnings = "",
246             api =
247                 """
248                     package test.pkg {
249                       public class MyClass extends test.pkg.PublicSuperParent implements test.pkg.PublicInterface test.pkg.PublicInterface2 {
250                         ctor public MyClass();
251                         method public void myMethod();
252                         method public void publicInterfaceMethod();
253                         method public void publicInterfaceMethod2();
254                         method public void publicMethod();
255                         method public void publicMethod2();
256                         field public static final int MY_CONSTANT = 5; // 0x5
257                       }
258                       public interface PublicInterface {
259                         method public void publicInterfaceMethod();
260                       }
261                       public interface PublicInterface2 {
262                         method public void publicInterfaceMethod2();
263                       }
264                       public abstract class PublicSuperParent {
265                         ctor public PublicSuperParent();
266                         method public void inheritedMethod1();
267                         method public void inheritedMethod2();
268                         method public abstract void publicMethod();
269                       }
270                     }
271                 """,
272             source =
273                 """
274                 package test.pkg;
275                 @SuppressWarnings({"unchecked", "deprecation", "all"})
276                 public class MyClass extends test.pkg.PublicSuperParent implements test.pkg.PublicInterface, test.pkg.PublicInterface2 {
277                 public MyClass() { throw new RuntimeException("Stub!"); }
278                 public void inheritedMethod2() { throw new RuntimeException("Stub!"); }
279                 public void myMethod() { throw new RuntimeException("Stub!"); }
280                 public void publicInterfaceMethod() { throw new RuntimeException("Stub!"); }
281                 public void publicInterfaceMethod2() { throw new RuntimeException("Stub!"); }
282                 public void publicMethod() { throw new RuntimeException("Stub!"); }
283                 public void publicMethod2() { throw new RuntimeException("Stub!"); }
284                 public static final int MY_CONSTANT = 5; // 0x5
285                 }
286                 """
287         )
288     }
289 
290     @Test
Rewriting type parameters in interfaces from hidden super classes and in throws listsnull291     fun `Rewriting type parameters in interfaces from hidden super classes and in throws lists`() {
292         checkStubs(
293             format = FileFormat.V2,
294             sourceFiles =
295                 arrayOf(
296                     java(
297                         """
298                     package test.pkg;
299 
300                     import java.io.IOException;
301                     import java.util.List;
302                     import java.util.Map;
303 
304                     @SuppressWarnings({"RedundantThrows", "WeakerAccess"})
305                     public class Generics {
306                         @SuppressWarnings("HiddenSuperclass") // HiddenParent is not public
307                         public class MyClass<X, Y extends Number> extends HiddenParent<X, Y> implements PublicInterface<X, Y> {
308                         }
309 
310                         class HiddenParent<M, N extends Number> extends PublicParent<M, N> {
311                             @SuppressWarnings("ReferencesHidden") // MyThrowable is not public
312                             public Map<M, Map<N, String>> createMap(List<M> list) throws MyThrowable {
313                                 return null;
314                             }
315 
316                             protected List<M> foo() {
317                                 return null;
318                             }
319 
320                         }
321 
322                         class MyThrowable extends IOException {
323                         }
324 
325                         public abstract class PublicParent<A, B extends Number> {
326                             protected abstract List<A> foo();
327                         }
328 
329                         public interface PublicInterface<A, B> {
330                             Map<A, Map<B, String>> createMap(List<A> list) throws IOException;
331                         }
332                     }
333                     """
334                     )
335                 ),
336             warnings = "",
337             api =
338                 """
339                 package test.pkg {
340                   public class Generics {
341                     ctor public Generics();
342                   }
343                   public class Generics.MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
344                     ctor public Generics.MyClass();
345                     method public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X>) throws java.io.IOException;
346                     method protected java.util.List<X> foo();
347                   }
348                   public static interface Generics.PublicInterface<A, B> {
349                     method public java.util.Map<A,java.util.Map<B,java.lang.String>> createMap(java.util.List<A>) throws java.io.IOException;
350                   }
351                   public abstract class Generics.PublicParent<A, B extends java.lang.Number> {
352                     ctor public Generics.PublicParent();
353                     method protected abstract java.util.List<A> foo();
354                   }
355                 }
356                 """,
357             source =
358                 if (SUPPORT_TYPE_USE_ANNOTATIONS) {
359                     """
360                 package test.pkg;
361                 @SuppressWarnings({"unchecked", "deprecation", "all"})
362                 public class Generics {
363                 public Generics() { throw new RuntimeException("Stub!"); }
364                 @SuppressWarnings({"unchecked", "deprecation", "all"})
365                 public class MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
366                 public MyClass() { throw new RuntimeException("Stub!"); }
367                 protected java.util.List<X> foo() { throw new RuntimeException("Stub!"); }
368                 public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X> list) throws java.io.IOException { throw new RuntimeException("Stub!"); }
369                 }
370                 @SuppressWarnings({"unchecked", "deprecation", "all"})
371                 public static interface PublicInterface<A, B> {
372                 public java.util.Map<A,java.util.Map<B,java.lang.String>> createMap(java.util.List<A> list) throws java.io.IOException;
373                 }
374                 @SuppressWarnings({"unchecked", "deprecation", "all"})
375                 public abstract class PublicParent<A, B extends java.lang.Number> {
376                 public PublicParent() { throw new RuntimeException("Stub!"); }
377                 protected abstract java.util.List<A> foo();
378                 }
379                 }
380                 """
381                 } else {
382                     """
383                 package test.pkg;
384                 @SuppressWarnings({"unchecked", "deprecation", "all"})
385                 public class Generics {
386                 public Generics() { throw new RuntimeException("Stub!"); }
387                 @SuppressWarnings({"unchecked", "deprecation", "all"})
388                 public class MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
389                 public MyClass() { throw new RuntimeException("Stub!"); }
390                 public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X> list) throws java.io.IOException { throw new RuntimeException("Stub!"); }
391                 protected java.util.List<X> foo() { throw new RuntimeException("Stub!"); }
392                 }
393                 @SuppressWarnings({"unchecked", "deprecation", "all"})
394                 public static interface PublicInterface<A, B> {
395                 public java.util.Map<A,java.util.Map<B,java.lang.String>> createMap(java.util.List<A> list) throws java.io.IOException;
396                 }
397                 @SuppressWarnings({"unchecked", "deprecation", "all"})
398                 public abstract class PublicParent<A, B extends java.lang.Number> {
399                 public PublicParent() { throw new RuntimeException("Stub!"); }
400                 protected abstract java.util.List<A> foo();
401                 }
402                 }
403                 """
404                 }
405         )
406     }
407 
408     @Test
Interface extending multiple interfacesnull409     fun `Interface extending multiple interfaces`() {
410         // Ensure that we handle sorting correctly where we're mixing super classes and implementing
411         // interfaces
412         // Real-world example: XmlResourceParser
413         check(
414             checkCompilation = true,
415             sourceFiles =
416                 arrayOf(
417                     java(
418                         """
419                     package android.content.res;
420                     import android.util.AttributeSet;
421                     import org.xmlpull.v1.XmlPullParser;
422 
423                     @SuppressWarnings("UnnecessaryInterfaceModifier")
424                     public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
425                         public void close();
426                     }
427                     """
428                     ),
429                     java(
430                         """
431                     package android.util;
432                     public interface AttributeSet {
433                     }
434                     """
435                     ),
436                     java(
437                         """
438                     package java.lang;
439                     public interface AutoCloseable {
440                     }
441                     """
442                     ),
443                     java(
444                         """
445                     package org.xmlpull.v1;
446                     public interface XmlPullParser {
447                     }
448                     """
449                     )
450                 ),
451             stubFiles =
452                 arrayOf(
453                     java(
454                         """
455                     package android.content.res;
456                     @SuppressWarnings({"unchecked", "deprecation", "all"})
457                     public interface XmlResourceParser extends android.util.AttributeSet, java.lang.AutoCloseable, org.xmlpull.v1.XmlPullParser {
458                     public void close();
459                     }
460                     """
461                     )
462                 )
463         )
464     }
465 
466     @Test
Functional Interfacesnull467     fun `Functional Interfaces`() {
468         checkStubs(
469             skipEmitPackages = emptyList(),
470             sourceFiles =
471                 arrayOf(
472                     java(
473                         """
474                     package com.android.metalava.test;
475 
476                     @SuppressWarnings("something") @FunctionalInterface
477                     public interface MyInterface {
478                         void run();
479                     }
480                     """
481                     )
482                 ),
483             warnings = "",
484             source =
485                 """
486                 package com.android.metalava.test;
487                 @SuppressWarnings({"unchecked", "deprecation", "all"})
488                 @java.lang.FunctionalInterface
489                 public interface MyInterface {
490                 public void run();
491                 }
492                 """,
493             checkTextStubEquivalence = true
494         )
495     }
496 
497     @Test
Extends and implements multiple interfacesnull498     fun `Extends and implements multiple interfaces`() {
499         check(
500             checkCompilation = true,
501             sourceFiles =
502                 arrayOf(
503                     java(
504                         """
505                     package test.pkg;
506 
507                     public class MainClass extends MyParentClass implements MyInterface1, MyInterface2 {
508                     }
509                     """
510                     ),
511                     java(
512                         """
513                     package test.pkg;
514 
515                     public interface MyInterface1 { }
516                     """
517                     ),
518                     java(
519                         """
520                     package test.pkg;
521 
522                     public interface MyInterface2 { }
523                     """
524                     ),
525                     java(
526                         """
527                     package test.pkg;
528 
529                     public class MyParentClass { }
530                     """
531                     )
532                 ),
533             stubFiles =
534                 arrayOf(
535                     java(
536                         """
537                         package test.pkg;
538                         @SuppressWarnings({"unchecked", "deprecation", "all"})
539                         public class MainClass extends test.pkg.MyParentClass implements test.pkg.MyInterface1, test.pkg.MyInterface2 {
540                         public MainClass() { throw new RuntimeException("Stub!"); }
541                         }
542                     """
543                     )
544                 )
545         )
546     }
547 }
548