Skip to content

Commit 4bd36a1

Browse files
authored
[SUREFIRE-1385] Add new parameter "promoteUserPropertiesToSystemProperties" (#762)
This allows to disable merging of user properties into effective system properties Log overwritten properties Clarify effective properties merging order
1 parent 1d19ec8 commit 4bd36a1

File tree

31 files changed

+274
-72
lines changed

31 files changed

+274
-72
lines changed

Diff for: maven-failsafe-plugin/pom.xml

+1-1
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>

Diff for: maven-surefire-common/pom.xml

+1-1
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>

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

+83-12
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,45 @@ 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 (only used as source if {@link #promoteUserPropertiesToSystemProperties} is {@code true})</li>
332334
* </ol>
333335
* 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}.
336+
* {@link #systemProperties}, {@link #getSystemPropertiesFile()} or {@link #systemPropertyVariables}.
337+
* <p>
338+
* 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.
339+
* Those include:
340+
* <ul>
341+
* <li>{@code java.library.path}</li>
342+
* <li>{@code file.encoding}</li>
343+
* <li>{@code jdk.map.althashing.threshold}</li>
344+
* <li>{@code line.separator}</li>
345+
* </ul>
335346
*
336347
* @since 2.5
337348
* @see #systemProperties
338349
*/
339350
@Parameter
340-
private Map<String, String> systemPropertyVariables;
351+
Map<String, String> systemPropertyVariables;
352+
353+
/**
354+
* If set to {@code true} will also pass all user properties exposed via {@link MavenSession#getUserProperties()} as system properties to a provider.
355+
* Those always take precedence over same named system properties set via any other means.
356+
*
357+
* @since 3.4.0
358+
* @see #systemPropertyVariables
359+
*/
360+
@Parameter(defaultValue = "true")
361+
boolean promoteUserPropertiesToSystemProperties;
341362

342363
/**
343364
* List of properties for configuring the testing provider. This is the preferred method of
@@ -425,7 +446,7 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
425446
private String jvm;
426447

427448
/**
428-
* Arbitrary JVM options to set on the command line.
449+
* Arbitrary JVM options to set on the command line. Only effective for forked executions.
429450
* <br>
430451
* <br>
431452
* Since the Version 2.17 using an alternate syntax for {@code argLine}, <b>@{...}</b> allows late replacement
@@ -438,6 +459,7 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
438459
* <a href="http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html">
439460
* http://maven.apache.org/surefire/maven-failsafe-plugin/faq.html</a>
440461
*
462+
* @see #forkCount
441463
* @since 2.1
442464
*/
443465
@Parameter(property = "argLine")
@@ -543,7 +565,7 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
543565
* @since 2.14
544566
*/
545567
@Parameter(property = "forkCount", defaultValue = "1")
546-
private String forkCount;
568+
String forkCount;
547569

548570
/**
549571
* 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 +1161,10 @@ protected List<ProviderInfo> createProviders(TestClassPath testClasspath) throws
11391161
new JUnit3ProviderInfo());
11401162
}
11411163

1142-
private SurefireProperties setupProperties() {
1143-
SurefireProperties sysProps = null;
1164+
SurefireProperties setupProperties() {
1165+
SurefireProperties sysPropsFromFile = null;
11441166
try {
1145-
sysProps = SurefireProperties.loadProperties(getSystemPropertiesFile());
1167+
sysPropsFromFile = SurefireProperties.loadProperties(getSystemPropertiesFile());
11461168
} catch (IOException e) {
11471169
String msg = "The file '" + getSystemPropertiesFile().getAbsolutePath() + "' can't be read.";
11481170
if (getConsoleLogger().isDebugEnabled()) {
@@ -1152,8 +1174,11 @@ private SurefireProperties setupProperties() {
11521174
}
11531175
}
11541176

1155-
SurefireProperties result = SurefireProperties.calculateEffectiveProperties(
1156-
getSystemProperties(), getSystemPropertyVariables(), getUserProperties(), sysProps);
1177+
SurefireProperties result = calculateEffectiveProperties(
1178+
getSystemProperties(),
1179+
getSystemPropertyVariables(),
1180+
promoteUserPropertiesToSystemProperties ? getUserProperties() : new Properties(),
1181+
sysPropsFromFile);
11571182

11581183
result.setProperty("basedir", getBasedir().getAbsolutePath());
11591184
result.setProperty("localRepository", getLocalRepositoryPath());
@@ -1184,6 +1209,38 @@ private SurefireProperties setupProperties() {
11841209
return result;
11851210
}
11861211

1212+
private SurefireProperties calculateEffectiveProperties(
1213+
Properties systemProperties,
1214+
Map<String, String> systemPropertyVariables,
1215+
Properties userProperties,
1216+
SurefireProperties sysPropsFromFile) {
1217+
SurefireProperties result = new SurefireProperties();
1218+
result.copyPropertiesFrom(systemProperties);
1219+
1220+
Collection<String> overwrittenProperties = result.copyPropertiesFrom(sysPropsFromFile);
1221+
if (!overwrittenProperties.isEmpty() && getConsoleLogger().isDebugEnabled()) {
1222+
getConsoleLogger().debug(getOverwrittenPropertiesLogMessage(overwrittenProperties, "systemPropertiesFile"));
1223+
}
1224+
overwrittenProperties = result.copyPropertiesFrom(systemPropertyVariables);
1225+
if (!overwrittenProperties.isEmpty() && getConsoleLogger().isDebugEnabled()) {
1226+
getConsoleLogger()
1227+
.debug(getOverwrittenPropertiesLogMessage(overwrittenProperties, "systemPropertyVariables"));
1228+
}
1229+
// We used to take all of our system properties and dump them in with the
1230+
// user specified properties for SUREFIRE-121, causing SUREFIRE-491.
1231+
// Not gonna do THAT any more... instead, we only propagate those system properties
1232+
// that have been explicitly specified by the user via -Dkey=value on the CLI
1233+
if (!userProperties.isEmpty()) {
1234+
overwrittenProperties = result.copyPropertiesFrom(userProperties);
1235+
if (!overwrittenProperties.isEmpty()) {
1236+
getConsoleLogger()
1237+
.warning(getOverwrittenPropertiesLogMessage(
1238+
overwrittenProperties, "user properties from Maven session"));
1239+
}
1240+
}
1241+
return result;
1242+
}
1243+
11871244
private Set<Object> systemPropertiesMatchingArgLine(SurefireProperties result) {
11881245
Set<Object> intersection = new HashSet<>();
11891246
if (isNotBlank(getArgLine())) {
@@ -1199,6 +1256,20 @@ private Set<Object> systemPropertiesMatchingArgLine(SurefireProperties result) {
11991256
return intersection;
12001257
}
12011258

1259+
private String getOverwrittenPropertiesLogMessage(
1260+
Collection<String> overwrittenProperties, String overwrittenBySource) {
1261+
if (overwrittenProperties.isEmpty()) {
1262+
throw new IllegalArgumentException("overwrittenProperties must not be empty");
1263+
}
1264+
// one or multiple?
1265+
ChoiceFormat propertyChoice = new ChoiceFormat("1#property|1>properties");
1266+
StringBuilder message = new StringBuilder("System ");
1267+
message.append(propertyChoice.format(overwrittenProperties.size())).append(" ");
1268+
message.append(overwrittenProperties.stream().collect(Collectors.joining("], [", "[", "]")));
1269+
message.append(" overwritten by ").append(overwrittenBySource);
1270+
return message.toString();
1271+
}
1272+
12021273
private void showToLog(SurefireProperties props, ConsoleLogger log) {
12031274
for (Object key : props.getStringKeySet()) {
12041275
String value = props.getProperty((String) key);
@@ -2718,7 +2789,7 @@ private Classpath getArtifactClasspath(Artifact surefireArtifact) throws MojoExe
27182789
return existing;
27192790
}
27202791

2721-
private Properties getUserProperties() {
2792+
Properties getUserProperties() {
27222793
return getSession().getUserProperties();
27232794
}
27242795

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

+36-28
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)