It has been a year, which means it is once again time to re-examine our TODO Lint rule.
TL;DR: The rule checks if a TODO includes mandatory information – an assignee and a date.
We have also explored providing alternatives when suggesting quick fixes:
Today we will add another feature: enforcing the Lint rule in test files.
Say we have this test file with an invalid TODO:
class MainActivityTest {
// TODO write tests
}
A quick and simple modification is to update the lint configuration in the
project’s build.gradle.kts file with checkTestSources:
lint {
// ...
checkTestSources = true
}
From the documentation and the comment in the sample DSL:
// Normally most lint checks are not run on test sources (except the checks
// dedicated to looking for mistakes in unit or instrumentation tests, unless
// ignoreTestSources is true). You can turn on normal lint checking in all
// sources with the following flag, false by default
For the purposes of today’s discussion, I do not want to enable all the Lint rules to run on my test sources but I do want the TODO Detector to.
One of the parameters in a Lint rule’s Implementation is the Scope. From the
documentation:
Scopeis an enum which lists various types of files that a detector may want to analyze.
For example, there is a scope for XML files, there is a scope for Java and Kotlin files, there is a scope for .class files, and so on.
Typically lint cares about which set of scopes apply, so most of the APIs take anEnumSet<Scope>, but we’ll often refer to this as just “the scope” instead of the “scope set”.
Read more about Scopes here. The “various types of files” the documentation refer to are listed here.
To recap, this is how the current Implementation of the TODO Detector looks like:
private val IMPLEMENTATION = Implementation(
TodoDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
The name Scope.JAVA_FILE_SCOPE confused me for a little bit – the documentation states an EnumSet
is required but the name implies it is a simple Scope. In actual truth, it is an EnumSet:
val JAVA_FILE_SCOPE: EnumSet<Scope> = EnumSet.of(JAVA_FILE)
So to enable inspection of test sources, we need to add the more aptly-named Scope.TEST_SOURCES:
private val IMPLEMENTATION = Implementation(
TodoDetector::class.java,
EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
After rebuilding the project to pick up the Lint rule changes, errors in test sources should now be flagged:
For completion, we should add a test for this new configuration. The API
has changed a bit since my previous post on unit testing Lint rules post,
but the idea is the same – provide a TestFile the unit tests can run in.
Read more about TestFiles in the API Guide.
Note that when creating a TestFile, there is a constructor that takes a to parameter:
@NonNull
public static TestFile kotlin(@NonNull String to, @NonNull @Language("kotlin") String source) {
return TestFiles.kotlin(to, source);
}
where to is the fully qualified path of the source.
I haven’t found any direct documentation on this, but this is exactly
what we need to indicate to Lint that a file is a test source. From one of the
platform Lint rules,
it looks like adding a test prefix is sufficient:
lint().files(
kotlin(
"test/test/pkg/TestClass.kt",
"""
package test.pkg
class TestClass {
// TODO-Zarah Some comments
}
"""
).indented()
)
The existing unit tests for this rule are pretty comprehensive, so for this change I opted to only add a missing date test. If this test passes it follows that the rule works and all other scenarios would pass as well.
As always, the updates to the Detector and the tests are available on GitHub.
To read my past posts on this topic, check out the posts tagged with Lint, some of which are linked below:
- Anatomy of a Lint Issue
- Detectors Unit Testing
- Multi-module Rules
- Multi-module Testing
- Providing LintFix Alternatives
Here are some first-party resources for Lint: