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

io.qameta.allure.trx.TrxPlugin Maven / Gradle / Ivy

There is a newer version: 2.32.0
Show newest version
/*
 *  Copyright 2019 Qameta Software OÜ
 *
 *  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.trx;

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.Label;
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 io.qameta.allure.parser.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static io.qameta.allure.entity.LabelName.PACKAGE;
import static io.qameta.allure.entity.LabelName.RESULT_FORMAT;
import static io.qameta.allure.entity.LabelName.SUITE;
import static io.qameta.allure.entity.LabelName.TEST_CLASS;
import static java.nio.file.Files.newDirectoryStream;

/**
 * @author charlie (Dmitry Baev).
 */
@SuppressWarnings("PMD.ExcessiveImports")
public class TrxPlugin implements Reader {

    private static final Logger LOGGER = LoggerFactory.getLogger(TrxPlugin.class);

    private static final String TEST_RUN_ELEMENT_NAME = "TestRun";

    public static final String TRX_RESULTS_FORMAT = "trx";
    public static final String RESULTS_ELEMENT_NAME = "Results";
    public static final String UNIT_TEST_RESULT_ELEMENT_NAME = "UnitTestResult";
    public static final String UNIT_TEST_INNER_RESULTS = "InnerResults";
    public static final String TEST_NAME_ATTRIBUTE = "testName";
    public static final String START_TIME_ATTRIBUTE = "startTime";
    public static final String END_TIME_ATTRIBUTE = "endTime";
    public static final String OUTCOME_ATTRIBUTE = "outcome";
    public static final String TEST_DEFINITIONS_ELEMENT = "TestDefinitions";
    public static final String UNIT_TEST_ELEMENT = "UnitTest";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String TEST_METHOD_ELEMENT = "TestMethod";
    public static final String CLASS_NAME_ATTRIBUTE = "className";
    public static final String PROPERTIES_ELEMENT = "Properties";
    public static final String PROPERTY_ATTRIBUTE = "Property";
    public static final String KEY_ELEMENT = "Key";
    public static final String VALUE_ELEMENT = "Value";
    public static final String DESCRIPTION_ELEMENT = "Description";
    public static final String EXECUTION_ELEMENT = "Execution";
    public static final String ID_ATTRIBUTE = "id";
    public static final String EXECUTION_ID_ATTRIBUTE = "executionId";
    public static final String OUTPUT_ELEMENT_NAME = "Output";
    public static final String MESSAGE_ELEMENT_NAME = "Message";
    public static final String STACK_TRACE_ELEMENT_NAME = "StackTrace";
    public static final String ERROR_INFO_ELEMENT_NAME = "ErrorInfo";
    public static final String STDOUT_ELEMENT_NAME = "StdOut";

    @Override
    public void readResults(final Configuration configuration,
                            final ResultsVisitor visitor,
                            final Path directory) {
        final RandomUidContext context = configuration.requireContext(RandomUidContext.class);
        listResults(directory).forEach(result -> parseTestRun(result, context, visitor));
    }

    protected void parseTestRun(final Path parsedFile, final RandomUidContext context, final ResultsVisitor visitor) {
        try {
            LOGGER.debug("Parsing file {}", parsedFile);

            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            final DocumentBuilder builder = factory.newDocumentBuilder();
            final Document document = builder.parse(parsedFile.toFile());
            final XmlElement testRunElement = new XmlElement(document.getDocumentElement());
            final String elementName = testRunElement.getName();
            if (!TEST_RUN_ELEMENT_NAME.equals(elementName)) {
                LOGGER.debug("{} is not a valid TRX file. Unknown root element {}", parsedFile, elementName);
                return;
            }
            final Map tests = new HashMap<>();
            testRunElement.getFirst(TEST_DEFINITIONS_ELEMENT)
                    .ifPresent(testDefinitions -> {
                        testDefinitions.get(UNIT_TEST_ELEMENT).forEach(unitTestElement -> {
                            final UnitTest unitTest = parseUnitTest(unitTestElement);
                            tests.put(unitTest.getExecutionId(), unitTest);
                        });
                    });
            testRunElement.getFirst(RESULTS_ELEMENT_NAME)
                    .ifPresent(resultsElement -> parseResults(resultsElement, tests, context, visitor));
        } catch (SAXException | ParserConfigurationException | IOException e) {
            LOGGER.error("Could not parse file {}: {}", parsedFile, e);
        }
    }

    protected UnitTest parseUnitTest(final XmlElement unitTestElement) {
        final String name = unitTestElement.getAttribute(NAME_ATTRIBUTE);
        final String className = unitTestElement.getFirst(TEST_METHOD_ELEMENT)
                .map(testMethod -> testMethod.getAttribute(CLASS_NAME_ATTRIBUTE))
                .orElse(null);
        final String description = unitTestElement.getFirst(DESCRIPTION_ELEMENT)
                .map(XmlElement::getValue)
                .orElse(null);
        final String executionId = unitTestElement.getFirst(EXECUTION_ELEMENT)
                .map(execution -> execution.getAttribute(ID_ATTRIBUTE))
                .orElse(null);
        final Map properties = parseProperties(unitTestElement);
        return new UnitTest(name, className, executionId, description, properties);
    }

    private Map parseProperties(final XmlElement unitTestElement) {
        final Map properties = new HashMap<>();
        unitTestElement.getFirst(PROPERTIES_ELEMENT)
                .ifPresent(propertiesElement -> parseProperties(properties, propertiesElement));
        return properties;
    }

    private void parseProperties(final Map properties, final XmlElement propertiesElement) {
        propertiesElement.get(PROPERTY_ATTRIBUTE)
                .forEach(propertyElement -> parseProperty(properties, propertyElement));
    }

    private void parseProperty(final Map properties, final XmlElement propertyElement) {
        final Optional key = propertyElement.getFirst(KEY_ELEMENT)
                .map(XmlElement::getValue);
        final Optional value = propertyElement.getFirst(VALUE_ELEMENT)
                .map(XmlElement::getValue);
        if (key.isPresent() && value.isPresent()) {
            properties.put(key.get(), value.get());
        }
    }

    protected void parseResults(final XmlElement resultsElement,
                                final Map tests,
                                final RandomUidContext context,
                                final ResultsVisitor visitor) {
        resultsElement.get(UNIT_TEST_RESULT_ELEMENT_NAME)
                .forEach(unitTestResult -> parseUnitTestResult(unitTestResult, tests, context, visitor));
    }

    protected void parseUnitTestResult(final XmlElement unitTestResult,
                                       final Map tests,
                                       final RandomUidContext context,
                                       final ResultsVisitor visitor) {
        final String executionId = unitTestResult.getAttribute(EXECUTION_ID_ATTRIBUTE);
        final String testName = unitTestResult.getAttribute(TEST_NAME_ATTRIBUTE);
        final String startTime = unitTestResult.getAttribute(START_TIME_ATTRIBUTE);
        final String endTime = unitTestResult.getAttribute(END_TIME_ATTRIBUTE);
        final String outcome = unitTestResult.getAttribute(OUTCOME_ATTRIBUTE);
        final String uid = context.getValue().get();
        final TestResult result = new TestResult()
                .setUid(uid)
                .setName(testName)
                .setStatus(parseStatus(outcome))
                .setTime(getTime(startTime, endTime));
        getStatusMessage(unitTestResult).ifPresent(result::setStatusMessage);
        getStatusTrace(unitTestResult).ifPresent(result::setStatusTrace);
        getLogMessage(unitTestResult).ifPresent(logMessage -> {
            final List lines = splitLines(logMessage);
            final List steps = lines
                    .stream()
                    .map(line -> new Step().setName(line))
                    .collect(Collectors.toList());
            final StageResult stageResult = new StageResult()
                    .setSteps(steps);
            result.setTestStage(stageResult);
        });
        Optional.ofNullable(tests.get(executionId)).ifPresent(unitTest -> {
            final String className = unitTest.getClassName();
            final String fullName = String.format("%s.%s", className, testName);
            result.setParameters(unitTest.getParameters());
            result.setDescription(unitTest.getDescription());
            result.setFullName(fullName);
            result.setHistoryId(fullName);
            result.addLabelIfNotExists(SUITE, className);
            result.addLabelIfNotExists(TEST_CLASS, className);
            result.addLabelIfNotExists(PACKAGE, className);
        });

        result.addLabelIfNotExists(RESULT_FORMAT, TRX_RESULTS_FORMAT);
        visitor.visitTestResult(result);

        unitTestResult.getFirst(UNIT_TEST_INNER_RESULTS)
            .ifPresent(innerResults -> {
                innerResults.get(UNIT_TEST_RESULT_ELEMENT_NAME)
                    .forEach(unitTestChildResult -> 
                        parseUnitTestResult(unitTestChildResult, tests, context, visitor, result.getLabels())
                    );
            }); 
    }

    protected void parseUnitTestResult(final XmlElement unitTestResult,
                                       final Map tests,
                                       final RandomUidContext context,
                                       final ResultsVisitor visitor,
                                       final List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy