1 /* 2 * Copyright (C) 2025 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.typealiasitem 18 19 import com.android.tools.metalava.model.PrimitiveTypeItem 20 import com.android.tools.metalava.model.testsuite.BaseModelTest 21 import com.android.tools.metalava.testing.createAndroidModuleDescription 22 import com.android.tools.metalava.testing.createCommonModuleDescription 23 import com.android.tools.metalava.testing.createProjectDescription 24 import com.android.tools.metalava.testing.kotlin 25 import com.google.common.truth.Truth.assertThat 26 import org.junit.Test 27 28 class CommonTypeAliasItemTest : BaseModelTest() { 29 @Test accessing type alias from codebasenull30 fun `accessing type alias from codebase`() { 31 runCodebaseTest( 32 kotlin( 33 """ 34 package test.pkg 35 typealias Foo = String 36 """ 37 ), 38 signature( 39 """ 40 // Signature format: 5.0 41 package test.pkg { 42 public typealias Foo = String; 43 } 44 """ 45 ), 46 ) { 47 codebase.assertTypeAlias("test.pkg.Foo") 48 } 49 } 50 51 @Test accessing type alias from packagenull52 fun `accessing type alias from package`() { 53 runCodebaseTest( 54 kotlin( 55 """ 56 package test.pkg 57 typealias Foo = String 58 """ 59 ), 60 signature( 61 """ 62 // Signature format: 5.0 63 package test.pkg { 64 public typealias Foo = String; 65 } 66 """ 67 ), 68 ) { 69 val pkg = codebase.assertPackage("test.pkg") 70 assertThat(pkg.typeAliases()).hasSize(1) 71 } 72 } 73 74 @Test type alias namenull75 fun `type alias name`() { 76 runCodebaseTest( 77 kotlin( 78 """ 79 package test.pkg 80 typealias Foo = String 81 """ 82 ), 83 signature( 84 """ 85 // Signature format: 5.0 86 package test.pkg { 87 public typealias Foo = String; 88 } 89 """ 90 ), 91 ) { 92 val typeAlias = codebase.assertTypeAlias("test.pkg.Foo") 93 assertThat(typeAlias.qualifiedName).isEqualTo("test.pkg.Foo") 94 assertThat(typeAlias.simpleName).isEqualTo("Foo") 95 } 96 } 97 98 @Test type alias visibilitynull99 fun `type alias visibility`() { 100 runCodebaseTest( 101 kotlin( 102 """ 103 package test.pkg 104 typealias PublicTypeAlias = String 105 @PublishedApi 106 internal typealias InternalTypeAlias = String 107 """ 108 ), 109 signature( 110 """ 111 // Signature format: 5.0 112 package test.pkg { 113 public typealias PublicTypeAlias = String; 114 @kotlin.PublishedApi internal typealias InternalTypeAlias = String; 115 } 116 """ 117 ), 118 ) { 119 val publicTypeAlias = codebase.assertTypeAlias("test.pkg.PublicTypeAlias") 120 assertThat(publicTypeAlias.modifiers.getVisibilityString()).isEqualTo("public") 121 122 val internalTypeAlias = codebase.assertTypeAlias("test.pkg.InternalTypeAlias") 123 assertThat(internalTypeAlias.modifiers.getVisibilityString()).isEqualTo("internal") 124 assertThat(internalTypeAlias.modifiers.annotations().single().qualifiedName) 125 .isEqualTo("kotlin.PublishedApi") 126 } 127 } 128 129 @Test private type alias visibilitynull130 fun `private type alias visibility`() { 131 // No signature case: private APIs aren't written to signature files 132 runCodebaseTest( 133 kotlin( 134 """ 135 package test.pkg 136 private typealias PrivateTypeAlias = String 137 """ 138 ), 139 ) { 140 val privateTypeAlias = codebase.assertTypeAlias("test.pkg.PrivateTypeAlias") 141 assertThat(privateTypeAlias.modifiers.getVisibilityString()).isEqualTo("private") 142 } 143 } 144 145 @Test annotations on type aliasnull146 fun `annotations on type alias`() { 147 runCodebaseTest( 148 inputSet( 149 kotlin( 150 """ 151 package test.pkg 152 typealias Unannotated = String 153 @AnnoA @AnnoB typealias Annotated = String 154 """ 155 ), 156 kotlin( 157 """ 158 package test.pkg 159 @Target(AnnotationTarget.TYPEALIAS) 160 annotation class AnnoA 161 @Target(AnnotationTarget.TYPEALIAS) 162 annotation class AnnoB 163 """ 164 ) 165 ), 166 inputSet( 167 signature( 168 """ 169 // Signature format: 5.0 170 package test.pkg { 171 @test.pkg.AnnoA @test.pkg.AnnoB public typealias Annotated = String; 172 public typealias Unannotated = String; 173 } 174 """ 175 ) 176 ), 177 ) { 178 val unannotated = codebase.assertTypeAlias("test.pkg.Unannotated") 179 assertThat(unannotated.modifiers.annotations()).isEmpty() 180 val annotated = codebase.assertTypeAlias("test.pkg.Annotated") 181 val annotations = annotated.modifiers.annotations() 182 assertThat(annotations).hasSize(2) 183 assertThat(annotations.map { it.qualifiedName }) 184 .containsExactly("test.pkg.AnnoA", "test.pkg.AnnoB") 185 } 186 } 187 188 @Test basic type alias typesnull189 fun `basic type alias types`() { 190 runCodebaseTest( 191 kotlin( 192 """ 193 package test.pkg 194 typealias PrimitiveType = Int 195 typealias ArrayType = IntArray 196 typealias ClassType = String 197 """ 198 ), 199 // This type aliases are sorted to match the kotlin code instead of alphabetically. 200 signature( 201 """ 202 // Signature format: 5.0 203 package test.pkg { 204 public typealias PrimitiveType = int; 205 public typealias ArrayType = int[]; 206 public typealias ClassType = String; 207 } 208 """ 209 ), 210 ) { 211 val primitiveType = codebase.assertTypeAlias("test.pkg.PrimitiveType").aliasedType 212 primitiveType.assertPrimitiveTypeItem { 213 assertThat(kind).isEqualTo(PrimitiveTypeItem.Primitive.INT) 214 } 215 val arrayType = codebase.assertTypeAlias("test.pkg.ArrayType").aliasedType 216 arrayType.assertArrayTypeItem { 217 componentType.assertPrimitiveTypeItem { 218 assertThat(kind).isEqualTo(PrimitiveTypeItem.Primitive.INT) 219 } 220 } 221 val classType = codebase.assertTypeAlias("test.pkg.ClassType").aliasedType 222 classType.assertClassTypeItem { assertThat(isString()).isTrue() } 223 } 224 } 225 226 @Test functional type alias typenull227 fun `functional type alias type`() { 228 // No signature case: functional types are just parsed as class types (b/169798041). 229 runCodebaseTest( 230 kotlin( 231 """ 232 package test.pkg 233 typealias FunctionType = (String) -> Int 234 """ 235 ), 236 ) { 237 val functionType = codebase.assertTypeAlias("test.pkg.FunctionType").aliasedType 238 functionType.assertLambdaTypeItem { 239 assertThat(parameterTypes).hasSize(1) 240 assertThat(parameterTypes.single().isString()).isTrue() 241 returnType.assertPrimitiveTypeItem { 242 assertThat(kind).isEqualTo(PrimitiveTypeItem.Primitive.INT) 243 } 244 } 245 } 246 } 247 248 @Test type alias referencing other type aliasnull249 fun `type alias referencing other type alias`() { 250 // type aliases should be expanded to the underlying type 251 // No signature case: the type aliases should be output as their underlying type, so having 252 // a type alias as a type in a signature file wouldn't make sense. 253 runCodebaseTest( 254 kotlin( 255 """ 256 package test.pkg 257 typealias Foo = String 258 typealias Bar = List<Foo> 259 """ 260 ) 261 ) { 262 val foo = codebase.assertTypeAlias("test.pkg.Foo") 263 assertThat(foo.aliasedType.isString()).isTrue() 264 val bar = codebase.assertTypeAlias("test.pkg.Bar") 265 bar.aliasedType.assertClassTypeItem { 266 assertThat(qualifiedName).isEqualTo("java.util.List") 267 assertThat(arguments).hasSize(1) 268 arguments.single().assertWildcardItem { 269 assertThat(extendsBound!!.isString()).isTrue() 270 } 271 } 272 } 273 } 274 275 @Test type parameter lists on type aliasesnull276 fun `type parameter lists on type aliases`() { 277 // Note: bounds are not allowed on type alias parameters. The aliased type is not allowed to 278 // be a type parameter itself. 279 runCodebaseTest( 280 kotlin( 281 """ 282 package test.pkg 283 typealias NoTypeParameter = String 284 typealias OneTypeParameter<T> = List<T> 285 typealias TwoTypeParameter<K, V> = Map.Entry<K, V> 286 """ 287 ), 288 signature( 289 """ 290 // Signature format: 5.0 291 package test.pkg { 292 public typealias NoTypeParameter = String; 293 public typealias OneTypeParameter<T> = java.util.List<? extends T>; 294 public typealias TwoTypeParameter<K, V> = java.util.Map.Entry<? extends K,? extends V>; 295 } 296 """ 297 ), 298 ) { 299 val noTypeParameter = codebase.assertTypeAlias("test.pkg.NoTypeParameter") 300 assertThat(noTypeParameter.typeParameterList).isEmpty() 301 302 val oneTypeParameter = codebase.assertTypeAlias("test.pkg.OneTypeParameter") 303 assertThat(oneTypeParameter.typeParameterList).hasSize(1) 304 val t = oneTypeParameter.typeParameterList.single() 305 assertThat(t.name()).isEqualTo("T") 306 val listT = oneTypeParameter.aliasedType 307 listT.assertClassTypeItem { 308 assertThat(qualifiedName).isEqualTo("java.util.List") 309 assertThat(arguments).hasSize(1) 310 arguments.single().assertWildcardItem { 311 extendsBound.assertVariableTypeItem { 312 assertThat(asTypeParameter).isEqualTo(t) 313 assertThat(t.type()).isEqualTo(this) 314 } 315 } 316 } 317 318 val twoTypeParameter = codebase.assertTypeAlias("test.pkg.TwoTypeParameter") 319 assertThat(twoTypeParameter.typeParameterList).hasSize(2) 320 val k = twoTypeParameter.typeParameterList[0] 321 assertThat(k.name()).isEqualTo("K") 322 val v = twoTypeParameter.typeParameterList[1] 323 assertThat(v.name()).isEqualTo("V") 324 val mapEntryKV = twoTypeParameter.aliasedType 325 mapEntryKV.assertClassTypeItem { 326 assertThat(qualifiedName).isEqualTo("java.util.Map.Entry") 327 assertThat(arguments).hasSize(2) 328 arguments[0].assertWildcardItem { 329 extendsBound.assertVariableTypeItem { 330 assertThat(asTypeParameter).isEqualTo(k) 331 assertThat(k.type()).isEqualTo(this) 332 } 333 } 334 arguments[1].assertWildcardItem { 335 extendsBound.assertVariableTypeItem { 336 assertThat(asTypeParameter).isEqualTo(v) 337 assertThat(v.type()).isEqualTo(this) 338 } 339 } 340 } 341 } 342 } 343 344 @Test expect actual typealiasnull345 fun `expect actual typealias`() { 346 val commonSource = 347 kotlin( 348 "commonMain/src/test/pkg/Foo.kt", 349 """ 350 package test.pkg 351 expect class Foo 352 """ 353 ) 354 val androidSource = 355 kotlin( 356 "androidMain/src/test/pkg/Foo.android.kt", 357 """ 358 package test.pkg 359 actual typealias Foo = String 360 """ 361 ) 362 runCodebaseTest( 363 inputSet( 364 androidSource, 365 commonSource, 366 ), 367 projectDescription = 368 createProjectDescription( 369 createAndroidModuleDescription(arrayOf(androidSource)), 370 createCommonModuleDescription(arrayOf(commonSource)), 371 ), 372 ) { 373 val fooAlias = codebase.assertTypeAlias("test.pkg.Foo") 374 assertThat(fooAlias.aliasedType.isString()).isTrue() 375 } 376 } 377 378 @Test type alias package emitnull379 fun `type alias package emit`() { 380 runCodebaseTest( 381 kotlin( 382 """ 383 package test.pkg 384 typealias Foo = String 385 """ 386 ), 387 signature( 388 """ 389 // Signature format: 5.0 390 package test.pkg { 391 public typealias Foo = String; 392 } 393 """ 394 ), 395 ) { 396 val pkg = codebase.assertPackage("test.pkg") 397 assertThat(pkg.emit).isTrue() 398 } 399 } 400 } 401