-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Björn Michael <b.michael@gmx.de>
- Loading branch information
Showing
5 changed files
with
159 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
junit-jupiter-api/src/main/java/org/junit/jupiter/api/AutoClose.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright 2015-2023 the original author or authors. | ||
* | ||
* All rights reserved. This program and the accompanying materials are | ||
* made available under the terms of the Eclipse Public License v2.0 which | ||
* accompanies this distribution and is available at | ||
* | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
|
||
package org.junit.jupiter.api; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import org.apiguardian.api.API; | ||
|
||
/** | ||
* The {@code AutoClose} annotation is used to automatically close resources used in JUnit 5 tests. | ||
* | ||
* <p> | ||
* This annotation should be applied to fields within JUnit 5 test classes. It indicates that the annotated | ||
* resource should be automatically closed after the test execution. The annotation targets | ||
* {@link java.lang.annotation.ElementType#FIELD} elements, allowing it to be applied to instance variables. | ||
* </p> | ||
* | ||
* <p> | ||
* By default, the {@code AutoClose} annotation expects the annotated resource to provide a {@code close()} method | ||
* that will be invoked for closing the resource. However, developers can customize the closing behavior by providing | ||
* a different method name through the {@code value} attribute. For example, setting {@code value = "destroy"} will | ||
* look for a method named {@code destroy()} to close the resource. | ||
* </p> | ||
* | ||
* <p> | ||
* The {@code AutoClose} annotation is retained at runtime, allowing it to be accessed and processed during test execution. | ||
* </p> | ||
* | ||
* @see java.lang.annotation.Retention | ||
* @see java.lang.annotation.Target | ||
* @since 5.11 | ||
*/ | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.FIELD) | ||
@API(status = API.Status.EXPERIMENTAL, since = "5.11") | ||
public @interface AutoClose { | ||
|
||
/** | ||
* Specifies the name of the method to invoke for closing the resource. | ||
* The default value is "close". | ||
* | ||
* @return the method name for closing the resource | ||
*/ | ||
String value() default "close"; | ||
|
||
} |
99 changes: 99 additions & 0 deletions
99
...t-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright 2015-2023 the original author or authors. | ||
* | ||
* All rights reserved. This program and the accompanying materials are | ||
* made available under the terms of the Eclipse Public License v2.0 which | ||
* accompanies this distribution and is available at | ||
* | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
package org.junit.jupiter.engine.extension; | ||
|
||
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.util.function.Predicate; | ||
import org.apiguardian.api.API; | ||
import org.junit.jupiter.api.AutoClose; | ||
import org.junit.jupiter.api.extension.AfterAllCallback; | ||
import org.junit.jupiter.api.extension.AfterEachCallback; | ||
import org.junit.jupiter.api.extension.ExtensionConfigurationException; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
import org.junit.jupiter.api.extension.ExtensionContext.Namespace; | ||
import org.junit.jupiter.api.extension.ExtensionContext.Store; | ||
import org.junit.platform.commons.logging.Logger; | ||
import org.junit.platform.commons.logging.LoggerFactory; | ||
import org.junit.platform.commons.util.ExceptionUtils; | ||
import org.junit.platform.commons.util.ReflectionUtils; | ||
|
||
/** | ||
* {@code AutoCloseExtension} is a JUnit Jupiter extension that closes resources if a field in a test class is annotated | ||
* with {@link AutoClose @AutoClose}. | ||
* | ||
* <p>Consult the Javadoc for {@link AutoClose} for details on the contract. | ||
* | ||
* @since 5.11 | ||
* @see AutoClose | ||
* @see AutoCloseable | ||
*/ | ||
@API(status = API.Status.EXPERIMENTAL, since = "5.11") | ||
public class AutoCloseExtension implements AfterAllCallback, AfterEachCallback { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(AutoCloseExtension.class); | ||
static final Namespace NAMESPACE = Namespace.create(AutoClose.class); | ||
|
||
@Override | ||
public void afterAll(ExtensionContext context) { | ||
Store contextStore = context.getStore(NAMESPACE); | ||
Class<?> testClass = context.getRequiredTestClass(); | ||
|
||
registerCloseables(contextStore, testClass, null); | ||
} | ||
|
||
@Override | ||
public void afterEach(ExtensionContext context) { | ||
Store contextStore = context.getStore(NAMESPACE); | ||
|
||
for (Object instance : context.getRequiredTestInstances().getAllInstances()) { | ||
registerCloseables(contextStore, instance.getClass(), instance); | ||
} | ||
} | ||
|
||
private void registerCloseables(Store contextStore, Class<?> testClass, /* @Nullable */ Object testInstance) { | ||
Predicate<Field> isStatic = testInstance == null ? ReflectionUtils::isStatic : ReflectionUtils::isNotStatic; | ||
findAnnotatedFields(testClass, AutoClose.class, isStatic).forEach(field -> { | ||
try { | ||
contextStore.put(field, asCloseableResource(testInstance, field)); | ||
} | ||
catch (Throwable t) { | ||
ExceptionUtils.throwAsUncheckedException(t); | ||
} | ||
}); | ||
} | ||
|
||
private static Store.CloseableResource asCloseableResource(/* @Nullable */ Object testInstance, Field field) { | ||
return () -> { | ||
Object toBeClosed = ReflectionUtils.tryToReadFieldValue(field, testInstance).get(); | ||
if (toBeClosed == null) { | ||
logger.warn(() -> | ||
"@AutoClose: Field " + getQualifiedFieldName(field) + " couldn't be closed because it was null."); | ||
return; | ||
} | ||
getAndDestroy(field, toBeClosed); | ||
}; | ||
} | ||
|
||
private static void getAndDestroy(Field field, Object toBeClosed) { | ||
String methodName = field.getAnnotation(AutoClose.class).value(); | ||
Method destroyMethod = ReflectionUtils.findMethod(toBeClosed.getClass(), methodName).orElseThrow( | ||
() -> new ExtensionConfigurationException("@AutoClose: Cannot resolve the destroy method " | ||
+ methodName + "() at " + getQualifiedFieldName(field) + ": " + field.getType().getSimpleName())); | ||
ReflectionUtils.invokeMethod(destroyMethod, toBeClosed); | ||
} | ||
|
||
private static String getQualifiedFieldName(Field field) { | ||
return field.getDeclaringClass().getSimpleName() + "." + field.getName(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters