Skip to content

Commit 99e2797

Browse files
author
Mat Walker
committed
Added error handling for when WebDriver calls are made when the connection has been lost (IE. killed!). Didnt want a ucky null objectref error
1 parent 784e914 commit 99e2797

File tree

1 file changed

+78
-27
lines changed

1 file changed

+78
-27
lines changed

src/main/java/TeamControlium/Controlium/SeleniumDriver.java

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,17 @@
66
import org.apache.commons.io.FilenameUtils;
77
import org.apache.commons.lang3.time.StopWatch;
88
import org.junit.jupiter.api.Assertions;
9-
import org.openqa.selenium.By;
10-
import org.openqa.selenium.JavascriptExecutor;
11-
import org.openqa.selenium.WebDriver;
12-
import org.openqa.selenium.WebElement;
13-
import org.openqa.selenium.chrome.ChromeDriver;
14-
import org.openqa.selenium.chrome.ChromeDriverService;
15-
import org.openqa.selenium.chrome.ChromeOptions;
16-
import org.openqa.selenium.edge.EdgeDriver;
17-
import org.openqa.selenium.edge.EdgeDriverService;
18-
import org.openqa.selenium.edge.EdgeOptions;
19-
import org.openqa.selenium.ie.InternetExplorerDriver;
20-
import org.openqa.selenium.ie.InternetExplorerDriverLogLevel;
21-
import org.openqa.selenium.ie.InternetExplorerDriverService;
22-
import org.openqa.selenium.ie.InternetExplorerOptions;
239

10+
import javax.management.RuntimeMBeanException;
2411
import javax.swing.text.Utilities;
2512
import java.io.BufferedReader;
2613
import java.io.File;
2714
import java.io.InputStreamReader;
15+
import java.lang.annotation.ElementType;
16+
import java.lang.annotation.Retention;
17+
import java.lang.annotation.RetentionPolicy;
18+
import java.lang.annotation.Target;
19+
import java.net.ConnectException;
2820
import java.nio.file.Files;
2921
import java.nio.file.Path;
3022
import java.nio.file.Paths;
@@ -34,6 +26,17 @@
3426
import java.util.List;
3527
import java.util.concurrent.TimeUnit;
3628

29+
import org.openqa.selenium.*;
30+
import org.openqa.selenium.chrome.ChromeDriver;
31+
import org.openqa.selenium.chrome.ChromeDriverService;
32+
import org.openqa.selenium.chrome.ChromeOptions;
33+
import org.openqa.selenium.edge.EdgeDriver;
34+
import org.openqa.selenium.edge.EdgeDriverService;
35+
import org.openqa.selenium.edge.EdgeOptions;
36+
import org.openqa.selenium.ie.InternetExplorerDriver;
37+
import org.openqa.selenium.ie.InternetExplorerDriverLogLevel;
38+
import org.openqa.selenium.ie.InternetExplorerDriverService;
39+
import org.openqa.selenium.ie.InternetExplorerOptions;
3740

3841
public class SeleniumDriver {
3942
// CONSTANT FIELDS
@@ -51,7 +54,6 @@ public class SeleniumDriver {
5154

5255
private WebDriver webDriver;
5356

54-
5557
private Duration _findTimeout = null;
5658
private Duration _pollInterval = null;
5759
private Duration _pageLoadTimeout = null;
@@ -155,6 +157,7 @@ public Duration setPageLoadTimeout(Duration pageLoadTimeout) {
155157
public Browsers setDevice(Devices device) { _device=device; return getDevice();}
156158

157159

160+
158161
public HTMLElement findElement(ObjectMapping objectMapping) { return findElement(null,objectMapping, false, false, getElementFindTimeout(), getPollInterval(), false);}
159162
public HTMLElement findElement(ObjectMapping objectMapping,boolean waitUntilStable) { return findElement(null,objectMapping, false, false, getElementFindTimeout(), getPollInterval(), waitUntilStable);}
160163
public HTMLElement findElement(ObjectMapping objectMapping,Duration timeout) { return findElement(null,objectMapping, false, false, timeout, getPollInterval(), false);}
@@ -185,7 +188,6 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
185188
boolean multiLogShown=false;
186189
boolean showMultiMatches=true;
187190
boolean isStable=false;
188-
189191
//
190192
// We look after our own implicitWait (timeout) and poll interval rather that Selenium's as we are using our FindElements to find the element we want. We use our
191193
// FindElements so that we can control if ONLY a single match is allowed (Selenium FindElement allows multiple matches silently - see By class implementation) and extra debug logging.
@@ -195,7 +197,7 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
195197

196198
Logger.WriteLine(Logger.LogLevels.TestDebug, "Timeout - %s", durationFormatted(timeout));
197199

198-
while (waitUntilStable && !isStable) {
200+
while (true) {
199201

200202
StopWatch timer = StopWatch.createStarted();
201203

@@ -234,6 +236,7 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
234236
throw new RuntimeException(errorText);
235237
}
236238

239+
// At this point we have a single match OR multiple matches
237240
if (showMultiMatches && !waitUntilSingle) {
238241
Logger.WriteLine(Logger.LogLevels.TestDebug, "From [%s], find [%s (%s)] returned %d matches (%s multiple matches).",
239242
(parentElement == null) ? "DOM Top Level" : parentElement.getMappingDetails().getFriendlyName(),
@@ -252,7 +255,7 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
252255
objectMapping.getFriendlyName(),
253256
clauseResults.size(),
254257
(allowMultipleMatches) ? "Allowing" : "Not");
255-
return clauseResults.get(0);
258+
break;
256259
} else {
257260
if (timer.getTime()>=totalTimeoutMillis) {
258261
Logger.WriteLine(Logger.LogLevels.Error, "From [%s], find [%s (%s)] returned %d matches (%s multiple matches). We must wait until stable, element 0 is NOT stable and timeout reached so throwing",
@@ -277,17 +280,15 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
277280
(allowMultipleMatches) ? "Allowing" : "Not");
278281
}
279282
}
280-
281283
}
282284
return clauseResults.get(0);
283285
}
284286

285287
public List<HTMLElement> findElements(HTMLElement parentElement, ObjectMapping mapping) {
286288

287-
List<WebElement> foundElements;
289+
List<WebElement> foundElements=null;
288290
List<HTMLElement> returnElements = new ArrayList<HTMLElement>();
289291

290-
291292
if (mapping==null) {
292293
Logger.WriteLine(Logger.LogLevels.Error,"ObjectMapping = null!");
293294
throw new RuntimeException("SeleniumDriver.FindElements called with mapping null!");
@@ -302,10 +303,14 @@ public List<HTMLElement> findElements(HTMLElement parentElement, ObjectMapping m
302303
Logger.WriteLine(Logger.LogLevels.FrameworkDebug,"Calling Selenium WebDriver findElements with By = [%s]",seleniumFindBy.toString());
303304
foundElements = (parentElement==null) ? webDriver.findElements(seleniumFindBy) : parentElement.getSeleniumnWebElement().findElements(seleniumFindBy);
304305
}
306+
catch (WebDriverException e)
307+
{
308+
checkIfConnectionIssue(e);
309+
}
305310
catch (Exception e) {
306311
if (parentElement==null) {
307312
Logger.WriteLine(Logger.LogLevels.Error, "Selenium error finding elements using find logic [%s] ([%s)]: %s", seleniumFindBy.toString(), mapping.getFriendlyName(), e.toString());
308-
throw new RuntimeException(String.format("Selenium error finding elements using find logic [%s] ([%s)]", seleniumFindBy.toString(), mapping.getFriendlyName()));
313+
throw new RuntimeException(String.format("Selenium error finding elements using find logic [%s] ([%s)]", seleniumFindBy.toString(), mapping.getFriendlyName()),e);
309314
}
310315
else {
311316
Logger.WriteLine(Logger.LogLevels.Error, "Selenium error finding elements offset from [%s (%s)] using find logic [%s] ([%s)]: %s",parentElement.getMappingDetails().getFriendlyName(),parentElement.getMappingDetails().getActualFindLogic(),seleniumFindBy.toString(), mapping.getFriendlyName(), e.toString());
@@ -344,11 +349,14 @@ public List<HTMLElement> findElements(HTMLElement parentElement, ObjectMapping m
344349
/// of executing Javascript in automated tests.
345350
public <T> T executeJavaScript(Class<T> type, String script, Object... args)
346351
{
347-
Object result;
352+
Object result=null;
348353
try
349354
{
350355
result = ((JavascriptExecutor)webDriver).executeScript(script, args);
351-
return type.cast(result);
356+
}
357+
catch (WebDriverException e)
358+
{
359+
checkIfConnectionIssue(e);
352360
}
353361
catch (Exception ex)
354362
{
@@ -364,16 +372,36 @@ public <T> T executeJavaScript(Class<T> type, String script, Object... args)
364372
exceptionString = String.format("executeJavaScript(\"%s\"): %s",script, exceptionString);
365373
throw new RuntimeException(exceptionString,ex);
366374
}
375+
return type.cast(result);
367376
}
368377

369378
/// <summary>Injects and executes Javascript in the currently active Selenium browser. If Selenium throws an error, test is aborted.</summary>
370379
/// <param name="script">Javascript that will be injected into the DOM and executed.</param>
371380
/// <param name="args">Any arguments passed in to the Javascript</param>
372381
public void executeJavaScriptNoReturnData(String script, Object[] args)
373382
{
374-
Object dummy = executeJavaScript(Object.class,script,args);
383+
Object dummy = executeJavaScript(Object.class,script,args);
384+
}
385+
386+
public void quit() {
387+
try {
388+
if (webDriver != null) {
389+
webDriver.quit();
390+
}
391+
}
392+
catch (Exception e) {};
375393
}
376394

395+
public void finalize() {
396+
//
397+
// We use finalize here in full awareness that it MAY NOT be called and that it may be called after a full clean-up! We are only doing this to prevent
398+
// a ruckload of browsers accumulating! As finalize is a terrible method to use, we use it VERY carefully!
399+
//
400+
try {
401+
webDriver.quit();
402+
}
403+
catch (Exception e) {}
404+
}
377405

378406
private void startOrConnectToSeleniumServer(boolean killFirst) {
379407
if (isLocalSelenium) {
@@ -497,7 +525,6 @@ private List<HTMLElement> getHtmlElements(HTMLElement parentElement, ObjectMappi
497525
return clauseResults;
498526
}
499527

500-
501528
private void setPathToDriverIfExistsAndIsExecutable(final String pathToDriver, final String driverExeProperty,String executable) {
502529
final File driver = new File(pathToDriver,executable);
503530
if (driver.exists() && driver.canExecute()) {
@@ -607,7 +634,6 @@ private void killAllProcesses(String name) {
607634
}
608635
}
609636

610-
611637
private int getProcessCount(String name) {
612638
int count=0;
613639
List<String[]> processInstanceCount = getProcessList();
@@ -639,5 +665,30 @@ private List<String[]> getProcessList() {
639665
return list;
640666
}
641667

668+
669+
private String CallingMethodDetails(StackTraceElement methodBase)
670+
{
671+
String methodName="";
672+
if (methodBase != null)
673+
{
674+
methodName = methodBase.getMethodName();
675+
if (methodName==null) methodName = "<Unknown>";
676+
}
677+
return String.format("%s",methodName);
678+
}
679+
680+
681+
private void checkIfConnectionIssue(WebDriverException e) throws RuntimeException {
682+
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
683+
StackTraceElement caller = stackTraceElements[2];
684+
if (e.getCause().getClass()==ConnectException.class) {
685+
throw new RuntimeException(String.format("Selenium Driver method [%s] called but Selenium WebDriver not connected!",CallingMethodDetails(caller)),e);
686+
}
687+
else
688+
{
689+
throw new RuntimeException(String.format("Selenium Driver method [%s] error using Selenium. See inner exception.",CallingMethodDetails(caller)),e);
690+
}
691+
}
692+
642693
}
643694

0 commit comments

Comments
 (0)