public interface GatfPlugin {
	void doExecute(GatfPluginConfig configuration, List<String> files) throws Exception;
	void setProject(Object project);
	void shutdown();
	void initilaizeContext(GatfExecutorConfig configuration, boolean flag) throws Exception;
	AcceptanceTestContext getContext();
	void setContext(AcceptanceTestContext context);
	List<TestCase> getAllTestCases(AcceptanceTestContext context, Set<String> relativeFileNames, List<String> targetFileNames);
	TestCase getAuthTestCase();
	void invokeServerLogApi(boolean success, TestCaseReport testCaseReport, TestCaseExecutorUtil testCaseExecutorUtil, boolean isFetchFailureLogs);
	void doSeleniumTest(GatfExecutorConfig configuration, List<String> files);
	GatfSelDebugger debugSeleniumTest(GatfExecutorConfig configuration, String selscript, String configPath);
}
 public interface GatfPluginConfig {

}
 @XStreamAlias("gatf-testdata-config")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataConfig implements Serializable {

	private static final long serialVersionUID = 1L;
	
	@XStreamConverter(value=MapKeyValueCustomXstreamConverter.class)
	private Map<String, String> globalVariables;
	
	private List<String> compareEnvBaseUrls;
	
	private List<GatfTestDataSource> dataSourceList;

	private List<GatfTestDataProvider> providerTestDataList;
	
	private List<GatfTestDataSourceHook> dataSourceHooks;
	
	private List<GatfTestDataSource> dataSourceListForProfiling;

	public Map<String, String> getGlobalVariables() {
		return globalVariables;
	}

	public void setGlobalVariables(Map<String, String> globalVariables) {
		this.globalVariables = globalVariables;
	}

	public List<GatfTestDataProvider> getProviderTestDataList() {
		return providerTestDataList;
	}

	public void setProviderTestDataList(
			List<GatfTestDataProvider> providerTestDataList) {
		this.providerTestDataList = providerTestDataList;
	}

	public List<String> getCompareEnvBaseUrls() {
		return compareEnvBaseUrls;
	}

	public void setCompareEnvBaseUrls(List<String> compareEnvBaseUrls) {
		this.compareEnvBaseUrls = compareEnvBaseUrls;
	}

	public List<GatfTestDataSource> getDataSourceList() {
		return dataSourceList;
	}

	public void setDataSourceList(List<GatfTestDataSource> dataSourceList) {
		this.dataSourceList = dataSourceList;
	}

	public List<GatfTestDataSourceHook> getDataSourceHooks() {
		return dataSourceHooks;
	}

	public void setDataSourceHooks(List<GatfTestDataSourceHook> dataSourceHooks) {
		this.dataSourceHooks = dataSourceHooks;
	}

	public List<GatfTestDataSource> getDataSourceListForProfiling() {
		return dataSourceListForProfiling;
	}

	public void setDataSourceListForProfiling(List<GatfTestDataSource> dataSourceListForProfiling) {
		this.dataSourceListForProfiling = dataSourceListForProfiling;
	}
}
 @XStreamAlias("gatf-testdata-provider")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataProvider implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String dataSourceName;
	
	private String providerName;
	
	private String providerClass;
	
	private String sourceProperties;
	
	private String providerProperties;
	
	private String queryStr;
	
	private Boolean enabled;
	
	protected String[] args;
	
	private Boolean live;

	public String getProviderName() {
		return providerName;
	}

	public void setProviderName(String providerName) {
		this.providerName = providerName;
	}

	public Boolean isEnabled() {
		return enabled!=null && enabled;
	}

	public void setEnabled(Boolean enabled) {
		this.enabled = enabled;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String getProviderProperties() {
		return providerProperties;
	}

	public void setProviderProperties(String providerProperties) {
		this.providerProperties = providerProperties;
	}

	public String getSourceProperties() {
		return sourceProperties;
	}

	public void setSourceProperties(String sourceProperties) {
		this.sourceProperties = sourceProperties;
	}

	public String getQueryStr() {
		return queryStr;
	}

	public void setQueryStr(String queryStr) {
		this.queryStr = queryStr;
	}

	public String[] getArgs() {
		return args;
	}

	public void setArgs(String[] args) {
		this.args = args;
	}

	public String getProviderClass() {
		return providerClass;
	}

	public void setProviderClass(String providerClass) {
		this.providerClass = providerClass;
	}

	public Boolean isLive() {
		return live!=null && live;
	}

	public void setLive(Boolean live) {
		this.live = live;
	}

	public GatfTestDataProvider(){}

	public GatfTestDataProvider(GatfTestDataProvider other) {
		super();
		this.dataSourceName = other.dataSourceName;
		this.providerName = other.providerName;
		this.providerClass = other.providerClass;
		this.sourceProperties = other.sourceProperties;
		this.providerProperties = other.providerProperties;
		this.queryStr = other.queryStr;
		this.enabled = other.enabled;
		if(other.args!=null) {
		    this.args = (String[])Arrays.asList(other.args).toArray(new String[other.args.length]);
		}
		this.live = other.live;
	}
}
 @XStreamAlias("gatf-testdata-source")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataSource implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String dataSourceName;
	
	private String dataSourceClass;
	
	private int poolSize = 1;
	
	private long newResourceCheckoutTimeMs = 1000;
	
	private String[] args;

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String[] getArgs() {
		return args;
	}

	public void setArgs(String[] args) {
		this.args = args;
	}

	public String getDataSourceClass() {
		return dataSourceClass;
	}

	public void setDataSourceClass(String dataSourceClass) {
		this.dataSourceClass = dataSourceClass;
	}

	public int getPoolSize() {
		return poolSize;
	}

	public void setPoolSize(int poolSize) {
		this.poolSize = poolSize;
	}
	
	public long getNewResourceCheckoutTimeMs() {
		return newResourceCheckoutTimeMs;
	}

	public void setNewResourceCheckoutTimeMs(long newResourceCheckoutTimeMs) {
		this.newResourceCheckoutTimeMs = newResourceCheckoutTimeMs;
	}
}
 @XStreamAlias("gatf-testdata-source-hook")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataSourceHook implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String hookName;
	
	private String hookClass;
	
	private String dataSourceName;
	
	private String[] queryStrs;
	
	private boolean executeOnStart;
	
	private boolean executeOnShutdown;
	
	public String getHookName() {
		return hookName;
	}

	public void setHookName(String hookName) {
		this.hookName = hookName;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String[] getQueryStrs() {
		return queryStrs;
	}

	public void setQueryStrs(String[] queryStrs) {
		this.queryStrs = queryStrs;
	}

	public String getHookClass() {
		return hookClass;
	}

	public void setHookClass(String hookClass) {
		this.hookClass = hookClass;
	}

	public boolean isExecuteOnStart() {
		return executeOnStart;
	}

	public void setExecuteOnStart(boolean executeOnStart) {
		this.executeOnStart = executeOnStart;
	}

	public boolean isExecuteOnShutdown() {
		return executeOnShutdown;
	}

	public void setExecuteOnShutdown(boolean executeOnShutdown) {
		this.executeOnShutdown = executeOnShutdown;
	}
}
 public class DistributedAcceptanceContext implements Serializable {

	private static final long serialVersionUID = 1L;

	public static enum Command
	{
		CONFIG_SHARE_REQ,
		CONFIG_SHARE_RES,
		TESTS_SHARE_REQ,
		TESTS_SHARE_RES,
		LOAD_TESTS_RES,
		SELENIUM_REQ,
		SELENIUM_RES,
		INVALID
	}
	
	private String node = null;
	
	private GatfExecutorConfig config = null;
	
	private Map<String, String> httpHeaders = null;
	
	private Map<String, String> soapEndpoints = null;
	
	private Map<String, String> soapMessages = null;
	
	private Map<String, String> soapActions = new HashMap<String, String>();
	
	List<Object[]> selTestdata = new ArrayList<Object[]>();
    
    private int index;
    
    private static Pool<Kryo> pool = new Pool<Kryo>(true, true) {
		@Override
		protected Kryo create() {
			Kryo k = new Kryo();
			k.setRegistrationRequired(false); 
            k.setClassLoader(DistributedGatfListener.class.getClassLoader());
            return k;
		}
	};
	
	public String getNode() {
		return node;
	}

	public void setNode(String node) {
		this.node = node;
	}

	public GatfExecutorConfig getConfig() {
		return config;
	}

	public void setConfig(GatfExecutorConfig config) {
		this.config = config;
	}

	public Map<String, String> getHttpHeaders() {
		return httpHeaders;
	}

	public void setHttpHeaders(Map<String, String> httpHeaders) {
		this.httpHeaders = httpHeaders;
	}

	public Map<String, String> getSoapEndpoints() {
		return soapEndpoints;
	}

	public void setSoapEndpoints(Map<String, String> soapEndpoints) {
		this.soapEndpoints = soapEndpoints;
	}

	public Map<String, String> getSoapMessages() {
		return soapMessages;
	}

	public void setSoapMessages(Map<String, String> soapMessages) {
		this.soapMessages = soapMessages;
	}

	public Map<String, String> getSoapActions() {
		return soapActions;
	}

	public void setSoapActions(Map<String, String> soapActions) {
		this.soapActions = soapActions;
	}
	
	public File getResourceFile(String filename) {
		File basePath = new File(config.getTestCasesBasePath());
	    File testPath = new File(basePath, config.getTestCaseDir());
	    File resource = new File(testPath, filename);
        if(!resource.exists()) {
            resource = new File(basePath, filename);
        }
		return resource;
    }

    public List<Object[]> getSelTestdata() {
        return selTestdata;
    }

    public void setSelTestdata(List<Object[]> selTestdata) {
        this.selTestdata = selTestdata;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
    
	public static void ser(Output output, Object o) {
        Kryo kryo = pool.obtain();
        try {
            kryo.writeClassAndObject(output, o);
            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.free(kryo);
        }
    }
    
    @SuppressWarnings("unchecked")
	public static <T> T unser(Input input) {
        Kryo kryo = pool.obtain();
        try {
            Object r = kryo.readClassAndObject(input);
            return (T)r;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.free(kryo);
        }
        return null;
    }
}
 public class DistributedGatfListener {

	private static final Logger logger = Logger.getLogger(DistributedGatfListener.class.getSimpleName());
	
	public static void main(String[] args) throws Exception {
		String workingDir = System.getProperty("user.dir");
	    int port = 9567;
	    if(args.length>1) {
	        try {
                port = Integer.parseInt(args[1]);
            } catch (Exception e) {
                logger.info("Invalid port number specified for listener, defaulting to 9567");
            }
	    }
	    if(args.length>2) {
	        try {
	        	workingDir = args[2].trim();
	        	if(!new File(workingDir).exists()) {
	        		throw new RuntimeException("Invalid working directory");
	        	}
            } catch (Exception e) {
            	throw new RuntimeException("Invalid working directory");
            }
	    }
	    
	    final String wd = workingDir;
		ServerSocket server = new ServerSocket(port);
		logger.info("Distributed GATF node listening on port "+port);
		try {
			while(true) {
				final Socket client = server.accept();
				
				Output oos = new Output(client.getOutputStream());
				Input ios = new Input(client.getInputStream());
				
				new Thread(new Runnable() {
					public void run() {
						try {
							handleCommand(ios, oos, wd);
						} catch (Exception e) {
							e.printStackTrace();
						} finally {
							if(client!=null)
							{
								try {
									client.close();
								} catch (IOException e) {
									e.printStackTrace();
								}
							}
						}
					}
				}).start();
			}
		} finally {
			server.close();
		}
	}
	
	private static void handleCommand(Input ois, final Output oos, final String workingDir) throws Exception {
		
		logger.info("Got a new distributed GATF request...");
		
		DistributedAcceptanceContext context = null;
		DistributedTestContext tContext = null;
		try {
			
			Command command = Command.values()[ois.readInt()];
			logger.info("Received command - " + command);
			if(command==Command.CONFIG_SHARE_REQ) {
				context = DistributedAcceptanceContext.unser(ois);
				if(context!=null) {
					oos.writeInt(Command.CONFIG_SHARE_RES.ordinal());
					oos.flush();
					logger.info("Fetched GATF configuration...");
				} else {
					oos.writeInt(Command.INVALID.ordinal());
					oos.flush();
					logger.info("Invalid GATF configuration received...");
				}
			} else {
				oos.writeInt(Command.INVALID.ordinal());
				oos.flush();
				logger.info("Invalid Command received...");
			}
			
			command = Command.values()[ois.readInt()];;
			logger.info("Received command - " + command);
			if(command==Command.TESTS_SHARE_REQ) {
				tContext = DistributedAcceptanceContext.unser(ois);
				if(tContext!=null) {
					oos.writeInt(Command.TESTS_SHARE_RES.ordinal());
					oos.flush();
					logger.info("Fetched GATF tests ...");
					
					logger.info("Started executing GATF tests...");
					GatfTestCaseExecutorUtil mojo = new GatfTestCaseExecutorUtil();
					
					boolean isLoadTestingEnabled = context.getConfig().isLoadTestingEnabled() && context.getConfig().getLoadTestingTime()>10000;
					
					Thread dlreporter = new Thread(new Runnable() {
						public void run() {
							try {
								while(true) {
									Object entry = RuntimeReportUtil.getDLEntry();
									if(entry!=null) {
										oos.writeInt(Command.LOAD_TESTS_RES.ordinal());
										DistributedAcceptanceContext.ser(oos, entry);
										oos.flush();
									}
									Thread.sleep(500);
								}
							} catch (Exception e) {
							    Object entry = null;
								while((entry=RuntimeReportUtil.getDLEntry())!=null)
								{
									if(entry!=null) {
										oos.writeInt(Command.LOAD_TESTS_RES.ordinal());
										DistributedAcceptanceContext.ser(oos, entry);
										oos.flush();
									}
								}
							}
						}
					});
					if(isLoadTestingEnabled) {
					    dlreporter.start();  
					}
					
					context.getConfig().setTestCasesBasePath(workingDir);
					context.getConfig().setOutFilesBasePath(workingDir);
					logger.info("Current working directory is: " + workingDir);
					
					GatfExecutorConfig localConfig = getConfig("gatf-config.xml", context.getConfig().getTestCasesBasePath());
					if(localConfig!=null) {
					    if(localConfig.getJavaHome()!=null) {
					        context.getConfig().setJavaHome(localConfig.getJavaHome());
					    }
                        if(localConfig.getTestCasesBasePath()!=null) {
                        	context.getConfig().setTestCasesBasePath(localConfig.getTestCasesBasePath());
                        }
					}
					
					String[] remoteJavaVersion = context.getConfig().getJavaVersion().split("\\.");
					String[] localJavaVersion = System.getProperty("java.version").split("\\.");
					if(remoteJavaVersion[0].equals(localJavaVersion[0])) {
					    if(remoteJavaVersion[1].compareTo(localJavaVersion[1])>0) {
					        logger.severe("Local java version is less than the remote java version, please upgrade local java to version >= " + context.getConfig().getJavaVersion());
					        throw new RuntimeException("Invalid java version found");
					    }
					} else if(remoteJavaVersion[0].compareTo(localJavaVersion[0])>0) {
					    logger.severe("Local java version is less than the remote java version, please upgrade local java to version >= " + context.getConfig().getJavaVersion());
					    throw new RuntimeException("Invalid java version found");
					}
					
					DistributedTestStatus report = mojo.handleDistributedTests(context, tContext);
					mojo.shutdown();
					Thread.sleep(2000);
					
					if(isLoadTestingEnabled) {
                        dlreporter.interrupt();
                        Thread.sleep(3000);
                    }
					
					oos.writeInt(Command.TESTS_SHARE_RES.ordinal());
					oos.flush();
					
					String fileName = UUID.randomUUID().toString()+".zip";
					report.setZipFileName(fileName);
					
					DistributedAcceptanceContext.ser(oos, report);
					oos.flush();
					logger.info("Writing GATF results...");
					
		        	File resource = new File(workingDir, context.getConfig().getOutFilesDir());
		        	
					ReportHandler.zipDirectory(resource, new String[]{".html",".csv"}, fileName, false, true);
					
					File zipFile = new File(resource, fileName);
					FileInputStream fis = new FileInputStream(zipFile);
					IOUtils.copy(fis, oos);
					fis.close();
					oos.flush();
					logger.info("Done Writing GATF results...");
				} else {
					oos.writeInt(Command.INVALID.ordinal());
					oos.flush();
					logger.info("Invalid GATF tests received...");
				}
			} else if(command==Command.SELENIUM_REQ) {
				
				File gcdir = new File(FileUtils.getTempDirectory(), "dist-gatf-code");
		        if(gcdir.exists()) {
		        	FileUtils.deleteDirectory(gcdir);
		        }
				gcdir.mkdir();
				String fileName = UUID.randomUUID().toString()+".zip";
				File zipFile = new File(gcdir, fileName);
	        	FileOutputStream fos = new FileOutputStream(zipFile);
	        	long length = ois.readLong();
	        	for (int i=0;i<length;i++) {
	        		fos.write(ois.read());
				}
				fos.flush();
				fos.close();
				
				ReportHandler.unzipZipFile(zipFile, gcdir.getAbsolutePath());
				zipFile.delete();
				
				gcdir = new File(gcdir, "gatf-code");
				
				boolean isLoadTestingEnabled = context.getConfig().isLoadTestingEnabled() && context.getConfig().getLoadTestingTime()>10000;
				
				final DistributedAcceptanceContext context1 = context;
				Thread dlreporter = new Thread(new Runnable() {
                    public void run() {
                        try {
                            while(true) {
                                Object entry = RuntimeReportUtil.getDLEntry();
                                if(entry!=null) {
                                    if(entry instanceof String) {
                                        String sentry = context1.getNode() + "|" + entry;
                                        entry = sentry;
                                    }
                                    oos.writeInt(Command.LOAD_TESTS_RES.ordinal());
                                    DistributedAcceptanceContext.ser(oos, entry);
                                    oos.flush();
                                }
                                Thread.sleep(500);
                            }
                        } catch (Exception e) {
                            Object entry = null;
                            while((entry=RuntimeReportUtil.getDLEntry())!=null)
                            {
                                if(entry!=null) {
                                    if(entry instanceof String) {
                                        String sentry = context1.getNode() + "|" + entry;
                                        entry = sentry;
                                    }
                                    oos.writeInt(Command.LOAD_TESTS_RES.ordinal());
                                    DistributedAcceptanceContext.ser(oos, entry);
                                    oos.flush();
                                }
                            }
                        }
                    }
                });
				if(isLoadTestingEnabled) {
				    dlreporter.start();
				}
				
				context.getConfig().setTestCasesBasePath(workingDir);
                context.getConfig().setOutFilesBasePath(workingDir);
                logger.info("Current working directory is: " + workingDir);
                
                GatfExecutorConfig localConfig = getConfig("gatf-config.xml", context.getConfig().getTestCasesBasePath());
                if(localConfig!=null) {
                    if(localConfig.getJavaHome()!=null) {
                        context.getConfig().setJavaHome(localConfig.getJavaHome());
                    }
                    if(localConfig.getSeleniumDriverConfigs()!=null && localConfig.getSeleniumDriverConfigs().length>0) {
                        SeleniumDriverConfig[] lconfigs = localConfig.getSeleniumDriverConfigs();
                        SeleniumDriverConfig[] rconfigs = context.getConfig().getSeleniumDriverConfigs();
                        for (int i=0;i<rconfigs.length;i++) {
                            SeleniumDriverConfig rseleniumDriverConfig = rconfigs[i];
                            for (SeleniumDriverConfig lseleniumDriverConfig : lconfigs) {
                                if(rseleniumDriverConfig.getDriverName().equals(lseleniumDriverConfig.getDriverName()) && lseleniumDriverConfig.getPath()!=null) {
                                    rconfigs[i] = lseleniumDriverConfig;
                                    break;
                                }
                            }
                        }
                        context.getConfig().setSeleniumDriverConfigs(rconfigs);
                        context.getConfig().setJavaHome(localConfig.getJavaHome());
                        if(localConfig.getTestCasesBasePath()!=null) {
                            if(new File(localConfig.getTestCasesBasePath()).exists()) {
                                context.getConfig().setTestCasesBasePath(localConfig.getTestCasesBasePath());
                            }
                        }
                    }
                }
                
                String[] remoteJavaVersion = context.getConfig().getJavaVersion().split("\\.");
                String[] localJavaVersion = System.getProperty("java.version").split("\\.");
                if(remoteJavaVersion[0].equals(localJavaVersion[0])) {
                    if(remoteJavaVersion[1].compareTo(localJavaVersion[1])>0) {
                        logger.severe("Local java version is less than the remote java version, please upgrade local java to version >= " + context.getConfig().getJavaVersion());
                        throw new RuntimeException("Invalid java version found");
                    }
                } else if(remoteJavaVersion[0].compareTo(localJavaVersion[0])>0) {
                    logger.severe("Local java version is less than the remote java version, please upgrade local java to version >= " + context.getConfig().getJavaVersion());
                    throw new RuntimeException("Invalid java version found");
                }
				
                System.out.println(gcdir.getAbsolutePath());
				URL[] urls = new URL[1];
	            urls[0] = gcdir.toURI().toURL();
				URLClassLoader classLoader = new URLClassLoader(urls, DistributedGatfListener.class.getClassLoader());
				Thread.currentThread().setContextClassLoader(classLoader);
				
				List<Class<SeleniumTest>> tests = new ArrayList<Class<SeleniumTest>>();
				List<String> testClassNames = DistributedAcceptanceContext.unser(ois);
				for (String clsname : testClassNames) {
					@SuppressWarnings("unchecked")
					Class<SeleniumTest> loadedClass = (Class<SeleniumTest>)classLoader.loadClass(clsname);
					tests.add(loadedClass);
				}
				
				tContext = DistributedAcceptanceContext.unser(ois);
				
				oos.writeInt(Command.SELENIUM_RES.ordinal());
				oos.flush();
				
				if(tests==null || tests.size()==0 || context.getConfig().isValidSeleniumRequest()) {
					boolean driverfound = true;
					for (SeleniumDriverConfig selConf : context.getConfig().getSeleniumDriverConfigs())
		            {
					    if(!new File(selConf.getPath()).exists()) {
	                        Path p = Paths.get(selConf.getPath());
	                        File df = context.getResourceFile(p.getFileName().toString());
	                        driverfound &= df.exists();
	                        if(df.exists()) {
	                            selConf.setPath(df.getAbsolutePath());
	                            System.setProperty(selConf.getName(), df.getAbsolutePath());
	                        }
	                    } else {
	                        driverfound &= true;
	                        System.setProperty(selConf.getName(), selConf.getPath());
	                    }
		            }
	                    
                    if(driverfound) {
                        oos.writeInt(0);
                        oos.flush();
                        logger.info("Selenium Test Request");
                        
                        GatfTestCaseExecutorUtil mojo = new GatfTestCaseExecutorUtil();
                        DistributedTestStatus report = mojo.handleDistributedSeleniumTests(context, tests, tContext);
                        mojo.shutdown();
                        Thread.sleep(2000);
                        
                        if(isLoadTestingEnabled) {
                            dlreporter.interrupt();
                            Thread.sleep(3000);
                        }
                        
                        oos.writeInt(Command.TESTS_SHARE_RES.ordinal());
                        oos.flush();
                        
                        fileName = UUID.randomUUID().toString()+".zip";
                        report.setZipFileName(fileName);
                        
                        DistributedAcceptanceContext.ser(oos, report);
                        oos.flush();
                        logger.info("Writing Selenium results...");
                        
                        File resource = new File(workingDir, context.getConfig().getOutFilesDir());
                        
                        String runPrefix = "DRun-" + tContext.getIndex();
                        
                        ReportHandler.zipDirectory(resource, new String[]{runPrefix}, fileName, false, false);
                        
                        zipFile = new File(resource, fileName);
                        FileInputStream fis = new FileInputStream(zipFile);
                        IOUtils.copy(fis, oos);
                        fis.close();
                        oos.flush();
                        logger.info("Done Writing Selenium results...");
                    } else {
                        oos.writeInt(1);
                        oos.flush();
                        logger.info("Selenium Test Request");
		            }
					
				} else {
					oos.writeInt(2);
					oos.flush();
					logger.info("Selenium Test Request");
				}
			} else {
				oos.writeInt(Command.INVALID.ordinal());
				oos.flush();
				logger.info("Invalid Command received...");
			}
			
		} catch (Exception e) {
			oos.write(0);
			oos.flush();
			logger.info("Error occurred during distributed GATF execution...");
			throw e;
		}
	}
	
	private static GatfExecutorConfig getConfig(String configFile, String testCasesBasePath) {
        GatfExecutorConfig configuration = null;
        if(configFile!=null) {
            try {
                File resource = null;
                File basePath = new File(testCasesBasePath);
                resource = new File(basePath, configFile);
                if(resource.exists()) {
                	if(configFile.trim().endsWith(".xml")) {
	                    XStream xstream = new XStream(new DomDriver("UTF-8"));
	                   
	                    xstream.allowTypes(new Class[]{GatfExecutorConfig.class,
	                            GatfTestDataConfig.class, GatfTestDataProvider.class});
	                    xstream.processAnnotations(new Class[]{GatfExecutorConfig.class,
	                             GatfTestDataConfig.class, GatfTestDataProvider.class});
	                    xstream.alias("gatf-testdata-source", GatfTestDataSource.class);
	                    xstream.alias("gatf-testdata-provider", GatfTestDataProvider.class);
	                    xstream.alias("gatf-testdata-source-hook", GatfTestDataSourceHook.class);
	                    xstream.alias("gatfTestDataConfig", GatfTestDataConfig.class);
	                    xstream.alias("seleniumDriverConfigs", SeleniumDriverConfig[].class);
	                    xstream.alias("seleniumDriverConfig", SeleniumDriverConfig.class);
	                    xstream.alias("testCaseHooksPaths", String[].class);
	                    xstream.alias("testCaseHooksPath", String.class);
	                    xstream.alias("args", String[].class);
	                    xstream.alias("arg", String.class);
	                    xstream.alias("testCaseHooksPaths", String[].class);
	                    xstream.alias("testCaseHooksPath", String.class);
	                    xstream.alias("queryStrs", String[].class);
	                    xstream.alias("queryStr", String.class);
	                    xstream.alias("distributedNodes", String[].class);
	                    xstream.alias("distributedNode", String.class);
	                    xstream.alias("ignoreFiles", String[].class);
	                    xstream.alias("orderedFiles", String[].class);
	                    xstream.alias("string", String.class);
	                    xstream.alias("seleniumScripts", String[].class);
	                    xstream.alias("seleniumScript", String.class);
	                    
	                    configuration = (GatfExecutorConfig)xstream.fromXML(resource);
                	} else if(configFile.trim().endsWith(".json")) {
                		configuration = WorkflowContextHandler.OM.readValue(resource, GatfExecutorConfig.class);
                	} else {
                		throw new RuntimeException("Invalid Config file, please provide either an xml or a json config file");
                	}
                	
                    if(configuration.getTestCasesBasePath()==null)
                        configuration.setTestCasesBasePath(testCasesBasePath);
                    
                    if(configuration.getOutFilesBasePath()==null)
                        configuration.setOutFilesBasePath(testCasesBasePath);
                    
                    if(configuration.getTestCaseDir()==null)
                        configuration.setTestCaseDir("");
                    
                    if(configuration.getNumConcurrentExecutions()==null)
                        configuration.setNumConcurrentExecutions(1);
                    
                    if(configuration.getHttpConnectionTimeout()==null)
                        configuration.setHttpConnectionTimeout(10000);
                    
                    if(configuration.getHttpRequestTimeout()==null)
                        configuration.setHttpRequestTimeout(10000);
                    
                    if(!configuration.isHttpCompressionEnabled())
                        configuration.setHttpCompressionEnabled(true);
                    
                    if(configuration.getConcurrentUserSimulationNum()==null)
                        configuration.setConcurrentUserSimulationNum(1);
                    
                    if(configuration.getLoadTestingReportSamples()==null)
                        configuration.setLoadTestingReportSamples(1);
                    
                    if(configuration.getConcurrentUserRampUpTime()==null)
                        configuration.setConcurrentUserRampUpTime(10000L);
                    
                    if(configuration.isEnabled()==null)
                        configuration.setEnabled(true);
                    
                    if(configuration.getRepeatSuiteExecutionNum()==null)
                        configuration.setRepeatSuiteExecutionNum(0);
                }
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        }
        return configuration;
    }
}
 public class DistributedGatfTester {

	private static final Logger logger = Logger.getLogger(DistributedGatfTester.class.getSimpleName());
	
	public static class DistributedConnection {
		private Socket sock;
		private String node;
		private Output oos = null;
		private Input ois = null;
		
		public String toString()
		{
			return node;
		}
	}
	
	public DistributedConnection distributeContext(String node, AcceptanceTestContext context, List<Object[]> testdata)
	{
		Socket client = null;
		DistributedConnection conn = null;
		try {
			logger.info("Connecting to node " + node);
			
			int port = 9567;
	        if(node.indexOf("@")!=-1) {
	            try {
	                String sp = node.substring(node.indexOf("@")+1);
	                node = node.substring(0, node.indexOf("@"));
	                port = Integer.parseInt(sp);
	            } catch (Exception e) {
	                logger.info("Invalid port number specified for distributed listener, defaulting to 9567");
	            }
	        }
			
			client = new Socket(node, port);
			DistributedAcceptanceContext disContext = context.getDistributedContext(node, testdata);
			
			Output oos = new Output(client.getOutputStream());
			Input ois = new Input(client.getInputStream());
			
			logger.info("Sending GATF configuration to node " + node);
			oos.writeInt(Command.CONFIG_SHARE_REQ.ordinal());
			DistributedAcceptanceContext.ser(oos, disContext);
			oos.flush();
			logger.info("Sent GATF configuration to node " + node);
			
			Command command = Command.values()[ois.readInt()];
			if(command==Command.CONFIG_SHARE_RES) {
				conn = new DistributedConnection();
				conn.sock = client;
				conn.node = node;
				conn.ois = ois;
				conn.oos = oos;
				logger.info("Sending GATF configuration Successful to node " + node);
			} else {
				logger.info("Sending GATF configuration Failed to node " + node);
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("Sending GATF configuration Failed to node " + node);
			if(client!=null)
			{
				try {
					client.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		return conn;
	}
	
	public FutureTask<DistributedTestStatus> distributeTests(List<TestCase> simTestCases, 
			final DistributedConnection connection, boolean dorep, int index, int numberOfRuns,
			final AcceptanceTestContext context, Set<String> relativeFileNames)
	{
		if(connection==null)return null;
		
		FutureTask<DistributedTestStatus> task = null;
		try {
			DistributedTestContext testContext = new DistributedTestContext();
			testContext.setSimTestCases(simTestCases);
			testContext.setIndex(index);
			testContext.setNumberOfRuns(numberOfRuns);
			testContext.setRelativeFileNames(relativeFileNames);
			
			logger.info("Sending GATF tests to node " + connection.node);
			connection.oos.writeInt(Command.TESTS_SHARE_REQ.ordinal());
			DistributedAcceptanceContext.ser(connection.oos, testContext);
			logger.info("Sent GATF tests to node " + connection.node);
			
			Command command = Command.values()[connection.ois.readInt()];;
			if(command==Command.TESTS_SHARE_RES) {
				task = new FutureTask<DistributedTestStatus>(new Callable<DistributedTestStatus>() {
					public DistributedTestStatus call() throws Exception {
						DistributedTestStatus res = null;
						Socket fClient = connection.sock;
						try {
							logger.info("Waiting for GATF tests Results from node " + connection.node);
							
							Command command = Command.values()[connection.ois.readInt()];;
							while(command==Command.LOAD_TESTS_RES)
							{
								Object lentry = DistributedAcceptanceContext.unser(connection.ois);
								RuntimeReportUtil.addEntry(lentry);
								command = Command.values()[connection.ois.readInt()];;
							}
							
							res = DistributedAcceptanceContext.unser(connection.ois);
							logger.info("Received GATF tests Results from node " + connection.node);
							
							String fileName = "dist-" + res.getZipFileName();
							File basePath = null;
				        	if(context.getGatfExecutorConfig().getOutFilesBasePath()!=null)
				        		basePath = new File(context.getGatfExecutorConfig().getOutFilesBasePath());
				        	else
				        	{
				        		basePath = new File(System.getProperty("user.dir"));
				        	}
				        	File resource = new File(basePath, context.getGatfExecutorConfig().getOutFilesDir());
				        	
				        	File zipFile = new File(resource, fileName);
				        	FileOutputStream fos = new FileOutputStream(zipFile);
							IOUtils.copy(connection.ois, fos);
							fos.flush();
							fos.close();
				        	
				        	ReportHandler.unzipZipFile(zipFile, resource.getAbsolutePath());
							
							logger.info("Done Receiving GATF tests Result files from node " + connection.node);
						} catch (Exception e) {
							e.printStackTrace();
							logger.info("Failure occurred while waiting for GATF tests Results from node " + connection.node);
						} finally {
							if(fClient!=null)
							{
								try {
									fClient.close();
								} catch (IOException e) {
									e.printStackTrace();
								}
							}
						}
						return res;
					}
				});
				new Thread(task).start();
				logger.info("Sending GATF tests Successful to node " + connection.node);
			} else {
				logger.info("Sending GATF tests Failed to node " + connection.node);
				if(connection!=null && connection.sock!=null)
				{
					try {
						connection.sock.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("Sending GATF tests Failed to node " + connection.node);
			if(connection!=null && connection.sock!=null)
			{
				try {
					connection.sock.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		return task;
	}
	
	public FutureTask<Object> distributeSeleniumTests(final DistributedConnection connection, int index, int numberOfRuns, File testClassesZip, 
			final List<String> testClassNames, final AcceptanceTestContext context)
	{
		if(connection==null)return null;
		
		FutureTask<Object> task = null;
		try {
            DistributedTestContext testContext = new DistributedTestContext();
            testContext.setIndex(index);
            testContext.setNumberOfRuns(numberOfRuns);
            
			logger.info("Sending GATF Selenium tests to node " + connection.node);
			connection.oos.writeInt(Command.SELENIUM_REQ.ordinal());
			FileInputStream fis = new FileInputStream(testClassesZip);
			connection.oos.writeLong(fis.getChannel().size());
			IOUtils.copy(fis, connection.oos);
			fis.close();
			DistributedAcceptanceContext.ser(connection.oos, testClassNames);
            DistributedAcceptanceContext.ser(connection.oos, testContext);
			
			Command command = Command.values()[connection.ois.readInt()];;
			if(command==Command.SELENIUM_RES) {
				int code = connection.ois.readInt();
				if(code==0) {
					task = new FutureTask<Object>(new Callable<Object>() {
						public Object call() throws Exception {
						    DistributedTestStatus res = null;
							Socket fClient = connection.sock;
							try {
								logger.info("Waiting for GATF Selenium tests Results from node " + connection.node);
								
								Command command = Command.values()[connection.ois.readInt()];;
	                            while(command==Command.LOAD_TESTS_RES)
	                            {
	                                Object lentry = DistributedAcceptanceContext.unser(connection.ois);
	                                RuntimeReportUtil.addEntry(lentry);
	                                command = Command.values()[connection.ois.readInt()];
	                            }
	                            
	                            res = DistributedAcceptanceContext.unser(connection.ois);
	                            logger.info("Received GATF Selenium tests Results from node " + connection.node);
	                            
	                            String fileName = "dist-" + res.getZipFileName();
	                            File basePath = null;
	                            if(context.getGatfExecutorConfig().getOutFilesBasePath()!=null)
	                                basePath = new File(context.getGatfExecutorConfig().getOutFilesBasePath());
	                            else
	                            {
	                                basePath = new File(System.getProperty("user.dir"));
	                            }
	                            File resource = new File(basePath, context.getGatfExecutorConfig().getOutFilesDir());
	                            
	                            File zipFile = new File(resource, fileName);
	                            FileOutputStream fos = new FileOutputStream(zipFile);
	                            IOUtils.copy(connection.ois, fos);
	                            fos.flush();
	                            fos.close();
	                            
	                            ReportHandler.unzipZipFile(zipFile, resource.getAbsolutePath());
								
								logger.info("Done Receiving GATF Selenium tests Results from node " + connection.node);
							} catch (Exception e) {
								e.printStackTrace();
								logger.info("Failure occurred while waiting for GATF Selenium tests Results from node " + connection.node);
							} finally {
								if(fClient!=null)
								{
									try {
										fClient.close();
									} catch (IOException e) {
										e.printStackTrace();
									}
								}
							}
							return res;
						}
					});
					new Thread(task).start();
					logger.info("Sending GATF Selenium tests Successful to node " + connection.node);
				} else {
				    if(code == 1) {
				        task = new FutureTask<Object>(new Callable<Object>() {
	                        public Object call() throws Exception {
	                            return new RuntimeException("No Driver found for specified browser");
	                        }
				        });
				    } else {
				        task = new FutureTask<Object>(new Callable<Object>() {
                            public Object call() throws Exception {
                                return new RuntimeException("Invalid configuration specified for selenium");
                            }
                        }); 
				    }
					logger.info("Sending GATF Selenium tests Failed to node " + connection.node);
					if(connection!=null && connection.sock!=null)
					{
						try {
							connection.sock.close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				}
			} else {
				logger.info("Sending GATF Selenium tests Failed to node " + connection.node);
				if(connection!=null && connection.sock!=null)
				{
					try {
						connection.sock.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				task = new FutureTask<Object>(new Callable<Object>() {
                    public Object call() throws Exception {
                        return new RuntimeException("Invalid configuration specified for selenium");
                    }
                });
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("Sending GATF Selenium tests Failed to node " + connection.node);
			if(connection!=null && connection.sock!=null)
			{
				try {
					connection.sock.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
            task = new FutureTask<Object>(new Callable<Object>() {
                public Object call() throws Exception {
                    return new RuntimeException("Invalid configuration specified for selenium");
                    }
            });
		}
		return task;
	}
}
 public static class DistributedConnection {
	private Socket sock;
	private String node;
	private Output oos = null;
	private Input ois = null;
		
	public String toString()
	{
		return node;
	}
}
 public class DistributedTestContext implements Serializable {

	private static final long serialVersionUID = 1L;

	private List<TestCase> simTestCases;
	
	private Set<String> relativeFileNames;
	
	private int index;
	
	private int numberOfRuns;

	public List<TestCase> getSimTestCases() {
		return simTestCases;
	}

	public void setSimTestCases(List<TestCase> simTestCases) {
		this.simTestCases = simTestCases;
	}

	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public int getNumberOfRuns() {
		return numberOfRuns;
	}

	public void setNumberOfRuns(int numberOfRuns) {
		this.numberOfRuns = numberOfRuns;
	}

	public Set<String> getRelativeFileNames() {
		return relativeFileNames;
	}

	public void setRelativeFileNames(Set<String> relativeFileNames) {
		this.relativeFileNames = relativeFileNames;
	}
}
 public class DistributedTestStatus implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String node;
	
	private String identifier;
	
	private String zipFileName;
	
	private Map<String, List<Long>> testPercentileTimes;
	
	private Map<String, List<Long>> runPercentileTimes;
	
	private TestSuiteStats suiteStats;
	
	Map<String, Map<Integer, String>> indexes;
	
	public String getNode() {
		return node;
	}

	public void setNode(String node) {
		this.node = node;
	}

	public String getIdentifier() {
		return identifier;
	}

	public void setIdentifier(String identifier) {
		this.identifier = identifier;
	}

	public TestSuiteStats getSuiteStats() {
		return suiteStats;
	}

	public void setSuiteStats(TestSuiteStats suiteStats) {
		this.suiteStats = suiteStats;
	}

	public String getZipFileName() {
		return zipFileName;
	}

	public void setZipFileName(String zipFileName) {
		this.zipFileName = zipFileName;
	}

	public Map<String, List<Long>> getTestPercentileTimes() {
		return testPercentileTimes;
	}

	public void setTestPercentileTimes(Map<String, List<Long>> percentileTimes) {
		this.testPercentileTimes = percentileTimes;
	}

	public Map<String, List<Long>> getRunPercentileTimes() {
		return runPercentileTimes;
	}

	public void setRunPercentileTimes(Map<String, List<Long>> runPercentileTimes) {
		this.runPercentileTimes = runPercentileTimes;
	}

    public Map<String, Map<Integer, String>> getIndexes() {
        return indexes;
    }

    public void setIndexes(Map<String, Map<Integer, String>> indexes) {
        this.indexes = indexes;
    }
}
 public class TestCaseExecutorUtil {

	private Logger logger = Logger.getLogger(TestCaseExecutorUtil.class.getSimpleName());
	
	private static final JSONResponseValidator jsonResponseValidator = new JSONResponseValidator();
	
	private static final XMLResponseValidator xmlResponseValidator = new XMLResponseValidator();
	
	private static final SOAPResponseValidator soapResponseValidator = new SOAPResponseValidator();
	
	private static final TextResponseValidator textResponseValidator = new TextResponseValidator();
	
	private static final NoContentResponseValidator noContentResponseValidator = new NoContentResponseValidator(); 
	
	private OkHttpClient client;
	
	private AcceptanceTestContext context = null;
	
	private boolean singleExecutionContext  = false;
	
    private void trustAllCertificates(OkHttpClient.Builder builder) {
    	final TrustManager[] trustAllCerts = new TrustManager[] {
    			new X509TrustManager() {
		            public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
		            public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
		            public X509Certificate[] getAcceptedIssuers() {
		            	return new java.security.cert.X509Certificate[]{};
		            }
		        }
    	};
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, trustAllCerts, new java.security.SecureRandom());
            
            final SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();
            builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
            builder.hostnameVerifier(new HostnameVerifier() {
            	@Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        } catch (Exception e) {
        }
    }
	
	public TestCaseExecutorUtil(AcceptanceTestContext context)
	{
		int numConcurrentConns = context.getGatfExecutorConfig().getNumConcurrentExecutions();
		if(context.getGatfExecutorConfig().getConcurrentUserSimulationNum()!=null) {
			numConcurrentConns = context.getGatfExecutorConfig().getConcurrentUserSimulationNum();
		}
		
		int maxConns = numConcurrentConns>100?numConcurrentConns/10:10;
		
		ConnectionPool pool = new ConnectionPool(maxConns, 5, TimeUnit.MINUTES);
		OkHttpClient.Builder builder = new OkHttpClient.Builder();
		builder.connectionPool(pool)
				.connectTimeout(context.getGatfExecutorConfig().getHttpConnectionTimeout(), TimeUnit.MILLISECONDS)
				.readTimeout(context.getGatfExecutorConfig().getHttpConnectionTimeout(), TimeUnit.MILLISECONDS)
				.followRedirects(true).followSslRedirects(true)
				.callTimeout(context.getGatfExecutorConfig().getHttpRequestTimeout(), TimeUnit.MILLISECONDS);
		
		trustAllCertificates(builder);
		
		client = builder.build();
		this.context = context;
	}
	
	private TestCaseExecutorUtil(){}
	
	public static TestCaseExecutorUtil getSingleConnection(AcceptanceTestContext context)
	{
		TestCaseExecutorUtil util = new TestCaseExecutorUtil();
		util.client = new OkHttpClient.Builder().build();
		util.context = context;
		return util;
	}
	
	public AcceptanceTestContext getContext() {
		return context;
	}

	/**
	 * @param testCase
	 * @param builder
	 * @throws Exception
	 * Handle the request content, handle flows for rest and soap separately...
	 */
	private void handleRequestContent(TestCase testCase, Request.Builder builder) throws Exception
	{
		RequestBody body = null;
		//Set the request body first
		if(testCase.getAcontent()!=null && !testCase.getAcontent().trim().isEmpty() 
				&& !testCase.getMethod().equals(HttpMethod.GET))
		{
			if(!testCase.isSoapBase())
			{
				body = RequestBody.create(testCase.getAcontent(), null);
			}
		}
		else if(testCase.getMultipartContent()!=null && !testCase.getMultipartContent().isEmpty())
		{
			MultipartBody.Builder mb = new MultipartBody.Builder().setType(MultipartBody.FORM);
			for (String filedet : testCase.getMultipartContent()) {
				String[] mulff = filedet.split(":");
				if(mulff.length==4 || mulff.length==3) {
					String controlname = mulff[0].trim();
					String type = mulff[1].trim();
					String fileNmOrTxt = mulff[2].trim();
					
					String contType = "";
					if(mulff.length==4)
					{
						contType= mulff[3].trim();
					}
					
					if(!type.equalsIgnoreCase("text") && !type.equalsIgnoreCase("file")) {
						logger.severe("Invalid type specified for file upload...skipping value - " + filedet);
						continue;
					}
					if(fileNmOrTxt.isEmpty()) {
						logger.severe("No file/text specified for file upload...skipping value - " + filedet);
						continue;
					}
					if(contType.isEmpty() && type.equalsIgnoreCase("text")) {
						logger.severe("No mime-type specified...skipping value - " + filedet);
						continue;
					}
					
					if(type.equalsIgnoreCase("file")) {
						try {
							File file = getResourceFile(context.getGatfExecutorConfig().getTestCasesBasePath(), fileNmOrTxt);
							if(!testCase.isSoapBase())
							{
								mb.addPart(Part.createFormData(controlname, fileNmOrTxt, RequestBody.create(file, null)));
							}
							else
							{
							    FileInputStream fis = new FileInputStream(file);
								byte[] fileData = IOUtils.toByteArray(fis);
								fis.close();
								String fileContents = Base64.getEncoder().encodeToString(fileData);
								if(testCase.getSoapParameterValues()==null)
								{
									testCase.setSoapParameterValues(new HashMap<String, String>());
								}
								testCase.getSoapParameterValues().put(controlname, fileContents);
							}
						} catch (Exception e) {
							logger.severe("No file found for file upload...skipping value - " + filedet);
							continue;
						}
					} else {
						if(!testCase.isSoapBase())
						{
							mb.addPart(Part.createFormData(controlname, fileNmOrTxt));
						}
						else
						{
							if(testCase.getSoapParameterValues()==null)
							{
								testCase.setSoapParameterValues(new HashMap<String, String>());
							}
							testCase.getSoapParameterValues().put(controlname, fileNmOrTxt);
						}
					}
				}			
			}
			body = mb.build();
		}
		else if(StringUtils.isNotBlank(testCase.getContentFile()))
		{
			String content = null;
			File file = getResourceFile(context.getGatfExecutorConfig().getTestCasesBasePath(), testCase.getContentFile());
			if(testCase.isSoapBase())
			{
			    FileInputStream fis = new FileInputStream(file);
                byte[] fileData = IOUtils.toByteArray(fis);
                fis.close();
				content = Base64.getEncoder().encodeToString(fileData);
			}
			else
			{
				content = FileUtils.readFileToString(file, "UTF-8");
			}
			body = RequestBody.create(content, null);
		}
		
		//Now set the URL with authentication tokens etc...
		if(!testCase.isSoapBase()) {
			String turl = testCase.getAurl();
			
			String url = testCase.getAurl();
			if(url.indexOf("?")!=-1) {
				url = url.substring(0, url.indexOf("?"));
			}
			
			if(testCase.getSimulationNumber()>=0 && context.getProviderTestDataMap(context.getGatfExecutorConfig().getAuthDataProvider())!=null
					&& context.getGatfExecutorConfig().isAuthEnabled()
					&& context.getGatfExecutorConfig().getAuthUrl().equals(testCase.getUrl())) 
			{
				String[] authParams = context.getGatfExecutorConfig().getAuthParamDetails();
				List<Map<String, String>> testDataLst = context.getProviderTestDataMap(context.getGatfExecutorConfig().getAuthDataProvider());
				String userVal = null, passwordVal = null;
				if(testDataLst.size()>=testCase.getSimulationNumber()) {
					int index = testCase.getSimulationNumber();
					if(index>0)
						index -= 1;
					userVal = testDataLst.get(index).get(authParams[0]);
					passwordVal = testDataLst.get(index).get(authParams[2]);
				} else {
					Random rd = new Random();
					int pos = rd.nextInt(testDataLst.size());
					userVal = testDataLst.get(pos).get(authParams[0]);
					passwordVal = testDataLst.get(pos).get(authParams[2]);
				}
				
				String content = "";
				
				if(authParams[1].equals("header")) {
					if(testCase.getHeaders()==null)
					{
						testCase.setHeaders(new HashMap<String, String>());
					}
					testCase.getHeaders().put(authParams[0], userVal);
				} else if(authParams[1].equals("queryparam")) {
					if(turl.indexOf("?")!=-1 && turl.indexOf("{"+authParams[0]+"}")!=-1) {
						turl = turl.replaceAll("\\{"+authParams[0]+"\\}", userVal);
					}
					else
					{
						if(turl.indexOf("?")!=-1) {
							turl += "&" + authParams[0] + "=" + userVal;
						} else {
							turl += "?" + authParams[0] + "=" + userVal;
						}
					}
				} else {
					content = authParams[0] + "=" + userVal + "&";
				}
				
				if(authParams[3].equals("header")) {
					testCase.getHeaders().put(authParams[2], passwordVal);
				} else if(authParams[3].equals("queryparam")) {
					if(turl.indexOf("?")!=-1 && turl.indexOf("{"+authParams[2]+"}")!=-1) {
						turl = turl.replaceAll("\\{"+authParams[2]+"\\}", passwordVal);
					}
					else
					{
						if(turl.indexOf("?")!=-1) {
							turl += "&" + authParams[2] + "=" + passwordVal;
						} else {
							turl += "?" + authParams[2] + "=" + passwordVal;
						}
					}
				} else {
					content += authParams[0] + "=" + passwordVal + "&";
				}
				
				testCase.setContent(content);
				if(StringUtils.isNotBlank(content)) {
					body = RequestBody.create(content, null);
				}
			}
			
			String authUrl = testCase.isServerApiTarget()?"":context.getGatfExecutorConfig().getAuthUrl();
			String[] authExtractAuthParams = testCase.isServerApiTarget()?context.getGatfExecutorConfig().getServerApiAuthExtractAuthParams()
					:context.getGatfExecutorConfig().getAuthExtractAuthParams();
			
			if(testCase.isSecure() && !authUrl.equals(url)) {
				String sessIdentifier = context.getSessionIdentifier(testCase);
				String tokenNm = authExtractAuthParams[2];
				if(context.getWorkflowContextHandler().getSuiteWorkflowContext(testCase)!=null &&
						context.getWorkflowContextHandler().getSuiteWorkflowContext(testCase).get(tokenNm)!=null)
				{
					sessIdentifier = context.getWorkflowContextHandler().getSuiteWorkflowContext(testCase).get(tokenNm);
				}
				
				Assert.assertNotNull("Authentication Token is null", sessIdentifier);
				
				if(authExtractAuthParams[3].equalsIgnoreCase("queryparam"))
				{
					if(turl.indexOf("?")!=-1 && turl.indexOf("{"+authExtractAuthParams[2]+"}")!=-1) {
						turl = turl.replaceAll("\\{"+authExtractAuthParams[2]+"\\}", sessIdentifier);
					}
					else
					{
						if(turl.indexOf("?")!=-1) {
							String urlpart = turl.substring(0, turl.indexOf("?")+1);
							String paramstr = turl.substring(turl.indexOf("?")+1);
							String[] params = paramstr.split("&");
							boolean isAppendAuthToken = false;
							for (String param : params) {
								String[] pair = param.split("=");
								String key = pair[0];
								String value = pair.length>1?pair[1]:"";
								if(key.trim().equals(authExtractAuthParams[2])) {
									urlpart += (key + "=" + sessIdentifier);
									isAppendAuthToken = true;
								} else {
									urlpart += (key + "=" + value);
								}
								urlpart += "&";
							}
							if(!isAppendAuthToken) {
								urlpart += (authExtractAuthParams[2] + "=" + sessIdentifier);
							}
							turl = urlpart;
						} else {
							turl += "?" + authExtractAuthParams[2] + "=" + sessIdentifier;
						}
					}
				}
				else if(authExtractAuthParams[3].equalsIgnoreCase("header"))
				{
					testCase.getHeaders().put(authExtractAuthParams[2], sessIdentifier);
				}
				else if(authExtractAuthParams[3].equalsIgnoreCase("cookie"))
				{
					testCase.getHeaders().put("Cookie", authExtractAuthParams[2] + "=" + sessIdentifier);
				}
			}

			String completeUrl = getUrl(testCase.getBaseUrl(), turl);
			
			testCase.setAurl(completeUrl);
			try {
			    builder = builder.url(testCase.getAurl());
            } catch (IllegalArgumentException e) {
                Assert.assertTrue("Invalid URL, please check the url provided ("+ completeUrl + ")", false);
            }
			
		} else {
			
			Assert.assertNotNull("No wsdlKey specified for SOAP test", testCase.getWsdlKey());
			Assert.assertNotNull("No operation specified for SOAP test", testCase.getOperationName());
			
			String request = testCase.getAcontent();
			
			String soapAction = context.getSoapActions().get(testCase.getWsdlKey()+testCase.getOperationName());
			if (request.contains(AcceptanceTestContext.SOAP_1_1_NAMESPACE)) {
                soapAction = soapAction != null ? "\"" + soapAction + "\"" : "";
                builder.addHeader(AcceptanceTestContext.PROP_SOAP_ACTION_11, soapAction);
                builder.addHeader(AcceptanceTestContext.PROP_CONTENT_TYPE, AcceptanceTestContext.MIMETYPE_TEXT_XML);
                //TODO recheck this later builder.addParameter(AcceptanceTestContext.PROP_CONTENT_TYPE, AcceptanceTestContext.MIMETYPE_TEXT_XML);
                testCase.getHeaders().put(AcceptanceTestContext.PROP_CONTENT_TYPE, AcceptanceTestContext.MIMETYPE_TEXT_XML);
            } else if (request.contains(AcceptanceTestContext.SOAP_1_2_NAMESPACE)) {
                String contentType = AcceptanceTestContext.MIMETYPE_APPLICATION_XML;
                if (soapAction != null) {
                    contentType = contentType + AcceptanceTestContext.PROP_DELIMITER + AcceptanceTestContext.PROP_SOAP_ACTION_12 + "\"" + soapAction + "\"";
                }
                builder.addHeader(AcceptanceTestContext.PROP_CONTENT_TYPE, contentType);
                testCase.getHeaders().put(AcceptanceTestContext.PROP_CONTENT_TYPE, contentType);
            }
			
			String endpoint = context.getSoapEndpoints().get(testCase.getWsdlKey());
			Assert.assertNotNull("No endpoints found for " + testCase.getWsdlKey()
					 + ", please define the wsdlLocFile in your config", endpoint);
			if(testCase.isSecure() && context.getGatfExecutorConfig().isSoapAuthEnabled() 
					&& !context.getGatfExecutorConfig().isSoapAuthTestCase(testCase)) {
				String sessIdentifier = context.getSessionIdentifier(testCase);
				String tokenNm = context.getGatfExecutorConfig().getAuthExtractAuthParams()[2];
				if(context.getWorkflowContextHandler().getSuiteWorkflowContext(testCase)!=null &&
						context.getWorkflowContextHandler().getSuiteWorkflowContext(testCase).get(tokenNm)!=null)
				{
					sessIdentifier = context.getWorkflowContextHandler().getSuiteWorkflowContext(testCase).get(tokenNm);
				}
				
				Assert.assertNotNull("Authentication Token is null", sessIdentifier);
				if(endpoint.indexOf("?")!=-1) {
					endpoint += "&" + context.getGatfExecutorConfig().getSoapAuthExtractAuthParams()[1] + "=" + sessIdentifier;
				} else {
					endpoint += "?" + context.getGatfExecutorConfig().getSoapAuthExtractAuthParams()[1] + "=" + sessIdentifier;
				}
			}
			testCase.setAurl(endpoint);
			
			if(request==null || request.trim().isEmpty())
			{
				Document soapMessage = context.getSoapMessages().get(testCase.getWsdlKey()+testCase.getOperationName());
				SOAPResponseValidator.processSOAPRequest(soapMessage, testCase);
				
				Transformer transformer = TransformerFactory.newInstance().newTransformer();
				transformer.setOutputProperty(OutputKeys.INDENT, "yes");        
				StringWriter sw = new StringWriter();
				StreamResult result = new StreamResult(sw);
				DOMSource source = new DOMSource(soapMessage);
				transformer.transform(source, result);
				request = sw.toString();
				body = RequestBody.create(request, null);
			}
			
			try {
			    builder = builder.url(testCase.getAurl());
            } catch (IllegalArgumentException e) {
                Assert.assertTrue("Invalid URL, please check the url provided ("+ testCase.getAurl() + ")", false);
            }
		}
		
		if(testCase.getMethod().equalsIgnoreCase("post") || testCase.getMethod().equalsIgnoreCase("put")) {
			if(body==null) {
				body = RequestBody.create(StringUtils.EMPTY, null);
			}
		}
		builder.method(testCase.getMethod().toUpperCase(), body);
	}
	
	public CompletableFuture<TestCaseReport> executeTestCase(TestCase testCase, TestCaseReport testCaseReport)
	{
		long start = System.currentTimeMillis();
		
		Request.Builder builder = new Request.Builder();
		Call call = null;
		
		try {
			if(!testCase.isExternalApi() && !testCase.isDisablePreHooks())
			{
				List<Method> preHook = context.getPrePostHook(testCase, true);
				if(preHook!=null) {
					for (Method method : preHook) {
						method.invoke(null, new Object[]{testCase});
					}
				}
				
				if(testCase.getPreExecutionDataSourceHookName()!=null) {
					try {
						context.executeDataSourceHook(testCase.getPreExecutionDataSourceHookName());
					} catch (Throwable e) {
						e.printStackTrace();
					}
				}
			}
			
			handleRequestContent(testCase, builder);
			
			if(testCase.getHeaders()!=null)
			{
				for (Map.Entry<String, String> entry : testCase.getHeaders().entrySet()) {
					builder = builder.addHeader(entry.getKey(), entry.getValue());
				}
			}
			
			testCaseReport.setTestCase(testCase);
			Request request = builder.build();
			
			if(!testCase.isExternalApi() && !testCase.isDisablePreHooks() && testCase.getPreWaitMs()!=null && testCase.getPreWaitMs()>0) {
				Thread.sleep(testCase.getPreWaitMs());
			}
			
			if(testCase.getPerfConfig()!=null && testCase.getPerfConfig().getType()!=null && !testCase.getPerfConfig().getType().equals("none")) {
				CompletableFuture<TestCaseReport> future = new CompletableFuture<TestCaseReport>();
				if(testCase.getPerfConfig().getType().startsWith("wrk")) {
					WrkTestCaseExecutor.execute(context, testCase, testCaseReport, isSingleExecutionContext());
				} else if(testCase.getPerfConfig().getType().equals("vegeta")) {
					VegetaTestCaseExecutor.execute(context, testCase, testCaseReport, isSingleExecutionContext());
				} else {
					AutocannonTestCaseExecutor.execute(context, testCase, testCaseReport, isSingleExecutionContext());
				}
				future.complete(testCaseReport);
				return future;
			} else {
				call = client.newCall(request);
				TestCaseResponseHandler rp = new TestCaseResponseHandler(testCase, testCaseReport, context, start);
				call.enqueue(rp);
				return rp.future;
			}
		} catch (Throwable e) {
			testCaseReport.setExecutionTime(System.currentTimeMillis() - start);
			testCaseReport.setStatus(TestStatus.Failed.status);
			testCaseReport.setFailureReason(TestFailureReason.Exception.status);
			testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
			testCaseReport.setError(e.getMessage());
			if(e.getMessage()==null && testCaseReport.getErrorText()!=null && testCaseReport.getErrorText().indexOf("\n")!=-1) {
				testCaseReport.setError(testCaseReport.getErrorText().substring(0, testCaseReport.getErrorText().indexOf("\n")));
			}
			e.printStackTrace();
			TestCaseResponseHandler rp = new TestCaseResponseHandler(testCase, testCaseReport, context, start);
			rp.onError(call, e);
			return rp.future;
		}
	}
	
	private File getResourceFile(String baseFolder, String filename) {
		try {
			File basePath = new File(baseFolder);
			File resource = new File(basePath, filename);
			return resource;
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * @author Sumeet Chhetri
	 * The Async HTTP Response Handler logic returns a TestCaseReport
	 */
	public static class TestCaseResponseHandler implements Callback {
		private final CompletableFuture<TestCaseReport> future = new CompletableFuture<>();
		
		private TestCase testCase;
		
		private TestCaseReport testCaseReport;
		
		private final AcceptanceTestContext context;
		
		private long start;
		
		public TestCaseResponseHandler(TestCase testCase, TestCaseReport testCaseReport, 
				AcceptanceTestContext context, long start)
		{
			this.testCase = testCase;
			this.testCaseReport = testCaseReport;
			Assert.assertNotNull("Testcase cannot be null", testCase);
			Assert.assertNotNull("TestCaseReport cannot be null", testCaseReport);
			this.context = context;
			Assert.assertNotNull("Context cannot be null", context);
			this.start = start;
			this.testCaseReport.setResponseContentType(testCase.getExpectedResContentType());
		}
		
		public void onError(Call call, Throwable e) {
			testCaseReport.setStatus(TestStatus.Failed.status);
			testCaseReport.setFailureReason(TestFailureReason.Exception.status);
			testCaseReport.setError(e.getMessage());
			testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
			testCase.setFailed(true);
			e.printStackTrace();
			future.completeExceptionally(e);
		}
		
		@Override
		public void onFailure(Call call, IOException e) {
			testCaseReport.setStatus(TestStatus.Failed.status);
			testCaseReport.setFailureReason(TestFailureReason.Exception.status);
			testCaseReport.setError(e.getMessage());
			testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
			testCase.setFailed(true);
			e.printStackTrace();
			future.completeExceptionally(e);
		}

		@Override
		public void onResponse(Call call, Response response) throws IOException {
			testCaseReport.setExecutionTime(System.currentTimeMillis() - start);
			
			if(testCase.getExpectedResContentType()!=null)
			{
				if(testCase.getExpectedResContentType()!=null && !testCase.getExpectedResContentType().trim().isEmpty()
						&& response.headers().values(HttpHeaders.CONTENT_TYPE)!=null 
						&& response.headers().values(HttpHeaders.CONTENT_TYPE).get(0)
							.indexOf(testCase.getExpectedResContentType())!=0)
				{
					testCaseReport.setError("Expected content type ["+testCase.getExpectedResContentType()
							+"] does not match actual content type ["+response.headers().values(HttpHeaders.CONTENT_TYPE).get(0)+"]");
					testCaseReport.setStatus(TestStatus.Failed.status);
					testCaseReport.setFailureReason(TestFailureReason.InvalidContentType.status);
					if(testCase.isAbortOnInvalidContentType())
					{
						response.body().close();
						future.complete(testCaseReport);
						return;
					}
				}
			}
			else if(response.headers().values(HttpHeaders.CONTENT_TYPE)!=null)
			{
				testCase.setExpectedResContentType(response.headers().values(HttpHeaders.CONTENT_TYPE).get(0));
			}
			
			String contType = testCase.getExpectedResContentType();
			testCaseReport.setResponseContentType(contType);
			
			testCaseReport.setExecutionTime(System.currentTimeMillis() - start);
			if(isMatchesContentType(MediaType.APPLICATION_JSON_TYPE, contType) || isMatchesContentType(MediaType.APPLICATION_XML_TYPE, contType)
					|| isMatchesContentType(MediaType.TEXT_PLAIN_TYPE, contType) || isMatchesContentType(MediaType.TEXT_HTML_TYPE, contType)
					|| isMatchesContentType(MediaType.TEXT_XML_TYPE, contType))
			{
				testCaseReport.setResponseContent(response.body().string());
			}
			else if(contType.indexOf("json")!=-1 || contType.indexOf("xml")!=-1 || contType.indexOf("text")!=-1)
			{
				testCaseReport.setResponseContent(response.body().string());
			}
			
			testCaseReport.setResHeaders(response.headers().toMultimap());
			if(!testCase.isSoapBase()) {
				if(isMatchesContentType(MediaType.APPLICATION_JSON_TYPE, testCase.getExpectedResContentType()))
				{
					jsonResponseValidator.validate(response, testCase, testCaseReport, context);
				}
				else if(isMatchesContentType(MediaType.APPLICATION_XML_TYPE, testCase.getExpectedResContentType())
						|| isMatchesContentType(MediaType.TEXT_XML_TYPE, testCase.getExpectedResContentType()))
				{
					xmlResponseValidator.validate(response, testCase, testCaseReport, context);
				}
				else if(isMatchesContentType(MediaType.TEXT_PLAIN_TYPE, testCase.getExpectedResContentType())
						|| isMatchesContentType(MediaType.TEXT_HTML_TYPE, testCase.getExpectedResContentType()))
				{
					textResponseValidator.validate(response, testCase, testCaseReport, context);
				}
				else
				{
					noContentResponseValidator.validate(response, testCase, testCaseReport, context);
					testCaseReport.setStatus(TestStatus.Success.status);
				}
			} else {
				soapResponseValidator.validate(response, testCase, testCaseReport, context);
			}
			
			if(!testCase.isExternalApi() && !testCase.isDisablePostHooks() && testCase.getPostWaitMs()!=null 
					&& testCase.getPostWaitMs()>0) {
				try {
					Thread.sleep(testCase.getPostWaitMs());
				} catch (InterruptedException e1) {
				}
			}
			
			future.complete(testCaseReport);
		}
		
		public static boolean isMatchesContentType(MediaType md, String contType) {
			MediaType md2 = MediaType.valueOf(contType);
			return md.getType().equalsIgnoreCase(md2.getType()) && md.getSubtype().equalsIgnoreCase(md2.getSubtype());
		}
	}

	private String getUrl(String baseUrl, String urlPart)
	{
		String fullUrl = baseUrl;
		fullUrl = fullUrl.trim() + "/" + urlPart.trim();
		String parturl = fullUrl.substring(fullUrl.indexOf("://")+3);
		fullUrl = fullUrl.substring(0, fullUrl.indexOf("://")+3) + parturl.replace("//", "/");
		Assert.assertTrue("Testcase URL is not valid " + fullUrl,AcceptanceTestContext.isValidUrl(fullUrl));
		return fullUrl;
	}
	
	public void shutdown()
	{
		client.connectionPool().evictAll();
	}

	public boolean isSingleExecutionContext() {
		return singleExecutionContext;
	}

	public void setSingleExecutionContext(boolean singleExecutionContext) {
		this.singleExecutionContext = singleExecutionContext;
	}
} public static class CSVTestCaseFinder extends CSVFamilyTestCaseFinder {
		
	public CSVTestCaseFinder(){}
		
	public CSVTestCaseFinder(Map<Integer, Integer> mappings,
			boolean ignoreHeaderLine, int sheet, char separator) {
		this.mappings = mappings;
		this.ignoreHeaderLine = ignoreHeaderLine;
		this.sheet = sheet;
		this.separator = separator;
	}
		
	@Override
	protected TestCaseFileType getFileType() {
		return TestCaseFileType.CSV;
	}
}
 public static class XLSTestCaseFinder extends CSVFamilyTestCaseFinder {
		
	public XLSTestCaseFinder(){}
		
	public XLSTestCaseFinder(Map<Integer, Integer> mappings,
			boolean ignoreHeaderLine, int sheet, char separator) {
		this.mappings = mappings;
		this.ignoreHeaderLine = ignoreHeaderLine;
		this.sheet = sheet;
		this.separator = separator;
	}
		
	@Override
	protected TestCaseFileType getFileType() {
		return TestCaseFileType.XLS;
	}
}
 public static class XLSXTestCaseFinder extends CSVFamilyTestCaseFinder {
		
	public XLSXTestCaseFinder(){}
		
	public XLSXTestCaseFinder(Map<Integer, Integer> mappings,
			boolean ignoreHeaderLine, int sheet, char separator) {
		this.mappings = mappings;
		this.ignoreHeaderLine = ignoreHeaderLine;
		this.sheet = sheet;
		this.separator = separator;
	}
		
	@Override
	protected TestCaseFileType getFileType() {
		return TestCaseFileType.XLSX;
	}
}
 public class JSONTestCaseFinder extends TestCaseFinder {

	protected TestCaseFileType getFileType() {
		return TestCaseFileType.JSON;
	}

	public List<TestCase> resolveTestCases(File testCaseFile) throws Exception {
		com.fasterxml.jackson.databind.ObjectMapper jsonMapper = WorkflowContextHandler.OM;
		List<TestCase> jsonTestCases = (List<TestCase>)jsonMapper.readValue(testCaseFile, new com.fasterxml.jackson.core.type.TypeReference<List<TestCase>>(){});
		return jsonTestCases;
	}
}
 public abstract class TestCaseFinder {

	public enum TestCaseFileType
	{
		XML(".xml"),
		JSON(".json"),
		CSV(".csv"),
		XLS(".xls"),
		XLSX(".xlsx");
		
		public String ext;
		
		private TestCaseFileType(String ext)
		{
			this.ext = ext;
		}
	}
	
	protected abstract TestCaseFileType getFileType();
	public abstract List<TestCase> resolveTestCases(File testCaseFile) throws Exception;
	
	private List<String> targetFileNames;
	
	public static final FileFilter DIR_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return file.isDirectory();
		}
	};
	
	public static final FileFilter FILE_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return !file.isDirectory();
		}
	};
	
	public static final FileFilter NOZIP_FILE_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return !file.isDirectory() && !file.getName().endsWith(".zip");
		}
	};
	
	public static void getFiles(File dir, FilenameFilter filter, List<File> fileLst)
	{
		if (dir.isDirectory()) {
			File[] files = dir.listFiles(filter);
			for (File file : files) {
				fileLst.add(file);
				getFiles(file, filter, fileLst);
			}
			
			files = dir.listFiles(DIR_FILTER);
			for (File file : files) {
				getFiles(file, filter, fileLst);
			}
		}
	}
	
	public List<String> getTargetFileNames() {
		if(targetFileNames==null) {
			targetFileNames = new ArrayList<String>();
		}
		return targetFileNames;
	}
	public void setTargetFileNames(List<String> targetFileNames) {
		this.targetFileNames = targetFileNames;
	}
	public List<TestCase> findTestCases(File dir, AcceptanceTestContext context, boolean considerConfig,
			Set<String> relativeFileNames)
	{
		if(context==null)
			considerConfig = false;
		
		String[] ignoreFiles = considerConfig?context.getGatfExecutorConfig().getIgnoreFiles():null;
		String[] orderedFiles = considerConfig?context.getGatfExecutorConfig().getOrderedFiles():null;
		boolean isOrderByFileName = considerConfig?context.getGatfExecutorConfig().isOrderByFileName():false;
		
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File folder, String name) {
				if(targetFileNames!=null && targetFileNames.size()>0)
				{
					for (String tfileName : targetFileNames) {
						return name.equalsIgnoreCase(tfileName) && name.toLowerCase().endsWith(getFileType().ext);
					}
					return false;
				}
				else
				{
					return name.toLowerCase().endsWith(getFileType().ext);
				}
			}
		};
		
		List<TestCase> testcases = new ArrayList<TestCase>();
		if (dir.isDirectory()) {
			List<File> files = new ArrayList<File>();
			getFiles(dir, filter, files);
			
			if(considerConfig)
			{
				files = filterFiles(ignoreFiles, files, dir);
				files = orderFiles(orderedFiles, isOrderByFileName, files, dir);
			}
			
			for (File file : files) {
				String relativeFileName = getRelativePath(file, dir);
				try {
					List<TestCase> testcasesTemp = resolveTestCases(file);
					if(testcasesTemp != null)
					{
						for (TestCase testCase : testcasesTemp) {
							testCase.setSourcefileName(relativeFileName);
							if(testCase.getSimulationNumber()==null)
							{
								testCase.setSimulationNumber(0);
							}
							if(considerConfig)
							{
							    if(StringUtils.isBlank(testCase.getBaseUrl())) {
							        testCase.setBaseUrl(context.getGatfExecutorConfig().getBaseUrl());
							    }
							}
							testCase.setExternalApi(false);
							testCase.setServerApiAuth(false);
							testCase.setServerApiTarget(false);
						}
						
						testcases.addAll(testcasesTemp);
						if(relativeFileNames!=null)relativeFileNames.add(relativeFileName);
					}
				} catch (Exception e) {
					System.out.println("Ignoring file due to invalid testcase format ... " + relativeFileName);
					//e.printStackTrace();
				}
			}
		}
		return testcases;
	}
	
	// returns null if file isn't relative to folder
	public static String getRelativePath(File file, File folder) {
	    String filePath = file.getAbsolutePath();
	    String folderPath = folder.getAbsolutePath();
	    if (filePath.startsWith(folderPath)) {
	        return filePath.substring(folderPath.length() + 1);
	    } else {
	        return null;
	    }
	}
	
	public static List<File> filterFiles(String[] ignoreFiles, List<File> testFiles, File dir)
	{
		if(ignoreFiles!=null)
		{
			List<File> files = new ArrayList<File>();
			for (String fileN : ignoreFiles) {
				fileN = fileN.trim();
				if(fileN.isEmpty()) {
					continue;
				}
				if(fileN.equals("*") || fileN.equals("*.*")) {
					return files;
				}
			}
			
			for (File file : testFiles) {
				boolean isIgnore = false;
				
				String completeFileName = getRelativePath(file, dir);
				String fileName = file.getName();
				
				for (String fileN : ignoreFiles) {
					fileN = fileN.trim();
					
					boolean isCompleteNameMatch = fileN.indexOf("\\")!=-1;
					
					if(fileN.isEmpty()) {
						continue;
					}
					
					if(fileN.startsWith("*.")) {
						String ext = fileN.substring(2);
						if((isCompleteNameMatch && completeFileName.endsWith(ext))
								|| (!isCompleteNameMatch && fileName.endsWith(ext))) {
							isIgnore = true;
							break;
						}
					} else if(fileN.endsWith("*")) {
						fileN = fileN.substring(0, fileN.lastIndexOf("*"));
						if((isCompleteNameMatch && completeFileName.startsWith(fileN))
								|| (!isCompleteNameMatch && fileName.startsWith(fileN))) {
							isIgnore = true; 
							break;
						}
					} else if(fileN.startsWith("*")) {
						fileN = fileN.substring(1);
						if((isCompleteNameMatch && completeFileName.endsWith(fileN))
								|| (!isCompleteNameMatch && fileName.endsWith(fileN))) {
							isIgnore = true;
							break;
						}
					} else if((isCompleteNameMatch && completeFileName.equals(fileN))
							|| (!isCompleteNameMatch && fileName.equals(fileN))) {
						isIgnore = true;
						break;
					}
				}
				if(isIgnore)
					continue;
				
				files.add(file);
			}
			return files;
		}
		return testFiles;
	}
	
	public static List<File> orderFiles(String[] orderedFiles, boolean isOrderByFileName, List<File> files, File dir)
	{
		if(orderedFiles!=null)
		{
			List<File> allFiles = new ArrayList<File>();
			for (String fileN : orderedFiles) {
				for (File file : files) {
					String completeFileName = getRelativePath(file, dir);
					if(completeFileName.equals(fileN)) {
						allFiles.add(file);
						break;
					}
				}
			}
			for (File file : files) {
				if(!allFiles.contains(file)) {
					allFiles.add(file);
				}
			}
			return allFiles;
		}
		else
		{
			if(isOrderByFileName) {
				AlphanumComparator<File> comparator = new AlphanumComparator<File>();
				Collections.sort(files, comparator);
			}
			return files;
		}
	}
	
	public static List<File> filterValidTestCaseFiles(List<File> testFiles)
	{
		List<File> allFiles = new ArrayList<File>();
		for (File file : testFiles) {
		    if(file.getName().toLowerCase().endsWith(".sel")) {
		        allFiles.add(file);
		        continue;
		    }
			try {
				new XMLTestCaseFinder().resolveTestCases(file);
				allFiles.add(file);
			} catch (Exception e) {
			}
		}
		return allFiles;
	}
}
 public class XMLTestCaseFinder extends TestCaseFinder {

	protected TestCaseFileType getFileType() {
		return TestCaseFileType.XML;
	}

	@SuppressWarnings("unchecked")
	public List<TestCase> resolveTestCases(File testCaseFile) throws Exception {
		XStream xstream = new XStream(new DomDriver("UTF-8"));
       
        xstream.allowTypes(new Class[]{TestCase.class});
		xstream.processAnnotations(new Class[]{TestCase.class});
		xstream.alias("TestCases", List.class);
		List<TestCase> xmlTestCases = (List<TestCase>)xstream.fromXML(testCaseFile);
		return xmlTestCases;
	}
}
 @JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class LoadTestResource implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String title;
	
	private String url;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}
}
 public class ReportHandler {
	
	public ReportHandler(String node, String identifier) {
		/*if(node!=null && identifier!=null)
		{
			distributedTestStatus = new DistributedTestStatus();
			distributedTestStatus.setNode(node);
			distributedTestStatus.setIdentifier(identifier);
		}*/
	}
	
	private static Logger logger = Logger.getLogger(ReportHandler.class.getSimpleName());
	
	private final Map<String, ConcurrentLinkedQueue<TestCaseReport>> finalTestResults = 
			new ConcurrentHashMap<String, ConcurrentLinkedQueue<TestCaseReport>>();
	
	private final Map<String, Integer> finalTestReportsDups = new ConcurrentHashMap<String, Integer>();
	
	//private DistributedTestStatus distributedTestStatus = null;
	
	private List<TestCaseStats> testCaseStats = new ArrayList<TestCaseStats>();
	
	private TestSuiteStats testSuiteStats = new TestSuiteStats();
	
	/*public DistributedTestStatus getDistributedTestStatus() {
		return distributedTestStatus;
	}*/

	public void clearForLoadTests(AcceptanceTestContext context)
	{
		testCaseStats = new ArrayList<TestCaseStats>();
		testSuiteStats = new TestSuiteStats();
		clearTestResults();
	}
	
	
	
	public void addToLoadTestResources(String prefix, int runNo, String url, List<LoadTestResource> loadTestResources) {
		LoadTestResource resource = new LoadTestResource();
		if(prefix==null)
			prefix = "Run";
		resource.setTitle(prefix+"-"+runNo);
		resource.setUrl(url);
		synchronized (loadTestResources) {
			loadTestResources.add(resource);
		}
	}
	
	public static void doFinalLoadTestReport(String prefix, TestSuiteStats testSuiteStats, AcceptanceTestContext acontext,
			List<String> nodes, List<String> nodeurls, List<LoadTestResource> loadTestResources)
	{
		VelocityContext context = new VelocityContext();
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(testSuiteStats);
			context.put("suiteStats", reportingJson);
			
			if(nodes==null)
			{
				reportingJson = WorkflowContextHandler.OM.writeValueAsString(loadTestResources);
				context.put("loadTestResources", reportingJson);
			}
			else
			{
				context.put("loadTestResources", "{}");
				context.put("nodes", nodes);
				context.put("nodeurls", nodeurls);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
        	File resource = acontext.getOutDir();
            VelocityEngine engine = new VelocityEngine();
            engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            engine.init();
            
            StringWriter writer = new StringWriter();
            engine.mergeTemplate("/gatf-templates/index-load.vm", "UTF-8", context, writer);

            if(prefix==null)
            	prefix = "";
            
            String filenm = resource.getAbsolutePath() + File.separator + prefix.replaceAll("[^a-zA-Z0-9-_\\.]", "_") + "index.html";
            BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
            fwriter.write(writer.toString());
            fwriter.close();
            
            //if(distributedTestStatus!=null)
            {
            	//distributedTestStatus.getReportFileContent().put(prefix + "index.html", writer.toString());
            }
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void doFinalDistributedLoadTestReport(AcceptanceTestContext acontext, List<TestSuiteStats> suiteStats, 
			List<List<LoadTestResource>> loadTestResources, List<String> nodes)
	{
		VelocityContext context = new VelocityContext();
		context.put("suiteStats", suiteStats);
		context.put("loadTestResources", loadTestResources);
		context.put("nodes", nodes);
		
		try
		{
			File resource = acontext.getOutDir();
            VelocityEngine engine = new VelocityEngine();
            engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            engine.init();
            
            StringWriter writer = new StringWriter();
            engine.mergeTemplate("/gatf-templates/distributed-index-load.vm", "UTF-8", context, writer);
            
            String filenm = resource.getAbsolutePath() + File.separator + "index.html";
            BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
            fwriter.write(writer.toString());
            fwriter.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public TestSuiteStats doLoadTestReporting(AcceptanceTestContext acontext, long startTime, TestExecutionPercentile testPercentiles,
			TestExecutionPercentile runPercentiles)
	{
		TestSuiteStats testSuiteStats = new TestSuiteStats();
		
		int total = 0, failed = 0, skipped = 0, totruns = 0, failruns = 0;
		long grpexecutionTime = 0L;
		
		for (Map.Entry<String, ConcurrentLinkedQueue<TestCaseReport>> entry :  getFinalTestResults().entrySet()) {
			try {
				List<TestCaseReport> reports = Arrays.asList(entry.getValue().toArray(new TestCaseReport[entry.getValue().size()]));
				
				for (TestCaseReport testCaseReport : reports) {
					
					total ++;
					if(testCaseReport.getNumberOfRuns()>1)
					{
						totruns += testCaseReport.getNumberOfRuns();
					}
					
					if(testCaseReport.getErrors()!=null && testCaseReport.getNumberOfRuns()>1)
					{
						failruns += testCaseReport.getErrors().size();
						if(testCaseReport.getError()!=null) {
							failruns += 1;
						}
					}
					
					if(testCaseReport.getStatus().equals(TestStatus.Skipped.status)) {
						skipped ++;
					} else if(!testCaseReport.getStatus().equals(TestStatus.Success.status)) {
						failed ++;
					}
					
					grpexecutionTime += testCaseReport.getExecutionTime();
					
					synchronized (testPercentiles) {
						testPercentiles.addExecutionTime(testCaseReport.getTestCase().getName(), testCaseReport.getExecutionTime());
					}
					synchronized (runPercentiles) {
						runPercentiles.addExecutionTime(testCaseReport.getTestCase().getIdentifier(), testCaseReport.getExecutionTime());
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		long endTime = System.currentTimeMillis();
		testSuiteStats.setTotalTestCount(total);
		testSuiteStats.setFailedTestCount(failed);
		testSuiteStats.setSkippedTestCount(skipped);
		testSuiteStats.setTotalRuns(totruns);
		testSuiteStats.setFailedRuns(failruns);
		testSuiteStats.setExecutionTime(endTime - startTime);
		testSuiteStats.setTotalSuiteRuns(1);
		testSuiteStats.setActualExecutionTime(grpexecutionTime);
		
		clearTestResults();
		
		return testSuiteStats;
	}
	
	private void doCompare(TestCaseReport testCaseReport, 
			Map<String, TestCaseReport> firstCompareCopy, Map<String, List<TestCaseCompareStatus>> compareStatuses,
			String testCaseSuiteKey) 
	{
		List<TestCaseCompareStatus> compareStatusLst = null;
		TestCase tc = testCaseReport.getTestCase();
		if(tc.getSimulationNumber()==1 && !firstCompareCopy.containsKey("Run-1"+tc.getName()))
		{
			firstCompareCopy.put("Run-1"+tc.getName(), testCaseReport);
			compareStatusLst = new ArrayList<TestCaseCompareStatus>();
			compareStatuses.put(tc.getName(), compareStatusLst);
		}
		else if(tc.getSimulationNumber()>1)
		{
			compareStatusLst = compareStatuses.get(tc.getName());
			TestCaseReport testCaseReportRun1 = firstCompareCopy.get("Run-1"+tc.getName());
			TestCaseCompareStatus comStatus = new TestCaseCompareStatus();	
			comStatus.setIdentifer(testCaseReport.getTestIdentifier());
			comStatus.setTestCaseName(tc.getName());
			comStatus.setTestSuiteKey(testCaseSuiteKey);
			
			if(!testCaseReportRun1.getStatus().equals(testCaseReport.getStatus()))
			{
				comStatus.setCompareStatusError("FAILED_STATUS_CODE");
			}
			if(comStatus.getCompareStatusError()==null
					&& testCaseReportRun1.getError()!=null && !testCaseReportRun1.getError().trim().isEmpty()
					&& testCaseReport.getError()!=null && !testCaseReport.getError().trim().isEmpty()
					&& !testCaseReportRun1.getError().equals(testCaseReport.getError()))
			{
				comStatus.setCompareStatusError("FAILED_ERROR_DETAILS");
			}
			if(comStatus.getCompareStatusError()==null
					&& testCaseReportRun1.getErrorText()!=null && !testCaseReportRun1.getErrorText().trim().isEmpty()
					&& testCaseReport.getErrorText()!=null && !testCaseReport.getErrorText().trim().isEmpty()
					&& !testCaseReportRun1.getErrorText().equals(testCaseReport.getErrorText()))
			{
				boolean eq = compareText(testCaseReport.getResponseContentType(), testCaseReport.getErrorText(), 
							testCaseReportRun1.getErrorText());
				if(!eq)
				{
					comStatus.setCompareStatusError("FAILED_ERROR_CONTENT");
				}
			}
			if(comStatus.getCompareStatusError()==null
					&& testCaseReportRun1.getResponseContentType()!=null 
					&& !testCaseReportRun1.getResponseContentType().trim().isEmpty()
					&& testCaseReport.getResponseContentType()!=null 
					&& !testCaseReport.getResponseContentType().trim().isEmpty()
					&& !testCaseReportRun1.getResponseContentType().equals(testCaseReport.getResponseContentType()))
			{
				comStatus.setCompareStatusError("FAILED_RESPONSE_TYPE");
			}
			if(comStatus.getCompareStatusError()==null
					&& testCaseReportRun1.getResponseContent()!=null && !testCaseReportRun1.getResponseContent().trim().isEmpty()
					&& testCaseReport.getResponseContent()!=null && !testCaseReport.getResponseContent().trim().isEmpty()
					&& !testCaseReportRun1.getResponseContent().equals(testCaseReport.getResponseContent()))
			{
				boolean eq = compareText(testCaseReport.getResponseContentType(), testCaseReport.getResponseContent(), 
						testCaseReportRun1.getResponseContent());
				if(!eq)
				{
					comStatus.setCompareStatusError("FAILED_RESPONSE_CONTENT");
				}
			}
			
			if(comStatus.getCompareStatusError()==null)
			{
				comStatus.setCompareStatusError("SUCCESS");
			}
			
			compareStatusLst.add(comStatus);
		}
	}
	
	/**
     * @param zipFile
     * @param directoryToExtractTo Provides file unzip functionality
     */
    public static void unzipZipFile(File zipFile, String directoryToExtractTo)
    {
        try
        {
        	new ZipFile(zipFile).extractAll(directoryToExtractTo);
        }
        catch (IOException ioe)
        {
        	logger.severe(ExceptionUtils.getStackTrace(ioe));
            return;
        }
    }
    
    /**
     * @param zipFile
     * @param directoryToExtractTo Provides file unzip functionality
     */
    public static void zipDirectory(File directory, final String[] fileFilters, String zipFileName, boolean addFolders, boolean suffixOrPrefix)
    {
        try
        {
            if (!directory.exists() || !directory.isDirectory())
            {
                directory.mkdirs();
                logger.info("Invalid Directory provided for zipping...");
                return;
            }
            File zipFile = new File(directory, zipFileName);
            FileOutputStream fos = new FileOutputStream(zipFile);
        	ZipOutputStream zos = new ZipOutputStream (fos);
        	
        	Collection<File> files = null;
        	if(addFolders) {
        	    if(suffixOrPrefix) {
        	        files = FileUtils.listFilesAndDirs(directory, new SuffixFileFilter(Arrays.asList(fileFilters)), FileFilterUtils.trueFileFilter());
        	    } else {
        	        files = FileUtils.listFilesAndDirs(directory, new PrefixFileFilter(Arrays.asList(fileFilters)), FileFilterUtils.trueFileFilter());
        	    }
        	} else {
        	    if(suffixOrPrefix) {
                    files = FileUtils.listFiles(directory, new SuffixFileFilter(Arrays.asList(fileFilters)), null);
        	    } else {
        	        files = FileUtils.listFiles(directory, new PrefixFileFilter(Arrays.asList(fileFilters)), null);
                }
        	}
        	
        	for (File file : files) {
        		if(file.isDirectory()) {
        			String dp = file.getAbsolutePath();
        			dp = dp.replace(directory.getAbsolutePath(), "");
        			dp = dp.replace(File.separator, "/");
        			if(dp.isEmpty())continue;
        			if(dp.startsWith("/")) {
        				dp = dp.substring(1);
        			}
        			ZipEntry zipEntry = new ZipEntry(dp + "/");
        			zos.putNextEntry(zipEntry);
        			zos.closeEntry();
        		} else {
        			String dp = file.getAbsolutePath();
        			dp = dp.replace(directory.getAbsolutePath(), "");
        			dp = dp.replace(File.separator, "/");
        			if(dp.isEmpty())continue;
        			if(dp.startsWith("/")) {
        				dp = dp.substring(1);
        			}
        			FileInputStream fis = new FileInputStream(file);
            		ZipEntry zipEntry = new ZipEntry(dp);
            		zos.putNextEntry(zipEntry);

            		byte[] bytes = new byte[1024];
            		int length;
            		while ((length = fis.read(bytes)) >= 0) {
            			zos.write(bytes, 0, length);
            		}

            		zos.closeEntry();
            		fis.close();
        		}
			}
        	
            zos.close();
			fos.close();
        }
        catch (IOException ioe)
        {
        	logger.severe(ExceptionUtils.getStackTrace(ioe));
            return;
        }
    }
	
	private boolean compareText(String type, String lhs, String rhs)
	{
		if(TestCaseResponseHandler.isMatchesContentType(MediaType.APPLICATION_JSON_TYPE, type))
		{
			try {
				JSONAssert.assertEquals(lhs, rhs,false);
				return true;
			} catch (Exception e) {
			} catch (AssertionError e) {
			}
		}
		else if(TestCaseResponseHandler.isMatchesContentType(MediaType.APPLICATION_XML_TYPE, type) 
		        || TestCaseResponseHandler.isMatchesContentType(MediaType.TEXT_XML_TYPE, type))
		{
			try {
				XMLAssert.assertXMLEqual(lhs, rhs);
				return true;
			} catch (Exception e) {
			} catch (AssertionError e) {
			}
		}
		return false;
	}

	public void doConcurrentRunReporting(AcceptanceTestContext acontext, long startTime, String reportFileName, int runNumber,
			boolean unzipFile, TestExecutionPercentile testPercentiles, TestExecutionPercentile runPercentiles, 
			String distRunPrefix) {
		GatfExecutorConfig config = acontext.getGatfExecutorConfig();
		int total = 0, failed = 0, totruns = 0, failruns = 0, skipped = 0;
		
		AlphanumComparator<String> comparator = new AlphanumComparator<String>();
		
		Map<String, List<TestCaseReport>> allTestCases = new TreeMap<String, List<TestCaseReport>>(comparator);
		Map<String, ConcurrentLinkedQueue<TestCaseReport>> tempMap = new TreeMap<String, ConcurrentLinkedQueue<TestCaseReport>>(comparator);
		
		if(distRunPrefix==null)
			tempMap.put("Run-"+ runNumber, getFinalTestResults().get("Run-"+ runNumber));
		else
			tempMap.put("Run-"+ runNumber, getFinalTestResults().get(distRunPrefix + "-" + runNumber));
		
		Map<String, List<TestCaseCompareStatus>> compareStatuses = new TreeMap<String, List<TestCaseCompareStatus>>(comparator);
		
		Map<String, TestCaseReport> firstCompareCopy = null;
		if(acontext.getGatfExecutorConfig().getCompareBaseUrlsNum()!=null
				&& acontext.getGatfExecutorConfig().getCompareBaseUrlsNum()>1)
		{
			firstCompareCopy = new HashMap<String, TestCaseReport>();
		}
		
		VelocityContext context = new VelocityContext();
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(allTestCases);
			context.put("testcaseReports", reportingJson);
			
			context.put("userSimulation", false);
			if(config.getConcurrentUserSimulationNum()!=null && config.getConcurrentUserSimulationNum()>1) {
				context.put("userSimulation", true);
			}
			
			context.put("compareEnabled", false);
			if(firstCompareCopy!=null) {
				context.put("compareEnabled", true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		String orf = reportFileName;
		for (Map.Entry<String, ConcurrentLinkedQueue<TestCaseReport>> entry :  tempMap.entrySet()) {
			reportFileName = orf;
			try {
				List<TestCaseReport> reports = new ArrayList<TestCaseReport>(entry.getValue());
				Collections.sort(reports, new Comparator<TestCaseReport>() {
					public int compare(TestCaseReport o1, TestCaseReport o2) {
						 return o1==null ?
						         (o2==null ? 0 : Integer.MIN_VALUE) :
						         (o2==null ? Integer.MAX_VALUE : Integer.valueOf(o1.getTestCase().getSequence()).compareTo(o2.getTestCase().getSequence()));
					}
				});
				
				int grptot = 0, grpfld = 0, grpskp = 0, grpruns = 0, grpflruns = 0;
				long grpexecutionTime = 0;
				
				for (TestCaseReport testCaseReport : reports) {
					
					if(firstCompareCopy!=null) 
					{
						doCompare(testCaseReport, firstCompareCopy, compareStatuses, entry.getKey());
					}
					
					TestCaseStats tesStat = new TestCaseStats();
					tesStat.setIdentifier(entry.getKey());
					tesStat.setSourceFileName(testCaseReport.getTestCase().getSourcefileName());
					tesStat.setWorkflowName(testCaseReport.getWorkflowName());
					tesStat.setTestCaseName(testCaseReport.getTestCase().getName());
					if(testCaseReport.getAverageExecutionTime()!=null)
						tesStat.setExecutionTime(testCaseReport.getAverageExecutionTime());
					else
						tesStat.setExecutionTime(testCaseReport.getExecutionTime());
					tesStat.setStatus(TestStatus.Success.status);
					total ++;
					grptot++;
					if(testCaseReport.getNumberOfRuns()>1)
					{
						grpruns += testCaseReport.getNumberOfRuns();
						totruns += testCaseReport.getNumberOfRuns();
					}
					
					if(testCaseReport.getErrors()!=null && testCaseReport.getNumberOfRuns()>1)
					{
						grpflruns += testCaseReport.getErrors().size();
						failruns += testCaseReport.getErrors().size();
						if(testCaseReport.getError()!=null) {
							failruns += 1;
							grpflruns += 1;
						}
					}
					
					if(testCaseReport.getStatus().equals(TestStatus.Skipped.status)) {
						skipped ++;
						grpskp ++;
						tesStat.setStatus(TestStatus.Skipped.status);
					} else if(!testCaseReport.getStatus().equals(TestStatus.Success.status)) {
						failed ++;
						grpfld ++;
						tesStat.setStatus(TestStatus.Failed.status);
					}
					testCaseStats.add(tesStat);
					populateRequestResponseHeaders(testCaseReport);
					grpexecutionTime += testCaseReport.getExecutionTime();
					
					synchronized (testPercentiles) {
						testPercentiles.addExecutionTime(testCaseReport.getTestCase().getName(), testCaseReport.getExecutionTime());
					}
					synchronized (runPercentiles) {
						runPercentiles.addExecutionTime(testCaseReport.getTestCase().getIdentifier(), testCaseReport.getExecutionTime());
					}
				}
				
        		TestGroupStats testGroupStats = new TestGroupStats();
				testGroupStats.setSourceFile(entry.getKey());
				testGroupStats.setExecutionTime(grpexecutionTime);
				testGroupStats.setTotalTestCount(grptot);
				testGroupStats.setFailedTestCount(grpfld);
				testGroupStats.setSkippedTestCount(grpskp);
				testGroupStats.setTotalRuns(grpruns);
				testGroupStats.setFailedRuns(grpflruns);
				
				if(firstCompareCopy!=null)
				{
					testGroupStats.setBaseUrl(reports.get(0).getTestCase().getBaseUrl());
				}
				
				for (TestCaseReport testCaseReport : reports) {
					if(testCaseReport.getTestCase().getReportResponseContent()!=null
							&& !testCaseReport.getTestCase().getReportResponseContent())
						testCaseReport.setResponseContent(null);
					testCaseReport.setTestCase(null);
				}
				
				allTestCases.put(entry.getKey(), reports);
				
				testSuiteStats.getGroupStats().add(testGroupStats);
				testSuiteStats.setActualExecutionTime(testSuiteStats.getActualExecutionTime() + grpexecutionTime);
				
				if(firstCompareCopy==null) {
					try
					{
						String reportingJson = WorkflowContextHandler.OM.writeValueAsString(allTestCases);
						context.put("testcaseReports", reportingJson);
						allTestCases.clear();
						context.put("compareStats", "{}");
						
						InputStream resourcesIS = GatfTestCaseExecutorUtil.class.getResourceAsStream("/gatf-resources");
			            if (resourcesIS != null)
			            {
			            	
			            	File resource = acontext.getOutDir();
			                /*if(runNumber==1 && unzipFile) {
			                	WorkflowContextHandler.copyResourcesToDirectory("gatf-resources", resource.getAbsolutePath());
			                	//unzipGatfResources(resourcesIS, resource.getAbsolutePath());
			                }*/
			                
			                VelocityEngine engine = new VelocityEngine();
			                engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
			                engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
			                engine.init();
			                
			                StringWriter writer = new StringWriter();
			                engine.mergeTemplate("/gatf-templates/index-run.vm", "UTF-8", context, writer);

			                if(reportFileName==null)
			                	reportFileName = "index.html";
			                
			                if(entry.getKey().startsWith("Run") && !entry.getKey().endsWith(".xml")) {
			                	reportFileName = reportFileName.replaceFirst("\\.html", "");
			                	reportFileName += entry.getKey().substring(4);
			                	reportFileName += ".html";
			                } else if(!reportFileName.equals("index.html")) {
			                	reportFileName = reportFileName.replaceFirst("\\.html", "");
			                	reportFileName += "1.html";
			                } else {
			                	reportFileName = "index1.html";
			                }
			                
			                String filenm = resource.getAbsolutePath() + File.separator + reportFileName.replaceAll("[^a-zA-Z0-9-_\\.]", "_");
			                BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
			                fwriter.write(writer.toString());
			                fwriter.close();
			                
			                /*if(distributedTestStatus!=null && distributedTestStatus.getReportFileContent().size()<5)
			                {
			                	distributedTestStatus.getReportFileContent().put(reportFileName, writer.toString());
			                }*/
			            }
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
        		
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		if(testSuiteStats.getTotalSuiteRuns()==0) {
			testSuiteStats.setTotalTestCount(total);
			testSuiteStats.setFailedTestCount(failed);
			testSuiteStats.setSkippedTestCount(skipped);
			testSuiteStats.setTotalRuns(totruns);
			testSuiteStats.setFailedRuns(failruns);
			testSuiteStats.setTotalUserSuiteRuns(1);
			testSuiteStats.setTotalSuiteRuns(1);
		} else {
			TestSuiteStats tempst = new TestSuiteStats();
			tempst.setTotalTestCount(total);
			tempst.setFailedTestCount(failed);
			tempst.setSkippedTestCount(skipped);
			tempst.setTotalRuns(totruns);
			tempst.setFailedRuns(failruns);
			tempst.setTotalSuiteRuns(1);
			testSuiteStats.updateStats(tempst, true);
		}
		
		if(distRunPrefix==null)
			getFinalTestResults().get("Run-"+ runNumber).clear();
		else
			getFinalTestResults().get(distRunPrefix + "-" + runNumber).clear();
	}

	public TestSuiteStats doReportingIndex(AcceptanceTestContext acontext, long suiteStartTime, String reportFileName, 
			int numberOfRuns, String prefix, boolean isLoadTestingEnabled) {
		GatfExecutorConfig config = acontext.getGatfExecutorConfig();
		
		VelocityContext context = new VelocityContext();
		try
		{
			context.put("userSimulation", false);
			if(config.getConcurrentUserSimulationNum()!=null && config.getConcurrentUserSimulationNum()>1) {
				context.put("userSimulation", true);
			}
			
			context.put("compareEnabled", false);
		} catch (Exception e) {
			e.printStackTrace();
		}

		context.put("testcaseReports", "[]");
		context.put("isMultipleRuns", acontext.getGatfExecutorConfig().getConcurrentUserSimulationNum()>1);
		
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(testCaseStats);
			context.put("testcaseStats", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			testSuiteStats.setTotalUserSuiteRuns(numberOfRuns);
			testSuiteStats.setExecutionTime(System.currentTimeMillis() - suiteStartTime);
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(testSuiteStats);
			context.put("suiteStats", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			context.put("compareStats", "{}");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			InputStream resourcesIS = GatfTestCaseExecutorUtil.class.getResourceAsStream("/gatf-resources");
            if (resourcesIS != null)
            {
            	File resource = acontext.getOutDir();
                VelocityEngine engine = new VelocityEngine();
                engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
                engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
                engine.init();
                
                if(reportFileName==null)
                	reportFileName = "index.html";
                
                context.put("thisFile", reportFileName.replaceFirst(".html", ""));
                context.put("isShowTable", false);
                context.put("runPrefix", prefix==null?"":prefix);
                context.put("isLoadTestingEnabled", isLoadTestingEnabled);
                
                StringWriter writer = new StringWriter();
                engine.mergeTemplate("/gatf-templates/index.vm", "UTF-8", context, writer);

                String filenm = resource.getAbsolutePath() + File.separator + reportFileName.replaceAll("[^a-zA-Z0-9-_\\.]", "_");
                BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
                fwriter.write(writer.toString());
                fwriter.close();
                
                /*if(distributedTestStatus!=null && distributedTestStatus.getReportFileContent().size()<5)
                {
                	distributedTestStatus.getReportFileContent().put(reportFileName, writer.toString());
                }*/
            }
		} catch (Exception e) {
			e.printStackTrace();
		}
		clearTestResults();
		
		return testSuiteStats;
	}

	public TestSuiteStats doReporting(AcceptanceTestContext acontext, long startTime, String reportFileName, String prefix,
			boolean isLoadTestingEnabled, TestExecutionPercentile testPercentiles, TestExecutionPercentile runPercentiles) {
		GatfExecutorConfig config = acontext.getGatfExecutorConfig();
		int total = 0, failed = 0, totruns = 0, failruns = 0, skipped = 0;
		
		AlphanumComparator<String> comparator = new AlphanumComparator<String>();
		
		Map<String, List<TestCaseReport>> allTestCases = new TreeMap<String, List<TestCaseReport>>(comparator);
		Map<String, ConcurrentLinkedQueue<TestCaseReport>> tempMap = new TreeMap<String, ConcurrentLinkedQueue<TestCaseReport>>(comparator);
		tempMap.putAll(getFinalTestResults());
		
		Map<String, List<TestCaseCompareStatus>> compareStatuses = new TreeMap<String, List<TestCaseCompareStatus>>(comparator);
		
		Map<String, TestCaseReport> firstCompareCopy = null;
		if(acontext.getGatfExecutorConfig().getCompareBaseUrlsNum()!=null
				&& acontext.getGatfExecutorConfig().getCompareBaseUrlsNum()>1)
		{
			firstCompareCopy = new HashMap<String, TestCaseReport>();
		}
		
		VelocityContext context = new VelocityContext();
		try
		{
			context.put("userSimulation", false);
			if(config.getConcurrentUserSimulationNum()!=null && config.getConcurrentUserSimulationNum()>1) {
				context.put("userSimulation", true);
			}
			
			context.put("compareEnabled", false);
			if(firstCompareCopy!=null) {
				context.put("compareEnabled", true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		//boolean unzipped = false;
		String orf = reportFileName;
		for (Map.Entry<String, ConcurrentLinkedQueue<TestCaseReport>> entry :  tempMap.entrySet()) {
			reportFileName = orf;
			try {
				List<TestCaseReport> reports = new ArrayList<TestCaseReport>(entry.getValue());
				Collections.sort(reports, new Comparator<TestCaseReport>() {
					public int compare(TestCaseReport o1, TestCaseReport o2) {
						 return o1==null ?
						         (o2==null ? 0 : Integer.MIN_VALUE) :
						         (o2==null ? Integer.MAX_VALUE : Integer.valueOf(o1.getTestCase().getSequence()).compareTo(o2.getTestCase().getSequence()));
					}
				});
				
				int grptot = 0, grpfld = 0, grpskp = 0, grpruns = 0, grpflruns = 0;
				long grpexecutionTime = 0;
				
				for (TestCaseReport testCaseReport : reports) {
					
					if(firstCompareCopy!=null) 
					{
						doCompare(testCaseReport, firstCompareCopy, compareStatuses, entry.getKey());
					}
					
					TestCaseStats tesStat = new TestCaseStats();
					tesStat.setIdentifier(entry.getKey());
					tesStat.setSourceFileName(testCaseReport.getTestCase().getSourcefileName());
					tesStat.setWorkflowName(testCaseReport.getWorkflowName());
					tesStat.setTestCaseName(testCaseReport.getTestCase().getName());
					if(testCaseReport.getAverageExecutionTime()!=null)
						tesStat.setExecutionTime(testCaseReport.getAverageExecutionTime());
					else
						tesStat.setExecutionTime(testCaseReport.getExecutionTime());
					tesStat.setStatus(TestStatus.Success.status);
					total ++;
					grptot++;
					if(testCaseReport.getNumberOfRuns()>1)
					{
						grpruns += testCaseReport.getNumberOfRuns();
						totruns += testCaseReport.getNumberOfRuns();
					}
					
					if(testCaseReport.getErrors()!=null && testCaseReport.getNumberOfRuns()>1)
					{
						grpflruns += testCaseReport.getErrors().size();
						failruns += testCaseReport.getErrors().size();
						if(testCaseReport.getError()!=null) {
							failruns += 1;
							grpflruns += 1;
						}
					}
					
					if(testCaseReport.getStatus().equals(TestStatus.Skipped.status)) {
						skipped ++;
						grpskp ++;
						tesStat.setStatus(TestStatus.Skipped.status);
					} else if(!testCaseReport.getStatus().equals(TestStatus.Success.status)) {
						failed ++;
						grpfld ++;
						tesStat.setStatus(TestStatus.Failed.status);
					}
					testCaseStats.add(tesStat);
					populateRequestResponseHeaders(testCaseReport);
					grpexecutionTime += testCaseReport.getExecutionTime();
					
					synchronized (testPercentiles) {
						testPercentiles.addExecutionTime(testCaseReport.getTestCase().getName(), testCaseReport.getExecutionTime());
					}
					synchronized (runPercentiles) {
						runPercentiles.addExecutionTime(testCaseReport.getTestCase().getIdentifier(), testCaseReport.getExecutionTime());
					}
				}
				
        		TestGroupStats testGroupStats = new TestGroupStats();
				testGroupStats.setSourceFile(entry.getKey());
				testGroupStats.setExecutionTime(grpexecutionTime);
				testGroupStats.setTotalTestCount(grptot);
				testGroupStats.setFailedTestCount(grpfld);
				testGroupStats.setSkippedTestCount(grpskp);
				testGroupStats.setTotalRuns(grpruns);
				testGroupStats.setFailedRuns(grpflruns);
				
				if(firstCompareCopy!=null)
				{
					testGroupStats.setBaseUrl(reports.get(0).getTestCase().getBaseUrl());
				}
				
				for (TestCaseReport testCaseReport : reports) {
					if(testCaseReport.getTestCase().getReportResponseContent()!=null
							&& !testCaseReport.getTestCase().getReportResponseContent())
						testCaseReport.setResponseContent(null);
					testCaseReport.setTestCase(null);
				}
				
				allTestCases.put(entry.getKey(), reports);
				
				testSuiteStats.getGroupStats().add(testGroupStats);
				testSuiteStats.setActualExecutionTime(testSuiteStats.getActualExecutionTime() + grpexecutionTime);
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		long endTime = System.currentTimeMillis();
		
		if(testSuiteStats.getTotalSuiteRuns()==0) {
			testSuiteStats.setTotalTestCount(total);
			testSuiteStats.setFailedTestCount(failed);
			testSuiteStats.setSkippedTestCount(skipped);
			testSuiteStats.setTotalRuns(totruns);
			testSuiteStats.setFailedRuns(failruns);
			testSuiteStats.setTotalUserSuiteRuns(1);
			testSuiteStats.setTotalSuiteRuns(1);
		} else {
			TestSuiteStats tempst = new TestSuiteStats();
			tempst.setTotalTestCount(total);
			tempst.setFailedTestCount(failed);
			tempst.setSkippedTestCount(skipped);
			tempst.setTotalRuns(totruns);
			tempst.setFailedRuns(failruns);
			tempst.setTotalSuiteRuns(1);
			testSuiteStats.updateStats(tempst, true);
		}
		
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(allTestCases);
			context.put("testcaseReports", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		context.put("isMultipleRuns", acontext.getGatfExecutorConfig().getConcurrentUserSimulationNum()>1);
		
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(testCaseStats);
			context.put("testcaseStats", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			testSuiteStats.setExecutionTime(endTime - startTime);
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(testSuiteStats);
			context.put("suiteStats", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(compareStatuses);
			context.put("compareStats", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			InputStream resourcesIS = GatfTestCaseExecutorUtil.class.getResourceAsStream("/gatf-resources");
            if (resourcesIS != null)
            {
            	File resource = acontext.getOutDir();
                /*if(!unzipped) {
                	WorkflowContextHandler.copyResourcesToDirectory("gatf-resources", resource.getAbsolutePath());
                	//unzipGatfResources(resourcesIS, resource.getAbsolutePath());
                }*/
                
                VelocityEngine engine = new VelocityEngine();
                engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
                engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
                engine.init();
                
                if(orf==null)
                	orf = "index.html";
                
                context.put("thisFile", orf.replaceFirst(".html", ""));
                context.put("isShowTable", true);
                context.put("runPrefix", prefix==null?"":prefix);
                context.put("isLoadTestingEnabled", isLoadTestingEnabled);
                
                StringWriter writer = new StringWriter();
                engine.mergeTemplate("/gatf-templates/index.vm", "UTF-8", context, writer);

                String filenm = resource.getAbsolutePath() + File.separator + orf.replaceAll("[^a-zA-Z0-9-_\\.]", "_");
                BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
                fwriter.write(writer.toString());
                fwriter.close();
                
                /*if(distributedTestStatus!=null && distributedTestStatus.getReportFileContent().size()<5)
                {
                	distributedTestStatus.getReportFileContent().put(orf, writer.toString());
                }*/
            }
		} catch (Exception e) {
			e.printStackTrace();
		}
		clearTestResults();
		
		return testSuiteStats;
	}

	public static void doTAReporting(String prefix, AcceptanceTestContext acontext, boolean isLoadTestingEnabled,
			TestExecutionPercentile testPercentiles, TestExecutionPercentile runPercentiles) {
		Map<String, List<Long>> testPercentileValues = testPercentiles.getPercentileTimes();
		Map<String, List<Long>> runPercentileValues = runPercentiles.getPercentileTimes();
		VelocityContext context = new VelocityContext();
		
		try
		{
			String reportingJson = WorkflowContextHandler.OM.writeValueAsString(testPercentileValues);
			context.put("testcaseTAReports", reportingJson);
			
			if(testPercentileValues.size()>0)
				context.put("isShowTAWrapper", testPercentileValues.values().iterator().next().size()>0);
			else
				context.put("isShowTAWrapper", false);
			
			if(runPercentileValues.size()>0)
			{
				List<Long> times90 = new ArrayList<Long>();
				List<Long> times50 = new ArrayList<Long>();
				for (List<Long> times : runPercentileValues.values()) {
					times90.add(times.get(0));
					times50.add(times.get(1));
				}
				
				runPercentileValues.put("All", times90);
				
				Collections.sort(times90);
				int index = Math.round((float)0.9 * times90.size());
				long time = times90.get(index-1);
				times90.clear();
				times90.add(time);
				
				Collections.sort(times50);
				index = Math.round((float)0.5 * times50.size());
				time = times50.get(index-1);
				times90.add(time);
			}
			
			reportingJson = WorkflowContextHandler.OM.writeValueAsString(runPercentileValues);
			context.put("runTAReports", reportingJson);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try
		{
			InputStream resourcesIS = GatfTestCaseExecutorUtil.class.getResourceAsStream("/gatf-resources");
            if (resourcesIS != null)
            {
            	File resource = acontext.getOutDir();
                VelocityEngine engine = new VelocityEngine();
                engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
                engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
                engine.init();
                
                context.put("isLoadTestingEnabled", isLoadTestingEnabled);
                
                StringWriter writer = new StringWriter();
                engine.mergeTemplate("/gatf-templates/index-ta.vm", "UTF-8", context, writer);

                prefix = prefix==null?"":prefix;
                
                String filenm = resource.getAbsolutePath() + File.separator + prefix.replaceAll("[^a-zA-Z0-9-_\\.]", "_") + "index-ta.html";
                BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
                fwriter.write(writer.toString());
                fwriter.close();
                
                /*if(distributedTestStatus!=null && distributedTestStatus.getReportFileContent().size()<5)
                {
                	distributedTestStatus.getReportFileContent().put("", writer.toString());
                }*/
            }
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void doSeleniumSummaryTestReport(Map<String, List<Map<String, Map<String, List<Object[]>>>>> summLstMap, AcceptanceTestContext acontext, int loadTestRunNum, String runPrefix)
    {
        VelocityContext context = new VelocityContext();
        try
        {
            context.put("testsMap", summLstMap);
            
            File resource = acontext.getOutDir();
            //InputStream resourcesIS = GatfTestCaseExecutorUtil.class.getResourceAsStream("/gatf-resources");
            /*if (resourcesIS != null)
            {
            	WorkflowContextHandler.copyResourcesToDirectory("gatf-resources", resource.getAbsolutePath());
            	//unzipGatfResources(resourcesIS, resource.getAbsolutePath());
            }*/
            
            VelocityEngine engine = new VelocityEngine();
            engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            engine.init();
            
            StringWriter writer = new StringWriter();
            engine.mergeTemplate("/gatf-templates/index-selenium-summ.vm", "UTF-8", context, writer);
            
            String filenm = resource.getAbsolutePath() + File.separator + runPrefix + "-" + loadTestRunNum + "-selenium-index.html";
            BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
            fwriter.write(writer.toString());
            fwriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	public static void doSeleniumFinalTestReport(Map<String, Map<Integer, String>> indexes, AcceptanceTestContext acontext)
    {
        VelocityContext context = new VelocityContext();
        try
        {
            context.put("indexes", indexes);
            File resource = acontext.getOutDir();
            
            VelocityEngine engine = new VelocityEngine();
            engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            engine.init();
            
            StringWriter writer = new StringWriter();
            engine.mergeTemplate("/gatf-templates/selenium-final-index.vm", "UTF-8", context, writer);
            
            String filenm = resource.getAbsolutePath() + File.separator + "selenium-index.html";
            BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
            fwriter.write(writer.toString());
            fwriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	/*private static void unzipGatfResources(InputStream resourcesIS, String path) throws IOException {
		File gctzip = File.createTempFile("gatf-resources-"+UUID.randomUUID(), ".zip");
		IOUtils.copy(resourcesIS, new FileOutputStream(gctzip));
    	unzipZipFile(gctzip, path);
	}*/
    
    public static void doSeleniumTestReport(String prefix, Object[] retvals, SeleniumTestResult result, AcceptanceTestContext acontext)
    {
        VelocityContext context = new VelocityContext();
        File resource = acontext.getOutDir();
        
        //InputStream resourcesIS = GatfTestCaseExecutorUtil.class.getResourceAsStream("/gatf-resources");
        /*if (resourcesIS != null)
        {
        	WorkflowContextHandler.copyResourcesToDirectory("gatf-resources", resource.getAbsolutePath());
        	//unzipGatfResources(resourcesIS, resource.getAbsolutePath());
        }*/
        
        try
        {
            context.put("selFileName", StringEscapeUtils.escapeHtml4((String)retvals[0]));
            context.put("selCode", StringEscapeUtils.escapeHtml4((String)retvals[1]));
            context.put("javaFileName", StringEscapeUtils.escapeHtml4((String)retvals[2]));
            context.put("javaCode", StringEscapeUtils.escapeHtml4((String)retvals[3]));
            context.put("selLogs", result.getLogs());
            context.put("succFail", result.isStatus()?"SUCCESS":"FAILED");
            context.put("StringEscapeUtils", StringEscapeUtils.class);
            
            VelocityEngine engine = new VelocityEngine();
            engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            engine.init();
            
            StringWriter writer = new StringWriter();
            engine.mergeTemplate("/gatf-templates/index-selenium-tr.vm", "UTF-8", context, writer);
            
            String filenm = resource.getAbsolutePath() + File.separator + prefix + ".html";
            BufferedWriter fwriter = new BufferedWriter(new FileWriter(new File(filenm)));
            fwriter.write(writer.toString());
            fwriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	public static void populateRequestResponseHeaders(TestCaseReport testCaseReport)
	{
		StringBuilder build = new StringBuilder();
		if(testCaseReport.getTestCase()!=null && testCaseReport.getTestCase().getHeaders()!=null)
		{
			for (Map.Entry<String, String> headerVal : testCaseReport.getTestCase().getHeaders().entrySet()) {
				build.append(headerVal.getKey());
				build.append(": ");
	            build.append(headerVal.getValue());
	            build.append("\n");
	            
	            if(headerVal.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_TYPE)) {
	            	testCaseReport.setRequestContentType(headerVal.getValue());
	            }
			}
			testCaseReport.setRequestHeaders(build.toString());
		}
		
		if(testCaseReport.getResHeaders()!=null)
		{
			build = new StringBuilder();
			for (Map.Entry<String, List<String>> entry : testCaseReport.getResHeaders().entrySet()) {
				build.append(entry.getKey());
				build.append(": ");
				
				boolean needsComma = false;
	            for (String value : entry.getValue()) {
	                if (needsComma) {
	                	build.append(", ");
	                } else {
	                    needsComma = true;
	                }
	                build.append(value);
	            }
	            build.append("\n");
			}
			testCaseReport.setResponseHeaders(build.toString());
		}
	}

	public Map<String, ConcurrentLinkedQueue<TestCaseReport>> getFinalTestResults() {
		return finalTestResults;
	}
	
	public void addTestCaseReport(TestCaseReport testCaseReport) {
		String key = testCaseReport.getTestCase().getIdentifier() + testCaseReport.getTestCase().getName();
		getFinalTestResults().get(testCaseReport.getTestCase().getIdentifier()).add(testCaseReport);
		if(!finalTestReportsDups.containsKey(key)) {
			finalTestReportsDups.put(key, 1);
		} else {
			Integer ncount = finalTestReportsDups.get(key) + 1;
			String ext = "-" + ncount;
			finalTestReportsDups.put(key, ncount);
			testCaseReport.setTestIdentifier(testCaseReport.getTestIdentifier()+ext);
		}
	}
	
	public void clearTestResults() {
		
		for (Map.Entry<String, ConcurrentLinkedQueue<TestCaseReport>> entry :  getFinalTestResults().entrySet()) {
			entry.getValue().clear();
		}
		finalTestReportsDups.clear();
	}
	
	public void initializeResultsHolders(int runNums, String fileName)
	{
		if(runNums>1)
		{
			for (int i = 0; i < runNums; i++)
			{
				finalTestResults.put("Run-" + (i+1), new ConcurrentLinkedQueue<TestCaseReport>());
			}
		}
		else
		{
			finalTestResults.put(fileName, new ConcurrentLinkedQueue<TestCaseReport>());
		}
	}
}
 public class RuntimeReportUtil {
    
	public static class LoadTestEntry implements Serializable
	{
		private static final long serialVersionUID = 1L;
		
		String node;
		String prefix;
		int runNo;
		String url;
		Date time;
		TestSuiteStats currStats;
		Map<String, Map<String, List<Object[]>>> currSelStats;
		
		public LoadTestEntry(String node, String prefix, int runNo, String url, TestSuiteStats currStats, Date time) {
			super();
			this.node = node;
			this.prefix = prefix;
			this.runNo = runNo;
			this.url = url;
			this.currStats = currStats;
			this.time = time;
		}
        
        public LoadTestEntry(String node, String prefix, int runNo, String url, Map<String, Map<String, List<Object[]>>> currSelStats) {
            super();
            this.node = node;
            this.prefix = prefix;
            this.runNo = runNo;
            this.url = url;
            this.currSelStats = currSelStats;
        }
        
        public LoadTestEntry() {
        }
		
		public String getNode() {
			return node;
		}
		public String getPrefix() {
			return prefix;
		}
		public int getRunNo() {
			return runNo;
		}
		public String getUrl() {
			return url;
		}
		public Date getTime() {
			return time;
		}
		public TestSuiteStats getCurrStats() {
			return currStats;
		}
        public Map<String, Map<String, List<Object[]>>> getCurrSelStats() {
            return currSelStats;
        }
	}
	
	private static volatile boolean registered = false;
	
	private static TestSuiteStats gloadStats = new TestSuiteStats();
	
	private static ConcurrentLinkedQueue<Object> Q = new ConcurrentLinkedQueue<Object>();
	
	private static ConcurrentLinkedQueue<Object> Qdl = new ConcurrentLinkedQueue<Object>();
	
	public static void registerConfigUI()
	{
		registered = true;
	}
	
	public static void unRegisterConfigUI()
	{
		registered = false;
		Q.clear();
		Qdl.clear();
		synchronized (gloadStats) {
			gloadStats = new TestSuiteStats();
		}
	}
	
	public static void addLEntry(LoadTestEntry lentry)
	{
		Qdl.add(lentry);
	}
	
	public static void addEntry(int runNo, boolean subtestPassed)
    {
	    if(registered)
        {
	        try {
                Q.add("local|"+runNo+"|"+(subtestPassed?1:0));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
	    else
	    {
	        try {
	            Qdl.add(runNo+"|"+(subtestPassed?1:0));
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
    }
	
	public static void addEntry(Object entry)
	{
		if(registered)
		{
		    if(entry instanceof LoadTestEntry) {
		        LoadTestEntry lentry = (LoadTestEntry)entry;
		        List<Object> parts = new ArrayList<Object>();
	            if(lentry.prefix==null)
	                lentry.prefix = "Run";
	            parts.add(lentry.prefix);//
	            parts.add(lentry.runNo+"");
	            parts.add(lentry.url);
	            parts.add(lentry.node);
	            parts.add(lentry.time);
	            parts.add(lentry.currStats.toList());
	            parts.add(lentry.currSelStats);
	            if(lentry.currStats!=null) {
	                synchronized (gloadStats) {
	                    gloadStats.updateStats(lentry.currStats, false);
	                    try {
	                        Q.add(parts);
	                    } catch (Exception e) {
	                        e.printStackTrace();
	                    }
	                }
	            }
		    } else {
		        try {
                    Q.add(entry);
                } catch (Exception e) {
                    e.printStackTrace();
                }
		    }
		}
		else
		{
		    if(entry instanceof LoadTestEntry) {
                LoadTestEntry lentry = (LoadTestEntry)entry;
    		    if(lentry.currStats!=null) {
                    synchronized (gloadStats) {
        				gloadStats.updateStats(lentry.currStats, false);
        				System.out.println(gloadStats);
        			}
    		    }
		    }
		}
	}
	
	public static void addEntry(String node, String prefix, int runNo, String url, TestSuiteStats currStats, Date time)
	{
		if(registered)
		{
			List<Object> parts = new ArrayList<Object>();
			if(prefix==null)
				prefix = "Run";
			parts.add(prefix);
			parts.add(runNo+"");
			parts.add(url);
			parts.add(node);
			parts.add(time.getTime());
			parts.add(currStats.toList());
			parts.add(null);
			synchronized (gloadStats) {
				gloadStats.updateStats(currStats, false);
				try {
					Q.add(parts);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		else
		{
			synchronized (gloadStats) {
				gloadStats.updateStats(currStats, false);
				System.out.println(gloadStats);
			}
		}
	}
	
	public static boolean isDone() {
	    return Q.isEmpty();
	}
	
	@SuppressWarnings("unchecked")
    public static byte[] getEntry()
	{
	    try {
    	    List<List<Object>> st = new ArrayList<List<Object>>();
    	    List<String> sst = new ArrayList<String>();
    	    Object parts = null;
    	    while((parts = Q.poll())!=null) {
    	        if(parts instanceof String) {
    	            sst.add((String)parts);
    	        } /*else if(parts instanceof Map) {
    	            st.add((Map<String, Object>)parts);
    	        }*/ else {
    	        	st.add((List<Object>)parts);
    	        }
    	        if(st.size()>=1000 || sst.size()>=1000)break;
    	    }
    	    //synchronized (gloadStats) {
    	    //	gstats = ",\"gstats\":" + WorkflowContextHandler.OM.writeValueAsString(gloadStats);
    	    //}
    	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    	    baos.write("{\"error\":\"Execution already in progress..\",\"lstats\":".getBytes("UTF-8"));
    	    baos.write(WorkflowContextHandler.OM.writeValueAsBytes(st));
    	    baos.write(",\"sstats\":".getBytes("UTF-8"));
    	    baos.write(WorkflowContextHandler.OM.writeValueAsBytes(sst));
	    	baos.write(",\"gstats\":".getBytes("UTF-8"));
    	    synchronized (gloadStats) {
        	    baos.write(WorkflowContextHandler.OM.writeValueAsBytes(gloadStats));
    	    }
    	    baos.write("}".getBytes("UTF-8"));
            //String arr = "{\"error\":\"Execution already in progress..\",\"lstats\":" + 
            //        WorkflowContextHandler.OM.writeValueAsString(st) + ",\"sstats\":" + 
            //        WorkflowContextHandler.OM.writeValueAsString(sst) + gstats + "}";
            return baos.toByteArray();
	    } catch (Exception e) {
            e.printStackTrace();
        }
	    return new byte[] {};
	}
	
	public static Object getDLEntry()
	{
		return Qdl.poll();
	}
}
 public static class LoadTestEntry implements Serializable
{
	private static final long serialVersionUID = 1L;
		
	String node;
	String prefix;
	int runNo;
	String url;
	Date time;
	TestSuiteStats currStats;
	Map<String, Map<String, List<Object[]>>> currSelStats;
		
	public LoadTestEntry(String node, String prefix, int runNo, String url, TestSuiteStats currStats, Date time) {
		super();
		this.node = node;
		this.prefix = prefix;
		this.runNo = runNo;
		this.url = url;
		this.currStats = currStats;
		this.time = time;
	}
        
       public LoadTestEntry(String node, String prefix, int runNo, String url, Map<String, Map<String, List<Object[]>>> currSelStats) {
           super();
           this.node = node;
           this.prefix = prefix;
           this.runNo = runNo;
           this.url = url;
           this.currSelStats = currSelStats;
       }
        
       public LoadTestEntry() {
       }
		
	public String getNode() {
		return node;
	}
	public String getPrefix() {
		return prefix;
	}
	public int getRunNo() {
		return runNo;
	}
	public String getUrl() {
		return url;
	}
	public Date getTime() {
		return time;
	}
	public TestSuiteStats getCurrStats() {
		return currStats;
	}
       public Map<String, Map<String, List<Object[]>>> getCurrSelStats() {
           return currSelStats;
       }
}
 public class TestCaseExecutionLogGenerator implements Runnable {

	private static ConcurrentLinkedQueue<String> Q = new ConcurrentLinkedQueue<String>();
	
	GatfExecutorConfig config;
	
	private Writer writer;
	
	public TestCaseExecutionLogGenerator(GatfExecutorConfig config)
	{
		this.config = config;
	}
	
	public static void log(TestCaseReport report)
	{
		Q.add(report.toStatisticsCsv());
	}

	public void run() {
		try {
			File basePath = new File(config.getOutFilesBasePath());
			File resource = new File(basePath, config.getOutFilesDir());
			writer = new BufferedWriter(new FileWriter(new File(resource.getAbsolutePath()
                    + File.separator + UUID.randomUUID().toString() + ".csv")));
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		try {
			while(true)
			{
				String logentry = null;
				while((logentry = Q.poll())==null)
				{
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						throw e;
					}
				}
				writer.write(logentry);
			}
		} catch (Exception e) {
			if(!(e instanceof InterruptedException))
			{
				e.printStackTrace();
			}
		} finally {
			if(writer!=null)
			{
				try {
					String logentry = null;
					while((logentry = Q.poll())!=null)
					{
						writer.write(logentry);
					}
					writer.flush();
					writer.close();
				} catch (Exception e) {
				}
			}
		}
	}
}
 @JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class TestCaseReport implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private String actualUrl;
	
	private String url;
	
	private String method;
	
	private String workflowName;

	private TestCase testCase;
	
	private String status;
	
	private String failureReason;
	
	private Integer numberOfRuns;
	
	private Long executionTime = 0L;
	
	private List<Long> executionTimes = new ArrayList<Long>();
	
	private Long averageExecutionTime;
	
	private String requestContent;
	
	private String requestHeaders;
	
	private String responseHeaders;
	
	private String requestContentType;
	
	private String responseContentType;
	
	private String responseContent;
	
	private Integer responseStatusCode;
	
	private String error;
	
	private String errorText;
	
	private Map<String, String> errors = new HashMap<String, String>();
	
	private String testIdentifier;
	
	private List<String> aexpectedNodes = new ArrayList<String>();
	
	private String serverLogs;

	@XStreamOmitField
	@JsonIgnore
	private Map<String, List<String>> resHeaders;
	
	private List<Map<String, Object>> perfResult = new ArrayList<Map<String, Object>>();
	
	public String getWorkflowName() {
		return workflowName;
	}

	public void setWorkflowName(String workflowName) {
		this.workflowName = workflowName;
	}

	public TestCase getTestCase() {
		return testCase;
	}

	public void setTestCase(TestCase testCase) {
		this.testCase = testCase;
		if(testCase!=null) {
			if(testCase.getIdentifier().equals(testCase.getSourcefileName()))
				setTestIdentifier(testCase.getIdentifier()+"\n"+testCase.getName());
			else
				setTestIdentifier(testCase.getIdentifier()+"\n"+testCase.getSourcefileName()+"\n"+testCase.getName());
			if(testCase.getAurl()!=null)
			{
				setActualUrl(testCase.getAurl());
			}
			else
			{
				setActualUrl(testCase.getUrl());
			}
			setRequestContent(testCase.getAcontent());
			setUrl(testCase.getUrl());
			setMethod(testCase.getMethod());
			setAexpectedNodes(testCase.getAexpectedNodes());
		}
	}
	
	public void setTestCaseExternalApi(TestCase testCase) {
		this.testCase = testCase;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public Integer getNumberOfRuns() {
		return numberOfRuns;
	}

	public void setNumberOfRuns(Integer numberOfRuns) {
		this.numberOfRuns = numberOfRuns;
	}

	public Long getExecutionTime() {
		return executionTime;
	}

	public void setExecutionTime(Long executionTime) {
		this.executionTime = executionTime;
	}

	public List<Long> getExecutionTimes() {
		return executionTimes;
	}

	public void setExecutionTimes(List<Long> executionTimes) {
		this.executionTimes = executionTimes;
	}

	public String getRequestContent() {
		return requestContent;
	}

	public void setRequestContent(String requestContent) {
		this.requestContent = requestContent;
	}

	public String getResponseHeaders() {
		return responseHeaders;
	}

	public void setResponseHeaders(String responseHeaders) {
		this.responseHeaders = responseHeaders;
	}

	public String getResponseContent() {
		return responseContent;
	}

	public void setResponseContent(String responseContent) {
		this.responseContent = responseContent;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public String getActualUrl() {
		return actualUrl;
	}

	public void setActualUrl(String actualUrl) {
		this.actualUrl = actualUrl;
	}

	public String getErrorText() {
		return errorText;
	}

	public void setErrorText(String errorText) {
		this.errorText = errorText;
	}

	public String getTestIdentifier() {
		return testIdentifier;
	}

	public void setTestIdentifier(String testIdentifier) {
		this.testIdentifier = testIdentifier;
	}

	public Long getAverageExecutionTime() {
		return averageExecutionTime;
	}

	public void setAverageExecutionTime(Long averageExecutionTime) {
		this.averageExecutionTime = averageExecutionTime;
	}

	public Map<String, String> getErrors() {
		return errors;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public void setErrors(Map<String, String> errors) {
		this.errors = errors;
	}

	public String getRequestHeaders() {
		return requestHeaders;
	}

	public void setRequestHeaders(String requestHeaders) {
		this.requestHeaders = requestHeaders;
	}

	public String getRequestContentType() {
		return requestContentType;
	}

	public void setRequestContentType(String requestContentType) {
		this.requestContentType = requestContentType;
	}

	public String getResponseContentType() {
		return responseContentType;
	}

	public void setResponseContentType(String responseContentType) {
		this.responseContentType = responseContentType;
	}

	public Integer getResponseStatusCode() {
		return responseStatusCode;
	}

	public void setResponseStatusCode(Integer responseStatusCode) {
		this.responseStatusCode = responseStatusCode;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public String getFailureReason() {
		return failureReason;
	}

	public void setFailureReason(String failureReason) {
		this.failureReason = failureReason;
	}

	public List<String> getAexpectedNodes() {
		return aexpectedNodes;
	}

	public void setAexpectedNodes(List<String> aexpectedNodes) {
		this.aexpectedNodes = aexpectedNodes;
	}

	public Map<String, List<String>> getResHeaders() {
		return resHeaders;
	}

	public void setResHeaders(Map<String, List<String>> resHeaders) {
		this.resHeaders = resHeaders;
	}

	public String getServerLogs() {
		return serverLogs;
	}

	public void setServerLogs(String serverLogs) {
		this.serverLogs = serverLogs;
	}

	public List<Map<String, Object>> getPerfResult() {
		return perfResult;
	}

	public void setPerfResult(List<Map<String, Object>> perfResult) {
		this.perfResult = perfResult;
	}

	public TestCaseReport(){}
	
	public TestCaseReport(TestCaseReport other) {
		super();
		this.actualUrl = other.actualUrl;
		this.workflowName = other.workflowName;
		this.testCase = other.testCase;
		this.status = other.status;
		this.numberOfRuns = other.numberOfRuns;
		this.executionTime = other.executionTime;
		if(other.executionTimes!=null)
		{
			this.executionTimes = new ArrayList<Long>(other.executionTimes);
		}
		this.requestContent = other.requestContent;
		this.responseHeaders = other.responseHeaders;
		this.responseContent = other.responseContent;
		this.error = other.error;
		this.errorText = other.errorText;
		this.testIdentifier = other.testIdentifier;
		if(other.errors!=null)
		{
			this.errors = other .errors;
		}
		this.responseStatusCode = other.responseStatusCode;
		this.method = other.method;
		this.failureReason = other.failureReason;
		this.perfResult = other.perfResult;
	}

	@Override
	public String toString() {
		final int maxLen = 10;
		StringBuilder builder = new StringBuilder();
		builder.append("TestCaseReport = [\n");
		builder.append("actualUrl=");
		builder.append(actualUrl);
		builder.append("\nurl=");
		builder.append(url);
		builder.append("\nmethod=");
		builder.append(method);
		builder.append("\nworkflowName=");
		builder.append(workflowName);
		builder.append("\nstatus=");
		builder.append(status);
		builder.append("\nfailureReason=");
		builder.append(failureReason);
		builder.append("\nnumberOfRuns=");
		builder.append(numberOfRuns);
		builder.append("\nexecutionTime=");
		builder.append(executionTime);
		builder.append("\nexecutionTimes=");
		builder.append(executionTimes != null ? toString(executionTimes, maxLen)
				: null);
		builder.append("\naverageExecutionTime=");
		builder.append(averageExecutionTime);
		builder.append("\nrequestContent=");
		builder.append(requestContent);
		builder.append("\nrequestHeaders=");
		builder.append(requestHeaders);
		builder.append("\nresponseHeaders=");
		builder.append(responseHeaders);
		builder.append("\nrequestContentType=");
		builder.append(requestContentType);
		builder.append("\nresponseContentType=");
		builder.append(responseContentType);
		builder.append("\nresponseContent=");
		builder.append(responseContent);
		builder.append("\nresponseStatusCode=");
		builder.append(responseStatusCode);
		builder.append("\nerror=");
		builder.append(error);
		builder.append("\nerrorText=");
		builder.append(errorText);
		builder.append("\nerrors=");
		builder.append(errors != null ? toString(errors.entrySet(), maxLen)
				: null);
		builder.append("\ntestIdentifier=");
		builder.append(testIdentifier);
		builder.append("]\n");
		return builder.toString();
	}

	private String toString(Collection<?> collection, int maxLen) {
		StringBuilder builder = new StringBuilder();
		builder.append("[");
		int i = 0;
		for (Iterator<?> iterator = collection.iterator(); iterator.hasNext()
				&& i < maxLen; i++) {
			if (i > 0)
				builder.append("\n");
			builder.append(iterator.next());
		}
		builder.append("]");
		return builder.toString();
	}
	
	public static enum TestStatus {
		Success("Success"),
		Skipped("Skipped"),
		Failed("Failed");
		
		public String status;
		private TestStatus(String status) {
			this.status = status;
		}
	}
	
	public static enum TestFailureReason {
		InvalidStatusCode("InvalidStatusCode"),
		Exception("Exception"),
		NodeValidationFailed("NodeValidationFailed"),
		InvalidContentType("InvalidContentType"),
		ExecuteConditionFailed("ExecConditionFailed");
		
		public String status;
		private TestFailureReason(String status) {
			this.status = status;
		}
	}
	
	private String sanitizeString(String inp)
	{
		return inp.replace("\"", "\\\"").replace("\n", "\\\n");
	}
	
	public String toStatisticsCsv()
	{
		StringBuilder builder = new StringBuilder();
		builder.append("\"");
		if(testCase.getSourcefileName().equals(testCase.getIdentifier()))
		{
			builder.append(sanitizeString(testCase.getSourcefileName()));
		}
		else
		{
			builder.append(sanitizeString(testCase.getSourcefileName()));
			builder.append("(");
			builder.append(testCase.getIdentifier());
			builder.append(")");
		}
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(sanitizeString(testCase.getName()));
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(method);
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(actualUrl);
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(sanitizeString(testCase.getDescription()));
		builder.append("\"");
		builder.append(",");
		builder.append(executionTime);
		builder.append(",");
		builder.append(responseStatusCode);
		builder.append(",");
		builder.append("\"");
		builder.append(status);
		builder.append("\"\n");
		return builder.toString();
	}
}
 public static enum TestFailureReason {
	InvalidStatusCode("InvalidStatusCode"),
	Exception("Exception"),
	NodeValidationFailed("NodeValidationFailed"),
	InvalidContentType("InvalidContentType"),
	ExecuteConditionFailed("ExecConditionFailed");
		
	public String status;
	private TestFailureReason(String status) {
		this.status = status;
	}
}
 public static enum TestStatus {
	Success("Success"),
	Skipped("Skipped"),
	Failed("Failed");
		
	public String status;
	private TestStatus(String status) {
		this.status = status;
	}
}
 public class TestExecutionPercentile {

	private Map<String, List<Long>> executionTimes90Percentile = new HashMap<String, List<Long>>();
	
	private Map<String, List<Long>> executionTimes50Percentile = new HashMap<String, List<Long>>();
	
	protected void addExecutionTime(String identifier, Long time)
	{
		addExecutionTime(identifier, time, true);
		addExecutionTime(identifier, time, false);
	}
	
	private void addExecutionTime(String identifier, Long time, boolean is90)
	{
		Map<String, List<Long>> mapo = is90?executionTimes90Percentile:executionTimes50Percentile;
		if(!mapo.containsKey(identifier)) {
			mapo.put(identifier, new ArrayList<Long>());
		}
		List<Long> times = mapo.get(identifier);
		if(times.size()==1000)
		{
			Collections.sort(times);
			int index = Math.round((float)(is90?0.9:0.5) * times.size());
			times = times.subList(index+1, times.size());
			mapo.put(identifier, times);
		}
		else
		{
			times.add(time);
		}
	}
	
	private void deducePercentileTimes()
	{
		Map<String, List<Long>> mapo = executionTimes90Percentile;
		for (Map.Entry<String, List<Long>> entry : mapo.entrySet()) {
			List<Long> times90 = entry.getValue();
			Collections.sort(times90);
			int index = Math.round((float)0.9 * times90.size());
			long time = times90.get(index-1);
			times90.clear();
			times90.add(time);
			
			List<Long> times50 = executionTimes50Percentile.get(entry.getKey());
			Collections.sort(times50);
			index = Math.round((float)0.5 * times50.size());
			time = times50.get(index-1);
			times50.clear();
			
			times90.add(time);
			mapo.put(entry.getKey(), times90);
		}
	}
	
	public Map<String, List<Long>> getPercentileTimes()
	{
		deducePercentileTimes();
		return executionTimes90Percentile;
	}
	
	public void mergePercentileTimes(Map<String, List<Long>> times)
	{
		for (Map.Entry<String, List<Long>> entry : times.entrySet()) {
			List<Long> times90 = executionTimes90Percentile.get(entry.getKey());
			List<Long> times50 = executionTimes50Percentile.get(entry.getKey());
			
			if(times90!=null)
			{
				times90.add(entry.getValue().get(0));
				times50.add(entry.getValue().get(1));
			}
			else
			{
				executionTimes90Percentile.put(entry.getKey(), new ArrayList<Long>());
				executionTimes90Percentile.get(entry.getKey()).add(entry.getValue().get(0));
				executionTimes50Percentile.put(entry.getKey(), new ArrayList<Long>());
				executionTimes50Percentile.get(entry.getKey()).add(entry.getValue().get(1));
			}
		}
	}
}
 @JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class TestSuiteStats implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private Integer totalTestCount = 0;
	
	private Integer failedTestCount = 0;
	
	private Integer skippedTestCount = 0;
	
	private Long executionTime = 0L;
	
	private Long actualExecutionTime = 0L;
	
	private Integer totalRuns = 0;
	
	private Integer failedRuns = 0;
	
	private Integer totalSuiteRuns = 0;
	
	private Integer totalUserSuiteRuns = 0;
	
	private List<TestGroupStats> groupStats = new ArrayList<TestGroupStats>();
	
	private List<Object[]> exStats = new ArrayList<Object[]>();

	public Integer getTotalTestCount() {
		return totalTestCount;
	}

	public void setTotalTestCount(Integer totalTestCount) {
		this.totalTestCount = totalTestCount;
	}

	public Integer getFailedTestCount() {
		return failedTestCount;
	}

	public void setFailedTestCount(Integer failedTestCount) {
		this.failedTestCount = failedTestCount;
	}

	public List<TestGroupStats> getGroupStats() {
		return groupStats;
	}

	public void setGroupStats(List<TestGroupStats> groupStats) {
		this.groupStats = groupStats;
	}

	public Long getExecutionTime() {
		return executionTime;
	}

	public void setExecutionTime(Long executionTime) {
		this.executionTime = executionTime;
	}

	public Long getActualExecutionTime() {
		return actualExecutionTime;
	}

	public void setActualExecutionTime(Long actualExecutionTime) {
		this.actualExecutionTime = actualExecutionTime;
	}

	public Integer getTotalRuns() {
		return totalRuns;
	}

	public void setTotalRuns(Integer totalRuns) {
		this.totalRuns = totalRuns;
	}

	public Integer getFailedRuns() {
		return failedRuns;
	}

	public void setFailedRuns(Integer failedRuns) {
		this.failedRuns = failedRuns;
	}
	
	public Integer getTotalSuiteRuns() {
		return totalSuiteRuns;
	}

	public void setTotalSuiteRuns(Integer totalSuiteRuns) {
		this.totalSuiteRuns = totalSuiteRuns;
	}

	public Integer getSkippedTestCount() {
		return skippedTestCount;
	}

	public void setSkippedTestCount(Integer skippedTestCount) {
		this.skippedTestCount = skippedTestCount;
	}

	public Integer getTotalUserSuiteRuns() {
		return totalUserSuiteRuns;
	}

	public void setTotalUserSuiteRuns(Integer totalUserSuiteRuns) {
		this.totalUserSuiteRuns = totalUserSuiteRuns;
	}

	public List<Object[]> getExStats() {
		return exStats;
	}

	public void setExStats(List<Object[]> exStats) {
		this.exStats = exStats;
	}

	public void updateStats(TestSuiteStats stats, boolean isUserRuns) {
		setActualExecutionTime(getActualExecutionTime() + stats.getActualExecutionTime());
		setFailedRuns(getFailedRuns() +  stats.getFailedRuns());
		setFailedTestCount(getFailedTestCount() + stats.getFailedTestCount());
		setTotalRuns(getTotalRuns() + stats.getTotalRuns());
		setTotalTestCount(getTotalTestCount() + stats.getTotalTestCount());
		if(!isUserRuns)
			setTotalSuiteRuns(getTotalSuiteRuns() + stats.getTotalSuiteRuns());
		setSkippedTestCount(getSkippedTestCount() + stats.getSkippedTestCount());
		setExecutionTime(getExecutionTime() + stats.getExecutionTime());
		//getExStats()
	}
	
	public void copy(TestSuiteStats stats) {
		totalTestCount = stats.getTotalTestCount();
		failedTestCount = stats.getFailedTestCount();
		skippedTestCount = stats.getSkippedTestCount();
		executionTime = stats.getExecutionTime();
		actualExecutionTime = stats.getActualExecutionTime();
		totalRuns = stats.getTotalRuns();
		failedRuns = stats.getFailedRuns();
		totalSuiteRuns = stats.getTotalSuiteRuns();
		totalUserSuiteRuns = stats.getTotalUserSuiteRuns();
		groupStats = stats.getGroupStats();
	}

	@Override
	public String toString() {
		final int maxLen = 10;
		StringBuilder builder = new StringBuilder();
		builder.append("TestSuiteStats [totalTestCount=");
		builder.append(totalTestCount);
		builder.append(", TotalUserSuiteRuns=");
		builder.append(totalUserSuiteRuns);
		builder.append(", failedTestCount=");
		builder.append(failedTestCount);
		builder.append(", skippedTestCount=");
		builder.append(skippedTestCount);
		builder.append(", executionTime=");
		builder.append(executionTime);
		builder.append(", actualExecutionTime=");
		builder.append(actualExecutionTime);
		builder.append(", totalRuns=");
		builder.append(totalRuns);
		builder.append(", failedRuns=");
		builder.append(failedRuns);
		builder.append(", groupStats=");
		builder.append(groupStats != null ? groupStats.subList(0,
				Math.min(groupStats.size(), maxLen)) : null);
		builder.append("]");
		return builder.toString();
	}
	
	public String show() {
		StringBuilder builder = new StringBuilder();
		builder.append("Result:\nTotalSuiteRuns=");
		builder.append(totalSuiteRuns);
		builder.append(", TotalUserSuiteRuns=");
		builder.append(totalUserSuiteRuns);
		builder.append(", TotalTestCount=");
		builder.append(totalTestCount);
		builder.append(", FailedTestCount=");
		builder.append(failedTestCount);
		builder.append(", SkippedTestCount=");
		builder.append(skippedTestCount);
		builder.append(", ExecutionTime=");
		builder.append(executionTime);
		builder.append(", ActualExecutionTime=");
		builder.append(actualExecutionTime);
		builder.append(", TotalRuns=");
		builder.append(totalRuns);
		builder.append(", FailedRuns=");
		builder.append(failedRuns);
		builder.append("\n");
		return builder.toString();
	}
	
	@SuppressWarnings("serial")
	public List<Object> toList() {
		return new ArrayList<Object>() {{
			add(totalTestCount);
			add(failedTestCount);
			add(skippedTestCount);
			add(executionTime);
		}};
	}
}
 public class ClassLoaderUtils {

	/**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     * 
     * @param packageName The base package
     * @return The classes
     * @throws ClassNotFoundException
     * @throws IOException
     */
    @SuppressWarnings({ "rawtypes" })
    public static List<Class> getClasses(String packageName) throws ClassNotFoundException, IOException
    {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements())
        {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs)
        {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes;
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     * 
     * @param directory The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
    @SuppressWarnings({ "rawtypes" })
    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException
    {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists())
        {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files)
        {
            if (file.isDirectory())
            {
                assert !file.getName().contains(".");
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            }
            else if (file.getName().endsWith(".class"))
            {
                Class claz = Thread.currentThread().getContextClassLoader()
                        .loadClass(packageName + '.' + file.getName().substring(0, file.getName().length() - 6));
                if (claz != null)
                {
                    classes.add(claz);
                }
                else
                {
                    classes.add(Class.forName(packageName + '.'
                            + file.getName().substring(0, file.getName().length() - 6)));
                }
            }
        }
        return classes;
    }

}
 @SuppressWarnings("serial")
public static class GatfSelCodeParseError extends RuntimeException {
    public GatfSelCodeParseError(String message) {
        super(message);
    }
    public GatfSelCodeParseError(String message, Throwable e) {
        super(message, e);
    }
}
 public class SeleniumCodeGeneratorAndUtil {
	
	static URLClassLoader classLoader = null;
	
	public static void clean() throws Exception {
	    File gcdir = new File(FileUtils.getTempDirectory(), "gatf-code");
        if(gcdir.exists()) {
            FileUtils.deleteDirectory(gcdir);
        }
        
        File dir = new File(FileUtils.getTempDirectory(), "gatf-code/com/gatf/selenium/");
        dir.mkdirs();
	}
	
	@SuppressWarnings("unchecked")
	public static SeleniumTest getSeleniumTest(String fileName, ClassLoader loader, AcceptanceTestContext context, Object[] retvals, GatfExecutorConfig config, boolean isLog, Map<Integer, Object[]> selToJavaLineMap) throws Exception
	{
	    List<String> commands = new ArrayList<String>();
		Command cmd = Command.read(context.getResourceFile(fileName), commands, context);
		String sourceCode =  cmd.javacode();
		
		String gatfJarPath = "gatf-alldep-jar.jar";
        try {
        	gatfJarPath = SeleniumCodeGeneratorAndUtil.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
		} catch (Throwable e) {
		}
        
        if(StringUtils.isNotBlank(config.getGatfJarPath())) {
        	gatfJarPath = config.getGatfJarPath().trim();
        }
		
		if(isLog) {
			String jc = cmd.javacode();
			System.out.println(jc);
	        System.out.println(new Formatter().formatSource(jc));
		}
		
		retvals[0] = fileName;
		retvals[1] = StringUtils.join(commands, '\n');
		retvals[3] = sourceCode;
		
		if(selToJavaLineMap!=null) {
			final AtomicInteger cl = new AtomicInteger(1);
			String sellineident = "/*GATF_ST_LINE@";
			sourceCode.lines().forEach(line -> {
				if(line.trim().startsWith(sellineident)) {
					boolean addOne = line.indexOf("_*/")!=-1;
					int indx = addOne?line.indexOf("_*/"):line.indexOf("*/");
					String fld = line.substring(line.indexOf(sellineident)+sellineident.length(), indx);
					String sflnm = fld.substring(0, fld.indexOf(":"));
					int sln = Integer.parseInt(fld.substring(fld.indexOf(":")+1));
					selToJavaLineMap.put(sln, new Object[] {sflnm, cl.get() + (addOne?1:0)});
				} else if(line.trim().startsWith("/*GATF_ST_START_*/")) {
					selToJavaLineMap.put(0, new Object[] {null, cl.get()});
				}
				cl.incrementAndGet();
			});
		}
	    
        File gcdir = new File(FileUtils.getTempDirectory(), "gatf-code");
        File dir = new File(FileUtils.getTempDirectory(), "gatf-code/com/gatf/selenium/");
        
        File srcfile = new File(dir, cmd.getClassName()+".java");
        retvals[2] = srcfile.getAbsolutePath();
        FileUtils.writeStringToFile(srcfile, sourceCode, "UTF-8");
        
        if(retvals.length==5) retvals[4] += "";
        
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if(compiler!=null) {
        	List<String> optionList = new ArrayList<String>();
            optionList.add("-g");
            optionList.add("-classpath");
            
            if(loader instanceof URLClassLoader) {
            	URL[] urls = ((URLClassLoader)loader).getURLs();
            	String cp = "";
            	for (URL url : urls) {
            		cp += url.getPath() + File.separator;
    			}
            	optionList.add(cp);
            } else {
            	optionList.add(gatfJarPath);
            }
        	
        	DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
	        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
	        Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(srcfile));
	        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, optionList, null, compilationUnit);
	        if (task.call()) {
	        	boolean errCd = false;
	        	for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
	        		if(diagnostic.getKind()==Kind.ERROR) {
	        			errCd = true;
	        			if(retvals.length==5) {
	        				retvals[4] += diagnostic.toString();
	        			}
	        			System.out.format("Error on line %d in %s%n", diagnostic.getLineNumber(), diagnostic.getSource().toUri());
	        		}
	                System.out.println(diagnostic.toString());
	            }
	        	
	        	if(errCd) {
	        		return null;
	        	}
	        	
	        	URL[] urls = new URL[1];
	            urls[0] = gcdir.toURI().toURL();
	            if(classLoader==null) {
	            	classLoader = new URLClassLoader(urls, loader);
	            }
	            Class<SeleniumTest> loadedClass = (Class<SeleniumTest>)classLoader.loadClass("com.gatf.selenium." + cmd.getClassName());
	            return loadedClass.getConstructor(new Class[]{AcceptanceTestContext.class, int.class}).newInstance(new Object[]{context, 1});
	        }
        }
        
        boolean isWindows = SystemUtils.IS_OS_WINDOWS;
        
        if(StringUtils.isBlank(config.getJavaHome())) {
        	throw new RuntimeException("Please provide a jdk home path in the gatf-config configuration file (javaHome)");
        }
        
        List<String> builderList = new ArrayList<>();
        String javacPath = Paths.get(config.getJavaHome(), "bin", "javac").toString();
        builderList.add((isWindows?"\"":"") + javacPath + (isWindows?"\"":""));
        builderList.add("-g");
        builderList.add("-classpath");
        builderList.add((isWindows?"\"":"") + gatfJarPath + (isWindows?"\"":""));
        builderList.add((isWindows?"\"":"") + retvals[2].toString() + (isWindows?"\"":""));
        
        ProcessBuilder pb = new ProcessBuilder(builderList);
        pb.redirectErrorStream(true);
        Process process = pb.start();
        BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream())); 

        boolean errd = false;
        String err = null;
        while((err = inStreamReader.readLine()) != null) {
            errd |= err.indexOf("error: ")!=-1;
            if(retvals.length==5 && err.indexOf("error: ")!=-1) {
				retvals[4] += err;
			}
            System.out.println(err);
        }
        
        if(errd) {
            return null;
        } else {
            URL[] urls = new URL[1];
            urls[0] = gcdir.toURI().toURL();
            if(classLoader==null) {
                classLoader = new URLClassLoader(urls, loader);
            }
            Class<SeleniumTest> loadedClass = (Class<SeleniumTest>)Class.forName("com.gatf.selenium." + cmd.getClassName(), true, classLoader);
            return loadedClass.getConstructor(new Class[]{AcceptanceTestContext.class, int.class}).newInstance(new Object[]{context, 1});
        }
	}
	
	public static File zipSeleniumTests() {
		File gcdir = new File(FileUtils.getTempDirectory(), "gatf-code");
		String fileName = UUID.randomUUID().toString()+".zip";
		File zipFile = new File(System.getProperty("java.io.tmpdir"), fileName);
		try {
			new ZipFile(zipFile).addFolder(gcdir);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return zipFile;
	}
	
	public static LoggingPreferences getLp(GatfExecutorConfig configuration) {
		LoggingPreferences lp = new LoggingPreferences();
		if(StringUtils.isNotEmpty(configuration.getSeleniumLoggerPreferences()))
		{
			String slp = configuration.getSeleniumLoggerPreferences().toLowerCase();
			if(slp.matches(".*browser\\(([a-zA-Z]+)\\).*")) {
				Matcher m = Pattern.compile(".*browser\\(([a-zA-Z]+)\\).*").matcher(slp);
				m.find();
				if(SeleniumTest.LOG_LEVEL_BY_NAME_MAP.containsKey(m.group(1)) && m.group(1).equalsIgnoreCase("off")
				        && SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1))!=null) {
					lp.enable(LogType.BROWSER, SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1)));
				}
			}
			if(slp.matches(".*client\\(([a-zA-Z]+)\\).*")) {
				Matcher m = Pattern.compile(".*client\\(([a-zA-Z]+)\\).*").matcher(slp);
				m.find();
				if(SeleniumTest.LOG_LEVEL_BY_NAME_MAP.containsKey(m.group(1)) && m.group(1).equalsIgnoreCase("off")
                        && SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1))!=null) {
					lp.enable(LogType.CLIENT, SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1)));
				}
			}
			if(slp.matches(".*driver\\(([a-zA-Z]+)\\).*")) {
				Matcher m = Pattern.compile(".*driver\\(([a-zA-Z]+)\\).*").matcher(slp);
				m.find();
				if(SeleniumTest.LOG_LEVEL_BY_NAME_MAP.containsKey(m.group(1)) && m.group(1).equalsIgnoreCase("off")
                        && SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1))!=null) {
					lp.enable(LogType.DRIVER, SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1)));
				}
			}
			if(slp.matches(".*performance\\(([a-zA-Z]+)\\).*")) {
				Matcher m = Pattern.compile(".*performance\\(([a-zA-Z]+)\\).*").matcher(slp);
				m.find();
				if(SeleniumTest.LOG_LEVEL_BY_NAME_MAP.containsKey(m.group(1)) && m.group(1).equalsIgnoreCase("off")
                        && SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1))!=null) {
					lp.enable(LogType.PERFORMANCE, SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1)));
				}
			}
			if(slp.matches(".*profiler\\(([a-zA-Z]+)\\).*")) {
				Matcher m = Pattern.compile(".*profiler\\(([a-zA-Z]+)\\).*").matcher(slp);
				m.find();
				if(SeleniumTest.LOG_LEVEL_BY_NAME_MAP.containsKey(m.group(1)) && m.group(1).equalsIgnoreCase("off")
                        && SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1))!=null) {
					lp.enable(LogType.PROFILER, SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1)));
				}
			}
			if(slp.matches(".*server\\(([a-zA-Z]+)\\).*")) {
				Matcher m = Pattern.compile(".*server\\(([a-zA-Z]+)\\).*").matcher(slp);
				m.find();
				if(SeleniumTest.LOG_LEVEL_BY_NAME_MAP.containsKey(m.group(1)) && m.group(1).equalsIgnoreCase("off")
                        && SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1))!=null) {
					lp.enable(LogType.SERVER, SeleniumTest.LOG_LEVEL_BY_NAME_MAP.get(m.group(1)));
				}
			}
		}
		return lp;
	}
}
 @XStreamAlias("seleniumDriverConfig")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class SeleniumDriverConfig implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    
    private String version;
    
    private String platform;
    
    private String driverName;

    private String path;

    private String arguments;

    private String url;

    @XStreamConverter(value=MapKeyValueAttributeXstreamConverter.class)
    private Map<String, String> capabilities = new HashMap<String, String>();

    @XStreamConverter(value=MapKeyValueAttributeXstreamConverter.class)
    private Map<String, String> properties = new HashMap<String, String>();  

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getVersion()
    {
        return version;
    }

    public void setVersion(String version)
    {
        this.version = version;
    }

    public String getPlatform()
    {
        return platform;
    }

    public void setPlatform(String platform)
    {
        this.platform = platform;
    }

    public String getDriverName()
    {
        return driverName;
    }

    public void setDriverName(String driverName)
    {
        this.driverName = driverName;
    }

    public String getPath()
    {
        return path;
    }

    public void setPath(String path)
    {
        this.path = path;
    }

    public String getArguments() {
		return arguments;
	}

	public void setArguments(String arguments) {
		this.arguments = arguments;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public Map<String, String> getCapabilities()
    {
        return capabilities;
    }

    public void setCapabilities(Map<String, String> capabilities)
    {
        this.capabilities = capabilities;
    }

    public Map<String,String> getProperties()
    {
        return properties;
    }

    public void setProperties(Map<String,String> properties)
    {
        this.properties = properties;
    }
} public abstract class SeleniumTest {
	@SuppressWarnings("serial")
	protected final static Map<String, Level> LOG_LEVEL_BY_NAME_MAP = new HashMap<String, Level>(){{
		put(Level.ALL.getName().toLowerCase(), Level.ALL);
		put(Level.CONFIG.getName().toLowerCase(), Level.CONFIG);
		put(Level.FINE.getName().toLowerCase(), Level.FINE);
		put(Level.FINER.getName().toLowerCase(), Level.FINER);
		put(Level.FINEST.getName().toLowerCase(), Level.FINEST);
		put(Level.INFO.getName().toLowerCase(), Level.INFO);
		put(Level.OFF.getName().toLowerCase(), Level.OFF);
		put(Level.SEVERE.getName().toLowerCase(), Level.SEVERE);
		put(Level.WARNING.getName().toLowerCase(), Level.WARNING);
	}};
	@SuppressWarnings("serial")
	protected final static HashSet<String> LOG_TYPES_SET = new HashSet<String>() {{
		add(LogType.BROWSER);
		add(LogType.CLIENT);
		add(LogType.DRIVER);
		add(LogType.PERFORMANCE);
		add(LogType.PROFILER);
		add(LogType.SERVER);
	}};

	private static final String TOP_LEVEL_PROV_NAME = UUID.randomUUID().toString();

	private transient AcceptanceTestContext ___cxt___ = null;

	List<SeleniumTestSession> sessions = new ArrayList<SeleniumTestSession>();

	protected String name;

	protected transient int sessionNum = -1;

	protected int index;

	protected void nextSession() {
		if(sessions.size()>sessionNum+1) {
			sessionNum++;
		}
	}

	protected void setSession(String sns, int sn, boolean startFlag) {
		if(sn>=0 && sessions.size()>sn) {
			sessionNum = sn;
		} else if(sns!=null) {
			sn = 0;
			for (SeleniumTestSession s : sessions) {
				if(s.sessionName.equalsIgnoreCase(sns)) {
					sessionNum = sn; 
				}
				sn++;
			}
		} else if(sn==-1 && sns==null) {
			sessionNum = 0;
		}
		if(startFlag) {
			startTest();
		}
	}

	@SuppressWarnings("serial")
	protected Set<String> addTest(String sessionName, String browserName) {
		final SeleniumTestSession s = new SeleniumTestSession();
		s.sessionName = sessionName;
		s.browserName = browserName;
		sessions.add(s);
		if(!s.__result__.containsKey(browserName)) {
			SeleniumResult r = new SeleniumResult();
			r.browserName = browserName;
			s.__result__.put(browserName, r);
		} else {
			//throw new RuntimeException("Duplicate browser defined");
		}
		return new HashSet<String>(){{add(s.sessionName); add((sessions.size()-1)+"");}};
	}

	protected void startTest() {
		if(getSession().__teststarttime__==0) {
			getSession().__teststarttime__ = System.nanoTime();
		}
	}

	public void pushResult(SeleniumTestResult result)
	{
		if(getSession().__subtestname__==null) {
			result.executionTime = System.nanoTime() - getSession().__teststarttime__;
			getSession().__result__.get(getSession().browserName).result = result;
		} else {
			getSession().__result__.get(getSession().browserName).__cresult__.put(getSession().__subtestname__, result);
			RuntimeReportUtil.addEntry(index, result.status);
		}
	}

	protected void newTopLevelProvider() {
		if(!getSession().providerTestDataMap.containsKey(TOP_LEVEL_PROV_NAME)) {
			getSession().providerTestDataMap.put(TOP_LEVEL_PROV_NAME, new ArrayList<Map<String,String>>());
			getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).add(new HashMap<String, String>());
		}
	}

	protected void newProvider(String name) {
		if(!getSession().providerTestDataMap.containsKey(name)) {
			getSession().providerTestDataMap.put(name, new ArrayList<Map<String,String>>());
		}
	}

	protected void addToTopLevelProviderTestDataMap(String name, String value) {
		getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0).put(name, value);
	}

	protected List<Map<String, String>> getProviderTestDataMap(String name) {
		return getAllProviderData(getPn(name));
	}

	protected String getFileProviderHash(String name) {
		return getFileProviderHash(name);
	}

	private SeleniumTestSession getSession() {
		return sessions.get(sessionNum);
	}

	protected boolean matchesSessionId(String sname, int sessionId) {
		if(sname==null && sessionId==-1)return true;
		if(StringUtils.isBlank(sname)) {
			return sessionNum==sessionId;
		} else {
			int sn = 0;
			for (SeleniumTestSession s : sessions) {
				if(s.sessionName.equalsIgnoreCase(sname)) {
					return sessionNum==sn; 
				}
				sn++;
			}
		}
		return false;
	}

	private List<Map<String, String>> getAllProviderData(String pn) {
		List<Map<String, String>> ep = pn!=null?___cxt___.getAnyProviderData(pn, null):null;
		List<Map<String, String>> tp = pn!=null?getSession().providerTestDataMap.get(pn):null;
		List<Map<String, String>> fp = null;
		if((ep!=null && tp!=null) || tp!=null) {
			fp = tp;
		} else if(ep!=null) {
			fp = ep;
		}
		return fp;
	}

	private Map<String, Object> getFinalDataMap(String pn, Integer pp) {
		Map<String, Object> _mt = new HashMap<String, Object>();
		List<Map<String, String>> fp = getAllProviderData(pn);
		_mt.putAll(___cxt___.getWorkflowContextHandler().getGlobalSuiteAndTestLevelParameters(null, CollectionUtils.isEmpty(fp)?null:fp.get(pp), index));
		if(getSession().providerTestDataMap.containsKey(TOP_LEVEL_PROV_NAME) && getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0)!=null) {
			_mt.putAll(getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0));
		}
		return _mt;
	}

	static Pattern p = Pattern.compile("\\$(p|P)\\{([\"'a-zA-Z0-9_\\.]+)\\.([0-9]+)\\.([a-zA-Z0-9_\\.]+)\\}");

	/*
	 * Three types of template variables can be evaluated
	 * 1. ${var-name} - a value inside a provider slot or a top-level variable
	 * 2. $v{var-name} - a current context java variable
	 * 3. $p{prov-name.position.var-name} - a direct reference to a variable in provider at a positional map of values
	 */
	protected String evaluate(String tmpl) {
		if(tmpl.indexOf("$")==-1)return tmpl;
		try
		{
			if(getSession().__provdetails__.size()>0) {
				ArrayList<String> keys = new ArrayList<String>(getSession().__provdetails__.keySet());
				for (int i=keys.size()-1;i>=0;i--)
				{
					String pn = keys.get(i);
					Integer pp = getSession().__provdetails__.get(pn);
					Map<String, Object> _mt = getFinalDataMap(pn, pp);
					if(tmpl.indexOf("$v{")!=-1) {
						tmpl = tmpl.replace("$v", "$");
						_mt.putAll(getSession().__vars__);
					}
					tmpl = ___cxt___.getWorkflowContextHandler().templatize(_mt, tmpl);
				}
			} else {
				Map<String, Object> _mt = getFinalDataMap(null, null);
				if(tmpl.indexOf("$v{")!=-1) {
					tmpl = tmpl.replace("$v", "$");
					_mt.putAll(getSession().__vars__);
				}
				tmpl = ___cxt___.getWorkflowContextHandler().templatize(_mt, tmpl);
			}
			String tmpln = tmpl;
			Matcher m = p.matcher(tmpl);
			Map<String, Object> _mt = new HashMap<String, Object>();
			boolean foundProvRef = false;
			while(m.find()) {
				String[] parts = m.group(4).split("\\.");
				if(parts.length>1) {
					String ivp = parts[0];
					for (int i = 1; i < parts.length; i++) {
						ivp += "." + parts[i];
					}
					String provName = m.group(2);
					if((provName.startsWith("\"") && provName.endsWith("\"")) || (provName.startsWith("'") && provName.endsWith("'"))) {
						provName = provName.substring(1, provName.length()-1);
					}
					if(getSession().providerTestDataMap.containsKey(provName)) {
						foundProvRef = true;
						tmpln = tmpln.replace(tmpl.substring(m.start(), m.end()), "${"+ivp+"}");
						_mt.putAll(getSession().providerTestDataMap.get(provName).get(Integer.valueOf(m.group(3))));
					}
				}
			}
			if(foundProvRef) {
				tmpl = ___cxt___.getWorkflowContextHandler().templatize(_mt, tmpln);
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return tmpl;
	}

	protected String getProviderDataValue(String key, boolean isVar) {
		if(isVar) {
			Object o = ___get_var__(key);
			if(o!=null && o instanceof String) {
				return o.toString();
			}
			return null;
		}
		if(getSession().providerTestDataMap.containsKey(TOP_LEVEL_PROV_NAME) && getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0).containsKey(key)) {
			return getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0).get(key);
		}
		if(getSession().__provdetails__.size()>0) {
			ArrayList<String> keys = new ArrayList<String>(getSession().__provdetails__.keySet());
			for (int i=keys.size()-1;i>=0;i--)
			{
				String pn = keys.get(i);
				Integer pp = getSession().__provdetails__.get(pn);
				List<Map<String, String>> _t = getAllProviderData(pn);
				if(_t!=null && _t.get(pp)!=null) {
					Map<String, String> _mt = ___cxt___.getWorkflowContextHandler().getGlobalSuiteAndTestLevelParameters(null, _t.get(pp), index);
					if(_mt.containsKey(key)) {
						return _mt.get(key);
					}
				}
			}
			return null;
		} else {
			Map<String, String> _mt = ___cxt___.getWorkflowContextHandler().getGlobalSuiteAndTestLevelParameters(null, null, index);
			return _mt.get(key);
		}
	}

	protected Object getProviderDataValueO(String key, boolean isVar) {
		if(isVar) {
			return ___get_var__(key);
		}
		if(getSession().providerTestDataMap.containsKey(TOP_LEVEL_PROV_NAME) && getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0).containsKey(key)) {
			return getSession().providerTestDataMap.get(TOP_LEVEL_PROV_NAME).get(0).get(key);
		}
		if(getSession().__provdetails__.size()>0) {
			ArrayList<String> keys = new ArrayList<String>(getSession().__provdetails__.keySet());
			for (int i=keys.size()-1;i>=0;i--)
			{
				String pn = keys.get(i);
				Integer pp = getSession().__provdetails__.get(pn);
				List<Map<String, String>> _t = getAllProviderData(pn);
				if(_t!=null && _t.get(pp)!=null) {
					Map<String, String> _mt = ___cxt___.getWorkflowContextHandler().getGlobalSuiteAndTestLevelParameters(null, _t.get(pp), index);
					if(_mt.containsKey(key)) {
						return _mt.get(key);
					}
				}
			}
			return null;
		} else {
			Map<String, String> _mt = ___cxt___.getWorkflowContextHandler().getGlobalSuiteAndTestLevelParameters(null, null, index);
			return _mt.get(key);
		}
	}

	protected void ___add_var__(String name, Object val) {
		if(getSession().__vars__.containsKey(name)) {
			throw new RuntimeException("Variable " + name + " redefined");
		}
		getSession().__vars__.put(name, val);
	}

	protected Object ___get_var__(String name) {
		if(!getSession().__vars__.containsKey(name)) {
			throw new RuntimeException("Variable " + name + " not defined");
		}
		return getSession().__vars__.get(name);
	}

	protected void addSubTest(String browserName, String stname) {
		if(getSession().__result__.containsKey(browserName)) {
			if(!getSession().__result__.get(getSession().browserName).__cresult__.containsKey(stname)) {
				getSession().__result__.get(browserName).__cresult__.put(stname, null);
			} else {
				throw new RuntimeException("Duplicate subtest defined");
			}
		} else {
			throw new RuntimeException("Invalid browser specified");
		}
	}

	protected WebDriver get___d___()
	{
		if(getSession().___d___.size()==0)return null;
		return getSession().___d___.get(getSession().__wpos__);
	}

	protected void set___d___(WebDriver ___d___)
	{
		___d___.manage().timeouts().pageLoadTimeout(100, TimeUnit.SECONDS);
		getSession().___d___.add(___d___);
	}

	protected class PrettyPrintingMap<K, V> {
		private Map<K, V> map;

		public PrettyPrintingMap(Map<K, V> map) {
			this.map = map;
		}

		public String toString() {
			StringBuilder sb = new StringBuilder();
			Iterator<Entry<K, V>> iter = map.entrySet().iterator();
			while (iter.hasNext()) {
				Entry<K, V> entry = iter.next();
				sb.append(entry.getKey());
				sb.append('=').append('"');
				sb.append(entry.getValue());
				sb.append('"');
				if (iter.hasNext()) {
					sb.append(',').append(' ');
				}
			}
			return sb.toString();

		}
	}

	private String getPn(String name) {
		String pn = name;
		if(index>0) {
			pn += index;
		}
		return pn;
	}

	protected void ___cxt___add_param__(String name, Object value)
	{
		___cxt___.getWorkflowContextHandler().addSuiteLevelParameter(index, name, value==null?null:value.toString());
	}

	protected void ___cxt___print_provider__(String name)
	{
		System.out.println(getAllProviderData(getPn(name)));
	}

	protected void ___cxt___print_provider__json(String name)
	{
		try
		{
			System.out.println(WorkflowContextHandler.OM.writerWithDefaultPrettyPrinter().writeValueAsString(getAllProviderData(getPn(name))));
		}
		catch (Exception e)
		{
		}
	}

	protected AcceptanceTestContext get___cxt___()
	{
		return ___cxt___;
	}

	protected void set___cxt___(AcceptanceTestContext ___cxt___)
	{
		this.___cxt___ = ___cxt___;
	}

	public List<SeleniumTestSession> get__sessions__()
	{
		return sessions;
	}

	protected void set__provname__(String __provname__)
	{
		getSession().__provdetails__.put(getPn(__provname__), -1);
	}

	protected void set__provpos__(String __provname__, int __provpos__)
	{
		getSession().__provdetails__.put(getPn(__provname__), __provpos__);
	}

	protected void rem__provname__(String __provname__)
	{
		getSession().__provdetails__.remove(getPn(__provname__));
	}

	protected void set__subtestname__(String __subtestname__)
	{
		if(__subtestname__!=null) {
			getSession().__subtestexecutiontime__ = System.nanoTime();
			if(getSession().__provdetails__.size()>0) {
				String fstn = "";
				ArrayList<String> keys = new ArrayList<String>(getSession().__provdetails__.keySet());
				for (int i=keys.size()-1;i>=0;i--)
				{
					String pn = keys.get(i);
					Integer pp = getSession().__provdetails__.get(pn);
					fstn += pn + "(" + pp + ") ";
				}
				__subtestname__ = fstn + __subtestname__;
			}
		} else {
			getSession().__subtestexecutiontime__ = System.nanoTime() - getSession().__subtestexecutiontime__;
			if(getSession().__subtestname__!=null) {
				getSession().__result__.get(getSession().browserName).__cresult__.get(getSession().__subtestname__).executionTime = getSession().__subtestexecutiontime__;
			}
		}
		getSession().__subtestname__ = __subtestname__;
	}

	protected WebDriver getWebDriver() {
		return getSession().___d___.get(getSession().__wpos__);
	}

	@SuppressWarnings("rawtypes")
	protected AndroidDriver getAndroidDriver() {
		if(getSession().___d___ instanceof AndroidDriver) {
			return (AndroidDriver)getSession().___d___;
		}
		return null;
	}

	@SuppressWarnings("rawtypes")
	protected IOSDriver getIOSDriver() {
		if(getSession().___d___ instanceof IOSDriver) {
			return (IOSDriver)getSession().___d___;
		}
		return null;
	}

	protected SelendroidDriver getSelendroidDriver() {
		if(getSession().___d___ instanceof SelendroidDriver) {
			return (SelendroidDriver)getSession().___d___;
		}
		return null;
	}

	public SeleniumTest(String name, AcceptanceTestContext ___cxt___, int index) {
		this.name = name;
		this.___cxt___ = ___cxt___;
		this.index = index;
		//this.properties = ___cxt___.getGatfExecutorConfig().getSelDriverConfigMap().get(name).getProperties();
	}

	public String getName() {
		return name;
	}

	public static interface Functor<I, O> {
		O f(I i, I j);
	}

	public static interface CondFunc {
		Integer f(Object[] args);
	}

	public void quit() {
		if(getSession().___d___.size()>0) {
			for (WebDriver d : getSession().___d___)
			{
				d.quit();
			}
		}
	}

	public void quitAll() {
		for (SeleniumTestSession s : sessions) {
			if(s.___d___.size()>0) {
				for (WebDriver d : s.___d___)
				{
					d.quit();
				}
			}
		}
	}

	public abstract void close();

	public abstract SeleniumTest copy(AcceptanceTestContext ctx, int index);

	public abstract List<SeleniumTestSession> execute(LoggingPreferences ___lp___) throws Exception;

	/*protected boolean checkifSessionIdExistsInSet(Set<String> st, String sname, String sid) {
        if(sname==null && sid==null)return true;
        if(sname!=null && st.contains(sname))return true;
        if(sid!=null && st.contains(sid))return true;
        return false;
    }*/

	public static class SeleniumTestResult implements Serializable {
		private static final long serialVersionUID = 1L;

		private Map<String, SerializableLogEntries> logs = new HashMap<String, SerializableLogEntries>();;

		private boolean status;

		long executionTime;

		private Map<String, Object[]> internalTestRes = new HashMap<String, Object[]>();

		public Map<String,SerializableLogEntries> getLogs()
		{
			return logs;
		}
		public boolean isStatus()
		{
			return status;
		}
		public long getExecutionTime()
		{
			return executionTime;
		}
		public Map<String,Object[]> getInternalTestRes()
		{
			return internalTestRes;
		}
		public SeleniumTestResult(WebDriver d, SeleniumTest test, LoggingPreferences ___lp___)
		{
			this.status = true;
			this.internalTestRes = test.getSession().internalTestRs;
			/*Logs logs = d.manage().logs();
            for (String s : LOG_TYPES_SET) {
                if(!logs.getAvailableLogTypes().contains(s))continue;
                LogEntries logEntries = logs.get(s);
                if(logEntries!=null && !logEntries.getAll().isEmpty()) {
                    this.logs.put(s, new SerializableLogEntries(logEntries.getAll())); 
                }
            }*/
		}
		public SeleniumTestResult(WebDriver d, SeleniumTest test, Throwable cause, LoggingPreferences ___lp___) {
			this.status = false;
			this.internalTestRes = test.getSession().internalTestRs;
			/*Logs logs = d.manage().logs();
            for (String s : LOG_TYPES_SET) {
                if(!logs.getAvailableLogTypes().contains(s))continue;
                LogEntries logEntries = logs.get(s);
                if(logEntries!=null && !logEntries.getAll().isEmpty()) {
                    this.logs.put(s, new SerializableLogEntries(logEntries.getAll())); 
                }
            }*/
			List<LogEntry> entries = new ArrayList<LogEntry>();
			entries.add(new LogEntry(Level.ALL, new Date().getTime(), cause.getMessage()));
			entries.add(new LogEntry(Level.ALL, new Date().getTime(), ExceptionUtils.getStackTrace(cause)));
			this.logs.put("gatf", new SerializableLogEntries(entries));
		}
		public SeleniumTestResult(SeleniumTest test, Throwable cause) {
			this.status = false;
			this.internalTestRes = test.getSession().internalTestRs;
			List<LogEntry> entries = new ArrayList<LogEntry>();
			entries.add(new LogEntry(Level.ALL, new Date().getTime(), cause.getMessage()));
			entries.add(new LogEntry(Level.ALL, new Date().getTime(), ExceptionUtils.getStackTrace(cause)));
			this.logs.put("gatf", new SerializableLogEntries(entries));
		}
	}

	protected static void randomize(List<WebElement> le, String v1, String v2, String v3) {
		if ((le.get(0).getTagName().toLowerCase().matches("input") && le.get(0).getAttribute("type").toLowerCase().matches("text|url|email|hidden"))
				|| (le.get(0).getTagName().toLowerCase().matches("textarea"))) {
			int count = 10, totalcount = 1;
			if(StringUtils.isNotBlank(v3)) {
				try {
					totalcount = Integer.parseInt(v3);
				} catch (Exception e) {
				}
			}
			if(v1.toLowerCase().equals("alpha") || v1.toLowerCase().equals("alphanumeric") || v1.toLowerCase().equals("numeric")) {
				if(StringUtils.isNotBlank(v2)) {
					try {
						count = Integer.parseInt(v2);
					} catch (Exception e) {
					}
				}
			}
			List<String> vals = new ArrayList<>();
			for(int i=0;i<totalcount;i++) {
				if(StringUtils.isBlank(v1) || v1.toLowerCase().equals("alpha")) {
					vals.add(RandomStringUtils.randomAlphabetic(count));
				} else if(v1.toLowerCase().equals("alphanumeric")) {
					vals.add(RandomStringUtils.randomAlphanumeric(count));
				} else if(v1.toLowerCase().equals("numeric")) {
					String fv = RandomStringUtils.randomNumeric(count);
					long v = Long.parseLong(fv);
					if(v==0) {
						fv = "1";
					}
					vals.add(fv);
				} else if(v1.toLowerCase().equals("value") && v2!=null) {
					vals.add(v2);
				}
			}
			le.get(0).sendKeys(StringUtils.join(vals, " "));
		} else if (le.get(0).getTagName().toLowerCase().matches("input") && le.get(0).getAttribute("type").toLowerCase().matches("number")) {
			long min = 0;
			long max = 99999;
			if(StringUtils.isNotBlank(v1)) {
				try {
					min = Long.parseLong(v1);
				} catch (Exception e) {
				}
			}
			if(StringUtils.isNotBlank(v2)) {
				try {
					max = Long.parseLong(v2);
				} catch (Exception e) {
				}
			}
			long num = (long)(min + (Math.random() * (max - min)));
			le.get(0).sendKeys(num+"");
		} else if (le.get(0).getTagName().toLowerCase().matches("select")) {
			randomizeSelect(le);
		} else if (le.get(0).getTagName().toLowerCase().matches("input") && le.get(0).getAttribute("type").toLowerCase().matches("checkbox")) {
			le.get(0).click();
		} else if (le.get(0).getTagName().toLowerCase().matches("input") && le.get(0).getAttribute("type").toLowerCase().matches("radio")) {
			le.get(0).click();
		}
	}

	protected void window(int pos) {
		if(pos>=0 && pos<getSession().___d___.size()) {
			getSession().__wpos__ = pos;
		} else {
			throw new RuntimeException("Invalid window number specified");
		}
	}

	protected void newWindow(LoggingPreferences lp) {
		try
		{
			Method m = getClass().getMethod("setupDriver"+getSession().browserName, new Class[]{LoggingPreferences.class});
			if(m!=null) {
				m.invoke(this, new Object[]{lp});
			}
			getSession().__wpos__++;
		}
		catch (Exception e)
		{
			throw new RuntimeException("Invalid browser name specified");
		}
	}

	@SuppressWarnings("rawtypes")
	protected void mzoompinch(Object ele, int x, int y, boolean isZoom) {
		try
		{
			if(ele==null) {
				int leftX = x;
				int rightX = get___d___().manage().window().getSize().getWidth() + leftX;
				int midX = (leftX + rightX) / 2;

				int upperY = y;
				int lowerY = get___d___().manage().window().getSize().getHeight() + upperY;
				int midY = (upperY + lowerY) / 2;

				int aX = (int) (midX * 0.3);
				int aY = (int) (midY * 1.7);
				int bX = (int) (midX * 0.8);
				int bY = (int) (midY * 1.2);

				int cX = (int) (midX * 1.7);
				int cY = (int) (midY * 0.3);
				int dX = (int) (midX * 1.2);
				int dY = (int) (midY * 0.8);

				TouchAction action0 = new TouchAction((MobileDriver)get___d___());
				TouchAction action1 = new TouchAction((MobileDriver)get___d___());

				if(isZoom) {
					action0.press(bX, bY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(aX, aY).release();
					action1.press(dX, dY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(cX, cY).release();
				} else {
					action0.press(aX, aY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(bX, bY).release();
					action1.press(cX, cY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(dX, dY).release();
				}

				MultiTouchAction mAction = new MultiTouchAction((MobileDriver)get___d___());
				mAction.add(action0).add(action1).perform();
			} else if(ele instanceof WebElement) {
				int leftX = ((WebElement)ele).getLocation().getX();
				int rightX = ((WebElement)ele).getSize().getWidth() + leftX;
				int midX = (leftX + rightX) / 2;

				int upperY = ((WebElement)ele).getLocation().getY();
				int lowerY = ((WebElement)ele).getSize().getHeight() + upperY;
				int midY = (upperY + lowerY) / 2;

				int aX = (int) (midX * 0.3);
				int aY = (int) (midY * 1.7);
				int bX = (int) (midX * 0.8);
				int bY = (int) (midY * 1.2);

				int cX = (int) (midX * 1.7);
				int cY = (int) (midY * 0.3);
				int dX = (int) (midX * 1.2);
				int dY = (int) (midY * 0.8);

				TouchAction action0 = new TouchAction((MobileDriver)get___d___());
				TouchAction action1 = new TouchAction((MobileDriver)get___d___());

				if(isZoom) {
					action0.press(bX, bY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(aX, aY).release();
					action1.press(dX, dY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(cX, cY).release();
				} else {
					action0.press(aX, aY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(bX, bY).release();
					action1.press(cX, cY).waitAction(java.time.Duration.ofMillis(1000)).moveTo(dX, dY).release();
				}

				MultiTouchAction mAction = new MultiTouchAction((MobileDriver)get___d___());
				mAction.add(action0).add(action1).perform();
			}
		}
		catch (Exception e)
		{
		}
	}

	protected static void randomizeSelect(List<WebElement> le) {
		try
		{
			Select s = new Select(le.get(0));
			if(s.getOptions().size()>0) {
				List<Integer> li = new ArrayList<Integer>();
				for (WebElement o : s.getOptions())
				{
					li.add(Integer.parseInt(o.getAttribute("index")));
				}
				for (WebElement o : s.getAllSelectedOptions())
				{
					li.remove(Integer.parseInt(o.getAttribute("index")));
				}
				if(s.isMultiple()) {
					s.deselectAll();
					if(li.size()==0) {
						s.selectByIndex(0);
					} else {
						int ri = (int)(Math.random()*(li.size()-1));
						s.selectByIndex(li.get(ri));
					}
				} else {
					int i = Integer.parseInt(s.getFirstSelectedOption().getAttribute("index"));
					s.deselectByIndex(i);
					if(li.size()==0) {
						s.selectByIndex(0);
					} else {
						int ri = (int)(Math.random()*(li.size()-1));
						s.selectByIndex(li.get(ri));
					}
				}
			}
		}
		catch (Exception e)
		{
		}
	}

	protected Object pluginize(String name, String signature, List<String> args, List<List<String>> args1) {
		try
		{
			String[] parts = signature.split("@");
			String clsname = parts[0].trim();
			String method = parts[1].trim();

			Class<?> cls = Class.forName(clsname);
			Method meth = null, meth1 = null;
			boolean twoargs = true;

			try {
				meth = cls.getMethod(method, new Class[]{String.class, Object[].class});
			} catch (Exception e) {
			}

			if(meth==null) {
				meth = cls.getMethod(method, new Class[]{Object[].class});
				twoargs = false;
			}

			try {
				Method meth2 = cls.getMethod("isValidParams", new Class[]{List.class, List.class});
				Boolean isValid = (Boolean)meth2.invoke(null, new Object[]{args, args1});
				if(isValid!=null && !isValid) {
					throw new RuntimeException("Problem executing plugin, invalid parameters provided " + name);
				}
			} catch (Exception e) {
			}

			Object[] nargs =  new Object[args.size()+3];
			int c = 0;
			int indx = -1;
			for (String arg : args) {
				indx++;

				boolean parameterize = true;
				try {
					meth1 = cls.getMethod("isParameterizeFirstSetParam", new Class[]{String.class, int.class});
					Boolean temp = (Boolean)meth1.invoke(null, new Object[]{arg, indx});
					if(temp!=null && !temp) {
						parameterize = temp;
					}
				} catch (Exception e) {
				}

				if(!parameterize) {
					nargs[c++] = arg.trim();
					continue;
				}

				if(arg.trim().startsWith("${") && arg.trim().endsWith("}")) {
					String vn = arg.trim().substring(2, arg.length()-1);
					nargs[c++] = evaluate(vn);
				} else if(arg.trim().startsWith("$v{") && arg.trim().endsWith("}")) {
					String vn = arg.trim().substring(3, arg.length()-1);
					nargs[c++] = ___get_var__(vn);
				} else if((arg.toLowerCase().trim().startsWith("file://") || arg.toLowerCase().trim().startsWith("http://") 
						|| arg.toLowerCase().trim().startsWith("https://"))) {
					if(arg.toLowerCase().trim().startsWith("file://")) {
						File _f = ___cxt___.getResourceFile(arg.trim().substring(7));
						arg = _f.toURI().toURL().toString();
					}
					nargs[c++] = Resources.toString(new URL(arg), Charset.forName("UTF-8"));
				} else if(arg.toLowerCase().trim().startsWith("$file://") || arg.toLowerCase().trim().startsWith("$http://") 
						|| arg.toLowerCase().trim().startsWith("$https://")) {
					if(arg.toLowerCase().trim().startsWith("file://")) {
						File _f = ___cxt___.getResourceFile(arg.trim().substring(7));
						arg = _f.toURI().toURL().toString();
					}
					String tmpl = Resources.toString(new URL(arg), Charset.forName("UTF-8"));
					tmpl = evaluate(tmpl);
					nargs[c++] = tmpl;
				} else {
					nargs[c++] = arg;
				}
			}

			List<List<Object>> nargs1 = new ArrayList<List<Object>>();
			if(args1!=null) {
				int indx1 = 0;
				int indx2 = -1;
				for (List<String> narg : args1) {
					List<Object> nargs2 = new ArrayList<Object>();
					for (String arg : narg) {
						indx2++;

						boolean parameterize = true;
						try {
							meth1 = cls.getMethod("isParameterizeSecondSetParam", new Class[]{String.class, int.class, int.class});
							Boolean temp = (Boolean)meth1.invoke(null, new Object[]{arg, indx1, indx2});
							if(temp!=null && !temp) {
								parameterize = temp;
							}
						} catch (Exception e) {
						}

						if(!parameterize) {
							nargs2.add(arg.trim());
							continue;
						}

						if(arg.trim().startsWith("${") && arg.trim().endsWith("}")) {
							String vn = arg.trim().substring(2, arg.length()-1);
							nargs2.add(evaluate(vn));
						} else if(arg.trim().startsWith("$v{") && arg.trim().endsWith("}")) {
							String vn = arg.trim().substring(3, arg.length()-1);
							nargs2.add(___get_var__(vn));
						} else if(arg.toLowerCase().trim().startsWith("file://") || arg.toLowerCase().trim().startsWith("http://") 
								|| arg.toLowerCase().trim().startsWith("https://")) {
							if(arg.toLowerCase().trim().startsWith("file://")) {
								File _f = ___cxt___.getResourceFile(arg.trim().substring(7));
								arg = _f.toURI().toURL().toString();
							}
							nargs2.add(Resources.toString(new URL(arg), Charset.forName("UTF-8")));
						} else if(arg.toLowerCase().trim().startsWith("$file://") || arg.toLowerCase().trim().startsWith("$http://") 
								|| arg.toLowerCase().trim().startsWith("$https://")) {
							if(arg.toLowerCase().trim().startsWith("file://")) {
								File _f = ___cxt___.getResourceFile(arg.trim().substring(7));
								arg = _f.toURI().toURL().toString();
							}
							String tmpl = Resources.toString(new URL(arg), Charset.forName("UTF-8"));
							tmpl = evaluate(tmpl);
							nargs2.add(tmpl);
						} else {
							nargs2.add(arg);
						}

						indx1++;
					}
					nargs1.add(nargs2);
				}

				indx1++;
			}

			nargs[c++] = nargs1;
			nargs[c++] = index==-1?0:index;
			nargs[c++] = ___cxt___;

			return meth.invoke(null, twoargs?new Object[]{name, nargs}:new Object[]{nargs});
		}
		catch (Exception e)
		{
			throw new RuntimeException("Problem executing plugin " + name, e);
		}
	}

	public class SelFunc<T, R> implements Function<T, R> {
		Object[] ____wi = new Object[2];

		@Override
		public R apply(T t) {
			return null;
		}
	}

	@SuppressWarnings("serial")
	private static List<WebElement> getElements(WebDriver d, SearchContext sc, String finder) {
		String by = finder.substring(0, finder.indexOf("@")).trim();
		if(by.charAt(0)==by.charAt(by.length()-1)) {
			if(by.charAt(0)=='"' || by.charAt(0)=='\'') {
				by = by.substring(1, by.length()-1);
			}
		}
		String classifier = finder.substring(finder.indexOf("@")+1).trim();
		if(classifier.charAt(0)==classifier.charAt(classifier.length()-1)) {
			if(classifier.charAt(0)=='"' || classifier.charAt(0)=='\'') {
				classifier = classifier.substring(1, classifier.length()-1);
			}
		}
		List< WebElement> el = null;
		if(by.equalsIgnoreCase("id")) {
			el = By.id(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("name")) {
			el = By.name(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("class") || by.equalsIgnoreCase("className")) {
			el = By.className(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("tag") || by.equalsIgnoreCase("tagname")) {
			el = By.tagName(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("xpath")) {
			el = By.xpath(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("cssselector") || by.equalsIgnoreCase("css")) {
			el = By.cssSelector(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("text")) {
			el = By.xpath("//*[contains(text(), '" + classifier+"']").findElements(sc);
		} else if(by.equalsIgnoreCase("linkText")) {
			el = By.linkText(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("partialLinkText")) {
			el = By.partialLinkText(classifier).findElements(sc);
		} else if(by.equalsIgnoreCase("active")) {
			el = new ArrayList<WebElement>(){{add(((TargetLocator)d).activeElement());}};
		}
		return el;
	}

	protected void uploadFile(List<WebElement> ret, String filePath) {
		ret.get(0).click();
		java.awt.datatransfer.StringSelection ss = new java.awt.datatransfer.StringSelection(filePath);
		java.awt.Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
		try {
			java.awt.Robot robot21 = new java.awt.Robot();
			robot21.keyPress(java.awt.event.KeyEvent.VK_CONTROL);
			robot21.keyPress(java.awt.event.KeyEvent.VK_V);
			robot21.keyRelease(java.awt.event.KeyEvent.VK_V);
			robot21.keyRelease(java.awt.event.KeyEvent.VK_CONTROL);
			Thread.sleep(1000);
			robot21.keyPress(java.awt.event.KeyEvent.VK_ENTER);
			robot21.keyRelease(java.awt.event.KeyEvent.VK_ENTER);
		} catch (AWTException e) {
		} catch (InterruptedException e) {
		}
	}

	private void elementAction(List<WebElement> ret, String action, String tvalue) {
		if(action.equalsIgnoreCase("click")) {
			for(final WebElement we: ret) {
				we.click();
				break;
			}
		} else if(action.equalsIgnoreCase("hover")) {
			for(final WebElement we: ret) {
				Actions ac = new Actions(get___d___());
				ac.moveToElement(we).perform();
				break;
			}
		} else if(action.equalsIgnoreCase("hoverandclick")) {
			for(final WebElement we: ret) {
				Actions ac = new Actions(get___d___());
				ac.moveToElement(we).click().perform();
				break;
			}
		} else if(action.equalsIgnoreCase("clear")) {
			for(final WebElement we: ret) {
				we.clear();
				blur(we);
				break;
			}
		} else if(action.equalsIgnoreCase("submit")) {
			for(final WebElement we: ret) {
				we.submit();
				break;
			}
		} else if(action.equalsIgnoreCase("type") || action.equalsIgnoreCase("sendkeys")) {
			for(final WebElement we: ret) {
				System.out.println("Type => " + tvalue);
				we.sendKeys(tvalue);
				blur(we);
				break;
			}
		} else if(action.equalsIgnoreCase("typenb") || action.equalsIgnoreCase("sendkeysnb")) {
			for(final WebElement we: ret) {
				System.out.println("Type => " + tvalue);
				we.sendKeys(tvalue);
				//blur(we);
				break;
			}
		} else if(action.equalsIgnoreCase("upload")) {
			uploadFile(ret, tvalue);
		} else if(action.equalsIgnoreCase("select")) {

		} else if(action.equalsIgnoreCase("chord")) {
			for(final WebElement we: ret) {
				we.sendKeys(Keys.chord(tvalue));
				blur(we);
				break;
			}
		} else if(action.equalsIgnoreCase("chordnb")) {
			for(final WebElement we: ret) {
				we.sendKeys(Keys.chord(tvalue));
				//blur(we);
				break;
			}
		} else if(action.equalsIgnoreCase("dblclick") || action.equalsIgnoreCase("doubleclick")) {
			for(final WebElement we: ret) {
				Actions ac = new Actions(get___d___());
				ac.moveToElement(we).doubleClick().perform();
				break;
			}
		}
	}
	
	protected void blur(WebElement we) {
		//get___d___().findElement(By.tagName("body"))
		((JavascriptExecutor)get___d___()).executeScript("arguments[0].blur();", we);
	}

	@SuppressWarnings("unchecked")
	protected List<WebElement> handleWaitFunc(final SearchContext sc, final List<WebElement> ce, final long timeOutInSeconds, final String classifier, final String by, String subselector, 
			boolean byselsame, String value, String[] values, String action, String oper, String tvalue, String exmsg, boolean noExcep, String ... layers) {
		return (List<WebElement>)handleWaitOrTransientProv(sc, ce, timeOutInSeconds, classifier, by, subselector, byselsame, value, values, action, oper, tvalue, exmsg, noExcep, layers);
	}

	@SuppressWarnings("unchecked")
	protected List<String[]> transientProviderData(final SearchContext sc, final List<WebElement> ce, final long timeOutInSeconds, final String classifier, final String by, String subselector, 
			boolean byselsame, String value, String[] values, String action, String oper, String tvalue, String exmsg, boolean noExcep, String ... layers) {
		return (List<String[]>)handleWaitOrTransientProv(sc, ce, timeOutInSeconds, classifier, by, subselector, byselsame, value, values, action, oper, tvalue, exmsg, noExcep, layers);
	}

	@SuppressWarnings("unchecked")
	private Object handleWaitOrTransientProv(final SearchContext sc, final List<WebElement> ce, final long timeOutInSeconds, final String classifier, final String by, String subselector, 
			boolean byselsame, String value, String[] values, final String action, String oper, String tvalue, String exmsg, boolean noExcep, String ... layers) {
		final WebDriver wsc = (WebDriver) sc;
		final Object[] o = new Object[2];
		final SeleniumTest test = this;
		long timeoutRemaining = 0;
		System.out.println("Searching element => " + by+"@"+classifier);
		if(timeOutInSeconds<=0) {
			List<WebElement> el = getElements(get___d___(), wsc, by+"@"+classifier);
			if (el == null || el.isEmpty())  {
			} else {
				boolean enabledCheck = false;
				if(action!=null && (action.equalsIgnoreCase("click") || action.equalsIgnoreCase("type") 
						|| action.equalsIgnoreCase("sendkeys") || action.equalsIgnoreCase("chord") 
						|| action.equalsIgnoreCase("typenb") || action.equalsIgnoreCase("sendkeysnb") 
						|| action.equalsIgnoreCase("chordnb"))) {
					enabledCheck = true;
				}

				WebElement elo = elementInteractable(test, el.get(0), enabledCheck, layers).apply(get___d___());
				if(elo!=null) {
					o[0] = el;
					o[1] = ce;
				}
			}
		} else {
			long start = System.currentTimeMillis();
			try {
				(new WebDriverWait(wsc, timeOutInSeconds)).until(new Function<WebDriver, Boolean>() {
					public Boolean apply(WebDriver input) {
						List<WebElement> ___ce___ = ce;
						try {
							List<WebElement> el = getElements(get___d___(), wsc, by+"@"+classifier);
							if (el == null || el.isEmpty()) return false;

							boolean enabledCheck = false;
							if(action!=null && (action.equalsIgnoreCase("click") || action.equalsIgnoreCase("type") 
									|| action.equalsIgnoreCase("sendkeys") || action.equalsIgnoreCase("chord") 
									|| action.equalsIgnoreCase("typenb") || action.equalsIgnoreCase("sendkeysnb") 
									|| action.equalsIgnoreCase("chordnb"))) {
								enabledCheck = true;
							}

							WebElement elo = elementInteractable(test, el.get(0), enabledCheck, layers).apply(get___d___());
							if(elo!=null) {
								o[0] = el;
								o[1] = ___ce___;
								return true;
							}
							return false;
						} catch(Exception e) {
							return false;
						}
					}
				});
			} catch (org.openqa.selenium.TimeoutException e) {
				throw new RuntimeException(exmsg, e);
			}
			timeoutRemaining = timeOutInSeconds - (System.currentTimeMillis() - start)/1000;
			//System.out.println("Timeout remaining = " + timeoutRemaining);
		}
		List<WebElement> ret = (List<WebElement>)o[0];
		Object resp = ret;
		boolean flag = true;
		if(ret!=null) {
			if((StringUtils.isNotBlank(value) || (values!=null && values.length>0)) && subselector!=null && !subselector.isEmpty()) {
				if(value!=null)
				{
					if(byselsame)
					{
						String rhs = null;
						if(subselector.equalsIgnoreCase("title")) {
							rhs = ((WebDriver)sc).getTitle();
						} else if(subselector.equalsIgnoreCase("currentUrl")) {
							rhs = ((WebDriver)sc).getCurrentUrl();
						} else if(subselector.equalsIgnoreCase("pageSource")) {
							rhs = ((WebDriver)sc).getPageSource();
						} else if(subselector.equalsIgnoreCase("width")) {
							rhs = String.valueOf(get___d___().manage().window().getSize().getWidth());
						} else if(subselector.equalsIgnoreCase("height")) {
							rhs = String.valueOf(get___d___().manage().window().getSize().getHeight());
						} else if(subselector.equalsIgnoreCase("xpos")) {
							rhs = String.valueOf(get___d___().manage().window().getPosition().getX());
						} else if(subselector.equalsIgnoreCase("ypos")) {
							rhs = String.valueOf(get___d___().manage().window().getPosition().getY());
						} else if(subselector.equalsIgnoreCase("alerttext")) {
							rhs = String.valueOf(get___d___().switchTo().alert().getText());
						}

						if(rhs!=null) {
							if(oper.startsWith("<=")) {
								flag &= value.compareTo(rhs)<=0;
							} else if(oper.startsWith(">=")) {
								flag &= value.compareTo(rhs)>=0;
							} else if(oper.startsWith("=")) {
								flag &= value.compareTo(rhs)==0;
							} else if(oper.startsWith("<")) {
								flag &= value.compareTo(rhs)<0;
							} else if(oper.startsWith(">")) {
								flag &= value.compareTo(rhs)>0;
							} else if(oper.startsWith("!=")) {
								flag &= value.compareTo(rhs)!=0;
							} else if(oper.startsWith("%") && oper.endsWith("%")) {
								flag &= value.contains(rhs);
							} else if(oper.startsWith("%")) {
								flag &= value.startsWith(rhs);
							} else if(oper.endsWith("%")) {
								flag &= value.endsWith(rhs);
							}
						}
					}

					for(final WebElement we: ret) {
						String rhs = null;
						if(subselector.equalsIgnoreCase("text")) {
							rhs = we.getText();
						} else if(subselector.equalsIgnoreCase("tagname")) {
							rhs = we.getTagName();
						} else if(subselector.toLowerCase().startsWith("attr@")) {
							rhs = we.getAttribute(value);
						} else if(subselector.toLowerCase().startsWith("cssvalue@")) {
							rhs = we.getCssValue(value);
						} else if(subselector.equalsIgnoreCase("width")) {
							rhs = String.valueOf(we.getSize().getWidth());
						} else if(subselector.equalsIgnoreCase("height")) {
							rhs = String.valueOf(we.getSize().getHeight());
						} else if(subselector.equalsIgnoreCase("xpos")) {
							rhs = String.valueOf(we.getLocation().getX());
						} else if(subselector.equalsIgnoreCase("ypos")) {
							rhs = String.valueOf(we.getLocation().getY());
						}

						if(rhs!=null) {
							if(oper.startsWith("<=")) {
								flag &= value.compareTo(rhs)<=0;
							} else if(oper.startsWith(">=")) {
								flag &= value.compareTo(rhs)>=0;
							} else if(oper.startsWith("=")) {
								flag &= value.compareTo(rhs)==0;
							} else if(oper.startsWith("<")) {
								flag &= value.compareTo(rhs)<0;
							} else if(oper.startsWith(">")) {
								flag &= value.compareTo(rhs)>0;
							} else if(oper.startsWith("!=")) {
								flag &= value.compareTo(rhs)!=0;
							} else if(oper.startsWith("%") && oper.endsWith("%")) {
								flag &= value.contains(rhs);
							} else if(oper.startsWith("%")) {
								flag &= value.startsWith(rhs);
							} else if(oper.endsWith("%")) {
								flag &= value.endsWith(rhs);
							}
						}

						break;
					}
				} else if(values!=null && values.length>0) {
					for(final WebElement we: ret) {
						String rhs = null;
						if(subselector.equalsIgnoreCase("text")) {
							rhs = we.getText();
						} else if(subselector.equalsIgnoreCase("tagname")) {
							rhs = we.getTagName();
						} else if(subselector.toLowerCase().startsWith("attr@")) {
							rhs = we.getAttribute(value);
						} else if(subselector.toLowerCase().startsWith("cssvalue@")) {
							rhs = we.getCssValue(value);
						} else if(subselector.equalsIgnoreCase("width")) {
							rhs = String.valueOf(we.getSize().getWidth());
						} else if(subselector.equalsIgnoreCase("height")) {
							rhs = String.valueOf(we.getSize().getHeight());
						} else if(subselector.equalsIgnoreCase("xpos")) {
							rhs = String.valueOf(we.getLocation().getX());
						} else if(subselector.equalsIgnoreCase("ypos")) {
							rhs = String.valueOf(we.getLocation().getY());
						}

						if(rhs!=null) {
							if(oper.startsWith("<=")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag |= nvalue.compareTo(rhs)<=0;
								}
								flag &= tflag;
							} else if(oper.startsWith(">=")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.compareTo(rhs)>=0;
								}
								flag &= tflag;
							} else if(oper.startsWith("=")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.compareTo(rhs)==0;
								}
								flag &= tflag;
							} else if(oper.startsWith("<")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.compareTo(rhs)<0;
								}
								flag &= tflag;
							} else if(oper.startsWith(">")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.compareTo(rhs)>0;
								}
								flag &= tflag;
							} else if(oper.startsWith("!=")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.compareTo(rhs)!=0;
								}
								flag &= tflag;
							} else if(oper.startsWith("%") && oper.endsWith("%")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.contains(rhs);
								}
								flag &= tflag;
							} else if(oper.startsWith("%")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.startsWith(rhs);
								}
								flag &= tflag;
							} else if(oper.endsWith("%")) {
								boolean tflag = true;
								for (int i=0;i<values.length;i++) {
									String nvalue = values[i];
									tflag &= nvalue.endsWith(rhs);
								}
								flag &= tflag;
							}
						}

						break;
					}
				}
			} else if(action!=null) {
				try {
					elementAction(ret, action, tvalue);
				} catch (WebDriverException e) {
					String exMsg = ExceptionUtils.getStackTrace(e);
					boolean isInvClk = exMsg.contains("org.openqa.selenium.ElementClickInterceptedException") 
							&& exMsg.contains("is not clickable at point (")
							&& exMsg.contains("Other element would receive the click");
					boolean isNotSel = exMsg.contains("org.openqa.selenium.ElementNotSelectableException");
					boolean isNotInt = exMsg.contains("org.openqa.selenium.ElementNotInteractableException");
					boolean isNotVis = exMsg.contains("org.openqa.selenium.ElementNotVisibleException");
					if(isInvClk || isNotSel || isNotInt || isNotVis) {
						resp = null;
					}
					if(timeoutRemaining>0 && (isInvClk || isNotSel || isNotInt || isNotVis)) {
						Exception lastException = null;
						while(timeoutRemaining>0) {
							try {
								Thread.sleep(1000);
								System.out.println("WDE-Retrying operation.....Timeout remaining = " + (timeoutRemaining-1) + " secs");
								elementAction(ret, action, tvalue);
								lastException = null;
								break;
							} catch (Exception e1) {
								lastException = e1;
								exMsg = ExceptionUtils.getStackTrace(e1);
								isInvClk = exMsg.contains("org.openqa.selenium.ElementClickInterceptedException") 
										&& exMsg.contains("is not clickable at point (")
										&& exMsg.contains("Other element would receive the click");
								isNotSel = exMsg.contains("org.openqa.selenium.ElementNotSelectableException");
								isNotInt = exMsg.contains("org.openqa.selenium.ElementNotInteractableException");
								isNotVis = exMsg.contains("org.openqa.selenium.ElementNotVisibleException");
								if(!(isInvClk || isNotSel || isNotInt || isNotVis)) {
									break;
								}
							}
							timeoutRemaining--;
						}
						if(lastException!=null) {
							lastException.printStackTrace();
						}
					} else {
						e.printStackTrace();
					}
				} catch (Exception e) {
					String exMsg = ExceptionUtils.getStackTrace(e);
					boolean isInvClk = exMsg.contains("org.openqa.selenium.ElementClickInterceptedException") 
							&& exMsg.contains("is not clickable at point (")
							&& exMsg.contains("Other element would receive the click");
					boolean isNotSel = exMsg.contains("org.openqa.selenium.ElementNotSelectableException");
					boolean isNotInt = exMsg.contains("org.openqa.selenium.ElementNotInteractableException");
					boolean isNotVis = exMsg.contains("org.openqa.selenium.ElementNotVisibleException");
					if(isInvClk || isNotSel || isNotInt || isNotVis) {
						resp = null;
					}
					if(timeoutRemaining>0 && (isInvClk || isNotSel || isNotInt || isNotVis)) {
						Exception lastException = null;
						while(timeoutRemaining>0) {
							try {
								Thread.sleep(1000);
								System.out.println("E-Retrying operation.....Timeout remaining = " + (timeoutRemaining-1) + " secs");
								elementAction(ret, action, tvalue);
								lastException = null;
								break;
							} catch (Exception e1) {
								lastException = e1;
								exMsg = ExceptionUtils.getStackTrace(e1);
								isInvClk = exMsg.contains("org.openqa.selenium.ElementClickInterceptedException") 
										&& exMsg.contains("is not clickable at point (")
										&& exMsg.contains("Other element would receive the click");
								isNotSel = exMsg.contains("org.openqa.selenium.ElementNotSelectableException");
								isNotInt = exMsg.contains("org.openqa.selenium.ElementNotInteractableException");
								isNotVis = exMsg.contains("org.openqa.selenium.ElementNotVisibleException");
								if(!(isInvClk || isNotSel || isNotInt || isNotVis)) {
									break;
								}
							}
							timeoutRemaining--;
						}
						if(lastException!=null) {
							lastException.printStackTrace();
						}
					} else {
						e.printStackTrace();
					}
				}
			} else if("selected".equalsIgnoreCase(subselector) || "enabled".equalsIgnoreCase(subselector) || "visible".equalsIgnoreCase(subselector)) {
				for(final WebElement we: ret) {
					if(subselector.equalsIgnoreCase("selected")) {
						flag &= we.isSelected();
					} else if(subselector.equalsIgnoreCase("enabled")) {
						flag &= we.isEnabled();
					} else if(subselector.equalsIgnoreCase("visible")) {
						flag &= we.isDisplayed();
					}
					break;
				}
			} else if(subselector!=null) {
				List<String> ssl = Arrays.asList(subselector.split("[\t ]*,[\t ]*"));
				List<String[]> rtl = new java.util.ArrayList<String[]>();
				for(final WebElement we: ret) {
					String[] t = new String[ssl.size()];
					for (int i=0;i<ssl.size();i++)
					{
						if(ssl.get(i).equalsIgnoreCase("text")) {
							t[i] = we.getText();
						} else if(ssl.get(i).equalsIgnoreCase("tagname")) {
							t[i] = we.getTagName();
						} else if(ssl.get(i).toLowerCase().startsWith("attr@")) {
							String atname = ssl.get(i).substring(5);
							if(atname.charAt(0)=='"' || atname.charAt(0)=='\'') {
								atname = atname.substring(1, atname.length()-1);
							}
							t[i] = we.getAttribute(atname);
						} else if(ssl.get(i).toLowerCase().startsWith("cssvalue@")) {
							String atname = ssl.get(i).substring(9);
							if(atname.charAt(0)=='"' || atname.charAt(0)=='\'') {
								atname = atname.substring(1, atname.length()-1);
							}
							t[i] = we.getCssValue(atname);
						} else if(ssl.get(i).equalsIgnoreCase("width")) {
							t[i] = String.valueOf(we.getSize().getWidth());
						} else if(ssl.get(i).equalsIgnoreCase("height")) {
							t[i] = String.valueOf(we.getSize().getHeight());
						} else if(ssl.get(i).equalsIgnoreCase("xpos")) {
							t[i] = String.valueOf(we.getLocation().getX());
						} else if(ssl.get(i).equalsIgnoreCase("ypos")) {
							t[i] = String.valueOf(we.getLocation().getY());
						}
					}
					rtl.add(t);
				}
				resp = rtl;
			}
		}
		if(!noExcep && !flag) {
			throw new RuntimeException(exmsg);
		} else if(noExcep) {
			//resp = null;
		}
		return resp;
	}

	protected void sleep(long wait) {
		try {
			Thread.sleep(wait);
		} catch (Exception ___e___15) {
		}
	}

	private static int getZIndex(WebElement el) {
		String zindex = el.getCssValue("z-index");
		if(zindex!=null) {
			try {
				return Integer.parseInt(el.getCssValue("z-index"));
			} catch (Exception e) {
			}
		}
		return 0;
	}

	private static boolean checkRectangleOnTop(Rectangle trec, Rectangle orec) {
		Point tmid = new Point(trec.getX() + trec.getWidth()/2, trec.getY() + trec.getHeight()/2);
		return tmid.getX()>orec.getX() && tmid.getX()<(orec.getX()+orec.getWidth()) && tmid.getY()>orec.getY() && tmid.getY()<(orec.getY()+orec.getHeight());
	}

	private static Rectangle getRect(WebElement e) {
		return new Rectangle(e.getLocation().getX(), e.getLocation().getY(), e.getSize().getWidth(), e.getSize().getHeight());
	}

	public static ExpectedCondition<WebElement> elementInteractable(SeleniumTest test, WebElement element, boolean enabledCheck, String ... layers) {
		return new ExpectedCondition<WebElement>() {
			@Override
			public WebElement apply(WebDriver driver) {
				try {
					WebElement telement = ExpectedConditions.elementToBeClickable(element).apply(driver);
					//element.isDisplayed() && (!enabledCheck || (enabledCheck && element.isEnabled()));
					if(telement!=null && layers!=null && layers.length>0) {
						int tzl = getZIndex(telement);
						Rectangle trec = getRect(telement);
						for (String layer : layers) {
							try {
								List<WebElement> el = getElements(test.get___d___(), test.get___d___(), layer);
								if(el!=null && el.size()>0 && /*el.get(0).isEnabled() &&*/ el.get(0).isDisplayed()) {
									int ozl = getZIndex(el.get(0));
									Rectangle orec = getRect(el.get(0));
									boolean temp = ozl>tzl && checkRectangleOnTop(trec, orec);
									if(!temp) {
										telement = null;
										break;
									}
								}
							} catch (Exception e) {
							}
						}
					}
					return telement;
				} catch (Exception e) {
					return null;
				}
			}
		};
	}

	protected void initStateFulProvider(String name) {
		String hash = getFileProviderHash(name);
		if(hash==null) {
			throw new RuntimeException("Only File providers can have stateful loops, provder " + name + " is not a file provider");
		}
		File stateFile = new File(name+"_s_state");
		if(!stateFile.exists()) {
			try {
				stateFile.createNewFile();
				FileUtils.write(stateFile, hash+";0", "UTF-8");
			} catch (Exception e) {
			}
		} else {
			try {
				String contents = FileUtils.readFileToString(stateFile, "UTF-8");
				if(!contents.startsWith(hash+";")) {
					throw new RuntimeException("File provider " + name + " has changed, cannot continue with stateful provider loop\n"
							+ "Delete the " + name+"_s_state" + " file to continue from the beginning of the provider loop");
				}
			} catch (Exception e) {
			}
		}
	}

	protected int preStateFulProvider(String name, int index) {
		File stateFile = new File(name+"_s_state");
		if(stateFile.exists()) {
			try {
				String contents = FileUtils.readFileToString(stateFile, "UTF-8");
				return Integer.valueOf(contents.substring(contents.indexOf(";")+1));
			} catch (Exception e) {
			}
		} else {
			System.out.println("File provider " + name + " has been deleted on the filesystem, but we will continue as far as we can");
		}
		return index;
	}

	protected void postStateFulProvider(String name, int index) {
		File stateFile = new File(name+"_s_state");
		if(stateFile.exists()) {
			try {
				String contents = FileUtils.readFileToString(stateFile, "UTF-8");
				contents = contents.substring(0, contents.indexOf(";")+1) + (index+1);
				FileUtils.write(stateFile, contents, "UTF-8");
			} catch (Exception e) {
			}
		} else {
			System.out.println("File provider " + name + " has been deleted on the filesystem, but we will continue as far as we can");
		}
	}

	protected byte[] screenshotAsBytes(WebDriver webDriver) throws IOException {
		Screenshot fpScreenshot = new AShot().shootingStrategy(ShootingStrategies.viewportPasting(1000)).takeScreenshot(webDriver);
		BufferedImage originalImage = fpScreenshot.getImage();
		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
			ImageIO.write(originalImage, "png", baos);
			baos.flush();
			return baos.toByteArray();
		}
	}

	protected void screenshotAsFile(WebDriver webDriver, String filepath) throws IOException {
		if(webDriver instanceof AppiumDriver) {
			File sc = ((TakesScreenshot)new Augmenter().augment(webDriver)).getScreenshotAs(OutputType.FILE);
			FileUtils.copyFile(sc, new File(filepath));
		} else {
			Screenshot fpScreenshot = new AShot().shootingStrategy(ShootingStrategies.viewportPasting(1000)).takeScreenshot(webDriver);
			BufferedImage originalImage = fpScreenshot.getImage();
			ImageIO.write(originalImage, "png", new FileOutputStream(filepath));
		}
	}

	protected void elementScreenshotAsFile(WebDriver webDriver, WebElement element, String filepath) throws IOException {
		if(webDriver instanceof AppiumDriver && element instanceof RemoteWebElement) {
			File sc = ((TakesScreenshot)new Augmenter().augment((RemoteWebElement)element)).getScreenshotAs(OutputType.FILE);
			FileUtils.copyFile(sc, new File(filepath));
		} else {
			Screenshot fpScreenshot = new AShot().takeScreenshot(webDriver, element);
			BufferedImage originalImage = fpScreenshot.getImage();
			ImageIO.write(originalImage, "png", new FileOutputStream(filepath));
		}
	}
	
	protected String getOutDir() {
		return ___cxt___.getOutDirPath();
	}
	
	public static void main(String[] args) throws Exception {
        GatfExecutorConfig config = Command.getConfig(args[1].trim(), null);
        config.setSeleniumLoggerPreferences("browser(OFF),client(OFF),driver(OFF),performance(OFF),profiler(OFF),server(OFF)");
        for (SeleniumDriverConfig selConf : config.getSeleniumDriverConfigs())
        {
            if(selConf.getDriverName()!=null) {
                System.setProperty(selConf.getDriverName(), selConf.getPath());
            }
        }
        
        System.setProperty("jdk.tls.client.protocols", "TLSv1,TLSv1.1,TLSv1.2");
        Security.setProperty("crypto.policy", "unlimited");
        
        AcceptanceTestContext c = new AcceptanceTestContext();
        c.setGatfExecutorConfig(config);
        c.validateAndInit(true);
        c.getWorkflowContextHandler().initializeSuiteContext(1);
    	
        try {
        	final LoggingPreferences lp = SeleniumCodeGeneratorAndUtil.getLp(config);
        	SeleniumTest dyn = (SeleniumTest)Class.forName(args[0]).getDeclaredConstructor(new Class[] {AcceptanceTestContext.class, int.class}).newInstance(new Object[] {c, 0});
        	System.out.println(dyn!=null?"SUCCESS":"FAILURE");
            if(dyn!=null) {
            	try {
                    dyn.execute(lp);
                } catch (Throwable e) {
                }
                dyn.quitAll();
            }
        } catch (GatfSelCodeParseError e) {
        	e.printStackTrace();
        } catch (Throwable e) {
        	e.printStackTrace();
        }
    }
}
 public static class SeleniumTestResult implements Serializable {
	private static final long serialVersionUID = 1L;

	private Map<String, SerializableLogEntries> logs = new HashMap<String, SerializableLogEntries>();;

	private boolean status;

	long executionTime;

	private Map<String, Object[]> internalTestRes = new HashMap<String, Object[]>();

	public Map<String,SerializableLogEntries> getLogs()
	{
		return logs;
	}
	public boolean isStatus()
	{
		return status;
	}
	public long getExecutionTime()
	{
		return executionTime;
	}
	public Map<String,Object[]> getInternalTestRes()
	{
		return internalTestRes;
	}
	public SeleniumTestResult(WebDriver d, SeleniumTest test, LoggingPreferences ___lp___)
	{
		this.status = true;
		this.internalTestRes = test.getSession().internalTestRs;
		/*Logs logs = d.manage().logs();
           for (String s : LOG_TYPES_SET) {
               if(!logs.getAvailableLogTypes().contains(s))continue;
               LogEntries logEntries = logs.get(s);
               if(logEntries!=null && !logEntries.getAll().isEmpty()) {
                   this.logs.put(s, new SerializableLogEntries(logEntries.getAll())); 
               }
           }*/
	}
	public SeleniumTestResult(WebDriver d, SeleniumTest test, Throwable cause, LoggingPreferences ___lp___) {
		this.status = false;
		this.internalTestRes = test.getSession().internalTestRs;
		/*Logs logs = d.manage().logs();
           for (String s : LOG_TYPES_SET) {
               if(!logs.getAvailableLogTypes().contains(s))continue;
               LogEntries logEntries = logs.get(s);
               if(logEntries!=null && !logEntries.getAll().isEmpty()) {
                   this.logs.put(s, new SerializableLogEntries(logEntries.getAll())); 
               }
           }*/
		List<LogEntry> entries = new ArrayList<LogEntry>();
		entries.add(new LogEntry(Level.ALL, new Date().getTime(), cause.getMessage()));
		entries.add(new LogEntry(Level.ALL, new Date().getTime(), ExceptionUtils.getStackTrace(cause)));
		this.logs.put("gatf", new SerializableLogEntries(entries));
	}
	public SeleniumTestResult(SeleniumTest test, Throwable cause) {
		this.status = false;
		this.internalTestRes = test.getSession().internalTestRs;
		List<LogEntry> entries = new ArrayList<LogEntry>();
		entries.add(new LogEntry(Level.ALL, new Date().getTime(), cause.getMessage()));
		entries.add(new LogEntry(Level.ALL, new Date().getTime(), ExceptionUtils.getStackTrace(cause)));
		this.logs.put("gatf", new SerializableLogEntries(entries));
	}
}
 public class SeleniumTestSession implements Serializable {
    private static final long serialVersionUID = 1L;
    
    protected transient List<WebDriver> ___d___ = new ArrayList<WebDriver>();
    protected transient int __wpos__ = 0;
    protected final Map<String, SeleniumResult> __result__ = new HashMap<String, SeleniumResult>();
    
    protected transient String __subtestname__ = null;
    protected transient long __subtestexecutiontime__ = 0L;
    protected transient long __teststarttime__ = 0L;
    protected transient final Map<String, Integer> __provdetails__ = new LinkedHashMap<String, Integer>();
    protected transient final Map<String, Object> __vars__ = new LinkedHashMap<String, Object>();
    
    protected final Map<String, Object[]> internalTestRs = new HashMap<String,Object[]>();
    
    protected transient final Map<String, List<Map<String, String>>> providerTestDataMap = new HashMap<String, List<Map<String,String>>>();

    protected String browserName;
    
    protected String sessionName;
    
    public String getSessionName() {
        return sessionName;
    }

    public static class SeleniumResult implements Serializable {
        private static final long serialVersionUID = 1L;

        String browserName;

        SeleniumTestResult result;

        Map<String, SeleniumTestResult>  __cresult__ = new LinkedHashMap<String, SeleniumTestResult>();

        public SeleniumTestResult getResult()
        {
            return result;
        }

        public Map<String,SeleniumTestResult> getSubTestResults()
        {
            return __cresult__;
        }

        public String getBrowserName()
        {
            return browserName;
        }
    }
    
    public Map<String, SeleniumResult> getResult() {
        return __result__;
    }
} public static class SeleniumResult implements Serializable {
    private static final long serialVersionUID = 1L;

    String browserName;

    SeleniumTestResult result;

    Map<String, SeleniumTestResult>  __cresult__ = new LinkedHashMap<String, SeleniumTestResult>();

    public SeleniumTestResult getResult()
    {
        return result;
    }

    public Map<String,SeleniumTestResult> getSubTestResults()
    {
        return __cresult__;
    }

    public String getBrowserName()
    {
        return browserName;
    }
} @SuppressWarnings("rawtypes")
public class GatfSelDebugger {
	private String selscript;
	private String srcCode;
	private BreakpointEvent cbe;
	private Class debugClass; 
	private VirtualMachine dvm;
	private ClassPrepareEvent cpe;
	private Set<Integer> breaks = new HashSet<Integer>();
	private Map<Integer, Object[]> selToJavaLineMap = new HashMap<Integer, Object[]>();
	private Thread eventThr = null;
	private volatile int running = 1;
	private volatile AtomicInteger activity = new AtomicInteger(0);
	
	public boolean isActivityObserved() {
		int actCount = activity.get();
		activity.set(0);
		if(actCount>0) {
			return true;
		}
		return false;
	}
	
    public String getSrcCode() {
		return srcCode;
	}

	public void setSrcCode(String srcCode) {
		this.srcCode = srcCode;
	}

	public String getSelscript() {
		return selscript;
	}

	public void setSelscript(String selscript) {
		this.selscript = selscript;
	}
	
	public Set<Integer> getDebuggableLines() {
		return selToJavaLineMap.keySet();
	}
	
	/*public int getCurrentLine() {
		if(cbe!=null && cbe.location().toString().indexOf(debugClass.getName()+":")!=-1) {
			for (Integer selln : selToJavaLineMap.keySet()) {
				int lineNumber = (Integer)selToJavaLineMap.get(selln)[1];
				if(lineNumber==cbe.location().lineNumber()) {
					return selln;
				}
			}
		}
		return 0;
	}*/
	
	public int getNextLine() {
		activity.incrementAndGet();
		if(cbe!=null && cbe.location().toString().indexOf(debugClass.getName()+":")!=-1) {
			boolean found = false;
			for (Integer selln : selToJavaLineMap.keySet()) {
				int lineNumber = (Integer)selToJavaLineMap.get(selln)[1];
				if(lineNumber==cbe.location().lineNumber()) {
					found = true;
				} else if(found) {
					return selln;
				}
			}
		}
		return 0;
	}

	public void connectAndLaunchVM(String[] args) throws IOException, IllegalConnectorArgumentsException, VMStartException {
		activity.incrementAndGet();
    	File dir = new File(FileUtils.getTempDirectory(), "gatf-code/");
    	String cp = System.getProperty("java.class.path") + File.pathSeparatorChar + dir.getAbsolutePath();
    	dvm = new VMLauncher("-cp "+cp, debugClass.getName() + " " + StringUtils.join(args, " ")).start();
    }

    public void enableClassPrepareRequest() {
    	activity.incrementAndGet();
        ClassPrepareRequest classPrepareRequest = dvm.eventRequestManager().createClassPrepareRequest();
        classPrepareRequest.addClassFilter(debugClass.getName());
        classPrepareRequest.enable();
    }

    public boolean unsetBreakPoint(int selLineNum) throws AbsentInformationException {
    	activity.incrementAndGet();
    	if(!selToJavaLineMap.containsKey(selLineNum) || !breaks.contains(selLineNum)) {
    		return false;
    	}
    	breaks.remove(selLineNum);
    	System.out.println("Removing breakpoint at line " + selLineNum);
    	int lineNumber = (Integer)selToJavaLineMap.get(selLineNum)[1];
    	List<BreakpointRequest> bpl = dvm.eventRequestManager().breakpointRequests();
    	for (BreakpointRequest bpr : bpl) {
			if(bpr.location().toString().indexOf(debugClass.getName()+":")!=-1 && bpr.location().lineNumber()==lineNumber) {
				dvm.eventRequestManager().deleteEventRequest(bpr);
				return true;
			}
    	}
    	return false;
    }

    public boolean setBreakPoint(int selLineNum) throws AbsentInformationException {
    	activity.incrementAndGet();
    	if(!selToJavaLineMap.containsKey(selLineNum)) {
    		return false;
    	}
    	System.out.println("Adding breakpoint at line " + selLineNum);
    	int lineNumber = (Integer)selToJavaLineMap.get(selLineNum)[1];
    	breaks.add(lineNumber);
        ClassType classType = (ClassType) cpe.referenceType();
        Location location = classType.locationsOfLine(lineNumber).get(0);
        BreakpointRequest bpReq = dvm.eventRequestManager().createBreakpointRequest(location);
        bpReq.enable();
        dvm.resume();
        return true;
    }

    public void displayVariables(LocatableEvent event) throws IncompatibleThreadStateException, AbsentInformationException {
    	activity.incrementAndGet();
        StackFrame stackFrame = event.thread().frame(0);
        if(stackFrame.location().toString().contains(debugClass.getName())) {
            Map<LocalVariable, Value> visibleVariables = stackFrame.getValues(stackFrame.visibleVariables());
            System.out.println("Variables at " +stackFrame.location().toString() +  " > ");
            for (Map.Entry<LocalVariable, Value> entry : visibleVariables.entrySet()) {
                System.out.println(entry.getKey().name() + " = " + entry.getValue());
            }
        }
    }

    public boolean enableStepRequest() throws AbsentInformationException {
    	activity.incrementAndGet();
        if(cbe!=null && cbe.location().toString().indexOf(debugClass.getName()+":")!=-1) {
        	setBreakPoint(getNextLine());
            return true;
        }
        return false;
    }
    
    public void suspend() {
    	activity.incrementAndGet();
    	dvm.suspend();
    }
    
    public void resume() {
    	activity.incrementAndGet();
    	dvm.resume();
    }
    
    public int getRunning() {
    	activity.incrementAndGet();
    	if(running==1) {
    		return getNextLine();
    	}
    	return 0;
    }
    
    public void destroy() {
    	dvm.exit(0);
    	while(running==1) {
    		try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
    	}
    }

    public static GatfSelDebugger debugSession(Class<? extends SeleniumTest> testClass, String[] args, Map<Integer, Object[]> selToJavaLineMap) throws Exception {
        GatfSelDebugger dbgIns = new GatfSelDebugger();
        dbgIns.debugClass = testClass;
        dbgIns.selToJavaLineMap = selToJavaLineMap;
        
    	dbgIns.connectAndLaunchVM(args);
        dbgIns.enableClassPrepareRequest();
        dbgIns.running = 1;
        
        dbgIns.eventThr = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					EventSet eventSet = null;
		            while ((eventSet = dbgIns.dvm.eventQueue().remove()) != null) {
		                for (Event event : eventSet) {
		                    if (event instanceof ClassPrepareEvent) {
		                    	System.out.println("ClassPrepareEvent received...");
		                    	dbgIns.cpe = (ClassPrepareEvent)event;
		                    	dbgIns.setBreakPoint(selToJavaLineMap.keySet().iterator().next());
		                    } else if (event instanceof BreakpointEvent) {
		                    	System.out.println("BreakpointEvent received...");
		                    	dbgIns.cbe = (BreakpointEvent) event;
		                        dbgIns.displayVariables(dbgIns.cbe);
		                    } else {
		                    	dbgIns.cbe = null;
		                    	System.out.println(event.getClass().getSimpleName() + " received...");
		                    	if(event instanceof VMDeathEvent) {
		                    		dbgIns.dvm.resume();
		                    	} else if(event instanceof VMDisconnectEvent) {
		                    		break;
		                    	} else {
		                    		dbgIns.dvm.resume();
		                    	}
		                    }
		                }
		            }
				} catch (VMDisconnectedException e) {
		            System.out.println("Virtual Machine disconnected");
		        } catch (InterruptedException e) {
		        	System.out.println("Stopping debugger session");
		        } catch (Exception e) {
		            e.printStackTrace();
		        } finally {
		        	dbgIns.eventThr = null;
		        	dbgIns.running = 0;
		        	if(dbgIns.dvm!=null) {
			            InputStreamReader reader = new InputStreamReader(dbgIns.dvm.process().getInputStream());
			            OutputStreamWriter writer = new OutputStreamWriter(System.out);
			            char[] buf = new char[512];
			            try {
							reader.read(buf);
							writer.write(buf);
				            writer.flush();
						} catch (IOException e) {
							e.printStackTrace();
						}
		        	}
		        }
			}
		});
        dbgIns.eventThr.start();
        System.out.println("Started debugger session");
        return dbgIns;
    }
} public class GatfConfigToolUtil implements GatfConfigToolMojoInt {

	protected String rootDir;
	
	private String ipAddress;
	
	private int port;
	
	private AcceptanceTestContext context = null;
	
	private TestCase authTestCase = null;
	
	public GatfConfigToolUtil() {
	}

	public void execute() throws Exception {
		HttpServer server = new HttpServer();

		final String mainDir = rootDir + File.separator + "gatf-config-tool";
		WorkflowContextHandler.copyResourcesToDirectory("gatf-config-tool", mainDir);
        
        final GatfConfigToolUtil mojo = this;
        
        createConfigFileIfNotExists(mojo, true, null);
        
        createConfigFileIfNotExists(mojo, false, null);
        
        createServerApiAndIssueTrackingApiFilesIfNotExists(mojo);
        
        server.addListener(new NetworkListener("ConfigServer", ipAddress, port));
        
        handleRootContext(server, mainDir, mojo);
        
        Function<String, GatfPlugin> f = new Function<String, GatfPlugin>() {
			@Override
			public GatfPlugin apply(String type) {
				GatfPlugin gp = null;
				if(type.equals("executor"))
				{
					gp = new GatfTestCaseExecutorUtil();
				}
				else
				{
					gp = new GatfTestGeneratorUtil();
				}
				return gp;
			}
		};
        
        server.getServerConfiguration().addHttpHandler(new GatfConfigurationHandler(mojo, f), "/configure");
        
        GatfReportsHandler repHandler = new GatfReportsHandler(mojo, f);
        server.getServerConfiguration().addHttpHandler(repHandler, "/reports");
        
        server.getServerConfiguration().addHttpHandler(new GatfMiscHandler(mojo), "/misc");
        
        server.getServerConfiguration().addHttpHandler(new GatfTestCaseFilesHandler(mojo), "/testcasefiles");
        
        server.getServerConfiguration().addHttpHandler(new GatfTestCaseHandler(mojo), "/testcases");
		
        server.getServerConfiguration().addHttpHandler(new GatfPluginExecutionHandler(mojo, f), "/execute");
        
        server.getServerConfiguration().addHttpHandler(new GatfProfileHandler(mojo, f), "/profile");
		
		try {
		    server.start();
		    new File("gatf.ctrl").createNewFile();
		    while(new File("gatf.ctrl").exists()) {
				Thread.sleep(10000);
				repHandler.clearInactiveDebugSessions();
		    }
		} catch (Exception e) {
		    System.err.println(e);
		}
	}

    protected static void createServerApiAndIssueTrackingApiFilesIfNotExists(GatfConfigToolMojoInt mojo) {
		String[] configFiles = {AcceptanceTestContext.GATF_SERVER_LOGS_API_FILE_NM, "gatf-issuetracking-api-int.xml"};
		for (String fileNm : configFiles) {
			if(!new File(mojo.getRootDir(), fileNm).exists())
	        {
	        	try {
	        		File loggingApiFile = new File(mojo.getRootDir(), fileNm);
					loggingApiFile.createNewFile();
		        	BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(loggingApiFile));
					bos.write("<TestCases></TestCases>".getBytes());
					bos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
	        }
		}
	}

    protected static void handleRootContext(HttpServer server, final String mainDir, final GatfConfigToolMojoInt mojo) {
		server.getServerConfiguration().addHttpHandler(
		    new HttpHandler() {
		        public void service(Request request, Response response) throws Exception {
		        	new CacheLessStaticHttpHandler(mainDir).service(request, response);
		        }
		    }, "/");
		
		server.getServerConfiguration().addHttpHandler(new GatfProjectZipHandler(mojo), "/projectZip");
	}

    protected static void sanitizeAndSaveGatfConfig(GatfExecutorConfig gatfConfig, GatfConfigToolMojoInt mojo, boolean isChanged) throws IOException
	{
		if(gatfConfig.getTestCasesBasePath()==null) {
			isChanged = true;
			gatfConfig.setTestCasesBasePath(mojo.getRootDir());
		}
		if(gatfConfig.getOutFilesBasePath()==null) {
			isChanged = true;
			gatfConfig.setOutFilesBasePath(mojo.getRootDir());
		}
		if(gatfConfig.getTestCaseDir()==null) {
			isChanged = true;
			gatfConfig.setTestCaseDir("data");
		}
		if(gatfConfig.getOutFilesDir()==null) {
			isChanged = true;
			gatfConfig.setOutFilesDir("out");
		}
		if(gatfConfig.isEnabled()==null) {
			isChanged = true;
			gatfConfig.setEnabled(true);
		}
		if(gatfConfig.getConcurrentUserSimulationNum()==null) {
			isChanged = true;
			gatfConfig.setConcurrentUserSimulationNum(0);
		}
		if(gatfConfig.getHttpConnectionTimeout()==null) {
			isChanged = true;
			gatfConfig.setHttpConnectionTimeout(10000);
		}
		if(gatfConfig.getHttpRequestTimeout()==null) {
			isChanged = true;
			gatfConfig.setHttpRequestTimeout(10000);
		}
		if(gatfConfig.getLoadTestingReportSamples()==null) {
			isChanged = true;
			gatfConfig.setLoadTestingReportSamples(3);
		}
		if(gatfConfig.getConcurrentUserRampUpTime()==null) {
			isChanged = true;
			gatfConfig.setConcurrentUserRampUpTime(0L);
		}
		if(gatfConfig.getNumConcurrentExecutions()==null) {
			isChanged = true;
			gatfConfig.setNumConcurrentExecutions(1);
		}
		if(gatfConfig.getRepeatSuiteExecutionNum()==null) {
			isChanged = true;
			gatfConfig.setRepeatSuiteExecutionNum(0);
		}
		
		String tmp = System.getProperty("D_JAVA_HOME")!=null?System.getProperty("D_JAVA_HOME"):System.getenv("D_JAVA_HOME");
		if(tmp!=null) {
			isChanged = true;
			gatfConfig.setJavaHome(tmp);
		}
		
		boolean isDocker = false;
		tmp = System.getProperty("D_DOCKER")!=null?System.getProperty("D_DOCKER"):System.getenv("D_DOCKER");
		if("true".equalsIgnoreCase(tmp)) {
			isChanged = true;
			isDocker = true;
			gatfConfig.setTestCasesBasePath("/workdir");
			gatfConfig.setOutFilesBasePath("/workdir");
		}
		
		tmp = System.getProperty("D_CHROME_DRIVER")!=null?System.getProperty("D_CHROME_DRIVER"):System.getenv("D_CHROME_DRIVER");
		if(tmp!=null && gatfConfig.getSeleniumDriverConfigs()!=null) {
			if(gatfConfig.getSeleniumDriverConfigs().length>0) {
				for (SeleniumDriverConfig sc : gatfConfig.getSeleniumDriverConfigs()) {
					if(sc.getName().equalsIgnoreCase("chrome")) {
						isChanged = true;
						sc.setPath(tmp);
						if(isDocker) {
							String args = "--whitelisted-ips --no-sandbox";
							if(StringUtils.isNotBlank(sc.getArguments())) {
								String cargs = sc.getArguments().trim();
								cargs = cargs.replace("--whitelisted-ips", "").replace("--no-sandbox", "");
								args += " " + cargs;
							}
							args = args.replaceAll("\\s+", " ");
							sc.setArguments(args);
						}
					}
				}
			}
		}
		
		tmp = System.getProperty("D_FF_DRIVER")!=null?System.getProperty("D_FF_DRIVER"):System.getenv("D_FF_DRIVER");
		if(tmp!=null && gatfConfig.getSeleniumDriverConfigs()!=null) {
			if(gatfConfig.getSeleniumDriverConfigs().length>0) {
				for (SeleniumDriverConfig sc : gatfConfig.getSeleniumDriverConfigs()) {
					if(sc.getName().equalsIgnoreCase("firefox")) {
						isChanged = true;
						sc.setPath(tmp);
					}
				}
			}
		}
		
		tmp = System.getProperty("D_GATF_JAR")!=null?System.getProperty("D_GATF_JAR"):System.getenv("D_GATF_JAR");
		if(tmp!=null) {
			isChanged = true;
			gatfConfig.setGatfJarPath(tmp);
		}
		
		if(isChanged)
		{
			synchronized (mojo) {
				FileUtils.writeStringToFile(new File(mojo.getRootDir(), "gatf-config.xml"), GatfTestCaseExecutorUtil.getConfigStrXml(gatfConfig), "UTF-8");
			}
		}
	}

	protected static void createConfigFileIfNotExists(GatfConfigToolMojoInt mojo, boolean isExecutor, Object config)
	{
		if(isExecutor && !new File(mojo.getRootDir(), "gatf-config.xml").exists() && !new File(mojo.getRootDir(), "gatf-config.json").exists()) {
			try {
				synchronized (mojo) {
					new File(mojo.getRootDir(), "gatf-config.xml").createNewFile();
				}
				GatfExecutorConfig gatfConfig = config==null?new GatfExecutorConfig():(GatfExecutorConfig)config;
				sanitizeAndSaveGatfConfig(gatfConfig, mojo, false);
			} catch (IOException e) {
			}
        } else if(!isExecutor && !new File(mojo.getRootDir(), "gatf-generator.xml").exists() && !new File(mojo.getRootDir(), "gatf-generator.json").exists()) {
			try {
				synchronized (mojo) {
					new File(mojo.getRootDir(), "gatf-generator.xml").createNewFile();
				}
				GatfConfiguration gatfConfig = config==null?new GatfConfiguration():(GatfConfiguration)config;
				synchronized (mojo) {
					FileUtils.writeStringToFile(new File(mojo.getRootDir(), "gatf-generator.xml"), GatfTestGeneratorUtil.getConfigStr(gatfConfig), "UTF-8");
				}
			} catch (IOException e) {
			}
        }
	}
	
	public static void main(String[] args) throws Exception {
		if(args.length>3 && args[0].equals("-configtool") && !args[1].trim().isEmpty() 
				&& !args[2].trim().isEmpty() && !args[3].trim().isEmpty()) {
			GatfConfigToolUtil mojo = new GatfConfigToolUtil();
			mojo.port = 9080;
			try {
				mojo.port = Integer.parseInt(args[1].trim());
			} catch (Exception e) {
			}
			mojo.ipAddress = args[2].trim();
			String cwd = System.getProperty("user.dir");
			if(!new File(cwd).isAbsolute()) {
				throw new RuntimeException("Invalid root dir, current Working directory should be an absolute path");
			}
			if(args[3].trim().equals(".")) {
				mojo.setRootDir(cwd);
			} else if(new File(args[3].trim()).exists()) {
				mojo.setRootDir(new File(args[3].trim()).getAbsolutePath());
			} else {
				if(new File(cwd + File.separatorChar + args[3].trim()).exists()) {
					mojo.setRootDir(new File(cwd + File.separatorChar + args[3].trim()).getAbsolutePath());
				} else {
					throw new RuntimeException("Invalid root dir");
				}
			}
			if(mojo.getRootDir().charAt(mojo.getRootDir().length()-1)=='/') {
				mojo.setRootDir(mojo.getRootDir().substring(0, mojo.getRootDir().length()-1));
			}
			mojo.execute();
		}
	}
	
	protected static void handleError(Throwable e, Response response, HttpStatus status) throws IOException
	{
		String configJson = e.getMessage()==null?ExceptionUtils.getStackTrace(e):e.getMessage();
		response.setContentLength(configJson.length());
        response.getWriter().write(configJson);
		response.setStatus(status==null?HttpStatus.INTERNAL_SERVER_ERROR_500:status);
		if(status==null)e.printStackTrace();
	}
	
	protected static void handleErrorJson(Throwable e, Response response, HttpStatus status) throws IOException
	{
		String configJson = e.getMessage()==null?ExceptionUtils.getStackTrace(e):e.getMessage();
		response.setContentType(MediaType.APPLICATION_XML);
		response.setContentLength(configJson.length());
        response.getWriter().write(configJson);
		response.setStatus(status==null?HttpStatus.INTERNAL_SERVER_ERROR_500:status);
		if(status==null)e.printStackTrace();
	}
	
	public static GatfExecutorConfig getGatfExecutorConfig(GatfConfigToolMojoInt mojo, GatfExecutorConfig config) throws Exception
	{
		createConfigFileIfNotExists(mojo, true, config);
		GatfExecutorConfig gatfConfig = config;
		if(gatfConfig == null)
		{
			synchronized (mojo) {
				if(new File(mojo.getRootDir(), "gatf-config.xml").exists()) {
					gatfConfig = GatfTestCaseExecutorUtil.getConfig(new FileInputStream(new File(mojo.getRootDir(), "gatf-config.xml")), true);
				} else {
					gatfConfig = GatfTestCaseExecutorUtil.getConfig(new FileInputStream(new File(mojo.getRootDir(), "gatf-config.json")), false);
				}
			}
		}
		if(gatfConfig == null) {
			throw new RuntimeException("Invalid configuration present");
		}
		sanitizeAndSaveGatfConfig(gatfConfig, mojo, config!=null);
		return gatfConfig;
	}
	
	protected static GatfConfiguration getGatfConfiguration(GatfConfigToolMojoInt mojo, GatfConfiguration config) throws Exception
	{
		createConfigFileIfNotExists(mojo, false, config);
		GatfConfiguration gatfConfig = config;
		if(gatfConfig == null)
		{
			gatfConfig = GatfTestGeneratorUtil.getConfig(new FileInputStream(new File(mojo.getRootDir(), "gatf-generator.xml")));
		}
		if(gatfConfig == null) {
			throw new RuntimeException("Invalid configuration present");
		}
		if(gatfConfig.getResourcepath()==null)
		{
			gatfConfig.setResourcepath(mojo.getRootDir() + "/generated");
		}
		synchronized (mojo) {
			FileUtils.writeStringToFile(new File(mojo.getRootDir(), "gatf-generator.xml"),  GatfTestGeneratorUtil.getConfigStr(gatfConfig), "UTF-8");
		}
		return gatfConfig;
	}
	
	public AcceptanceTestContext getContext() {
		return context;
	}

	public void setContext(AcceptanceTestContext context) {
		this.context = context;
	}

	public TestCase getAuthTestCase() {
		return authTestCase;
	}

	public void setAuthTestCase(TestCase authTestCase) {
		this.authTestCase = authTestCase;
	}
    
	public String getRootDir() {
	    return rootDir;
	}

	public void setRootDir(String rootDir) {
	    this.rootDir = rootDir;
	}
}

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.function.Function;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FalseFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.openqa.selenium.logging.LoggingPreferences;
import com.gatf.GatfPlugin;
import com.gatf.GatfPluginConfig;
import com.gatf.executor.dataprovider.GatfTestDataConfig;
import com.gatf.executor.dataprovider.GatfTestDataProvider;
import com.gatf.executor.dataprovider.GatfTestDataSource;
import com.gatf.executor.dataprovider.GatfTestDataSourceHook;
import com.gatf.executor.distributed.DistributedAcceptanceContext;
import com.gatf.executor.distributed.DistributedGatfListener;
import com.gatf.executor.distributed.DistributedGatfTester;
import com.gatf.executor.distributed.DistributedGatfTester.DistributedConnection;
import com.gatf.executor.distributed.DistributedTestContext;
import com.gatf.executor.distributed.DistributedTestStatus;
import com.gatf.executor.executor.TestCaseExecutorUtil;
import com.gatf.executor.finder.CSVFamilyTestCaseFinder.CSVTestCaseFinder;
import com.gatf.executor.finder.CSVFamilyTestCaseFinder.XLSTestCaseFinder;
import com.gatf.executor.finder.CSVFamilyTestCaseFinder.XLSXTestCaseFinder;
import com.gatf.executor.finder.JSONTestCaseFinder;
import com.gatf.executor.finder.TestCaseFinder;
import com.gatf.executor.finder.XMLTestCaseFinder;
import com.gatf.executor.report.LoadTestResource;
import com.gatf.executor.report.ReportHandler;
import com.gatf.executor.report.RuntimeReportUtil;
import com.gatf.executor.report.RuntimeReportUtil.LoadTestEntry;
import com.gatf.executor.report.TestCaseExecutionLogGenerator;
import com.gatf.executor.report.TestCaseReport;
import com.gatf.executor.report.TestCaseReport.TestFailureReason;
import com.gatf.executor.report.TestCaseReport.TestStatus;
import com.gatf.executor.report.TestExecutionPercentile;
import com.gatf.executor.report.TestSuiteStats;
import com.gatf.generator.core.ClassLoaderUtils;
import com.gatf.selenium.Command.GatfSelCodeParseError;
import com.gatf.selenium.SeleniumCodeGeneratorAndUtil;
import com.gatf.selenium.SeleniumDriverConfig;
import com.gatf.selenium.SeleniumTest;
import com.gatf.selenium.SeleniumTest.SeleniumTestResult;
import com.gatf.selenium.SeleniumTestSession;
import com.gatf.selenium.SeleniumTestSession.SeleniumResult;
import com.gatf.selenium.gatfjdb.GatfSelDebugger;
import com.gatf.ui.GatfConfigToolUtil;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

    }

    public void setConfigFile(String configFile) {
        this.configFile = configFile;
    }

    public void setHttpCompressionEnabled(boolean httpCompressionEnabled) {
        this.httpCompressionEnabled = httpCompressionEnabled;
    }

    public void setHttpConnectionTimeout(Integer httpConnectionTimeout) {
        this.httpConnectionTimeout = httpConnectionTimeout;
    }

    public void setHttpRequestTimeout(Integer httpRequestTimeout) {
        this.httpRequestTimeout = httpRequestTimeout;
    }

    public void setConcurrentUserSimulationNum(Integer concurrentUserSimulationNum) {
        this.concurrentUserSimulationNum = concurrentUserSimulationNum;
    }

    public void setTestDataConfigFile(String testDataConfigFile) {
        this.testDataConfigFile = testDataConfigFile;
    }

    public void setAuthDataProvider(String authDataProvider) {
        this.authDataProvider = authDataProvider;
    }

    public void setCompareEnabled(boolean compareEnabled) {
        this.compareEnabled = compareEnabled;
    }

    public void setTestCaseHooksPath(String[] testCaseHooksPath) {
        this.testCaseHooksPath = testCaseHooksPath;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public void setLoadTestingEnabled(boolean loadTestingEnabled) {
        this.loadTestingEnabled = loadTestingEnabled;
    }

    public void setLoadTestingTime(Long loadTestingTime) {
        this.loadTestingTime = loadTestingTime;
    }

    public void setConcurrentUserRampUpTime(Long concurrentUserRampUpTime) {
        this.concurrentUserRampUpTime = concurrentUserRampUpTime;
    }

    public void setLoadTestingReportSamples(Integer loadTestingReportSamples) {
        this.loadTestingReportSamples = loadTestingReportSamples;
    }

    public void setStartTime(Long startTime) {
        this.startTime = startTime;
    }
    
    public void setSelDebugger(boolean selDebugger) {
		this.selDebugger = selDebugger;
	}

	public void setContext(AcceptanceTestContext context) {
        this.context = context;
    }

    private void sortAndOrderTestCases(List<TestCase> allTestCases, GatfExecutorConfig configuration) {
        List<TestCase> pretestcases = new ArrayList<TestCase>();
        List<TestCase> posttestcases = new ArrayList<TestCase>();
        for (TestCase testCase : allTestCases) {
            if ((testCase.getUrl() != null && testCase.getUrl().equalsIgnoreCase(configuration.getAuthUrl())) || configuration.isSoapAuthTestCase(testCase)) {
                pretestcases.add(testCase);
            }
            testCase.setSimulationNumber(0);
        }
        if (configuration.isAuthEnabled() && pretestcases.size() > 0) {
            authTestCase = pretestcases.get(0);
        }
        for (TestCase testCase : allTestCases) {
            if ((testCase.getUrl() != null && !testCase.getUrl().equalsIgnoreCase(configuration.getAuthUrl()) && !configuration.isSoapAuthTestCase(testCase))
                    || (testCase.getUrl() == null && testCase.isSoapBase())) {
                posttestcases.add(testCase);
            }
        }

        Collections.sort(posttestcases, new Comparator<TestCase>() {
            public int compare(TestCase o1, TestCase o2) {
                return o1 == null ? (o2 == null ? 0 : Integer.MIN_VALUE) : (o2 == null ? Integer.MAX_VALUE : Integer.valueOf(o1.getSequence()).compareTo(o2.getSequence()));
            }
        });

        allTestCases.clear();
        allTestCases.addAll(pretestcases);
        allTestCases.addAll(posttestcases);

        int sequence = 0;
        for (TestCase testCase : allTestCases) {
            if (testCase.getSequence() <= 0)
                testCase.setSequence(sequence++);
            else
                sequence = testCase.getSequence() + 1;
        }
    }

    public List<TestCase> getAllTestCases(AcceptanceTestContext context, Set<String> relativeFileNames, List<String> targetFileNames) {
        List<TestCase> allTestCases = new ArrayList<TestCase>();
        File testCaseDirectory = context.getResourceFile(context.getGatfExecutorConfig().getTestCaseDir());

        TestCaseFinder finder = new XMLTestCaseFinder();
        finder.setTargetFileNames(targetFileNames);
        allTestCases.addAll(finder.findTestCases(testCaseDirectory, context, true, relativeFileNames));

        finder = new JSONTestCaseFinder();
        finder.setTargetFileNames(targetFileNames);
        allTestCases.addAll(finder.findTestCases(testCaseDirectory, context, true, relativeFileNames));


        finder = new CSVTestCaseFinder();
