-
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 VerifyOnlyElementInFlux
Refaster rule
#617
Conversation
Looks good. No mutations were possible for these changes. |
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.
Nice draft, in the small hours of the day ;)
The best ones, right? 😄 |
I wanted to extend the rule by also accepting inputs that match
But (!) while I was trying to make the rule work, I found a very weird situation that I cannot explain. The first version of the @BeforeTemplate
Duration before(Flux<T> flux, T object) {
return flux.collect(toImmutableList())
.as(StepVerifier::create)
.assertNext(collection -> assertThat(collection).containsExactly(object))
.verifyComplete();
} Alongside @BeforeTemplate
Duration before(Flux<T> flux, T object) {
return Refaster.anyOf(flux.collect(toImmutableList()), flux.collect(toImmutableSet()))
.as(StepVerifier::create)
.assertNext(collection -> assertThat(collection).containsExactly(object))
.verifyComplete();
} I am not sure how ImmutableSet<Duration> testVerifyOnlyElementInFlux() {
return ImmutableSet.of(
Flux.just(1)
.collect(toImmutableList())
.as(StepVerifier::create)
.assertNext(list -> assertThat(list).containsExactly(1))
.verifyComplete(),
Flux.just(2)
.collect(toImmutableSet())
.as(StepVerifier::create)
.assertNext(set -> assertThat(set).containsExactly(2))
.verifyComplete());
} I would expect the result to be: ImmutableSet<Duration> testVerifyOnlyElementInFlux() {
return ImmutableSet.of(
Flux.just(1).as(StepVerifier::create).expectNext(1).verifyComplete(),
Flux.just(2).as(StepVerifier::create).expectNext(2).verifyComplete());
} The fun part? Now the rule does not match diff (-expected +actual):
@@ -438,7 +438,11 @@
ImmutableSet<Duration> testVerifyOnlyElementInFlux() {
return ImmutableSet.of(
- Flux.just(1).as(StepVerifier::create).expectNext(1).verifyComplete(),
+ Flux.just(1)
+ .collect(toImmutableList())
+ .as(StepVerifier::create)
+ .assertNext(list -> assertThat(list).containsExactly(1))
+ .verifyComplete(),
Flux.just(2).as(StepVerifier::create).expectNext(2).verifyComplete());
}
} Am I missing something? Am I using |
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 @BeforeTemplate is quite big, can it be a problem? I don't know if/how it can be reduced in size;
I proposed a change that makes it one line smaller. In general, this is not really a problem as we are matching a bigger statement in this case.
- I am pretty sure the name of the rule is not the best, please suggest one that follows existing standards if you have better ideas;
I applied our future naming algorithm which is based on the content of the after template.
- It could be extended to toImmutableSet as well (see comment)
I used flux.collect(toImmutableList()) instead of flux.collectList() because of the rule FluxCollectToImmutableList;
Yes that is a good idea. I pushed some changes to make it work. I also tried it with Refaster#anyOf
but it didn't really work that well 🤔. With the more specific before template it seems better to have two different before templates because of the Immutable{List,Set}<T>
in the return types.
How does it work if there is a match for this rule, but with flux.collectList()? Will error-prone perform "two passes" and first move flux.collectList() to flux.collect(toImmutableList()) and then apply my rule?
Yes Error Prone will fix it in two runs. That's why our internal patch.sh
script runs again when changes are introduced by Error Prone.
Added a commit with some changes 😄, thanks for opening the PR 🚀 !
@@ -435,4 +435,8 @@ Duration testStepVerifierLastStepVerifyErrorMessage() { | |||
Duration testStepVerifierLastStepVerifyTimeout() { | |||
return StepVerifier.create(Mono.empty()).verifyTimeout(Duration.ZERO); | |||
} | |||
|
|||
Duration testVerifyOnlyElementInFlux() { | |||
return Flux.just(1).as(StepVerifier::create).expectNext(1).verifyComplete(); |
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.
Let's use distinct values in the tests :).
|
||
@AfterTemplate | ||
Duration after(Flux<T> flux, T object) { | ||
return flux.as(StepVerifier::create).expectNext(object).verifyComplete(); |
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 flux.as(StepVerifier::create).expectNext(object).verifyComplete(); | |
return flux.as(StepVerifier::create).expectNext(object); |
Besides the fact that the return types are now slightly different we can drop the verifyComplete
part.
Assuming the tests pass, we can do this rewrite as we test that the list literally contains one object. Strictly speaking this is not a behavior preserving change because of the updated return type. The verifyComplete
should be able to handle it though.
As a result we should also move this up as it belongs with the other StepVerifier.Step
return type rules.
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.
Should've replied to this comment :) #617 (comment)
Hmm, I tried looking into this and combining them but had the same issue. As before template I had the following:
Had the same problem, requires some more brain cycles at a different point in time :). Now have to focus on something else. (Note to self: didn't check the Javadoc yet) |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
Looks good. No mutations were possible for these changes. |
Hey @rickie thanks for the review! I won't have much time in the next days, but I'll pick this up. Just one thing about making it one line smaller: in my understanding, if we don't check for I'll come back to this asap, probably in the weekend 😄 |
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 a quick peek; nice rule!
Just one thing about making it one line smaller: in my understanding, if we don't check for
verifyComplete()
we could have a situation where the input does not doverifyComplete()
, and the output would give a warningUnfinished step verifier
.
As-is the rule will match both with and without verifyComplete()
, meaning it's more generic; a property we generally like. It's okay if a rule also matches code that is "otherwise wrong" 😄
return flux.collect(toImmutableSet()) | ||
.as(StepVerifier::create) | ||
.assertNext(set -> assertThat(set).containsExactly(object)); |
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 is not equivalent to the code below, since toImmutableSet()
drops duplicates. We could add an XXX
comment, or drop this case 🤔.
(If we keep it: Should indeed have a closer look at that Refaster#anyOf
issue.)
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, that would indeed be the best option. Might find some time for that later this week, but not sure.
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.
TBH, I think fixing this issue requires creating a Refaster rule in Error Prone and debugging why this specific Refaster#anyOf
usage doesn't work. That sounds like a bit more work. I'd suggest to drop the toImmutableSet()
and create an XXX and pick up that debugging later. WDYT? @Stephan202
@@ -1295,6 +1295,28 @@ StepVerifier.Step<T> after(StepVerifier.Step<T> step, T object) { | |||
} | |||
} | |||
|
|||
/** Avoid collecting when verifying only the given element is in the given {@link Flux}. */ |
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.
/** Avoid collecting when verifying only the given element is in the given {@link Flux}. */ | |
/** Avoid value collection when verifying that a {@link Flux} emits exactly one value. */ |
Isn't this a problem? Let's say the original code is: Flux.just(1)
.collect(toImmutableList())
.as(StepVerifier::create)
.assertNext(list -> assertThat(list).containsExactly(1)); This code is correct and does not give warnings. We are sure the flux is completed because we collected to list and then verified the only element in the list was the expected one. Flux.just(1).as(StepVerifier::create).expectNext(1); We are not verifying if the flux is completed, which gives the warning |
That code might not yield a warning, but that's just a limitation of IDEA; it is just as incorrect. Neither variant verifies anything 😬. The following check should fail, but doesn't (because there's no verification): Flux.just(2)
.collect(toImmutableList())
.as(StepVerifier::create)
.assertNext(list -> assertThat(list).containsExactly(1)); |
I am sorry for not coming back at this for more than a month, it has been really busy lately 😄 I hope there's no rush. To be honest, I still don't understand. Take the following snippet: @BeforeTemplate
StepVerifier.Step<ImmutableList<T>> before(Flux<T> flux, T object) {
return flux.collect(toImmutableList())
.as(StepVerifier::create)
.assertNext(list -> assertThat(list).containsExactly(object));
}
@AfterTemplate
StepVerifier.Step<T> after(Flux<T> flux, T object) {
return flux.as(StepVerifier::create).expectNext(object);
} The code in the |
5cad15f
to
200aafc
Compare
Looks good. No mutations were possible for these changes. |
Hello, just passing through 👀 I think the confusion is that
is also checking for one and only one element (Since we only have one As far as I can tell, the @AfterTemplate is the less verbose method to check for this. |
Hey @mohamedsamehsalah thanks for passing by!
So they are testing two different things 🤔 |
Thanks for thoroughly thinking about this and explaining your thought process, I like it 😄! One important assumption we are making here, which should be true for 99% of the cases, is that someone is using something like Having the
So together with the knowledge that an Let's consider the following failing test:
Would correctly be rewritten to:
Which would fail with:
Here a quick (not fullproof) GitHub search on Picnic code that shows we are in all cases verifying. If I'm missing anything in my explanation, please let me know 😄, happy to elaborate further! |
200aafc
to
36444a3
Compare
Looks good. No mutations were possible for these changes. |
058a070
to
8492c5a
Compare
@rickie thanks a lot for the amazing explanation! Also, for the offline discussion 😄 To finish up the PR, I decided to move on with the suggestion to remove the Just a question about the test framework. I tried minimising the test by removing the
And if we give
So I decided to just stick to also having a |
Looks good. No mutations were possible for these changes. |
1 similar comment
Looks good. No mutations were possible for these changes. |
8492c5a
to
6536c11
Compare
Looks good. No mutations were possible for these changes. |
6536c11
to
db51011
Compare
Looks good. No mutations were possible for these changes. |
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.
Changes LGTM. Let's indeed not consider the toImmutableSet
for now.
db51011
to
3da3627
Compare
@giovannizotta Your reasoning about the tests is correct, we can use the |
Looks good. No mutations were possible for these changes. |
3da3627
to
b198db4
Compare
b198db4
to
1d52d89
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.
Nice discussions!
Rebased and added a commit. Suggested commit message:
Introduce `FluxAsStepVerifierExpectNext` Refaster rule (#617)
static final class FluxAsStepVerifierExpectNext<T> { | ||
@BeforeTemplate | ||
StepVerifier.Step<ImmutableList<T>> before(Flux<T> flux, T object) { | ||
return flux.collect(toImmutableList()) |
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.
Though it comes with some caveats, we can also match other list-producing collectors here, such as toCollection(ArrayList::new)
.
.collect(toImmutableList()) | ||
.as(StepVerifier::create) | ||
.assertNext(list -> assertThat(list).containsExactly(2)) | ||
.verifyComplete(); |
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 .verifyComplete()
can be omitted (which hides that we do change the return type here :).)
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.
Ahh, we can do the <?>
as return type indeed.
Looks good. No mutations were possible for these changes. |
1 similar comment
Looks good. No mutations were possible for these changes. |
ImmutableSet<StepVerifier.Step<?>> testFluxAsStepVerifierExpectNext() { | ||
return ImmutableSet.of( | ||
Flux.just(1) | ||
.collect(toImmutableList()) | ||
.as(StepVerifier::create) | ||
.assertNext(list -> assertThat(list).containsExactly(2)), | ||
Flux.just(3) | ||
.collect(toCollection(ArrayList::new)) | ||
.as(StepVerifier::create) | ||
.assertNext(list -> assertThat(list).containsExactly(4))); | ||
} |
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.
After pushing this I realized that perhaps for rule one test case would suffice, or at least be more consistent with what we do elsewhere. 🤷
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.
I agree, I'll add a commit to drop the second test case.
Will merge once 🍏. |
Looks good. No mutations were possible for these changes. |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
When verifying a
Flux
only has one element, avoid collecting to list.Notes:
@BeforeTemplate
is quite big, can it be a problem? I don't know if/how it can be reduced in size;toImmutableSet
as well (see comment)flux.collect(toImmutableList())
instead offlux.collectList()
because of the ruleFluxCollectToImmutableList
;flux.collectList()
? Will error-prone perform "two passes" and first moveflux.collectList()
toflux.collect(toImmutableList())
and then apply my rule?