Home | History | Annotate | Download | only in metalava
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.tools.metalava
     18 
     19 import org.intellij.lang.annotations.Language
     20 import org.junit.Test
     21 
     22 class ApiFromTextTest : DriverTest() {
     23 
     24     @Test
     25     fun `Loading a signature file and writing the API back out`() {
     26         val source = """
     27                 package test.pkg {
     28                   public class MyTest {
     29                     ctor public MyTest();
     30                     method public int clamp(int);
     31                     method public java.lang.Double convert(java.lang.Float);
     32                     field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
     33                     field public java.lang.Number myNumber;
     34                   }
     35                 }
     36                 """
     37 
     38         check(
     39             compatibilityMode = true,
     40             signatureSource = source,
     41             api = source
     42         )
     43     }
     44 
     45     @Test
     46     fun `Infer fully qualified names from shorter names`() {
     47         check(
     48             compatibilityMode = true,
     49             extraArguments = arrayOf("--annotations-in-signatures"),
     50             signatureSource = """
     51                 package test.pkg {
     52                   public class MyTest {
     53                     ctor public MyTest();
     54                     method public int clamp(int);
     55                     method public double convert(@Nullable Float, byte[], Iterable<java.io.File>);
     56                   }
     57                 }
     58                 """,
     59             api = """
     60                 package test.pkg {
     61                   public class MyTest {
     62                     ctor public MyTest();
     63                     method public int clamp(int);
     64                     method public double convert(@androidx.annotation.Nullable java.lang.Float, byte[], java.lang.Iterable<java.io.File>);
     65                   }
     66                 }
     67                 """
     68         )
     69     }
     70 
     71     @Test
     72     fun `Loading a signature file with alternate modifier order`() {
     73         // Regression test for https://github.com/android/android-ktx/issues/242
     74         val source = """
     75                 package test.pkg {
     76                   deprecated public class MyTest {
     77                     ctor deprecated public Foo(int, int);
     78                     method deprecated public static final void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
     79                     field deprecated public static java.util.List<java.lang.String> LIST;
     80                   }
     81                 }
     82                 """
     83         check(
     84             compatibilityMode = true,
     85             signatureSource = source,
     86             api = """
     87                 package test.pkg {
     88                   public deprecated class MyTest {
     89                     ctor public deprecated MyTest(int, int);
     90                     method public static final deprecated void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
     91                     field public static deprecated java.util.List<java.lang.String> LIST;
     92                   }
     93                 }
     94                 """
     95         )
     96     }
     97 
     98     @Test
     99     fun `Test generics, superclasses and interfaces`() {
    100         val source = """
    101             package a.b.c {
    102               public abstract interface MyStream<T, S extends a.b.c.MyStream<T, S>> {
    103               }
    104             }
    105             package test.pkg {
    106               public final class Foo extends java.lang.Enum {
    107                 ctor public Foo(int);
    108                 ctor public Foo(int, int);
    109                 method public static test.pkg.Foo valueOf(java.lang.String);
    110                 method public static final test.pkg.Foo[] values();
    111                 enum_constant public static final test.pkg.Foo A;
    112                 enum_constant public static final test.pkg.Foo B;
    113               }
    114               public abstract interface MyBaseInterface {
    115               }
    116               public abstract interface MyInterface<T> implements test.pkg.MyBaseInterface {
    117               }
    118               public abstract interface MyInterface2<T extends java.lang.Number> implements test.pkg.MyBaseInterface {
    119               }
    120               public static abstract class MyInterface2.Range<T extends java.lang.Comparable<? super T>> {
    121                 ctor public MyInterface2.Range();
    122               }
    123               public static class MyInterface2.TtsSpan<C extends test.pkg.MyInterface<?>> {
    124                 ctor public MyInterface2.TtsSpan();
    125               }
    126               public final class Test<T> {
    127                 ctor public Test();
    128                 method public abstract <T extends java.util.Collection<java.lang.String>> T addAllTo(T);
    129                 method public static <T & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T>);
    130                 method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws java.lang.Throwable;
    131                 field public static java.util.List<java.lang.String> LIST;
    132               }
    133             }
    134                 """
    135 
    136         check(
    137             compatibilityMode = true,
    138             signatureSource = source,
    139             api = source
    140         )
    141     }
    142 
    143     @Test
    144     fun `Test constants`() {
    145         val source = """
    146                 package test.pkg {
    147                   public class Foo2 {
    148                     ctor public Foo2();
    149                     field public static final java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
    150                     field public static final char HEX_INPUT = 61184; // 0xef00 '\uef00'
    151                     field protected int field00;
    152                     field public static final boolean field01 = true;
    153                     field public static final int field02 = 42; // 0x2a
    154                     field public static final long field03 = 42L; // 0x2aL
    155                     field public static final short field04 = 5; // 0x5
    156                     field public static final byte field05 = 5; // 0x5
    157                     field public static final char field06 = 99; // 0x0063 'c'
    158                     field public static final float field07 = 98.5f;
    159                     field public static final double field08 = 98.5;
    160                     field public static final java.lang.String field09 = "String with \"escapes\" and \u00a9...";
    161                     field public static final double field10 = (0.0/0.0);
    162                     field public static final double field11 = (1.0/0.0);
    163                   }
    164                 }
    165                 """
    166 
    167         check(
    168             compatibilityMode = true,
    169             signatureSource = source,
    170             api = source
    171         )
    172     }
    173 
    174     @Test
    175     fun `Test inner classes`() {
    176         val source = """
    177                 package test.pkg {
    178                   public abstract class Foo {
    179                     ctor public Foo();
    180                     method public static final deprecated synchronized void method1();
    181                     method public static final deprecated synchronized void method2();
    182                   }
    183                   protected static final deprecated class Foo.Inner1 {
    184                     ctor protected Foo.Inner1();
    185                   }
    186                   protected static abstract deprecated class Foo.Inner2 {
    187                     ctor protected Foo.Inner2();
    188                   }
    189                   protected static abstract deprecated interface Foo.Inner3 {
    190                     method public default void method3();
    191                     method public static void method4(int);
    192                   }
    193                 }
    194                 """
    195 
    196         check(
    197             compatibilityMode = true,
    198             signatureSource = source,
    199             api = source
    200         )
    201     }
    202 
    203     @Test
    204     fun `Test throws`() {
    205         val source = """
    206                 package test.pkg {
    207                   public final class Test<T> {
    208                     ctor public Test();
    209                     method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws java.lang.Throwable;
    210                   }
    211                 }
    212                 """
    213 
    214         check(
    215             compatibilityMode = true,
    216             signatureSource = source,
    217             api = source
    218         )
    219     }
    220 
    221     @Test
    222     fun `Loading a signature file with annotations on classes, fields, methods and parameters`() {
    223         @Language("TEXT")
    224         val source = """
    225                 package test.pkg {
    226                   @androidx.annotation.UiThread public class MyTest {
    227                     ctor public MyTest();
    228                     method @androidx.annotation.IntRange(from=10, to=20) public int clamp(int);
    229                     method public java.lang.Double? convert(java.lang.Float myPublicName);
    230                     field public java.lang.Number? myNumber;
    231                   }
    232                 }
    233                 """
    234 
    235         check(
    236             compatibilityMode = false,
    237             inputKotlinStyleNulls = true,
    238             omitCommonPackages = false,
    239             signatureSource = source,
    240             api = source
    241         )
    242     }
    243 
    244     @Test
    245     fun `Enums and annotations`() {
    246         // In non-compat mode we write interfaces out as "@interface" (instead of abstract class)
    247         // and similarly for enums we write "enum" instead of "class extends java.lang.Enum".
    248         // Make sure we can also read this back in.
    249         val source = """
    250                 package android.annotation {
    251                   public @interface SuppressLint {
    252                     method public abstract String[] value();
    253                   }
    254                 }
    255                 package test.pkg {
    256                   public enum Foo {
    257                     enum_constant public static final test.pkg.Foo A;
    258                     enum_constant public static final test.pkg.Foo B;
    259                   }
    260                 }
    261                 """
    262 
    263         check(
    264             compatibilityMode = false,
    265             outputKotlinStyleNulls = false,
    266             signatureSource = source,
    267             api = source
    268         )
    269     }
    270 
    271     @Test
    272     fun `Enums and annotations exported to compat`() {
    273         val source = """
    274                 package android.annotation {
    275                   public @interface SuppressLint {
    276                   }
    277                 }
    278                 package test.pkg {
    279                   public final enum Foo {
    280                     enum_constant public static final test.pkg.Foo A;
    281                     enum_constant public static final test.pkg.Foo B;
    282                   }
    283                 }
    284                 """
    285 
    286         check(
    287             compatibilityMode = true,
    288             signatureSource = source,
    289             api = """
    290                 package android.annotation {
    291                   public abstract class SuppressLint implements java.lang.annotation.Annotation {
    292                   }
    293                 }
    294                 package test.pkg {
    295                   public final class Foo extends java.lang.Enum {
    296                     enum_constant public static final test.pkg.Foo A;
    297                     enum_constant public static final test.pkg.Foo B;
    298                   }
    299                 }
    300                 """
    301         )
    302     }
    303 
    304     @Test
    305     fun `Sort throws list by full name`() {
    306         check(
    307             compatibilityMode = true,
    308             signatureSource = """
    309                     package android.accounts {
    310                       public abstract interface AccountManagerFuture<V> {
    311                         method public abstract boolean cancel(boolean);
    312                         method public abstract V getResult() throws android.accounts.OperationCanceledException, java.io.IOException, android.accounts.AuthenticatorException;
    313                         method public abstract V getResult(long, java.util.concurrent.TimeUnit) throws android.accounts.OperationCanceledException, java.io.IOException, android.accounts.AuthenticatorException;
    314                         method public abstract boolean isCancelled();
    315                         method public abstract boolean isDone();
    316                       }
    317                     }
    318                     """,
    319             api = """
    320                     package android.accounts {
    321                       public abstract interface AccountManagerFuture<V> {
    322                         method public abstract boolean cancel(boolean);
    323                         method public abstract V getResult() throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
    324                         method public abstract V getResult(long, java.util.concurrent.TimeUnit) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
    325                         method public abstract boolean isCancelled();
    326                         method public abstract boolean isDone();
    327                       }
    328                     }
    329                     """
    330         )
    331     }
    332 
    333     @Test
    334     fun `Loading a signature file with default values`() {
    335         @Language("TEXT")
    336         val source = """
    337                 package test.pkg {
    338                   public final class Foo {
    339                     ctor public Foo();
    340                     method public final void error(int p = "42", java.lang.Integer? int2 = "null");
    341                   }
    342                   public class Foo2 {
    343                     ctor public Foo2();
    344                     method public void foo(java.lang.String! = "null", java.lang.String! = "\"Hello World\"", int = "42");
    345                   }
    346                 }
    347                 """
    348 
    349         check(
    350             compatibilityMode = false,
    351             inputKotlinStyleNulls = true,
    352             omitCommonPackages = false,
    353             signatureSource = source,
    354             api = source
    355         )
    356     }
    357 }