
org.evosuite.maven.util.EvoSuiteRunner Maven / Gradle / Ivy
Show all versions of evosuite-maven-plugin Show documentation
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
package org.evosuite.maven.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingResult;
import org.eclipse.aether.RepositorySystemSession;
import org.evosuite.EvoSuite;
import org.evosuite.runtime.util.JavaExecCmdUtil;
import org.evosuite.utils.LoggingUtils;
/**
* Note: we cannot call EvoSuite directly on same JVM, like the following:
ContinuousTestGeneration ctg = new ContinuousTestGeneration(target,cp,prefix,conf);
ctg.execute();
* Reason is that Maven uses its own classloaders, and setting their classpath
* becomes very messy, if possible at all.
* So, we need to call EvoSuite on separated process
*
*
* TODO: most likely this code should be moved to a library, as other plugins (eg Netbeans)
* will use it as well
*
*/
public class EvoSuiteRunner {
public static final String EVOSUITE_HOME_VARIABLE = "EVOSUITE_HOME";
/**
* The maven logger of the plugin
*/
private final Log logger;
private final List artifacts;
private final ProjectBuilder projectBuilder;
private final RepositorySystemSession repoSession;
private Process process;
public EvoSuiteRunner(Log logger, List artifacts,
ProjectBuilder projectBuilder, RepositorySystemSession repoSession) {
super();
this.logger = logger;
this.artifacts = artifacts;
this.projectBuilder = projectBuilder;
this.repoSession = repoSession;
}
public void registerShutDownHook(){
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
if(process != null){
process.destroy();
}
}
});
}
/**
* This is blocking
* @param params
* @return
*/
public boolean runEvoSuite(String dir, List params){
List cmd = getCommandToRunEvoSuite();
if(cmd==null){
return false;
}
cmd.addAll(params);
return runProcess(dir,cmd);
}
/**
* We run the EvoSuite that is provided with the plugin
*
* @return
*/
private List getCommandToRunEvoSuite(){
logger.debug("EvoSuite Maven Plugin Artifacts: "+Arrays.toString(artifacts.toArray()));
Artifact evosuite = null;
for(Artifact art : artifacts){
//first find the main EvoSuite jar among the dependencies
if(art.getArtifactId().equals("evosuite-master")){
evosuite = art;
break;
}
}
if(evosuite==null){
logger.error("CRITICAL ERROR: plugin can detect EvoSuite executable");
return null;
}
logger.debug("EvoSuite located at: "+evosuite.getFile());
/*
* now, build a project descriptor for evosuite, which is needed to
* query all of its dependencies
*/
DefaultProjectBuildingRequest req = new DefaultProjectBuildingRequest();
req.setRepositorySession(repoSession);
req.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
req.setSystemProperties(System.getProperties());
req.setResolveDependencies(true);
ProjectBuildingResult res;
try {
res = projectBuilder.build(evosuite, req);
} catch (ProjectBuildingException e) {
logger.error("Failed: "+e.getMessage(),e);
return null;
}
//build the classpath to run EvoSuite
String cp = evosuite.getFile().getAbsolutePath();
for(Artifact dep : res.getProject().getArtifacts()){
cp += File.pathSeparator+dep.getFile().getAbsolutePath();
}
logger.debug("EvoSuite classpath: "+cp);
String entryPoint = EvoSuite.class.getName();
List cmd = new ArrayList<>();
cmd.add(JavaExecCmdUtil.getJavaBinExecutablePath()/*"java"*/);
cmd.add("-D" + LoggingUtils.USE_DIFFERENT_LOGGING_XML_PARAMETER + "=logback-ctg-entry.xml");
cmd.add("-Dlogback.configurationFile=logback-ctg-entry.xml");
cmd.add("-cp");
cmd.add(cp);
cmd.add(entryPoint);
return cmd;
}
/**
* Check if there is a valid installation of EvoSuite on the current machine
*
*/
@Deprecated
private List getCommandToRunExternalEvoSuite(){
/*
* Note: we keep this code in case we want to implement
* the option of running an external EvoSuite
*/
String home = System.getenv(EVOSUITE_HOME_VARIABLE);
if(home==null || home.isEmpty()){
logger.error("Need to set the environment variable "+EVOSUITE_HOME_VARIABLE+
" pointing to where EvoSuite is installed");
return null;
}
File folder = new File(home);
if(!folder.exists()){
logger.error("EvoSuite home "+home+" does not exist");
return null;
}
if(!folder.isDirectory()){
logger.error("EvoSuite home "+home+" is not a folder");
return null;
}
/*
* TODO: this will need to be changed once we finalize how to
* distribute EvoSuite
*/
File[] jars = folder.listFiles(new FilenameFilter(){
@Override
public boolean accept(File dir, String name) {
String lc = name.toLowerCase();
return lc.startsWith("evosuite") && lc.endsWith(".jar");
}});
if(jars.length == 0){
logger.error("No evosuite jar in "+folder.getPath());
return null;
}
if(jars.length > 1){
/*
* sort in way the largest file is first.
* this is needed if we put as HOME where we compile EvoSuite
*/
Arrays.sort(jars,new Comparator(){
@Override
public int compare(File o1, File o2) {
return (int) (o2.length() - o1.length());
}});
}
String evo = jars[0].getAbsolutePath();
logger.info("Going to use EvoSuite jar: "+evo);
List cmd = new ArrayList<>();
cmd.add(JavaExecCmdUtil.getJavaBinExecutablePath()/*"java"*/);
cmd.add("-jar");
cmd.add(""+evo);
return cmd;
}
private boolean runProcess(String baseDir, List cmd){
try{
if(baseDir==null){
baseDir = System.getProperty("user.dir");
}
logger.debug("Working directory: "+baseDir);
if(logger.isDebugEnabled()) {
logger.debug("Going to execute command: " + String.join(" ", cmd));
}
File dir = new File(baseDir);
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(dir);
builder.redirectErrorStream(true);
process = builder.start();
handleProcessOutput(process,logger);
//output
int exitCode = process.waitFor();
if(exitCode != 0){
logger.error("Error in EvoSuite");
return false;
} else {
logger.debug("EvoSuite terminated");
}
} catch (IOException e) {
logger.error("Failed to start EvoSuite: "+e.getMessage(), e);
return false;
} catch (InterruptedException e) {
if(process!=null){
try {
//be sure streamers are closed, otherwise process might hang on Windows
process.getOutputStream().close();
process.getInputStream().close();
process.getErrorStream().close();
} catch (Exception t){
logger.error("Failed to close process stream: "+t.toString());
}
process.destroy();
}
return false;
}
process = null;
return true;
}
private void handleProcessOutput(final Process process, final Log logger){
Thread reader = new Thread(){
@Override
public void run(){
try{
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
while(!this.isInterrupted()){
String line = in.readLine();
if(line!=null && !line.isEmpty()){
logger.info(line);
}
}
} catch(Exception e){
logger.debug("Exception while reading spawn process output: "+ e.toString());
}
}
};
reader.start();
logger.debug("Started thread to read spawn process output");
}
}