Embedder.java
package org.jbehave.core.embedder;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.jbehave.core.ConfigurableEmbedder;
import org.jbehave.core.Embeddable;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.embedder.MetaFilter.MetaMatcher;
import org.jbehave.core.embedder.StoryTimeouts.TimeoutParser;
import org.jbehave.core.embedder.executors.FixedThreadExecutors;
import org.jbehave.core.failures.BatchFailures;
import org.jbehave.core.failures.FailingUponPendingStep;
import org.jbehave.core.junit.AnnotatedEmbedderRunner;
import org.jbehave.core.junit.AnnotatedEmbedderUtils;
import org.jbehave.core.model.Story;
import org.jbehave.core.model.StoryMaps;
import org.jbehave.core.reporters.ReportsCount;
import org.jbehave.core.reporters.StepdocReporter;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.reporters.ViewGenerator;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.ProvidedStepsFactory;
import org.jbehave.core.steps.StepFinder;
import org.jbehave.core.steps.Stepdoc;
/**
* The Embedder is a facade allowing all functionality to be embedded into other
* run contexts, such as IDEs (e.g. via JUnit support) or CLIs (via Ant or
* Maven).
*/
public class Embedder {
protected StoryMapper storyMapper;
protected EmbedderMonitor embedderMonitor;
protected EmbedderClassLoader classLoader;
protected EmbedderControls embedderControls;
protected EmbedderFailureStrategy embedderFailureStrategy;
protected Configuration configuration;
protected InjectableStepsFactory stepsFactory;
protected List<String> metaFilters;
protected Map<String,MetaMatcher> metaMatchers;
protected Properties systemProperties;
protected ExecutorService executorService;
protected boolean executorServiceCreated;
protected PerformableTree performableTree;
protected StoryManager storyManager;
protected TimeoutParser[] timeoutParsers;
public Embedder() {
this(new PrintStreamEmbedderMonitor());
}
public Embedder(EmbedderMonitor embedderMonitor) {
this(new StoryMapper(), new PerformableTree(), embedderMonitor);
}
public Embedder(StoryMapper storyMapper, PerformableTree performableTree, EmbedderMonitor embedderMonitor) {
this.storyMapper = storyMapper;
this.performableTree = performableTree;
this.embedderMonitor = embedderMonitor;
}
public void mapStoriesAsPaths(List<String> storyPaths) {
EmbedderControls embedderControls = embedderControls();
embedderMonitor.usingControls(embedderControls);
if (embedderControls.skip()) {
embedderMonitor.storiesSkipped(storyPaths);
return;
}
processSystemProperties();
StoryManager storyManager = storyManager();
for (String storyPath : storyPaths) {
Story story = storyManager.storyOfPath(storyPath);
embedderMonitor.mappingStory(storyPath, metaFilters());
storyMapper.map(story, new MetaFilter("", embedderMonitor));
for (String filter : metaFilters) {
storyMapper.map(story, new MetaFilter(filter, embedderMonitor));
}
}
generateMapsView(storyMapper.getStoryMaps());
}
private void generateMapsView(StoryMaps storyMaps) {
Configuration configuration = configuration();
StoryReporterBuilder builder = configuration.storyReporterBuilder();
File outputDirectory = builder.outputDirectory();
Properties viewResources = builder.viewResources();
ViewGenerator viewGenerator = configuration.viewGenerator();
try {
embedderMonitor.generatingMapsView(outputDirectory, storyMaps, viewResources);
viewGenerator.generateMapsView(outputDirectory, storyMaps, viewResources);
} catch (RuntimeException e) {
embedderMonitor.mapsViewGenerationFailed(outputDirectory, storyMaps, viewResources, e);
throw new ViewGenerationFailed(outputDirectory, storyMaps, viewResources, e);
}
}
public void runAsEmbeddables(List<String> classNames) {
EmbedderControls embedderControls = embedderControls();
embedderMonitor.usingControls(embedderControls);
if (embedderControls.skip()) {
embedderMonitor.embeddablesSkipped(classNames);
return;
}
BatchFailures failures = new BatchFailures(embedderControls.verboseFailures());
for (Embeddable embeddable : embeddables(classNames, classLoader())) {
String name = embeddable.getClass().getName();
try {
embedderMonitor.runningEmbeddable(name);
embeddable.useEmbedder(this);
embeddable.run();
} catch (Throwable e) {
if (embedderControls.batch()) {
// collect and postpone decision to throw exception
failures.put(name, e);
} else {
if (ignoreFailure(embedderControls)) {
embedderMonitor.embeddableFailed(name, e);
} else {
throw new RunningEmbeddablesFailed(name, e);
}
}
}
}
if (embedderControls.batch() && failures.size() > 0) {
if (ignoreFailure(embedderControls)) {
embedderMonitor.batchFailed(failures);
} else {
throw new RunningEmbeddablesFailed(failures);
}
}
}
private boolean ignoreFailure(EmbedderControls embedderControls) {
boolean ignore = embedderControls.ignoreFailureInStories();
if (embedderControls.generateViewAfterStories()) {
ignore = ignore && embedderControls.ignoreFailureInView();
}
return ignore;
}
private List<Embeddable> embeddables(List<String> classNames, EmbedderClassLoader classLoader) {
List<Embeddable> embeddables = new ArrayList<>();
for (String className : classNames) {
if (!classLoader.isAbstract(className)) {
embeddables.add(classLoader.newInstance(Embeddable.class, className));
}
}
return embeddables;
}
public void runStoriesWithAnnotatedEmbedderRunner(List<String> classNames) {
EmbedderClassLoader classLoader = classLoader();
for (String className : classNames) {
embedderMonitor.runningWithAnnotatedEmbedderRunner(className);
AnnotatedEmbedderRunner runner = AnnotatedEmbedderUtils.annotatedEmbedderRunner(className, classLoader);
try {
Object annotatedInstance = runner.createTest();
if (annotatedInstance instanceof Embeddable) {
((Embeddable) annotatedInstance).run();
} else {
embedderMonitor.annotatedInstanceNotOfType(annotatedInstance, Embeddable.class);
}
} catch (Throwable e) {
throw new AnnotatedEmbedderRunFailed(runner, e);
}
}
}
public void runStoriesAsPaths(List<String> storyPaths) {
processSystemProperties();
EmbedderControls embedderControls = embedderControls();
embedderMonitor.usingControls(embedderControls);
if (embedderControls.skip()) {
embedderMonitor.storiesSkipped(storyPaths);
return;
}
try {
// set up run context
StoryManager storyManager = storyManager();
MetaFilter filter = metaFilter();
BatchFailures failures = new BatchFailures(embedderControls.verboseFailures());
// run stories
storyManager.runStoriesAsPaths(storyPaths, filter, failures);
// handle any failures
handleFailures(failures);
} finally {
// generate reports view regardless of failures in running stories
// (if configured to do so)
try {
if (embedderControls.generateViewAfterStories()) {
generateReportsView();
}
} finally {
// shutdown regardless of failures in reports view
shutdownExecutorService();
// reset story manager as executor service is shutdown
storyManager = null;
}
}
}
private void handleFailures(BatchFailures failures) {
if (failures.size() > 0) {
if (embedderControls().ignoreFailureInStories()) {
embedderMonitor.batchFailed(failures);
} else {
embedderFailureStrategy().handleFailures(failures);
}
}
}
private void handleFailures(ReportsCount count) {
boolean failed = count.failed();
if (configuration().pendingStepStrategy() instanceof FailingUponPendingStep) {
failed = failed || count.pending();
}
if (failed) {
if (embedderControls().ignoreFailureInView()) {
embedderMonitor.reportsViewFailures(count);
} else {
embedderFailureStrategy().handleFailures(count);
}
}
}
public void generateReportsView() {
StoryReporterBuilder builder = configuration().storyReporterBuilder();
File outputDirectory = builder.outputDirectory();
List<String> formatNames = builder.formatNames(true);
generateReportsView(outputDirectory, formatNames, builder.viewResources());
}
public void generateReportsView(File outputDirectory, List<String> formats, Properties viewResources) {
if (embedderControls().skip()) {
embedderMonitor.reportsViewNotGenerated();
return;
}
ViewGenerator viewGenerator = configuration().viewGenerator();
try {
embedderMonitor.generatingReportsView(outputDirectory, formats, viewResources);
viewGenerator.generateReportsView(outputDirectory, formats, viewResources);
} catch (RuntimeException e) {
embedderMonitor.reportsViewGenerationFailed(outputDirectory, formats, viewResources, e);
throw new ViewGenerationFailed(outputDirectory, formats, viewResources, e);
}
ReportsCount count = viewGenerator.getReportsCount();
embedderMonitor.reportsViewGenerated(count);
handleFailures(count);
}
public void generateSurefireReport() {
StoryReporterBuilder builder = configuration().storyReporterBuilder();
if (builder.hasSurefireReporter()) {
builder.surefireReporter().generate(storyManager().performableRoot(), builder.outputDirectory());
}
}
public void reportStepdocs() {
reportStepdocs(configuration(), stepsFactory().createCandidateSteps());
}
public void reportStepdocs(Configuration configuration, List<CandidateSteps> candidateSteps) {
StepFinder finder = configuration.stepFinder();
StepdocReporter reporter = configuration.stepdocReporter();
List<Object> stepsInstances = finder.stepsInstances(candidateSteps);
reporter.stepdocs(finder.stepdocs(candidateSteps), stepsInstances);
}
public void reportStepdocsAsEmbeddables(List<String> classNames) {
EmbedderControls embedderControls = embedderControls();
if (embedderControls.skip()) {
embedderMonitor.embeddablesSkipped(classNames);
return;
}
for (Embeddable embeddable : embeddables(classNames, classLoader())) {
if (embeddable instanceof ConfigurableEmbedder) {
ConfigurableEmbedder configurableEmbedder = (ConfigurableEmbedder) embeddable;
Embedder configuredEmbedder = configurableEmbedder.configuredEmbedder();
List<CandidateSteps> steps = configuredEmbedder.stepsFactory().createCandidateSteps();
reportStepdocs(configuredEmbedder.configuration(), steps);
} else {
embedderMonitor.embeddableNotConfigurable(embeddable.getClass().getName());
}
}
}
public void reportMatchingStepdocs(String stepAsString) {
Configuration configuration = configuration();
List<CandidateSteps> candidateSteps = stepsFactory().createCandidateSteps();
StepFinder finder = configuration.stepFinder();
StepdocReporter reporter = configuration.stepdocReporter();
List<Stepdoc> matching = finder.findMatching(stepAsString, candidateSteps);
List<Object> stepsInstances = finder.stepsInstances(candidateSteps);
reporter.stepdocsMatching(stepAsString, matching, stepsInstances);
}
public void processSystemProperties() {
Properties properties = systemProperties();
embedderMonitor.processingSystemProperties(properties);
if (!properties.isEmpty()) {
for (Object key : properties.keySet()) {
String name = (String) key;
String value = properties.getProperty(name);
System.setProperty(name, value);
embedderMonitor.systemPropertySet(name, value);
}
}
}
public EmbedderClassLoader classLoader() {
if (classLoader == null) {
this.classLoader = new EmbedderClassLoader(this.getClass().getClassLoader());
}
return classLoader;
}
public Configuration configuration() {
if (configuration == null) {
this.configuration = new MostUsefulConfiguration();
}
configureThreads(configuration, embedderControls().threads());
return configuration;
}
public InjectableStepsFactory stepsFactory() {
if (stepsFactory == null) {
stepsFactory = new ProvidedStepsFactory();
}
return stepsFactory;
}
public EmbedderControls embedderControls() {
if (embedderControls == null) {
embedderControls = new EmbedderControls();
}
return embedderControls;
}
public EmbedderMonitor embedderMonitor() {
return embedderMonitor;
}
public EmbedderFailureStrategy embedderFailureStrategy() {
if (embedderFailureStrategy == null) {
this.embedderFailureStrategy = new ThrowingRunningStoriesFailed();
}
return embedderFailureStrategy;
}
public boolean hasExecutorService() {
return executorService != null;
}
public ExecutorService executorService() {
if (executorService == null) {
executorService = createExecutorService();
executorServiceCreated = true;
}
return executorService;
}
/**
* Creates a {@link ThreadPoolExecutor} using the number of threads defined
* in the {@link EmbedderControls#threads()}
*
* @return An ExecutorService
*/
private ExecutorService createExecutorService() {
return new FixedThreadExecutors().create(embedderControls());
}
/**
* Shuts down executor service, if it was created by Embedder.
* ExecutorServices provided by the
* {@link #useExecutorService(ExecutorService)} need to be managed by the
* provider.
*/
protected void shutdownExecutorService() {
if (executorServiceCreated) {
executorService.shutdownNow();
executorService = null;
executorServiceCreated = false;
}
}
public StoryManager storyManager() {
if (storyManager == null) {
storyManager = createStoryManager();
}
return storyManager;
}
private StoryManager createStoryManager() {
return new StoryManager(configuration(), stepsFactory(), embedderControls(), embedderMonitor(),
executorService(), performableTree(), timeoutParsers());
}
protected void configureThreads(Configuration configuration, int threads) {
StoryReporterBuilder reporterBuilder = configuration.storyReporterBuilder();
reporterBuilder.withMultiThreading(threads > 1);
configuration.useStoryReporterBuilder(reporterBuilder);
}
public List<String> metaFilters() {
if (metaFilters == null) {
metaFilters = new ArrayList<>();
}
return metaFilters;
}
public Map<String,MetaMatcher> metaMatchers() {
if (metaMatchers == null) {
metaMatchers = new HashMap<>();
}
return metaMatchers;
}
public MetaFilter metaFilter() {
return new MetaFilter(StringUtils.join(metaFilters(), " "), embedderMonitor, metaMatchers());
}
public PerformableTree performableTree() {
return performableTree;
}
public Properties systemProperties() {
if (systemProperties == null) {
systemProperties = new Properties();
}
return systemProperties;
}
public TimeoutParser[] timeoutParsers() {
if (timeoutParsers == null) {
timeoutParsers = new TimeoutParser[]{};
}
return timeoutParsers;
}
public void useClassLoader(EmbedderClassLoader classLoader) {
this.classLoader = classLoader;
}
public void useConfiguration(Configuration configuration) {
this.configuration = configuration;
}
public void useStepsFactory(InjectableStepsFactory stepsFactory) {
this.stepsFactory = stepsFactory;
}
public void useEmbedderControls(EmbedderControls embedderControls) {
this.embedderControls = embedderControls;
}
public void useEmbedderFailureStrategy(EmbedderFailureStrategy failureStategy) {
this.embedderFailureStrategy = failureStategy;
}
public void useEmbedderMonitor(EmbedderMonitor embedderMonitor) {
this.embedderMonitor = embedderMonitor;
}
public void useExecutorService(ExecutorService executorService) {
this.executorService = executorService;
embedderMonitor.usingExecutorService(executorService);
}
public void useMetaFilters(List<String> metaFilters) {
this.metaFilters = metaFilters;
}
public void useMetaMatchers(Map<String,MetaMatcher> metaMatchers) {
this.metaMatchers = metaMatchers;
}
public void usePerformableTree(PerformableTree performableTree) {
this.performableTree = performableTree;
}
public void useSystemProperties(Properties systemProperties) {
this.systemProperties = systemProperties;
}
public void useTimeoutParsers(TimeoutParser... timeoutParsers) {
this.timeoutParsers = timeoutParsers;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
public static interface EmbedderFailureStrategy {
void handleFailures(BatchFailures failures);
void handleFailures(ReportsCount count);
}
public static class ThrowingRunningStoriesFailed implements EmbedderFailureStrategy {
@Override
public void handleFailures(BatchFailures failures) {
throw new RunningStoriesFailed(failures);
}
@Override
public void handleFailures(ReportsCount count) {
throw new RunningStoriesFailed(count);
}
}
@SuppressWarnings("serial")
public static class AnnotatedEmbedderRunFailed extends RuntimeException {
public AnnotatedEmbedderRunFailed(AnnotatedEmbedderRunner runner, Throwable cause) {
super("Annotated embedder run failed with runner " + runner.toString(), cause);
}
}
@SuppressWarnings("serial")
public static class RunningEmbeddablesFailed extends RuntimeException {
public RunningEmbeddablesFailed(String name, Throwable failure) {
super("Failure in running embeddable: " + name, failure);
}
public RunningEmbeddablesFailed(BatchFailures failures) {
super("Failures in running embeddables: " + failures);
}
}
@SuppressWarnings("serial")
public static class RunningStoriesFailed extends RuntimeException {
public RunningStoriesFailed(ReportsCount reportsCount) {
super("Failures in running stories: " + reportsCount);
}
public RunningStoriesFailed(BatchFailures failures) {
super("Failures in running stories: " + failures);
}
}
@SuppressWarnings("serial")
public static class ViewGenerationFailed extends RuntimeException {
public ViewGenerationFailed(File outputDirectory, List<String> formats, Properties viewResources,
RuntimeException cause) {
super("View generation failed to " + outputDirectory + " for formats " + formats + " and resources "
+ viewResources, cause);
}
public ViewGenerationFailed(File outputDirectory, StoryMaps storyMaps, Properties viewResources,
RuntimeException cause) {
super("View generation failed to " + outputDirectory + " for story maps " + storyMaps + " for resources "
+ viewResources, cause);
}
}
}