[go: nahoru, domu]

blob: faac842fd9fcef71b763786950f5e829af280ef6 [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.lint
import androidx.compose.lint.test.Stubs
import androidx.compose.lint.test.bytecodeStub
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class ReturnFromAwaitPointerEventScopeDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = ReturnFromAwaitPointerEventScopeDetector()
override fun getIssues(): MutableList<Issue> =
mutableListOf(ReturnFromAwaitPointerEventScopeDetector.ExitAwaitPointerEventScope)
private val ForEachGestureStub: TestFile = bytecodeStub(
filename = "ForEachGesture.kt",
filepath = "androidx/compose/foundation/gestures",
checksum = 0xf41a4b04,
"""
package androidx.compose.foundation.gestures
import androidx.compose.ui.input.pointer.PointerInputScope
suspend fun PointerInputScope.forEachGesture(block: suspend PointerInputScope.() -> Unit) {
block()
}
""",
"""
META-INF/main.kotlin_module:
H4sIAAAAAAAAAH2NSwrCMBCGR0SErCToVrC4UsghtFakG0EvEJqxBmomJBOo
tzfS7gQXw//6YABgCgCTfPNRQaDYamcCWdOrhl6eIqoHJWc0W3KqxcgpYJSr
Muj2PKQSGRumULNcVBROunmOS26Wd+1/OLEXxb83nX5TYjk7UJ/ho9j8wMkq
63xi5ck6xiDXtxQ9OmNdex2qy3evbJdtzQXs4AM20YY08QAAAA==
""",
"""
androidx/compose/foundation/gestures/ForEachGestureKt.class:
H4sIAAAAAAAAALVVz08bRxT+Zm1ss0BiNpACaRwS3IYfIWtoadM6Qo2ISVZ1
DIohVcWhGq8XZ7C9Y+3OWuSGcqjUf6OHnqOe0h4qRG/9o6q+sQ0BTIIUqZY8
870337z55s2b2X/+/fMvAF/iW4YV7lcDKar7tiubLRl69q6M/CpXQvp2zQtV
FHihvS6DAndfPuna36skGEN6j7e53eB+zd6o7HkueWMMV3bPcBl+mS32rREJ
W/itSNktKXzlBfZmt3e0s+zKlpcv1qVqCN/eazft3ch3tSAS0kPLJ+OuDGSk
hE8q16RPIOpoz88Vz8vLMzb+v2h5uPhRURcv2cLDE8K2L1R+Nb/Qv6XVy/LQ
F+SivCBblEHN3vNUJeCCtsZ9Xyre3WYpajR4peERbeZDNKk0k1iZD2crCZMh
Ify2rFN1PJjt19PvuUD0MIYxMoQhXGGY688BJTsgicINbecE6sJNM1yveWpt
4/nG9pZTKvxU3i5vFkqPC48ZxmYvXMjCNROjGGMYOpXLJK4zpJxSeetRaa3A
MHIm0cOYwOQgPsEUhc2qlyLMnr8YKx9VNQwDlYZ06wzTl10R0pvVkRuetk9N
eN+dYRg9pjzzFKdXgJPPaLZj9Fww3SQZWF0Dg/z7QqMcoeoSQ3h4kDEPD0wj
bZjGhNGBE11opHvG4cHUKuEpI8fmjZyxfDcdm5pJMStukWWZVqqDWG7ASljx
CZZL5OJHvyaMVPLp0c/f/f2WHR5oM506em3ETSM1qZdeZqQN1rHw09vPHDsL
+8qjCpD+8ejWq04qR88+bPfriiG+Jqt0OiNlxd36M97a0sXPcLVI+SpFzYoX
9DxWUbq88YIHQts952BZ1HzePeAbzyNKbtNz/LYIBQ0/endf6M6dH93kAW96
dNpnaGZZRoHrrQsdfbI350VfPCzBQBz6RzQMIEHWElkuYtDHNL4wf+8PXI3h
x7cY/w3xNz/8jhtvaCCGZWoTYJtJmvIFYZNCac8YkvSRAKa7AfApbnYWGEcG
t2gZjaZxm9gruiKI/VU3ElLUf03/azEyBjua3rUGHnTaHL6hfp28d0jvzA5i
DrIOPqMWnzu4i1kHc5jfAQuxgHs7GAwxEGIxxM0QmRD3Q9zumHaIxH8TzBqW
1AYAAA==
"""
)
private val stubs = arrayOf(
Stubs.Composable,
Stubs.Modifier,
UiStubs.Density,
UiStubs.PointerInputScope,
UiStubs.PointerEvent,
ForEachGestureStub,
UiStubs.Alignment,
)
@Test
fun awaitPointerEventScope_standalone_shouldNotWarn() {
expectClean(
"""
package test
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
@Composable
fun TestComposable() {
Modifier.pointerInput(Unit) {
awaitPointerEventScope {
}
}
}
"""
)
}
@Test
fun awaitPointerEventScope_insideForEach_shouldNotWarn() {
expectClean(
"""
package test
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
@Composable
fun TestComposable() {
Modifier.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
}
}
}
}
"""
)
}
@Test
fun awaitPointerEventScope_otherMethodName_shouldNotWarn() {
expectClean(
"""
package test
fun <T> awaitPointerEventScope(block: () -> T): T {
return block()
}
fun TestComposable() {
val result = awaitPointerEventScope {
"Result"
}
println(result)
}
"""
)
}
@Test
fun awaitPointerEventScope_assignedToVariable_shouldWarn() {
lint().files(
kotlin(
"""
package test
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
@Composable
fun TestComposable() {
Modifier.pointerInput(Unit) {
while (true) {
val assigned = awaitPointerEventScope {
}
}
}
}
"""
),
*stubs,
)
.run()
.expect(
"""
src/test/test.kt:12: $WarningMessage
val assigned = awaitPointerEventScope {
~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
.trimIndent()
)
}
@Test
fun awaitPointerEventScope_returnedFromMethod_shouldWarn() {
lint().files(
kotlin(
"""
package test
import androidx.compose.ui.input.pointer.PointerInputScope
private suspend fun PointerInputScope.doSomethingInInputScope(): Any {
return awaitPointerEventScope {
}
}
"""
),
*stubs,
)
.run()
.expect(
"""
src/test/test.kt:7: $WarningMessage
return awaitPointerEventScope {
~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
.trimIndent()
)
}
@Test
fun awaitPointerEventScope_assignedFromLambdaMethod_shouldWarn() {
lint().files(
kotlin(
"""
package test
import androidx.compose.ui.input.pointer.PointerInputScope
private suspend fun PointerInputScope.doSomethingInInputScope(): Boolean {
val result = run {
awaitPointerEventScope {
true
}
}
return result
}
private suspend fun PointerInputScope.doSomethingInInputScope(nullable: String?): Boolean {
val result = nullable?.let {
awaitPointerEventScope {
true
}
} ?: false
return result
}
"""
),
*stubs,
)
.run()
.expect(
"""
src/test/test.kt:8: $WarningMessage
awaitPointerEventScope {
~~~~~~~~~~~~~~~~~~~~~~
src/test/test.kt:18: $WarningMessage
awaitPointerEventScope {
~~~~~~~~~~~~~~~~~~~~~~
0 errors, 2 warnings
"""
.trimIndent()
)
}
@Test
fun awaitPointerEventScope_returnedFromLambdaMethod_shouldWarn() {
lint().files(
kotlin(
"""
package test
import androidx.compose.ui.input.pointer.PointerInputScope
private suspend fun PointerInputScope.doSomethingInInputScope(): Boolean {
return run {
awaitPointerEventScope {
true
}
}
}
private suspend fun PointerInputScope.doSomethingInInputScope(nullable: String?): Boolean {
return nullable?.let {
awaitPointerEventScope {
true
}
} ?: false
}
"""
),
*stubs,
)
.run()
.expect(
"""
src/test/test.kt:8: $WarningMessage
awaitPointerEventScope {
~~~~~~~~~~~~~~~~~~~~~~
src/test/test.kt:16: $WarningMessage
awaitPointerEventScope {
~~~~~~~~~~~~~~~~~~~~~~
0 errors, 2 warnings
"""
.trimIndent()
)
}
@Test
fun awaitPointerEventScope_notAssignedToVariable_shouldNotWarn() {
expectClean(
"""
package test
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
@Composable
fun TestComposable() {
Modifier.pointerInput(Unit) {
while (true) {
awaitPointerEventScope {
}
}
}
}
"""
)
}
private fun expectClean(source: String) {
lint()
.files(kotlin(source), *stubs)
.run()
.expectClean()
}
private val WarningMessage: String =
"Warning: ${ReturnFromAwaitPointerEventScopeDetector.ErrorMessage} " +
"[${ReturnFromAwaitPointerEventScopeDetector.IssueId}]"
}