Skip to content

Commit

Permalink
Merge pull request #1007 from plastic-karma/issue_675
Browse files Browse the repository at this point in the history
allowing markers in jvm argument to preserve commas
  • Loading branch information
hcoles authored Apr 4, 2022
2 parents 018db30 + e877878 commit 874ad99
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.pitest.mutationtest.commandline;

import joptsimple.OptionSet;
import joptsimple.OptionSpec;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* JVM args preprocessor to allow single arguments with commas.
* The processor will first replace all commas with '@' if the comma is within a region of the argument marked by { and }.
* The processor will then split up the arguments by commas that are outside the markers.
* As a final step the '@' are replaced with commas again.
*/
public class CommaAwareArgsProcessor {

private static final char REGION_BEGIN = '{';
private static final char REGION_END = '}';
private final OptionSpec<String> optionsSpec;

public CommaAwareArgsProcessor(OptionSpec<String> args) {
this.optionsSpec = args;
}

public List<String> values(OptionSet userArgs) {
final String commandLineOption = optionsSpec.value(userArgs);
if (commandLineOption == null) {
return Collections.emptyList();
}

Set<Integer> modifiedIndices = new HashSet<>();
String preprocessedOptions = replaceCommas(commandLineOption, modifiedIndices);

String[] arguments = preprocessedOptions.split(",");
return postProcess(modifiedIndices, arguments);
}

/**
* Put commas back and delete region marker.
*/
private List<String> postProcess(Set<Integer> modifiedIndices, String[] arguments) {
List<String> newArguments = new ArrayList<>();
int base = 0;
for (String argument : arguments) {
newArguments.add(buildNewArgument(modifiedIndices, base, argument).toString());
base += argument.length() + 1;
}
return newArguments;
}

private StringBuilder buildNewArgument(Set<Integer> modifiedIndices, int base, String argument) {
StringBuilder newArgument = new StringBuilder();
for (int j = 0; j < argument.length(); j++) {
char current = argument.charAt(j);

// Only remove region markers, if commas have been replaced. Otherwise treat them as part of the argument.
if (!modifiedIndices.isEmpty() && (current == REGION_BEGIN || current == REGION_END)) {
continue;
}
if (current == '@' && modifiedIndices.contains(j + base)) {
newArgument.append(',');
} else {
newArgument.append(current);
}
}
return newArgument;
}

private String replaceCommas(String single, Set<Integer> modifiedIndices) {
StringBuilder newString = new StringBuilder();
boolean inSpecialRegion = false;
for (int i = 0; i < single.length(); i++) {
char current = single.charAt(i);
char tobeAdded = current;
if (current == REGION_BEGIN && !inSpecialRegion) {
inSpecialRegion = true;
} else if (current == REGION_END && inSpecialRegion) {
inSpecialRegion = false;
} else if (inSpecialRegion && current == ',') {
tobeAdded = '@';
modifiedIndices.add(i);
}
newString.append(tobeAdded);
}
return newString.toString();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public class OptionsParser {
private final OptionSpec<String> mutators;
private final OptionSpec<String> features;
private final OptionSpec<String> jvmArgs;
private final CommaAwareArgsProcessor jvmArgsProcessor;
private final OptionSpec<Float> timeoutFactorSpec;
private final OptionSpec<Long> timeoutConstSpec;
private final OptionSpec<String> excludedMethodsSpec;
Expand Down Expand Up @@ -205,9 +206,10 @@ public OptionsParser(Predicate<String> dependencyFilter) {
.describedAs("comma separated list of features to enable/disable.");

this.jvmArgs = parserAccepts(CHILD_JVM).withRequiredArg()
.withValuesSeparatedBy(',')
.describedAs("comma separated list of child JVM args");

this.jvmArgsProcessor = new CommaAwareArgsProcessor(jvmArgs);

this.detectInlinedCode = parserAccepts(USE_INLINED_CODE_DETECTION)
.withOptionalArg()
.ofType(Boolean.class)
Expand Down Expand Up @@ -408,7 +410,7 @@ private ParseResult parseCommandLine(final ReportOptions data,
data.setMutators(this.mutators.values(userArgs));
data.setFeatures(this.features.values(userArgs));
data.setDependencyAnalysisMaxDistance(this.depth.value(userArgs));
data.addChildJVMArgs(this.jvmArgs.values(userArgs));
data.addChildJVMArgs(this.jvmArgsProcessor.values(userArgs));

data.setFullMutationMatrix(this.fullMutationMatrixSpec.value(userArgs));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.pitest.mutationtest.commandline;

import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.pitest.mutationtest.config.ConfigOption;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@RunWith(Parameterized.class)
public class CommaAwareArgsProcessorTest {

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ Arrays.asList("--unrelated", "a"), Collections.emptyList() },
{ Arrays.asList("--jvmArgs", "a"), Arrays.asList("a") },
{ Arrays.asList("--jvmArgs", "a,b,c,d"), Arrays.asList("a","b","c","d") },
{ Arrays.asList("--jvmArgs", "a;b,c;d"), Arrays.asList("a;b","c;d") },
{ Arrays.asList("--jvmArgs", "{a,b,c,d}"), Arrays.asList("a,b,c,d") },
{ Arrays.asList("--jvmArgs", "{a;b;c;d}"), Arrays.asList("{a;b;c;d}") }, // no separators so region markers will not be removed
{ Arrays.asList("--jvmArgs", "{a,b},{c,d},{e,f}"), Arrays.asList("a,b","c,d","e,f") },
{ Arrays.asList("--jvmArgs", "{a,b@c,d}"), Arrays.asList("a,b@c,d") }, // pre-existing '@' will not be replaced with ","
});
}

private final List<String> input;
private final List<String> expected;


public CommaAwareArgsProcessorTest(List<String> input, List<String> expected) {
this.input = input;
this.expected = expected;
}

@Test
public void parseArgs() {
OptionParser optionParser = new OptionParser();
ArgumentAcceptingOptionSpec<String> args = optionParser
.accepts(ConfigOption.CHILD_JVM.getParamName())
.withRequiredArg()
.describedAs("jvm args for child JVM");
ArgumentAcceptingOptionSpec<String> unrelatedArgs = optionParser
.accepts("unrelated")
.withRequiredArg()
.describedAs("unrelatedArgs");

final OptionSet userArgs = optionParser.parse(this.input.toArray(new String[0]));
Assert.assertEquals(this.expected, new CommaAwareArgsProcessor(args).values(userArgs));
}

}

0 comments on commit 874ad99

Please sign in to comment.