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