Skip to content

Commit d282b32

Browse files
committed
Move parameter resolver into to the test template
1 parent 708e225 commit d282b32

File tree

2 files changed

+103
-91
lines changed

2 files changed

+103
-91
lines changed

junit-jupiter-migration-support/src/main/java/org/junit/jupiter/migrationsupport/parameterized/ParameterizedExtension.java

Lines changed: 62 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import static org.junit.platform.commons.meta.API.Usage.Experimental;
1818

1919
import java.lang.reflect.Constructor;
20+
import java.lang.reflect.Executable;
2021
import java.lang.reflect.Field;
2122
import java.lang.reflect.Method;
22-
import java.util.ArrayList;
2323
import java.util.Collection;
2424
import java.util.List;
2525
import java.util.Map;
@@ -41,10 +41,9 @@
4141
import org.junit.runners.Parameterized;
4242

4343
@API(Experimental)
44-
public class ParameterizedExtension implements TestTemplateInvocationContextProvider, ParameterResolver {
44+
public class ParameterizedExtension implements TestTemplateInvocationContextProvider {
4545
private static ExtensionContext.Namespace parameters = ExtensionContext.Namespace.create(
4646
ParameterizedExtension.class);
47-
private int parametersCollectionIndex = 0;
4847

4948
/**
5049
* Indicate whether we can provide parameterized support.
@@ -54,86 +53,33 @@ public class ParameterizedExtension implements TestTemplateInvocationContextProv
5453
*/
5554
@Override
5655
public boolean supports(ContainerExtensionContext context) {
57-
return hasParametersMethod(context) && hasCorrectParameterFields(context);
56+
return hasParametersMethod(context) && validInjectionMix(context);
5857
}
5958

60-
@Override
61-
public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
62-
// grabbing the parent ensures the paremeters are stored in the same store.
63-
return context.getParent().flatMap(ParameterizedExtension::parameters).map(
64-
ParameterizedExtension::testTemplateContextsFromParameters).orElse(Stream.empty());
65-
}
66-
67-
/**
68-
* Since the parameterized runner in JUnit 4 could only resolve constructor parameters
69-
* this extension once again here only support them on the constructor and require an {@code @Parameters} method
70-
*
71-
* @param parameterContext the context for the parameter to be resolved; never
72-
* {@code null}
73-
* @param extensionContext the extension context for the {@code Executable}
74-
* about to be invoked; never {@code null}
75-
* @return true if the above is met otherwise false.
76-
*/
77-
@Override
78-
public boolean supports(ParameterContext parameterContext, ExtensionContext extensionContext) {
79-
return hasParametersMethod(extensionContext)
80-
&& parameterContext.getDeclaringExecutable() instanceof Constructor;
81-
}
82-
83-
@Override
84-
public Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext)
85-
throws ParameterResolutionException {
86-
int parameterCount = parameterContext.getDeclaringExecutable().getParameterCount();
87-
Object[] parameters = resolveParametersForConstructor(extensionContext, parameterCount);
88-
89-
int parameterIndex = parameterContext.getIndex();
90-
// move to the next set of parametersFields
91-
if (lastParameterToBeResolved(parameterContext)) {
92-
this.parametersCollectionIndex++;
93-
}
94-
95-
return parameters[parameterIndex];
96-
}
97-
98-
/**
99-
* Retrieves the Object[] of the current iteration we are working on.
100-
*
101-
* @param extensionContext the extensionContext
102-
* @param parameterCount the amount of parameters of the constructor.
103-
*
104-
* @return the Object[] for this parameter iteration.
105-
* @throws ParameterResolutionException If the amount of arguments of the constructor doesn't match the amount
106-
* of arguments of the currently resolved object[]
107-
*/
108-
private Object[] resolveParametersForConstructor(ExtensionContext extensionContext, int parameterCount)
109-
throws ParameterResolutionException {
110-
// @formatter:off
111-
return parameters(extensionContext).map(ArrayList::new)
112-
.map(l -> l.get(this.parametersCollectionIndex))
113-
.filter(params -> params.length == parameterCount)
114-
.orElseThrow(ParameterizedExtension::unMatchedAmountOfParametersException);
115-
// @formatter:on
116-
}
117-
118-
private static boolean hasCorrectParameterFields(ExtensionContext context) {
59+
private static boolean validInjectionMix(ExtensionContext context) {
11960
List<Field> fields = parametersFields(context);
120-
boolean hasFieldInjection = !fields.isEmpty();
61+
boolean hasParameterFields = !fields.isEmpty();
62+
boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields);
63+
boolean hasArgsConstructor = hasArgsConstructor(context);
12164

122-
if (hasArgsConstructor(context) && hasFieldInjection) {
123-
return false;
65+
if (hasArgsConstructor) {
66+
return !hasParameterFields;
12467
}
125-
else if (hasFieldInjection) {
126-
return areParametersFormedCorrectly(fields);
68+
else {
69+
return !hasParameterFields || hasCorrectParameterFields;
12770
}
71+
}
12872

129-
return true;
73+
@Override
74+
public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext context) {
75+
// grabbing the parent ensures the parameters are stored in the same store across multiple TestTemplates.
76+
return context.getParent().flatMap(ParameterizedExtension::parameters).map(
77+
o -> testTemplateContextsFromParameters(o, context)).orElse(Stream.empty());
13078
}
13179

13280
private static boolean areParametersFormedCorrectly(List<Field> fields) {
13381
List<Integer> parameterValues = parameterIndexes(fields);
134-
13582
List<Integer> duplicateIndexes = duplicatedIndexes(parameterValues);
136-
13783
boolean hasAllIndexes = indexRangeComplete(parameterValues);
13884

13985
return hasAllIndexes && duplicateIndexes.isEmpty();
@@ -166,10 +112,6 @@ private static Boolean indexRangeComplete(List<Integer> parameterValues) {
166112
// @formatter:on
167113
}
168114

169-
private static boolean lastParameterToBeResolved(ParameterContext parameterContext) {
170-
return parameterContext.getIndex() == parameterContext.getDeclaringExecutable().getParameterCount() - 1;
171-
}
172-
173115
private static Optional<Collection<Object[]>> parameters(ExtensionContext context) {
174116
return context.getStore(parameters).getOrComputeIfAbsent("parameterMethod",
175117
k -> new ParameterWrapper(callParameters(context)), ParameterWrapper.class).getValue();
@@ -201,20 +143,48 @@ private static Optional<Method> ensureSingleParametersMethod(Class<?> testClass)
201143
m -> m.isAnnotationPresent(Parameterized.Parameters.class)).stream().findFirst();
202144
}
203145

204-
private static Stream<TestTemplateInvocationContext> testTemplateContextsFromParameters(Collection<Object[]> o) {
205-
return o.stream().map(ParameterizedExtension::contextFactory);
146+
private static Stream<TestTemplateInvocationContext> testTemplateContextsFromParameters(Collection<Object[]> o,
147+
ExtensionContext context) {
148+
List<Field> fields = parametersFields(context);
149+
boolean hasParameterFields = !fields.isEmpty();
150+
boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields);
151+
152+
if (!hasParameterFields) {
153+
return o.stream().map(ParameterizedExtension::parameterResolver);
154+
}
155+
else if (hasCorrectParameterFields) {
156+
return o.stream().map(ParameterizedExtension::contextFactory);
157+
}
158+
159+
return Stream.empty();
206160
}
207161

208-
private static TestTemplateInvocationContext contextFactory(Object[] parameters) {
209-
return new TestTemplateInvocationContext() {
162+
private static TestTemplateInvocationContext parameterResolver(Object[] objects) {
163+
List<Extension> parameterResolvers = singletonList(new ParameterResolver() {
210164
@Override
211-
public List<Extension> getAdditionalExtensions() {
212-
return singletonList(new InjectionExtension(parameters));
165+
public boolean supports(ParameterContext parameterContext, ExtensionContext extensionContext)
166+
throws ParameterResolutionException {
167+
final Executable declaringExecutable = parameterContext.getDeclaringExecutable();
168+
return declaringExecutable instanceof Constructor
169+
&& declaringExecutable.getParameterCount() == objects.length;
213170
}
214-
};
171+
172+
@Override
173+
public Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext)
174+
throws ParameterResolutionException {
175+
return objects[parameterContext.getIndex()];
176+
}
177+
});
178+
179+
return templateWithExtensions(parameterResolvers);
180+
}
181+
182+
private static TestTemplateInvocationContext contextFactory(Object[] parameters) {
183+
return templateWithExtensions(singletonList(new InjectionExtension(parameters)));
215184
}
216185

217186
private static class InjectionExtension implements TestInstancePostProcessor {
187+
218188
private final Object[] parameters;
219189

220190
public InjectionExtension(Object[] parameters) {
@@ -235,6 +205,16 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex
235205
param.set(testInstance, this.parameters[paramIndex]);
236206
}
237207
}
208+
209+
}
210+
211+
private static TestTemplateInvocationContext templateWithExtensions(List<Extension> extensions) {
212+
return new TestTemplateInvocationContext() {
213+
@Override
214+
public List<Extension> getAdditionalExtensions() {
215+
return extensions;
216+
}
217+
};
238218
}
239219

240220
private static boolean hasArgsConstructor(ExtensionContext context) {

junit-jupiter-migration-support/src/test/java/org/junit/jupiter/migrationsupport/parameterized/ParametrizedExtensionTests.java renamed to junit-jupiter-migration-support/src/test/java/org/junit/jupiter/migrationsupport/parameterized/ParameterizedExtensionTests.java

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import org.junit.runners.Parameterized;
4141
import org.junit.runners.Parameterized.Parameters;
4242

43-
class ParametrizedExtensionTests {
43+
class ParameterizedExtensionTests {
4444

4545
@Test
4646
void parametrizedWithParameterFieldInjection() {
@@ -63,7 +63,7 @@ public static Collection<Object[]> data() {
6363
public int fExpected;
6464

6565
@TestTemplate
66-
public void test() {
66+
void test() {
6767
assertEquals(fExpected, compute(fInput));
6868
}
6969

@@ -104,7 +104,7 @@ public ParameterizedTestWithConstructor(int a, int b) {
104104
}
105105

106106
@TestTemplate
107-
public void test() {
107+
void test() {
108108
assertNotEquals(a, b);
109109
}
110110
}
@@ -127,7 +127,7 @@ public UnMatchedConstructor(int a) {
127127
}
128128

129129
@TestTemplate
130-
public void dummy() {
130+
void dummy() {
131131

132132
}
133133
}
@@ -152,7 +152,7 @@ public static Collection<Object[]> data() {
152152
}
153153

154154
@TestTemplate
155-
public void dummy() {
155+
void dummy() {
156156

157157
}
158158
}
@@ -182,7 +182,7 @@ public static Collection<Object[]> data() {
182182
}
183183

184184
@TestTemplate
185-
public void dummy() {
185+
void dummy() {
186186

187187
}
188188
}
@@ -208,7 +208,7 @@ public static int params() {
208208
}
209209

210210
@TestTemplate
211-
public void dummy() {
211+
void dummy() {
212212

213213
}
214214
}
@@ -231,7 +231,7 @@ public static Collection<Object[]> data() {
231231
}
232232

233233
@TestTemplate
234-
public void dummy() {
234+
void dummy() {
235235

236236
}
237237
}
@@ -256,7 +256,7 @@ public static Collection<Object[]> data() {
256256
}
257257

258258
@TestTemplate
259-
public void dummy() {
259+
void dummy() {
260260

261261
}
262262
}
@@ -287,6 +287,38 @@ void dummy() {
287287
}
288288
}
289289

290+
@Test
291+
void multipleTestTemplatesShouldBeRun() {
292+
ExecutionEventRecorder eventRecorder = executeTestsForClass(MultipleTestTemplates.class);
293+
assertThat(eventRecorder.getTestSuccessfulCount()).isEqualTo(4);
294+
}
295+
296+
@ExtendWith(ParameterizedExtension.class)
297+
static class MultipleTestTemplates {
298+
private static int invocationCount = 0;
299+
300+
public MultipleTestTemplates(int a) {
301+
302+
}
303+
304+
@Parameters
305+
public static Collection<Object[]> data() {
306+
invocationCount++;
307+
assertEquals(1, invocationCount);
308+
return Arrays.asList(new Object[][] { { 3 }, { 4 } });
309+
}
310+
311+
@TestTemplate
312+
void firstTemplate() {
313+
314+
}
315+
316+
@TestTemplate
317+
void secondTemplate() {
318+
319+
}
320+
}
321+
290322
private ExecutionEventRecorder executeTestsForClass(Class<?> testClass) {
291323
LauncherDiscoveryRequest request = request().selectors(selectClass(testClass)).build();
292324
JupiterTestEngine engine = new JupiterTestEngine();

0 commit comments

Comments
 (0)