Skip to content
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

Any extension that calls invocation.proceed() multiple times like @Retry misbehaves #1862

Open
Vampire opened this issue Jan 10, 2024 · 0 comments
Labels

Comments

@Vampire
Copy link
Member

Vampire commented Jan 10, 2024

Describe the bug

MethodInvocation has an Iterator for interceptors.
If you call invocation.proceed(), then the iterator is queried for the next interceptor which is then invoked.
So if you call invocation.proceed() a second or further time, all subsequent iterators for that method invocation are not invoked.

MethodInvocation is pretty thin state-wise.
For most things it is just a data-holder.
The only things that are directly stateful are when setArguments(...) is called and when proceed() is called.

I guess the most proper solution would be to replace this.arguments = arguments; by System.arraycopy(arguments, 0, this.arguments, 0, arguments.length); and getting rid of the iterator for interceptors, but instead having an own MethodInvocation for each interceptor that knows the next interceptor to call.

This should be a backwards compatible change and fix all extensions that might do something similar to what @Retry does.

To Reproduce

  • Have a global extension (don't forget to register it in META-INF/services) like
    class GlobalRetryExtension implements IGlobalExtension {
       private final Retry retry = Retried.getAnnotation(Retry)
    
       private final RetryExtension extension = new RetryExtension()
    
       @Override
       void visitSpec(SpecInfo spec) {
          if (!spec.getAnnotation(Retry)) {
             extension.visitSpecAnnotation(retry, spec)
          }
       }
    
       @Retry(mode = SETUP_FEATURE_CLEANUP)
       private static final class Retried {
       }
    }
  • Have a specification with a @TempDir field
  • Make sure the specification fails so that it is retried

Now either set breakpoints in the RetryIterationInterceptor and TempDirInterceptor, or check in the feature the presence of the temp dir, or similar.

Expected behavior

Retrying (and any interceptors doing something similar) should properly invoke subsequent interceptors each time.

Actual behavior

With non-suspending breakpoints that do some logging on the two mentioned interceptors, one on the proceed() line and one on the following line for each, you get output like:

retry try
temp dir: .../spock_build_should_fail_i_0_rootDir6715396349974787942
temp dir destroy:.../spock_build_should_fail_i_0_rootDir6715396349974787942
retry after try
retry try
retry after try
retry try
retry after try
retry try
retry after try

Dependencies

Spock 2.3-groovy-3.0

@Vampire Vampire added the bug label Jan 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant