...26import net.serenitybdd.cucumber.suiteslicing.ScenarioFilter;27import net.serenitybdd.cucumber.suiteslicing.TestStatistics;28import net.serenitybdd.cucumber.suiteslicing.WeightedCucumberScenarios;29import net.serenitybdd.cucumber.util.PathUtils;30import net.serenitybdd.cucumber.util.Splitter;31import net.thucydides.core.ThucydidesSystemProperty;32import net.thucydides.core.guice.Injectors;33import net.thucydides.core.util.EnvironmentVariables;34import net.thucydides.core.webdriver.Configuration;35import org.junit.runner.Description;36import org.junit.runner.manipulation.NoTestsRemainException;37import org.junit.runner.notification.RunNotifier;38import org.junit.runners.ParentRunner;39import org.junit.runners.model.InitializationError;40import org.junit.runners.model.RunnerScheduler;41import org.junit.runners.model.Statement;42import org.slf4j.Logger;43import org.slf4j.LoggerFactory;44import java.net.URI;45import java.nio.file.Paths;46import java.util.ArrayList;47import java.util.Collection;48import java.util.List;49import java.util.Optional;50import java.util.concurrent.atomic.AtomicInteger;51import java.util.function.Function;52import java.util.function.Predicate;53import static java.util.stream.Collectors.toList;54import static net.thucydides.core.ThucydidesSystemProperty.SERENITY_BATCH_COUNT;55import static net.thucydides.core.ThucydidesSystemProperty.SERENITY_BATCH_NUMBER;56import static net.thucydides.core.ThucydidesSystemProperty.SERENITY_FORK_COUNT;57import static net.thucydides.core.ThucydidesSystemProperty.SERENITY_FORK_NUMBER;58/**59 * Glue code for running Cucumber via Serenity.60 * Sets up Serenity reporting and instrumentation.61 */62public class CucumberSerenityRunner extends ParentRunner<FeatureRunner> {63 private static final Logger LOGGER = LoggerFactory.getLogger(CucumberSerenityRunner.class);64 private final List<FeatureRunner> children = new ArrayList<FeatureRunner>();65 private final EventBus bus;66 private final ThreadLocalRunnerSupplier runnerSupplier;67 private static ThreadLocal<RuntimeOptions> RUNTIME_OPTIONS = new ThreadLocal<>();68 private final List<CucumberFeature> features;69 private final Plugins plugins;70 private boolean multiThreadingAssumed = false;71 /**72 * Constructor called by JUnit.73 *74 * @param clazz the class with the @RunWith annotation.75 * @throws InitializationError if there is another problem76 */77 public CucumberSerenityRunner(Class clazz) throws InitializationError {78 super(clazz);79 ClassLoader classLoader = clazz.getClassLoader();80 ResourceLoader resourceLoader = new MultiLoader(classLoader);81 Assertions.assertNoCucumberAnnotatedMethods(clazz);82 83 // Parse the options early to provide fast feedback about invalid options84 RuntimeOptions annotationOptions = new CucumberOptionsAnnotationParser(resourceLoader)85 .withOptionsProvider(new JUnitCucumberOptionsProvider())86 .parse(clazz)87 .build();88 RuntimeOptions runtimeOptions = new EnvironmentOptionsParser(resourceLoader)89 .parse(Env.INSTANCE)90 .build(annotationOptions);91 runtimeOptions.addUndefinedStepsPrinterIfSummaryNotDefined();92 JUnitOptions junitAnnotationOptions = new JUnitOptionsParser()93 .parse(clazz)94 .build();95 JUnitOptions junitOptions = new JUnitOptionsParser()96 .parse(runtimeOptions.getJunitOptions())97 .setStrict(runtimeOptions.isStrict())98 .build(junitAnnotationOptions);99 setRuntimeOptions(runtimeOptions);100 FeatureLoader featureLoader = new FeatureLoader(resourceLoader);101 FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(featureLoader, runtimeOptions);102 // Parse the features early. Don't proceed when there are lexer errors103 this.features = featureSupplier.get();104 ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);105 this.plugins = new Plugins(classLoader, new PluginFactory(),runtimeOptions);106 this.bus = new TimeServiceEventBus(TimeService.SYSTEM);107 Configuration systemConfiguration = Injectors.getInjector().getInstance(Configuration.class);108 SerenityReporter reporter = new SerenityReporter(systemConfiguration, resourceLoader);109 addSerenityReporterPlugin(plugins,reporter);110 BackendSupplier backendSupplier = new BackendModuleBackendSupplier(resourceLoader, classFinder, runtimeOptions);111 this.runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier);112 Filters filters = new Filters(runtimeOptions);113 for (CucumberFeature cucumberFeature : features) {114 FeatureRunner featureRunner = new FeatureRunner(cucumberFeature, filters, runnerSupplier, junitOptions);115 if (!featureRunner.isEmpty()) {116 children.add(featureRunner);117 }118 }119 }120 private static RuntimeOptions DEFAULT_RUNTIME_OPTIONS;121 public static void setRuntimeOptions(RuntimeOptions runtimeOptions) {122 RUNTIME_OPTIONS.set(runtimeOptions);123 DEFAULT_RUNTIME_OPTIONS = runtimeOptions;124 }125 public static RuntimeOptions currentRuntimeOptions() {126 return (RUNTIME_OPTIONS.get() != null) ? RUNTIME_OPTIONS.get() : DEFAULT_RUNTIME_OPTIONS;127 }128 private static Collection<String> environmentSpecifiedTags(List<?> existingTags) {129 EnvironmentVariables environmentVariables = Injectors.getInjector().getInstance(EnvironmentVariables.class);130 String tagsExpression = ThucydidesSystemProperty.TAGS.from(environmentVariables,"");131 List<String> existingTagsValues = existingTags.stream().map(Object::toString).collect(toList());132 return Splitter.on(",").trimResults().omitEmptyStrings().splitToList(tagsExpression).stream()133 .map(CucumberSerenityRunner::toCucumberTag).filter(t -> !existingTagsValues.contains(t)).collect(toList());134 }135 private static String toCucumberTag(String from) {136 String tag = from.replaceAll(":","=");137 if (tag.startsWith("~@") || tag.startsWith("@")) { return tag; }138 if (tag.startsWith("~")) { return "~@" + tag.substring(1); }139 return "@" + tag;140 }141 public static Runtime createSerenityEnabledRuntime(ResourceLoader resourceLoader,142 ClassLoader classLoader,143 RuntimeOptions runtimeOptions,144 Configuration systemConfiguration) {145 ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);146 setRuntimeOptions(runtimeOptions);...