blob: 7d3ec96fa69667e813b06a8b0f49a5fd146e7b3b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.surefire.its.fixture;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.maven.shared.utils.io.FileUtils;
import org.apache.maven.shared.verifier.VerificationException;
import org.apache.maven.shared.verifier.Verifier;
import org.apache.maven.shared.verifier.util.ResourceExtractor;
import static java.util.Collections.singletonMap;
import static java.util.Collections.unmodifiableList;
/**
* Encapsulate all needed features to start a maven run
* <br>
*
* @author Kristian Rosenvold
*/
public final class MavenLauncher {
private static final File SETTINGS_XML_PATH = settingsXmlPath();
private final List<String> cliOptions = new ArrayList<>();
private final List<String> goals = new ArrayList<>();
private final Map<String, String> envVars = new HashMap<>();
private final Map<String, String> props = new LinkedHashMap<>();
private File unpackedAt;
private Verifier verifier;
private OutputValidator validator;
private final Class<?> testCaseBeingRun;
private final String resourceName;
private final String suffix;
private final String[] cli;
private boolean expectFailure;
MavenLauncher(Class<?> testClass, String resourceName, String suffix, String[] cli) {
this.testCaseBeingRun = testClass;
this.resourceName = resourceName;
this.suffix = suffix != null ? suffix : "";
this.cli = cli == null ? null : cli.clone();
resetGoals();
resetCliOptions();
}
public MavenLauncher(Class<?> testClass, String resourceName, String suffix) {
this(testClass, resourceName, suffix, null);
}
public File getUnpackedAt() {
return ensureUnpacked();
}
private File ensureUnpacked() {
if (unpackedAt == null) {
unpackedAt = simpleExtractResources(testCaseBeingRun, resourceName);
}
return unpackedAt;
}
public void moveUnpackTo(File dest) throws IOException {
FileUtils.deleteDirectory(dest);
//noinspection ResultOfMethodCallIgnored
boolean moved = getUnpackedAt().renameTo(dest);
if (!moved) {
String fileEncoding = System.getProperty("file.encoding");
String os = System.getProperty("os.name");
String version = System.getProperty("os.version");
String arch = System.getProperty("os.arch");
throw new IOException("Could not move " + getUnpackedAt() + " to " + dest + " (file.encoding="
+ fileEncoding + ", " + os + " " + version + " " + arch + ").");
}
unpackedAt = dest;
}
private void resetGoals() {
goals.clear();
}
private void addCliOption(String cliOption) {
cliOptions.add(cliOption);
}
private StackTraceElement findTopElemenent(StackTraceElement[] stackTrace, Class<?> testClassToLookFor) {
StackTraceElement bestmatch = null;
for (StackTraceElement stackTraceElement : stackTrace) {
if (stackTraceElement.getClassName().equals(testClassToLookFor.getName())) {
bestmatch = stackTraceElement;
}
}
return bestmatch;
}
private static StackTraceElement[] getStackTraceElements() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
return e.getStackTrace();
}
}
public void reset() {
resetGoals();
resetCliOptions();
}
private void resetCliOptions() {
cliOptions.clear();
}
public MavenLauncher getSubProjectLauncher(String subProject) {
MavenLauncher mavenLauncher =
new MavenLauncher(testCaseBeingRun, resourceName + File.separator + subProject, suffix, cli);
mavenLauncher.unpackedAt = new File(ensureUnpacked(), subProject);
return mavenLauncher;
}
public OutputValidator getSubProjectValidator(String subProject) throws VerificationException {
String subProjectBasedir = getValidator().getSubFile(subProject).getAbsolutePath();
String settingsXml = settingsXmlPath().getAbsolutePath();
Verifier subProjectVerifier = createVerifier(subProjectBasedir, settingsXml, null);
return new OutputValidator(subProjectVerifier);
}
public MavenLauncher addEnvVar(String key, String value) {
envVars.put(key, value);
return this;
}
public MavenLauncher verifyFileNotPresent(String subFile) throws VerificationException {
getVerifier().verifyFileNotPresent(getValidator().getSubFile(subFile).getAbsolutePath());
return this;
}
public MavenLauncher showErrorStackTraces() {
addCliOption("-e");
return this;
}
public MavenLauncher debugLogging() {
addCliOption("-X");
return this;
}
public MavenLauncher failNever() {
addCliOption("-fn");
return this;
}
public MavenLauncher offline() {
addCliOption("-o");
return this;
}
public MavenLauncher skipClean() {
writeGoal("-Dclean.skip=true" /* for maven-clean-plugin < 3.0 */);
writeGoal("-Dmaven.clean.skip=true" /* for maven-clean-plugin 3.0+ */);
return this;
}
public MavenLauncher addGoal(String goal) {
writeGoal(goal);
return this;
}
public FailsafeOutputValidator executeVerify() {
return new FailsafeOutputValidator(conditionalExec("verify"));
}
public OutputValidator executeTest() {
return conditionalExec("test");
}
List<String> getGoals() {
return unmodifiableList(goals);
}
private void writeGoal(String newGoal) {
if (newGoal != null && newGoal.startsWith("-D")) {
String sysPropKey = newGoal.contains("=") ? newGoal.substring(0, newGoal.indexOf('=')) : newGoal;
String sysPropStarter = sysPropKey + "=";
for (ListIterator<String> it = goals.listIterator(); it.hasNext(); ) {
String goal = it.next();
if (goal.equals(sysPropKey) || goal.startsWith(sysPropStarter)) {
System.out.printf(
"[WARNING] System property already exists '%s'. Overriding to '%s'.%n", goal, newGoal);
it.set(newGoal);
return;
}
}
}
goals.add(newGoal);
}
private OutputValidator conditionalExec(String goal) {
OutputValidator verify;
try {
verify = execute(goal);
} catch (SurefireVerifierException exc) {
if (expectFailure) {
return getValidator();
} else {
throw exc;
}
}
if (expectFailure) {
throw new RuntimeException("Expecting build failure, have got none!");
}
return verify;
}
public MavenLauncher withFailure() {
expectFailure = true;
return this;
}
public OutputValidator execute(String goal) {
addGoal(goal);
return executeCurrentGoals();
}
public OutputValidator executeCurrentGoals() {
try {
List<String> goalsAndProps = new ArrayList<>(goals);
for (Entry<String, String> e : props.entrySet()) {
String key = e.getKey();
String val = e.getValue();
goalsAndProps.add(val == null ? "-D" + key : "-D" + key + "=" + val);
}
getVerifier().setCliOptions(cliOptions);
getVerifier().executeGoals(goalsAndProps, envVars);
return getValidator();
} catch (VerificationException e) {
throw new SurefireVerifierException(e.getLocalizedMessage(), e);
} finally {
getVerifier().resetStreams();
}
}
public MavenLauncher activateProfile(String profile) {
return addGoal("-P" + profile);
}
public MavenLauncher sysProp(String key, String value) {
return sysProp(singletonMap(key, value));
}
public MavenLauncher sysProp(Map<String, String> properties) {
props.putAll(properties);
return this;
}
public MavenLauncher sysProp(String key, boolean value) {
return sysProp(singletonMap(key, Boolean.toString(value)));
}
public MavenLauncher sysProp(String key, int value) {
return sysProp(singletonMap(key, Integer.toString(value)));
}
public MavenLauncher sysProp(String key, double value) {
return sysProp(singletonMap(key, Double.toString(value)));
}
public MavenLauncher showExceptionMessages() {
addCliOption("-e");
return this;
}
public MavenLauncher deleteSiteDir() {
try {
FileUtils.deleteDirectory(getValidator().getSubFile("site"));
} catch (IOException e) {
throw new SurefireVerifierException(e);
}
return this;
}
public OutputValidator getValidator() {
if (validator == null) {
validator = new OutputValidator(getVerifier());
}
return validator;
}
public void setForkJvm(boolean forkJvm) {
getVerifier().setForkJvm(forkJvm);
}
public String getLocalRepository() {
return getVerifier().getLocalRepository();
}
public void setAutoclean(boolean autoclean) {
getVerifier().setAutoclean(autoclean);
}
public void setLogFileName(String logFileName) {
getVerifier().setLogFileName(logFileName);
}
private Verifier getVerifier() {
if (verifier == null) {
try {
String unpackedPath = ensureUnpacked().getAbsolutePath();
String settingsXml = SETTINGS_XML_PATH.getAbsolutePath();
verifier = createVerifier(unpackedPath, settingsXml, cli);
} catch (VerificationException e) {
throw new RuntimeException(e);
}
}
return verifier;
}
private File simpleExtractResources(Class<?> cl, String resourcePath) {
if (!resourcePath.startsWith("/")) {
resourcePath = "/" + resourcePath;
}
File tempDir = getUnpackDir();
File testDir = new File(tempDir, resourcePath);
try {
File parentPom = new File(tempDir.getParentFile(), "pom.xml");
if (!parentPom.exists()) {
URL resource = cl.getResource("/pom.xml");
FileUtils.copyURLToFile(resource, parentPom);
}
FileUtils.deleteDirectory(testDir);
File file = ResourceExtractor.extractResourceToDestination(cl, resourcePath, tempDir, true);
return file.getCanonicalFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private File getUnpackDir() {
String tempDirPath = System.getProperty("maven.test.tmpdir", System.getProperty("java.io.tmpdir"));
return new File(tempDirPath, testCaseBeingRun.getSimpleName() + "_" + getTestMethodName() + suffix);
}
public File getArtifactPath(String gid, String aid, String version, String ext) {
return new File(verifier.getArtifactPath(gid, aid, version, ext));
}
String getTestMethodName() {
// dirty. Im sure we can use junit4 rules to attach testname to thread instead
StackTraceElement[] stackTrace = getStackTraceElements();
StackTraceElement topInTestClass;
topInTestClass = findTopElemenent(stackTrace, testCaseBeingRun);
if (topInTestClass == null) {
// Look in superclass...
topInTestClass = findTopElemenent(stackTrace, testCaseBeingRun.getSuperclass());
}
if (topInTestClass != null) {
return topInTestClass.getMethodName();
}
throw new IllegalStateException("Cannot find " + testCaseBeingRun.getName() + "in stacktrace");
}
private static Verifier createVerifier(String basedir, String settingsFile, String[] defaultCliOptions)
throws VerificationException {
Verifier verifier = defaultCliOptions == null
? new Verifier(basedir, settingsFile, false)
: new Verifier(basedir, settingsFile, false, defaultCliOptions);
verifier.getVerifierProperties().setProperty("use.mavenRepoLocal", "true");
return verifier;
}
private static File settingsXmlPath() {
try {
return new File(System.getProperty("maven.settings.file")).getCanonicalFile();
} catch (IOException e) {
throw new IllegalStateException(e.getLocalizedMessage(), e);
}
}
}