Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spock unit test is stuck when parallel stream is used

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)

Thread Dump

like image 979
MFIhsan Avatar asked Oct 31 '22 17:10

MFIhsan


1 Answers

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
}

like image 136
Newt0n Avatar answered Nov 08 '22 05:11

Newt0n