io.qameta.allure.maven.AllureGenerateMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of allure-maven Show documentation
Show all versions of allure-maven Show documentation
Maven integration with Allure reporting
The newest version!
/*
* Copyright 2016-2024 Qameta Software Inc
*
* Licensed 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 io.qameta.allure.maven;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.reporting.MavenReportException;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import static io.qameta.allure.maven.AllureCommandline.ALLURE_DEFAULT_VERSION;
/**
* @author Dmitry Baev [email protected] Date: 04.08.15
*/
@SuppressWarnings({"PMD.GodClass", "ClassFanOutComplexity"})
public abstract class AllureGenerateMojo extends AllureBaseMojo {
public static final String ALLURE_OLD_PROPERTIES = "allure.properties";
public static final String ALLURE_NEW_PROPERTIES = "report.properties";
public static final String CATEGORIES_FILE_NAME = "categories.json";
/**
* The project build directory. For maven projects it is usually the target folder.
*/
@Parameter(defaultValue = "${project.build.directory}", readonly = true)
protected String buildDirectory;
/**
* The project reporting output directory. For maven projects it is usually the target/site
* folder.
*/
@Parameter(defaultValue = "${project.reporting.outputDirectory}", readonly = true)
protected String reportingOutputDirectory;
/**
* The path to Allure results directory. In general it is the directory created by allure
* adaptor and contains allure xml files and attachments. This path can be relative from build
* directory (for maven it is the target directory) or absolute (absolute only for
* report
mojo). Will be ignored for bulk
mojo.
*/
@Parameter(property = "allure.results.directory", defaultValue = "allure-results/")
protected String resultsDirectory;
/**
* The version on Allure report to generate.
*/
@Parameter(property = "report.version")
protected String reportVersion;
@Parameter(property = "allure.report.directory",
defaultValue = "${project.reporting.outputDirectory}/allure-maven-plugin")
protected String reportDirectory;
/**
* Report timeout parameter in seconds.
*/
@Parameter(property = "allure.report.timeout", defaultValue = "60")
protected int reportTimeout;
/**
* This is key-value map which defines executor.json file content. Default content:
* {"buildName":"${project.name}","name":"Maven","type":"maven"}
*/
@Parameter
protected final Map executorInfo = new HashMap<>();
/**
* The path to the allure.properties file.
*/
@Parameter(defaultValue = "report.properties")
protected String propertiesFilePath;
@Parameter(property = "allure.install.directory", defaultValue = "${project.basedir}/.allure")
protected String installDirectory;
@Parameter(property = "allure.download.url")
protected String allureDownloadUrl;
@Parameter(property = "session", defaultValue = "${session}", readonly = true)
protected MavenSession session;
@Component(role = SettingsDecrypter.class)
protected SettingsDecrypter decrypter;
@Component
protected DependencyResolver dependencyResolver;
/**
* The additional Allure properties such as issue tracker pattern.
*/
@Parameter
protected Map properties = new HashMap<>();
/**
* The report generation mode.
*/
@Parameter
protected Boolean singleFile;
/**
* {@inheritDoc}
*/
@Override
protected String getOutputDirectory() {
return getReportDirectory();
}
/**
* {@inheritDoc}
*/
@Override
protected void executeReport(final Locale locale) throws MavenReportException {
try {
this.installAllure();
getLog().info(String.format("Generate Allure report (%s) with version %s",
getMojoName(), reportVersion != null ? reportVersion : ALLURE_DEFAULT_VERSION));
getLog().info("Generate Allure report to " + getReportDirectory());
final List inputDirectories = getInputDirectories();
if (inputDirectories.isEmpty()) {
getLog().warn(
"Allure report was skipped because there is no results directories found.");
return;
}
this.loadProperties(inputDirectories);
this.loadCategories(inputDirectories);
this.copyExecutorInfo(inputDirectories);
this.generateReport(inputDirectories);
render(getSink(), getName(locale));
} catch (Exception e) {
throw new MavenReportException("Could not generate the report", e);
}
}
private void copyExecutorInfo(final List inputDirectories) throws IOException {
addPropertyIfAbsent("name", "Maven");
addPropertyIfAbsent("type", "maven");
addPropertyIfAbsent("buildName", getProject() == null ? "N/A" : getProject().getName());
final ObjectMapper mapper = new ObjectMapper();
for (Path dir : inputDirectories) {
final Path executorInfoFile = dir.resolve("executor.json");
mapper.writeValue(executorInfoFile.toFile(), executorInfo);
}
}
private void addPropertyIfAbsent(final String key, final String value) {
if (!executorInfo.containsKey(key)) {
executorInfo.put(key, value);
}
}
private void loadCategories(final List inputDirectories)
throws URISyntaxException, IOException, DependencyResolutionRequiredException {
final URL categoriesUrl = createProjectClassLoader().getResource(CATEGORIES_FILE_NAME);
if (categoriesUrl == null) {
getLog().info("Can't find information about categories");
return;
}
for (Path dir : inputDirectories) {
final Path categories = Paths.get(categoriesUrl.toURI());
Files.copy(categories, dir.resolve(CATEGORIES_FILE_NAME),
StandardCopyOption.REPLACE_EXISTING);
}
}
private void loadProperties(final List inputDirectories)
throws IOException, DependencyResolutionRequiredException {
final Properties properties = new Properties();
readPropertiesFile(properties);
readPropertiesFileFromClasspath(ALLURE_OLD_PROPERTIES, properties);
readPropertiesFileFromClasspath(ALLURE_NEW_PROPERTIES, properties);
readPropertiesFromMap(properties);
prepareProperties(properties);
for (Path dir : inputDirectories) {
try (OutputStream os = Files.newOutputStream(dir.resolve(ALLURE_OLD_PROPERTIES))) {
properties.store(os, null);
} catch (IOException e) {
getLog().info(
String.format("Can't store properties in directory %s", dir.toString()), e);
}
}
}
private void installAllure() throws MavenReportException {
try {
final AllureCommandline commandline =
new AllureCommandline(Paths.get(installDirectory), reportVersion);
getLog().info(String.format("Allure installation directory %s", installDirectory));
getLog().info(String.format("Try to finding out allure %s", commandline.getVersion()));
if (commandline.allureNotExists()) {
if (StringUtils.isNotBlank(allureDownloadUrl)) {
getLog().info("Downloading allure commandline from " + allureDownloadUrl);
commandline.download(allureDownloadUrl,
ProxyUtils.getProxy(session, decrypter));
getLog().info("Downloading allure commandline complete");
} else {
commandline.downloadWithMaven(session, dependencyResolver);
}
}
} catch (IOException e) {
getLog().error("Installation error", e);
throw new MavenReportException("Can't install allure", e);
}
}
protected void generateReport(final List resultsPaths) throws MavenReportException {
try {
final Path reportPath = Paths.get(getReportDirectory());
final AllureCommandline commandline = new AllureCommandline(
Paths.get(getInstallDirectory()), reportVersion, reportTimeout);
getLog().info("Generate report to " + reportPath);
commandline.generateReport(resultsPaths, reportPath, Boolean.TRUE.equals(singleFile));
getLog().info("Report generated successfully.");
} catch (Exception e) {
getLog().error("Generation error", e);
throw new MavenReportException("Can't generate allure report data", e);
}
}
/**
* Read system properties from file {@link #propertiesFilePath}.
*
* @throws IOException if any occurs.
*/
protected void readPropertiesFile(final Properties properties) throws IOException {
final Path path = Paths.get(propertiesFilePath);
if (Files.exists(path)) {
try (InputStream is = Files.newInputStream(path)) {
properties.load(is);
}
}
}
/**
* Read allure.properties from classpath.
*
* @throws IOException if any occurs.
*/
protected void readPropertiesFileFromClasspath(final String propertiesFileName,
final Properties properties) throws IOException, DependencyResolutionRequiredException {
try (InputStream is = createProjectClassLoader().getResourceAsStream(propertiesFileName)) {
if (is != null) {
properties.load(is);
}
}
}
/**
* Set properties from {@link #properties}.
*/
protected void readPropertiesFromMap(final Properties properties) {
for (Map.Entry property : this.properties.entrySet()) {
if (property.getKey() != null && property.getValue() != null) {
properties.setProperty(property.getKey(), property.getValue());
}
}
}
/**
* Replaces the placeholders in properties. You can use properties like: name1=value1
* name2=value2 with ${name1} You can also use system and maven properties for the placeholder.
*/
protected void prepareProperties(final Properties properties) {
final Properties allProperties = new Properties();
allProperties.putAll(properties);
allProperties.putAll(
getProject() == null ? Collections.emptyMap() : getProject().getProperties());
allProperties.putAll(System.getProperties());
for (String name : properties.stringPropertyNames()) {
properties.setProperty(name,
StringSubstitutor.replace(properties.getProperty(name), allProperties));
}
}
/**
* Render allure report page in project-reports.html.
*/
protected void render(final Sink sink, final String title) {
sink.head();
sink.title();
sink.text(title);
sink.title_();
sink.head_();
sink.body();
sink.lineBreak();
final Path indexHtmlFile = Paths.get(getReportDirectory(), "index.html");
final String relativePath =
Paths.get(reportingOutputDirectory).relativize(indexHtmlFile).toString();
sink.rawText(String.format("",
relativePath));
sink.link(relativePath);
sink.body_();
sink.flush();
sink.close();
}
/**
* Return ClassLoader with classpath elements.
*/
protected ClassLoader createProjectClassLoader()
throws MalformedURLException, DependencyResolutionRequiredException {
final List result = new ArrayList<>();
for (String element : project.getTestClasspathElements()) {
if (element != null) {
final URL url = Paths.get(element).toUri().toURL();
result.add(url);
}
}
return new URLClassLoader(result.toArray(new URL[0]));
}
/**
* {@inheritDoc}
*/
@Override
public boolean canGenerateReport() {
return !isAggregate() || project.isExecutionRoot();
}
/**
* Is the current report aggregated?
*/
protected boolean isAggregate() {
return false;
}
/**
* Returns true if given path is an existed directory.
*/
protected boolean isDirectoryExists(final Path path) {
return Files.exists(path) && Files.isDirectory(path);
}
/**
* Get list of Allure results directories to generate the report.
*/
protected abstract List getInputDirectories();
/**
* Get the current mojo name.
*/
protected abstract String getMojoName();
/**
* The directory to generate Allure report into.
*/
public String getReportDirectory() {
return reportDirectory;
}
public String getInstallDirectory() {
return installDirectory;
}
}