Skip to content

Commit 355e113

Browse files
authored
Merge branch 'main' into feature/enable-specific-global-extensions-in-junit
2 parents 09dca93 + 490e663 commit 355e113

File tree

14 files changed

+315
-12
lines changed

14 files changed

+315
-12
lines changed

.github/workflows/cross-version.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
with:
6363
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
6464
arguments: |
65+
-Ptesting.enableJaCoCo=false \
6566
-PjavaToolchain.version=${{ matrix.jdk.version }} \
6667
-Dscan.tag.JDK_${{ matrix.jdk.version }} \
6768
build \
@@ -101,6 +102,7 @@ jobs:
101102
with:
102103
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
103104
arguments: |
105+
-Ptesting.enableJaCoCo=false \
104106
-PjavaToolchain.version=${{ matrix.jdk }} \
105107
-PjavaToolchain.implementation=j9 \
106108
-Dscan.tag.JDK_${{ matrix.jdk }} \

.github/workflows/main.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ jobs:
3434
with:
3535
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
3636
arguments: |
37-
-Ptesting.enableJaCoCo \
3837
:platform-tooling-support-tests:test \
3938
build \
4039
jacocoRootReport \
4140
--no-configuration-cache # Disable configuration cache due to https://github.com/diffplug/spotless/issues/2318
4241
- name: Upload to Codecov.io
43-
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
42+
uses: codecov/codecov-action@5c47607acb93fed5485fdbf7232e8a31425f672a # v5
4443
with:
4544
token: ${{ secrets.CODECOV_TOKEN }}
4645

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ builds of the next OpenJDK.
4949
Code coverage using [JaCoCo] for the latest build is available on [Codecov].
5050

5151
A code coverage report can also be generated locally via the [Gradle Wrapper] by
52-
executing `./gradlew -Ptesting.enableJaCoCo clean jacocoRootReport`. The results will be available
52+
executing `./gradlew clean jacocoRootReport`. The results will be available
5353
in `build/reports/jacoco/jacocoRootReport/html/index.html`.
5454

5555
## Develocity

documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ JUnit repository on GitHub.
9595
a test-scoped `ExtensionContext` in `Extension` methods called during test class
9696
instantiation. This behavior will become the default in future versions of JUnit.
9797
* `@TempDir` is now supported on test class constructors.
98+
* Parameterized tests now support argument count validation.
99+
If the `junit.jupiter.params.argumentCountValidation=strict` configuration parameter
100+
or the `@ParameterizedTest(argumentCountValidation = STRICT)` attribute is set, any
101+
mismatch between the declared number of arguments and the number of arguments provided
102+
by the arguments source will result in an error. By default, it's still only an error if
103+
there are fewer arguments provided than declared.
98104
* The new `PreInterruptCallback` extension point defines the API for `Extensions` that
99105
wish to be called prior to invocations of `Thread#interrupt()` by the `@Timeout`
100106
extension.

documentation/src/docs/asciidoc/user-guide/writing-tests.adoc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,29 @@ The following annotations are repeatable:
20202020
* `@CsvFileSource`
20212021
* `@ArgumentsSource`
20222022

2023+
[[writing-tests-parameterized-tests-argument-count-validation]]
2024+
==== Argument Count Validation
2025+
2026+
WARNING: Argument count validation is currently an _experimental_ feature. You're invited to
2027+
give it a try and provide feedback to the JUnit team so they can improve and eventually
2028+
<<api-evolution, promote>> this feature.
2029+
2030+
By default, when an arguments source provides more arguments than the test method needs,
2031+
those additional arguments are ignored and the test executes as usual.
2032+
This can lead to bugs where arguments are never passed to the parameterized test method.
2033+
2034+
To prevent this, you can set argument count validation to 'strict'.
2035+
Then, any additional arguments will cause an error instead.
2036+
2037+
To change this behavior for all tests, set the `junit.jupiter.params.argumentCountValidation`
2038+
<<running-tests-config-params, configuration parameter>> to `strict`.
2039+
To change this behavior for a single test,
2040+
use the `argumentCountValidation` attribute of the `@ParameterizedTest` annotation:
2041+
2042+
[source,java,indent=0]
2043+
----
2044+
include::{testDir}/example/ParameterizedTestDemo.java[tags=argument_count_validation]
2045+
----
20232046

20242047
[[writing-tests-parameterized-tests-argument-conversion]]
20252048
==== Argument Conversion

documentation/src/test/java/example/ParameterizedTestDemo.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.junit.jupiter.api.extension.ExtensionContext;
5252
import org.junit.jupiter.api.extension.ParameterContext;
5353
import org.junit.jupiter.api.parallel.Execution;
54+
import org.junit.jupiter.params.ArgumentCountValidationMode;
5455
import org.junit.jupiter.params.ParameterizedTest;
5556
import org.junit.jupiter.params.aggregator.AggregateWith;
5657
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
@@ -607,4 +608,13 @@ static Stream<String> otherProvider() {
607608
return Stream.of("bar");
608609
}
609610
// end::repeatable_annotations[]
611+
612+
@extensions.ExpectToFail
613+
// tag::argument_count_validation[]
614+
@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)
615+
@CsvSource({ "42, -666" })
616+
void testWithArgumentCountValidation(int number) {
617+
assertTrue(number > 0);
618+
}
619+
// end::argument_count_validation[]
610620
}

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ foojayResolver = { id = "org.gradle.toolchains.foojay-resolver", version = "0.8.
9292
gitPublish = { id = "org.ajoberstar.git-publish", version = "4.2.2" }
9393
jmh = { id = "me.champeau.jmh", version = "0.7.2" }
9494
nexusPublish = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" }
95-
plantuml = { id = "io.freefair.plantuml", version = "8.10.2" }
95+
plantuml = { id = "io.freefair.plantuml", version = "8.11" }
9696
shadow = { id = "com.gradleup.shadow", version = "8.3.5" }
9797
spotless = { id = "com.diffplug.spotless", version = "7.0.0.BETA4" }
9898
versions = { id = "com.github.ben-manes.versions", version = "0.51.0" }

gradle/plugins/build-parameters/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ buildParameters {
6767
description = "Testing related parameters"
6868
bool("enableJaCoCo") {
6969
description = "Enables JaCoCo test coverage reporting"
70-
defaultValue = false
70+
defaultValue = true
7171
}
7272
bool("enableJFR") {
7373
description = "Enables Java Flight Recorder functionality"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2015-2024 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.params;
12+
13+
import org.apiguardian.api.API;
14+
import org.junit.jupiter.params.provider.ArgumentsSource;
15+
16+
/**
17+
* Enumeration of argument count validation modes for {@link ParameterizedTest @ParameterizedTest}.
18+
*
19+
* <p>When an {@link ArgumentsSource} provides more arguments than declared by the test method,
20+
* there might be a bug in the test method or the {@link ArgumentsSource}.
21+
* By default, the additional arguments are ignored.
22+
* {@link ArgumentCountValidationMode} allows you to control how additional arguments are handled.
23+
*
24+
* @since 5.12
25+
* @see ParameterizedTest
26+
*/
27+
@API(status = API.Status.EXPERIMENTAL, since = "5.12")
28+
public enum ArgumentCountValidationMode {
29+
/**
30+
* Use the default validation mode.
31+
*
32+
* <p>The default validation mode may be changed via the
33+
* {@value ArgumentCountValidator#ARGUMENT_COUNT_VALIDATION_KEY} configuration parameter
34+
* (see the User Guide for details on configuration parameters).
35+
*/
36+
DEFAULT,
37+
38+
/**
39+
* Use the "none" argument count validation mode.
40+
*
41+
* <p>When there are more arguments provided than declared by the test method,
42+
* these additional arguments are ignored.
43+
*/
44+
NONE,
45+
46+
/**
47+
* Use the strict argument count validation mode.
48+
*
49+
* <p>When there are more arguments provided than declared by the test method, this raises an error.
50+
*/
51+
STRICT,
52+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2015-2024 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.params;
12+
13+
import java.lang.reflect.Method;
14+
import java.util.Arrays;
15+
import java.util.Optional;
16+
17+
import org.junit.jupiter.api.extension.ExtensionConfigurationException;
18+
import org.junit.jupiter.api.extension.ExtensionContext;
19+
import org.junit.jupiter.api.extension.InvocationInterceptor;
20+
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
21+
import org.junit.jupiter.params.provider.Arguments;
22+
import org.junit.platform.commons.logging.Logger;
23+
import org.junit.platform.commons.logging.LoggerFactory;
24+
import org.junit.platform.commons.util.Preconditions;
25+
26+
class ArgumentCountValidator implements InvocationInterceptor {
27+
private static final Logger logger = LoggerFactory.getLogger(ArgumentCountValidator.class);
28+
29+
static final String ARGUMENT_COUNT_VALIDATION_KEY = "junit.jupiter.params.argumentCountValidation";
30+
private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(
31+
ArgumentCountValidator.class);
32+
33+
private final ParameterizedTestMethodContext methodContext;
34+
private final Arguments arguments;
35+
36+
ArgumentCountValidator(ParameterizedTestMethodContext methodContext, Arguments arguments) {
37+
this.methodContext = methodContext;
38+
this.arguments = arguments;
39+
}
40+
41+
@Override
42+
public void interceptTestTemplateMethod(InvocationInterceptor.Invocation<Void> invocation,
43+
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
44+
validateArgumentCount(extensionContext, arguments);
45+
invocation.proceed();
46+
}
47+
48+
private ExtensionContext.Store getStore(ExtensionContext context) {
49+
return context.getRoot().getStore(NAMESPACE);
50+
}
51+
52+
private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) {
53+
ArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode(extensionContext);
54+
switch (argumentCountValidationMode) {
55+
case DEFAULT:
56+
case NONE:
57+
return;
58+
case STRICT:
59+
int testParamCount = extensionContext.getRequiredTestMethod().getParameterCount();
60+
int argumentsCount = arguments.get().length;
61+
Preconditions.condition(testParamCount == argumentsCount, () -> String.format(
62+
"Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided.%nNote: the provided arguments are %s",
63+
testParamCount, argumentsCount, Arrays.toString(arguments.get())));
64+
break;
65+
default:
66+
throw new ExtensionConfigurationException(
67+
"Unsupported argument count validation mode: " + argumentCountValidationMode);
68+
}
69+
}
70+
71+
private ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionContext extensionContext) {
72+
ParameterizedTest parameterizedTest = methodContext.annotation;
73+
if (parameterizedTest.argumentCountValidation() != ArgumentCountValidationMode.DEFAULT) {
74+
return parameterizedTest.argumentCountValidation();
75+
}
76+
else {
77+
return getArgumentCountValidationModeConfiguration(extensionContext);
78+
}
79+
}
80+
81+
private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration(ExtensionContext extensionContext) {
82+
String key = ARGUMENT_COUNT_VALIDATION_KEY;
83+
ArgumentCountValidationMode fallback = ArgumentCountValidationMode.NONE;
84+
ExtensionContext.Store store = getStore(extensionContext);
85+
return store.getOrComputeIfAbsent(key, __ -> {
86+
Optional<String> optionalConfigValue = extensionContext.getConfigurationParameter(key);
87+
if (optionalConfigValue.isPresent()) {
88+
String configValue = optionalConfigValue.get();
89+
Optional<ArgumentCountValidationMode> enumValue = Arrays.stream(
90+
ArgumentCountValidationMode.values()).filter(
91+
mode -> mode.name().equalsIgnoreCase(configValue)).findFirst();
92+
if (enumValue.isPresent()) {
93+
logger.config(() -> String.format(
94+
"Using ArgumentCountValidationMode '%s' set via the '%s' configuration parameter.",
95+
enumValue.get().name(), key));
96+
return enumValue.get();
97+
}
98+
else {
99+
logger.warn(() -> String.format(
100+
"Invalid ArgumentCountValidationMode '%s' set via the '%s' configuration parameter. "
101+
+ "Falling back to the %s default value.",
102+
configValue, key, fallback.name()));
103+
return fallback;
104+
}
105+
}
106+
else {
107+
return fallback;
108+
}
109+
}, ArgumentCountValidationMode.class);
110+
}
111+
}

0 commit comments

Comments
 (0)