Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.qameta.allure.allure1.Allure1Plugin Maven / Gradle / Ivy
package io.qameta.allure.allure1;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import io.qameta.allure.Reader;
import io.qameta.allure.context.RandomUidContext;
import io.qameta.allure.core.Configuration;
import io.qameta.allure.core.ResultsVisitor;
import io.qameta.allure.entity.Attachment;
import io.qameta.allure.entity.Label;
import io.qameta.allure.entity.LabelName;
import io.qameta.allure.entity.Link;
import io.qameta.allure.entity.Parameter;
import io.qameta.allure.entity.StageResult;
import io.qameta.allure.entity.Status;
import io.qameta.allure.entity.Step;
import io.qameta.allure.entity.TestResult;
import io.qameta.allure.entity.Time;
import org.allurefw.allure1.AllureUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.qatools.allure.model.Description;
import ru.yandex.qatools.allure.model.DescriptionType;
import ru.yandex.qatools.allure.model.ParameterKind;
import ru.yandex.qatools.allure.model.TestCaseResult;
import ru.yandex.qatools.allure.model.TestSuiteResult;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.fasterxml.jackson.databind.MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME;
import static io.qameta.allure.entity.LabelName.ISSUE;
import static io.qameta.allure.entity.LabelName.PACKAGE;
import static io.qameta.allure.entity.LabelName.PARENT_SUITE;
import static io.qameta.allure.entity.LabelName.RESULT_FORMAT;
import static io.qameta.allure.entity.LabelName.SUB_SUITE;
import static io.qameta.allure.entity.LabelName.SUITE;
import static io.qameta.allure.entity.LabelName.TEST_CLASS;
import static io.qameta.allure.entity.LabelName.TEST_METHOD;
import static io.qameta.allure.entity.Status.BROKEN;
import static io.qameta.allure.entity.Status.FAILED;
import static io.qameta.allure.entity.Status.PASSED;
import static io.qameta.allure.entity.Status.SKIPPED;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsFirst;
import static java.util.stream.Collectors.toList;
/**
* Plugin that reads results from Allure1 data format.
*
* @since 2.0
*/
@SuppressWarnings({"PMD.ExcessiveImports", "PMD.GodClass", "PMD.TooManyMethods"})
public class Allure1Plugin implements Reader {
private static final Logger LOGGER = LoggerFactory.getLogger(Allure1Plugin.class);
private static final String UNKNOWN = "unknown";
private static final String MD_5 = "md5";
private static final String ISSUE_URL_PROPERTY = "allure.issues.tracker.pattern";
private static final String TMS_LINK_PROPERTY = "allure.tests.management.pattern";
private static final Comparator PARAMETER_COMPARATOR =
comparing(Parameter::getName, nullsFirst(naturalOrder()))
.thenComparing(Parameter::getValue, nullsFirst(naturalOrder()));
public static final String ENVIRONMENT_BLOCK_NAME = "environment";
public static final String ALLURE1_RESULTS_FORMAT = "allure1";
private final ObjectMapper jsonMapper = new ObjectMapper();
private final ObjectMapper xmlMapper;
public Allure1Plugin() {
final SimpleModule module = new XmlParserModule()
.addDeserializer(ru.yandex.qatools.allure.model.Status.class, new StatusDeserializer());
xmlMapper = new XmlMapper()
.configure(USE_WRAPPER_NAME_AS_PROPERTY_NAME, true)
.setAnnotationIntrospector(new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()))
.registerModule(module);
}
@Override
public void readResults(final Configuration configuration,
final ResultsVisitor visitor,
final Path resultsDirectory) {
final Properties allureProperties = loadAllureProperties(resultsDirectory);
final RandomUidContext context = configuration.requireContext(RandomUidContext.class);
final Map environment = processEnvironment(resultsDirectory);
getStreamOfAllure1Results(resultsDirectory).forEach(testSuite -> testSuite.getTestCases()
.forEach(testCase -> {
convert(context.getValue(), resultsDirectory, visitor, testSuite, testCase, allureProperties);
getEnvironmentParameters(testCase).forEach(param ->
environment.put(param.getName(), param.getValue())
);
})
);
visitor.visitExtra(ENVIRONMENT_BLOCK_NAME, environment);
}
private List getEnvironmentParameters(final TestCaseResult testCase) {
return testCase.getParameters().stream().filter(this::hasEnvType).collect(toList());
}
private Properties loadAllureProperties(final Path resultsDirectory) {
final Path propertiesFile = resultsDirectory.resolve("allure.properties");
final Properties properties = new Properties();
if (Files.exists(propertiesFile)) {
try (InputStream propFile = Files.newInputStream(propertiesFile)) {
properties.load(propFile);
} catch (IOException e) {
LOGGER.error("Error while reading allure.properties file: %s", e.getMessage());
}
}
properties.putAll(System.getProperties());
return properties;
}
@SuppressWarnings("PMD.ExcessiveMethodLength")
private void convert(final Supplier randomUid,
final Path directory,
final ResultsVisitor visitor,
final TestSuiteResult testSuite,
final TestCaseResult source,
final Properties properties) {
final TestResult dest = new TestResult();
final String suiteName = firstNonNull(testSuite.getTitle(), testSuite.getName(), "unknown test suite");
final String testClass = firstNonNull(
findLabelValue(source.getLabels(), TEST_CLASS.value()),
findLabelValue(testSuite.getLabels(), TEST_CLASS.value()),
testSuite.getName(),
UNKNOWN
);
final String testMethod = firstNonNull(
findLabelValue(source.getLabels(), TEST_METHOD.value()),
source.getName(),
UNKNOWN
);
final String name = firstNonNull(source.getTitle(), source.getName(), "unknown test case");
final List parameters = getParameters(source);
final Optional historyId = findLabel(source.getLabels(), "historyId");
if (historyId.isPresent()) {
dest.setHistoryId(historyId.get().getValue());
} else {
dest.setHistoryId(getHistoryId(String.format("%s#%s", testClass, name), parameters));
}
dest.setUid(randomUid.get());
dest.setName(name);
dest.setFullName(String.format("%s.%s", testClass, testMethod));
final Status status = convert(source.getStatus());
dest.setStatus(status);
dest.setTime(Time.create(source.getStart(), source.getStop()));
dest.setParameters(parameters);
dest.setDescription(getDescription(testSuite.getDescription(), source.getDescription()));
dest.setDescriptionHtml(getDescriptionHtml(testSuite.getDescription(), source.getDescription()));
Optional.ofNullable(source.getFailure()).ifPresent(failure -> {
dest.setStatusMessage(failure.getMessage());
dest.setStatusTrace(failure.getStackTrace());
});
if (!source.getSteps().isEmpty() || !source.getAttachments().isEmpty()) {
StageResult testStage = new StageResult();
if (!source.getSteps().isEmpty()) {
//@formatter:off
testStage.setSteps(convert(
source.getSteps(),
step -> convert(directory, visitor, step, status, dest.getStatusMessage(), dest.getStatusTrace()))
);
//@formatter:on
}
if (!source.getAttachments().isEmpty()) {
testStage.setAttachments(convert(source.getAttachments(), at -> convert(directory, visitor, at)));
}
testStage.setStatus(status);
testStage.setStatusMessage(dest.getStatusMessage());
testStage.setStatusTrace(dest.getStatusTrace());
dest.setTestStage(testStage);
}
final Set set = new TreeSet<>(
comparing(Label::getName, nullsFirst(naturalOrder()))
.thenComparing(Label::getValue, nullsFirst(naturalOrder()))
);
set.addAll(convert(testSuite.getLabels(), this::convert));
set.addAll(convert(source.getLabels(), this::convert));
dest.setLabels(new ArrayList<>(set));
dest.findAllLabels(ISSUE).forEach(issue ->
dest.getLinks().add(getLink(ISSUE, issue, getIssueUrl(issue, properties)))
);
dest.findOneLabel("testId").ifPresent(testId ->
dest.getLinks().add(new Link().setName(testId).setType("tms")
.setUrl(getTestCaseIdUrl(testId, properties)))
);
//TestNG nested suite
final Optional testGroupLabel = dest.findOneLabel("testGroup");
final Optional testSuiteLabel = dest.findOneLabel("testSuite");
if (testGroupLabel.isPresent() && testSuiteLabel.isPresent()) {
dest.addLabelIfNotExists(PARENT_SUITE, testSuiteLabel.get());
dest.addLabelIfNotExists(SUITE, testGroupLabel.get());
dest.addLabelIfNotExists(SUB_SUITE, testClass);
} else {
dest.addLabelIfNotExists(SUITE, suiteName);
}
dest.addLabelIfNotExists(TEST_CLASS, testClass);
dest.addLabelIfNotExists(TEST_METHOD, testMethod);
dest.addLabelIfNotExists(PACKAGE, testClass);
dest.findAllLabels("status_details").stream()
.filter("flaky"::equalsIgnoreCase)
.findAny()
.ifPresent(value -> dest.setFlaky(true));
dest.addLabelIfNotExists(RESULT_FORMAT, ALLURE1_RESULTS_FORMAT);
visitor.visitTestResult(dest);
}
private List convert(final Collection source, final Function converter) {
return convert(source, t -> true, converter);
}
private List convert(final Collection source,
final Predicate predicate,
final Function converter) {
return Objects.isNull(source) ? null : source.stream()
.filter(predicate)
.map(converter)
.collect(toList());
}
private Step convert(final Path source,
final ResultsVisitor visitor,
final ru.yandex.qatools.allure.model.Step s,
final Status testStatus,
final String message,
final String trace) {
final Status status = convert(s.getStatus());
final Step current = new Step()
.setName(s.getTitle() == null ? s.getName() : s.getTitle())
.setTime(new Time()
.setStart(s.getStart())
.setStop(s.getStop())
.setDuration(s.getStop() - s.getStart()))
.setStatus(status)
.setSteps(convert(s.getSteps(), step -> convert(source, visitor, step, testStatus, message, trace)))
.setAttachments(convert(s.getAttachments(), attach -> convert(source, visitor, attach)));
//Copy test status details to each step set the same status
if (Objects.equals(status, testStatus)) {
current.setStatusMessage(message);
current.setStatusMessage(trace);
}
return current;
}
private Label convert(final ru.yandex.qatools.allure.model.Label label) {
return new Label()
.setName(label.getName())
.setValue(label.getValue());
}
private Parameter convert(final ru.yandex.qatools.allure.model.Parameter parameter) {
return new Parameter()
.setName(parameter.getName())
.setValue(parameter.getValue());
}
private Attachment convert(final Path source,
final ResultsVisitor visitor,
final ru.yandex.qatools.allure.model.Attachment attachment) {
final Path attachmentFile = source.resolve(attachment.getSource());
if (Files.isRegularFile(attachmentFile)) {
final Attachment found = visitor.visitAttachmentFile(attachmentFile);
if (Objects.nonNull(attachment.getType())) {
found.setType(attachment.getType());
}
if (Objects.nonNull(attachment.getTitle())) {
found.setName(attachment.getTitle());
}
return found;
} else {
visitor.error("Could not find attachment " + attachment.getSource() + " in directory " + source);
return new Attachment()
.setType(attachment.getType())
.setName(attachment.getTitle())
.setSize(0L);
}
}
public static Status convert(final ru.yandex.qatools.allure.model.Status status) {
if (Objects.isNull(status)) {
return Status.UNKNOWN;
}
switch (status) {
case FAILED:
return FAILED;
case BROKEN:
return BROKEN;
case PASSED:
return PASSED;
case CANCELED:
case SKIPPED:
case PENDING:
return SKIPPED;
default:
return Status.UNKNOWN;
}
}
private List getParameters(final TestCaseResult source) {
final TreeSet parametersSet = new TreeSet<>(
comparing(Parameter::getName, nullsFirst(naturalOrder()))
.thenComparing(Parameter::getValue, nullsFirst(naturalOrder()))
);
parametersSet.addAll(convert(source.getParameters(), this::hasArgumentType, this::convert));
return new ArrayList<>(parametersSet);
}
private String getDescription(final Description... descriptions) {
return Stream.of(descriptions)
.filter(Objects::nonNull)
.filter(isHtmlDescription().negate())
.map(Description::getValue)
.collect(Collectors.joining("\n\n"));
}
private String getDescriptionHtml(final Description... descriptions) {
return Stream.of(descriptions)
.filter(Objects::nonNull)
.filter(isHtmlDescription())
.map(Description::getValue)
.collect(Collectors.joining(""));
}
private Predicate isHtmlDescription() {
return description -> DescriptionType.HTML.equals(description.getType());
}
private String findLabelValue(final List labels, final String labelName) {
return labels.stream()
.filter(label -> labelName.equals(label.getName()))
.map(ru.yandex.qatools.allure.model.Label::getValue)
.findAny()
.orElse(null);
}
private Optional findLabel(
final List labels, final String labelName) {
return labels.stream()
.filter(label -> labelName.equals(label.getName()))
.findAny();
}
private boolean hasArgumentType(final ru.yandex.qatools.allure.model.Parameter parameter) {
return Objects.isNull(parameter.getKind()) || ParameterKind.ARGUMENT.equals(parameter.getKind());
}
private boolean hasEnvType(final ru.yandex.qatools.allure.model.Parameter parameter) {
return ParameterKind.ENVIRONMENT_VARIABLE.equals(parameter.getKind());
}
private Link getLink(final LabelName labelName, final String value, final String url) {
return new Link().setName(value).setType(labelName.value()).setUrl(url);
}
private String getIssueUrl(final String issue, final Properties properties) {
return String.format(properties.getProperty(ISSUE_URL_PROPERTY, "%s"), issue);
}
private String getTestCaseIdUrl(final String testCaseId, final Properties properties) {
return String.format(properties.getProperty(TMS_LINK_PROPERTY, "%s"), testCaseId);
}
private Stream getStreamOfAllure1Results(final Path source) {
return Stream.concat(xmlFiles(source), jsonFiles(source));
}
private Stream xmlFiles(final Path source) {
try {
return AllureUtils.listTestSuiteXmlFiles(source)
.stream()
.map(this::readXmlTestSuiteFile)
.filter(Optional::isPresent)
.map(Optional::get);
} catch (IOException e) {
LOGGER.error("Could not list allure1 xml files", e);
return Stream.empty();
}
}
private Stream jsonFiles(final Path source) {
try {
return AllureUtils.listTestSuiteJsonFiles(source)
.stream()
.map(this::readJsonTestSuiteFile)
.filter(Optional::isPresent)
.map(Optional::get);
} catch (IOException e) {
LOGGER.error("Could not list allure1 json files", e);
return Stream.empty();
}
}
private Optional readXmlTestSuiteFile(final Path source) {
try (InputStream is = Files.newInputStream(source)) {
return Optional.of(xmlMapper.readValue(is, TestSuiteResult.class));
} catch (IOException e) {
LOGGER.error("Could not read result {}: {}", source, e);
}
return Optional.empty();
}
private Optional readJsonTestSuiteFile(final Path source) {
try (InputStream is = Files.newInputStream(source)) {
return Optional.of(jsonMapper.readValue(is, TestSuiteResult.class));
} catch (IOException e) {
LOGGER.error("Could not read result {}: {}", source, e);
return Optional.empty();
}
}
@SafeVarargs
private static T firstNonNull(final T... items) {
return Stream.of(items)
.filter(Objects::nonNull)
.findFirst()
.orElseThrow(() -> new IllegalStateException(
"firstNonNull method should have at least one non null parameter"
));
}
private static String getHistoryId(final String name, final List parameters) {
final MessageDigest digest = getMessageDigest();
digest.update(name.getBytes(UTF_8));
parameters.stream()
.sorted(PARAMETER_COMPARATOR)
.forEachOrdered(parameter -> {
digest.update(Objects.toString(parameter.getName()).getBytes(UTF_8));
digest.update(Objects.toString(parameter.getValue()).getBytes(UTF_8));
});
final byte[] bytes = digest.digest();
return new BigInteger(1, bytes).toString(16);
}
private static MessageDigest getMessageDigest() {
try {
return MessageDigest.getInstance(MD_5);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Could not find md5 hashing algorithm", e);
}
}
private Map processEnvironment(final Path directory) {
final Map environment = processEnvironmentProperties(directory);
environment.putAll(processEnvironmentXml(directory));
return environment;
}
private Map processEnvironmentProperties(final Path directory) {
final Path envPropsFile = directory.resolve("environment.properties");
final Map items = new HashMap<>();
if (Files.exists(envPropsFile)) {
try (InputStream is = Files.newInputStream(envPropsFile)) {
final Properties properties = new Properties();
properties.load(is);
properties.forEach((key, value) -> items.put(String.valueOf(key), String.valueOf(value)));
} catch (IOException e) {
LOGGER.error("Could not read environments.properties file " + envPropsFile, e);
}
}
return items;
}
private Map processEnvironmentXml(final Path directory) {
final Path envXmlFile = directory.resolve("environment.xml");
final Map items = new HashMap<>();
if (Files.exists(envXmlFile)) {
try (InputStream fis = Files.newInputStream(envXmlFile)) {
xmlMapper.readValue(fis, ru.yandex.qatools.commons.model.Environment.class).getParameter().forEach(p ->
items.put(p.getKey(), p.getValue())
);
} catch (Exception e) {
LOGGER.error("Could not read environment.xml file " + envXmlFile.toAbsolutePath(), e);
}
}
return items;
}
}