Skip to content

Commit 3a63022

Browse files
committed
Add queries for VisibleForTesting annotations
1 parent 308628e commit 3a63022

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Finds code which uses elements that are only intended to be used by tests.
3+
*
4+
* Since these elements are not considered part of the public API, relying on them
5+
* should be avoided because they could be removed in future versions or their behavior
6+
* could change without any announcement.
7+
*
8+
* @kind problem
9+
*/
10+
11+
import java
12+
13+
class TestOnlyAnnotation extends Annotation {
14+
TestOnlyAnnotation() {
15+
getType().hasName([
16+
// JetBrains annotations
17+
"TestOnly",
18+
])
19+
}
20+
}
21+
22+
from Annotatable elementForTesting, Expr usage
23+
where
24+
(
25+
elementForTesting.getAnAnnotation() instanceof TestOnlyAnnotation
26+
or
27+
elementForTesting.getAnAnnotation().getType().hasName([
28+
// Android, Guava and JetBrains annotations
29+
"VisibleForTesting",
30+
])
31+
// To reduce false positives only consider elements which are public for testing and
32+
// are used from other package
33+
and elementForTesting.getCompilationUnit().getPackage() != usage.getCompilationUnit().getPackage()
34+
)
35+
and (
36+
elementForTesting.(Field).getAnAccess() = usage
37+
or elementForTesting = usage.(Call).getCallee().getSourceDeclaration()
38+
or elementForTesting = usage.(TypeAccess).getType().(RefType).getSourceDeclaration()
39+
)
40+
// Ignore if access is from test code
41+
and not (
42+
usage.getEnclosingCallable().getDeclaringType() instanceof TestClass
43+
or usage.getFile().getAbsolutePath().matches("%/test/%")
44+
)
45+
// Ignore if caller is itself also only intended for testing
46+
and not usage.getEnclosingCallable().getAnAnnotation() instanceof TestOnlyAnnotation
47+
select usage, "Uses element '$@' which is only visible for testing", elementForTesting, elementForTesting.getName()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Finds `protected` and `public` elements which are annotated with an annotation indicating
3+
* that the element is only supposed to be used by test code, respectively only has increased
4+
* visibility to be accessible by tests.
5+
*
6+
* Prefer making the element at most package-private (i.e. no access modifier), otherwise
7+
* the element might be accessed by accident outside of test code. Normally test code is in
8+
* the same package and can therefore access package-private elements. If the test is not
9+
* in the same package for some reason, a workaround could be to create a `public` accessor
10+
* class in the same package in the test sources which provides access to package-private
11+
* elements in the main source for the tests in the other package.
12+
*
13+
* @kind problem
14+
*/
15+
16+
import java
17+
18+
from Annotatable annotated
19+
where
20+
annotated.getAnAnnotation().getType().hasName([
21+
// Android, Guava and JetBrains annotations
22+
"VisibleForTesting",
23+
// JetBrains annotations
24+
"TestOnly",
25+
])
26+
and (
27+
annotated.(Modifiable).isProtected()
28+
or annotated.(Modifiable).isPublic()
29+
)
30+
select annotated, "Element is made publicly visible for testing; should be at most package-private"

0 commit comments

Comments
 (0)