-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce JUnitValueSource
check
#188
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A parameterized test to check the parameterized test is some mind blowing stuff 🤯.
Pretty cool stuff man. This definitely is a nice check!
I'm in doubt whether it's the right approach for this PR. To be honest, to make the test easier to comprehend it'd actually lean towards not using this setup.
For the identification
test it'll be quite some code but make the test way easier to read.
In the identification
we can show all the variants it can flag. In the replacement
test, we don't need to test every variant that we can rewrite. It'll be enough to show how the replacement is performed and that it works.
If we do it like that, I think the size will be reasonable still.
Perhaps @Stephan202 can give some input as well.
WDYT of this proposal @oxkitsune?
I left some other comments as well :).
A general note on method names:
Try to look at the method names in "isolation" do they explain what they are really doing? Is it clear what we're trying to achieve with that method?
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flushing some comments.
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
(Note, this was a WIP commit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By changing the way we use the Matcher
at the start we can already rule out some checks. Now we know it is a method that is eligible to rewrite. That makes it easier. We can make some more assumptions (that we are sure of), to make the code more elegant.
Let's discuss tomorrow morning, would be nice to add some extra explanation here and there.
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/JUnitValueSourceTest.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
f76cdc8
to
893f65b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, one big open point for me is how we currently handle the Stream.of(1, 2).map(Arguments::arguments)
in the code. It is an edge case that prevents us from making the code quite a bit cleaner. Will have to get back to that 🤔.
Added a commit and left some comments.
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/JUnitValueSourceTest.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/JUnitValueSourceTest.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/JUnitValueSourceTest.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/JUnitValueSourceTest.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/JUnitValueSourceTest.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to remove this and the comment above that.
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
0f0786e
to
a3b8fb7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few commits. PTAL @oxkitsune 😄.
There are two XXX
's which I consider to be out of scope for now but important to write down.
The PR is now ready for your 👀 @Stephan202.
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
3214894
to
fcfd455
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased and added a commit. Cool feature!
You will notice that I refactored quite a bit. While I do agree that we should be reasonable when defining the scope of a test, we should:
- Err on the side of correctness, but to avoid false-positives and to ensure a good first impression of users who try out the library.
- (As a result) aim for very high test coverage.
- (Where reasonable) try to support code bases which aren't as uniform as Picnic's.
Still TODO:
- Extend test coverage, primarily "should not match" cases. (See the
XXX
comments I left.) - ~One more full pass over the code.
AnnotationTree annotationTree = | ||
ASTHelpers.getAnnotationWithSimpleName( | ||
tree.getModifiers().getAnnotations(), "MethodSource"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can avoid this if we use the annotations
matcher defined above; will push a proposal.
tree.getModifiers().getAnnotations(), "MethodSource"); | ||
|
||
return tryConstructValueSourceFix(parameterType, annotationTree, state) | ||
.map(fix -> describeMatch(tree, fix.build())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's only one caller, we can push the .build()
into tryConstructValueSourceFix
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we're flagging the issue on tree
, but I think it's more intuitive if annotationTree
is flagged.
case "Integer": | ||
return "ints"; | ||
default: | ||
return type.toLowerCase() + "s"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return type.toLowerCase() + "s"; | |
return type.toLowerCase(Locale.ROOT) + "s"; |
(We should have a check for this 🤔.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed :).
.filter(MethodInvocationTree.class::isInstance) | ||
.map(MethodInvocationTree.class::cast) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This surprised me a bit, until I realized that this is about unwrapping the argument(...)
call. Let's make that more explicit.
Edit: the code I'll push doesn't have this limitation.
.flatMap(mit -> mit.getArguments().stream()) | ||
.filter(JUnitValueSource::isCompileTimeConstant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In combination with the test below we're assuming that the arguments(...)
call accepts precisely one argument, while there could be multiple (for example if the value factory is used in multiple places (and those places may be in another class)).
methodSourceAnnotation, | ||
String.format( | ||
"@ValueSource(%s = {%s})", | ||
toValueSourceAttributeName(parameterType.toString()), attributeValue)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .toString()
here will yield e.g. java.lang.String
, which is not what is intended; the resultant code won't compile. (There should be test cases for each case in toValueSourceAttributeName
.)
NB: For cases like these it's useful to run ./run-mutation-tests.sh
, then inspect error-prone-contrib/target/pit-reports/index.html
. Integrating PIT with the build is on the TODO list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For generic types such as Class<?>
this will also create an invalid @ValueSource
argument.
private static Optional<ReturnTree> getReturnTree(MethodTree methodTree) { | ||
return methodTree.getBody().getStatements().stream() | ||
.filter(ReturnTree.class::isInstance) | ||
.findFirst() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is more than one we should back out, not just take the first one.
.filter(method -> method.getName().contentEquals(factoryMethodName)) | ||
.findFirst() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may be overloads; we need to select the nullary version.
private static final Matcher<ExpressionTree> STREAM_OF_ARGUMENTS = | ||
staticMethod().onClass(Stream.class.getName()).named("of"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't actually test that the values are arguments 🤔 (While the code below does assume that the Stream.of
arguments are method invocations.)
"", | ||
" private static Stream<Arguments> fooTestCases() {", | ||
" return Stream.of(arguments('a'), arguments('b'), arguments(CONST_CHAR));", | ||
" }", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Internally we generally place the value factory methods before the associated test method; suggest we do the same here.
Oops, just meant to re-request review from myself 😬. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oxkitsune have you started working on the tests? Asking to prevent double work :).
"", | ||
" @ParameterizedTest", | ||
" @MethodSource(\"streamOfClassesAndClassArguments\")", | ||
" void string(Class<?> c) {}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
" void string(Class<?> c) {}", | |
" void clazz(Class<?> c) {}", |
(Don't have the code checked out right now; something to fix later.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied.
I have! |
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
07cdbb1
to
08c6784
Compare
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So a bit more work is needed. Can have a closer look later.
...r-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/util/MoreJUnitMatchers.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documenting some additional caveats; will try to cover these in one go.
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Show resolved
Hide resolved
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java
Outdated
Show resolved
Hide resolved
08c6784
to
7e25353
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased and added one more commit. Documented as many limitations as I could identify; those are for another day.
Suggested commit message:
Introduce `JUnitValueSource` check (#188)
This new check replaces JUnit `@MethodSource` usages with an equivalent
`@ValueSource` annotation where possible.
public JUnitValueSource() {} | ||
|
||
@Override | ||
public Description matchMethod(MethodTree tree, VisitorState state) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had another look at this. It's easier not to special-case the nullary ()
syntax; instead we can back-off if there are overloads.
...r-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/util/MoreJUnitMatchers.java
Outdated
Show resolved
Hide resolved
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added one more commit.
|
||
private static Matcher<ExpressionTree> isArrayArgumentValueCandidate() { | ||
return anyOf( | ||
classLiteral((tree, state) -> true), (tree, state) -> ASTHelpers.constValue(tree) != null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realized we can write this in a nicer way.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yoooooo the quality and epicness of this check 🤯🤯🤯.
Great improvements @Stephan202 , let's get this beast merged and see the impact 😄.
Added a commit to drop a space and will rebase. After that when its 🍏 ==> merge!
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
1 similar comment
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
Implement BugPattern to migrate parameterized JUnit tests with a single argument that's known at compile time to the
@ValueSource
annotation.The
@ValueSource
annotation supports:java.lang
wrapperjava.lang.String
java.lang.Class
Example:
Suggested commit message: