1 /* <lambda>null2 * Copyright (C) 2024 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.fielditem 18 19 import com.android.tools.metalava.model.FieldItem 20 import com.android.tools.metalava.model.testsuite.BaseModelTest 21 import com.android.tools.metalava.model.testsuite.assertHasNonNullNullability 22 import com.android.tools.metalava.model.testsuite.assertHasNullableNullability 23 import com.android.tools.metalava.model.testsuite.runNullabilityTest 24 import com.android.tools.metalava.testing.KnownSourceFiles 25 import com.android.tools.metalava.testing.java 26 import com.android.tools.metalava.testing.kotlin 27 import com.google.common.truth.Truth.assertWithMessage 28 import java.io.PrintWriter 29 import java.io.StringWriter 30 import kotlin.test.assertEquals 31 import org.junit.Test 32 33 /** Common tests for implementations of [FieldItem]. */ 34 class CommonFieldItemTest : BaseModelTest() { 35 36 @Test 37 fun `Test access type parameter of outer class`() { 38 runCodebaseTest( 39 signature( 40 """ 41 // Signature format: 2.0 42 package test.pkg { 43 public class Outer<O> { 44 } 45 public class Outer.Middle { 46 } 47 public class Outer.Middle.Inner { 48 field public O field; 49 } 50 } 51 """ 52 ), 53 java( 54 """ 55 package test.pkg; 56 57 public class Outer<O> { 58 private Outer() {} 59 60 public class Middle { 61 private Middle() {} 62 public class Inner { 63 private Inner() {} 64 public O field; 65 } 66 } 67 } 68 """ 69 ), 70 ) { 71 val oTypeParameter = codebase.assertClass("test.pkg.Outer").typeParameterList.single() 72 val fieldType = 73 codebase.assertClass("test.pkg.Outer.Middle.Inner").assertField("field").type() 74 75 fieldType.assertReferencesTypeParameter(oTypeParameter) 76 } 77 } 78 79 @Test 80 fun `Test implicit nullability of enum constant`() { 81 runNullabilityTest( 82 java( 83 """ 84 package test.pkg; 85 86 public enum Foo { 87 ENUM1 88 } 89 """ 90 ), 91 signature( 92 """ 93 // Signature format: 2.0 94 package test.pkg { 95 public enum Foo { 96 enum_constant public test.pkg.Foo ENUM1; 97 } 98 } 99 """ 100 ), 101 kotlin( 102 """ 103 package test.pkg 104 105 enum class Foo { 106 ENUM1 107 } 108 """ 109 ), 110 signature( 111 """ 112 // Signature format: 5.0 113 package test.pkg { 114 public enum Foo { 115 enum_constant public test.pkg.Foo ENUM1; 116 } 117 } 118 """ 119 ), 120 ) { 121 val enumConstant = codebase.assertClass("test.pkg.Foo").fields().single() 122 123 // Annotations should not be added as it is implicitly non-null. 124 enumConstant.type().assertHasNonNullNullability(false) 125 } 126 } 127 128 @Test 129 fun `Test implicit nullability of static final String`() { 130 runNullabilityTest( 131 java( 132 """ 133 package test.pkg; 134 135 public class Foo { 136 public static final String CONST = "CONST"; 137 } 138 """ 139 ), 140 signature( 141 """ 142 // Signature format: 2.0 143 package test.pkg { 144 public class Foo { 145 field public static final String CONST = "CONST"; 146 } 147 } 148 """ 149 ), 150 kotlin( 151 """ 152 package test.pkg 153 154 class Foo { 155 companion object { 156 const val CONST = "CONST" 157 } 158 } 159 """ 160 ), 161 signature( 162 """ 163 // Signature format: 5.0 164 package test.pkg { 165 public class Foo { 166 field public static final String CONST = "CONST"; 167 } 168 } 169 """ 170 ), 171 ) { 172 val stringConstant = codebase.assertClass("test.pkg.Foo").assertField("CONST") 173 174 stringConstant.type().assertHasNonNullNullability(false) 175 } 176 } 177 178 @Test 179 fun `Test implicit nullability of companion object`() { 180 runCodebaseTest( 181 // Only Kotlin has companion objects. 182 kotlin( 183 """ 184 package test.pkg 185 186 class Foo { 187 companion object { 188 } 189 } 190 """ 191 ), 192 ) { 193 val companionObject = codebase.assertClass("test.pkg.Foo").fields().single() 194 195 companionObject.type().assertHasNonNullNullability(false) 196 } 197 } 198 199 @Test 200 fun `Test nullability of field annotated with @NonNull or kotlin equivalent`() { 201 runNullabilityTest( 202 java( 203 """ 204 package test.pkg; 205 import libcore.util.NonNull; 206 207 public class Foo { 208 @NonNull 209 public String field; 210 } 211 """ 212 ), 213 signature( 214 """ 215 // Signature format: 2.0 216 package test.pkg { 217 public class Foo { 218 field @NonNull public String field; 219 } 220 } 221 """ 222 ), 223 kotlin( 224 """ 225 package test.pkg 226 227 class Foo { 228 var field: String = "" 229 } 230 """ 231 ), 232 signature( 233 """ 234 // Signature format: 5.0 235 package test.pkg { 236 public class Foo { 237 field public String field; 238 } 239 } 240 """ 241 ), 242 ) { 243 val field = codebase.assertClass("test.pkg.Foo").assertField("field") 244 245 // Do not check the annotation as type use annotations are ambiguous in signature files 246 // that do not specify `kotlin-name-type-order=yes` 247 field.type().assertHasNonNullNullability() 248 } 249 } 250 251 @Test 252 fun `Test nullability of field annotated with @not-type-use-NonNull`() { 253 runCodebaseTest( 254 inputSet( 255 KnownSourceFiles.notTypeUseNonNullSource, 256 java( 257 """ 258 package test.pkg; 259 import java.util.Map; 260 import not.type.use.NonNull; 261 262 public class Foo<T> { 263 @NonNull public String field1; 264 @NonNull public String[] field2; 265 @NonNull public String[][] field3; 266 @NonNull public T field4; 267 @NonNull public Map.Entry<T, String> field5; 268 } 269 """ 270 ), 271 ), 272 inputSet( 273 signature( 274 """ 275 // Signature format: 2.0 276 package test.pkg { 277 public class Foo<T> { 278 field @NonNull public String field1; 279 field @NonNull public String[] field2; 280 field @NonNull public String[][] field3; 281 field @NonNull public T field4; 282 field @NonNull public java.util.Map.Entry<T, String> field5; 283 } 284 } 285 """ 286 ), 287 ), 288 // Kotlin does not care about different nullability annotations. 289 ) { 290 val expectedTypes = 291 mapOf( 292 "field1" to "java.lang.String", 293 "field2" to "java.lang.String![]", 294 "field3" to "java.lang.String![]![]", 295 "field4" to "T", 296 "field5" to "java.util.Map.Entry<T!,java.lang.String!>", 297 ) 298 for (field in codebase.assertClass("test.pkg.Foo").fields()) { 299 val name = field.name() 300 val expectedType = expectedTypes[name]!! 301 // Compare the kotlin style format of the field to ensure that only the outermost 302 // type is affected by the not-type-use nullability annotation. 303 assertWithMessage(name) 304 .that(field.type().toTypeString(kotlinStyleNulls = true)) 305 .isEqualTo(expectedType) 306 } 307 } 308 } 309 310 @Test 311 fun `Test nullability of field annotated with @not-type-use-Nullable`() { 312 runCodebaseTest( 313 inputSet( 314 KnownSourceFiles.notTypeUseNullableSource, 315 java( 316 """ 317 package test.pkg; 318 import java.util.Map; 319 import not.type.use.Nullable; 320 321 public class Foo<T> { 322 @Nullable public String field1; 323 @Nullable public String[] field2; 324 @Nullable public String[][] field3; 325 @Nullable public T field4; 326 @Nullable public Map.Entry<T, String> field5; 327 } 328 """ 329 ), 330 ), 331 inputSet( 332 signature( 333 """ 334 // Signature format: 2.0 335 package test.pkg { 336 public class Foo<T> { 337 field @Nullable public String field1; 338 field @Nullable public String[] field2; 339 field @Nullable public String[][] field3; 340 field @Nullable public T field4; 341 field @Nullable public java.util.Map.Entry<T, String> field5; 342 } 343 } 344 """ 345 ), 346 ), 347 // Kotlin does not care about different nullability annotations. 348 ) { 349 val expectedTypes = 350 mapOf( 351 "field1" to "java.lang.String?", 352 "field2" to "java.lang.String![]?", 353 "field3" to "java.lang.String![]![]?", 354 "field4" to "T?", 355 "field5" to "java.util.Map.Entry<T!,java.lang.String!>?", 356 ) 357 for (field in codebase.assertClass("test.pkg.Foo").fields()) { 358 val name = field.name() 359 val expectedType = expectedTypes[name]!! 360 // Compare the kotlin style format of the field to ensure that only the outermost 361 // type is affected by the not-type-use nullability annotation. 362 assertWithMessage(name) 363 .that(field.type().toTypeString(kotlinStyleNulls = true)) 364 .isEqualTo(expectedType) 365 } 366 } 367 } 368 369 @Test 370 fun `Test nullability of non-null field annotated with @Nullable or kotlin equivalent`() { 371 runNullabilityTest( 372 java( 373 """ 374 package test.pkg; 375 import not.type.use.Nullable; 376 377 public class Foo { 378 @Nullable 379 public static final String CONST = "CONST"; 380 } 381 """ 382 ), 383 signature( 384 """ 385 // Signature format: 2.0 386 package test.pkg { 387 public class Foo { 388 field @Nullable public static final String CONST = "CONST"; 389 } 390 } 391 """ 392 ), 393 kotlin( 394 """ 395 package test.pkg 396 397 class Foo { 398 companion object { 399 const val CONST: String? = "CONST" 400 } 401 } 402 """ 403 ), 404 signature( 405 """ 406 // Signature format: 5.0 407 package test.pkg { 408 public class Foo { 409 field public static final String? CONST = "CONST"; 410 } 411 } 412 """ 413 ), 414 ) { 415 val stringConstant = codebase.assertClass("test.pkg.Foo").assertField("CONST") 416 417 // Do not check the annotation as type use annotations are ambiguous in signature files 418 // that do not specify `kotlin-name-type-order=yes` 419 stringConstant.type().assertHasNullableNullability() 420 } 421 } 422 423 @Test 424 fun `Test implicit nullability of constant field initialized from @NonNull method`() { 425 runCodebaseTest( 426 inputSet( 427 KnownSourceFiles.nonNullSource, 428 java( 429 """ 430 package test.pkg; 431 import android.annotation.NonNull; 432 433 public class Foo { 434 public static final String CONST = method(); 435 @NonNull 436 private static String method() {return "CONST";} 437 } 438 """ 439 ), 440 ), 441 inputSet( 442 kotlin( 443 """ 444 package test.pkg 445 446 class Foo { 447 companion object { 448 const val CONST = method() 449 private fun method() = "CONST" 450 } 451 } 452 """ 453 ), 454 ), 455 ) { 456 val stringConstant = codebase.assertClass("test.pkg.Foo").assertField("CONST") 457 458 stringConstant.type().assertHasNonNullNullability(false) 459 } 460 } 461 462 @Test 463 fun `Test handling of Float MIN_NORMAL`() { 464 runCodebaseTest( 465 signature( 466 """ 467 // Signature format: 2.0 468 package test.pkg { 469 public class Test { 470 field public static final float MIN_NORMAL1 = 1.17549435E-38f; 471 field public static final float MIN_NORMAL2 = 1.1754944E-38f; 472 field public static final float MIN_NORMAL3 = 0x1.0p-126f; 473 } 474 } 475 """ 476 ), 477 java( 478 """ 479 package test.pkg; 480 481 public class Test { 482 private Test() {} 483 484 public static final float MIN_NORMAL1 = 1.17549435E-38f; 485 public static final float MIN_NORMAL2 = 1.1754944E-38f; 486 public static final float IN_NORMAL3 = 0x1.0p-126f; 487 } 488 """ 489 ), 490 ) { 491 val testClass = codebase.assertClass("test.pkg.Test") 492 493 val minNormalBits = java.lang.Float.MIN_NORMAL.toBits() 494 for (field in testClass.fields()) { 495 val value = field.initialValue(true) as Float 496 val valueBits = value.toBits() 497 assertEquals( 498 minNormalBits, 499 valueBits, 500 message = 501 "field ${field.name()} - expected ${Integer.toHexString(minNormalBits)}, found ${Integer.toHexString(valueBits)}" 502 ) 503 504 val written = 505 StringWriter() 506 .apply { 507 PrintWriter(this).use { out -> field.writeValueWithSemicolon(out) } 508 } 509 .toString() 510 511 assertEquals(" = 1.17549435E-38f;", written, message = "field ${field.name()}") 512 } 513 } 514 } 515 } 516