Skip to content

Commit

Permalink
Fixed some issues with nested classes
Browse files Browse the repository at this point in the history
Rewrote the logic in Extension_Mats wrt. nested tests. Test context have
(possibly) parents, and this can be used to resolve the root test
context, and the test context where the Extension_Mats is registered.

This change instead always walks up the context tree to find the
extension, or determine that the extension is not registered at any
level above, and thus the current context is the root Mats context.

Also changed the Extension_MatsRegistration to not trigger when the
annotation is not present, or when there is already an Extension_Mats
present in the test context hierarchy.

Finally, changed AbstractMatsAnnotatedClass to only skip fields that are
an instance of MatsFactory, MatsFuturizer fields do not cause issues.
  • Loading branch information
staale committed Feb 6, 2025
1 parent 5036830 commit 32eb24e
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.mats3.test.jupiter;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import javax.sql.DataSource;

import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
Expand Down Expand Up @@ -111,6 +113,9 @@ public static Extension_Mats createWithDb(MatsSerializer<?> matsSerializer) {
* <p>
* Note that if you crate multiple {@link Extension_Mats}, then this will only provide the last created extension.
* In that case, you should instead provide the actual MatsFactory to each extension.
* <p>
* In a scenario with nested classes, we only register the Extension_Mats in the top level class, where
* the extension has been added with {@link org.junit.jupiter.api.extension.RegisterExtension}.
*
* @param extensionContext
* to get {@link Extension_Mats} from
Expand All @@ -122,48 +127,67 @@ public static Extension_Mats getExtension(ExtensionContext extensionContext) {
Extension_Mats extensionMats = extensionContext.getStore(NAMESPACE).get(Extension_Mats.class,
Extension_Mats.class);
if (extensionMats == null) {
// If we have a parent context, we should get the Extension_Mats from there
if (extensionContext.getParent().isPresent()) {
return getExtension(extensionContext.getParent().get());
}
throw new IllegalStateException("Could not find Extension_Mats in ExtensionContext,"
+ " make sure to include Extension_Mats as a test extension.");
}
return extensionMats;
}

/**
* Checks if the {@link Extension_Mats} is registered in the test context already.
*
* @param context current test context
* @return true if the {@link Extension_Mats} is registered, false otherwise
*/
public static boolean isExtensionRegistered(ExtensionContext context) {
// Check if the Extension_Mats is registered in the current context or any parent context
return context.getStore(NAMESPACE).get(Extension_Mats.class, Extension_Mats.class) != null
|| context.getParent().filter(Extension_Mats::isExtensionRegistered).isPresent();
}

/**
* Executed by Jupiter before any test method is executed. (Once at the start of the class.)
*/
@Override
public void beforeAll(ExtensionContext context) {
context.getStore(NAMESPACE).put(Extension_Mats.class, this);
// Handle the "nesting level'ing" of the beforeAll/afterAll
int nestingLevel = _nestinglevel.getAndIncrement();
// ?: Are we at the top level?
if (nestingLevel != 0) {
// -> No not at top, so then we ignore the beforeAll
log.debug("+++ Jupiter +++ beforeAll(..) invoked, but ignoring since nesting level is > 0: "
+ nestingLevel);
}
else {
// -> Yes, we are at the top level, so then we do the beforeAll
super.beforeAll();
Optional<Extension_Mats> parentExtension = context.getParent()
.map(parentContext -> parentContext.getStore(NAMESPACE))
.map(store -> store.get(Extension_Mats.class, Extension_Mats.class));
Store contextStore = context.getStore(NAMESPACE);

// Are we already inside a Extension_Mats?
if (parentExtension.isPresent()) {
log.debug("+++ Jupiter +++ beforeAll(..) invoked, but ignoring since we are already registered");
// Also add ourselves to the current context, so that we can be found by other extensions.
// contextStore.put(Extension_Mats.class, parentExtension.get());
return;
}

// -> No, we are not inside a Extension_Mats, so this is the top level, and we do the beforeAll
contextStore.put(Extension_Mats.class, this);
super.beforeAll();
}

/**
* Executed by Jupiter after all test methods have been executed. (Once at the end of the class.)
*/
@Override
public void afterAll(ExtensionContext context) {
// Handle the "nesting level'ing" of the beforeAll/afterAll
int nestingLevel = _nestinglevel.decrementAndGet();
// ?: Are we at the top level?
if (nestingLevel != 0) {
// -> No not at top, so then we ignore the afterAll
log.debug("--- Jupiter --- afterAll(..) invoked, but ignoring since nesting level is > 0: "
+ nestingLevel);
}
else {
// -> Yes, we are at the top level, so then we do the afterAll
super.afterAll();
Optional<Extension_Mats> parentExtension = context.getParent()
.map(parentContext -> parentContext.getStore(NAMESPACE))
.map(store -> store.get(Extension_Mats.class, Extension_Mats.class));

// Are we inside a Extension_Mats?
if (parentExtension.isPresent()) {
// -> Yes, we are inside a Extension_Mats, so we do not do the afterAll
log.debug("--- Jupiter --- afterAll(..) invoked, but ignoring since we are not at the root");
return;
}
// -> No, we are not inside a Extension_Mats, so this is the top level, and we do the afterAll
super.afterAll();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ class Extension_MatsRegistration implements Extension, BeforeAllCallback, AfterA

@Override
public void beforeAll(ExtensionContext context) throws ReflectiveOperationException {
// If the Extension_Mats is already registered, we do not need to do anything.
if (Extension_Mats.isExtensionRegistered(context)) {
return;
}
MatsTest matsTest = context.getRequiredTestClass().getAnnotation(MatsTest.class);
// If the test class is not annotated with MatsTest, then we should not register the Extension_Mats in
// this context.
if (matsTest == null) {
return;
}

SerializerFactory serializerFactory = matsTest.serializerFactory().getDeclaredConstructor().newInstance();
MatsSerializer<?> matsSerializer = serializerFactory.createSerializer();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,11 @@ private void addTestFieldsAsBeans(Object testInstance) {
addTestFieldsAsBeans(fieldInstance);
return;
}
if (MatsFactory.class.equals(field.getType())) {
if (fieldInstance instanceof MatsFactory) {
// -> Yes, then we should not register this as a bean
if (log.isTraceEnabled()) log.trace(LOG_PREFIX + " \\- Skipping field, as it is a MatsFactory.");
return;
}
if (MatsFuturizer.class.equals(field.getType())) {
// -> Yes, then we should not register this as a bean
if (log.isTraceEnabled()) log.trace(LOG_PREFIX + " \\- Skipping field, as it is a MatsFuturizer.");
return;
}

// ?: Is there no bean registered with this name, and we have a value for the field?
if (!_applicationContext.containsBean(fieldName)) {
Expand Down

0 comments on commit 32eb24e

Please sign in to comment.