Skip to content

Commit 150df59

Browse files
committed
WIP refactor to CleanupMode
1 parent 3cf5a74 commit 150df59

File tree

7 files changed

+240
-43
lines changed

7 files changed

+240
-43
lines changed

spock-core/src/main/java/org/spockframework/builder/BuilderHelper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.codehaus.groovy.runtime.MetaClassHelper;
2020
import org.spockframework.runtime.GroovyRuntimeUtil;
21+
import org.spockframework.util.CollectionUtil;
2122

2223
public class BuilderHelper {
2324
public static Object createInstance(Class clazz, Object... args) {
@@ -31,6 +32,14 @@ public static Object createInstance(Class clazz, Object... args) {
3132

3233
// IDEA: could support creation of collection types here
3334

35+
if (clazz.isEnum()) {
36+
if (args.length == 1 && args[0] instanceof String) {
37+
return Enum.valueOf(clazz, (String) args[0]);
38+
} else {
39+
throw new IllegalArgumentException("Cannot create enum " + clazz.getName() + " with arguments " + args);
40+
}
41+
}
42+
3443
if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0) {
3544
String kind = clazz.isPrimitive() ? "primitive" : clazz.isInterface() ? "interface" : "abstract";
3645
throw new RuntimeException(String.format( "Cannot instantiate %s type %s", kind, clazz.getName()));

spock-core/src/main/java/org/spockframework/runtime/extension/builtin/TempDirExtension.java

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
*/
15+
116
package org.spockframework.runtime.extension.builtin;
217

318
import org.spockframework.runtime.extension.IAnnotationDrivenExtension;
19+
import org.spockframework.runtime.extension.IMethodInterceptor;
420
import org.spockframework.runtime.model.*;
521
import org.spockframework.tempdir.TempDirConfiguration;
6-
import org.spockframework.util.*;
22+
import org.spockframework.util.Beta;
23+
import org.spockframework.util.Checks;
24+
import org.spockframework.util.UnreachableCodeError;
725
import spock.lang.TempDir;
826

927
import java.util.EnumSet;
@@ -25,21 +43,47 @@ public TempDirExtension(TempDirConfiguration configuration) {
2543

2644
@Override
2745
public void visitFieldAnnotation(TempDir annotation, FieldInfo field) {
28-
TempDirInterceptor interceptor = TempDirInterceptor.forField(field, configuration.baseDir, !annotation.cleanup() || configuration.keep);
46+
TempDir.CleanupMode cleanupMode = annotation.cleanup();
47+
cleanupMode = cleanupMode == TempDir.CleanupMode.DEFAULT ? configuration.cleanup : cleanupMode;
48+
TempDirInterceptor interceptor = TempDirInterceptor.forField(field, configuration.baseDir, annotation, cleanupMode);
2949

30-
// attach interceptor
3150
SpecInfo specInfo = field.getParent();
3251
if (field.isShared()) {
3352
specInfo.getBottomSpec().addSharedInitializerInterceptor(interceptor);
3453
} else {
3554
specInfo.addInitializerInterceptor(interceptor);
3655
}
56+
if (cleanupMode == TempDir.CleanupMode.ON_SUCCESS) {
57+
registerForAllFeatures(specInfo, new TempDirInterceptor.FailureTracker(annotation));
58+
}
59+
}
60+
61+
private static void registerForAllFeatures(SpecInfo specInfo, IMethodInterceptor interceptor) {
62+
specInfo.getBottomSpec().getAllFeatures().forEach(featureInfo -> featureInfo.getFeatureMethod().addInterceptor(interceptor));
3763
}
3864

3965
@Override
4066
public void visitParameterAnnotation(TempDir annotation, ParameterInfo parameter) {
41-
Checks.checkArgument(VALID_METHOD_KINDS.contains(parameter.getParent().getKind()), () -> "@TempDir can only be used on setup, setupSpec or feature method parameters.");
42-
TempDirInterceptor interceptor = TempDirInterceptor.forParameter(parameter, configuration.baseDir, configuration.keep);
43-
parameter.getParent().addInterceptor(interceptor);
67+
TempDir.CleanupMode cleanupMode = annotation.cleanup();
68+
cleanupMode = cleanupMode == TempDir.CleanupMode.DEFAULT ? configuration.cleanup : cleanupMode;
69+
MethodInfo methodInfo = parameter.getParent();
70+
Checks.checkArgument(VALID_METHOD_KINDS.contains(methodInfo.getKind()), () -> "@TempDir can only be used on setup, setupSpec or feature method parameters.");
71+
TempDirInterceptor interceptor = TempDirInterceptor.forParameter(parameter, configuration.baseDir, annotation, cleanupMode);
72+
methodInfo.addInterceptor(interceptor);
73+
74+
if (cleanupMode == TempDir.CleanupMode.ON_SUCCESS) {
75+
TempDirInterceptor.FailureTracker failureTracker = new TempDirInterceptor.FailureTracker(annotation);
76+
switch (methodInfo.getKind()) {
77+
case SETUP:
78+
case SETUP_SPEC:
79+
registerForAllFeatures(methodInfo.getParent(), failureTracker);
80+
break;
81+
case FEATURE:
82+
methodInfo.addInterceptor(failureTracker);
83+
break;
84+
default:
85+
throw new UnreachableCodeError();
86+
}
87+
}
4488
}
4589
}

spock-core/src/main/java/org/spockframework/runtime/extension/builtin/TempDirInterceptor.java

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
*/
15+
116
package org.spockframework.runtime.extension.builtin;
217

318
import org.codehaus.groovy.runtime.ResourceGroovyMethods;
@@ -8,6 +23,7 @@
823
import org.spockframework.runtime.model.FieldInfo;
924
import org.spockframework.runtime.model.ParameterInfo;
1025
import org.spockframework.util.*;
26+
import spock.lang.TempDir;
1127

1228
import java.io.File;
1329
import java.io.IOException;
@@ -17,10 +33,10 @@
1733
import java.nio.file.Path;
1834
import java.nio.file.SimpleFileVisitor;
1935
import java.nio.file.attribute.BasicFileAttributes;
36+
import java.util.Objects;
2037
import java.util.regex.Pattern;
2138

2239
import static java.nio.file.FileVisitResult.CONTINUE;
23-
import static org.spockframework.runtime.model.MethodInfo.MISSING_ARGUMENT;
2440

2541
/**
2642
* @author dqyuan
@@ -36,18 +52,21 @@ public class TempDirInterceptor implements IMethodInterceptor {
3652
private final IThrowableBiConsumer<IMethodInvocation, Path, Exception> valueSetter;
3753
private final String name;
3854
private final Path parentDir;
39-
private final boolean keep;
55+
private final TempDir annotation;
56+
private final TempDir.CleanupMode cleanupMode;
4057

4158
private TempDirInterceptor(
4259
IThrowableBiConsumer<IMethodInvocation, Path, Exception> valueSetter,
4360
String name,
4461
Path parentDir,
45-
boolean keep) {
62+
TempDir annotation,
63+
TempDir.CleanupMode cleanupMode) {
4664

4765
this.valueSetter = valueSetter;
4866
this.name = name;
4967
this.parentDir = parentDir;
50-
this.keep = keep;
68+
this.annotation = annotation;
69+
this.cleanupMode = cleanupMode;
5170
}
5271

5372
private String dirPrefix(IMethodInvocation invocation) {
@@ -88,7 +107,9 @@ protected Path setUp(IMethodInvocation invocation) throws Exception {
88107
@Override
89108
public void intercept(IMethodInvocation invocation) throws Throwable {
90109
Path path = setUp(invocation);
91-
invocation.getStore(NAMESPACE).put(path, new TempDirContainer(path, keep));
110+
// not super thrilled about identityHashCode, but apparently annotations use field equality and not identity
111+
TempDirContainer old = invocation.getStore(NAMESPACE).put(System.identityHashCode(annotation), new TempDirContainer(path, cleanupMode));
112+
Checks.checkState(old == null, () -> "Replaced other value: " + old.path);
92113
invocation.proceed();
93114
}
94115

@@ -149,44 +170,87 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
149170
}
150171
}
151172

152-
static TempDirInterceptor forField(FieldInfo fieldInfo, Path parentDir, boolean keep) {
173+
static TempDirInterceptor forField(FieldInfo fieldInfo, Path parentDir, TempDir annotation, TempDir.CleanupMode cleanupMode) {
153174
IThrowableFunction<Path, ?, Exception> typeMapper = createPathToTypeMapper(fieldInfo.getType());
154175
return new TempDirInterceptor(
155176
(invocation, path) -> fieldInfo.writeValue(invocation.getInstance(), typeMapper.apply(path)),
156177
fieldInfo.getName(),
157178
parentDir,
158-
keep);
179+
annotation,
180+
cleanupMode);
159181
}
160182

161-
static TempDirInterceptor forParameter(ParameterInfo parameterInfo, Path parentDir, boolean keep) {
183+
static TempDirInterceptor forParameter(ParameterInfo parameterInfo, Path parentDir, TempDir annotation, TempDir.CleanupMode cleanupMode) {
162184
IThrowableFunction<Path, ?, Exception> typeMapper = createPathToTypeMapper(parameterInfo.getReflection().getType());
163185
return new TempDirInterceptor(
164186
(IMethodInvocation invocation, Path path) -> {
165187
invocation.resolveArgument(parameterInfo.getIndex(), typeMapper.apply(path));
166188
},
167189
parameterInfo.getName(),
168190
parentDir,
169-
keep);
191+
annotation,
192+
cleanupMode);
193+
}
194+
195+
static class FailureTracker implements IMethodInterceptor {
196+
private final TempDir annotation;
197+
198+
FailureTracker(TempDir annotation) {
199+
this.annotation = annotation;
200+
}
201+
202+
@Override
203+
public void intercept(IMethodInvocation invocation) throws Throwable {
204+
TempDirContainer tempDirContainer = invocation.getStore(NAMESPACE).get(System.identityHashCode(annotation), TempDirContainer.class);
205+
try {
206+
invocation.proceed();
207+
} catch (Throwable t) {
208+
tempDirContainer.markFailed();
209+
throw t;
210+
}
211+
}
170212
}
171213

172214
static class TempDirContainer implements AutoCloseable {
173215
private final Path path;
174-
private final boolean keep;
216+
private final TempDir.CleanupMode cleanupMode;
217+
private volatile boolean failed = false;
175218

176-
TempDirContainer(Path path, boolean keep) {
219+
TempDirContainer(Path path, TempDir.CleanupMode cleanupMode) {
177220
this.path = path;
178-
this.keep = keep;
221+
this.cleanupMode = cleanupMode;
222+
}
223+
224+
void markFailed() {
225+
failed = true;
226+
}
227+
228+
private void destroy(Path path) throws IOException {
229+
switch (cleanupMode) {
230+
case ON_SUCCESS:
231+
if (failed) {
232+
System.err.printf("TempDir '%s' not deleted because the test failed, please delete it manually after investigation.%n",
233+
path.toAbsolutePath());
234+
return;
235+
}
236+
case ALWAYS:
237+
case DEFAULT:
238+
deleteTempDir(path);
239+
break;
240+
case NEVER:
241+
break;
242+
default:
243+
throw new IllegalStateException("Unknown cleanup mode: " + cleanupMode);
244+
}
179245
}
180246

181247
@Override
182248
public void close() {
183-
if (!keep) {
184249
try {
185-
deleteTempDir(path);
250+
destroy(path);
186251
} catch (IOException e) {
187252
ExceptionUtil.sneakyThrow(e);
188253
}
189-
}
190254
}
191255
}
192256
}

spock-core/src/main/java/org/spockframework/runtime/model/MethodKind.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
/*
2-
* Copyright 2009 the original author or authors.
2+
* Copyright 2024 the original author or authors.
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
713
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
1514
*/
1615

1716
package org.spockframework.runtime.model;
@@ -54,4 +53,8 @@ public boolean isFeatureScopedFixtureMethod() {
5453
public boolean isSpecScopedFixtureMethod() {
5554
return this == SETUP_SPEC || this == CLEANUP_SPEC;
5655
}
56+
57+
public boolean isInitializerMethod() {
58+
return this == INITIALIZER || this == SHARED_INITIALIZER;
59+
}
5760
}

spock-core/src/main/java/org/spockframework/tempdir/TempDirConfiguration.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import org.spockframework.util.Beta;
44
import spock.config.ConfigurationObject;
5+
import spock.lang.TempDir;
56

67
import java.nio.file.Path;
8+
import java.util.Optional;
79

810
/**
911
*
@@ -32,8 +34,15 @@ public class TempDirConfiguration {
3234
public Path baseDir = null;
3335

3436
/**
35-
* Whether to keep the temp directory or not after test,
36-
* default is system property {@code spock.tempDir.keep} or false if it is not set.
37+
* Whether to keep the temp directory or not after test if it failed,
38+
* default is system property {@link TempDir#TEMP_DIR_CLEANUP_PROPERTY} or {@link TempDir.CleanupMode#ALWAYS} if it is not set.
39+
*
40+
* @see TempDir#cleanup()
41+
* @see TempDir#TEMP_DIR_CLEANUP_PROPERTY
42+
*
43+
* @since 2.3
3744
*/
38-
public boolean keep = Boolean.getBoolean("spock.tempDir.keep");
45+
public TempDir.CleanupMode cleanup = Optional.ofNullable(System.getProperty(TempDir.TEMP_DIR_CLEANUP_PROPERTY))
46+
.map(TempDir.CleanupMode::valueOf)
47+
.orElse(TempDir.CleanupMode.ALWAYS);
3948
}

spock-core/src/main/java/spock/lang/TempDir.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,31 @@
4949
@Target({ElementType.FIELD, ElementType.PARAMETER})
5050
@ExtensionAnnotation(TempDirExtension.class)
5151
public @interface TempDir {
52+
String TEMP_DIR_CLEANUP_PROPERTY = "spock.tempdir.cleanup";
53+
5254
/**
5355
* Whether to cleanup the directory after the test.
56+
*
57+
* @since 2.3
5458
*/
55-
boolean cleanup() default true;
59+
CleanupMode cleanup() default CleanupMode.DEFAULT;
60+
61+
enum CleanupMode {
62+
/**
63+
* Use the default cleanup mode, configured via {@link #TEMP_DIR_CLEANUP_PROPERTY} or {@link org.spockframework.tempdir.TempDirConfiguration#cleanup}.
64+
*/
65+
DEFAULT,
66+
/**
67+
* Always cleanup the directory after the test.
68+
*/
69+
ALWAYS,
70+
/**
71+
* Cleanup the directory only if the test has succeeded.
72+
*/
73+
ON_SUCCESS,
74+
/**
75+
* Never cleanup the directory after the test.
76+
*/
77+
NEVER
78+
}
5679
}

0 commit comments

Comments
 (0)