Skip to content

Commit 7215199

Browse files
committed
Annotate nullability in org.junit.jupiter.engine.descriptor
1 parent 76b01bb commit 7215199

19 files changed

+136
-67
lines changed

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Set;
2525
import java.util.function.Function;
2626

27+
import org.jspecify.annotations.Nullable;
2728
import org.junit.jupiter.api.extension.ExecutableInvoker;
2829
import org.junit.jupiter.api.extension.Extension;
2930
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -54,6 +55,7 @@ abstract class AbstractExtensionContext<T extends TestDescriptor> implements Ext
5455

5556
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExtensionContext.class);
5657

58+
@Nullable
5759
private final ExtensionContext parent;
5860
private final EngineExecutionListener engineExecutionListener;
5961
private final T testDescriptor;
@@ -64,8 +66,8 @@ abstract class AbstractExtensionContext<T extends TestDescriptor> implements Ext
6466
private final LauncherStoreFacade launcherStoreFacade;
6567
private final NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> valuesStore;
6668

67-
AbstractExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener, T testDescriptor,
68-
JupiterConfiguration configuration, ExtensionRegistry extensionRegistry,
69+
AbstractExtensionContext(@Nullable ExtensionContext parent, EngineExecutionListener engineExecutionListener,
70+
T testDescriptor, JupiterConfiguration configuration, ExtensionRegistry extensionRegistry,
6971
LauncherStoreFacade launcherStoreFacade) {
7072

7173
Preconditions.notNull(testDescriptor, "TestDescriptor must not be null");
@@ -109,7 +111,7 @@ private NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.suppor
109111
}
110112

111113
private static NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
112-
ExtensionContext parent, LauncherStoreFacade launcherStoreFacade,
114+
@Nullable ExtensionContext parent, LauncherStoreFacade launcherStoreFacade,
113115
NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> closeAction) {
114116
NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> parentStore;
115117
if (parent == null) {
@@ -172,7 +174,8 @@ private void publishFileEntry(String name, ThrowingConsumer<Path> action,
172174
Function<Path, FileEntry> fileEntryCreator) {
173175
Path dir = createOutputDirectory();
174176
Path path = dir.resolve(name);
175-
Preconditions.condition(path.getParent().equals(dir), () -> "name must not contain path separators: " + name);
177+
Preconditions.condition(path.getParent() != null && path.getParent().equals(dir),
178+
() -> "name must not contain path separators: " + name);
176179
try {
177180
action.accept(path);
178181
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13+
import static java.util.Objects.requireNonNull;
1314
import static java.util.stream.Collectors.joining;
1415
import static org.apiguardian.api.API.Status.INTERNAL;
1516
import static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeAfterCallbacks;
@@ -37,6 +38,7 @@
3738
import java.util.function.Supplier;
3839

3940
import org.apiguardian.api.API;
41+
import org.jspecify.annotations.Nullable;
4042
import org.junit.jupiter.api.TestInstance.Lifecycle;
4143
import org.junit.jupiter.api.extension.AfterAllCallback;
4244
import org.junit.jupiter.api.extension.BeforeAllCallback;
@@ -92,7 +94,10 @@ public abstract class ClassBasedTestDescriptor extends JupiterTestDescriptor
9294

9395
protected final ClassInfo classInfo;
9496

97+
@Nullable
9598
private LifecycleMethods lifecycleMethods;
99+
100+
@Nullable
96101
private TestInstanceFactory testInstanceFactory;
97102

98103
ClassBasedTestDescriptor(UniqueId uniqueId, Class<?> testClass, Supplier<String> displayNameSupplier,
@@ -148,7 +153,7 @@ private void validateDisplayNameAnnotation(DiscoveryIssueReporter reporter) {
148153
}
149154

150155
protected void validateCoreLifecycleMethods(DiscoveryIssueReporter reporter) {
151-
Validatable.reportAndClear(this.lifecycleMethods.discoveryIssues, reporter);
156+
Validatable.reportAndClear(requireLifecycleMethods().discoveryIssues, reporter);
152157
}
153158

154159
protected void validateClassTemplateInvocationLifecycleMethods(DiscoveryIssueReporter reporter) {
@@ -198,15 +203,17 @@ public final JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext
198203
registerExtensionsFromConstructorParameters(registry, getTestClass());
199204
}
200205

201-
this.lifecycleMethods.beforeAll.forEach(method -> registerExtensionsFromExecutableParameters(registry, method));
206+
requireLifecycleMethods().beforeAll.forEach(
207+
method -> registerExtensionsFromExecutableParameters(registry, method));
202208
// Since registerBeforeEachMethodAdapters() and registerAfterEachMethodAdapters() also
203209
// invoke registerExtensionsFromExecutableParameters(), we invoke those methods before
204210
// invoking registerExtensionsFromExecutableParameters() for @AfterAll methods,
205211
// thereby ensuring proper registration order for extensions registered via @ExtendWith
206212
// on parameters in lifecycle methods.
207213
registerBeforeEachMethodAdapters(registry);
208214
registerAfterEachMethodAdapters(registry);
209-
this.lifecycleMethods.afterAll.forEach(method -> registerExtensionsFromExecutableParameters(registry, method));
215+
requireLifecycleMethods().afterAll.forEach(
216+
method -> registerExtensionsFromExecutableParameters(registry, method));
210217
registerExtensionsFromInstanceFields(registry, getTestClass());
211218

212219
ThrowableCollector throwableCollector = createThrowableCollector();
@@ -288,6 +295,7 @@ public void cleanUp(JupiterEngineExecutionContext context) throws Exception {
288295
this.testInstanceFactory = null;
289296
}
290297

298+
@Nullable
291299
private TestInstanceFactory resolveTestInstanceFactory(ExtensionRegistry registry) {
292300
List<TestInstanceFactory> factories = registry.getExtensions(TestInstanceFactory.class);
293301

@@ -347,19 +355,19 @@ protected final TestInstances instantiateTestClass(Optional<TestInstances> outer
347355
invokeTestInstancePreConstructCallbacks(new DefaultTestInstanceFactoryContext(getTestClass(), outerInstance),
348356
registry, extensionContext);
349357
Object instance = this.testInstanceFactory != null //
350-
? invokeTestInstanceFactory(outerInstance, extensionContext) //
358+
? invokeTestInstanceFactory(this.testInstanceFactory, outerInstance, extensionContext) //
351359
: invokeTestClassConstructor(outerInstance, registry, extensionContext);
352360
return outerInstances.map(instances -> DefaultTestInstances.of(instances, instance)) //
353361
.orElse(DefaultTestInstances.of(instance));
354362
}
355363

356-
private Object invokeTestInstanceFactory(Optional<Object> outerInstance,
364+
private Object invokeTestInstanceFactory(TestInstanceFactory testInstanceFactory, Optional<Object> outerInstance,
357365
ExtensionContextSupplier extensionContext) {
358366
Object instance;
359367

360368
try {
361-
ExtensionContext actualExtensionContext = extensionContext.get(this.testInstanceFactory);
362-
instance = this.testInstanceFactory.createTestInstance(
369+
ExtensionContext actualExtensionContext = extensionContext.get(testInstanceFactory);
370+
instance = testInstanceFactory.createTestInstance(
363371
new DefaultTestInstanceFactoryContext(getTestClass(), outerInstance), actualExtensionContext);
364372
}
365373
catch (Throwable throwable) {
@@ -370,7 +378,7 @@ private Object invokeTestInstanceFactory(Optional<Object> outerInstance,
370378
}
371379

372380
String message = "TestInstanceFactory [%s] failed to instantiate test class [%s]".formatted(
373-
this.testInstanceFactory.getClass().getName(), getTestClass().getName());
381+
testInstanceFactory.getClass().getName(), getTestClass().getName());
374382
if (StringUtils.isNotBlank(throwable.getMessage())) {
375383
message += ": " + throwable.getMessage();
376384
}
@@ -390,7 +398,7 @@ private Object invokeTestInstanceFactory(Optional<Object> outerInstance,
390398
instanceClassName += "@" + Integer.toHexString(System.identityHashCode(instanceClass));
391399
}
392400
String message = "TestInstanceFactory [%s] failed to return an instance of [%s] and instead returned an instance of [%s].".formatted(
393-
this.testInstanceFactory.getClass().getName(), testClassName, instanceClassName);
401+
testInstanceFactory.getClass().getName(), testClassName, instanceClassName);
394402

395403
throw new TestInstantiationException(message);
396404
}
@@ -438,7 +446,7 @@ private void invokeBeforeAllMethods(JupiterEngineExecutionContext context) {
438446
ThrowableCollector throwableCollector = context.getThrowableCollector();
439447
Object testInstance = extensionContext.getTestInstance().orElse(null);
440448

441-
for (Method method : this.lifecycleMethods.beforeAll) {
449+
for (Method method : requireLifecycleMethods().beforeAll) {
442450
throwableCollector.execute(() -> {
443451
try {
444452
executableInvoker.invoke(method, testInstance, extensionContext, registry,
@@ -467,7 +475,7 @@ private void invokeAfterAllMethods(JupiterEngineExecutionContext context) {
467475
ThrowableCollector throwableCollector = context.getThrowableCollector();
468476
Object testInstance = extensionContext.getTestInstance().orElse(null);
469477

470-
this.lifecycleMethods.afterAll.forEach(method -> throwableCollector.execute(() -> {
478+
requireLifecycleMethods().afterAll.forEach(method -> throwableCollector.execute(() -> {
471479
try {
472480
executableInvoker.invoke(method, testInstance, extensionContext, registry,
473481
ReflectiveInterceptorCall.ofVoidMethod(InvocationInterceptor::interceptAfterAllMethod));
@@ -500,13 +508,13 @@ private boolean isPerClassLifecycle(JupiterEngineExecutionContext context) {
500508
}
501509

502510
private void registerBeforeEachMethodAdapters(ExtensionRegistrar registrar) {
503-
registerMethodsAsExtensions(this.lifecycleMethods.beforeEach, registrar,
511+
registerMethodsAsExtensions(requireLifecycleMethods().beforeEach, registrar,
504512
this::synthesizeBeforeEachMethodAdapter);
505513
}
506514

507515
private void registerAfterEachMethodAdapters(ExtensionRegistrar registrar) {
508516
// Make a local copy since findAfterEachMethods() returns an immutable list.
509-
List<Method> afterEachMethods = new ArrayList<>(this.lifecycleMethods.afterEach);
517+
List<Method> afterEachMethods = new ArrayList<>(requireLifecycleMethods().afterEach);
510518

511519
// Since the bottom-up ordering of afterEachMethods will later be reversed when the
512520
// synthesized AfterEachMethodAdapters are executed within TestMethodTestDescriptor,
@@ -546,14 +554,21 @@ private void invokeMethodInExtensionContext(Method method, ExtensionContext cont
546554
ReflectiveInterceptorCall.ofVoidMethod(interceptorCall));
547555
}
548556

557+
private LifecycleMethods requireLifecycleMethods() {
558+
return requireNonNull(this.lifecycleMethods);
559+
}
560+
549561
protected static class ClassInfo {
550562

551563
private final List<DiscoveryIssue> discoveryIssues = new ArrayList<>();
552564

553565
final Class<?> testClass;
554566
final Set<TestTag> tags;
555567
final Lifecycle lifecycle;
568+
569+
@Nullable
556570
ExecutionMode defaultChildExecutionMode;
571+
557572
final ExclusiveResourceCollector exclusiveResourceCollector;
558573

559574
ClassInfo(Class<?> testClass, JupiterConfiguration configuration) {

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassExtensionContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.List;
1616
import java.util.Optional;
1717

18+
import org.jspecify.annotations.Nullable;
1819
import org.junit.jupiter.api.TestInstance.Lifecycle;
1920
import org.junit.jupiter.api.extension.ExtensionContext;
2021
import org.junit.jupiter.api.extension.TestInstances;
@@ -33,6 +34,7 @@ final class ClassExtensionContext extends AbstractExtensionContext<ClassBasedTes
3334

3435
private final ThrowableCollector throwableCollector;
3536

37+
@Nullable
3638
private TestInstances testInstances;
3739

3840
ClassExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationTestDescriptor.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13+
import static java.util.Objects.requireNonNull;
1314
import static org.apiguardian.api.API.Status.INTERNAL;
1415
import static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeAfterCallbacks;
1516
import static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeBeforeCallbacks;
@@ -23,6 +24,7 @@
2324
import java.util.stream.Stream;
2425

2526
import org.apiguardian.api.API;
27+
import org.jspecify.annotations.Nullable;
2628
import org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback;
2729
import org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;
2830
import org.junit.jupiter.api.extension.ClassTemplateInvocationContext;
@@ -46,11 +48,14 @@ public class ClassTemplateInvocationTestDescriptor extends JupiterTestDescriptor
4648
public static final String SEGMENT_TYPE = "class-template-invocation";
4749

4850
private final ClassTemplateTestDescriptor parent;
51+
52+
@Nullable
4953
private ClassTemplateInvocationContext invocationContext;
54+
5055
private final int index;
5156

5257
public ClassTemplateInvocationTestDescriptor(UniqueId uniqueId, ClassTemplateTestDescriptor parent,
53-
ClassTemplateInvocationContext invocationContext, int index, TestSource source,
58+
ClassTemplateInvocationContext invocationContext, int index, @Nullable TestSource source,
5459
JupiterConfiguration configuration) {
5560
super(uniqueId, invocationContext.getDisplayName(index), source, configuration);
5661
this.parent = parent;
@@ -67,7 +72,7 @@ public int getIndex() {
6772
@Override
6873
protected ClassTemplateInvocationTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {
6974
return new ClassTemplateInvocationTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), parent,
70-
this.invocationContext, this.index, getSource().orElse(null), this.configuration);
75+
requiredInvocationContext(), this.index, getSource().orElse(null), this.configuration);
7176
}
7277

7378
// --- TestDescriptor ------------------------------------------------------
@@ -111,18 +116,18 @@ public Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResou
111116
@Override
112117
public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {
113118
MutableExtensionRegistry registry = context.getExtensionRegistry();
114-
List<Extension> additionalExtensions = this.invocationContext.getAdditionalExtensions();
119+
List<Extension> additionalExtensions = requiredInvocationContext().getAdditionalExtensions();
115120
if (!additionalExtensions.isEmpty()) {
116121
MutableExtensionRegistry childRegistry = createRegistryFrom(registry, Stream.empty());
117122
additionalExtensions.forEach(
118-
extension -> childRegistry.registerExtension(extension, this.invocationContext));
123+
extension -> childRegistry.registerExtension(extension, requiredInvocationContext()));
119124
registry = childRegistry;
120125
}
121126
ExtensionContext extensionContext = new ClassTemplateInvocationExtensionContext(context.getExtensionContext(),
122127
context.getExecutionListener(), this, context.getConfiguration(), registry,
123128
context.getLauncherStoreFacade());
124129
ThrowableCollector throwableCollector = createThrowableCollector();
125-
throwableCollector.execute(() -> this.invocationContext.prepareInvocation(extensionContext));
130+
throwableCollector.execute(() -> requiredInvocationContext().prepareInvocation(extensionContext));
126131
return context.extend() //
127132
.withExtensionRegistry(registry) //
128133
.withExtensionContext(extensionContext) //
@@ -177,4 +182,8 @@ public void cleanUp(JupiterEngineExecutionContext context) throws Exception {
177182
this.invocationContext = null;
178183
super.cleanUp(context);
179184
}
185+
186+
private ClassTemplateInvocationContext requiredInvocationContext() {
187+
return requireNonNull(this.invocationContext);
188+
}
180189
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DisplayNameUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.function.BiFunction;
2323
import java.util.function.Supplier;
2424

25+
import org.jspecify.annotations.Nullable;
2526
import org.junit.jupiter.api.DisplayName;
2627
import org.junit.jupiter.api.DisplayNameGeneration;
2728
import org.junit.jupiter.api.DisplayNameGenerator;
@@ -82,7 +83,7 @@ static String determineDisplayName(AnnotatedElement element, Supplier<String> di
8283
}
8384

8485
static void validateAnnotation(AnnotatedElement element, Supplier<String> elementDescription,
85-
Supplier<TestSource> sourceProvider, DiscoveryIssueReporter reporter) {
86+
Supplier<@Nullable TestSource> sourceProvider, DiscoveryIssueReporter reporter) {
8687
findAnnotation(element, DisplayName.class) //
8788
.map(DisplayName::value) //
8889
.filter(StringUtils::isBlank) //

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13+
import org.jspecify.annotations.Nullable;
1314
import org.junit.jupiter.api.DynamicNode;
1415
import org.junit.jupiter.api.extension.ExtensionContext;
1516
import org.junit.jupiter.engine.config.JupiterConfiguration;
@@ -27,7 +28,7 @@ abstract class DynamicNodeTestDescriptor extends JupiterTestDescriptor {
2728

2829
protected final int index;
2930

30-
DynamicNodeTestDescriptor(UniqueId uniqueId, int index, DynamicNode dynamicNode, TestSource testSource,
31+
DynamicNodeTestDescriptor(UniqueId uniqueId, int index, DynamicNode dynamicNode, @Nullable TestSource testSource,
3132
JupiterConfiguration configuration) {
3233
super(uniqueId, dynamicNode.getDisplayName(), testSource, configuration);
3334
this.index = index;

0 commit comments

Comments
 (0)