Skip to content

Commit acb6e65

Browse files
authored
Provide access to source element annotations for TempDirFactory
This allows inspecting the declaration site of `@TempDir` in `TempDirFactory`. Resolves #3390.
1 parent 73818a1 commit acb6e65

File tree

10 files changed

+331
-85
lines changed

10 files changed

+331
-85
lines changed

documentation/src/docs/asciidoc/user-guide/writing-tests.adoc

+5
Original file line numberDiff line numberDiff line change
@@ -2672,6 +2672,11 @@ The following example demonstrates how to use the custom `@JimfsTempDir` annotat
26722672
include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation_usage]
26732673
----
26742674

2675+
Meta-annotations or additional annotations on the field or parameter the `TempDir`
2676+
annotation is declared on might expose additional attributes to configure the factory.
2677+
Such annotations and related attributes can be accessed via the `AnnotatedElementContext`
2678+
parameter of `createTempDirectory`.
2679+
26752680
You can use the `junit.jupiter.tempdir.factory.default`
26762681
<<running-tests-config-params, configuration parameter>> to specify the fully qualified
26772682
class name of the `TempDirFactory` you would like to use by default. Just like for

documentation/src/test/java/example/TempDirectoryDemo.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import example.util.ListWriter;
3333

3434
import org.junit.jupiter.api.Test;
35+
import org.junit.jupiter.api.extension.AnnotatedElementContext;
3536
import org.junit.jupiter.api.extension.ExtensionContext;
3637
import org.junit.jupiter.api.io.TempDir;
3738
import org.junit.jupiter.api.io.TempDirFactory;
@@ -110,8 +111,9 @@ void factoryTest(@TempDir(factory = Factory.class) Path tempDir) {
110111
static class Factory implements TempDirFactory {
111112

112113
@Override
113-
public Path createTempDirectory(ExtensionContext context) throws IOException {
114-
return Files.createTempDirectory(context.getRequiredTestMethod().getName());
114+
public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
115+
throws IOException {
116+
return Files.createTempDirectory(extensionContext.getRequiredTestMethod().getName());
115117
}
116118

117119
}
@@ -133,7 +135,8 @@ static class JimfsTempDirFactory implements TempDirFactory {
133135
private final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
134136

135137
@Override
136-
public Path createTempDirectory(ExtensionContext context) throws IOException {
138+
public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
139+
throws IOException {
137140
return Files.createTempDirectory(fileSystem.getPath("/"), "junit");
138141
}
139142

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2015-2023 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.api.extension;
12+
13+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
14+
15+
import java.lang.annotation.Annotation;
16+
import java.lang.reflect.AnnotatedElement;
17+
import java.util.List;
18+
import java.util.Optional;
19+
20+
import org.apiguardian.api.API;
21+
import org.junit.platform.commons.support.AnnotationSupport;
22+
23+
/**
24+
* {@code AnnotatedElementContext} encapsulates the <em>context</em> in which an
25+
* {@link #getAnnotatedElement() AnnotatedElement} is declared.
26+
*
27+
* <p>For example, an {@code AnnotatedElementContext} is used in
28+
* {@link org.junit.jupiter.api.io.TempDirFactory TempDirFactory} to allow inspecting
29+
* the field or parameter the {@link org.junit.jupiter.api.io.TempDir TempDir}
30+
* annotation is declared on.
31+
*
32+
* <p>This interface is not intended to be implemented by clients.
33+
*
34+
* @since 5.10
35+
*/
36+
@API(status = EXPERIMENTAL, since = "5.10")
37+
public interface AnnotatedElementContext {
38+
39+
/**
40+
* Get the {@link AnnotatedElement} for this context.
41+
*
42+
* <h4>WARNING</h4>
43+
* <p>When searching for annotations on the annotated element in this context,
44+
* favor {@link #isAnnotated(Class)}, {@link #findAnnotation(Class)}, and
45+
* {@link #findRepeatableAnnotations(Class)} over methods in the
46+
* {@link AnnotatedElement} API due to a bug in {@code javac} on JDK versions prior
47+
* to JDK 9.
48+
*
49+
* @return the annotated element; never {@code null}
50+
*/
51+
AnnotatedElement getAnnotatedElement();
52+
53+
/**
54+
* Determine if an annotation of {@code annotationType} is either
55+
* <em>present</em> or <em>meta-present</em> on the {@link AnnotatedElement} for
56+
* this context.
57+
*
58+
* <h4>WARNING</h4>
59+
* <p>Favor the use of this method over directly invoking
60+
* {@link AnnotatedElement#isAnnotationPresent(Class)} due to a bug in {@code javac}
61+
* on JDK versions prior to JDK 9.
62+
*
63+
* @param annotationType the annotation type to search for; never {@code null}
64+
* @return {@code true} if the annotation is present or meta-present
65+
* @see #findAnnotation(Class)
66+
* @see #findRepeatableAnnotations(Class)
67+
*/
68+
default boolean isAnnotated(Class<? extends Annotation> annotationType) {
69+
return AnnotationSupport.isAnnotated(getAnnotatedElement(), annotationType);
70+
}
71+
72+
/**
73+
* Find the first annotation of {@code annotationType} that is either
74+
* <em>present</em> or <em>meta-present</em> on the {@link AnnotatedElement} for
75+
* this context.
76+
*
77+
* <h4>WARNING</h4>
78+
* <p>Favor the use of this method over directly invoking annotation lookup
79+
* methods in the {@link AnnotatedElement} API due to a bug in {@code javac} on JDK
80+
* versions prior to JDK 9.
81+
*
82+
* @param <A> the annotation type
83+
* @param annotationType the annotation type to search for; never {@code null}
84+
* @return an {@code Optional} containing the annotation; never {@code null} but
85+
* potentially empty
86+
* @see #isAnnotated(Class)
87+
* @see #findRepeatableAnnotations(Class)
88+
*/
89+
default <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {
90+
return AnnotationSupport.findAnnotation(getAnnotatedElement(), annotationType);
91+
}
92+
93+
/**
94+
* Find all <em>repeatable</em> {@linkplain Annotation annotations} of
95+
* {@code annotationType} that are either <em>present</em> or
96+
* <em>meta-present</em> on the {@link AnnotatedElement} for this context.
97+
*
98+
* <h4>WARNING</h4>
99+
* <p>Favor the use of this method over directly invoking annotation lookup
100+
* methods in the {@link AnnotatedElement} API due to a bug in {@code javac} on JDK
101+
* versions prior to JDK 9.
102+
*
103+
* @param <A> the annotation type
104+
* @param annotationType the repeatable annotation type to search for; never
105+
* {@code null}
106+
* @return the list of all such annotations found; neither {@code null} nor
107+
* mutable, but potentially empty
108+
* @see #isAnnotated(Class)
109+
* @see #findAnnotation(Class)
110+
* @see java.lang.annotation.Repeatable
111+
*/
112+
default <A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType) {
113+
return AnnotationSupport.findRepeatableAnnotations(getAnnotatedElement(), annotationType);
114+
}
115+
116+
}

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java

+31-49
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010

1111
package org.junit.jupiter.api.extension;
1212

13+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1314
import static org.apiguardian.api.API.Status.STABLE;
1415

1516
import java.lang.annotation.Annotation;
17+
import java.lang.reflect.AnnotatedElement;
1618
import java.lang.reflect.Executable;
1719
import java.lang.reflect.Parameter;
1820
import java.util.List;
@@ -36,7 +38,7 @@
3638
* @see java.lang.reflect.Constructor
3739
*/
3840
@API(status = STABLE, since = "5.0")
39-
public interface ParameterContext {
41+
public interface ParameterContext extends AnnotatedElementContext {
4042

4143
/**
4244
* Get the {@link Parameter} for this context.
@@ -89,63 +91,43 @@ default Executable getDeclaringExecutable() {
8991
Optional<Object> getTarget();
9092

9193
/**
92-
* Determine if an annotation of {@code annotationType} is either
93-
* <em>present</em> or <em>meta-present</em> on the {@link Parameter} for
94-
* this context.
95-
*
96-
* <h4>WARNING</h4>
97-
* <p>Favor the use of this method over directly invoking
98-
* {@link Parameter#isAnnotationPresent(Class)} due to a bug in {@code javac}
99-
* on JDK versions prior to JDK 9.
100-
*
101-
* @param annotationType the annotation type to search for; never {@code null}
102-
* @return {@code true} if the annotation is present or meta-present
94+
* {@inheritDoc}
95+
* @since 5.10
96+
*/
97+
@API(status = EXPERIMENTAL, since = "5.10")
98+
@Override
99+
default AnnotatedElement getAnnotatedElement() {
100+
return getParameter();
101+
}
102+
103+
/**
104+
* {@inheritDoc}
103105
* @since 5.1.1
104-
* @see #findAnnotation(Class)
105-
* @see #findRepeatableAnnotations(Class)
106106
*/
107-
boolean isAnnotated(Class<? extends Annotation> annotationType);
107+
@API(status = STABLE, since = "5.10")
108+
@Override
109+
default boolean isAnnotated(Class<? extends Annotation> annotationType) {
110+
return AnnotatedElementContext.super.isAnnotated(annotationType);
111+
}
108112

109113
/**
110-
* Find the first annotation of {@code annotationType} that is either
111-
* <em>present</em> or <em>meta-present</em> on the {@link Parameter} for
112-
* this context.
113-
*
114-
* <h4>WARNING</h4>
115-
* <p>Favor the use of this method over directly invoking annotation lookup
116-
* methods in the {@link Parameter} API due to a bug in {@code javac} on JDK
117-
* versions prior to JDK 9.
118-
*
119-
* @param <A> the annotation type
120-
* @param annotationType the annotation type to search for; never {@code null}
121-
* @return an {@code Optional} containing the annotation; never {@code null} but
122-
* potentially empty
114+
* {@inheritDoc}
123115
* @since 5.1.1
124-
* @see #isAnnotated(Class)
125-
* @see #findRepeatableAnnotations(Class)
126116
*/
127-
<A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType);
117+
@API(status = STABLE, since = "5.10")
118+
@Override
119+
default <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {
120+
return AnnotatedElementContext.super.findAnnotation(annotationType);
121+
}
128122

129123
/**
130-
* Find all <em>repeatable</em> {@linkplain Annotation annotations} of
131-
* {@code annotationType} that are either <em>present</em> or
132-
* <em>meta-present</em> on the {@link Parameter} for this context.
133-
*
134-
* <h4>WARNING</h4>
135-
* <p>Favor the use of this method over directly invoking annotation lookup
136-
* methods in the {@link Parameter} API due to a bug in {@code javac} on JDK
137-
* versions prior to JDK 9.
138-
*
139-
* @param <A> the annotation type
140-
* @param annotationType the repeatable annotation type to search for; never
141-
* {@code null}
142-
* @return the list of all such annotations found; neither {@code null} nor
143-
* mutable, but potentially empty
124+
* {@inheritDoc}
144125
* @since 5.1.1
145-
* @see #isAnnotated(Class)
146-
* @see #findAnnotation(Class)
147-
* @see java.lang.annotation.Repeatable
148126
*/
149-
<A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType);
127+
@API(status = STABLE, since = "5.10")
128+
@Override
129+
default <A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType) {
130+
return AnnotatedElementContext.super.findRepeatableAnnotations(annotationType);
131+
}
150132

151133
}

junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirFactory.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.nio.file.Path;
1919

2020
import org.apiguardian.api.API;
21+
import org.junit.jupiter.api.extension.AnnotatedElementContext;
2122
import org.junit.jupiter.api.extension.ExtensionContext;
2223

2324
/**
@@ -30,8 +31,9 @@
3031
*
3132
* <p>Implementations must provide a no-args constructor and should not make any
3233
* assumptions regarding when and how many times they are instantiated, but they
33-
* can assume that {@link #createTempDirectory(ExtensionContext)} and {@link #close()}
34-
* will both be called once per instance, in this order, and from the same thread.
34+
* can assume that {@link #createTempDirectory(AnnotatedElementContext, ExtensionContext)}
35+
* and {@link #close()} will both be called once per instance, in this order,
36+
* and from the same thread.
3537
*
3638
* <p>A {@link TempDirFactory} can be configured <em>globally</em> for the
3739
* entire test suite via the {@value TempDir#DEFAULT_FACTORY_PROPERTY_NAME}
@@ -53,11 +55,14 @@ public interface TempDirFactory extends Closeable {
5355
* not be associated with the {@link java.nio.file.FileSystems#getDefault()
5456
* default FileSystem}.
5557
*
56-
* @param context the current extension context; never {@code null}
58+
* @param elementContext the context of the field or parameter where
59+
* {@code @TempDir} is declared; never {@code null}
60+
* @param extensionContext the current extension context; never {@code null}
5761
* @return the path to the newly created temporary directory; never {@code null}
5862
* @throws Exception in case of failures
5963
*/
60-
Path createTempDirectory(ExtensionContext context) throws Exception;
64+
Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
65+
throws Exception;
6166

6267
/**
6368
* {@inheritDoc}
@@ -82,7 +87,8 @@ public Standard() {
8287
}
8388

8489
@Override
85-
public Path createTempDirectory(ExtensionContext context) throws IOException {
90+
public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
91+
throws IOException {
8692
return Files.createTempDirectory(TEMP_DIR_PREFIX);
8793
}
8894

0 commit comments

Comments
 (0)