Skip to content

Commit c582647

Browse files
authored
refactor(test): provide better shaped junit extensions for testing runtimes (#4234)
1 parent 703502f commit c582647

File tree

26 files changed

+672
-506
lines changed

26 files changed

+672
-506
lines changed

core/common/boot/src/main/java/org/eclipse/edc/boot/system/runtime/BaseRuntime.java

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class BaseRuntime {
6060
private final ConfigurationLoader configurationLoader;
6161
private final List<ServiceExtension> serviceExtensions = new ArrayList<>();
6262
protected Monitor monitor;
63+
protected ServiceExtensionContext context;
6364

6465
public BaseRuntime() {
6566
this(new ServiceLocatorImpl());
@@ -73,22 +74,39 @@ protected BaseRuntime(ServiceLocator serviceLocator) {
7374
public static void main(String[] args) {
7475
programArgs = args;
7576
var runtime = new BaseRuntime();
76-
runtime.boot();
77+
runtime.boot(true);
7778
}
7879

7980
/**
80-
* Main entry point to runtime initialization. Calls all methods
81-
* and sets up a context shutdown hook at runtime shutdown.
81+
* Main entry point to runtime initialization.
82+
*
83+
* @param addShutdownHook add a shutdown hook if true, don't otherwise.
8284
*/
83-
public void boot() {
84-
boot(true);
85-
}
85+
public void boot(boolean addShutdownHook) {
86+
monitor = createMonitor();
87+
var config = configurationLoader.loadConfiguration(monitor);
88+
context = createServiceExtensionContext(config);
8689

87-
/**
88-
* Main entry point to runtime initialization. Calls all methods.
89-
*/
90-
public void bootWithoutShutdownHook() {
91-
boot(false);
90+
try {
91+
var newExtensions = createExtensions(context);
92+
bootExtensions(context, newExtensions);
93+
94+
newExtensions.stream().map(InjectionContainer::getInjectionTarget).forEach(serviceExtensions::add);
95+
if (addShutdownHook) {
96+
getRuntime().addShutdownHook(new Thread(this::shutdown));
97+
}
98+
99+
if (context.hasService(HealthCheckService.class)) {
100+
var startupStatusRef = new AtomicReference<>(HealthCheckResult.Builder.newInstance().component("BaseRuntime").success().build());
101+
var healthCheckService = context.getService(HealthCheckService.class);
102+
healthCheckService.addStartupStatusProvider(startupStatusRef::get);
103+
}
104+
105+
} catch (Exception e) {
106+
onError(e);
107+
}
108+
109+
monitor.info(format("Runtime %s ready", context.getRuntimeId()));
92110
}
93111

94112
/**
@@ -169,31 +187,4 @@ protected Monitor createMonitor() {
169187
return ExtensionLoader.loadMonitor(programArgs);
170188
}
171189

172-
private void boot(boolean addShutdownHook) {
173-
monitor = createMonitor();
174-
var config = configurationLoader.loadConfiguration(monitor);
175-
var context = createServiceExtensionContext(config);
176-
177-
try {
178-
var newExtensions = createExtensions(context);
179-
bootExtensions(context, newExtensions);
180-
181-
newExtensions.stream().map(InjectionContainer::getInjectionTarget).forEach(serviceExtensions::add);
182-
if (addShutdownHook) {
183-
getRuntime().addShutdownHook(new Thread(this::shutdown));
184-
}
185-
186-
if (context.hasService(HealthCheckService.class)) {
187-
var startupStatusRef = new AtomicReference<>(HealthCheckResult.Builder.newInstance().component("BaseRuntime").success().build());
188-
var healthCheckService = context.getService(HealthCheckService.class);
189-
healthCheckService.addStartupStatusProvider(startupStatusRef::get);
190-
}
191-
192-
} catch (Exception e) {
193-
onError(e);
194-
}
195-
196-
monitor.info(format("Runtime %s ready", context.getRuntimeId()));
197-
}
198-
199190
}

core/common/boot/src/test/java/org/eclipse/edc/boot/system/runtime/BaseRuntimeTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public void initialize(ServiceExtensionContext context) {
6161
void baseRuntime_shouldBoot() {
6262
when(serviceLocator.loadImplementors(eq(ServiceExtension.class), anyBoolean())).thenReturn(List.of(new BaseExtension()));
6363

64-
runtime.boot();
64+
runtime.boot(true);
6565

6666
verify(monitor, never()).severe(anyString(), any());
6767
}
@@ -73,7 +73,7 @@ void baseRuntime_shouldNotBootWithException() {
7373
doThrow(new EdcException("Failed to start base extension")).when(extension).start();
7474
when(serviceLocator.loadImplementors(eq(ServiceExtension.class), anyBoolean())).thenReturn(List.of(extension));
7575

76-
assertThatThrownBy(runtime::boot).isInstanceOf(EdcException.class);
76+
assertThatThrownBy(() -> runtime.boot(true)).isInstanceOf(EdcException.class);
7777
verify(monitor).severe(startsWith("Error booting runtime: Failed to start base extension"), any(EdcException.class));
7878
}
7979

@@ -83,7 +83,7 @@ void shouldSetStartupCheckProvider_whenHealthCheckServiceIsRegistered() {
8383
when(serviceLocator.loadImplementors(eq(ServiceExtension.class), anyBoolean())).thenReturn(List.of(
8484
new BaseExtension(), registerService(HealthCheckService.class, healthCheckService)));
8585

86-
runtime.boot();
86+
runtime.boot(true);
8787

8888
verify(healthCheckService).addStartupStatusProvider(any());
8989
}
@@ -92,7 +92,7 @@ void shouldSetStartupCheckProvider_whenHealthCheckServiceIsRegistered() {
9292
void shouldLoadConfiguration() {
9393
when(serviceLocator.loadImplementors(eq(ServiceExtension.class), anyBoolean())).thenReturn(List.of(new BaseExtension()));
9494

95-
runtime.boot();
95+
runtime.boot(true);
9696

9797
verify(serviceLocator).loadImplementors(ConfigurationExtension.class, false);
9898
}

core/common/junit/src/main/java/org/eclipse/edc/junit/extensions/EdcClassRuntimesExtension.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020

2121
/**
2222
* Utility class that permits to run multiple EDC runtimes statically
23+
*
24+
* @deprecated please use either {@link RuntimePerMethodExtension} or {@link RuntimePerClassExtension}.
2325
*/
26+
@Deprecated(since = "0.7.0")
2427
public class EdcClassRuntimesExtension implements BeforeAllCallback, AfterAllCallback {
2528

2629
public final EdcRuntimeExtension[] extensions;

core/common/junit/src/main/java/org/eclipse/edc/junit/extensions/EdcExtension.java

Lines changed: 22 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,19 @@
1515

1616
package org.eclipse.edc.junit.extensions;
1717

18-
import org.eclipse.edc.boot.system.DefaultServiceExtensionContext;
19-
import org.eclipse.edc.boot.system.ServiceLocator;
20-
import org.eclipse.edc.boot.system.ServiceLocatorImpl;
21-
import org.eclipse.edc.boot.system.runtime.BaseRuntime;
22-
import org.eclipse.edc.spi.EdcException;
23-
import org.eclipse.edc.spi.monitor.Monitor;
2418
import org.eclipse.edc.spi.system.ConfigurationExtension;
25-
import org.eclipse.edc.spi.system.ServiceExtensionContext;
2619
import org.eclipse.edc.spi.system.SystemExtension;
27-
import org.eclipse.edc.spi.system.configuration.Config;
2820
import org.eclipse.edc.spi.system.configuration.ConfigFactory;
29-
import org.jetbrains.annotations.NotNull;
3021
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
3122
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
3223
import org.junit.jupiter.api.extension.ExtensionContext;
3324
import org.junit.jupiter.api.extension.ParameterContext;
3425
import org.junit.jupiter.api.extension.ParameterResolutionException;
3526
import org.junit.jupiter.api.extension.ParameterResolver;
3627

37-
import java.util.ArrayList;
38-
import java.util.LinkedHashMap;
39-
import java.util.List;
4028
import java.util.Map;
4129

30+
import static java.util.Collections.emptyMap;
4231
import static org.eclipse.edc.util.types.Cast.cast;
4332

4433
/**
@@ -47,19 +36,19 @@
4736
* injection of runtime services is supported.
4837
* <p>
4938
* If only basic dependency injection is needed, use {@link DependencyInjectionExtension} instead.
39+
*
40+
* @deprecated please use either {@link RuntimePerMethodExtension} or {@link RuntimePerClassExtension}.
5041
*/
51-
public class EdcExtension extends BaseRuntime implements BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver {
52-
private final LinkedHashMap<Class<?>, Object> serviceMocks = new LinkedHashMap<>();
53-
private final MultiSourceServiceLocator serviceLocator;
54-
private DefaultServiceExtensionContext context;
42+
@Deprecated(since = "0.7.0")
43+
public class EdcExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver {
44+
protected final EmbeddedRuntime runtime;
5545

5646
public EdcExtension() {
57-
this(new MultiSourceServiceLocator());
47+
this(new EmbeddedRuntime("runtime", emptyMap()));
5848
}
5949

60-
private EdcExtension(MultiSourceServiceLocator serviceLocator) {
61-
super(serviceLocator);
62-
this.serviceLocator = serviceLocator;
50+
protected EdcExtension(EmbeddedRuntime runtime) {
51+
this.runtime = runtime;
6352
}
6453

6554
/**
@@ -69,39 +58,35 @@ private EdcExtension(MultiSourceServiceLocator serviceLocator) {
6958
* @param mock the service mock
7059
*/
7160
public <T> void registerServiceMock(Class<T> type, T mock) {
72-
serviceMocks.put(type, mock);
61+
runtime.registerServiceMock(type, mock);
7362
}
7463

7564
/**
7665
* Registers a service extension with the runtime.
7766
*/
7867
public <T extends SystemExtension> void registerSystemExtension(Class<T> type, SystemExtension extension) {
79-
serviceLocator.registerSystemExtension(type, extension);
68+
runtime.registerSystemExtension(type, extension);
8069
}
8170

8271
@Override
83-
public void beforeTestExecution(ExtensionContext extensionContext) throws Exception {
84-
bootWithoutShutdownHook();
72+
public void beforeTestExecution(ExtensionContext extensionContext) {
73+
runtime.boot(false);
8574
}
8675

8776
@Override
88-
public void afterTestExecution(ExtensionContext context) throws Exception {
89-
shutdown();
90-
// clear the systemExtensions map to prevent it from piling up between subsequent runs
91-
serviceLocator.clearSystemExtensions();
92-
}
93-
94-
public DefaultServiceExtensionContext getContext() {
95-
return context;
77+
public void afterTestExecution(ExtensionContext context) {
78+
runtime.shutdown();
9679
}
9780

9881
@Override
9982
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
10083
var type = parameterContext.getParameter().getParameterizedType();
10184
if (type.equals(EdcExtension.class)) {
10285
return true;
86+
} else if (type.equals(EmbeddedRuntime.class)) {
87+
return true;
10388
} else if (type instanceof Class) {
104-
return context.hasService(cast(type));
89+
return runtime.getContext().hasService(cast(type));
10590
}
10691
return false;
10792
}
@@ -111,8 +96,10 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
11196
var type = parameterContext.getParameter().getParameterizedType();
11297
if (type.equals(EdcExtension.class)) {
11398
return this;
99+
} else if (type.equals(EmbeddedRuntime.class)) {
100+
return runtime;
114101
} else if (type instanceof Class) {
115-
return context.getService(cast(type));
102+
return runtime.getContext().getService(cast(type));
116103
}
117104
return null;
118105
}
@@ -122,55 +109,7 @@ public void setConfiguration(Map<String, String> configuration) {
122109
}
123110

124111
public <T> T getService(Class<T> clazz) {
125-
return context.getService(clazz);
126-
}
127-
128-
@Override
129-
protected @NotNull ServiceExtensionContext createContext(Monitor monitor, Config config) {
130-
context = new TestServiceExtensionContext(monitor, config, serviceMocks);
131-
return context;
132-
}
133-
134-
/**
135-
* A service locator that allows additional extensions to be manually loaded by a test fixture. This locator return
136-
* the union of registered extensions and extensions loaded by the delegate.
137-
*/
138-
private static class MultiSourceServiceLocator implements ServiceLocator {
139-
private final ServiceLocator delegate = new ServiceLocatorImpl();
140-
private final LinkedHashMap<Class<? extends SystemExtension>, List<SystemExtension>> systemExtensions;
141-
142-
MultiSourceServiceLocator() {
143-
systemExtensions = new LinkedHashMap<>();
144-
}
145-
146-
@Override
147-
public <T> List<T> loadImplementors(Class<T> type, boolean required) {
148-
List<T> extensions = cast(systemExtensions.getOrDefault(type, new ArrayList<>()));
149-
extensions.addAll(delegate.loadImplementors(type, required));
150-
return extensions;
151-
}
152-
153-
/**
154-
* This implementation will override singleton implementions found by the delegate.
155-
*/
156-
@Override
157-
public <T> T loadSingletonImplementor(Class<T> type, boolean required) {
158-
var extensions = systemExtensions.get(type);
159-
if (extensions == null || extensions.isEmpty()) {
160-
return delegate.loadSingletonImplementor(type, required);
161-
} else if (extensions.size() > 1) {
162-
throw new EdcException("Multiple extensions were registered for type: " + type.getName());
163-
}
164-
return type.cast(extensions.get(0));
165-
}
166-
167-
public <T extends SystemExtension> void registerSystemExtension(Class<T> type, SystemExtension extension) {
168-
systemExtensions.computeIfAbsent(type, k -> new ArrayList<>()).add(extension);
169-
}
170-
171-
public void clearSystemExtensions() {
172-
systemExtensions.clear();
173-
}
112+
return runtime.getService(clazz);
174113
}
175114

176115
}

0 commit comments

Comments
 (0)