All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.maven.GenerateMojo Maven / Gradle / Ivy

Go to download

Plugin used to run EvoSuite to automatically generate high coverage JUnit tests

There is a newer version: 1.0.6
Show newest version
/**
 * 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 .
 */
package org.evosuite.maven;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.eclipse.aether.RepositorySystemSession;
import org.evosuite.Properties;
import org.evosuite.maven.util.EvoSuiteRunner;
import org.evosuite.maven.util.FileUtils;
import org.evosuite.maven.util.HistoryChanges;
import org.evosuite.utils.SpawnProcessKeepAliveChecker;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Generate JUnit tests
 */
@Mojo( name = "generate" , requiresDependencyResolution = ResolutionScope.TEST, requiresDependencyCollection = ResolutionScope.TEST)
@Execute(phase = LifecyclePhase.COMPILE)
public class GenerateMojo extends AbstractMojo {

	/**
	 * Total Memory (in MB) that CTG will use
	 */
	@Parameter( property = "memoryInMB", defaultValue = "800" )
	private int memoryInMB;

	/**
	 * Number of cores CTG will use
	 */
	@Parameter( property = "cores", defaultValue = "1" )
	private int numberOfCores;

	/**
	 * Comma ',' separated list of CUTs to use in CTG. If none specified, then test all classes
	 */
	@Parameter( property = "cuts" )
	private String cuts;

	/**
	 * Absolute path to a file having the list of CUTs specified. This is needed for operating
	 * systems like Windows that have constraints on the size of input parameters and so could
	 * not use "cuts" parameter instead if too many CUTs are specified
	 */
	@Parameter( property = "cutsFile")
	private String cutsFile;

	/**
	 * How many minutes to allocate for each class
	 */
	@Parameter( property = "timeInMinutesPerClass", defaultValue = "2" )
	private int timeInMinutesPerClass;

	/**
	 * How many minutes to allocate for each project/module. If this parameter is not set, then the total time will be timeInMinutesPerClass x number_of_classes
	 */
	@Parameter( property = "timeInMinutesPerProject", defaultValue = "0" )
	private int timeInMinutesPerProject;

	/**
	 * Coverage criterion. Can define more than one criterion by using a ':' separated list
	 */
	// FIXME would be nice to have the value of Properties.CRITERION but seems to be not possible
	@Parameter( property = "criterion", defaultValue = "LINE:BRANCH:EXCEPTION:WEAKMUTATION:OUTPUT:METHOD:METHODNOEXCEPTION:CBRANCH" )
	private String criterion;

	@Parameter(property = "spawnManagerPort", defaultValue = "")
	private Integer spawnManagerPort;

	@Parameter( property = "extraArgs" , defaultValue = "")
	private String extraArgs;

	/**
	 * Schedule used to run CTG (SIMPLE, BUDGET, SEEDING, BUDGET_AND_SEEDING, HISTORY)
	 */
	@Parameter( property = "schedule", defaultValue = "BUDGET" )
	private String schedule;

	@Parameter(defaultValue = "${project}", required = true, readonly = true)
	private MavenProject project;

	@Parameter(defaultValue = "${plugin.artifacts}", required = true, readonly = true)
	private List artifacts;

	@Component
	private ProjectBuilder projectBuilder;

	@Parameter(defaultValue="${repositorySystemSession}", required = true, readonly = true)
	private RepositorySystemSession repoSession;

	/**
	 * Defines files in the source directories to include (all .java files by default).
	 */
	private String[] includes = {"**/*.java"};

	/**
	 * Defines which of the included files in the source directories to exclude (non by default).
	 */
	private String[] excludes;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException{

		getLog().info("Going to generate tests with EvoSuite");
		getLog().info("Total memory: "+memoryInMB+"mb");
		getLog().info("Time per class: "+timeInMinutesPerClass+" minutes");
		getLog().info("Number of used cores: "+numberOfCores);
		
		if(cuts!=null){
			getLog().info("Specified classes under test: "+cuts);
		}
		
		String target = null;
		String cp = null;
		
		try {

			//the targets we want to generate tests for, ie the CUTs
			for(String element : project.getCompileClasspathElements()){
				if(element.endsWith(".jar")){  // we only target what has been compiled to a folder
					continue;
				}

				File file = new File(element);
				if(!file.exists()){
					/*
					 * don't add to target an element that does not exist
					 */
					continue;
				}

				if(! file.getAbsolutePath().startsWith(project.getBasedir().getAbsolutePath()) ){
					/*
						This can happen in multi-module projects when module A has dependency on
						module B. Then, both A and B source folders will end up on compile classpath,
						although we are interested only in A
					 */
					continue;
				}

				if(target == null){
					target = element;
				} else {
					target = target + File.pathSeparator + element;
				}
			}

			//build the classpath
			Set alreadyAdded = new HashSet<>();
			for(String element : project.getTestClasspathElements()){
				if(element.toLowerCase().contains("powermock")){
					//PowerMock just leads to a lot of troubles, as it includes tools.jar code
					getLog().warn("Skipping PowerMock dependency at: "+element);
					continue;
				}
				if(element.toLowerCase().contains("jmockit")){
					//JMockit has same issue
					getLog().warn("Skipping JMockit dependency at: "+element);
					continue;
				}
				getLog().debug("TEST ELEMENT: "+element);
				cp = addPathIfExists(cp, element, alreadyAdded);
			}

		} catch (DependencyResolutionRequiredException e) {
			getLog().error("Error: "+e.getMessage(),e);
			return;
		}

		File basedir = project.getBasedir();
		
		getLog().info("Target: "+target);
		getLog().debug("Classpath: "+cp);
		getLog().info("Basedir: "+basedir.getAbsolutePath());
		if(target==null || cp==null || basedir==null){
			getLog().info("Nothing to test");
			return;
		}

		runEvoSuiteOnSeparatedProcess(target, cp, basedir.getAbsolutePath());

	}

	private String addPathIfExists(String cp, String element, Set alreadyExist) {
		File file = new File(element);
		if(!file.exists()){
            /*
             * don't add to CP an element that does not exist
             */
			return cp;
        }

		if(alreadyExist.contains(element)){
			return cp;
		}

		alreadyExist.add(element);

		if(cp == null){
            cp = element;
        } else {
            cp = cp + File.pathSeparator + element;
        }
		return cp;
	}

	private void runEvoSuiteOnSeparatedProcess(String target, String cp, String dir) throws MojoFailureException {
			
		List params = new ArrayList<>();
		params.add("-continuous");
		params.add("execute");
		params.add("-target");
		params.add(target);
		params.add("-Dcriterion=" + criterion);
		params.add("-Dctg_schedule=" + schedule);
		if (schedule.toUpperCase().equals(Properties.AvailableSchedule.HISTORY.toString())) {
			try {
				List files = FileUtils.scan(this.project.getCompileSourceRoots(), this.includes, this.excludes);
				HistoryChanges.keepTrack(dir, files);
			} catch (Exception e) {
				throw new MojoFailureException("", e);
			}

			params.add("-Dctg_history_file=" + dir + File.separator + Properties.CTG_DIR + File.separator + "history_file");
		}
		params.add("-Dctg_memory="+memoryInMB);
		params.add("-Dctg_cores="+numberOfCores);

		int port;
		if(spawnManagerPort != null) {
			SpawnProcessKeepAliveChecker.getInstance().registerToRemoteServerAndDieIfFails(spawnManagerPort);
			port = spawnManagerPort;
		} else {
			port = SpawnProcessKeepAliveChecker.getInstance().startServer();
		}
		params.add("-Dspawn_process_manager_port=" + port);


		if (timeInMinutesPerProject != 0) {
			params.add("-Dctg_time="+timeInMinutesPerProject);
			params.add("-Dctg_min_time_per_job="+timeInMinutesPerClass);
		} else {
			params.add("-Dctg_time_per_class="+timeInMinutesPerClass); // there is no time limit, so test all classes X minutes
		}
		if(cuts!=null){
			params.add("-Dctg_selected_cuts="+cuts);
		}
		if(cutsFile!=null){
			params.add("-Dctg_selected_cuts_file_location="+cutsFile);
		}

		if(extraArgs!=null && !extraArgs.isEmpty()){

			String args = "";

			//note this does not for properly for parameters with strings using spaces
			String[] tokens = extraArgs.split(" ");
			for(String token : tokens){
				token = token.trim();
				if(token.isEmpty()){
					continue;
				}
				if(!token.startsWith("-D")){
					getLog().error("Invalid extra argument \""+token+"\". It should start with a -D");
				} else {
					args += " " +token;
				}
			}

			params.add("-Dctg_extra_args=\""+args+"\"");
		}

		String path = writeClasspathToFile(cp);
		params.add("-DCP_file_path="+path);
		//params.add("-DCP=" + cp); //this did not work properly on Windows
		
		EvoSuiteRunner runner = new EvoSuiteRunner(getLog(),artifacts,projectBuilder,repoSession);
		runner.registerShutDownHook();
		boolean ok = runner.runEvoSuite(dir,params);

		if(spawnManagerPort != null) {
			SpawnProcessKeepAliveChecker.getInstance().unRegister();
		} else {
			SpawnProcessKeepAliveChecker.getInstance().stopServer();
		}

		if(!ok){
			throw new MojoFailureException("Failed to correctly execute EvoSuite");
		}
	}

	private String writeClasspathToFile(String classpath) {

		try {
			File file = File.createTempFile("EvoSuite_classpathFile",".txt");
			file.deleteOnExit();

			BufferedWriter out = new BufferedWriter(new FileWriter(file));
			String line = classpath;
			out.write(line);
			out.newLine();
			out.close();

			return file.getAbsolutePath();

		} catch (Exception e) {
			throw new IllegalStateException("Failed to create tmp file for classpath specification: "+e.getMessage());
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy