Skip to content

Commit edeeae2

Browse files
Merge pull request #5 from anotherchrisberry/intercept-setup-cleanup
catch exceptions in setup/cleanup methods
2 parents 5bafa22 + 39a076b commit edeeae2

File tree

8 files changed

+159
-8
lines changed

8 files changed

+159
-8
lines changed

Diff for: .gitignore

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
.gradle
33
out
44
*.iml
5+
*.ipr
6+
*.iws
57
build
8+
gradle
9+
gradlew
10+
gradlew.bat
611
.classpath
712
.project
8-
.settings/
9-
bin/
13+
.settings
14+
bin

Diff for: build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
apply plugin: "groovy"
22
apply plugin: "maven"
33
apply plugin: "maven-publish"
4+
apply plugin: "idea"
45

56
repositories() {
67
mavenCentral()

Diff for: src/main/groovy/com/anotherchrisberry/spock/extensions/retry/RetryInterceptor.groovy

+14-4
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,23 @@ class RetryInterceptor implements IMethodInterceptor {
3030
throw t
3131
}
3232
invocation.spec.cleanupMethods.each {
33-
if (it.reflection) {
34-
ReflectionUtil.invokeMethod(invocation.target, it.reflection)
33+
try {
34+
if (it.reflection) {
35+
ReflectionUtil.invokeMethod(invocation.target, it.reflection)
36+
}
37+
} catch (Throwable t2) {
38+
LOG.warn("Retry caught failure ${attempts + 1} / ${retryMax + 1} while cleaning up", t2)
3539
}
3640
}
3741
invocation.spec.setupMethods.each {
38-
if (it.reflection) {
39-
ReflectionUtil.invokeMethod(invocation.target, it.reflection)
42+
try {
43+
if (it.reflection) {
44+
ReflectionUtil.invokeMethod(invocation.target, it.reflection)
45+
}
46+
} catch (Throwable t2) {
47+
// increment counter, since this is the start of the re-run
48+
attempts++
49+
LOG.info("Retry caught failure ${attempts + 1} / ${retryMax + 1} while setting up", t2)
4050
}
4151
}
4252
}

Diff for: src/main/groovy/com/anotherchrisberry/spock/extensions/retry/RetrySpecExtension.groovy

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.anotherchrisberry.spock.extensions.retry
22

3+
import org.spockframework.compiler.model.FeatureMethod
34
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
45
import org.spockframework.runtime.model.FeatureInfo
6+
import org.spockframework.runtime.model.MethodInfo
57
import org.spockframework.runtime.model.SpecInfo
68

79
class RetrySpecExtension extends AbstractAnnotationDrivenExtension<RetryOnFailure> {
@@ -31,7 +33,7 @@ class RetrySpecExtension extends AbstractAnnotationDrivenExtension<RetryOnFailur
3133
List<FeatureInfo> featuresToRetry = [selfAndSubSpecs.features].flatten().unique()
3234
for (FeatureInfo feature : featuresToRetry) {
3335
clearInterceptors(feature)
34-
feature.getFeatureMethod().addInterceptor(new RetryInterceptor(getNumberOfRetries(retries)))
36+
addInterceptors(feature, retries)
3537
}
3638
}
3739
}
@@ -41,7 +43,25 @@ class RetrySpecExtension extends AbstractAnnotationDrivenExtension<RetryOnFailur
4143
return Integer.parseInt(System.getProperty("spock-retry.times", defaultRetries))
4244
}
4345

46+
private List<MethodInfo> getInterceptableMethods(FeatureInfo feature) {
47+
SpecInfo spec = feature.getSpec()
48+
[ spec.setupMethods,
49+
spec.setupSpecMethods,
50+
spec.cleanupMethods,
51+
spec.cleanupSpecMethods,
52+
feature.featureMethod
53+
].flatten().unique() as List<MethodInfo>
54+
}
55+
4456
private void clearInterceptors(FeatureInfo featureInfo) {
45-
featureInfo.featureMethod.interceptors.removeAll { it.class == RetryInterceptor }
57+
List<MethodInfo> interceptableMethods = getInterceptableMethods(featureInfo)
58+
interceptableMethods.each { it.interceptors.removeAll { it.class == RetryInterceptor } }
59+
}
60+
61+
private void addInterceptors(FeatureInfo featureInfo, RetryOnFailure retries) {
62+
def interceptor = new RetryInterceptor(getNumberOfRetries(retries))
63+
getInterceptableMethods(featureInfo).each {
64+
it.addInterceptor(interceptor)
65+
}
4666
}
4767
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.anotherchrisberry.spock.extensions.retry
2+
3+
import spock.lang.Specification
4+
5+
@RetryOnFailure(times=2)
6+
class RetryOnFailureCleanupSpec extends Specification {
7+
8+
static Integer cleanupCalls = 0
9+
10+
void cleanup() {
11+
if (cleanupCalls < 2) {
12+
cleanupCalls++
13+
throw new RuntimeException("have not tried enough times ($cleanupCalls)")
14+
}
15+
}
16+
17+
void 'class level test'() {
18+
given:
19+
Mockable mockable = Mock()
20+
21+
when:
22+
mockable.doMockyThing()
23+
24+
then:
25+
cleanupCalls == 2
26+
1 * mockable.doMockyThing()
27+
0 * _
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.anotherchrisberry.spock.extensions.retry
2+
3+
class RetryOnFailureCleanupSubclassSpec extends RetryOnFailureCleanupSpec {
4+
5+
void cleanupSpec() {
6+
cleanupCalls = 0
7+
}
8+
9+
void 'should call parent setup method'() {
10+
given:
11+
Mockable mockable = Mock()
12+
13+
when:
14+
mockable.doMockyThing()
15+
16+
then:
17+
cleanupCalls == 2
18+
1 * mockable.doMockyThing()
19+
0 * _
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.anotherchrisberry.spock.extensions.retry
2+
3+
import spock.lang.Specification
4+
5+
@RetryOnFailure(times=2)
6+
class RetryOnFailureSetupSpec extends Specification {
7+
8+
static Integer setupCalls = 0
9+
10+
void setup() {
11+
if (setupCalls < 2) {
12+
setupCalls++
13+
throw new RuntimeException("have not tried enough times ($setupCalls)")
14+
}
15+
}
16+
17+
void 'class level test'() {
18+
given:
19+
Mockable mockable = Mock()
20+
21+
when:
22+
mockable.doMockyThing()
23+
24+
then:
25+
setupCalls == 2
26+
1 * mockable.doMockyThing()
27+
0 * _
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.anotherchrisberry.spock.extensions.retry
2+
3+
@RetryOnFailure(times=5)
4+
class RetryOnFailureSetupSubclassSpec extends RetryOnFailureSetupSpec {
5+
6+
static Integer specCalls = 0
7+
8+
void setupSpec() {
9+
setupCalls = 0
10+
}
11+
12+
void 'should call parent setup method'() {
13+
given:
14+
Mockable mockable = Mock()
15+
16+
when:
17+
mockable.doMockyThing()
18+
19+
then:
20+
setupCalls == 2
21+
1 * mockable.doMockyThing()
22+
0 * _
23+
}
24+
25+
void 'should still try to run own method when setup fails in superclass'() {
26+
when:
27+
if (specCalls < 3) {
28+
specCalls++
29+
throw new RuntimeException("have not tried enough times ($specCalls)")
30+
}
31+
32+
then:
33+
setupCalls == 2
34+
specCalls == 3
35+
}
36+
}

0 commit comments

Comments
 (0)