Skip to content

Commit 503f858

Browse files
committed
Add BeforeAll
1 parent e8820eb commit 503f858

File tree

4 files changed

+77
-61
lines changed

4 files changed

+77
-61
lines changed

junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/DefaultTimeZone.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.lang.annotation.Target;
1818

1919
import org.apiguardian.api.API;
20+
import org.junit.jupiter.api.extension.ExtendWith;
2021

2122
/**
2223
* {@code @DefaultTimeZone} is a JUnit Jupiter extension to change the value
@@ -52,6 +53,7 @@
5253
@Inherited
5354
@WritesDefaultTimeZone
5455
@API(status = API.Status.STABLE, since = "6.1")
56+
@ExtendWith(DefaultTimeZoneExtension.class)
5557
public @interface DefaultTimeZone {
5658

5759
/**

junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/DefaultTimeZoneExtension.java

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,71 @@
1010

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

13-
import java.lang.reflect.AnnotatedElement;
1413
import java.util.Optional;
1514
import java.util.TimeZone;
1615

1716
import org.junit.jupiter.api.extension.AfterEachCallback;
17+
import org.junit.jupiter.api.extension.BeforeAllCallback;
1818
import org.junit.jupiter.api.extension.BeforeEachCallback;
1919
import org.junit.jupiter.api.extension.ExtensionConfigurationException;
2020
import org.junit.jupiter.api.extension.ExtensionContext;
2121
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
22-
import org.junit.jupiter.api.extension.ExtensionContext.Store;
2322
import org.junit.jupiter.api.util.TimeZoneProvider.NullTimeZoneProvider;
2423
import org.junit.platform.commons.support.AnnotationSupport;
2524
import org.junit.platform.commons.support.ReflectionSupport;
2625

27-
class DefaultTimeZoneExtension implements BeforeEachCallback, AfterEachCallback {
26+
class DefaultTimeZoneExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
2827

2928
private static final Namespace NAMESPACE = Namespace.create(DefaultTimeZoneExtension.class);
3029

31-
private static final String KEY = "DefaultTimeZone";
30+
private static final String CUSTOM_KEY = "CustomTimeZone";
31+
private static final String DEFAULT_KEY = "DefaultTimeZone";
32+
33+
@Override
34+
public void beforeAll(ExtensionContext context) throws Exception {
35+
createTimeZoneFromAnnotation(context) //
36+
.ifPresent(timeZone -> store(context, CUSTOM_KEY, timeZone));
37+
}
3238

3339
@Override
3440
public void beforeEach(ExtensionContext context) {
35-
AnnotatedElement element = context.getElement().orElse(null);
36-
AnnotationSupport.findAnnotation(element, DefaultTimeZone.class).ifPresent(
37-
annotation -> setDefaultTimeZone(context.getStore(NAMESPACE), annotation));
41+
createTimeZoneFromAnnotation(context) //
42+
.or(() -> load(context, CUSTOM_KEY)) //
43+
.ifPresent(timeZone -> setDefaultTimeZone(context, timeZone));
44+
}
45+
46+
private void setDefaultTimeZone(ExtensionContext context, TimeZone customTimeZone) {
47+
store(context, DEFAULT_KEY, TimeZone.getDefault());
48+
TimeZone.setDefault(customTimeZone);
3849
}
3950

40-
private void setDefaultTimeZone(Store store, DefaultTimeZone annotation) {
51+
private static Optional<TimeZone> createTimeZoneFromAnnotation(ExtensionContext context) {
52+
return AnnotationSupport.findAnnotation(context.getElement(), DefaultTimeZone.class) //
53+
.map(DefaultTimeZoneExtension::createTimeZone);
54+
}
55+
56+
private static TimeZone createTimeZone(DefaultTimeZone annotation) {
4157
validateCorrectConfiguration(annotation);
42-
TimeZone defaultTimeZone;
43-
if (annotation.timeZoneProvider() != NullTimeZoneProvider.class)
44-
defaultTimeZone = createTimeZone(annotation.timeZoneProvider());
45-
else
46-
defaultTimeZone = createTimeZone(annotation.value());
47-
// defer storing the current default time zone until the new time zone could be created from the configuration
48-
// (this prevents cases where misconfigured extensions store default time zone now and restore it later,
49-
// which leads to race conditions in our tests)
50-
storeDefaultTimeZone(store);
51-
TimeZone.setDefault(defaultTimeZone);
58+
59+
if (!annotation.value().isEmpty()) {
60+
return createTimeZoneFromZoneId(annotation.value());
61+
}
62+
else {
63+
return createTimeZoneFromProvider(annotation.timeZoneProvider());
64+
}
5265
}
5366

5467
private static void validateCorrectConfiguration(DefaultTimeZone annotation) {
5568
boolean noValue = annotation.value().isEmpty();
5669
boolean noProvider = annotation.timeZoneProvider() == NullTimeZoneProvider.class;
57-
if (noValue == noProvider)
70+
if (noValue == noProvider) {
5871
throw new ExtensionConfigurationException(
59-
"Either a valid time zone id or a TimeZoneProvider must be provided to " + DefaultTimeZone.class.getSimpleName());
72+
"Either a valid time zone id or a TimeZoneProvider must be provided to "
73+
+ DefaultTimeZone.class.getSimpleName());
74+
}
6075
}
6176

62-
private static TimeZone createTimeZone(String timeZoneId) {
77+
private static TimeZone createTimeZoneFromZoneId(String timeZoneId) {
6378
TimeZone configuredTimeZone = TimeZone.getTimeZone(timeZoneId);
6479
// TimeZone::getTimeZone returns with GMT as fallback if the given ID cannot be understood
6580
if (configuredTimeZone.equals(TimeZone.getTimeZone("GMT")) && !"GMT".equals(timeZoneId)) {
@@ -72,33 +87,32 @@ private static TimeZone createTimeZone(String timeZoneId) {
7287
return configuredTimeZone;
7388
}
7489

75-
private static TimeZone createTimeZone(Class<? extends TimeZoneProvider> providerClass) {
90+
private static TimeZone createTimeZoneFromProvider(Class<? extends TimeZoneProvider> providerClass) {
7691
try {
7792
TimeZoneProvider provider = ReflectionSupport.newInstance(providerClass);
7893
return Optional.ofNullable(provider.get()).orElse(TimeZone.getTimeZone("GMT"));
7994
}
8095
catch (Exception exception) {
8196
throw new ExtensionConfigurationException("Could not instantiate TimeZoneProvider because of exception",
82-
exception);
97+
exception);
8398
}
8499
}
85100

86-
private void storeDefaultTimeZone(Store store) {
87-
store.put(KEY, TimeZone.getDefault());
88-
}
89-
90101
@Override
91102
public void afterEach(ExtensionContext context) {
92-
AnnotatedElement element = context.getElement().orElse(null);
93-
AnnotationSupport.findAnnotation(element, DefaultTimeZone.class).ifPresent(
94-
__ -> resetDefaultTimeZone(context.getStore(NAMESPACE)));
103+
load(context, DEFAULT_KEY).ifPresent(TimeZone::setDefault);
104+
}
105+
106+
private static void store(ExtensionContext context, String key, TimeZone value) {
107+
getStore(context).put(key, value);
108+
}
109+
110+
private static Optional<TimeZone> load(ExtensionContext context, String key) {
111+
return Optional.ofNullable(getStore(context).get(key, TimeZone.class));
95112
}
96113

97-
private void resetDefaultTimeZone(Store store) {
98-
TimeZone timeZone = store.get(KEY, TimeZone.class);
99-
// default time zone is null if the extension was misconfigured and execution failed in "before"
100-
if (timeZone != null)
101-
TimeZone.setDefault(timeZone);
114+
private static ExtensionContext.Store getStore(ExtensionContext context) {
115+
return context.getStore(NAMESPACE);
102116
}
103117

104118
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ public class MutableExtensionRegistry implements ExtensionRegistry, ExtensionReg
5151
private static final Logger logger = LoggerFactory.getLogger(MutableExtensionRegistry.class);
5252

5353
private static final List<Extension> DEFAULT_STATELESS_EXTENSIONS = List.of( //
54-
new DisabledCondition(), //
55-
new AutoCloseExtension(), //
56-
new TimeoutExtension(), //
57-
new RepeatedTestExtension(), //
58-
new TestInfoParameterResolver(), //
59-
new TestReporterParameterResolver() //
54+
new DisabledCondition(), //
55+
new AutoCloseExtension(), //
56+
new TimeoutExtension(), //
57+
new RepeatedTestExtension(), //
58+
new TestInfoParameterResolver(), //
59+
new TestReporterParameterResolver() //
6060
);
6161

6262
/**
@@ -102,14 +102,14 @@ private static void registerAutoDetectedExtensions(MutableExtensionRegistry exte
102102
List<Class<? extends Extension>> excludedExtensions = new ArrayList<>();
103103

104104
ServiceLoader<Extension> serviceLoader = ServiceLoader.load(Extension.class,
105-
ClassLoaderUtils.getDefaultClassLoader());
105+
ClassLoaderUtils.getDefaultClassLoader());
106106
ServiceLoaderUtils.filter(serviceLoader, clazz -> {
107-
boolean included = filter.test(clazz);
108-
if (!included) {
109-
excludedExtensions.add(clazz);
110-
}
111-
return included;
112-
}) //
107+
boolean included = filter.test(clazz);
108+
if (!included) {
109+
excludedExtensions.add(clazz);
110+
}
111+
return included;
112+
}) //
113113
.forEach(extensionRegistry::registerAutoDetectedExtension);
114114

115115
logExcludedExtensions(excludedExtensions);
@@ -124,7 +124,7 @@ private static void logExcludedExtensions(List<Class<? extends Extension>> exclu
124124
.toList();
125125
// @formatter:on
126126
logger.config(() -> "Excluded auto-detected extensions due to configured includes/excludes: %s".formatted(
127-
excludeExtensionNames));
127+
excludeExtensionNames));
128128
}
129129
}
130130

@@ -217,7 +217,7 @@ public void registerUninitializedExtension(Class<?> testClass, Field source,
217217
Preconditions.notNull(initializer, "initializer must not be null");
218218

219219
logger.trace(() -> "Registering local extension (late-init) for [%s]%s".formatted(source.getType().getName(),
220-
buildSourceInfo(source)));
220+
buildSourceInfo(source)));
221221

222222
LateInitEntry entry = getLateInitExtensions(testClass) //
223223
.add(new LateInitEntry(testClass, initializer));

jupiter-tests/src/test/java/org/junit/jupiter/api/util/DefaultTimeZoneTests.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,10 @@ class ConfigurationTests {
206206
@DisplayName("on method level, throws exception")
207207
void throwsWhenConfigurationIsBad() {
208208
ExecutionResults results = executeTestMethod(BadMethodLevelConfigurationTestCases.class,
209-
"badConfiguration");
209+
"badConfiguration");
210210
results.testEvents().assertThatEvents().haveAtMost(1,
211-
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
212-
message(it -> it.contains("@DefaultTimeZone not configured correctly."))));
211+
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
212+
message(it -> it.contains("@DefaultTimeZone not configured correctly."))));
213213
}
214214

215215
@Test
@@ -219,8 +219,8 @@ void shouldThrowWithBadConfiguration() {
219219
ExecutionResults results = executeTestClass(BadClassLevelConfigurationTestCases.class);
220220

221221
results.testEvents().assertThatEvents().haveAtMost(1,
222-
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
223-
message(it -> it.contains("@DefaultTimeZone not configured correctly."))));
222+
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
223+
message(it -> it.contains("@DefaultTimeZone not configured correctly."))));
224224
}
225225

226226
@AfterEach
@@ -290,8 +290,8 @@ void throwsForMutuallyExclusiveOptions() {
290290
ExecutionResults results = executeTestMethod(BadTimeZoneProviderTestCases.class, "notExclusive");
291291

292292
results.testEvents().assertThatEvents().haveAtMost(1,
293-
finishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(it -> it.contains(
294-
"Either a valid time zone id or a TimeZoneProvider must be provided"))));
293+
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
294+
message(it -> it.contains("Either a valid time zone id or a TimeZoneProvider must be provided"))));
295295
}
296296

297297
@Test
@@ -301,8 +301,8 @@ void throwsForEmptyOptions() {
301301
ExecutionResults results = executeTestMethod(BadTimeZoneProviderTestCases.class, "empty");
302302

303303
results.testEvents().assertThatEvents().haveAtMost(1,
304-
finishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(it -> it.contains(
305-
"Either a valid time zone id or a TimeZoneProvider must be provided"))));
304+
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
305+
message(it -> it.contains("Either a valid time zone id or a TimeZoneProvider must be provided"))));
306306
}
307307

308308
@Test
@@ -312,8 +312,8 @@ void throwsForBadConstructor() {
312312
ExecutionResults results = executeTestMethod(BadTimeZoneProviderTestCases.class, "noConstructor");
313313

314314
results.testEvents().assertThatEvents().haveAtMost(1,
315-
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
316-
message(it -> it.contains("Could not instantiate TimeZoneProvider because of exception"))));
315+
finishedWithFailure(instanceOf(ExtensionConfigurationException.class),
316+
message(it -> it.contains("Could not instantiate TimeZoneProvider because of exception"))));
317317
}
318318

319319
}

0 commit comments

Comments
 (0)