Skip to content

Commit

Permalink
Merge pull request #1275 from hcoles/feature/modulepath
Browse files Browse the repository at this point in the history
Pass process parameters by file
  • Loading branch information
hcoles authored Feb 4, 2025
2 parents 1b2625b + fdb184e commit 40fa0fb
Show file tree
Hide file tree
Showing 14 changed files with 190 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,12 @@ public class OptionsParser {
private final ArgumentAcceptingOptionSpec<Boolean> exportLineCoverageSpec;
private final OptionSpec<String> javaExecutable;
private final OptionSpec<KeyValuePair> pluginPropertiesSpec;

// unused but temporarily retained
private final OptionSpec<String> testPluginSpec;
private final ArgumentAcceptingOptionSpec<Boolean> includeLaunchClasspathSpec;

// unused but temporarily retained
private final ArgumentAcceptingOptionSpec<Boolean> useClasspathJarSpec;
private final OptionSpec<File> projectBaseSpec;
private final OptionSpec<String> inputEncoding;
Expand Down Expand Up @@ -463,8 +467,6 @@ private ParseResult parseCommandLine(final ReportOptions data,

data.setIncludeLaunchClasspath(booleanValue(includeLaunchClasspathSpec, userArgs));

data.setUseClasspathJar(booleanValue(useClasspathJarSpec, userArgs));

data.setShouldCreateTimestampedReports(booleanValue(timestampedReportsSpec, userArgs));

data.setNumberOfThreads(this.threadsSpec.value(userArgs));
Expand Down Expand Up @@ -559,7 +561,7 @@ private void setClassPath(final OptionSet userArgs, final ReportOptions data) {
LOG.warning("Unable to read class path file:" + this.classPathFile.value(userArgs).getAbsolutePath() + " - "
+ ioe.getMessage());
}
data.setUseClasspathJar(true);

}
elements.addAll(this.additionalClassPathSpec.values(userArgs));
data.setClassPathElements(elements);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,14 +350,6 @@ public void shouldAcceptFileWithListOfAdditionalClassPathElements() {
assertTrue(actual.contains("/etc/bar"));
}

@Test
public void alsoSetsUseClasspathJarWhenClasspathFileProvided() {
final ClassLoader classLoader = getClass().getClassLoader();
final File classPathFile = new File(classLoader.getResource("testClassPathFile.txt").getFile());
final ReportOptions ro = parseAddingRequiredArgs("--classPathFile",
classPathFile.getAbsolutePath());
assertThat(ro.useClasspathJar()).isTrue();
}

@Test
public void shouldFailWhenNoMutationsSetByDefault() {
Expand Down Expand Up @@ -621,28 +613,10 @@ public void shouldIncludePluginPropertyValuesWhenMultipleKeys() {
assertEquals("2", actual.getFreeFormProperties().getProperty("bar"));
}

@Test
public void shouldDefaultToNotUsingAClasspathJar() {
final ReportOptions actual = parseAddingRequiredArgs();
assertFalse(actual.useClasspathJar());
}

@Test
public void shouldUseClasspathJarWhenFlagSet() {
final ReportOptions actual = parseAddingRequiredArgs("--useClasspathJar");
assertTrue(actual.useClasspathJar());
}

@Test
public void shouldUseClasspathJarWhenTrueSupplied() {
final ReportOptions actual = parseAddingRequiredArgs("--useClasspathJar=true");
assertTrue(actual.useClasspathJar());
}

@Test
public void shouldNotUseClasspathJarWhenFalseSupplied() {
final ReportOptions actual = parseAddingRequiredArgs("--useClasspathJar=false");
assertFalse(actual.useClasspathJar());
public void shouldNotErrorWhenLegacyClasspathJarWhenFlagSet() {
assertThatCode(() -> parseAddingRequiredArgs("--useClasspathJar")).doesNotThrowAnyException();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class CoverageProcess {
public CoverageProcess(final ProcessArgs processArgs,
final CoverageOptions arguments, final ServerSocket socket,
final List<String> testClasses, final Consumer<CoverageResult> handler) {
this.process = new WrappingProcess(socket.getLocalPort(), processArgs,
this.process = WrappingProcess.create(socket.getLocalPort(), processArgs,
CoverageMinion.class);

this.crt = new CommunicationThread(socket, new SendData(arguments, testClasses), new Receive(handler));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ public class ReportOptions {

private String testPlugin = "";

private boolean useClasspathJar;

private Path projectBase;
private Charset inputEncoding;
Expand Down Expand Up @@ -625,14 +624,6 @@ public TestPluginArguments createMinionSettings() {
this.getIncludedTestMethods(), this.skipFailingTests());
}

public boolean useClasspathJar() {
return useClasspathJar;
}

public void setUseClasspathJar(boolean useClasspathJar) {
this.useClasspathJar = useClasspathJar;
}

public Path getProjectBase() {
return projectBase;
}
Expand Down Expand Up @@ -729,7 +720,6 @@ public String toString() {
.add("excludedRunners=" + excludedRunners)
.add("includedTestMethods=" + includedTestMethods)
.add("testPlugin='" + testPlugin + "'")
.add("useClasspathJar=" + useClasspathJar)
.add("projectBase=" + projectBase)
.add("inputEncoding=" + inputEncoding)
.add("outputEncoding=" + outputEncoding)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class MutationTestProcess {

public MutationTestProcess(final ServerSocket socket,
final ProcessArgs processArgs, final MinionArguments arguments) {
this.process = new WrappingProcess(socket.getLocalPort(), processArgs,
this.process = WrappingProcess.create(socket.getLocalPort(), processArgs,
MutationTestMinion.class);

this.idMap = new ConcurrentHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ public AnalysisResult execute(File baseDir, ReportOptions data,

final CoverageOptions coverageOptions = settings.createCoverageOptions();
final LaunchOptions launchOptions = new LaunchOptions(ja,
settings.getJavaExecutable(), createJvmArgs(data), environmentVariables)
.usingClassPathJar(data.useClasspathJar());
settings.getJavaExecutable(), createJvmArgs(data), environmentVariables);

final ProjectClassPaths cps = data.getMutationClassPaths();

Expand Down
164 changes: 164 additions & 0 deletions pitest-entry/src/main/java/org/pitest/process/Java9Process.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package org.pitest.process;

import static java.util.Arrays.asList;
import static org.pitest.functional.prelude.Prelude.or;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

import org.pitest.functional.FCollection;

/**
* Process for java 9+, using file to pass all parameters
*/
public class Java9Process implements WrappingProcess {

private final int port;
private final ProcessArgs processArgs;
private final Class<?> minionClass;
private JavaProcess process;

private final long pid = ProcessHandle.current().pid();

private static int counter = 0;

public Java9Process(int port, ProcessArgs args, Class<?> minionClass) {
this.port = port;
this.processArgs = args;
this.minionClass = minionClass;
}

public void start() throws IOException {
String[] args = { "" + this.port };

ProcessBuilder processBuilder = createProcessBuilder(
this.processArgs.getJavaExecutable(),
this.processArgs.getJvmArgs(),
this.minionClass, asList(args),
this.processArgs.getJavaAgentFinder(),
this.processArgs.getLaunchClassPath());


configureProcessBuilder(processBuilder, this.processArgs.getWorkingDir(),
this.processArgs.getEnvironmentVariables());

Process process = processBuilder.start();
this.process = new JavaProcess(process, this.processArgs.getStdout(),
this.processArgs.getStdErr());
}

public boolean isAlive() {
return process.isAlive();
}

private void configureProcessBuilder(ProcessBuilder processBuilder,
File workingDirectory, Map<String, String> environmentVariables) {
processBuilder.directory(workingDirectory);
Map<String, String> environment = processBuilder.environment();

environment.putAll(environmentVariables);
}

public void destroy() {
this.process.destroy();
}

public JavaProcess getProcess() {
return this.process;
}

private ProcessBuilder createProcessBuilder(String javaProc,
List<String> args, Class<?> mainClass, List<String> programArgs,
JavaAgent javaAgent, String classPath) {
List<String> cmd = createLaunchArgs(javaAgent, args, mainClass,
programArgs, classPath);

removeJacocoAgent(cmd);

try {
// all arguments are passed via a temporary file, thereby avoiding command line length limits
Path argsFile = createArgsFile(cmd);
return new ProcessBuilder(asList(javaProc, "@" + argsFile.toFile().getAbsolutePath()));
} catch (IOException e) {
throw new RuntimeException(e);
}

}
private void removeJacocoAgent(List<String> cmd) {
removeFromClassPath(cmd, line -> line.startsWith("-javaagent") && line.contains("jacoco"));
}

private static void removeFromClassPath(List<String> cmd, Predicate<String> match) {
for (int i = cmd.size() - 1; i >= 0; i--) {
if (match.test(cmd.get(i))) {
cmd.remove(i);
}
}
}

private List<String> createLaunchArgs(JavaAgent agentJarLocator, List<String> args, Class<?> mainClass,
List<String> programArgs, String classPath) {

List<String> cmd = new ArrayList<>();

cmd.add("-classpath");
cmd.add(classPath);

addPITJavaAgent(agentJarLocator, cmd);

cmd.addAll(args);

addLaunchJavaAgents(cmd);

cmd.add(mainClass.getName());
cmd.addAll(programArgs);
return cmd;
}

private Path createArgsFile(List<String> cmd) throws IOException {
// To avoid conflicts with running analysis, we use the PID as part of the file name
// To prevent conflicts between multiple threads counter is used
// All files should be deleted on process exit, although some garbage may be left
// if the process is killed. Files are however created in the system temp directory
// so should be cleaned up on reboot
String name = "pitest-args-" + pid + "-" + nextCounter();
Path args = Files.createTempFile(name, ".args");
args.toFile().deleteOnExit();
Files.write(args, cmd);
return args;
}

private static void addPITJavaAgent(JavaAgent agentJarLocator,
List<String> cmd) {
final Optional<String> jarLocation = agentJarLocator.getJarLocation();
jarLocation.ifPresent(l -> cmd.add("-javaagent:" + l));
}

private static void addLaunchJavaAgents(List<String> cmd) {
RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();
List<String> agents = FCollection.filter(rt.getInputArguments(),
or(isJavaAgentParam(), isEnvironmentSetting()));
cmd.addAll(agents);
}

private static Predicate<String> isEnvironmentSetting() {
return a -> a.startsWith("-D");
}

private static Predicate<String> isJavaAgentParam() {
return a -> a.toLowerCase().startsWith("-javaagent");
}

private static synchronized int nextCounter() {
return counter++;
}

}
19 changes: 1 addition & 18 deletions pitest-entry/src/main/java/org/pitest/process/LaunchOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,20 @@ public class LaunchOptions {
private final List<String> childJVMArgs;
private final JavaExecutableLocator javaExecutable;
private final Map<String, String> environmentVariables;
private final boolean usingClassPathJar;

public LaunchOptions(JavaAgent javaAgentFinder) {
this(javaAgentFinder, new DefaultJavaExecutableLocator(), Collections
.emptyList(), new HashMap<>());
}

public LaunchOptions(JavaAgent javaAgentFinder,
JavaExecutableLocator javaExecutable,
List<String> childJVMArgs,
Map<String, String> environmentVariables) {
this(javaAgentFinder, javaExecutable, childJVMArgs, environmentVariables, false);
}

public LaunchOptions(JavaAgent javaAgentFinder,
JavaExecutableLocator javaExecutable,
List<String> childJVMArgs,
Map<String, String> environmentVariables,
boolean usingClassPathJar) {
Map<String, String> environmentVariables) {
this.javaAgentFinder = javaAgentFinder;
this.childJVMArgs = childJVMArgs;
this.javaExecutable = javaExecutable;
this.environmentVariables = environmentVariables;
this.usingClassPathJar = usingClassPathJar;
}

public JavaAgent getJavaAgentFinder() {
Expand All @@ -67,11 +57,4 @@ public Map<String, String> getEnvironmentVariables() {
return this.environmentVariables;
}

public LaunchOptions usingClassPathJar(boolean useJar) {
return new LaunchOptions(javaAgentFinder, javaExecutable, childJVMArgs, environmentVariables, useJar);
}

public boolean useClasspathJar() {
return usingClassPathJar;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public final class ProcessArgs {
private File workingDir = null;
private String javaExecutable;
private Map<String, String> environmentVariables;
private boolean useClasspathJar = false;

private ProcessArgs(final String launchClassPath) {
this.launchClassPath = launchClassPath;
Expand Down Expand Up @@ -92,16 +91,12 @@ public String getJavaExecutable() {
return this.javaExecutable;
}

public boolean useClasspathJar() {
return useClasspathJar;
}


public ProcessArgs andLaunchOptions(final LaunchOptions launchOptions) {
this.jvmArgs = launchOptions.getChildJVMArgs();
this.javaAgentFinder = launchOptions.getJavaAgentFinder();
this.javaExecutable = launchOptions.getJavaExecutable();
this.environmentVariables = launchOptions.getEnvironmentVariables();
this.useClasspathJar = launchOptions.useClasspathJar();
return this;
}

Expand Down
Loading

0 comments on commit 40fa0fb

Please sign in to comment.