diff options
Diffstat (limited to 'mobile/android/fenix/mozilla-detekt-rules')
15 files changed, 657 insertions, 0 deletions
diff --git a/mobile/android/fenix/mozilla-detekt-rules/.gitignore b/mobile/android/fenix/mozilla-detekt-rules/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/.gitignore @@ -0,0 +1 @@ +/build diff --git a/mobile/android/fenix/mozilla-detekt-rules/build.gradle b/mobile/android/fenix/mozilla-detekt-rules/build.gradle new file mode 100644 index 0000000000..8588c0504f --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'kotlin' + +dependencies { + compileOnly ComponentsDependencies.tools_detekt_api + implementation ComponentsDependencies.androidx_annotation + + testImplementation ComponentsDependencies.tools_detekt_api + testImplementation ComponentsDependencies.tools_detekt_test + testImplementation FenixDependencies.junitApi + testImplementation FenixDependencies.junitParams + testRuntimeOnly FenixDependencies.junitEngine +} + +test { + useJUnitPlatform {} +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/CustomRulesetConsoleReport.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/CustomRulesetConsoleReport.kt new file mode 100644 index 0000000000..f7e8280617 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/CustomRulesetConsoleReport.kt @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules + +import io.gitlab.arturbosch.detekt.api.ConsoleReport +import io.gitlab.arturbosch.detekt.api.Detektion + +/** + * A reporter that custom formats violations of our custom lint rules. + */ +class CustomRulesetConsoleReport : ConsoleReport() { + @Suppress("DEPRECATION") // locationAsString + override fun render(detektion: Detektion): String? { + return detektion.findings["mozilla-detekt-rules"]?.fold("") { accumulator, finding -> + accumulator + "${finding.id}:\n ${finding.file}\n ${finding.messageOrDescription()}\n\n" + // This creates an extra newline at the very end but it's not worth fixing. + } + } +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/CustomRulesetProvider.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/CustomRulesetProvider.kt new file mode 100644 index 0000000000..c2955fb9c0 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/CustomRulesetProvider.kt @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.RuleSet +import io.gitlab.arturbosch.detekt.api.RuleSetProvider +import org.mozilla.fenix.detektrules.perf.MozillaBannedPropertyAccess +import org.mozilla.fenix.detektrules.perf.MozillaRunBlockingCheck +import org.mozilla.fenix.detektrules.perf.MozillaStrictModeSuppression +import org.mozilla.fenix.detektrules.perf.MozillaUseLazyMonitored + +class CustomRulesetProvider : RuleSetProvider { + override val ruleSetId: String = "mozilla-detekt-rules" + + override fun instance(config: Config): RuleSet = RuleSet( + ruleSetId, + listOf( + MozillaBannedPropertyAccess(config), + MozillaStrictModeSuppression(config), + MozillaCorrectUnitTestRunner(config), + MozillaRunBlockingCheck(config), + MozillaUseLazyMonitored(config), + ), + ) +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/MozillaCorrectUnitTestRunner.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/MozillaCorrectUnitTestRunner.kt new file mode 100644 index 0000000000..89735ae877 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/MozillaCorrectUnitTestRunner.kt @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.internal.PathFilters +import org.jetbrains.kotlin.psi.KtAnnotationEntry + +private val BANNED_TEST_RUNNERS = setOf( + // When updating this list, also update the violation message. + "AndroidJUnit4", + "RobolectricTestRunner", +) + +// There is a change to how we output violations in a different PR so the formatting for message +// might be weird but it should still be readable. +private val VIOLATION_MSG = """The following code blocks: + | + | @RunWith(AndroidJUnit4::class) + | @Config(application = TestApplication::class) + | OR + | @RunWith(RobolectricTestRunner::class) + | @Config(application = TestApplication::class) + | + | should be replaced with: + | + | @RunWith(FenixRobolectricTestRunner::class) + | + | To reduce redundancy of setting @Config. No @Config specification is necessary because + | the FenixRobolectricTestRunner sets it automatically. + | + | Relatedly, adding robolectric to a test increases its runtime non-trivially so please + | ensure Robolectric is necessary before adding it.""".trimMargin() + +/** + * Ensures we're using the correct Robolectric unit test runner. + */ +class MozillaCorrectUnitTestRunner(config: Config) : Rule(config) { + override val issue = Issue( + MozillaCorrectUnitTestRunner::class.simpleName!!, + Severity.Defect, + "Verifies we're using the correct Robolectric unit test runner", + Debt.FIVE_MINS, + ) + + override val filters: PathFilters? + get() = PathFilters.of( + includes = listOf("**/test/**"), // unit tests only. + excludes = emptyList(), + ) + + override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) { + super.visitAnnotationEntry(annotationEntry) + + // Example of an annotation: @RunWith(FenixRobolectricUnitTestRunner::class) + val annotationClassName = annotationEntry.shortName?.identifier + if (annotationClassName == "RunWith") { + // Example arg in parens: "(FenixRobolectricUnitTestRunner::class)" + val argInParens = annotationEntry.lastChild.node.chars + val argClassName = argInParens.substring(1 until argInParens.indexOf(":")) + + val isInViolation = BANNED_TEST_RUNNERS.any { it == argClassName } + if (isInViolation) { + report(CodeSmell(issue, Entity.from(annotationEntry), VIOLATION_MSG)) + } + } + } +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaBannedPropertyAccess.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaBannedPropertyAccess.kt new file mode 100644 index 0000000000..dcd467cf44 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaBannedPropertyAccess.kt @@ -0,0 +1,52 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules.perf + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import org.jetbrains.kotlin.psi.* + +class MozillaBannedPropertyAccess(config: Config = Config.empty) : Rule(config) { + override val issue = Issue( + "MozillaBannedPropertyAccess", + Severity.Defect, + DESCR, + Debt.FIVE_MINS, + ) + + private val banned by lazy { + val bannedPropertiesList = valueOrDefault("bannedProperties", "") + if (bannedPropertiesList.contentEquals("")) { + listOf() + } else { + bannedPropertiesList.split(",") + } + } + + override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { + super.visitDotQualifiedExpression(expression) + val possiblyBannedPropertyAccess = expression.node.chars + + // Does the current property access, a.b.c.d.possibly.Banned, end with + // it. Use endsWith() so that package qualification does not interfere + // with the check. In other words, if the configuration tells this rule + // to ban d.definitely.Banned, flag a use of the property via + // x.y.z.d.definitely.Banned. + banned.filter { possiblyBannedPropertyAccess.endsWith(it) }.map { + CodeSmell( + issue, + Entity.from(expression), + "Using $possiblyBannedPropertyAccess is not allowed because accessing property $it is against Mozilla policy. See 'mozilla-detekt-rules' stanza in 'config/detekt.yml' for more information.\n", + ) + }.forEach { report(it) } + } +} + +internal const val DESCR = "Using banned property" diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaRunBlockingCheck.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaRunBlockingCheck.kt new file mode 100644 index 0000000000..9064814bda --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaRunBlockingCheck.kt @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules.perf + +import io.gitlab.arturbosch.detekt.api.* +import org.jetbrains.kotlin.psi.* + +private const val VIOLATION_MSG = "Please use `org.mozilla.fenix.perf.runBlockingIncrement` instead " + + "because it allows us to monitor the code for performance regressions." + +/** + * A check to prevent us from working around mechanisms we implemented in + * @see org.mozilla.fenix.perf.RunBlockingCounter.runBlockingIncrement to count how many runBlocking + * are used. + */ +class MozillaRunBlockingCheck(config: Config) : Rule(config) { + + override val issue = Issue( + "MozillaRunBlockingCheck", + Severity.Performance, + "Prevents us from working around mechanisms we implemented to count how many " + + "runBlocking are used", + Debt.TWENTY_MINS, + ) + + override fun visitImportDirective(importDirective: KtImportDirective) { + if (importDirective.importPath?.toString() == "kotlinx.coroutines.runBlocking") { + report(CodeSmell(issue, Entity.from(importDirective), VIOLATION_MSG)) + } + } +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaStrictModeSuppression.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaStrictModeSuppression.kt new file mode 100644 index 0000000000..cc07bcde7e --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaStrictModeSuppression.kt @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules.perf + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtImportDirective + +private const val VIOLATION_MSG = "Please use `components.strictMode.resetAfter` instead because it has " + + "performance improvements and additional code to monitor for performance regressions." + +/** + * A check to prevent us from working around mechanisms we implemented to prevent suppressing StrictMode. + */ +class MozillaStrictModeSuppression(config: Config) : Rule(config) { + override val issue = Issue( + "MozillaStrictModeSuppression", + Severity.Performance, + "Prevents us from working around mechanisms we implemented to prevent suppressing StrictMode", + Debt.TEN_MINS, + ) + + override fun visitImportDirective(importDirective: KtImportDirective) { + super.visitImportDirective(importDirective) + reportIfImportAcResetAfter(importDirective) + } + + override fun visitCallExpression(expression: KtCallExpression) { + super.visitCallExpression(expression) + reportIfCallStrictModeSetPolicy(expression) + } + + private fun reportIfImportAcResetAfter(importDirective: KtImportDirective) { + if (importDirective.importPath?.toString() == "mozilla.components.support.ktx.android.os.resetAfter") { + report(CodeSmell(issue, Entity.from(importDirective), VIOLATION_MSG)) + } + } + + private fun reportIfCallStrictModeSetPolicy(expression: KtCallExpression) { + // There is probably a more correct way of doing this but this API is not well documented + // so it's not worth our time, I think. + val receiver = expression.parent?.firstChild?.node?.chars + val calledMethod = expression.calleeExpression?.firstChild?.node?.chars + + // This won't catch if setVmPolicy is imported directly. However, this is unlikely so let's + // not handle it now. Maybe we can add it when we add tests for this file. + if (receiver == "StrictMode") { + val violationMsg = when (calledMethod) { + "setThreadPolicy" -> VIOLATION_MSG + "setVmPolicy" -> + "NOT YET IMPLEMENTED: please consult the perf team about implementing" + + "`StrictModeManager.resetAfter`: we want to understand the performance implications " + + "of suppressing setVmPolicy before allowing it." + else -> null + } + + violationMsg?.let { msg -> + report(CodeSmell(issue, Entity.from(expression), msg)) + } + } + } +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaUseLazyMonitored.kt b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaUseLazyMonitored.kt new file mode 100644 index 0000000000..9239ac2d27 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/java/org/mozilla/fenix/detektrules/perf/MozillaUseLazyMonitored.kt @@ -0,0 +1,85 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules.perf + +import androidx.annotation.VisibleForTesting +import androidx.annotation.VisibleForTesting.Companion.PRIVATE +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.internal.PathFilters +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyDelegate + +private const val FUN_LAZY = "lazy" + +private const val ERROR_MESSAGE = "Please use `by lazyMonitored` instead of `by lazy` " + + "to declare application components (e.g. variables in Components.kt): it contains extra code to " + + "monitor for performance regressions. This change is not necessary for non-components." + +/** + * Prevents use of lazy in component groups files where lazyMonitored should be used instead. + */ +open class MozillaUseLazyMonitored(config: Config) : Rule(config) { + override val issue = Issue( + this::class.java.simpleName, + Severity.Performance, + "Prevents use of lazy in component groups files where lazyMonitored should be used instead", + Debt.FIVE_MINS, + ) + + override val filters = PathFilters.of( + includes = COMPONENT_GROUP_FILES.map { "**$it" }, + excludes = emptyList(), + ) + + override fun visitPropertyDelegate(delegate: KtPropertyDelegate) { + super.visitPropertyDelegate(delegate) + + val delegateFunction = delegate.expression?.node?.firstChildNode // the "lazy" in "by lazy { ..." + if (delegateFunction?.chars == FUN_LAZY) { + report(CodeSmell(issue, Entity.from(delegate), ERROR_MESSAGE)) + } + } + + override fun visitProperty(property: KtProperty) { + super.visitProperty(property) + + // I only expect components to be declared as members, not at the top level or inside block scopes. + if (!property.isMember) return + + val rightHandSideOfAssignment = property.initializer + + // This is intended to catch `val example = lazy {` or `... lazy(`. It may be possible to + // make lazy not the first node to the right of the equal sign but it's probably uncommon + // enough that we don't handle that case. + val assignedFunction = rightHandSideOfAssignment?.firstChild + if (assignedFunction?.node?.chars == FUN_LAZY) { + report(CodeSmell(issue, Entity.from(property), ERROR_MESSAGE)) + } + } + + companion object { + /** + * All files that represent a "component group". + */ + @VisibleForTesting(otherwise = PRIVATE) + val COMPONENT_GROUP_FILES = listOf( + "Analytics", + "BackgroundServices", + "Components", + "Core", + "IntentProcessors", + "PerformanceComponent", + "Push", + "Services", + "UseCases", + ).map { "app/src/main/java/org/mozilla/fenix/components/$it.kt" } + } +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.ConsoleReport b/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.ConsoleReport new file mode 100644 index 0000000000..fb899601e4 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.ConsoleReport @@ -0,0 +1 @@ +org.mozilla.fenix.detektrules.CustomRulesetConsoleReport diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider b/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider new file mode 100644 index 0000000000..81e4111972 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider @@ -0,0 +1 @@ +org.mozilla.fenix.detektrules.CustomRulesetProvider diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/config.yml b/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/config.yml new file mode 100644 index 0000000000..ce0237ab02 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/main/resources/config.yml @@ -0,0 +1,3 @@ + MozillaBannedPropertyAccess: + active: true + bannedProperties: "Sample.Banned" diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaBannedPropertyAccessTest.kt b/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaBannedPropertyAccessTest.kt new file mode 100644 index 0000000000..fde95cb599 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaBannedPropertyAccessTest.kt @@ -0,0 +1,94 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +@file:Suppress("Deprecation") + +package org.mozilla.fenix.detektrules.perf + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.test.lint +import io.gitlab.arturbosch.detekt.test.yamlConfig +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream + +internal class MozillaBannedPropertyAccessTest { + + private lateinit var config: Config + + @BeforeEach + fun setup() { + config = yamlConfig("/config.yml") + } + + @Test + internal fun `non compliant property access should warn`() { + val findings = + MozillaBannedPropertyAccess(config).lint( + NONCOMPLIANT_ACCESS.trimIndent(), + ) + assertEquals(1, findings.size) + assertEquals(DESCR, findings[0].issue.description) + } + + @DisplayName("compliant ") + @MethodSource("compliantProvider") + @ParameterizedTest(name = "{1} should not warn") + internal fun testCompliantWhen(source: String) { + val findings = + MozillaBannedPropertyAccess(config).lint( + source, + ) + assertTrue(findings.isEmpty()) + } + + companion object { + @JvmStatic + fun compliantProvider(): Stream<Arguments> = + Stream.of( + arguments(COMPLIANT_ACCESS, "Safe property access"), + ) + } +} + +const val NONCOMPLIANT_ACCESS = """ + class Test { + lateinit var x: String + class Sample { + companion object { + public var Banned = "true".toBoolean() + } + } + init { + if (Sample.Banned) { + x = "true" + } + } + } + """ + +const val COMPLIANT_ACCESS = """ + class Test { + lateinit var x: String + class Sample { + companion object { + public var Banned = "true".toBoolean() + public var Allowed = "false".toBoolean() + + } + } + init { + if (Sample.Allowed) { + x = "true" + } + } + } + """ diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaStrictModeSuppressionTest.kt b/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaStrictModeSuppressionTest.kt new file mode 100644 index 0000000000..d78f8a7361 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaStrictModeSuppressionTest.kt @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules.perf + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.test.lint +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class MozillaStrictModeSuppressionTest { + + private lateinit var check: MozillaStrictModeSuppression + + @BeforeEach + fun setUp() { + check = MozillaStrictModeSuppression(Config.empty) + } + + @Test + fun `GIVEN a file with import not resetAfter WHEN linting THEN pass`() { + val file = """import android.os.StrictMode""" + assertNoFinding(file) + } + + @Test + fun `GIVEN a file with import ac resetAfter WHEN linting THEN fail`() { + val file = """import mozilla.components.support.ktx.android.os.resetAfter""" + assertOneFinding(file) + } + + @Test + fun `GIVEN a file with StrictMode getVmPolicy WHEN linting THEN pass`() { + val file = """fun main() { + | val policy = StrictMode.getVmPolicy() + | }""".trimMargin() + assertNoFinding(file) + } + + @Test + fun `GIVEN a file with StrictMode setVmPolicy WHEN linting THEN fail`() { + val file = """fun main() { + | val policy = StrictMode.VmPolicy.Builder.build() + | StrictMode.setVmPolicy(policy) + | }""".trimMargin() + assertOneFinding(file) + } + + @Test + fun `GIVEN a file with StrictMode setThreadPolicy WHEN linting THEN fail`() { + val file = """fun main() { + | val policy = StrictMode.ThreadPolicy.Builder.build() + | StrictMode.setThreadPolicy(policy) + | }""".trimMargin() + assertOneFinding(file) + } + + private fun assertNoFinding(fileStr: String) { + val findings = check.lint(fileStr) + assertEquals(0, findings.size) + } + + private fun assertOneFinding(fileStr: String) { + val findings = check.lint(fileStr) + assertEquals(1, findings.size) + assertEquals(check.issue, findings.first().issue) + } +} diff --git a/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaUseLazyMonitoredTest.kt b/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaUseLazyMonitoredTest.kt new file mode 100644 index 0000000000..2ed6fb68a9 --- /dev/null +++ b/mobile/android/fenix/mozilla-detekt-rules/src/test/java/org/mozilla/fenix/detektrules/perf/MozillaUseLazyMonitoredTest.kt @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.detektrules.perf + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.PathFilters +import io.gitlab.arturbosch.detekt.test.lint +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import java.io.File +import java.nio.file.Paths + +internal class MozillaUseLazyMonitoredTest { + + private lateinit var actualCheck: MozillaUseLazyMonitored + private lateinit var check: TestMozillaUseLazyMonitored + + @BeforeEach + fun setUp() { + actualCheck = MozillaUseLazyMonitored(Config.empty) + check = TestMozillaUseLazyMonitored() + } + + @Test + fun `GIVEN the hard-coded component group file paths THEN there are some to run the lint on`() { + assertFalse(MozillaUseLazyMonitored.COMPONENT_GROUP_FILES.isEmpty()) + } + + @Test + fun `GIVEN the hard-coded component group file paths THEN they all exist`() { + MozillaUseLazyMonitored.COMPONENT_GROUP_FILES.forEach { path -> + // The tests working directory is `<repo>/mozilla-detekt-rules` so we need to back out. + val modifiedPath = "../$path" + + val errorMessage = "This lint rule hard-codes paths to files containing \"component groups\". " + + "One of these files - $path - no longer exists. Please update the check " + + "${MozillaUseLazyMonitored::class.java.simpleName} with the new path." + assertTrue(File(modifiedPath).exists(), errorMessage) + } + } + + @Test + fun `GIVEN the hard-coded component group file paths THEN none of the files are ignored`() { + MozillaUseLazyMonitored.COMPONENT_GROUP_FILES.forEach { + assertTrue(actualCheck.filters?.isIgnored(Paths.get(it)) == false) + } + } + + @Test + fun `GIVEN a snippet with lazyMonitored THEN there are no findings`() { + val text = getCodeInClass("val example by lazyMonitored { 4 }") + assertNoFindings(text) + } + + @ParameterizedTest + @ValueSource( + strings = [ + "val example by lazy { 4 }", + "val example by lazy { 4 }", + "val example\nby\nlazy\n{\n4\n}", + ], + ) + fun `GIVEN a snippet with by lazy THEN there is a finding`(innerText: String) { + val text = getCodeInClass(innerText) + assertOneFinding(text) + } + + @ParameterizedTest + @ValueSource( + strings = [ + "val example = lazy(::exampleFun)", + "val example = lazy ( ::exampleFun)", + "val example\n=\nlazy\n(\n::exampleFun\n)", + ], + ) + fun `GIVEN a snippet with = lazy THEN there is a finding`(innerText: String) { + val text = getCodeInClass(innerText) + assertOneFinding(text) + } + + private fun assertNoFindings(text: String) { + val findings = check.lint(text) + assertEquals(0, findings.size) + } + + private fun assertOneFinding(text: String) { + val findings = check.lint(text) + assertEquals(1, findings.size) + assertEquals(check.issue, findings[0].issue) + } +} + +private fun getCodeInClass(text: String): String = """class Example() { + $text +}""" + +class TestMozillaUseLazyMonitored : MozillaUseLazyMonitored(Config.empty) { + // If left on their own, the path filters will prevent us from running checks on raw strings. + // I'm not sure the best way to work around that so we just override the value. + override val filters: PathFilters? = null +} |