• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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