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  *      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.lifecycle.runtime.testing.lint
18 
19 import androidx.lifecycle.testing.lint.TestLifecycleOwnerInCoroutineDetector
20 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
21 import com.android.tools.lint.checks.infrastructure.TestFile
22 import com.android.tools.lint.detector.api.Detector
23 import com.android.tools.lint.detector.api.Issue
24 import org.junit.Test
25 import org.junit.runner.RunWith
26 import org.junit.runners.JUnit4
27 
28 @RunWith(JUnit4::class)
29 class TestLifecycleOwnerInCoroutineDetectorTest : LintDetectorTest() {
30 
getDetectornull31     override fun getDetector(): Detector = TestLifecycleOwnerInCoroutineDetector()
32 
33     override fun getIssues(): MutableList<Issue> =
34         mutableListOf(TestLifecycleOwnerInCoroutineDetector.ISSUE)
35 
36     private val lifecycleStub: TestFile =
37         bytecode(
38             "libs/lifecycle.jar",
39             kotlin(
40                     """
41         package androidx.lifecycle
42 
43         abstract class Lifecycle {
44             enum class State { CREATED, STARTED, RESUMED }
45         }
46         """
47                 )
48                 .indented(),
49             0xf3659215,
50             """
51         META-INF/main.kotlin_module:
52         H4sIAAAAAAAA/2NgYGBmYGBgBGJWKM3AJcrFnVqRmFuQk6qXlp8vxBaSWlzi
53         XcKlySWenV+Sk5lXoZecX5RfWpKZl1qsVwKUFOIDKXEqzcxJSS0q9i5RYtBi
54         AAAIhggPWgAAAA==
55         """,
56             """
57         androidx/lifecycle/Lifecycle$State.class:
58         H4sIAAAAAAAA/41T30/TUBT+btetXa0yJiIg/sJNuk0ZoII6RCYMnQxJKC4x
59         eypbxULXJmtH9I0n/xD/AsFEjUaz+OgfZTz3DgkEozz03Hu++51zvnvu6c9f
60         X74DuI0lhhHLa7R8p/E67zov7fqbumvnK392KTO0QlsBY5iqbFrbVt61vI18
61         yWs3Zyr/DyzMFhjOHI1TIDPEZhzPCWcZ+o1Dac2w5XgbhXKmyhAxMlUdMSga
62         oogzyOErJ2BInaQqg5ayqVTKs5o2Q/J4CYbTXYbfajie5TKwMqnatty2TVVG
63         jUztZIWUVLVYeV4yGdInitBxFn1xSDjHcLZ2SNjK+qZdDwsKzjNE667vkfA+
64         I3OcoWMQQxoGcEFBHwkQmldeMtz9SyszJ7vFncOh864VBIW/5Tr6kKTkEq7w
65         57lKkoUM3o8/PVTmV0vFtdKCjjR0fuPrhJlrxVWBGV0sQ9hqyXy+zLFcF7vB
66         oM7UXTEgKtIaQXEVhoqcjhSS3L/F0FvZ8kMi5Zft0GpYoUXXkJrbERprxk2U
67         nnSLoNcO98Zp15hgmO7s6Jo0IGlSIqFJqqz+eMsGOjuTalJOSuOdnXH2SFGl
68         H+9iUkLalNQIfTJ9UR4+ySg1hv/VUAVz1ArRVQb9AB7bCmmA5/0GoT0Vx7Of
69         tZvrdmvNWnfFePp1y61aLYf7+2DcdDY8K2y3eKKy59kt8Sy8sZrpt1t1e9Hh
70         vMHVthc6TbvqBA4FFj3Pp+qO7wVzE9SoKN1dnhvkTwDQauyvOb4mhvgw8l4R
71         k/40sgvklRAB75mSzQ1/grZLW4lAEIUJ8iLZK10KTvFEYncaZ8S5gh4kKOKx
72         iFMRRy+SdMJT9wsGoO2h/yOGO7j4XvhxmqTL+xyDojhLS0rZzxjpQOIcJqrq
73         3RNco1pcfuogc5r08LPB2DdIS9nIHkbNrLyHrJmN7uGmme0WUjFGXzdkEbLA
74         xr5CepHMRz5h/ANGhTMhCycrnMmocG5+xu0P6N8VYg63Q8YTIYpmBEU8ou52
75         rx5BWazzeErrAzq/Q+ypGiJlTJdxlyzucXO/jAJmamABsWZriAXQAzwMoARI
76         BzAC5H4DVwG07rEFAAA=
77         """,
78             """
79         androidx/lifecycle/Lifecycle.class:
80         H4sIAAAAAAAA/31QTU8UQRB91TM7uw6jLCiwq8hXiAEPDhATEiVEJTHZZNRE
81         yF721DvTYLOzPcl0L8Hb/hb+AScTD2bD0R9lqFnUI314Ve/1q+rq+v3n5y8A
82         r7FGWJYmKwudXca5PlXp9zRXcfIvq4MIzXN5IeNcmrP4S/9cpa4OjxAcaKPd
83         IcHb2u5GqCEI4aNO8N03bQkryX2N3xLmkkHhcm3iT8rJTDrJmhheeDwYVVAj
84         0IClS12xHc6yXcLmZByFoiVC0ZyMQ9EQjRetyXhP7NAb8j7Ubq4C0RSVd4+4
85         D2rHTjpF2LhvmM2pqY4lQvRffDVw/JejIuPq2UQb9Xk07KvyRPZzVuaTIpV5
86         V5a64n/FqGOMKo9yaa3iDYTHxahM1Udd3bW/jozTQ9XVVrP5vTEFP6oLY/11
87         CN5cdaiaGAHjU2bxlPMiXv5A45oTgWeMwVT0scwY3RnwACFHD88ZQ9Za7F18
88         18bKtKqNVY77rM+wN+rB6+BhB48YMVtBs4M5zPdAFo/xpAffIrRYsAgsFm8B
89         Whw3Ci0CAAA=
90         """
91         )
92 
93     private val testLifecycleOwnerStub: TestFile =
94         bytecode(
95             "libs/testlifecycleowner.jar",
96             kotlin(
97                     """
98         package androidx.lifecycle.testing
99 
100         import androidx.lifecycle.Lifecycle
101 
102         class TestLifecycleOwner {
103             var currentState: Lifecycle.State = Lifecycle.State.STARTED
104         }
105         """
106                 )
107                 .indented(),
108             0x5140a562,
109             """
110         META-INF/main.kotlin_module:
111         H4sIAAAAAAAA/2NgYGBmYGBgBGJWKM3AJcrFnVqRmFuQk6qXlp8vxBaSWlzi
112         XcKlySWenV+Sk5lXoZecX5RfWpKZl1qsVwKUFOIDKXEqzcxJSS0q9i5RYtBi
113         AAAIhggPWgAAAA==
114         """,
115             """
116         androidx/lifecycle/testing/TestLifecycleOwner.class:
117         H4sIAAAAAAAA/41TXU8TQRQ9s213S62l5ZviJyDQImxB3kpQREmaVDC0aUx4
118         WrYjTNnuJrvTim88+UP8BWo0Gh9Mw6M/yninrFg+jLzcO/fec8+ZuTPz89f3
119         HwBWsMKwYLl13xP1I9MRr7n91na4KXkghbtvVsmX/2S337jcN8AY0g2rbZmO
120         RYjtvQa3pYEIg74qXCHXGCJzuVoSMegJRGEwTF6hcMY6XZGW5Ab6GIxKdX2n
121         +vwZw3T5/y3FJG4g2YcEbjIk7Zbvc1d2K0n0q4KGNENUHoiAwbyK8N+nLDL0
122         73O50UPKMDOXu862GKbKnr9vNrjc8y3hBqbluh6VhEfrLU9utRxHCQQXBWbn
123         rsOfq9GkVql54fFaHOMMdw496QjXbLSbpnAl913LMUuu9Elb2IGBCYZh+4Db
124         h6H4S8u3mpyAXcmLd1nsyVQUyX5RXedt3EngFu4yZMqh4AsurbolLTqM1mxH
125         6EExZWIM7JBSR0JFBVrVlxg2O8dDCW1MS2jpznFCi2unQTw61jle1grsaezk
126         va6ltZ1sOpLVCtFXJ+9SlEklOsfZaDyW1iejcSMdV2zLjJQwfPnaFg8lw8RO
127         y5WiyUtuWwRiz+Hrf8dP72HDq9Oo+8vC5Vut5h73qxZhGAbKnm05NcsXKg6T
128         0xe5zkZ3jjRR8Vq+zTeF6hkPe2qX1LFEbzKqxkSenij5BYp08iPkI+Rj3WiR
129         ojXC0DmRzH9FPP8JqS/IfKRYg0k2hUi3k/4X/QIdBYU8xWMAg+QzGMIwoRWX
130         SRVVi+U/I/PhjETvJo2e5ljY3LutDEYxFhI9Ia8p9PxA9hvu5efPbUp16CHf
131         yCku5FOr8a7GklJkIbWG5a59iEfkNyl7n7YwuYtICVMlTJPFA2VmSpjF3C5Y
132         gBzyu+gLMBhgPoAe0D9CMsBQgOEAowHGfgPeFoO+3AQAAA==
133         """
134         )
135 
136     private val coroutineStub: TestFile =
137         bytecode(
138             "libs/coroutinecontext.jar",
139             kotlin(
140                     """
141         package kotlinx.coroutines
142 
143         object EmptyCoroutineContext : CoroutineContext
144 
145         interface CoroutineContext
146         """
147                 )
148                 .indented(),
149             0x37041da2,
150             """
151         META-INF/main.kotlin_module:
152         H4sIAAAAAAAA/2NgYGBmYGBgBGJWKM3AJcrFnVqRmFuQk6qXlp8vxBaSWlzi
153         XcKlySWenV+Sk5lXoZecX5RfWpKZl1qsVwKUFOIDKXEqzcxJSS0q9i5RYtBi
154         AAAIhggPWgAAAA==
155         """,
156             """
157         kotlinx/coroutines/CoroutineContext.class:
158         H4sIAAAAAAAA/41OPUsDQRB9s2c+PL8uRiHWYusmwc5KAsJBRFCwuWpzWWVz
159         l13I7oUr87ssJLU/Spwz2Fg5A2/evGHezOfX+weAG5wRLgsXSmNrmbuVq4Kx
160         2svJL504G3QdOiBCslBrJUtl3+TjbKFzViNCb7rblw86qLkK6pYgluuI7amB
161         FoEKlmrTdENm8xHhfLvpxmIgYpEwex1sN2MxpGY4JlxN//ESnwGh/1e+LgIh
162         fnbVKtf3ptSEi6fKBrPUL8abWanvrHVBBeOsb/M97GEXAqc/2EOf64i9W5zt
163         DFGKToouI/YbiFMc4DADeRzhOIPwOPFIvgHKkw26UwEAAA==
164         """,
165             """
166         kotlinx/coroutines/EmptyCoroutineContext.class:
167         H4sIAAAAAAAA/41STW/TQBB9uwmN6xqalo+mlO9SqfSA24obFVKJimQpGIlW
168         kaqeNs6qbOLsInsdhVtO/BD+QcWhEkgoghs/CjFrAkg0B2ztzL63M88zs/7+
169         49MXAE+wwbDZNzZVehQmJjOFVVrm4cHgrX3X/I2bRls5sjUwhnpPDEWYCn0a
170         vur0ZEJshWF9hsbF9EsMc3tKK/uMobL5qB2gBs9HFfMMVftG5Qxbrf+t5imD
171         t5ekpZwP7jS8KD482o+bBwEWEcwTWafSWiY7DXvSdjKhdB4KrY0VVhnax8bG
172         RZqS1NL0u+FLaUVXWEEcHwwrNCTmDJXO+kSNlEPbtOvukPZk7Pu8wcs1GXvf
173         3vPGZLzLt9nzmse/fpjjde5Cdxk2ZjU2o6er/3KP+5Zh7XWhrRrISA9Vrjqp
174         3P/bBc2uabqSYbFFWXEx6MjsSFAMw3LLJCJti0w5PCX9Q1NkiXyhHFidCrcv
175         yGKH5lcFXRmtVTdQ8nepeYeXyXN66f4I3SMUkmduUFvn8M/K4/vTYJDMA7LB
176         rwAs0A6UePlP8gpFu2fhM/jxOa58xNJZSXCsl/YOHpa/K82HBK6doBLheoQb
177         ZLHiTCOiGm+egOVYwy06zxHkuJ3D+wnnrXWf6wIAAA==
178         """
179         )
180 
181     private val runTestCoroutineStub: TestFile =
182         bytecode(
183             "libs/testbuilders.jar",
184             kotlin(
185                     """
186         package kotlinx.coroutines.test
187 
188         import kotlinx.coroutines.*
189 
190         public fun runTest(
191             context: CoroutineContext = EmptyCoroutineContext,
192             timeout: Int = 0,
193             testBody: () -> Unit
194         ) { }
195         """
196                 )
197                 .indented(),
198             0xd166e09a,
199             """
200         META-INF/main.kotlin_module:
201         H4sIAAAAAAAA/2NgYGBmYGBgBGJWKM3AJcrFnVqRmFuQk6qXlp8vxBaSWlzi
202         XcKlySWenV+Sk5lXoZecX5RfWpKZl1qsVwKUFOIDKXEqzcxJSS0q9i5RYtBi
203         AAAIhggPWgAAAA==
204         """,
205             """
206         kotlinx/coroutines/test/TestBuildersKt.class:
207         H4sIAAAAAAAA/61TW08TURD+Tm+7ra2UcpG7KLeCyBbEW0pMoIFkBaqxSEzw
208         5bBd6rbbXbJ7lsCL4W/4M/SJ+GB49kcZ52xbRW0iD7TJzHfm8s2cmT3ff3z9
209         BmAVzxlmG66wLedUM1zPDYTlmL4mTF9oeyQ2Asuump6/LRQwhmydn3DN5k5N
210         e3VYNw2yRhkUL3BkMIOe3+nCVurAkusI81QU9XaUVj9pakeBYwjLdXxtq40K
211         xfl9hnc3w7XW8b91LFF8EVJP7bheTaub4tDjFgVzx3EFbyWWXVEObLtI1zJa
212         JVQkGSauVLHI7Dnc1nRHeJRvGb6CWwwDxgfTaLQJXnOPN00KZJjL7/w9t+IV
213         S0WS1KixNDK4nUIaPQyqXMGGWz1T0cswc61RUMvCappkZGA6w+T/xszQ017d
214         dNU84oFNie9vaIX6v3eWo893Id9sHouzvysoGKYp6OXK3nq5tMmw0K2trpnF
215         NEYxlsQIxmnV17iMgrtpxJFIIYJ7DL2dq+2agle54DSnSPMkSg+GSRGn6TYk
216         iJD91JKoQKi6zPDy8nwodXmeimQjoRpqqazaPo3ks5fnI5ECW1FViiEUDVGM
217         UHwlnU2MqLlYjvwFRTKuMCqJXKefPxZ39XUuNWhxsZJbNcmxQxcrB81D09vj
218         h7Yp812D2/vcs+S5bUxWrJrDReARnn4TOPLD0Z0Ty7fI/evbXf/9MBgyFcGN
219         xi4/blOkKm7gGeaWJQ/DbY79FsOVRCzTWGOQvwiG5ZwRJRuwTWc5vMxCLnWB
220         7GIuR/JzGLZCMkFXTyOFR4QnW4FIoi8kyqAfA+SXqBeDlLEa5il4TLonSi41
221         DAx1Nok7GCIsq5aJKk56bDz28RNSXzBxicnt8Tgd4tG1hdHFC9xvNfGEZAyR
222         jBK2MxhmJeivUNEENaJSCwmKexpGF/CMdImqTFHk9AGiOmZ0zJLEnI485nUs
223         4MEBmI9FPDyA6iPuY8lH0kefj/4Q9/rQfgIMLZFBnQUAAA==
224         """
225         )
226 
227     @Test
228     fun errors() {
229         lint()
230             .files(
231                 kotlin(
232                     """
233                 package example.foo
234 
235                 import androidx.lifecycle.Lifecycle
236                 import androidx.lifecycle.testing.TestLifecycleOwner
237                 import kotlinx.coroutines.test.runTest
238 
239                 fun testSetCurrentStateInRunTest() = runTest {
240                     val owner = TestLifecycleOwner()
241                     owner.currentState = Lifecycle.State.RESUMED
242                 }
243 
244                 fun testSetCurrentStateInRunTestWithTimeOut() = runTest(timeout = 5000) {
245                     val owner = TestLifecycleOwner()
246                     owner.currentState = Lifecycle.State.RESUMED
247                 }
248             """
249                 ),
250                 coroutineStub,
251                 lifecycleStub,
252                 runTestCoroutineStub,
253                 testLifecycleOwnerStub
254             )
255             .run()
256             .expect(
257                 """
258 src/example/foo/test.kt:8: Error: Incorrect use of currentState property inside of Coroutine, please use the suspending setCurrentState() function. [TestLifecycleOwnerInCoroutine]
259                 fun testSetCurrentStateInRunTest() = runTest {
260                                                      ~~~~~~~
261 src/example/foo/test.kt:13: Error: Incorrect use of currentState property inside of Coroutine, please use the suspending setCurrentState() function. [TestLifecycleOwnerInCoroutine]
262                 fun testSetCurrentStateInRunTestWithTimeOut() = runTest(timeout = 5000) {
263                                                                 ~~~~~~~
264 2 errors, 0 warnings
265             """
266                     .trimIndent()
267             )
268     }
269 
270     @Test
noErrorsnull271     fun noErrors() {
272         lint()
273             .files(
274                 kotlin(
275                     """
276                 package example.foo
277 
278                 import androidx.lifecycle.Lifecycle
279                 import androidx.lifecycle.testing.TestLifecycleOwner
280                 import kotlinx.coroutines.test.runTest
281 
282                 fun testSetCurrentStateInRunTest() = runTest {
283                     val owner = TestLifecycleOwner()
284                     owner.setCurrentState(Lifecycle.State.RESUMED)
285                 }
286             """
287                 ),
288                 coroutineStub,
289                 lifecycleStub,
290                 runTestCoroutineStub,
291                 testLifecycleOwnerStub
292             )
293             .run()
294             .expectClean()
295     }
296 }
297