1 /*
2  * Copyright 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 androidx.compose.lint
18 
19 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
20 import com.android.tools.lint.detector.api.Detector
21 import com.android.tools.lint.detector.api.Issue
22 import org.junit.Test
23 import org.junit.runner.RunWith
24 import org.junit.runners.Parameterized
25 
26 @RunWith(Parameterized::class)
27 class AsCollectionDetectorTest(val types: CollectionType) : LintDetectorTest() {
28 
getDetectornull29     override fun getDetector(): Detector = AsCollectionDetector()
30 
31     override fun getIssues(): MutableList<Issue> = mutableListOf(AsCollectionDetector.ISSUE)
32 
33     private val collectionTilde = "~".repeat(types.collection.length)
34 
35     @Test
36     fun immutableAsImmutable() {
37         lint()
38             .files(
39                 ScatterMapClass,
40                 ScatterSetClass,
41                 ObjectListClass,
42                 kotlin(
43                     """
44                         package androidx.compose.lint
45 
46                         import androidx.collection.${types.immutable}
47 
48                         fun foo(collection: ${types.immutable}${types.params}): ${types.collection}${types.params} =
49                             collection.as${types.collection}()
50                         """
51                 )
52             )
53             .run()
54             .expect(
55                 """
56 src/androidx/compose/lint/test.kt:7: Error: Use method as${types.collection}() only for public API usage [AsCollectionCall]
57                             collection.as${types.collection}()
58                             ~~~~~~~~~~~~~$collectionTilde~~
59 1 errors, 0 warnings
60             """
61             )
62     }
63 
64     @Test
mutableAsImmutablenull65     fun mutableAsImmutable() {
66         lint()
67             .files(
68                 ScatterMapClass,
69                 ScatterSetClass,
70                 ObjectListClass,
71                 kotlin(
72                     """
73                         package androidx.compose.lint
74 
75                         import androidx.collection.Mutable${types.immutable}
76 
77                         fun foo(collection: Mutable${types.immutable}${types.params}): ${types.collection}${types.params} =
78                             collection.as${types.collection}()
79                         """
80                 )
81             )
82             .run()
83             .expect(
84                 """
85 src/androidx/compose/lint/test.kt:7: Error: Use method as${types.collection}() only for public API usage [AsCollectionCall]
86                             collection.as${types.collection}()
87                             ~~~~~~~~~~~~~$collectionTilde~~
88 1 errors, 0 warnings
89             """
90             )
91     }
92 
93     @Test
mutableAsMutablenull94     fun mutableAsMutable() {
95         lint()
96             .files(
97                 ScatterMapClass,
98                 ScatterSetClass,
99                 ObjectListClass,
100                 kotlin(
101                     """
102                         package androidx.compose.lint
103 
104                         import androidx.collection.Mutable${types.immutable}
105 
106                         fun foo(collection: Mutable${types.immutable}${types.params}): Mutable${types.collection}${types.params} =
107                             collection.asMutable${types.collection}()
108                         """
109                 )
110             )
111             .run()
112             .expect(
113                 """
114 src/androidx/compose/lint/test.kt:7: Error: Use method asMutable${types.collection}() only for public API usage [AsCollectionCall]
115                             collection.asMutable${types.collection}()
116                             ~~~~~~~~~~~~~~~~~~~~$collectionTilde~~
117 1 errors, 0 warnings
118             """
119             )
120     }
121 
122     @Test
nonCollectionAsnull123     fun nonCollectionAs() {
124         lint()
125             .files(
126                 kotlin(
127                     """
128                         package androidx.compose.lint
129 
130                         fun foo(): ${types.collection}${types.params} =
131                             WeirdCollection().as${types.collection}()
132 
133                         class WeirdCollection {
134                             fun asList(): List<String>? = null
135                             fun asSet(): Set<String>? = null
136                             fun asMap(): Map<String, String>? = null
137                         }
138                         """
139                 )
140             )
141             .run()
142             .expectClean()
143     }
144 
145     class CollectionType(val immutable: String, val collection: String, val params: String)
146 
147     companion object {
148         @JvmStatic
149         @Parameterized.Parameters(name = "{0}")
initParametersnull150         fun initParameters() =
151             listOf(
152                 CollectionType("ScatterMap", "Map", "<String, String>"),
153                 CollectionType("ScatterSet", "Set", "<String>"),
154                 CollectionType("ObjectList", "List", "<String>")
155             )
156 
157         val ScatterMapClass =
158             kotlin(
159                 """
160             package androidx.collection
161             sealed class ScatterMap<K, V> {
162                 fun asMap(): Map<K, V> = mapOf()
163             }
164 
165             class MutableScatterMap<K, V> : ScatterMap<K, V>() {
166                 fun asMutableMap(): MutableMap<K, V> = mutableMapOf()
167             }
168             """
169                     .trimIndent()
170             )
171 
172         val ScatterSetClass =
173             kotlin(
174                 """
175             package androidx.collection
176             sealed class ScatterSet<E> {
177                 fun asSet(): Set<E> = setOf()
178             }
179 
180             class MutableScatterSet<E> : ScatterSet<E>() {
181                 fun asMutableSet(): MutableSet<E> = mutableSetOf()
182             }
183             """
184                     .trimIndent()
185             )
186 
187         val ObjectListClass =
188             kotlin(
189                 """
190             package androidx.collection
191             sealed class ObjectList<E> {
192                 fun asList(): List<E> = listOf()
193             }
194 
195             class MutableObjectList<E> : ObjectList<E>() {
196                 fun asMutableList(): MutableList<E> = mutableListOf()
197             }
198             """
199                     .trimIndent()
200             )
201     }
202 }
203