Java tutorial
/** * Copyright (C) 2010-2016 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 <http://www.gnu.org/licenses/>. */ 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.utils.LoggingUtils; /** * Note: we cannot call EvoSuite directly on same JVM, like the following: <p> <code> ContinuousTestGeneration ctg = new ContinuousTestGeneration(target,cp,prefix,conf); ctg.execute(); </code> <p> * 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 * * <p> * 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<Artifact> artifacts; private final ProjectBuilder projectBuilder; private final RepositorySystemSession repoSession; private Process process; public EvoSuiteRunner(Log logger, List<Artifact> 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<String> params) { List<String> 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<String> 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<String> cmd = new ArrayList<>(); cmd.add("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<String> 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<File>() { @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<String> cmd = new ArrayList<>(); cmd.add("java"); cmd.add("-jar"); cmd.add("" + evo); return cmd; } private boolean runProcess(String baseDir, List<String> 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"); } }