Loading packages/SystemUI/checks/src/com/android/internal/systemui/lint/DoNotDirectlyConstructKosmosDetector.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.internal.systemui.lint import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Issue 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.android.tools.lint.detector.api.SourceCodeScanner import com.intellij.psi.PsiMethod import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.getContainingUClass /** * Detects direct construction of `Kosmos()` in subclasses of SysuiTestCase, which can and should * use `testKosmos`. See go/thetiger */ class DoNotDirectlyConstructKosmosDetector : Detector(), SourceCodeScanner { override fun getApplicableConstructorTypes() = listOf("com.android.systemui.kosmos.Kosmos") override fun visitConstructor( context: JavaContext, node: UCallExpression, constructor: PsiMethod, ) { val superClassNames = node.getContainingUClass()?.superTypes.orEmpty().map { it.resolve()?.qualifiedName } if (superClassNames.contains("com.android.systemui.SysuiTestCase")) { context.report( issue = ISSUE, scope = node, location = context.getLocation(node.methodIdentifier), message = "Prefer testKosmos to direct Kosmos() in sysui tests. go/testkosmos", ) } super.visitConstructor(context, node, constructor) } companion object { @JvmStatic val ISSUE = Issue.create( id = "DoNotDirectlyConstructKosmos", briefDescription = "Prefer testKosmos to direct Kosmos() in sysui tests. go/testkosmos", explanation = """ SysuiTestCase.testKosmos allows us to pre-populate a Kosmos instance with team-standard fixture values, and makes it easier to make centralized changes when necessary. See go/testkosmos """, category = Category.TESTING, priority = 8, severity = Severity.WARNING, implementation = Implementation( DoNotDirectlyConstructKosmosDetector::class.java, Scope.JAVA_FILE_SCOPE, ), ) } } packages/SystemUI/checks/src/com/android/internal/systemui/lint/RunTestShouldUseKosmosDetector.kt +1 −5 Original line number Diff line number Diff line Loading @@ -29,16 +29,12 @@ import com.intellij.psi.PsiMethod import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.getContainingUFile /** * Detects test function naming violations regarding use of the backtick-wrapped space-allowed * feature of Kotlin functions. */ /** Detects use of `TestScope.runTest` when we should use `Kosmos.runTest` by go/kosmos-runtest */ class RunTestShouldUseKosmosDetector : Detector(), SourceCodeScanner { override fun getApplicableMethodNames() = listOf("runTest") override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { if (method.getReceiver()?.qualifiedName == "kotlinx.coroutines.test.TestScope") { val imports = node.getContainingUFile()?.imports.orEmpty().mapNotNull { it.importReference?.asSourceString() Loading packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DoNotDirectlyConstructKosmosTest.kt 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.internal.systemui.lint import com.android.tools.lint.checks.infrastructure.TestFile import com.android.tools.lint.checks.infrastructure.TestFiles import com.android.tools.lint.checks.infrastructure.TestLintResult import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Issue import org.junit.Test class DoNotDirectlyConstructKosmosTest : SystemUILintDetectorTest() { override fun getDetector(): Detector = DoNotDirectlyConstructKosmosDetector() override fun getIssues(): List<Issue> = listOf(DoNotDirectlyConstructKosmosDetector.ISSUE) @Test fun wronglyTriesToDirectlyConstructKosmos() { val runOnSource = runOnSource( """ package test.pkg.name import com.android.systemui.kosmos.Kosmos import com.android.systemui.SysuiTestCase class MyTest: SysuiTestCase { val kosmos = Kosmos() } """ ) runOnSource .expectWarningCount(1) .expect( """ src/test/pkg/name/MyTest.kt:7: Warning: Prefer testKosmos to direct Kosmos() in sysui tests. go/testkosmos [DoNotDirectlyConstructKosmos] val kosmos = Kosmos() ~~~~~~ 0 errors, 1 warnings """ ) } @Test fun okToConstructKosmosIfNotInSysuiTestCase() { val runOnSource = runOnSource( """ package test.pkg.name import com.android.systemui.kosmos.Kosmos class MyTest { val kosmos = Kosmos() } """ ) runOnSource.expectWarningCount(0) } private fun runOnSource(source: String): TestLintResult { return lint() .files(TestFiles.kotlin(source).indented(), kosmosStub, sysuiTestCaseStub) .issues(DoNotDirectlyConstructKosmosDetector.ISSUE) .run() } companion object { private val kosmosStub: TestFile = kotlin( """ package com.android.systemui.kosmos class Kosmos """ ) private val sysuiTestCaseStub: TestFile = kotlin( """ package com.android.systemui class SysuiTestCase """ ) } } Loading
packages/SystemUI/checks/src/com/android/internal/systemui/lint/DoNotDirectlyConstructKosmosDetector.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.internal.systemui.lint import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Issue 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.android.tools.lint.detector.api.SourceCodeScanner import com.intellij.psi.PsiMethod import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.getContainingUClass /** * Detects direct construction of `Kosmos()` in subclasses of SysuiTestCase, which can and should * use `testKosmos`. See go/thetiger */ class DoNotDirectlyConstructKosmosDetector : Detector(), SourceCodeScanner { override fun getApplicableConstructorTypes() = listOf("com.android.systemui.kosmos.Kosmos") override fun visitConstructor( context: JavaContext, node: UCallExpression, constructor: PsiMethod, ) { val superClassNames = node.getContainingUClass()?.superTypes.orEmpty().map { it.resolve()?.qualifiedName } if (superClassNames.contains("com.android.systemui.SysuiTestCase")) { context.report( issue = ISSUE, scope = node, location = context.getLocation(node.methodIdentifier), message = "Prefer testKosmos to direct Kosmos() in sysui tests. go/testkosmos", ) } super.visitConstructor(context, node, constructor) } companion object { @JvmStatic val ISSUE = Issue.create( id = "DoNotDirectlyConstructKosmos", briefDescription = "Prefer testKosmos to direct Kosmos() in sysui tests. go/testkosmos", explanation = """ SysuiTestCase.testKosmos allows us to pre-populate a Kosmos instance with team-standard fixture values, and makes it easier to make centralized changes when necessary. See go/testkosmos """, category = Category.TESTING, priority = 8, severity = Severity.WARNING, implementation = Implementation( DoNotDirectlyConstructKosmosDetector::class.java, Scope.JAVA_FILE_SCOPE, ), ) } }
packages/SystemUI/checks/src/com/android/internal/systemui/lint/RunTestShouldUseKosmosDetector.kt +1 −5 Original line number Diff line number Diff line Loading @@ -29,16 +29,12 @@ import com.intellij.psi.PsiMethod import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.getContainingUFile /** * Detects test function naming violations regarding use of the backtick-wrapped space-allowed * feature of Kotlin functions. */ /** Detects use of `TestScope.runTest` when we should use `Kosmos.runTest` by go/kosmos-runtest */ class RunTestShouldUseKosmosDetector : Detector(), SourceCodeScanner { override fun getApplicableMethodNames() = listOf("runTest") override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { if (method.getReceiver()?.qualifiedName == "kotlinx.coroutines.test.TestScope") { val imports = node.getContainingUFile()?.imports.orEmpty().mapNotNull { it.importReference?.asSourceString() Loading
packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DoNotDirectlyConstructKosmosTest.kt 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.internal.systemui.lint import com.android.tools.lint.checks.infrastructure.TestFile import com.android.tools.lint.checks.infrastructure.TestFiles import com.android.tools.lint.checks.infrastructure.TestLintResult import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Issue import org.junit.Test class DoNotDirectlyConstructKosmosTest : SystemUILintDetectorTest() { override fun getDetector(): Detector = DoNotDirectlyConstructKosmosDetector() override fun getIssues(): List<Issue> = listOf(DoNotDirectlyConstructKosmosDetector.ISSUE) @Test fun wronglyTriesToDirectlyConstructKosmos() { val runOnSource = runOnSource( """ package test.pkg.name import com.android.systemui.kosmos.Kosmos import com.android.systemui.SysuiTestCase class MyTest: SysuiTestCase { val kosmos = Kosmos() } """ ) runOnSource .expectWarningCount(1) .expect( """ src/test/pkg/name/MyTest.kt:7: Warning: Prefer testKosmos to direct Kosmos() in sysui tests. go/testkosmos [DoNotDirectlyConstructKosmos] val kosmos = Kosmos() ~~~~~~ 0 errors, 1 warnings """ ) } @Test fun okToConstructKosmosIfNotInSysuiTestCase() { val runOnSource = runOnSource( """ package test.pkg.name import com.android.systemui.kosmos.Kosmos class MyTest { val kosmos = Kosmos() } """ ) runOnSource.expectWarningCount(0) } private fun runOnSource(source: String): TestLintResult { return lint() .files(TestFiles.kotlin(source).indented(), kosmosStub, sysuiTestCaseStub) .issues(DoNotDirectlyConstructKosmosDetector.ISSUE) .run() } companion object { private val kosmosStub: TestFile = kotlin( """ package com.android.systemui.kosmos class Kosmos """ ) private val sysuiTestCaseStub: TestFile = kotlin( """ package com.android.systemui class SysuiTestCase """ ) } }