public class App {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Callable<Boolean> worker1 = () -> run(new CustomConfiguration("P1"));
Callable<Boolean> worker2 = () -> run(new CustomConfiguration("P2"));
List<Callable<Boolean>> workers = Arrays.asList(worker1, worker2);
List<Future<Boolean>> futures = executorService.invokeAll(workers);
while (futures.stream().anyMatch(f -> !f.isDone())) {
System.out.println("Task is still not done...");
Thread.sleep(200);
}
System.out.println("All Done");
executorService.shutdown();
}
public static Boolean run(CustomConfiguration customConfiguration) {
Class clazz = App.class;
ClassLoader classLoader = clazz.getClassLoader();
ResourceLoader resourceLoader = new MultiLoader(classLoader);
CustomCucumberOptions customCustomCucumberOptions = new CustomCucumberOptions();
customCustomCucumberOptions.setFeatures(new String[]{"src/main/java/com/yvr"});
customCustomCucumberOptions.setGlue(new String[]{"com.yvr"});
customCustomCucumberOptions.setPlugin(new String[]{"pretty", "html:build/brazil-cucumber-tests/", "json:build/brazil-cucumber-tests/report.json"});
CustomRuntimeOptionsFactory runtimeOptionsFactory = new CustomRuntimeOptionsFactory(customCustomCucumberOptions, clazz);
runtimeOptionsFactory.getExtraArgs().add("--tags");
runtimeOptionsFactory.getExtraArgs().add("@Two");
RuntimeOptions runtimeOptions = runtimeOptionsFactory.create();
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
FeatureResultListener resultListener = new FeatureResultListener(runtimeOptions.reporter(classLoader), runtimeOptions.isStrict());
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
// Todo: How should I pass the customConfiguration to this run so every scenario has it?
for (CucumberFeature cucumberFeature : runtimeOptions.cucumberFeatures(resourceLoader)) {
cucumberFeature.run(
runtimeOptions.formatter(classLoader),
resultListener,
runtime);
}
Formatter formatter = runtimeOptions.formatter(classLoader);
formatter.done();
formatter.close();
runtime.printSummary();
if (!resultListener.isPassed()) {
throw new CucumberException(resultListener.getFirstError());
}
return true;
}
}
public class FirstFeatureGlue {
CustomConfiguration customConfiguration;
public FirstFeatureGlue(CustomConfiguration customConfiguration) {
this.customConfiguration = customConfiguration;
}
@Given("^A Customer (.*).$")
public void a_Customer(String customer) {
System.out.println("Customer: " + customer);
}
@When("^Do something.$")
public void do_something() {
System.out.println("Do Something, depending on: " + customConfiguration.getMyParameter());
}
@Then("^Something will happen.$")
public void something_will_happen() {
System.out.println("Something will happen");
}
}
// Todo: How should I pass the customConfiguration to this run so every scenario has it?
Cucumber v4 supports parallel execution over scenarios. You are executing different configurations of Cucumber in parallel. There are benefits to upgrading to v4 but not specifically for what you are trying to achieve.Now I am guess you're trying to pass some sort of environment configuration to your step definitions and execute against different environments in parallel. There are a few ways to do this but the least complicated is to configure multiple executions in your CI system and execute these in parallel. You configure each execution with a different parameter. Depending on this environment parameter your CustomConfiguration will return different values. For example if you are using Maven with the JUnit Runner you'd configure two jobs in your CI to execute `mvn verify -DENVIRONMENT=acceptance` and `mvn verify -DENVIRONMENT=integration`
class CustomConfigurationsContainerSingleton {
private volatile Map<Long, CustomConfiguration> parameters = new ConcurrentHashMap<>();
private static class InstanceHolder { public static CustomConfigurationsContainerSingleton instance = new CustomConfigurationsContainerSingleton(); }
private CustomConfigurationsContainerSingleton() { }
static CustomConfigurationsContainerSingleton getInstance() { return InstanceHolder.instance; }
public void set(CustomConfiguration customConfiguration) { parameters.put(Thread.currentThread().getId(), customConfiguration); }
public CustomConfiguration get() { return parameters.get(Thread.currentThread().getId()); }
public void remove() { parameters.remove(Thread.currentThread().getId()); }}
public class App {
private static final SimpleDateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Date datetime = new Date(); Callable<Boolean> worker1 = () -> run(datetime, "@First", new CustomConfiguration("P1")); Callable<Boolean> worker2 = () -> run(datetime, "@Second", new CustomConfiguration("P2"));
List<Callable<Boolean>> workers = Arrays.asList(worker1, worker2);
List<Future<Boolean>> futures = executorService.invokeAll(workers);
while (futures.stream().anyMatch(f -> !f.isDone())) { System.out.println("Task is still not done..."); Thread.sleep(200); }
System.out.println("All Done"); executorService.shutdown(); }
public static Boolean run(Date datetime, String tag, CustomConfiguration customConfiguration) {
Class clazz = App.class; ClassLoader classLoader = clazz.getClassLoader(); ResourceLoader resourceLoader = new MultiLoader(classLoader);
CustomCucumberOptions customCustomCucumberOptions = new CustomCucumberOptions(); customCustomCucumberOptions.setFeatures(new String[]{"src/main/java/com/yvr"}); customCustomCucumberOptions.setGlue(new String[]{"com.yvr"});
String outputFolder = "Out_" + TIMESTAMP.format(datetime) + "_" + tag + "/";
customCustomCucumberOptions.setPlugin(new String[]{"pretty", "html:build/" + outputFolder, "json:build/" + outputFolder + "report.json"});
CustomRuntimeOptionsFactory runtimeOptionsFactory = new CustomRuntimeOptionsFactory(customCustomCucumberOptions, clazz); runtimeOptionsFactory.getExtraArgs().add("--tags");
runtimeOptionsFactory.getExtraArgs().add(tag);
RuntimeOptions runtimeOptions = runtimeOptionsFactory.create();
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader); FeatureResultListener resultListener = new FeatureResultListener(runtimeOptions.reporter(classLoader), runtimeOptions.isStrict()); Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
try { System.out.println("Thread Id: " + Thread.currentThread().getId() + ", parameter: " + customConfiguration.getMyParameter());
CustomConfigurationsContainerSingleton.getInstance().set(customConfiguration);
for (CucumberFeature cucumberFeature : runtimeOptions.cucumberFeatures(resourceLoader)) { cucumberFeature.run( runtimeOptions.formatter(classLoader), resultListener, runtime); }
Formatter formatter = runtimeOptions.formatter(classLoader);
formatter.done(); formatter.close(); runtime.printSummary();
if (!resultListener.isPassed()) { throw new CucumberException(resultListener.getFirstError()); }
} finally { CustomConfigurationsContainerSingleton.getInstance().remove(); }
return true; }}
public class MainFeatureGlue {
@Given("^A Customer (.*).$") public void a_Customer(String customer) { System.out.println("Customer: " + customer); }
@When("^Do something.$") public void do_something() {
System.out.println("Thread Id: " + Thread.currentThread().getId() + ", do Something, depending on: " + CustomConfigurationsContainerSingleton.getInstance().get().getMyParameter());
}
@Then("^Something will happen.$") public void something_will_happen() { System.out.println("Something will happen"); }}
private volatile Map<Long, CustomConfiguration> parameters = new ConcurrentHashMap<>();
private volatile Map<Long, CustomConfiguration> parameters = new ConcurrentHashMap<>();
Wow. Okay. Good luck with that. :D
>private volatile Map<Long, CustomConfiguration> parameters = new ConcurrentHashMap<>();
This looks like something that would be best solved with a ThreadLocal.
class CustomConfigurationsContainerSingleton {
private final ThreadLocal threadLocal = new ThreadLocal();
private static class InstanceHolder {
public static CustomConfigurationsContainerSingleton instance = new CustomConfigurationsContainerSingleton();
}
private CustomConfigurationsContainerSingleton() {
}
static CustomConfigurationsContainerSingleton getInstance() {
return InstanceHolder.instance;
}
public void set(CustomConfiguration customConfiguration) {
threadLocal.set(customConfiguration);
}
public CustomConfiguration get() {
return (CustomConfiguration) threadLocal.get();
}
public void remove() {
threadLocal.remove();
}
}
class CustomConfigurationsContainerSingleton {
private final ThreadLocal<CustomConfiguration> threadLocal = new ThreadLocal<>();
private static class InstanceHolder {
public static CustomConfigurationsContainerSingleton instance = new CustomConfigurationsContainerSingleton();
}
private CustomConfigurationsContainerSingleton() {
}
static CustomConfigurationsContainerSingleton getInstance() {
return InstanceHolder.instance;
}
public void set(CustomConfiguration customConfiguration) {
threadLocal.set(customConfiguration);
}
public CustomConfiguration get() {
return threadLocal.get();
}
public void remove() {
threadLocal.remove();
}
}