1 /* 2 * Copyright (C) 2023 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.model.testsuite.methoditem 18 19 import com.android.tools.metalava.model.provider.InputFormat 20 import com.android.tools.metalava.model.testsuite.BaseModelTest 21 import com.android.tools.metalava.testing.KnownSourceFiles 22 import com.android.tools.metalava.testing.java 23 import com.android.tools.metalava.testing.kotlin 24 import com.google.common.truth.Truth.assertWithMessage 25 import org.junit.Assert.assertEquals 26 import org.junit.Assert.assertNull 27 import org.junit.Test 28 29 /** Common tests for implementations of [ParameterItem]. */ 30 class CommonParameterItemTest : BaseModelTest() { 31 32 @Test Test deprecated parameter by annotationnull33 fun `Test deprecated parameter by annotation`() { 34 runCodebaseTest( 35 signature( 36 """ 37 // Signature format: 2.0 38 package test.pkg { 39 public class Bar { 40 method public void foo(@Deprecated int); 41 } 42 } 43 """ 44 ), 45 java( 46 """ 47 package test.pkg; 48 49 public class Bar { 50 public void foo(@Deprecated int i) {} 51 } 52 """ 53 ), 54 kotlin( 55 """ 56 package test.pkg 57 58 class Bar { 59 fun foo(@Deprecated i: Int) {} 60 } 61 """ 62 ), 63 ) { 64 val parameterItem = 65 codebase.assertClass("test.pkg.Bar").methods().single().parameters().single() 66 val annotation = parameterItem.modifiers.annotations().single() 67 if (inputFormat == InputFormat.KOTLIN) { 68 assertEquals("kotlin.Deprecated", annotation.qualifiedName) 69 } else { 70 assertEquals("java.lang.Deprecated", annotation.qualifiedName) 71 } 72 assertEquals("originallyDeprecated", true, parameterItem.originallyDeprecated) 73 assertEquals("effectivelyDeprecated", true, parameterItem.effectivelyDeprecated) 74 } 75 } 76 77 @Test Test not deprecated parameternull78 fun `Test not deprecated parameter`() { 79 runCodebaseTest( 80 signature( 81 """ 82 // Signature format: 2.0 83 package test.pkg { 84 public class Bar { 85 method public void foo(int); 86 } 87 } 88 """ 89 ), 90 java( 91 """ 92 package test.pkg; 93 94 public class Bar { 95 public void foo(int i) {} 96 } 97 """ 98 ), 99 kotlin( 100 """ 101 package test.pkg 102 103 class Bar { 104 fun foo(i: Int) {} 105 } 106 """ 107 ), 108 ) { 109 val parameterItem = 110 codebase.assertClass("test.pkg.Bar").methods().single().parameters().single() 111 assertEquals("originallyDeprecated", false, parameterItem.originallyDeprecated) 112 assertEquals("effectivelyDeprecated", false, parameterItem.effectivelyDeprecated) 113 } 114 } 115 116 @Test Test publicName reports correct name when specifiednull117 fun `Test publicName reports correct name when specified`() { 118 runCodebaseTest( 119 inputSet( 120 signature( 121 """ 122 // Signature format: 2.0 123 package test.pkg { 124 public class Bar { 125 method public void foo(int baz); 126 } 127 } 128 """ 129 ), 130 ), 131 inputSet( 132 KnownSourceFiles.supportParameterName, 133 java( 134 """ 135 package test.pkg; 136 137 import androidx.annotation.ParameterName; 138 139 public class Bar { 140 public void foo(@ParameterName("baz") int baz) {} 141 } 142 """ 143 ), 144 ), 145 inputSet( 146 kotlin( 147 """ 148 package test.pkg 149 150 class Bar { 151 fun foo(baz: Int) {} 152 } 153 """ 154 ), 155 ), 156 ) { 157 val parameterItem = 158 codebase.assertClass("test.pkg.Bar").methods().single().parameters().single() 159 assertEquals("name()", "baz", parameterItem.name()) 160 assertEquals("publicName()", "baz", parameterItem.publicName()) 161 } 162 } 163 164 @Test Test publicName reports correct name when not specifiednull165 fun `Test publicName reports correct name when not specified`() { 166 runCodebaseTest( 167 signature( 168 """ 169 // Signature format: 2.0 170 package test.pkg { 171 public class Bar { 172 method public void foo(int); 173 } 174 } 175 """ 176 ), 177 java( 178 """ 179 package test.pkg; 180 181 public class Bar { 182 public void foo(int baz) {} 183 } 184 """ 185 ), 186 // Kotlin treats all parameter names as public. 187 ) { 188 val parameterItem = 189 codebase.assertClass("test.pkg.Bar").methods().single().parameters().single() 190 assertNull("publicName()", parameterItem.publicName()) 191 } 192 } 193 194 @Test Test publicName reports correct name when called on binary class - Object#equalsnull195 fun `Test publicName reports correct name when called on binary class - Object#equals`() { 196 runCodebaseTest( 197 java( 198 """ 199 package test.pkg; 200 201 public abstract class Bar { 202 } 203 """ 204 ), 205 // No need to check any other sources as the source is not being tested, only used to 206 // trigger the test run. 207 ) { 208 val parameterItem = 209 codebase 210 .assertResolvedClass("java.lang.Object") 211 .assertMethod("equals", "java.lang.Object") 212 .parameters() 213 .single() 214 // For some reason Object.equals(Object obj) provides the actual parameter name. 215 // Probably, because it was compiled with a late enough version of javac, and/or with 216 // the appropriate options to record the parameter name. 217 assertEquals("name()", "obj", parameterItem.name()) 218 assertEquals("publicName()", "obj", parameterItem.publicName()) 219 } 220 } 221 222 @Test Test publicName reports correct name when called on binary class - ViewGroup#onLayoutnull223 fun `Test publicName reports correct name when called on binary class - ViewGroup#onLayout`() { 224 runCodebaseTest( 225 java( 226 """ 227 package test.pkg; 228 229 public abstract class Bar extends android.view.ViewGroup { 230 } 231 """ 232 ), 233 // No need to check any other sources as the source is not being tested, only used to 234 // trigger the test run. 235 ) { 236 val parameterItems = 237 codebase 238 .assertResolvedClass("android.view.ViewGroup") 239 .assertMethod("onLayout", "boolean, int, int, int, int") 240 .parameters() 241 // For some reason ViewGroup.onLayout(boolean, int, int, int, int) does not provide the 242 // actual parameter name. Probably, because it was compiled with an older version of 243 // javac, and/or without the appropriate options to record the parameter name. 244 val expectedNames = listOf("p", "p1", "p2", "p3", "p4") 245 for (i in parameterItems.indices) { 246 val parameterItem = parameterItems[i] 247 val expectedName = expectedNames[i] 248 assertEquals("$i:name()", expectedName, parameterItem.name()) 249 assertNull("$i:publicName()$parameterItem", parameterItem.publicName()) 250 } 251 } 252 } 253 254 @Test Test nullability of parameter annotated with @not-type-use-NonNullnull255 fun `Test nullability of parameter annotated with @not-type-use-NonNull`() { 256 runCodebaseTest( 257 inputSet( 258 KnownSourceFiles.notTypeUseNonNullSource, 259 java( 260 """ 261 package test.pkg; 262 import java.util.Map; 263 import not.type.use.NonNull; 264 265 public class Foo<T> { 266 public void method1(@NonNull String p); 267 public void method2(@NonNull String[] p); 268 public void method3(@NonNull String[][] p); 269 public void method4(@NonNull T p); 270 public void method5(@NonNull Map.Entry<T, String> p); 271 } 272 """ 273 ), 274 ), 275 inputSet( 276 signature( 277 """ 278 // Signature format: 2.0 279 package test.pkg { 280 public class Foo<T> { 281 method public void method1(@NonNull String); 282 method public void method2(@NonNull String[]); 283 method public void method3(@NonNull String[][]); 284 method public void method4(@NonNull T); 285 method public void method5(@NonNull java.util.Map.Entry<T, String>); 286 } 287 } 288 """ 289 ), 290 ), 291 // Kotlin does not care about different nullability annotations. 292 ) { 293 val expectedTypes = 294 mapOf( 295 "method1" to "java.lang.String", 296 "method2" to "java.lang.String![]", 297 "method3" to "java.lang.String![]![]", 298 "method4" to "T", 299 "method5" to "java.util.Map.Entry<T!,java.lang.String!>", 300 ) 301 for (method in codebase.assertClass("test.pkg.Foo").methods()) { 302 val name = method.name() 303 val expectedType = expectedTypes[name]!! 304 // Compare the kotlin style format of the parameter to ensure that only the 305 // outermost type is affected by the not-type-use nullability annotation. 306 val type = method.parameters().single().type() 307 assertWithMessage(name) 308 .that(type.toTypeString(kotlinStyleNulls = true)) 309 .isEqualTo(expectedType) 310 } 311 } 312 } 313 314 @Test Test nullability of parameter annotated with @not-type-use-Nullablenull315 fun `Test nullability of parameter annotated with @not-type-use-Nullable`() { 316 runCodebaseTest( 317 inputSet( 318 KnownSourceFiles.notTypeUseNullableSource, 319 java( 320 """ 321 package test.pkg; 322 import java.util.Map; 323 import not.type.use.Nullable; 324 325 public class Foo<T> { 326 public void method1(@Nullable String p); 327 public void method2(@Nullable String[] p); 328 public void method3(@Nullable String[][] p); 329 public void method4(@Nullable T p); 330 public void method5(@Nullable Map.Entry<T, String> p); 331 } 332 """ 333 ), 334 ), 335 inputSet( 336 signature( 337 """ 338 // Signature format: 2.0 339 package test.pkg { 340 public class Foo<T> { 341 method public void method1(@Nullable String); 342 method public void method2(@Nullable String[]); 343 method public void method3(@Nullable String[][]); 344 method public void method4(@Nullable T); 345 method public void method5(@Nullable java.util.Map.Entry<T, String>); 346 } 347 } 348 """ 349 ), 350 ), 351 // Kotlin does not care about different nullability annotations. 352 ) { 353 val expectedTypes = 354 mapOf( 355 "method1" to "java.lang.String?", 356 "method2" to "java.lang.String![]?", 357 "method3" to "java.lang.String![]![]?", 358 "method4" to "T?", 359 "method5" to "java.util.Map.Entry<T!,java.lang.String!>?", 360 ) 361 for (method in codebase.assertClass("test.pkg.Foo").methods()) { 362 val name = method.name() 363 val expectedType = expectedTypes[name]!! 364 // Compare the kotlin style format of the parameter to ensure that only the 365 // outermost type is affected by the not-type-use nullability annotation. 366 val type = method.parameters().single().type() 367 assertWithMessage(name) 368 .that(type.toTypeString(kotlinStyleNulls = true)) 369 .isEqualTo(expectedType) 370 } 371 } 372 } 373 374 @Test Test nullability of non-Kotlin varargsnull375 fun `Test nullability of non-Kotlin varargs`() { 376 runCodebaseTest( 377 inputSet( 378 KnownSourceFiles.notTypeUseNonNullSource, 379 KnownSourceFiles.notTypeUseNullableSource, 380 java( 381 """ 382 package test.pkg; 383 import not.type.use.NonNull; 384 import not.type.use.Nullable; 385 386 public class Foo { 387 public void nullable(@Nullable String... p); 388 public void nonNull(@NonNull String... p); 389 public void platform(String... p); 390 } 391 """ 392 ), 393 ), 394 inputSet( 395 signature( 396 """ 397 // Signature format: 2.0 398 package test.pkg { 399 public class Foo { 400 method public void nullable(@Nullable String... p); 401 method public void nonNull(@NonNull String... p); 402 method public void platform(String... p); 403 } 404 } 405 """ 406 ), 407 ), 408 // Kotlin does not care about different nullability annotations. 409 ) { 410 val expectedTypes = 411 mapOf( 412 "nullable" to "java.lang.String!...?", 413 "nonNull" to "java.lang.String!...", 414 "platform" to "java.lang.String!...!", 415 ) 416 for (method in codebase.assertClass("test.pkg.Foo").methods()) { 417 val name = method.name() 418 val expectedType = expectedTypes[name]!! 419 // Compare the kotlin style format of the parameter to ensure that only the 420 // outermost type is affected by the not-type-use nullability annotation. 421 val type = method.parameters().single().type() 422 assertWithMessage(name) 423 .that(type.toTypeString(kotlinStyleNulls = true)) 424 .isEqualTo(expectedType) 425 } 426 } 427 } 428 429 @Test Test nullability of Kotlin varargs lastnull430 fun `Test nullability of Kotlin varargs last`() { 431 runCodebaseTest( 432 kotlin( 433 """ 434 package test.pkg 435 436 class Foo { 437 fun nullable(vararg p: String?) 438 fun nonNull(vararg p: String) 439 } 440 """ 441 ), 442 signature( 443 """ 444 // Signature format: 5.0 445 package test.pkg { 446 public class Foo { 447 method public void nullable(String?... p); 448 method public void nonNull(String... p); 449 } 450 } 451 """ 452 ), 453 // Kotlin does not care about different nullability annotations. 454 ) { 455 val expectedTypes = 456 mapOf( 457 "nullable" to "java.lang.String?...", 458 "nonNull" to "java.lang.String...", 459 ) 460 for (method in codebase.assertClass("test.pkg.Foo").methods()) { 461 val name = method.name() 462 val parameterItem = method.parameters().single() 463 464 // Make sure that it is modelled as a varargs parameter. 465 assertWithMessage("$name isVarArgs").that(parameterItem.isVarArgs()).isTrue() 466 467 // Compare the kotlin style format of the parameter to ensure that only the 468 // outermost type is affected by the not-type-use nullability annotation. 469 val type = parameterItem.type() 470 val expectedType = expectedTypes[name]!! 471 assertWithMessage("$name type") 472 .that(type.toTypeString(kotlinStyleNulls = true)) 473 .isEqualTo(expectedType) 474 } 475 } 476 } 477 478 @Test Test nullability of Kotlin varargs not-lastnull479 fun `Test nullability of Kotlin varargs not-last`() { 480 runCodebaseTest( 481 kotlin( 482 """ 483 package test.pkg 484 485 fun nullable(vararg p: Any?, i: String = "") {} 486 fun nonNull(vararg p: Any, i: String = "") {} 487 """ 488 ), 489 ) { 490 val expectedTypes = 491 mapOf( 492 "nullable" to "java.lang.Object?[]", 493 "nonNull" to "java.lang.Object[]", 494 ) 495 for (method in codebase.assertClass("test.pkg.TestKt").methods()) { 496 val name = method.name() 497 val parameterItem = method.parameters().first() 498 499 // Make sure that it is modelled as a varargs parameter. 500 assertWithMessage("$name isVarArgs").that(parameterItem.isVarArgs()).isTrue() 501 502 // Compare the kotlin style format of the parameter to ensure that only the 503 // outermost type is affected by the not-type-use nullability annotation. 504 val type = parameterItem.type() 505 val expectedType = expectedTypes[name]!! 506 assertWithMessage(name) 507 .that(type.toTypeString(kotlinStyleNulls = true)) 508 .isEqualTo(expectedType) 509 } 510 } 511 } 512 513 @Test Test nullability of Kotlin varargs last in inline reified funnull514 fun `Test nullability of Kotlin varargs last in inline reified fun`() { 515 runCodebaseTest( 516 kotlin( 517 """ 518 package test.pkg 519 520 inline fun <reified T> nullable(vararg elements: T?) = listOf(*elements) 521 inline fun <reified T> nonNull(vararg elements: T) = listOf(*elements) 522 """ 523 ), 524 ) { 525 val expectedTypes = 526 mapOf( 527 "nullable" to "T?...", 528 "nonNull" to "T...", 529 ) 530 for (method in codebase.assertClass("test.pkg.TestKt").methods()) { 531 val name = method.name() 532 val parameterItem = method.parameters().single() 533 534 // Make sure that it is modelled as a varargs parameter. 535 assertWithMessage("$name isVarArgs").that(parameterItem.isVarArgs()).isTrue() 536 537 // Compare the kotlin style format of the parameter to ensure that only the 538 // outermost type is affected by the not-type-use nullability annotation. 539 val type = parameterItem.type() 540 val expectedType = expectedTypes[name]!! 541 assertWithMessage("$name type") 542 .that(type.toTypeString(kotlinStyleNulls = true)) 543 .isEqualTo(expectedType) 544 } 545 } 546 } 547 } 548