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

net.thucydides.cucumber.ThucydidesReporter Maven / Gradle / Ivy

The newest version!
package net.thucydides.cucumber;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import cucumber.runtime.StepDefinitionMatch;
import gherkin.formatter.Formatter;
import gherkin.formatter.Reporter;
import gherkin.formatter.model.*;
import net.thucydides.core.Thucydides;
import net.thucydides.core.ThucydidesListeners;
import net.thucydides.core.ThucydidesReports;
import net.thucydides.core.model.*;
import net.thucydides.core.reports.ReportService;
import net.thucydides.core.requirements.FileSystemRequirementsTagProvider;
import net.thucydides.core.steps.BaseStepListener;
import net.thucydides.core.steps.ExecutedStepDescription;
import net.thucydides.core.steps.StepEventBus;
import net.thucydides.core.steps.StepFailure;
import net.thucydides.core.webdriver.Configuration;
import net.thucydides.core.webdriver.ThucydidesWebDriverSupport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.junit.internal.AssumptionViolatedException;

import java.io.File;
import java.io.IOException;
import java.util.*;

import static ch.lambdaj.Lambda.*;
import static org.apache.commons.lang3.StringUtils.isEmpty;

/**
 * Generates Thucydides reports.
 *
 * @author L.Carausu ([email protected])
 */
public class ThucydidesReporter implements Formatter, Reporter {


    private static final String OPEN_PARAM_CHAR = "\uff5f";
    private static final String CLOSE_PARAM_CHAR = "\uff60";

    private static final List SKIPPED_TAGS = ImmutableList.of("@skip", "@wip");

    private final Queue stepQueue;

    private Configuration systemConfiguration;

    private ThreadLocal thucydidesListenersThreadLocal;

    private final List baseStepListeners;

    private Feature currentFeature;

    private int currentExample = 0;

    private boolean examplesRunning;

    private List> exampleRows;

    private int exampleCount = 0;

    private DataTable table;

    private boolean firstStep = true;

    private String currentUri;


    private static Optional forcedStoryResult = Optional.absent();
    private static Optional forcedScenarioResult = Optional.absent();

    private void clearStoryResult() {
        forcedStoryResult = Optional.absent();
    }

    private void clearScenarioResult() {
        forcedScenarioResult = Optional.absent();
    }


    private boolean isPendingStory() {
        return ((forcedStoryResult.or(TestResult.UNDEFINED) == TestResult.PENDING)
                || (forcedScenarioResult.or(TestResult.UNDEFINED) == TestResult.PENDING));
    }

    private boolean isSkippedStory() {
        return ((forcedStoryResult.or(TestResult.UNDEFINED) == TestResult.SKIPPED)
                || (forcedScenarioResult.or(TestResult.UNDEFINED) == TestResult.SKIPPED));
    }

    public ThucydidesReporter(Configuration systemConfiguration) {
        this.systemConfiguration = systemConfiguration;
        this.stepQueue = new LinkedList<>();
        thucydidesListenersThreadLocal = new ThreadLocal<>();
        baseStepListeners = Lists.newArrayList();
        clearStoryResult();
    }

    protected ThucydidesListeners getThucydidesListeners() {
        if (thucydidesListenersThreadLocal.get() == null) {
            ThucydidesListeners listeners = ThucydidesReports.setupListeners(systemConfiguration);
            thucydidesListenersThreadLocal.set(listeners);
            synchronized (baseStepListeners) {
                baseStepListeners.add(listeners.getBaseStepListener());
            }
        }
        return thucydidesListenersThreadLocal.get();
    }

    protected ReportService getReportService() {
        return ThucydidesReports.getReportService(systemConfiguration);
    }

    @Override
    public void syntaxError(String state, String event, List legalEvents, String uri, Integer line) {
    }

    @Override
    public void uri(String uri) {
        currentUri = uri;
        //Cucumber reports only the feature file name but thucydides needs the whole directory structure
        //starting from the root path of the features or stories , that's why i have to complete the uri
        //currently it works only for features with different names - for more we need a complete uri from cucuber
        FileSystemRequirementsTagProvider tagProvider = new FileSystemRequirementsTagProvider();
        try {
            Optional rootDirectoryPath = tagProvider.getRootDirectoryPath();
            if(rootDirectoryPath.isPresent()) {
                File rootDirectory = new File(rootDirectoryPath.get());
                if (rootDirectory.exists()) {
                    Collection files = FileUtils.listFiles(rootDirectory, new FeatureFileFilter(uri), TrueFileFilter.INSTANCE);
                    if (files.size() > 0) {
                        File firstMatch = files.iterator().next();
                        currentUri = firstMatch.getAbsolutePath().substring(rootDirectoryPath.get().length() + 1);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private class FeatureFileFilter implements IOFileFilter {

        private String featureFileName;
        public FeatureFileFilter(String featureFileName){
            this.featureFileName = featureFileName;
        }

        @Override
        public boolean accept(File file) {
            return file.getName().equals(featureFileName);
        }

        @Override
        public boolean accept(File dir, String name) {
            return name.equals(featureFileName);
        }
    }

    @Override
    public void feature(Feature feature) {

        assureTestSuiteFinished();

        currentFeature = feature;

        configureDriver(feature);
        getThucydidesListeners().withDriver(ThucydidesWebDriverSupport.getDriver());
        Story userStory = Story.withIdAndPath(feature.getId(), feature.getName(), currentUri).asFeature();

        if (!isEmpty(feature.getDescription())) {
            userStory = userStory.withNarrative(feature.getDescription());
        }
        StepEventBus.getEventBus().testSuiteStarted(userStory);

        checkForPending(feature);
        checkForSkipped(feature);
    }

    private void checkForPending(Feature feature) {
        if (isPending(feature.getTags())) {
            forcedStoryResult = Optional.of(TestResult.PENDING);
            StepEventBus.getEventBus().suspendTest();
        }
    }

    private void checkForSkipped(Feature feature) {
        if (isSkippedOrWIP(feature.getTags())) {
            forcedStoryResult = Optional.of(TestResult.SKIPPED);
            StepEventBus.getEventBus().suspendTest();
        }
    }

    private void checkForPending(Scenario scenario) {
        if (isPending(scenario.getTags())) {
            forcedScenarioResult = Optional.of(TestResult.PENDING);
            StepEventBus.getEventBus().suspendTest();
        }
    }

    private void checkForSkipped(Scenario scenario) {
        if (isSkippedOrWIP(scenario.getTags())) {
            forcedScenarioResult = Optional.of(TestResult.SKIPPED);
            StepEventBus.getEventBus().suspendTest();
        }
    }

    private boolean isPending(List tags) {
        return hasTag("@pending", tags);
    }

    private boolean isSkippedOrWIP(List tags) {
        for (Tag tag : tags) {
            if (SKIPPED_TAGS.contains(tag.getName())) {
                return true;
            }
        }
        return false;
    }

    private boolean hasTag(String tagName, List tags) {
        for (Tag tag : tags) {
            if (tag.getName().equals(tagName)) {
                return true;
            }
        }
        return false;
    }

    private void configureDriver(Feature feature) {
        StepEventBus.getEventBus().setUniqueSession(systemConfiguration.getUseUniqueBrowser());
        String requestedDriver = getDriverFrom(feature);
        if (StringUtils.isNotEmpty(requestedDriver)) {
            ThucydidesWebDriverSupport.initialize(requestedDriver);
        } else {
            ThucydidesWebDriverSupport.initialize();
        }
    }

    private String getDriverFrom(Feature feature) {
        List tags = feature.getTags();
        String requestedDriver = null;
        for (Tag tag : tags) {
            if (tag.getName().startsWith("@driver:")) {
                requestedDriver = tag.getName().substring(8);
            }
        }
        return requestedDriver;
    }


    @Override
    public void scenarioOutline(ScenarioOutline scenarioOutline) {
    }

    @Override
    public void examples(Examples examples) {
        reinitializeExamples();
        List examplesTableRows = examples.getRows();
        List headers = getHeadersFrom(examplesTableRows);
        List> rows = getValuesFrom(examplesTableRows, headers);

        for (int i = 1; i < examplesTableRows.size(); i++) {
            addRow(exampleRows, headers, examplesTableRows.get(i));
        }
        table = (table == null) ?
                thucydidesTableFrom(headers, rows, examples.getName(), examples.getDescription())
                : addTableRowsTo(table, headers, rows, examples.getName(), examples.getDescription());
        exampleCount = examples.getRows().size() - 1;// table.getSize();
    }

    private void reinitializeExamples() {
        examplesRunning = true;
        currentExample = 0;
        exampleRows = new ArrayList<>();
    }

    private List getHeadersFrom(List examplesTableRows) {
        ExamplesTableRow headerRow = examplesTableRows.get(0);
        return headerRow.getCells();
    }

    private List> getValuesFrom(List examplesTableRows, List headers) {

        List> rows = Lists.newArrayList();

        for (int row = 1; row < examplesTableRows.size(); row++) {
            Map rowValues = Maps.newHashMap();
            int column = 0;
            for (String cellValue : examplesTableRows.get(row).getCells()) {
                String columnName = headers.get(column++);
                rowValues.put(columnName, cellValue);
            }
            rows.add(rowValues);
        }
        return rows;
    }

    private void addRow(List> exampleRows,
                        List headers,
                        ExamplesTableRow currentTableRow) {
        Map row = new HashMap<>();
        for (int j = 0; j < headers.size(); j++) {
            row.put(headers.get(j), currentTableRow.getCells().get(j));
        }
        exampleRows.add(row);
    }


    private DataTable thucydidesTableFrom(List headers,
                                          List> rows,
                                          String name,
                                          String description) {
        return DataTable.withHeaders(headers).andMappedRows(rows).andTitle(name).andDescription(description).build();
    }

    private DataTable addTableRowsTo(DataTable table, List headers,
                                     List> rows,
                                     String name,
                                     String description) {
        table.startNewDataSet(name, description);
        for (Map row : rows) {
            table.addRow(rowValuesFrom(headers, row));
        }
        return table;
    }

    private Map rowValuesFrom(List headers, Map row) {
        Map rowValues = Maps.newHashMap();
        for (String header : headers) {
            rowValues.put(header, row.get(header));
        }
        return ImmutableMap.copyOf(rowValues);
    }

    @Override
    public void startOfScenarioLifeCycle(Scenario scenario) {
        if (examplesRunning) {

            if (firstStep) {
                startScenario(scenario);
                StepEventBus.getEventBus().useExamplesFrom(table);
                firstStep = false;
            }
            startExample();
        } else {
            startScenario(scenario);
        }
    }

    private void startScenario(Scenario scenario) {
        StepEventBus.getEventBus().testStarted(scenario.getName());
        StepEventBus.getEventBus().addDescriptionToCurrentTest(scenario.getDescription());
        StepEventBus.getEventBus().addTagsToCurrentTest(convertCucumberTags(currentFeature.getTags()));
        StepEventBus.getEventBus().addTagsToCurrentTest(convertCucumberTags(scenario.getTags()));
        getThucydidesListeners().withDriver(ThucydidesWebDriverSupport.getDriver());
    }


    private List convertCucumberTags(List cucumberTags) {
        List tags = Lists.newArrayList();
        for (Tag tag : cucumberTags) {
            tags.add(TestTag.withValue(tag.getName().substring(1)));
        }
        return ImmutableList.copyOf(tags);
    }

    @Override
    public void endOfScenarioLifeCycle(Scenario scenario) {
        if (examplesRunning) {
            finishExample();
        } else {
            generateReports();
        }
    }

    private void startExample() {
        Map data = exampleRows.get(currentExample);
        StepEventBus.getEventBus().clearStepFailures();
        StepEventBus.getEventBus().exampleStarted(data);
        currentExample++;
    }

    private void finishExample() {
        StepEventBus.getEventBus().exampleFinished();
        exampleCount--;
        if (exampleCount == 0) {
            examplesRunning = false;
            generateReports();
        }
    }

    @Override
    public void background(Background background) {
        StepEventBus.getEventBus().setBackgroundDescription(background.getDescription());
    }

    @Override
    public void scenario(Scenario scenario) {
        clearScenarioResult();
        checkForPending(scenario);
        checkForSkipped(scenario);
    }

    @Override
    public void step(Step step) {
        stepQueue.add(step);
    }


    @Override
    public void done() {
        assureTestSuiteFinished();
    }

    @Override
    public void close() {
        assureTestSuiteFinished();
    }

    private void assureTestSuiteFinished() {
        if (currentFeature != null) {
            stepQueue.clear();
            StepEventBus.getEventBus().testSuiteFinished();
            StepEventBus.getEventBus().clear();
            Thucydides.done();
            table = null;
            firstStep = true;
        }
    }

    @Override
    public void eof() {

    }

    @Override
    public void before(Match match, Result result) {
    }

    @Override
    public void result(Result result) {
        Step currentStep = stepQueue.poll();
        if (Result.PASSED.equals(result.getStatus())) {
            StepEventBus.getEventBus().stepFinished();
        } else if (Result.FAILED.equals(result.getStatus())) {
            failed(stepTitleFrom(currentStep), result.getError());
        } else if (Result.SKIPPED.equals(result)) {
            StepEventBus.getEventBus().stepIgnored();
        } else if (Result.UNDEFINED.equals(result)) {
            StepEventBus.getEventBus().stepStarted(ExecutedStepDescription.withTitle(stepTitleFrom(currentStep)));
            StepEventBus.getEventBus().stepPending();
        }

        if (stepQueue.isEmpty()) {
            if (examplesRunning) { //finish enclosing step because testFinished resets the queue
                StepEventBus.getEventBus().stepFinished();
            }
            updatePendingResults();
            updateSkippedResults();
            StepEventBus.getEventBus().testFinished();
        }
    }

    private void updatePendingResults() {
        if (isPendingStory()) {
            StepEventBus.getEventBus().setAllStepsTo(TestResult.PENDING);
        }
    }

    private void updateSkippedResults() {
        if (isSkippedStory()) {
            StepEventBus.getEventBus().setAllStepsTo(TestResult.SKIPPED);
        }
    }


    public void failed(String stepTitle, Throwable cause) {
        Throwable rootCause = cause.getCause() != null ? cause.getCause() : cause;
        StepEventBus.getEventBus().updateCurrentStepTitle(stepTitle);
        if (isAssumptionFailure(rootCause)) {
            StepEventBus.getEventBus().assumptionViolated(rootCause.getMessage());
        } else {
            StepEventBus.getEventBus().stepFailed(new StepFailure(ExecutedStepDescription.withTitle(normalized(stepTitle)), rootCause));
        }
    }

    private boolean isAssumptionFailure(Throwable rootCause) {
        return (AssumptionViolatedException.class.isAssignableFrom(rootCause.getClass()));
    }

    @Override
    public void after(Match match, Result result) {
    }

    @Override
    public void match(Match match) {
        if (match instanceof StepDefinitionMatch) {
            Step currentStep = stepQueue.peek();
            String stepTitle = stepTitleFrom(currentStep);
            StepEventBus.getEventBus().stepStarted(ExecutedStepDescription.withTitle(stepTitle));
            StepEventBus.getEventBus().updateCurrentStepTitle(normalized(stepTitle));
        }
    }

    private String stepTitleFrom(Step currentStep) {
        return currentStep.getKeyword() + currentStep.getName();
    }

    @Override
    public void embedding(String mimeType, byte[] data) {
    }

    @Override
    public void write(String text) {
    }

    private synchronized void generateReports() {
        getReportService().generateReportsFor(getAllTestOutcomes());
    }

    public List getAllTestOutcomes() {
        return flatten(extract(baseStepListeners, on(BaseStepListener.class).getTestOutcomes()));
    }

    private String normalized(String value) {
        return value.replaceAll(OPEN_PARAM_CHAR, "{").replaceAll(CLOSE_PARAM_CHAR, "}");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy