1 /*
2  * Copyright 2021 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  *      https://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 @file:Suppress("UnstableApiUsage")
18 
19 package com.google.accompanist.permissions.lint
20 
21 import com.android.tools.lint.checks.infrastructure.TestFiles
22 import com.android.tools.lint.checks.infrastructure.TestLintResult
23 import com.android.tools.lint.checks.infrastructure.TestLintTask
24 import org.junit.Test
25 import org.junit.runner.RunWith
26 import org.junit.runners.JUnit4
27 
28 /* ktlint-disable max-line-length */
29 
30 /**
31  * Test for [PermissionsLaunchDetector].
32  */
33 @RunWith(JUnit4::class)
34 internal class PermissionsLaunchDetectorTest {
35 
checknull36     private fun check(fileToAdd: String): TestLintResult {
37         return TestLintTask.lint()
38             .files(
39                 LaunchPermissionsStub,
40                 ComposableStub,
41                 TestFiles.kt(fileToAdd)
42             )
43             .allowMissingSdk()
44             .issues(PermissionsLaunchDetector.PermissionLaunchedDuringComposition)
45             .run()
46     }
47 
48     @Test
errorsnull49     fun errors() {
50         check(
51             """
52             import androidx.compose.runtime.Composable
53             import com.google.accompanist.permissions.*
54 
55             @Composable
56             fun Test() {
57                 PermissionState().launchPermissionRequest()
58                 MultiplePermissionsState().launchMultiplePermissionRequest()
59             }
60 
61             val lambda = @Composable {
62                 PermissionState().launchPermissionRequest()
63                 MultiplePermissionsState().launchMultiplePermissionRequest()
64             }
65 
66             val lambda2: @Composable () -> Unit = {
67                 PermissionState().launchPermissionRequest()
68                 MultiplePermissionsState().launchMultiplePermissionRequest()
69             }
70 
71             @Composable
72             fun LambdaParameter(content: @Composable () -> Unit) {}
73 
74             @Composable
75             fun Test2() {
76                 LambdaParameter(content = {
77                     PermissionState().launchPermissionRequest()
78                     MultiplePermissionsState().launchMultiplePermissionRequest()
79                 })
80                 LambdaParameter {
81                     PermissionState().launchPermissionRequest()
82                     MultiplePermissionsState().launchMultiplePermissionRequest()
83                 }
84             }
85 
86             fun test3() {
87                 val localLambda1 = @Composable {
88                     PermissionState().launchPermissionRequest()
89                     MultiplePermissionsState().launchMultiplePermissionRequest()
90                 }
91 
92                 val localLambda2: @Composable () -> Unit = {
93                     PermissionState().launchPermissionRequest()
94                     MultiplePermissionsState().launchMultiplePermissionRequest()
95                 }
96             }
97         """
98         )
99             .expect(
100                 """
101                     src/test.kt:7: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
102                                     PermissionState().launchPermissionRequest()
103                                                       ~~~~~~~~~~~~~~~~~~~~~~~
104                     src/test.kt:8: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
105                                     MultiplePermissionsState().launchMultiplePermissionRequest()
106                                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107                     src/test.kt:12: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
108                                     PermissionState().launchPermissionRequest()
109                                                       ~~~~~~~~~~~~~~~~~~~~~~~
110                     src/test.kt:13: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
111                                     MultiplePermissionsState().launchMultiplePermissionRequest()
112                                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
113                     src/test.kt:17: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
114                                     PermissionState().launchPermissionRequest()
115                                                       ~~~~~~~~~~~~~~~~~~~~~~~
116                     src/test.kt:18: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
117                                     MultiplePermissionsState().launchMultiplePermissionRequest()
118                                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119                     src/test.kt:27: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
120                                         PermissionState().launchPermissionRequest()
121                                                           ~~~~~~~~~~~~~~~~~~~~~~~
122                     src/test.kt:28: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
123                                         MultiplePermissionsState().launchMultiplePermissionRequest()
124                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125                     src/test.kt:31: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
126                                         PermissionState().launchPermissionRequest()
127                                                           ~~~~~~~~~~~~~~~~~~~~~~~
128                     src/test.kt:32: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
129                                         MultiplePermissionsState().launchMultiplePermissionRequest()
130                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
131                     src/test.kt:38: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
132                                         PermissionState().launchPermissionRequest()
133                                                           ~~~~~~~~~~~~~~~~~~~~~~~
134                     src/test.kt:39: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
135                                         MultiplePermissionsState().launchMultiplePermissionRequest()
136                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
137                     src/test.kt:43: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
138                                         PermissionState().launchPermissionRequest()
139                                                           ~~~~~~~~~~~~~~~~~~~~~~~
140                     src/test.kt:44: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]
141                                         MultiplePermissionsState().launchMultiplePermissionRequest()
142                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143                     14 errors, 0 warnings
144                 """.trimIndent()
145             )
146     }
147 
148     @Test
noErrorsnull149     fun noErrors() {
150         check(
151             """
152             import com.google.accompanist.permissions.*
153 
154             fun test() {
155                 PermissionState().launchPermissionRequest()
156                 MultiplePermissionsState().launchMultiplePermissionRequest()
157             }
158 
159             val lambda = {
160                 PermissionState().launchPermissionRequest()
161                 MultiplePermissionsState().launchMultiplePermissionRequest()
162             }
163 
164             val lambda2: () -> Unit = {
165                 PermissionState().launchPermissionRequest()
166                 MultiplePermissionsState().launchMultiplePermissionRequest()
167             }
168 
169             fun lambdaParameter(action: () -> Unit) {}
170 
171             fun test2() {
172                 lambdaParameter(action = {
173                     PermissionState().launchPermissionRequest()
174                     MultiplePermissionsState().launchMultiplePermissionRequest()
175                 })
176                 lambdaParameter {
177                     PermissionState().launchPermissionRequest()
178                     MultiplePermissionsState().launchMultiplePermissionRequest()
179                 }
180             }
181 
182             fun test3() {
183                 val localLambda1 = {
184                     PermissionState().launchPermissionRequest()
185                     MultiplePermissionsState().launchMultiplePermissionRequest()
186                 }
187 
188                 val localLambda2: () -> Unit = {
189                     PermissionState().launchPermissionRequest()
190                     MultiplePermissionsState().launchMultiplePermissionRequest()
191                 }
192             }
193         """
194         )
195             .expectClean()
196     }
197 }
198 
199 private val LaunchPermissionsStub = TestFiles.kt(
200     "com/google/accompanist/permissions/LaunchPermissions.kt",
201     """
202         package com.google.accompanist.permissions
203 
204         class PermissionState {
launchPermissionRequestnull205             fun launchPermissionRequest()
206         }
207 
208         class MultiplePermissionsState {
209             fun launchMultiplePermissionRequest()
210         }
211     """
212 ).indented().within("src")
213 
214 private val ComposableStub = TestFiles.kt(
215     "androidx/compose/runtime/Composable.kt",
216     """
217         package androidx.compose.runtime
218 
219         annotation class Composable
220     """
221 ).indented().within("src")
222 /* ktlint-enable max-line-length */
223