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

io.cucumber.core.runner.Runner Maven / Gradle / Ivy

There is a newer version: 7.20.1
Show newest version
package io.cucumber.core.runner;

import io.cucumber.core.backend.Backend;
import io.cucumber.core.backend.CucumberBackendException;
import io.cucumber.core.backend.CucumberInvocationTargetException;
import io.cucumber.core.backend.ObjectFactory;
import io.cucumber.core.backend.StaticHookDefinition;
import io.cucumber.core.eventbus.EventBus;
import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.core.gherkin.Step;
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.core.snippets.SnippetGenerator;
import io.cucumber.core.stepexpression.StepTypeRegistry;
import io.cucumber.plugin.event.HookType;
import io.cucumber.plugin.event.Location;
import io.cucumber.plugin.event.SnippetsSuggestedEvent;
import io.cucumber.plugin.event.SnippetsSuggestedEvent.Suggestion;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;

import static io.cucumber.core.exception.ExceptionUtils.throwAsUncheckedException;
import static io.cucumber.core.runner.StackManipulation.removeFrameworkFrames;
import static java.util.Collections.emptyList;

public final class Runner {

    private static final Logger log = LoggerFactory.getLogger(Runner.class);

    private final CachingGlue glue;
    private final EventBus bus;
    private final Collection backends;
    private final Options runnerOptions;
    private final ObjectFactory objectFactory;
    private List snippetGenerators;

    public Runner(
            EventBus bus, Collection backends, ObjectFactory objectFactory, Options runnerOptions
    ) {
        this.bus = bus;
        this.runnerOptions = runnerOptions;
        this.backends = backends;
        this.glue = new CachingGlue(bus);
        this.objectFactory = objectFactory;
        List gluePaths = runnerOptions.getGlue();
        log.debug(() -> "Loading glue from " + gluePaths);
        for (Backend backend : backends) {
            log.debug(() -> "Loading glue for backend " + backend.getClass().getName());
            backend.loadGlue(this.glue, gluePaths);
        }
    }

    public EventBus getBus() {
        return bus;
    }

    public void runPickle(Pickle pickle) {
        try {
            StepTypeRegistry stepTypeRegistry = createTypeRegistryForPickle(pickle);
            snippetGenerators = createSnippetGeneratorsForPickle(stepTypeRegistry);

            // Java8 step definitions will be added to the glue here
            buildBackendWorlds();

            glue.prepareGlue(stepTypeRegistry);

            TestCase testCase = createTestCaseForPickle(pickle);
            testCase.run(bus);
        } finally {
            glue.removeScenarioScopedGlue();
            disposeBackendWorlds();
        }
    }

    private StepTypeRegistry createTypeRegistryForPickle(Pickle pickle) {
        String language = pickle.getLanguage();
        Locale locale = new Locale(language);
        return new StepTypeRegistry(locale);
    }

    public void runBeforeAllHooks() {
        executeHooks(glue.getBeforeAllHooks());
    }

    public void runAfterAllHooks() {
        executeHooks(glue.getAfterAllHooks());
    }

    private void executeHooks(List afterAllHooks) {
        ThrowableCollector throwableCollector = new ThrowableCollector();
        for (StaticHookDefinition staticHookDefinition : afterAllHooks) {
            throwableCollector.execute(() -> executeHook(staticHookDefinition));
        }
        Throwable throwable = throwableCollector.getThrowable();
        if (throwable != null) {
            throwAsUncheckedException(throwable);
        }
    }

    private void executeHook(StaticHookDefinition hookDefinition) {
        if (runnerOptions.isDryRun()) {
            return;
        }
        try {
            hookDefinition.execute();
        } catch (CucumberBackendException e) {
            CucumberException exception = new CucumberException(String.format("" +
                    "Could not invoke hook defined at '%s'.\n" +
                    "It appears there was a problem with the hook definition.",
                hookDefinition.getLocation()), e);
            throwAsUncheckedException(exception);
        } catch (CucumberInvocationTargetException e) {
            Throwable throwable = removeFrameworkFrames(e);
            throwAsUncheckedException(throwable);
        }
    }

    private List createSnippetGeneratorsForPickle(StepTypeRegistry stepTypeRegistry) {
        return backends.stream()
                .map(Backend::getSnippet)
                .filter(Objects::nonNull)
                .map(s -> new SnippetGenerator(s, stepTypeRegistry.parameterTypeRegistry()))
                .collect(Collectors.toList());
    }

    private void buildBackendWorlds() {
        objectFactory.start();
        for (Backend backend : backends) {
            backend.buildWorld();
        }
    }

    private TestCase createTestCaseForPickle(Pickle pickle) {
        if (pickle.getSteps().isEmpty()) {
            return new TestCase(bus.generateId(), emptyList(), emptyList(), emptyList(), pickle,
                runnerOptions.isDryRun());
        }

        List testSteps = createTestStepsForPickleSteps(pickle);
        List beforeHooks = createTestStepsForBeforeHooks(pickle.getTags());
        List afterHooks = createTestStepsForAfterHooks(pickle.getTags());
        return new TestCase(bus.generateId(), testSteps, beforeHooks, afterHooks, pickle, runnerOptions.isDryRun());
    }

    private void disposeBackendWorlds() {
        for (Backend backend : backends) {
            backend.disposeWorld();
        }
        objectFactory.stop();
    }

    private List createTestStepsForPickleSteps(Pickle pickle) {
        List testSteps = new ArrayList<>();

        for (Step step : pickle.getSteps()) {
            PickleStepDefinitionMatch match = matchStepToStepDefinition(pickle, step);
            List afterStepHookSteps = createAfterStepHooks(pickle.getTags());
            List beforeStepHookSteps = createBeforeStepHooks(pickle.getTags());
            testSteps.add(new PickleStepTestStep(bus.generateId(), pickle.getUri(), step, beforeStepHookSteps,
                afterStepHookSteps, match));
        }

        return testSteps;
    }

    private List createTestStepsForBeforeHooks(List tags) {
        return createTestStepsForHooks(tags, glue.getBeforeHooks(), HookType.BEFORE);
    }

    private List createTestStepsForAfterHooks(List tags) {
        return createTestStepsForHooks(tags, glue.getAfterHooks(), HookType.AFTER);
    }

    private PickleStepDefinitionMatch matchStepToStepDefinition(Pickle pickle, Step step) {
        try {
            PickleStepDefinitionMatch match = glue.stepDefinitionMatch(pickle.getUri(), step);
            if (match != null) {
                return match;
            }
            emitSnippetSuggestedEvent(pickle, step);
            return new UndefinedPickleStepDefinitionMatch(pickle.getUri(), step);
        } catch (AmbiguousStepDefinitionsException e) {
            return new AmbiguousPickleStepDefinitionsMatch(pickle.getUri(), step, e);
        }
    }

    private void emitSnippetSuggestedEvent(Pickle pickle, Step step) {
        List snippets = generateSnippetsForStep(step);
        if (snippets.isEmpty()) {
            return;
        }
        Suggestion suggestion = new Suggestion(step.getText(), snippets);
        Location scenarioLocation = pickle.getLocation();
        Location stepLocation = step.getLocation();
        SnippetsSuggestedEvent event = new SnippetsSuggestedEvent(bus.getInstant(), pickle.getUri(), scenarioLocation,
            stepLocation, suggestion);
        bus.send(event);
    }

    private List createAfterStepHooks(List tags) {
        return createTestStepsForHooks(tags, glue.getAfterStepHooks(), HookType.AFTER_STEP);
    }

    private List createBeforeStepHooks(List tags) {
        return createTestStepsForHooks(tags, glue.getBeforeStepHooks(), HookType.BEFORE_STEP);
    }

    private List createTestStepsForHooks(
            List tags, Collection hooks, HookType hookType
    ) {
        return hooks.stream()
                .filter(hook -> hook.matches(tags))
                .map(hook -> new HookTestStep(bus.generateId(), hookType, new HookDefinitionMatch(hook)))
                .collect(Collectors.toList());
    }

    private List generateSnippetsForStep(Step step) {
        List snippets = new ArrayList<>();
        for (SnippetGenerator snippetGenerator : snippetGenerators) {
            List snippet = snippetGenerator.getSnippet(step, runnerOptions.getSnippetType());
            snippets.addAll(snippet);
        }
        return snippets;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy