Skip to content

Commit

Permalink
Merge pull request #460 from permissions-dispatcher/kotlin-lint
Browse files Browse the repository at this point in the history
Add kotlin test cases for lint module
  • Loading branch information
hotchemi committed Apr 16, 2018
2 parents 749f3ff + 2777c03 commit c6219d6
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 63 deletions.
Expand Up @@ -8,6 +8,7 @@
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.psi.PsiElement;

import org.jetbrains.uast.UAnnotation;
import org.jetbrains.uast.UBlockExpression;
Expand All @@ -16,6 +17,7 @@
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UMethod;
import org.jetbrains.uast.UQualifiedReferenceExpression;
import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression;
import org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression;
import org.jetbrains.uast.visitor.AbstractUastVisitor;

Expand Down Expand Up @@ -55,11 +57,15 @@ private static class OnRequestPermissionsResultChecker extends AbstractUastVisit

private final String className;

private final boolean isKotlin;

private boolean hasRuntimePermissionAnnotation;


private OnRequestPermissionsResultChecker(JavaContext context, UClass klass) {
this.context = context;
this.className = klass.getName();
isKotlin = context.getPsiFile() != null && "kotlin".equals(context.getPsiFile().getLanguage().getID());
}

@Override
Expand All @@ -79,13 +85,13 @@ public boolean visitMethod(UMethod node) {
if (!"onRequestPermissionsResult".equals(node.getName())) {
return true;
}
if (hasRuntimePermissionAnnotation && !isGeneratedMethodCalled(node, className)) {
if (hasRuntimePermissionAnnotation && !isGeneratedMethodCalled(node, className, isKotlin)) {
context.report(ISSUE, context.getLocation(node), "Generated onRequestPermissionsResult method not called");
}
return true;
}

private static boolean isGeneratedMethodCalled(UMethod method, String className) {
private static boolean isGeneratedMethodCalled(UMethod method, String className, boolean isKotlin) {
UExpression methodBody = method.getUastBody();
if (methodBody == null) {
return false;
Expand All @@ -94,25 +100,30 @@ private static boolean isGeneratedMethodCalled(UMethod method, String className)
UBlockExpression methodBodyExpression = (UBlockExpression) methodBody;
List<UExpression> expressions = methodBodyExpression.getExpressions();
for (UExpression expression : expressions) {
if (isKotlin && expression instanceof KotlinUFunctionCallExpression) {
KotlinUFunctionCallExpression functionalExpression = (KotlinUFunctionCallExpression) expression;
if ("onRequestPermissionsResult".equals(functionalExpression.getMethodName())) {
return true;
}
}

if (!(expression instanceof UQualifiedReferenceExpression)) {
continue;
}

UQualifiedReferenceExpression referenceExpression = (UQualifiedReferenceExpression) expression;
String receiverName = referenceExpression.getReceiver().toString();
UExpression receiverExpression = referenceExpression.getReceiver();
PsiElement receiverPsi = receiverExpression.getPsi();
if (receiverPsi == null) {
continue; // can this case be happened?
}
String receiverName = receiverPsi.getText();
if ("super".equals(receiverName)) {
// skip super method call
continue;
}

boolean isKotlin = false;

try {
if (referenceExpression instanceof KotlinUQualifiedReferenceExpression) {
isKotlin = true;
}
} catch (NoClassDefFoundError ignored) {}

if (isKotlin) {
if (isKotlin && referenceExpression instanceof KotlinUQualifiedReferenceExpression) {
if ("onRequestPermissionsResult".equals(referenceExpression.getResolvedName())) {
return true;
}
Expand Down
@@ -0,0 +1,88 @@
package permissions.dispatcher

import org.intellij.lang.annotations.Language
import org.junit.Test

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import permissions.dispatcher.Utils.onNeedsPermission

class CallNeedsPermissionDetectorKtTest {

@Test
@Throws(Exception::class)
fun callNeedsPermissionMethod() {
@Language("kotlin") val foo = """
package com.example
import permissions.dispatcher.NeedsPermission
class Foo {
@NeedsPermission("Test")
fun fooBar() {
}
fun hoge() {
fooBar()
}
}
""".trimMargin()

val expectedText = """
|src/com/example/Foo.kt:11: Error: Trying to access permission-protected method directly [CallNeedsPermission]
| fooBar()
| ~~~~~~~~
|1 errors, 0 warnings
""".trimMargin()

lint()
.files(
java(onNeedsPermission),
kt(foo))
.issues(CallNeedsPermissionDetector.ISSUE)
.run()
.expect(expectedText)
.expectErrorCount(1)
.expectWarningCount(0)
}

@Test
@Throws(Exception::class)
fun callNeedsPermissionMethodNoError() {
@Language("kotlin") val foo = """
package com.example
class Foo {
fun someMethod() {
val baz = Baz()
baz.noFooBar()
}
}
""".trimMargin()

@Language("kotlin") val baz = """
package com.example
import permissions.dispatcher.NeedsPermission
class Baz {
@NeedsPermission("Test")
fun fooBar() {
}
fun noFooBar() {
}
}
""".trimMargin()

lint()
.files(
java(onNeedsPermission),
kt(foo),
kt(baz))
.issues(CallNeedsPermissionDetector.ISSUE)
.run()
.expectClean()
}
}
Expand Up @@ -16,9 +16,10 @@ class CallNeedsPermissionDetectorTest {
package com.example;
import permissions.dispatcher.NeedsPermission;
public class Foo {
@NeedsPermission("Test")
public void fooBar() {
void fooBar() {
}
public void hoge() {
Expand All @@ -28,7 +29,7 @@ class CallNeedsPermissionDetectorTest {
""".trimMargin()

val expectedText = """
|src/com/example/Foo.java:10: Error: Trying to access permission-protected method directly [CallNeedsPermission]
|src/com/example/Foo.java:11: Error: Trying to access permission-protected method directly [CallNeedsPermission]
| fooBar();
| ~~~~~~~~
|1 errors, 0 warnings
Expand All @@ -52,7 +53,7 @@ class CallNeedsPermissionDetectorTest {
package com.example;
public class Foo {
public void someMethod() {
void someMethod() {
Baz baz = new Baz();
baz.noFooBar();
}
Expand All @@ -66,10 +67,10 @@ class CallNeedsPermissionDetectorTest {
public class Baz {
@NeedsPermission("Test")
public void fooBar() {
void fooBar() {
}
public void noFooBar() {
void noFooBar() {
}
}
""".trimMargin()
Expand Down
@@ -0,0 +1,98 @@
package permissions.dispatcher

import org.intellij.lang.annotations.Language
import org.junit.Test

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.onRationaleAnnotation
import permissions.dispatcher.Utils.runtimePermission

class CallOnRequestPermissionsResultDetectorKtTest {

@Test
@Throws(Exception::class)
fun callOnRequestPermissionsResultDetectorNoErrorForKotlin() {
@Language("kotlin") val foo = """
package permissions.dispatcher
@RuntimePermissions
class Foo : android.app.Activity {
fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
onRequestPermissionsResult(requestCode, grantResults)
}
@NeedsPermission("Camera")
fun showCamera() {
}
@OnShowRationale("Camera")
fun someMethod() {
}
}
""".trimMargin()

@Language("kotlin") val generatedClass = """
package permissions.dispatcher
fun Foo.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
}
""".trimMargin()

lint()
.files(
java(runtimePermission),
java(onNeedsPermission),
java(onRationaleAnnotation),
kt(generatedClass),
kt(foo))
.issues(CallOnRequestPermissionsResultDetector.ISSUE)
.run()
.expectClean()
}

@Test
@Throws(Exception::class)
fun callOnRequestPermissionsResultDetector() {
@Language("kotlin") val foo = """
package permissions.dispatcher
@RuntimePermissions
class Foo: android.app.Activity {
fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
@NeedsPermission("Camera")
fun showCamera() {
}
@OnShowRationale("Camera")
fun someMethod() {
}
}
""".trimMargin()

val expectedText = """
|src/permissions/dispatcher/Foo.kt:5: Error: Generated onRequestPermissionsResult method not called [NeedOnRequestPermissionsResult]
| fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
| ^
|1 errors, 0 warnings
""".trimMargin()

lint()
.files(
java(runtimePermission),
java(onNeedsPermission),
java(onRationaleAnnotation),
kt(foo))
.issues(CallOnRequestPermissionsResultDetector.ISSUE)
.run()
.expect(expectedText)
.expectErrorCount(1)
.expectWarningCount(0)
}
}
Expand Up @@ -4,7 +4,6 @@ import org.intellij.lang.annotations.Language
import org.junit.Test

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.onRationaleAnnotation
Expand Down Expand Up @@ -97,46 +96,4 @@ class CallOnRequestPermissionsResultDetectorTest {
.expectErrorCount(1)
.expectWarningCount(0)
}

@Test
@Throws(Exception::class)
fun callOnRequestPermissionsResultDetectorNoErrorForKotlin() {
@Language("kotlin") val foo = """
package permissions.dispatcher
@RuntimePermissions
class Foo : android.app.Activity {
fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
onRequestPermissionsResult(requestCode, grantResults)
}
@NeedsPermission("Camera")
fun showCamera() {
}
@OnShowRationale("Camera")
fun someMethod() {
}
}
""".trimMargin()

@Language("kotlin") val generatedClass = """
package permissions.dispatcher
fun Foo.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
}
""".trimMargin()

lint()
.files(
java(runtimePermission),
java(onNeedsPermission),
java(onRationaleAnnotation),
kt(generatedClass),
kt(foo))
.issues(CallOnRequestPermissionsResultDetector.ISSUE)
.run()
.expectClean()
}
}

0 comments on commit c6219d6

Please sign in to comment.