Add Lint rules that warn when using enqueue() for `PeriodicWorkRequest`s.
* The lint rule suggests that they use `enqueueUniquePeriodicWork` instead.
Test: Added unit tests.
Change-Id: I1047b86d5e7a1ad4139c17dff0665c001833b68f
diff --git a/work/workmanager-lint/src/main/java/androidx/work/lint/PeriodicEnqueueIssueDetector.kt b/work/workmanager-lint/src/main/java/androidx/work/lint/PeriodicEnqueueIssueDetector.kt
new file mode 100644
index 0000000..3f14013
--- /dev/null
+++ b/work/workmanager-lint/src/main/java/androidx/work/lint/PeriodicEnqueueIssueDetector.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.work.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
+
+class PeriodicEnqueueIssueDetector : Detector(), SourceCodeScanner {
+ companion object {
+ val ISSUE = Issue.create(
+ id = "BadPeriodicWorkRequestEnqueue",
+ briefDescription = "Use `enqueueUniquePeriodicWork()` instead of `enqueue()`",
+ explanation = """
+ When using `enqueue()` for `PeriodicWorkRequest`s, you might end up enqueuing
+ duplicate requests unintentionally. You should be using
+ `enqueueUniquePeriodicWork` with an `ExistingPeriodicWorkPolicy.KEEP` instead.
+ """,
+ androidSpecific = true,
+ category = Category.CORRECTNESS,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ PeriodicEnqueueIssueDetector::class.java, Scope.JAVA_FILE_SCOPE
+ )
+ )
+ }
+
+ override fun getApplicableMethodNames(): List<String>? = listOf("enqueue")
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (context.evaluator.isMemberInClass(method, "androidx.work.WorkManager")) {
+ val periodic = node.valueArguments.filter { argument ->
+ val type = argument.getExpressionType()?.canonicalText
+ type == "androidx.work.PeriodicWorkRequest" ||
+ type == "java.util.List<? extends androidx.work.PeriodicWorkRequest>" ||
+ type == "java.util.List<? extends androidx.work.WorkRequest>"
+ }
+ if (periodic.isNotEmpty()) {
+ context.report(
+ ISSUE,
+ context.getLocation(method),
+ message = "Use `enqueueUniquePeriodicWork()` instead of `enqueue()`"
+ )
+ }
+ }
+ }
+}
diff --git a/work/workmanager-lint/src/main/java/androidx/work/lint/WorkManagerIssueRegistry.kt b/work/workmanager-lint/src/main/java/androidx/work/lint/WorkManagerIssueRegistry.kt
index 8a42724..8e69bab 100644
--- a/work/workmanager-lint/src/main/java/androidx/work/lint/WorkManagerIssueRegistry.kt
+++ b/work/workmanager-lint/src/main/java/androidx/work/lint/WorkManagerIssueRegistry.kt
@@ -25,6 +25,7 @@
class WorkManagerIssueRegistry : IssueRegistry() {
override val minApi: Int = CURRENT_API
override val issues: List<Issue> = listOf(
- BadConfigurationProviderIssueDetector.ISSUE
+ BadConfigurationProviderIssueDetector.ISSUE,
+ PeriodicEnqueueIssueDetector.ISSUE
)
}
diff --git a/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt b/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt
new file mode 100644
index 0000000..28e1cf4
--- /dev/null
+++ b/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2019 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.work.lint
+
+import androidx.work.lint.Stubs.ONE_TIME_WORK_REQUEST
+import androidx.work.lint.Stubs.PERIODIC_WORK_REQUEST
+import androidx.work.lint.Stubs.WORK_MANAGER
+import androidx.work.lint.Stubs.WORK_REQUEST
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+
+class PeriodicEnqueueIssueDetectorTest {
+
+ @Test
+ fun warnWhenEnqueueingPeriodicWork() {
+ val snippet = kotlin(
+ "com/example/Snippet.kt",
+ """
+ package com.example
+
+ import androidx.work.PeriodicWorkRequest
+ import androidx.work.WorkManager
+
+ class Test {
+ fun enqueueWork() {
+ val request = PeriodicWorkRequest()
+ val workManager: WorkManager = TODO("Get an implementation of WorkManager")
+ workManager.enqueue(request)
+ }
+ }
+ """
+ ).indented().within("src")
+
+ /* ktlint-disable max-line-length */
+ lint().files(
+ WORK_MANAGER,
+ WORK_REQUEST,
+ ONE_TIME_WORK_REQUEST,
+ PERIODIC_WORK_REQUEST,
+ snippet
+ )
+ .issues(PeriodicEnqueueIssueDetector.ISSUE)
+ .run()
+ .expect("""
+ src/androidx/work/WorkManager.kt:4: Warning: Use enqueueUniquePeriodicWork() instead of enqueue() [BadPeriodicWorkRequestEnqueue]
+ fun enqueue(request: WorkRequest)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """.trimIndent())
+ /* ktlint-enable max-line-length */
+ }
+
+ @Test
+ fun warnWhenEnqueuingListOfPeriodicWork() {
+ val snippet = kotlin(
+ "com/example/Snippet.kt",
+ """
+ package com.example
+
+ import androidx.work.PeriodicWorkRequest
+ import androidx.work.WorkManager
+
+ class Test {
+ fun enqueueWork() {
+ val requests = listOf(PeriodicWorkRequest())
+ val workManager: WorkManager = TODO("Get an implementation of WorkManager")
+ workManager.enqueue(requests)
+ }
+ }
+ """
+ ).indented().within("src")
+
+ /* ktlint-disable max-line-length */
+ lint().files(
+ WORK_MANAGER,
+ WORK_REQUEST,
+ ONE_TIME_WORK_REQUEST,
+ PERIODIC_WORK_REQUEST,
+ snippet
+ )
+ .issues(PeriodicEnqueueIssueDetector.ISSUE)
+ .run()
+ .expect("""
+ src/androidx/work/WorkManager.kt:5: Warning: Use enqueueUniquePeriodicWork() instead of enqueue() [BadPeriodicWorkRequestEnqueue]
+ fun enqueue(requests: List<WorkRequest>)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """.trimIndent())
+ /* ktlint-enable max-line-length */
+ }
+
+ @Test
+ fun warnWhenEnqueuingListOfPeriodicWorkAndOneTimeWork() {
+ val snippet = kotlin(
+ "com/example/Snippet.kt",
+ """
+ package com.example
+
+ import androidx.work.WorkRequest
+ import androidx.work.OneTimeWorkRequest
+ import androidx.work.PeriodicWorkRequest
+ import androidx.work.WorkManager
+
+ class Test {
+ fun enqueueWork() {
+ val requests = listOf(OneTimeWorkRequest(), PeriodicWorkRequest())
+ val workManager: WorkManager = TODO("Get an implementation of WorkManager")
+ workManager.enqueue(requests)
+ }
+ }
+ """
+ ).indented().within("src")
+
+ /* ktlint-disable max-line-length */
+ lint().files(
+ WORK_MANAGER,
+ WORK_REQUEST,
+ ONE_TIME_WORK_REQUEST,
+ PERIODIC_WORK_REQUEST,
+ snippet
+ )
+ .issues(PeriodicEnqueueIssueDetector.ISSUE)
+ .run()
+ .expect("""
+ src/androidx/work/WorkManager.kt:5: Warning: Use enqueueUniquePeriodicWork() instead of enqueue() [BadPeriodicWorkRequestEnqueue]
+ fun enqueue(requests: List<WorkRequest>)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """.trimIndent())
+ /* ktlint-enable max-line-length */
+ }
+
+ @Test
+ fun noWarningsWhenUsingOneTimeWorkRequest() {
+ val snippet = kotlin(
+ "com/example/Snippet.kt",
+ """
+ package com.example
+
+ import androidx.work.PeriodicWorkRequest
+ import androidx.work.WorkManager
+
+ class Test {
+ fun enqueueWork() {
+ val request = OneTimeWorkRequest()
+ val workManager: WorkManager = TODO("Get an implementation of WorkManager")
+ workManager.enqueue(request)
+ }
+ }
+ """
+ ).indented().within("src")
+
+ lint().files(
+ WORK_MANAGER,
+ WORK_REQUEST,
+ ONE_TIME_WORK_REQUEST,
+ PERIODIC_WORK_REQUEST,
+ snippet
+ )
+ .issues(PeriodicEnqueueIssueDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun noWarningsWhenEnqueuingListOfOneTimeWorkRequests() {
+ val snippet = kotlin(
+ "com/example/Snippet.kt",
+ """
+ package com.example
+
+ import androidx.work.PeriodicWorkRequest
+ import androidx.work.WorkManager
+
+ class Test {
+ fun enqueueWork() {
+ val requests = listOf(OneTimeWorkRequest())
+ val workManager: WorkManager = TODO("Get an implementation of WorkManager")
+ workManager.enqueue(requests)
+ }
+ }
+ """
+ ).indented().within("src")
+
+ lint().files(
+ WORK_MANAGER,
+ WORK_REQUEST,
+ ONE_TIME_WORK_REQUEST,
+ PERIODIC_WORK_REQUEST,
+ snippet
+ )
+ .issues(PeriodicEnqueueIssueDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+}
\ No newline at end of file
diff --git a/work/workmanager-lint/src/test/java/androidx/work/lint/Stubs.kt b/work/workmanager-lint/src/test/java/androidx/work/lint/Stubs.kt
index 6b158d2c..1034d25 100644
--- a/work/workmanager-lint/src/test/java/androidx/work/lint/Stubs.kt
+++ b/work/workmanager-lint/src/test/java/androidx/work/lint/Stubs.kt
@@ -45,4 +45,45 @@
"""
)
.indented().within("src")
-}
\ No newline at end of file
+
+ val WORK_REQUEST: TestFile = kotlin(
+ "androidx/work/WorkRequest.kt",
+ """
+ package androidx.work
+
+ open class WorkRequest
+ """
+ ).indented().within("src")
+
+ val ONE_TIME_WORK_REQUEST: TestFile = kotlin(
+ "androidx/work/OneTimeWorkRequest.kt",
+ """
+ package androidx.work
+
+ class OneTimeWorkRequest: WorkRequest()
+ """
+ ).indented().within("src")
+
+ val PERIODIC_WORK_REQUEST: TestFile = kotlin(
+ "androidx/work/PeriodicWorkRequest.kt",
+ """
+ package androidx.work
+
+ class PeriodicWorkRequest: WorkRequest()
+ """
+ ).indented().within("src")
+
+ val WORK_MANAGER: TestFile = kotlin(
+ "androidx/work/WorkManager.kt",
+ """
+ package androidx.work
+
+ interface WorkManager {
+ fun enqueue(request: WorkRequest)
+ fun enqueue(requests: List<WorkRequest>)
+ fun enqueueUniqueWork(name: String, request: PeriodicWorkRequest)
+ }
+ """
+ )
+ .indented().within("src")
+}