Skip to content

Commit 476b18a

Browse files
committed
[SUREFIRE-1385] Add new parameter "userPropertyVariables" to overwrite
user properties Log overwritten properties Clarify effective properties merging order
1 parent 805f6b7 commit 476b18a

File tree

30 files changed

+244
-67
lines changed

30 files changed

+244
-67
lines changed

maven-failsafe-plugin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<parent>
2424
<groupId>org.apache.maven.surefire</groupId>
2525
<artifactId>surefire</artifactId>
26-
<version>3.3.2-SNAPSHOT</version>
26+
<version>3.4.0-SNAPSHOT</version>
2727
</parent>
2828

2929
<groupId>org.apache.maven.plugins</groupId>

maven-surefire-common/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<parent>
2424
<groupId>org.apache.maven.surefire</groupId>
2525
<artifactId>surefire</artifactId>
26-
<version>3.3.2-SNAPSHOT</version>
26+
<version>3.4.0-SNAPSHOT</version>
2727
</parent>
2828

2929
<artifactId>maven-surefire-common</artifactId>

maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.IOException;
2525
import java.math.BigDecimal;
2626
import java.nio.file.Files;
27+
import java.text.ChoiceFormat;
2728
import java.util.ArrayList;
2829
import java.util.Arrays;
2930
import java.util.Collection;
@@ -319,25 +320,49 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
319320
*/
320321
@Deprecated
321322
@Parameter
322-
private Properties systemProperties;
323+
Properties systemProperties;
323324

324325
/**
325326
* List of System properties to pass to a provider.
326327
* The effective system properties given to a provider are contributed from several sources:
327328
* <ol>
329+
* <li>properties set via {@link #argLine} with {@code -D} (only for forked executions)</li>
328330
* <li>{@link #systemProperties}</li>
329331
* <li>{@link AbstractSurefireMojo#getSystemPropertiesFile()} (set via parameter {@code systemPropertiesFile} on some goals)</li>
330332
* <li>{@link #systemPropertyVariables}</li>
331-
* <li>User properties from {@link MavenSession#getUserProperties()}, usually set via CLI options given with {@code -D}</li>
333+
* <li>User properties from {@link MavenSession#getUserProperties()}, usually set via CLI options given with {@code -D} on the current Maven process</li>
334+
* <li>{@link #userPropertyVariables}</li>
332335
* </ol>
333336
* Later sources may overwrite same named properties from earlier sources, that means for example that one cannot overwrite user properties with either
334-
* {@link #systemProperties}, {@link AbstractSurefireMojo#getSystemPropertiesFile()} or {@link #systemPropertyVariables}.
337+
* {@link #systemProperties}, {@link #getSystemPropertiesFile()} or {@link #systemPropertyVariables} but only with
338+
* {@link #userPropertyVariables}.
339+
* <p>
340+
* Certain properties may only be overwritten via {@link #argLine} (applicable only for forked executions) because their values are cached and only evaluated at the start of the JRE.
341+
* Those include:
342+
* <ul>
343+
* <li>{@code java.library.path}</li>
344+
* <li>{@code file.encoding}</li>
345+
* <li>{@code jdk.map.althashing.threshold}</li>
346+
* <li>{@code line.separator}</li>
347+
* </ul>
335348
*
336349
* @since 2.5
337350
* @see #systemProperties
338351
*/
339352
@Parameter
340-
private Map<String, String> systemPropertyVariables;
353+
Map<String, String> systemPropertyVariables;
354+
355+
/**
356+
* List of user properties to pass to a provider.
357+
* Similar to {@link #systemPropertyVariables} but having a higher precedence, therefore allows to overwrite user properties from the current Maven session.
358+
* This should only be used in case a user property from the parent process needs to be explicitly overwritten.
359+
* Regular properties should be set via {@link #systemPropertyVariables} instead in order to allow them to be overwritten
360+
* via CLI arguments ({@code -Dmyproperty=myvalue})
361+
* @since 3.4
362+
* @see #systemPropertyVariables
363+
*/
364+
@Parameter
365+
Map<String, String> userPropertyVariables;
341366

342367
/**
343368
* List of properties for configuring the testing provider. This is the preferred method of
@@ -425,7 +450,7 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
425450
private String jvm;
426451

427452
/**
428-
* Arbitrary JVM options to set on the command line.
453+
* Arbitrary JVM options to set on the command line. Only effective for forked executions.
429454
* <br>
430455
* <br>
431456
* Since the Version 2.17 using an alternate syntax for {@code argLine}, <b>@{...}</b> allows late replacement
@@ -438,6 +463,7 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
438463
* <a href="http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html">
439464
* http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html</a>
440465
*
466+
* @see #forkCount
441467
* @since 2.1
442468
*/
443469
@Parameter(property = "argLine")
@@ -543,7 +569,7 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
543569
* @since 2.14
544570
*/
545571
@Parameter(property = "forkCount", defaultValue = "1")
546-
private String forkCount;
572+
String forkCount;
547573

548574
/**
549575
* Indicates if forked VMs can be reused. If set to "false", a new VM is forked for each test class to be executed.
@@ -1139,10 +1165,10 @@ protected List<ProviderInfo> createProviders(TestClassPath testClasspath) throws
11391165
new JUnit3ProviderInfo());
11401166
}
11411167

1142-
private SurefireProperties setupProperties() {
1143-
SurefireProperties sysProps = null;
1168+
SurefireProperties setupProperties() {
1169+
SurefireProperties sysPropsFromFile = null;
11441170
try {
1145-
sysProps = SurefireProperties.loadProperties(getSystemPropertiesFile());
1171+
sysPropsFromFile = SurefireProperties.loadProperties(getSystemPropertiesFile());
11461172
} catch (IOException e) {
11471173
String msg = "The file '" + getSystemPropertiesFile().getAbsolutePath() + "' can't be read.";
11481174
if (getConsoleLogger().isDebugEnabled()) {
@@ -1152,8 +1178,12 @@ private SurefireProperties setupProperties() {
11521178
}
11531179
}
11541180

1155-
SurefireProperties result = SurefireProperties.calculateEffectiveProperties(
1156-
getSystemProperties(), getSystemPropertyVariables(), getUserProperties(), sysProps);
1181+
SurefireProperties result = calculateEffectiveProperties(
1182+
getSystemProperties(),
1183+
getSystemPropertyVariables(),
1184+
getUserProperties(),
1185+
userPropertyVariables,
1186+
sysPropsFromFile);
11571187

11581188
result.setProperty("basedir", getBasedir().getAbsolutePath());
11591189
result.setProperty("localRepository", getLocalRepositoryPath());
@@ -1184,6 +1214,43 @@ private SurefireProperties setupProperties() {
11841214
return result;
11851215
}
11861216

1217+
private SurefireProperties calculateEffectiveProperties(
1218+
Properties systemProperties,
1219+
Map<String, String> systemPropertyVariables,
1220+
Properties userProperties,
1221+
Map<String, String> userPropertyVariables,
1222+
SurefireProperties sysPropsFromFile) {
1223+
SurefireProperties result = new SurefireProperties();
1224+
result.copyPropertiesFrom(systemProperties);
1225+
1226+
Collection<String> overwrittenProperties = result.copyPropertiesFrom(sysPropsFromFile);
1227+
if (!overwrittenProperties.isEmpty()) {
1228+
getConsoleLogger().debug(getOverwrittenPropertiesLogMessage(overwrittenProperties, "sysPropsFile"));
1229+
}
1230+
overwrittenProperties = result.copyPropertiesFrom(systemPropertyVariables);
1231+
if (!overwrittenProperties.isEmpty()) {
1232+
getConsoleLogger()
1233+
.debug(getOverwrittenPropertiesLogMessage(overwrittenProperties, "systemPropertyVariables"));
1234+
}
1235+
// We used to take all of our system properties and dump them in with the
1236+
// user specified properties for SUREFIRE-121, causing SUREFIRE-491.
1237+
// Not gonna do THAT any more... instead, we only propagate those system properties
1238+
// that have been explicitly specified by the user via -Dkey=value on the CLI
1239+
1240+
overwrittenProperties = result.copyPropertiesFrom(userProperties);
1241+
if (!overwrittenProperties.isEmpty()) {
1242+
getConsoleLogger()
1243+
.warning(getOverwrittenPropertiesLogMessage(
1244+
overwrittenProperties, "user properties from Maven session"));
1245+
}
1246+
overwrittenProperties = result.copyPropertiesFrom(userPropertyVariables);
1247+
if (!overwrittenProperties.isEmpty()) {
1248+
getConsoleLogger()
1249+
.warning(getOverwrittenPropertiesLogMessage(overwrittenProperties, "userPropertyVariables"));
1250+
}
1251+
return result;
1252+
}
1253+
11871254
private Set<Object> systemPropertiesMatchingArgLine(SurefireProperties result) {
11881255
Set<Object> intersection = new HashSet<>();
11891256
if (isNotBlank(getArgLine())) {
@@ -1199,6 +1266,20 @@ private Set<Object> systemPropertiesMatchingArgLine(SurefireProperties result) {
11991266
return intersection;
12001267
}
12011268

1269+
private String getOverwrittenPropertiesLogMessage(
1270+
Collection<String> overwrittenProperties, String overwrittenBySource) {
1271+
if (overwrittenProperties.isEmpty()) {
1272+
throw new IllegalArgumentException("overwrittenProperties must not be empty");
1273+
}
1274+
// one or multiple?
1275+
ChoiceFormat propertyChoice = new ChoiceFormat("1#property|1>properties");
1276+
StringBuilder message = new StringBuilder("System ");
1277+
message.append(propertyChoice.format(overwrittenProperties.size())).append(" ");
1278+
message.append(overwrittenProperties.stream().collect(Collectors.joining("], [", "[", "]")));
1279+
message.append(" overwritten by ").append(overwrittenBySource);
1280+
return message.toString();
1281+
}
1282+
12021283
private void showToLog(SurefireProperties props, ConsoleLogger log) {
12031284
for (Object key : props.getStringKeySet()) {
12041285
String value = props.getProperty((String) key);
@@ -2718,7 +2799,7 @@ private Classpath getArtifactClasspath(Artifact surefireArtifact) throws MojoExe
27182799
return existing;
27192800
}
27202801

2721-
private Properties getUserProperties() {
2802+
Properties getUserProperties() {
27222803
return getSession().getUserProperties();
27232804
}
27242805

maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Enumeration;
2828
import java.util.HashSet;
2929
import java.util.LinkedHashSet;
30+
import java.util.LinkedList;
3031
import java.util.List;
3132
import java.util.Map;
3233
import java.util.Properties;
@@ -40,7 +41,7 @@
4041
import static java.util.Map.Entry;
4142

4243
/**
43-
* A properties implementation that preserves insertion order.
44+
* A {@link Properties} implementation that preserves insertion order.
4445
*/
4546
public class SurefireProperties extends Properties implements KeyValueSource {
4647
private static final Collection<String> KEYS_THAT_CANNOT_BE_USED_AS_SYSTEM_PROPERTIES =
@@ -64,9 +65,17 @@ public SurefireProperties(KeyValueSource source) {
6465

6566
@Override
6667
public synchronized void putAll(Map<?, ?> t) {
67-
for (Entry<?, ?> entry : t.entrySet()) {
68-
put(entry.getKey(), entry.getValue());
68+
putAllInternal(t);
69+
}
70+
71+
private Collection<String> putAllInternal(Map<?, ?> source) {
72+
Collection<String> overwrittenProperties = new LinkedList<>();
73+
for (Entry<?, ?> entry : source.entrySet()) {
74+
if (put(entry.getKey(), entry.getValue()) != null) {
75+
overwrittenProperties.add(entry.getKey().toString());
76+
}
6977
}
78+
return overwrittenProperties;
7079
}
7180

7281
@Override
@@ -92,12 +101,28 @@ public synchronized Enumeration<Object> keys() {
92101
return Collections.enumeration(items);
93102
}
94103

95-
public void copyPropertiesFrom(Properties source) {
104+
/**
105+
* Copies all keys and values from source to these properties, overwriting existing properties with same name
106+
* @param source
107+
* @return all overwritten property names (may be empty if there was no property name clash)
108+
*/
109+
public Collection<String> copyPropertiesFrom(Properties source) {
96110
if (source != null) {
97-
putAll(source);
111+
return putAllInternal(source);
112+
} else {
113+
return Collections.emptyList();
98114
}
99115
}
100116

117+
/**
118+
* Copies all keys and values from source to these properties, overwriting existing properties with same name
119+
* @param source
120+
* @return all overwritten property names (may be empty if there was no property name clash)
121+
*/
122+
public Collection<String> copyPropertiesFrom(Map<String, String> source) {
123+
return copyProperties(this, source);
124+
}
125+
101126
public Iterable<Object> getStringKeySet() {
102127
return keySet();
103128
}
@@ -121,34 +146,17 @@ public void copyToSystemProperties() {
121146
}
122147
}
123148

124-
static SurefireProperties calculateEffectiveProperties(
125-
Properties systemProperties,
126-
Map<String, String> systemPropertyVariables,
127-
Properties userProperties,
128-
SurefireProperties props) {
129-
SurefireProperties result = new SurefireProperties();
130-
result.copyPropertiesFrom(systemProperties);
131-
132-
result.copyPropertiesFrom(props);
133-
134-
copyProperties(result, systemPropertyVariables);
135-
136-
// We used to take all of our system properties and dump them in with the
137-
// user specified properties for SUREFIRE-121, causing SUREFIRE-491.
138-
// Not gonna do THAT any more... instead, we only propagate those system properties
139-
// that have been explicitly specified by the user via -Dkey=value on the CLI
140-
141-
result.copyPropertiesFrom(userProperties);
142-
return result;
143-
}
144-
145-
private static void copyProperties(Properties target, Map<String, String> source) {
149+
private static Collection<String> copyProperties(Properties target, Map<String, String> source) {
150+
Collection<String> overwrittenProperties = new LinkedList<>();
146151
if (source != null) {
147152
for (String key : source.keySet()) {
148153
String value = source.get(key);
149-
target.setProperty(key, value == null ? "" : value);
154+
if (target.setProperty(key, value == null ? "" : value) != null) {
155+
overwrittenProperties.add(key);
156+
}
150157
}
151158
}
159+
return overwrittenProperties;
152160
}
153161

154162
@Override

0 commit comments

Comments
 (0)