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 }