I am having a weird behavior with Java 8 parallel streams in Spock unit tests.
The following code takes a list of PlanConfigConstraintValidator
and verifies if all return true
.
Unit test for this code in Spock is given below. Everything worked fine until I added .parallel()
. When parallel stream is used, the unit test is stuck and never stops.
If I return only one Mock(PlanConfigConstraintValidator)
, it works, but not when the size is above one.
I have provided the thread dump also at the bottom.
@Override
public boolean isValid(PlanConfig planConfig, ConstraintValidatorContext context) {
return getPlanConfigConstraintValidators().stream().parallel()
.filter(validator -> shouldValidate(planConfig, validator))
.allMatch(validator -> isValid(planConfig, context, validator));
}
Here is the unit test code
def "isValid - all validators return true"() {
when:
def validator = Spy(PlanConfigValidator) {
getPlanConfigConstraintValidators() >> [
Mock(PlanConfigConstraintValidator),
Mock(PlanConfigConstraintValidator),
]
shouldValidate(_, _) >> true
isValid(_, _, _) >> true
}
def result = validator.isValid(new PlanConfig(), Mock(ConstraintValidatorContext))
then:
result
}
Thread Dump
"main" #1 prio=5 os_prio=0 tid=0x00000000026aa000 nid=0x1dd78 in Object.wait() [0x00000000034ac000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076da712d8> (a java.util.stream.MatchOps$MatchTask)
at java.util.concurrent.ForkJoinTask.externalAwaitDone(ForkJoinTask.java:334)
- locked <0x000000076da712d8> (a java.util.stream.MatchOps$MatchTask)
at java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:405)
at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734)
at java.util.stream.MatchOps$MatchOp.evaluateParallel(MatchOps.java:242)
at java.util.stream.MatchOps$MatchOp.evaluateParallel(MatchOps.java:196)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
at java.util.stream.ReferencePipeline.allMatch(ReferencePipeline.java:454)
I had a similar issue today and it seems that the issue is related to the implementation of a Spy
in Spock.
Using parallelStream()
didn't solve the issue in my case.
For me the discussion here has been helpful.
I ended up with an anonymous subclass. The example of the OP would look like this with my solution:
def "isValid - all validators return true"() {
when:
def validator = new PlanConfigValidator() {
@Override
List<PlanConfigConstraintValidator> getPlanConfigConstraintValidators() {
Arrays.asList(Mock(PlanConfigConstraintValidator),
Mock(PlanConfigConstraintValidator))
}
@Override
boolean shouldValidate(_,_) {
return true
}
@Override
boolean isValid(_,_,_) {
return true
}
}
def result = validator.isValid(new PlanConfig(), Mock(ConstraintValidatorContext))
then:
result
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With