eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverSessionsManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of driver-ui Show documentation
Show all versions of driver-ui Show documentation
Testerra test automation framework - driver-ui module
/*
* Testerra
*
* (C) 2020, Peter Lehmann, T-Systems Multimedia Solutions GmbH, Deutsche Telekom AG
*
* Deutsche Telekom AG and all other contributors /
* copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.webdrivermanager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import eu.tsystems.mms.tic.testframework.common.Testerra;
import eu.tsystems.mms.tic.testframework.events.ContextUpdateEvent;
import eu.tsystems.mms.tic.testframework.exceptions.SystemException;
import eu.tsystems.mms.tic.testframework.internal.metrics.MetricsController;
import eu.tsystems.mms.tic.testframework.internal.metrics.MetricsType;
import eu.tsystems.mms.tic.testframework.report.model.context.SessionContext;
import eu.tsystems.mms.tic.testframework.report.utils.IExecutionContextController;
import eu.tsystems.mms.tic.testframework.testing.WebDriverManagerProvider;
import eu.tsystems.mms.tic.testframework.useragents.BrowserInformation;
import eu.tsystems.mms.tic.testframework.utils.DefaultCapabilityUtils;
import eu.tsystems.mms.tic.testframework.webdriver.WebDriverFactory;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.SessionId;
import org.openqa.selenium.support.events.EventFiringDecorator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* @todo Migrate to {@link DefaultWebDriverManager}
*/
@Deprecated
public final class WebDriverSessionsManager implements WebDriverManagerProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(WebDriverSessionsManager.class);
private static final Map WEB_DRIVER_FACTORIES = new HashMap<>();
public static final Map SESSION_STARTUP_ERRORS = new LinkedHashMap<>();
private static final Map EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP = new ConcurrentHashMap<>();
private static final Map THREAD_SESSION_KEY_WEBDRIVER_MAP = new ConcurrentHashMap<>();
private static final Map WEBDRIVER_THREAD_ID_MAP = new ConcurrentHashMap<>();
static final Map WEBDRIVER_SESSIONS_CONTEXTS_MAP = new ConcurrentHashMap<>();
private static final Queue> beforeQuitActions = new ConcurrentLinkedQueue<>();
private static final Queue> afterQuitActions = new ConcurrentLinkedQueue<>();
private static final Queue> WEBDRIVER_STARTUP_HANDLERS = new ConcurrentLinkedQueue<>();
private static final String FULL_SESSION_KEY_SPLIT_MARKER = "___";
private static final Set webDriverFactories = Testerra.getInjector().getInstance(Key.get(new TypeLiteral>() {
}));
static final Queue> webDriverRequestConfigurators = new ConcurrentLinkedQueue<>();
private static final IExecutionContextController executionContextController = Testerra.getInjector().getInstance(IExecutionContextController.class);
private WebDriverSessionsManager() {
}
private static String getThreadSessionKey(String sessionKey) {
Thread currentThread = Thread.currentThread();
return currentThread.getId() + FULL_SESSION_KEY_SPLIT_MARKER + sessionKey;
}
static {
/**
* Getting multi binder set programmatically
* @see {https://groups.google.com/forum/#!topic/google-guice/EUnNStmrhOk}
*/
webDriverFactories.stream()
.sorted(Comparator.comparing(f -> f.getClass().getSimpleName()))
.forEach(webDriverFactory -> {
webDriverFactory.getSupportedBrowsers().forEach(browser -> WEB_DRIVER_FACTORIES.put(browser, webDriverFactory));
});
}
private static void storeWebDriverSession(WebDriver decoratedWebDriver, SessionContext sessionContext) {
WebDriverRequest webDriverRequest = sessionContext.getWebDriverRequest();
final String sessionKey = webDriverRequest.getSessionKey();
final String threadSessionKey = getThreadSessionKey(sessionKey);
THREAD_SESSION_KEY_WEBDRIVER_MAP.put(threadSessionKey, decoratedWebDriver);
final long threadId = Thread.currentThread().getId();
WEBDRIVER_THREAD_ID_MAP.put(decoratedWebDriver, threadId);
/*
store driver to session context relation
*/
WEBDRIVER_SESSIONS_CONTEXTS_MAP.put(decoratedWebDriver, sessionContext);
}
private static void unlinkFromThread(String sessionKey, WebDriver decoratedWebDriver) {
final String sessionIdentifier = createSessionIdentifier(decoratedWebDriver, sessionKey);
LOGGER.trace("Unlink from thread: " + sessionIdentifier);
String threadSessionKey = getThreadSessionKey(sessionKey);
THREAD_SESSION_KEY_WEBDRIVER_MAP.remove(threadSessionKey, decoratedWebDriver);
final long threadId = Thread.currentThread().getId();
WEBDRIVER_THREAD_ID_MAP.remove(decoratedWebDriver, threadId);
executionContextController.clearCurrentSessionContext();
/*
Log something about the session handling maps
*/
String msg = "Removed WebDriver session: " + sessionKey;
msg += "\n Remaining sessions: ";
int i = 0;
for (WebDriver webDriver : WEBDRIVER_THREAD_ID_MAP.keySet()) {
Long tid = WEBDRIVER_THREAD_ID_MAP.get(webDriver);
String key = getSessionKey(webDriver);
msg += "\n " + key + " in thread " + tid;
i++;
}
msg += "\n => " + i + " sessions (map: " + THREAD_SESSION_KEY_WEBDRIVER_MAP.size() + ")";
LOGGER.debug(msg);
}
/**
* Introduce an own webdriver object. Selenium session will be released in this case.
*
* @param webDriver .
* @param sessionKey .
*/
static void introduceWebDriver(final String sessionKey, WebDriver webDriver) {
if (!(webDriver instanceof RemoteWebDriver)) {
throw new IllegalArgumentException(
"The driver object of the argument must be an instance of RemoteWebDriver");
}
LOGGER.info("Introducing webdriver object");
UnspecificWebDriverRequest request = new UnspecificWebDriverRequest();
request.setSessionKey(sessionKey);
// create new session context
SessionContext sessionContext = new SessionContext(request);
VisualEventDriverListener visualListener = new VisualEventDriverListener();
WebDriver decoratedDriver = new EventFiringDecorator(
new EventLoggingDriverListener(),
visualListener
).decorate(webDriver);
visualListener.driver = decoratedDriver;
// store to method context
executionContextController.getCurrentMethodContext().ifPresent(methodContext -> {
methodContext.addSessionContext(sessionContext);
executionContextController.setCurrentSessionContext(sessionContext);
});
storeWebDriverSession(decoratedDriver, sessionContext);
}
public static void registerWebDriverBeforeShutdownHandler(Consumer beforeQuit) {
beforeQuitActions.add(beforeQuit);
}
public static void registerWebDriverAfterShutdownHandler(Consumer afterQuit) {
afterQuitActions.add(afterQuit);
}
public static void registerWebDriverAfterStartupHandler(Consumer afterStart) {
WEBDRIVER_STARTUP_HANDLERS.add(afterStart);
}
public static void unregisterWebDriverAfterStartupHandler(Consumer afterStart) {
WEBDRIVER_STARTUP_HANDLERS.remove(afterStart);
}
private static String createSessionIdentifier(WebDriver webDriver, String sessionKey) {
WebDriver originalDriver = WEB_DRIVER_MANAGER.getOriginalFromDecorated(webDriver);
return String.format("%s (sessionKey=%s)", originalDriver.getClass().getSimpleName(), sessionKey);
}
public static void shutdownWebDriver(WebDriver webDriver) {
WebDriver decoratedWebDriver = checkForWrappedWebDriver(webDriver);
String sessionKey = getSessionKey(decoratedWebDriver);
String sessionIdentifier = createSessionIdentifier(decoratedWebDriver, sessionKey);
beforeQuitActions.forEach(webDriverConsumer -> {
try {
LOGGER.trace("Call before shutdown handler");
webDriverConsumer.accept(decoratedWebDriver);
} catch (Exception e) {
LOGGER.error("Failed executing before shutdown handler", e);
}
});
LOGGER.info("Shutting down " + sessionIdentifier);
WebDriverManagerUtils.quitWebDriverSession(decoratedWebDriver);
afterQuitActions.forEach(webDriverConsumer -> {
try {
LOGGER.trace("Call after shutdown handler");
webDriverConsumer.accept(decoratedWebDriver);
} catch (Exception e) {
LOGGER.error("Failed executing after shutdown handler", e);
}
});
unlinkFromThread(sessionKey, decoratedWebDriver);
if (WEBDRIVER_SESSIONS_CONTEXTS_MAP.get(decoratedWebDriver) != null) {
WEBDRIVER_SESSIONS_CONTEXTS_MAP.get(decoratedWebDriver).updateEndTime(new Date());
}
WEBDRIVER_SESSIONS_CONTEXTS_MAP.remove(decoratedWebDriver);
if (sessionKey.startsWith(SessionContext.EXCLUSIVE_PREFIX)) {
EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.remove(sessionKey);
}
LOGGER.debug("Shut down: " + sessionIdentifier);
}
static void shutdownAllThreadSessions() {
getWebDriversFromCurrentThread().forEach(WebDriverSessionsManager::shutdownWebDriver);
}
public static Stream getWebDriversFromCurrentThread() {
long threadId = Thread.currentThread().getId();
return getWebDriversFromThread(threadId);
}
public static Stream readExclusiveWebDrivers() {
return EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.values().stream();
}
static void shutdownAllSessions() {
THREAD_SESSION_KEY_WEBDRIVER_MAP.values().forEach(WebDriverSessionsManager::shutdownWebDriver);
EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.values().forEach(WebDriverSessionsManager::shutdownWebDriver);
// This should not be necessary but we do it anyway
// THREAD_SESSION_KEY_WEBDRIVER_MAP.clear();
// WEBDRIVER_THREAD_ID_MAP.clear();
// EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.clear();
// WEBDRIVER_SESSIONS_CONTEXTS_MAP.clear();
}
/**
* Returns true if any session is active.
*
* @return .
*/
static boolean hasAnySessionActive() {
return hasSessionActiveInThisThread();
}
static boolean hasSessionActiveInThisThread() {
long threadId = Thread.currentThread().getId();
return getWebDriversFromThread(threadId).findAny().isPresent();
}
static synchronized String makeSessionExclusive(final WebDriver webDriver) {
WebDriver decoratedWebDriver = checkForWrappedWebDriver(webDriver);
if (EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.containsValue(decoratedWebDriver)) {
LOGGER.error("Session already set exclusive.");
return null;
}
SessionContext sessionContext = getSessionContext(decoratedWebDriver).get();
String sessionKey = sessionContext.getSessionKey();
unlinkFromThread(sessionKey, decoratedWebDriver);
/*
Add session to exclusive map.
*/
String exclusiveSessionKey = SessionContext.EXCLUSIVE_PREFIX + UUID.randomUUID().toString();
EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.put(exclusiveSessionKey, decoratedWebDriver);
/*
introduce session context to execution context
*/
sessionContext.setSessionKey(exclusiveSessionKey);
sessionContext.getWebDriverRequest().setShutdownAfterTest(false);
sessionContext.getWebDriverRequest().setShutdownAfterTestFailed(false);
executionContextController.getExecutionContext().addExclusiveSessionContext(sessionContext);
Testerra.getEventBus().post(new ContextUpdateEvent().setContext(sessionContext));
LOGGER.info("Promoted " + createSessionIdentifier(webDriver, sessionKey) + " to " + createSessionIdentifier(webDriver, exclusiveSessionKey));
return exclusiveSessionKey;
}
public static void shutdownSessionKey(final String key) {
if (EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.containsKey(key)) {
shutdownWebDriver(EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.get(key));
EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.remove(key);
} else if (THREAD_SESSION_KEY_WEBDRIVER_MAP.containsKey(key)) {
shutdownWebDriver(THREAD_SESSION_KEY_WEBDRIVER_MAP.get(key));
}
}
/**
* @deprecated Use {@link #getSessionContext(WebDriver)} instead
*/
static String getSessionKey(WebDriver webDriver) {
return getSessionContext(webDriver).map(SessionContext::getSessionKey).orElse("no session");
}
static Stream getWebDriversFromThread(final long threadId) {
return WEBDRIVER_THREAD_ID_MAP.entrySet().stream().filter(entry -> entry.getValue() == threadId).map(Map.Entry::getKey);
}
public static WebDriver getWebDriver(final WebDriverRequest webDriverRequest) {
String sessionKey = webDriverRequest.getSessionKey();
WebDriver existingWebDriver = null;
/*
Check for exclusive session
*/
if (sessionKey.startsWith(SessionContext.EXCLUSIVE_PREFIX)) {
// returning exclusive session
if (EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.containsKey(sessionKey)) {
existingWebDriver = EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.get(sessionKey);
} else {
throw new SystemException("No Session for key: " + sessionKey);
}
} else {
String fullSessionKey = getThreadSessionKey(sessionKey);
existingWebDriver = THREAD_SESSION_KEY_WEBDRIVER_MAP.get(fullSessionKey);
}
/*
session already exists?
*/
if (existingWebDriver != null) {
/*
Link sessionContext to methodContext if not exist
e.g. Session was created in setup method and reused in test method or exclusive session is used in current test.
*/
if (sessionKey.startsWith(SessionContext.EXCLUSIVE_PREFIX)) {
// Link an exclusive sessionContext
executionContextController.getExecutionContext().readExclusiveSessionContexts()
.filter(sessionContext -> sessionContext.getSessionKey().equals(sessionKey))
.findFirst()
.ifPresent(sessionContext ->
executionContextController.getCurrentMethodContext().ifPresent(currentMethodContext ->
currentMethodContext.addSessionContext(sessionContext)
));
} else {
// Link a normal sessionContext
executionContextController.getCurrentSessionContext().ifPresent(currentSessionContext ->
executionContextController.getCurrentMethodContext().ifPresent(currentMethodContext ->
currentMethodContext.addSessionContext(currentSessionContext)
));
}
return existingWebDriver;
}
/*
**** STARTING NEW SESSION ****
*/
/*
decide which session manager to use
*/
String browser = webDriverRequest.getBrowser();
if (StringUtils.isBlank(browser)) {
throw new SystemException(String.format("No browser configured. Please define one in %s.setBrowser() or property '%s'", WebDriverRequest.class.getSimpleName(),
IWebDriverManager.Properties.BROWSER_SETTING));
}
if (WEB_DRIVER_FACTORIES.containsKey(browser)) {
WebDriverFactory webDriverFactory = WEB_DRIVER_FACTORIES.get(browser);
// Catch all existing browser caps and add them to specific browser options
final WebDriverRequest finalWebDriverRequest = webDriverFactory.prepareWebDriverRequest(webDriverRequest);
// Update webDriverRequest with caps of external or global defined request configurators
webDriverRequestConfigurators.forEach(handler -> handler.accept(finalWebDriverRequest));
// Create session context and link to method context
SessionContext sessionContext = new SessionContext(finalWebDriverRequest);
executionContextController.getCurrentMethodContext().ifPresent(methodContext -> {
methodContext.addSessionContext(sessionContext);
});
executionContextController.setCurrentSessionContext(sessionContext);
logRequest(finalWebDriverRequest, sessionContext);
/*
setup new session
*/
MetricsController metricsController = Testerra.getInjector().getInstance(MetricsController.class);
metricsController.start(sessionContext, MetricsType.SESSION_LOAD);
WebDriver newRawWebDriver = webDriverFactory.createWebDriver(finalWebDriverRequest, sessionContext);
metricsController.stop(sessionContext, MetricsType.SESSION_LOAD);
if (!sessionContext.getActualBrowserName().isPresent()) {
BrowserInformation browserInformation = WebDriverManagerUtils.getBrowserInformation(newRawWebDriver);
sessionContext.setUserAgent(browserInformation.getUserAgent());
sessionContext.setActualBrowserName(browserInformation.getBrowserName());
sessionContext.setActualBrowserVersion(browserInformation.getBrowserVersion());
}
if (newRawWebDriver instanceof RemoteWebDriver) {
SessionId sessionId = ((RemoteWebDriver) newRawWebDriver).getSessionId();
sessionContext.setRemoteSessionId(sessionId.toString());
} else {
sessionContext.setRemoteSessionId(sessionContext.getId());
}
Duration diff = metricsController.getDuration(sessionContext, MetricsType.SESSION_LOAD);
LOGGER.info(String.format(
"Started %s (sessionKey=%s, node=%s, userAgent=%s) in %02d:%02d.%03d",
newRawWebDriver.getClass().getSimpleName(),
sessionContext.getSessionKey(),
sessionContext.getNodeUrl().map(Object::toString).orElse("(unknown)"),
sessionContext.getActualBrowserName().orElse("(unknown)") + ":" + sessionContext.getActualBrowserVersion().orElse("(unknown)"),
diff.toMinutesPart(), diff.toSecondsPart(), diff.toMillisPart()
));
WebDriver decoratedDriver = webDriverFactory.setupNewWebDriverSession(newRawWebDriver, sessionContext);
storeWebDriverSession(decoratedDriver, sessionContext);
Testerra.getEventBus().post(new ContextUpdateEvent().setContext(sessionContext));
String sessionIdentifier = createSessionIdentifier(newRawWebDriver, sessionKey);
/*
run the handlers
*/
WEBDRIVER_STARTUP_HANDLERS.forEach(webDriverConsumer -> {
try {
webDriverConsumer.accept(decoratedDriver);
} catch (Exception e) {
LOGGER.error("Failed executing handler after starting up " + sessionIdentifier, e);
}
});
return decoratedDriver;
} else {
throw new SystemException(String.format("Your requested browser %s is unknown. Please check your configuration.", browser));
}
}
private static void logRequest(WebDriverRequest request, SessionContext sessionContext) {
Map cleanedCapsMap = new DefaultCapabilityUtils().clean(request.getCapabilities());
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
LOGGER.info(String.format(
"New %s (sessionKey=%s, server=%s) with capabilities:\n%s",
request.getClass().getSimpleName(),
sessionContext.getSessionKey(),
(request.getServerUrl().isPresent() ? request.getServerUrl().get() : "local"),
gson.toJson(cleanedCapsMap)
));
LOGGER.debug(String.format("Starting (sessionKey=%s) here", sessionContext.getSessionKey()), new Throwable());
}
@Deprecated
static void registerWebDriverFactory(WebDriverFactory webDriverFactory, String... browsers) {
LOGGER.debug("Register " + webDriverFactory.getClass().getSimpleName() + " for browsers " + String.join(", ", browsers));
for (String browser : browsers) {
WEB_DRIVER_FACTORIES.put(browser, webDriverFactory);
}
}
private static WebDriver checkForWrappedWebDriver(WebDriver webDriver) {
if (!(webDriver instanceof WebDriver)) {
throw new IllegalArgumentException(webDriver.getClass().getSimpleName() + " is no instance of " + WebDriver.class.getSimpleName());
}
return webDriver;
}
public static boolean isExclusiveSession(WebDriver webDriver) {
webDriver = checkForWrappedWebDriver(webDriver);
return EXCLUSIVE_SESSION_KEY_WEBDRIVER_MAP.containsValue(webDriver);
}
public static Optional getSessionContext(WebDriver webDriver) {
webDriver = checkForWrappedWebDriver(webDriver);
return Optional.ofNullable(WEBDRIVER_SESSIONS_CONTEXTS_MAP.get(webDriver));
}
public static Optional getWebDriver(SessionContext sessionContext) {
return WEBDRIVER_SESSIONS_CONTEXTS_MAP.entrySet().stream()
.filter(entry -> entry.getValue() == sessionContext)
.map(Map.Entry::getKey)
.findFirst();
}
public static Optional getRequestedBrowser(WebDriver webDriver) {
return getSessionContext(webDriver).map(SessionContext::getWebDriverRequest).map(WebDriverRequest::getBrowser);
}
public static WebDriverFactory getWebDriverFactory(String browser) {
if (WEB_DRIVER_FACTORIES.containsKey(browser)) {
return WEB_DRIVER_FACTORIES.get(browser);
} else {
throw new RuntimeException("No " + WebDriverFactory.class.getSimpleName() + " registered for browser: " + browser);
}
}
public static Stream readSessionContexts() {
return WEBDRIVER_SESSIONS_CONTEXTS_MAP.values().stream();
}
public static Stream readWebDrivers() {
return WEBDRIVER_SESSIONS_CONTEXTS_MAP.keySet().stream();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy