Skip to content

Commit

Permalink
Merge pull request #48 from virtualansoftware/parallelExecution
Browse files Browse the repository at this point in the history
Parallel execution
  • Loading branch information
elans3 committed Apr 19, 2021
2 parents 6a45c9f + 0b47aa0 commit f501176
Show file tree
Hide file tree
Showing 40 changed files with 1,035 additions and 56 deletions.
4 changes: 2 additions & 2 deletions modules/idaithalam/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<artifactId>idaithalam</artifactId>
<packaging>jar</packaging>
<name>idaithalam-platform-testing</name>
<version>1.2.2-SNAPSHOT</version>
<version>1.2.4-SNAPSHOT</version>
<properties>
<cucumblan-api.version>1.1.9</cucumblan-api.version>
<cucumblan-api.version>1.1.11-SNAPSHOT</cucumblan-api.version>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.virtualan.idaithalam.contract;

import java.net.URL;
import java.net.URLClassLoader;

/**
* The type Execution classloader.
*/
public class ExecutionClassloader extends URLClassLoader {

/**
* Instantiates a new Execution classloader.
*
* @param urls the urls
* @param classLoader the class loader
*/
public ExecutionClassloader(URL[] urls, ClassLoader classLoader) {
super(urls, classLoader);
}

@Override
public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
return super.loadClass(name, resolve);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
Expand All @@ -35,6 +36,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.Logger;
import net.masterthought.cucumber.Configuration;
Expand Down Expand Up @@ -108,20 +110,27 @@ public static int validateContract(String featureHeading, String path)
byte exitStatus;
try {
String fileIndex = UUID.randomUUID().toString();
VirtualanClassLoader classLoader = new VirtualanClassLoader(IdaithalamExecutor.class.getClassLoader());
feature = featureHeading;
addConfToClasspath(classLoader, path);
generateFeatureFile(path);
String[] argv = getCucumberOptions(path);
VirtualanClassLoader classLoaderParnet = new VirtualanClassLoader(IdaithalamExecutor.class.getClassLoader());
ExecutionClassloader classLoader = addConfToClasspath(classLoaderParnet, path);
generateFeatureFile(classLoader, path);
String[] argv = getCucumberOptions(path, fileIndex);
exitStatus = Main.run(argv, classLoader);
generateReport(path, fileIndex);
generateReport(featureHeading, path, fileIndex);
} catch (IOException | UnableToProcessException e) {
LOGGER.severe("Provide appropriate input data? : " + e.getMessage());
throw new UnableToProcessException("Provide appropriate input data? : " + e.getMessage());
}
return exitStatus;
}

private static String[] getCucumberOptions(String path, String build) {
return new String[]{
"-p", path == null ? "json:target/cucumber-"+build+".json" : "json:"+path+"/cucumber-"+build+".json",
"-p", path == null ? "html:target/cucumber-html-report.html" : "html:"+path+"/cucumber-html-report.html",
"--glue", "io.virtualan.cucumblan.core", "", path == null ? "conf/feature/" : path+"/feature/",
};
}

/**
* Validate contract int.
*
Expand All @@ -136,14 +145,12 @@ public static int validateContract(String featureHeading, String path, int runId
byte exitStatus;
try {
String fileIndex = UUID.randomUUID().toString();
VirtualanClassLoader classLoader = new VirtualanClassLoader(IdaithalamExecutor.class.getClassLoader());
feature = featureHeading;
addConfToClasspath(classLoader, path);
addConfToClasspath( classLoader, path + File.separator + runId);
generateFeatureFile(path + File.separator + runId);
String[] argv = getCucumberOptions(path + File.separator + runId);
VirtualanClassLoader classLoaderParent = new VirtualanClassLoader(IdaithalamExecutor.class.getClassLoader());
ExecutionClassloader classLoader = addConfToClasspath( classLoaderParent, path + File.separator + runId);
generateFeatureFile(classLoader, path + File.separator + runId);
String[] argv = getCucumberOptions(path + File.separator + runId, fileIndex);
exitStatus = Main.run(argv, classLoader);
generateReport(path + File.separator + runId, fileIndex);
generateReport(featureHeading, path + File.separator + runId, fileIndex);
} catch (IOException | UnableToProcessException e) {
LOGGER.severe("Provide appropriate input data? : " + e.getMessage());
throw new UnableToProcessException("Provide appropriate input data? : " + e.getMessage());
Expand All @@ -155,13 +162,13 @@ public static int validateContract(String featureHeading, String path, int runId
* generate cucumber report
*/

private static void generateReport(String path, String index) {
private static void generateReport(String featureHeading, String path, String index) {
path = path == null ? "target" : path;
File reportOutputDirectory = new File(path);
List<String> jsonFiles = new ArrayList<>();
jsonFiles.add(path+"/cucumber.json");
jsonFiles.add(path+"/cucumber-"+index+".json");
String buildNumber = index;
String projectName = feature + " - Testing";
String projectName = featureHeading + " - Testing";
Configuration configuration = new Configuration(reportOutputDirectory, projectName);
configuration.setNotFailingStatuses(Collections.singleton(Status.SKIPPED));
configuration.setBuildNumber(buildNumber);
Expand Down Expand Up @@ -192,44 +199,37 @@ private static String[] getCucumberOptions(String path) {
* @throws MalformedURLException
*/

private static void addConfToClasspath(VirtualanClassLoader classLoader, String path) throws MalformedURLException {
private static ExecutionClassloader addConfToClasspath(VirtualanClassLoader classLoader, String path) throws MalformedURLException {
path = path == null ? "conf" : path;
ExecutionClassloader cl = new ExecutionClassloader(new URL[] {new File(path).toURI().toURL()}, classLoader);
Thread.currentThread().setContextClassLoader(cl);
return cl;
}

/**
* The type Execution classloader.
*/
static class ExecutionClassloader extends URLClassLoader {
public static Properties readCucumblan(ClassLoader classLoader) {
Properties propertiesForInstance = new Properties();

/**
* Instantiates a new Execution classloader.
*
* @param urls the urls
* @param classLoader the class loader
*/
ExecutionClassloader(URL[] urls, ClassLoader classLoader) {
super(urls, classLoader);
}

@Override
public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
return super.loadClass(name, resolve);
try {
InputStream stream = classLoader.getResourceAsStream("cucumblan.properties");
if (stream != null) {
propertiesForInstance.load(stream);
} else {
LOGGER.warning("unable to load cucumblan.properties");
}
} catch (Exception var3) {
LOGGER.warning("cucumblan.properties not found");
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
return propertiesForInstance;
}

/**
* Generate the feature file for the provided collection
*
* @throws IOException
*/

private static void generateFeatureFile(String path) throws IOException, UnableToProcessException {
private static void generateFeatureFile(ExecutionClassloader classloader, String path) throws IOException, UnableToProcessException {
if(path == null || !new File(path).exists()) {
if (!new File("conf").exists()) {
new File("conf").mkdir();
Expand All @@ -239,9 +239,11 @@ private static void generateFeatureFile(String path) throws IOException, UnableT
if (!new File(path+"/feature").exists()) {
new File(path+"/feature").mkdir();
}
List<List<Item>> items = FeatureFileGenerator.generateFeatureFile(path);
String okta = ApplicationConfiguration.getProperty("service.api.okta");
String featureTitle = ApplicationConfiguration.getProperty("virtualan.data.heading");
Properties properties = readCucumblan(classloader);
List<List<Item>> items = FeatureFileGenerator.generateFeatureFile(properties, path);

String okta = properties.getProperty("service.api.okta");
String featureTitle = properties.getProperty("virtualan.data.heading");

for(int i=0; i< items.size(); i++){
MustacheFactory mf = new DefaultMustacheFactory();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.virtualan.idaithalam.core.api;

import io.virtualan.idaithalam.core.domain.ExecutionPlanner;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

@Slf4j
public class MassApiExecutor {

private static final int NTHREDS = 1;

public static boolean invoke(String configMapper) throws InterruptedException {
Yaml yaml = new Yaml(new Constructor(ExecutionPlanner.class));
InputStream inputStream = MassApiExecutor.class.getClassLoader()
.getResourceAsStream(configMapper);
ExecutionPlanner executionPlanner = yaml.load(inputStream);
ExecutorService executor = Executors
.newFixedThreadPool(executionPlanner.getApiExecutor().size());
List<Future<Integer>> futures = new ArrayList<>();
executionPlanner.getApiExecutor().stream().forEach(
x -> {
Callable worker = new ParallelExecutor(x);
Future future = executor.submit(worker);
futures.add(future);
});

// This will make the executor accept no new threads
// and finish all existing threads in the queue
executor.shutdown();
while (!executor.isTerminated()) {
}
// Wait until all threads are finish
executor.awaitTermination(executionPlanner.getTimeout(), TimeUnit.MINUTES);
boolean bool = futures.stream().anyMatch(x -> {
try {
return x.get() != 0;
} catch (InterruptedException | ExecutionException e) {
return false;
}
});

log.info("Finished all api execution");
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.virtualan.idaithalam.core.api;

import io.virtualan.idaithalam.contract.IdaithalamExecutor;
import io.virtualan.idaithalam.core.domain.ApiExecutorParam;
import io.virtualan.idaithalam.core.generator.ExcelToCollectionGenerator;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ParallelExecutor implements Callable<Integer> {

private String outputDir;
private String inputExcel;
private String env;
private String reportTitle;
private Map<String, String> cucumblanProperies;

ParallelExecutor(ApiExecutorParam apiExecutorPrarm) {
this.outputDir = apiExecutorPrarm.getOutputDir();
this.inputExcel = apiExecutorPrarm.getInputExcel();
this.env = apiExecutorPrarm.getEnv();
this.reportTitle = apiExecutorPrarm.getReportTitle();
this.cucumblanProperies = apiExecutorPrarm.getCucumblanProperies();
}


@Override
public Integer call() {
int status = 0;
try {
File f = new File(outputDir);
if (!f.exists()) {
f.mkdirs();
}
ExcelToCollectionGenerator.createCollection(null, inputExcel, outputDir);

if(cucumblanProperies != null && !cucumblanProperies.isEmpty()) {
File file = new File(outputDir +File.separator+"cucumblan.properties");
Properties properties = new Properties();
properties.load(new FileInputStream(file));
cucumblanProperies.entrySet().stream().forEach(
x -> {
properties.setProperty(x.getKey(), x.getValue());
}
);
ExcelToCollectionGenerator.createPrpos(outputDir,
(Map)properties,
"cucumblan.properties");
}
//Generate feature and summary page html report for the selected testcase from the excel
status = IdaithalamExecutor
.validateContract(env + " : " + reportTitle,
outputDir);
} catch (Exception e) {
log.warn(env + " : " + reportTitle + " : " + e.getMessage());
status = 1;
e.printStackTrace();
}
log.info(env + " : " + reportTitle + " : status : " + status);
return status;
}
}
Loading

0 comments on commit f501176

Please sign in to comment.