com.seleniumtests.core.SeleniumTestsContextManager Maven / Gradle / Ivy
/**
* Orignal work: Copyright 2015 www.seleniumtests.com
* Modified work: Copyright 2016 www.infotel.com
* Copyright 2017-2019 B.Hecquet
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seleniumtests.core;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.testng.ISuite;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.xml.XmlSuite;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.seleniumtests.core.runner.SeleniumRobotTestListener;
import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.driver.TestType;
import com.seleniumtests.util.TestConfigurationParser;
import com.seleniumtests.util.logging.SeleniumRobotLogger;
/**
* SeleniumTestsContextManager provides ways to manage global context, thread context and test level context.
*/
public class SeleniumTestsContextManager {
private static final Logger logger = SeleniumRobotLogger.getLogger(SeleniumTestsContext.class);
private static String rootPath;
private static String dataPath;
private static String cachePath;
private static String appDataPath;
private static String featuresPath;
private static String configPath;
private static String applicationName;
private static String applicationNameWithVersion;
private static String applicationVersion;
private static String coreVersion;
private static Boolean deployedMode;
public static final String DATA_FOLDER_NAME = "data";
public static final String CACHE_FOLDER_NAME = "cache";
public static final String SELENIUM_VERSION = "3.14.0";
// global level context
private static SeleniumTestsContext globalContext;
// thread level SeleniumTestsContext
private static ThreadLocal threadLocalContext = new ThreadLocal<>();
// relationship between a SeleniumTestsContext and a TestNG test ( tag in XML)
private static Map testContext = Collections.synchronizedMap(new HashMap());
// relationship between a SeleniumTestsContext and a test class
private static Map classContext = Collections.synchronizedMap(new HashMap());
// relationship between a SeleniumTestsContext and a test method
private static Map methodContext = Collections.synchronizedMap(new HashMap());
private SeleniumTestsContextManager() {
// As a utility class, it is not meant to be instantiated.
}
public static SeleniumTestsContext getGlobalContext() {
if (globalContext == null) {
throw new ConfigurationException("SeleniumTestsContextManager.getGlobalContext() MUST be called after SeleniumTestsContextManager.initGlobalContext()");
}
return globalContext;
}
public static SeleniumTestsContext getThreadContext() {
if (threadLocalContext.get() == null) {
throw new ConfigurationException("SeleniumTestsContextManager.getThreadContext() MUST be called after SeleniumTestsContextManager.initThreadContext()");
}
return threadLocalContext.get();
}
public static void initGlobalContext(ISuite suiteContext) {
if (suiteContext != null ) {
generateApplicationPath(suiteContext.getXmlSuite());
}
ITestContext testNGCtx = new DefaultTestNGContext(suiteContext);
ITestContext newTestNGCtx = getContextFromConfigFile(testNGCtx);
globalContext = new SeleniumTestsContext(newTestNGCtx);
}
public static void initGlobalContext(ITestContext testNGCtx) {
// generate all paths used by test application
if (testNGCtx != null && testNGCtx.getCurrentXmlTest() != null) {
generateApplicationPath(testNGCtx.getCurrentXmlTest().getSuite());
}
ITestContext newTestNGCtx = getContextFromConfigFile(testNGCtx);
globalContext = new SeleniumTestsContext(newTestNGCtx);
}
private static String getKeyForMethod(ITestContext testNGCtx, String className, String methodName) {
return testNGCtx.getName() + "_" + className + "." + methodName;
}
private static String getKeyForClass(ITestContext testNGCtx, String className) {
return testNGCtx.getName() + "_" + className;
}
public static SeleniumTestsContext storeTestContext(ITestContext testNGCtx) {
SeleniumTestsContext tstContext = new SeleniumTestsContext(getContextFromConfigFile(testNGCtx));
setTestContext(testNGCtx, tstContext);
return tstContext;
}
public static void setTestContext(ITestContext testNGCtx, SeleniumTestsContext tstContext) {
testContext.put(testNGCtx.getName(), tstContext);
}
/**
* Returns test context if it exists. Else, create a new one
* @param testNGCtx
* @return
*/
public static SeleniumTestsContext getTestContext(ITestContext testNGCtx) {
if (testContext.get(testNGCtx.getName()) != null) {
return testContext.get(testNGCtx.getName());
} else {
return new SeleniumTestsContext(getContextFromConfigFile(testNGCtx));
}
}
public static SeleniumTestsContext storeClassContext(ITestContext testNGCtx, String className) {
// unicity is on test + class because a class could be executed by 2 tests at the same time (e.g: ParallelMode.TESTS)
String key = getKeyForClass(testNGCtx, className);
SeleniumTestsContext clsContext;
if (classContext.get(key) != null) {
return classContext.get(key);
} else if (testContext.get(testNGCtx.getName()) != null) {
clsContext = new SeleniumTestsContext(testContext.get(testNGCtx.getName()));
} else {
clsContext = new SeleniumTestsContext(getContextFromConfigFile(testNGCtx));
}
setClassContext(testNGCtx, className, clsContext);;
return clsContext;
}
public static void setClassContext(ITestContext testNGCtx, String className, SeleniumTestsContext clsContext) {
classContext.put(getKeyForClass(testNGCtx, className), clsContext);
}
/**
* Returns class context from test NG context and class name
* @param testNGCtx
* @param className
* @return
*/
public static SeleniumTestsContext getClassContext(ITestContext testNGCtx, String className) {
String keyClass = getKeyForClass(testNGCtx, className);
if (classContext.get(keyClass) != null) {
return classContext.get(keyClass);
} else if (testContext.get(testNGCtx.getName()) != null) {
return testContext.get(testNGCtx.getName());
} else {
return new SeleniumTestsContext(getContextFromConfigFile(testNGCtx));
}
}
public static SeleniumTestsContext storeMethodContext(ITestContext testNGCtx, String className, String methodName) {
// unicity is on test + class + method because the same method name may exist in several classes or 2 testNG tests could execute the same test methods
SeleniumTestsContext mtdContext = getMethodContext(testNGCtx, className, methodName, true);
setMethodContext(testNGCtx, className, methodName, mtdContext);
return mtdContext;
}
public static void setMethodContext(ITestContext testNGCtx, String className, String methodName, SeleniumTestsContext mtdContext) {
methodContext.put(getKeyForMethod(testNGCtx, className, methodName), mtdContext);
}
/**
* Get the context that will be used for the test method
* Search for (by order) a specific method context, class context, test context. If none is found, create one
* If found, returns a copy of the context if 'createCopy' is true
* @param testNGCtx
* @param className
* @param methodName
* @param createCopy if true and we find class or test context, returns a copy
* @return
*/
public static SeleniumTestsContext getMethodContext(ITestContext testNGCtx, String className, String methodName, boolean createCopy) {
// unicity is on test + class + method because the same method name may exist in several classes or 2 testNG tests could execute the same test methods
String keyMethod = getKeyForMethod(testNGCtx, className, methodName);
String keyClass = getKeyForClass(testNGCtx, className);
if (methodContext.get(keyMethod) != null) {
return methodContext.get(keyMethod);
} else if (classContext.get(keyClass) != null) {
return createCopy ? new SeleniumTestsContext(classContext.get(keyClass)): classContext.get(keyClass);
} else if (testContext.get(testNGCtx.getName()) != null) {
return createCopy ? new SeleniumTestsContext(testContext.get(testNGCtx.getName())): testContext.get(testNGCtx.getName());
} else {
return new SeleniumTestsContext(getContextFromConfigFile(testNGCtx));
}
}
public static Map getTestContext() {
return testContext;
}
public static Map getClassContext() {
return classContext;
}
public static Map getMethodContext() {
return methodContext;
}
/**
* Get parameters from configuration file.
* @param iTestContext
* @param configParser
* @return Map with parameters from the given file.
*/
private static Map getParametersFromConfigFile(final ITestContext iTestContext,
TestConfigurationParser configParser) {
Map parameters;
// get parameters
if (iTestContext.getCurrentXmlTest() != null) {
parameters = iTestContext.getCurrentXmlTest().getSuite().getParameters();
} else {
parameters = iTestContext.getSuite().getXmlSuite().getParameters();
}
// insert parameters
for (Node node: configParser.getParameterNodes()) {
parameters.put(node.getAttributes().getNamedItem("name").getNodeValue(),
node.getAttributes().getNamedItem("value").getNodeValue());
}
return parameters;
}
/**
*
* @param iTestContext
* @return run mode corresponding to the given test context
*/
private static String setRunMode(final ITestContext iTestContext){
String runMode;
if (System.getProperty(SeleniumTestsContext.RUN_MODE) != null) {
runMode = System.getProperty(SeleniumTestsContext.RUN_MODE);
} else if (iTestContext.getSuite().getParameter(SeleniumTestsContext.RUN_MODE) != null) {
runMode = iTestContext.getSuite().getParameter(SeleniumTestsContext.RUN_MODE);
} else {
runMode = "LOCAL";
}
return runMode;
}
/**
* Get service parameters from configuration file.
* Only the parameters corresponding to the defined runMode.
* @param parameters
* @param runMode
* @param iTestContext
* @param configParser
* @return Map with service parameters from the given file.
*/
private static Map getServiceParameters(Map parameters, String runMode, TestConfigurationParser configParser) {
for (Node node: configParser.getServiceNodes()) {
if (node.getAttributes().getNamedItem("name").getNodeValue().equalsIgnoreCase(runMode)) {
NodeList nList = node.getChildNodes();
for (int i = 0; i < nList.getLength(); i++ ) {
Node paramNode = nList.item(i);
if ("parameter".equals(paramNode.getNodeName())) {
parameters.put(paramNode.getAttributes().getNamedItem("name").getNodeValue(),
paramNode.getAttributes().getNamedItem("value").getNodeValue());
}
}
}
}
return parameters;
}
/**
* Set the parameters for the test with parameters from XML configuration file.
* @param iTestContext
* @return iTestContext set with parameters from external config file
*/
public static ITestContext getContextFromConfigFile(final ITestContext iTestContext) {
if (iTestContext != null
&& iTestContext.getSuite().getParameter(SeleniumTestsContext.TEST_CONFIGURATION) != null) {
File suiteFile = new File(iTestContext.getSuite().getXmlSuite().getFileName());
String configFile = suiteFile.getPath().replace(suiteFile.getName(), "") + iTestContext.getSuite().getParameter(SeleniumTestsContext.TEST_CONFIGURATION);
TestConfigurationParser configParser = new TestConfigurationParser(configFile);
Map parameters = getParametersFromConfigFile(iTestContext, configParser);
// get configuration for services.
String runMode = setRunMode(iTestContext);
parameters = getServiceParameters(parameters, runMode, configParser);
//
parameters.put(SeleniumTestsContext.DEVICE_LIST, configParser.getDeviceNodesAsJson());
if (iTestContext.getCurrentXmlTest() != null) {
// iTestContext is a test context provided by TestNG
iTestContext.getCurrentXmlTest().getSuite().setParameters(parameters);
} else {
// iTestContext is a DefaultTestNGContext
iTestContext.getSuite().getXmlSuite().setParameters(parameters);
}
}
return iTestContext;
}
public static void initThreadContext() {
initThreadContext(globalContext.getTestNGContext(), null, null, null);
}
public static void initThreadContext(ITestContext testNGCtx, String testName, String className, ITestResult testResult) {
ITestContext newTestNGCtx = getContextFromConfigFile(testNGCtx);
SeleniumTestsContext seleniumTestsCtx = new SeleniumTestsContext(newTestNGCtx);
threadLocalContext.set(seleniumTestsCtx);
// update some values after init. These init call the thread context previously created
if (testResult != null) {
seleniumTestsCtx.configureContext(testResult);
}
}
/**
* Update the current thread context without recreating it
* This is a correction for issue #94
* @param testName
*/
public static void updateThreadContext(ITestResult testResult) {
if (threadLocalContext.get() != null) {
threadLocalContext.get().configureContext(testResult);
}
}
public static void setGlobalContext(final SeleniumTestsContext ctx) {
globalContext = ctx;
}
public static void setThreadContext(final SeleniumTestsContext ctx) {
threadLocalContext.set(ctx);
}
/**
* get SR context stored in test result if it exists. Else, create a new one (happens when a test method has been skipped for example)
* called from reporters only
* @param testNGCtx
* @param testName
* @param testResult
*/
public static void setThreadContextFromTestResult(ITestContext testNGCtx, String testName, String className, ITestResult testResult) {
if (testResult == null) {
throw new ConfigurationException("Cannot set context from testResult as it is null");
}
if (testResult.getAttribute(SeleniumRobotTestListener.TEST_CONTEXT) != null) {
setThreadContext((SeleniumTestsContext)testResult.getAttribute(SeleniumRobotTestListener.TEST_CONTEXT));
} else {
logger.error("Result did not contain thread context, initializing a new one");
initThreadContext(testNGCtx, testName, className, testResult);
testResult.setAttribute(SeleniumRobotTestListener.TEST_CONTEXT, getThreadContext());
}
}
public static void removeThreadContext() {
threadLocalContext.remove();
}
/**
* Build the root path of STF
* method for guessing it is different if we are inside a jar (built mode) or in development
* @param clazz
* @param path
* @return
*/
public static void getPathFromClass(Class> clazz, StringBuilder path) {
try {
String url = URLDecoder.decode(clazz.getProtectionDomain().getCodeSource().getLocation().getFile(), "UTF-8" );
if (url.endsWith(".jar")) {
path.append((new File(url).getParentFile().getAbsoluteFile().toString() + "/").replace(File.separator, "/"));
deployedMode = true;
} else {
path.append((new File(url).getParentFile().getParentFile().getAbsoluteFile().toString() + "/").replace(File.separator, "/"));
deployedMode = false;
}
} catch (UnsupportedEncodingException e) {
logger.error(e);
}
}
/**
* reads -version.txt file which should be available for all application
* It's generated by maven antrun task
* From the version found, generate an application version by removing SNAPSHOT (if any) and trailing build version
* 1.2.0-SNAPSHOT => 1.2
* @return
*/
private static String readApplicationVersion() {
return readApplicationVersion(String.format("%s-version.txt", applicationName));
}
private static String readCoreVersion() {
return readApplicationVersion("core-version.txt");
}
public static String readApplicationVersion(String resourceName) {
try {
String version = IOUtils.toString(SeleniumTestsContextManager.class.getClassLoader().getResourceAsStream(resourceName));
if (version.isEmpty()) {
return "0.0";
}
String[] versionParts = version.split("\\.", 3);
try {
return String.format("%s.%s", versionParts[0], versionParts[1]);
} catch (IndexOutOfBoundsException e) {
return versionParts[0];
}
} catch (IOException | NullPointerException e) {
logger.warn("application version has not been read. It may have not been generated. Execute maven build before launching test");
return "0.0";
}
}
/**
* Generate all applications path
* - root
* - data
* - config
* @param xmlSuite
*/
public static void generateApplicationPath(XmlSuite xmlSuite) {
StringBuilder path = new StringBuilder();
getPathFromClass(SeleniumTestsContext.class, path);
rootPath = path.toString();
// in case launching unit test from eclipse, a temp file is generated outside the standard folder structure
// APPLICATION_NAME and DATA_PATH must be rewritten
// application name is get from the testNG file path (the subdir name after 'data')
try {
applicationNameWithVersion = xmlSuite.getFileName().replace(File.separator, "/").split("/"+ DATA_FOLDER_NAME + "/")[1].split("/")[0];
Pattern appVersion = Pattern.compile("([a-zA-Z0-9-]+)(_.*)?");
Matcher appVersionMatcher = appVersion.matcher(applicationNameWithVersion);
if (appVersionMatcher.matches()) {
applicationName = appVersionMatcher.group(1);
} else {
applicationName = applicationNameWithVersion;
}
dataPath = xmlSuite.getFileName().replace(File.separator, "/").split("/"+ DATA_FOLDER_NAME + "/")[0] + "/" + DATA_FOLDER_NAME + "/";
} catch (IndexOutOfBoundsException | NullPointerException e) {
applicationName = "core";
applicationNameWithVersion = "core";
dataPath = Paths.get(rootPath, DATA_FOLDER_NAME).toString() + "/";
}
featuresPath = Paths.get(dataPath, applicationNameWithVersion, "features").toString();
configPath = Paths.get(dataPath, applicationNameWithVersion, "config").toString();
appDataPath = Paths.get(dataPath, applicationNameWithVersion).toString();
cachePath = Paths.get(rootPath, CACHE_FOLDER_NAME, applicationNameWithVersion).toString();
if (applicationVersion == null) {
applicationVersion = readApplicationVersion();
}
if (coreVersion == null) {
coreVersion = readCoreVersion();
}
// create data folder if it does not exist (it should already exist)
if (!new File(dataPath).isDirectory()) {
new File(dataPath).mkdirs();
}
if (!new File(appDataPath).isDirectory()) {
new File(appDataPath).mkdirs();
}
if (!new File(cachePath).isDirectory()) {
new File(cachePath).mkdirs();
}
}
/**
* Returns application root path
* @return
*/
public static String getRootPath() {
return rootPath;
}
/**
* Returns location of feature files
* @return
*/
public static String getFeaturePath() {
return featuresPath;
}
public static String getApplicationName() {
return applicationName;
}
public static String getApplicationNameWithVersion() {
return applicationNameWithVersion;
}
public static String getApplicationVersion() {
return applicationVersion;
}
public static String getCoreVersion() {
return coreVersion;
}
/**
* Returns location of config files
* @return
*/
public static String getConfigPath() {
return configPath;
}
/**
* Returns location of data folder
* @return
*/
public static String getDataPath() {
return dataPath;
}
/**
* Returns location of data folder for this application
* @return
*/
public static String getApplicationDataPath() {
return appDataPath;
}
/**
* Returns location of cache folder for this application
* @return
*/
public static String getCachePath() {
return cachePath;
}
public static Boolean getDeployedMode() {
if (deployedMode == null) {
getPathFromClass(SeleniumTestsContext.class, new StringBuilder());
}
return deployedMode;
}
public static boolean isWebTest() {
return getThreadContext().getTestType().family().equals(TestType.WEB);
}
public static boolean isMobileTest() {
return getThreadContext().getTestType().isMobile();
}
public static boolean isNonGuiTest() {
return getThreadContext().getTestType().family().equals(TestType.NON_GUI);
}
public static boolean isAppTest() {
return getThreadContext().getTestType().family().equals(TestType.APP);
}
public static boolean isMobileAppTest() {
return getThreadContext().getTestType().family().equals(TestType.APP) && getThreadContext().getTestType().isMobile();
}
public static boolean isMobileWebTest() {
return getThreadContext().getTestType().family().equals(TestType.WEB) && getThreadContext().getTestType().isMobile();
}
public static boolean isDesktopAppTest() {
return getThreadContext().getTestType().family().equals(TestType.APP) && !getThreadContext().getTestType().isMobile();
}
public static boolean isDesktopWebTest() {
return getThreadContext().getTestType().family().equals(TestType.WEB) && !getThreadContext().getTestType().isMobile();
}
public static String getSuiteName() {
return getGlobalContext().getTestNGContext().getSuite().getName();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy