My question is, how to prevent Selenium from creating multiple chrome instances?
Details:
Using Selenium and chromedriver I created an automation testing program. Strangely, although most runs are as expected, about one out of twenty would create many chrome console (black, empty) windows, with one actual browser window. The browser window would finish the work but not disposed correctly, and no exceptions were thrown. Once I manually closed the only browser window, all other windows were closed too.
This time I caught it in visual studio debug mode, I found even my program paused at a breakpoint, the chrome console windows were still being spawned. In the task manager there were 17 chrome.exe sessions, and no chrome was created before my program started.
Out of all the console windows, only one (the leftmost one on screen, maybe the first one that was created) had some information and all others were blank:
[2400:1204:0412/171721.457:ERROR:gl_surface_egl.cc(808)] EGL Driver
message (Error) eglCreateContext: Requested GLES version (3.0) is
greater than max supported (2, 0).
[2400:1204:0412/171721.578:ERROR:gl_context_egl.cc(352)]
eglCreateContext failed with error EGL_SUCCESS
Then after the only browser window finished routine work, another spawned console window contained some additional information:
[8016:13204:0412/172631.461:ERROR:cache_util_win.cc(20)] Unable to
move the cache: Access is denied. (0x5)
[8016:13204:0412/172631.462:ERROR:cache_util.cc(146)] Unable to move
cache folder
C:\Users\cal\AppData\Local\Temp\scoped_dir2496_2059150988\Default\Cache\Cache_Data
to
C:\Users\cal\AppData\Local\Temp\scoped_dir2496_2059150988\Default\Cache\old_Cache_Data_000
[8016:13204:0412/172631.463:ERROR:disk_cache.cc(186)] Unable to create
cache
Below is my c# code for creating chrome browser:
public static ChromeDriver GetChromeBrowser(bool bHeadless = false, string sProxy = "", string chromeProfileDir = "")
{
var chromeoptions = GetChromeOptions(bHeadless, chromeProfileDir);
var chromeDriverService = ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
// hide prompt window
chromeDriverService.HideCommandPromptWindow = true;
ChromeDriver browser = null;
int nCount = 0;
while (browser == null && nCount < 3)
{
try
{
browser = new ChromeDriver(chromeDriverService, chromeoptions, TimeSpan.FromSeconds(180));
}
catch (Exception ex)
{
// try a different port:
chromeoptions = GetChromeOptions(bHeadless, chromeProfileDir);
}
nCount++;
}
return browser;
}
private static ChromeOptions GetChromeOptions(bool bHeadless = false, string chromeProfileDir = "")
{
var chromeoptions = new ChromeOptions();
if (!string.IsNullOrEmpty(chromeProfileDir))
{
string sProfileFullPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), chromeProfileDir);
if (!Directory.Exists(sProfileFullPath))
{
try
{
Directory.CreateDirectory(sProfileFullPath);
}
catch (Exception)
{
}
}
chromeoptions.AddArguments("--profile-directory=" + sProfileFullPath);
}
chromeoptions.AddArguments("--disable-notifications");
chromeoptions.AddArguments("--no-sandbox");
chromeoptions.AddArguments("--disable-dev-shm-usage");
chromeoptions.UnhandledPromptBehavior = UnhandledPromptBehavior.Dismiss;
int nPortNumber = GetARandomPortNumber();
chromeoptions.AddArguments($"--remote-debugging-port={nPortNumber.ToString()}");
if (bHeadless)
{
chromeoptions.AddArguments("--headless");
chromeoptions.AddArguments("--disable-gpu");
// do not load images:
chromeoptions.AddArguments("--blink-settings=imagesEnabled=false");
}
return chromeoptions;
}
I have also made a screenshot to show how it look like. As you can see, many blank chrome windows were created.
The problem is highly intermittent and hard to reproduce. I don't know if it is enough to pinpoint the problem. Any suggestion is appreciated.
Related
I have a windows form and I make the following call to open chrome up with a URL:
string uri = Uri.EscapeUriString(URL.ToString());
Process process = Process.Start("chrome", uri + " --new-window");
return process.Id;
The problem is that I get the error: "Process with an id of "xxxxxxx" is not running" when I call the following:
procsChrome = Process.GetProcessById(processID);
The processID is the one returned in the previous code snippet. Now, this WORKS when I have NO CHROME WINDOW OPEN.
But if I have another chrome window open, I get this error. I monitored the processes in Task Manager and when no chrome is open, a new process with the the process id returned from the code snippet 1 is created.
When one or more chrome windows are already open, I get a process id from code snippet 1, but the newly opened chrome window is running under some other id. Why is this happening?
Edit 2:
Here is the method I get the error on. The parameter int processID is the process.Id returned above from code snippet 1.
public static string GetCodeFromURL(int processID)
{
Process procsChrome = null;
try
{
if (processID == -1)
{
Console.WriteLine("-1 returned as ID");
return null;
}
procsChrome = Process.GetProcessById(processID); //I GET ERROR HERE
// the chrome process must have a window
if (procsChrome.MainWindowHandle == IntPtr.Zero)
MessageBox.Show("Process failed");
// to find the tabs we first need to locate something reliable - the 'New Tab' button
AutomationElement root = AutomationElement.FromHandle(procsChrome.MainWindowHandle);
var SearchBar = root.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Address and search bar"));
if (SearchBar != null)
{
bool noCode = false;
while (noCode == false)
{
string fullURL = (string)SearchBar.GetCurrentPropertyValue(ValuePatternIdentifiers.ValueProperty);
if (!(fullURL.Contains("code") && fullURL.Contains("state")))
{
if (procsChrome.HasExited)
{
Console.WriteLine("Process failed. User closed browser.");
procsChrome.Close();
noCode = true;
}
}
else
{
string stateToken = HttpUtility.ParseQueryString(fullURL.Substring(new[] { 0, fullURL.IndexOf('?') }.Max())).Get("state");
if (stateToken.Equals("296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601"))
{
noCode = true;
string code = HttpUtility.ParseQueryString(fullURL.Substring(new[] { 0, fullURL.IndexOf('?') }.Max())).Get("code");
procsChrome.CloseMainWindow();
procsChrome.Close();
return code;
}
}
}
}
}
catch (Exception exception)
{
Console.WriteLine("An exception occured on getting the URL. Please try again. The exception is: " + exception.ToString());
return null;
}
return null;
}
Thanks.
Chrome seems to behave in this way by default, creating anothing process and closing the original one, which means you end up with an incorrect Process ID. And when you have multiple Chrome processes with the same website/title you end up having no control over it.
As some suggested using the parameter "--incognito" will solve it, whoever this would also create other unwanted behavioural issues.
I've looked at the Chrome parameters list and tried a few, using the parameter "--no-service-autorun" seems to have solved the issue.
Not sure why chrome is behaving like that. Can you try to have your code launch chrome in incognito mode?
Using the test code below i was able to launch 2 distinct incognito processes each with their own process id. Note: these launched in the same browser window as 2 tabs.
I think using --incognito will get you out of trouble.
public class LaunchChromeProof
{
private readonly ITestOutputHelper _output;
public LaunchChromeProof(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void CanLaunchChromeIcognito_AndFetchProcesses_ByID()
{
var chromePath = #"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
var chromeProcess = Process.Start(chromePath, "www.google.com --incognito");
Assert.NotEqual(0, chromeProcess.Id);
//launch a second session
var chrome2ndProcess = Process.Start(chromePath, "www.google.com --incognito");
Assert.NotEqual(0, chrome2ndProcess.Id);
_output.WriteLine($"Session 1 ProcessID: {chromeProcess.Id}");
_output.WriteLine($"Session 2 ProcessID: {chrome2ndProcess.Id}");
//check we can fetch the correct process by the process id for each session
var fetchProcess1 = Process.GetProcessById(chromeProcess.Id);
Assert.Equal(chromeProcess.Id, fetchProcess1.Id);
var fetchProcess2 = Process.GetProcessById(chrome2ndProcess.Id);
Assert.Equal(chrome2ndProcess.Id, fetchProcess2.Id);
_output.WriteLine($"Fetched Session 1 Process: {fetchProcess1.Id}");
_output.WriteLine($"Fetched Session 2 Process: {fetchProcess2.Id}");
}
It's all in the title. I will start chrome driver with the code that I have below, and it will only load up the resources such as JQuery, Bootstrap, and others. It will take an exceptionally long time to load the page as well when this happens. I'll get errors saying that the resource failed to load due to them timing out.
I'm pretty ignorant on what I need to do, so any documentation that someone is willing to throw my way which helped them find the solution, or ever something that is similar will be greatly appreciated. Here's what I've tried:
1) disabling the proxy settings using the ChromeOptions object
2) Disabling SSL certificates
3) Disabling AVG
4) Disabling firewall
5) Internet connection is fine in my room
Another thing that I tried doing was looking at the log file with ChromeOptions.SetLogginPreference(string, logLevel), which worked in ChromeDriver75 but it no longer works in ChromeDriver76. The same code for looking at logs doesn't work the same across drivers.
//SETTING UP THE DRIVER WITH SOME OPTIONS
public static IWebDriver GetDriver()
{
IWebDriver iwd;
ChromeOptions co = new ChromeOptions(){Proxy = null};
co.AddAdditionalCapability("ACCEPT_SSL_CERTS", true, true);
co.AddAdditionalCapability("ACCEPT_INSECURE_CERTS", true, true);
co.AddArguments("--ignore-certificate-errors", "--ignore-ssl-errors");
co.SetLoggingPreference("all", logLevel: LogLevel.All);
iwd = new ChromeDriver("path/to/driver", co);
}
//ME TRYING TO LOAD THE SITE 10 TIMES
public bool TryLoad()
{
//bool for if it was completed or not
bool b = false;
for (int i = 0; i < 9; i++)
try
{
Console.WriteLine();
Gen.Write("\r\nLoading New Page==>", false, ConsoleColor.Cyan);
p.iwd_Driver.GoTo(p.sURL);
if (WasEverythingWasLoaded(p))
{
Gen.Write(" (/) Page Done!", true, ConsoleColor.Green);
b = true;
return b;
}
Gen.Write($"{(i == 0 ? "Failed to load resources" : $"Failed to load resources {i + 1} times")}.", false, ConsoleColor.Red);
}
catch (WebDriverException e)
{
Gen.Write($"\r\n\r\nFATAL::Failed to load the page {p.sURL}\r\nException info: {e}\r\n\r\n", true, ConsoleColor.Red, ConsoleColor.White);
}
finally
{
if (!b)
Gen.Write($"\r\nAttempting again==>", true, ConsoleColor.DarkYellow);
}
try
{
Gen.Write("\r\nLoading New Page==>", false, ConsoleColor.Cyan);
p.iwd_Driver.GoTo(p.sURL);
if (WasEverythingWasLoaded(p))
{
Gen.Write(" (/) Page Done!", true, ConsoleColor.Green);
b = true;
return b;
}
}
catch (WebDriverException e)
{
Gen.Write($"Something is very wrong and needs to be acknowledged here... try flushing the DNS, restarting VS, restarting computer," +
$" canceling some services etc. because it isn't just Selenium that is being a problem here...\r\nException Info: {e}");
}
catch (Exception e)
{
Gen.Write($"\r\n\r\nFATAL::Exception when loading {p.sURL}\r\nException info: {e}\r\n\r\n", true, ConsoleColor.Red, ConsoleColor.DarkGray);
}
finally
{
if (!b)
p.iwd_Driver.Terminate();
}
return b;
}
//trying to interogate the log files. I want to check to see if everything was
//successfully loaded. If not, then I'm going to try again. This works with
// chormedriver75 but not with chromedriver76 for some reason...
private static bool WasEverythingWasLoaded(Page p)
{
ILogs v = p.iwd_Driver.Manage().Logs;
IReadOnlyCollection<LogEntry> logs;
try
{
logs = p.iwd_Driver.Manage().Logs.GetLog(LogType.Browser);
}
catch (NullReferenceException)
{
Gen.Write("Looks like I can't take logs with this version...", true, ConsoleColor.Blue, ConsoleColor.White);
return true;
}
//spit out the logs
foreach (LogEntry le in logs)
Gen.Write($"LogEntry: {le.Message}", true, ConsoleColor.Red, ConsoleColor.White);
//if the count of the relevant logs is greater than a certain amount, then fail it
return logs.Where(x =>
x.Level == LogLevel.Severe
&& x.Message.ToLower().Contains("failed to load resource")
&& !x.Message.Contains(#"https://wes.sandbook.ca/favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)")
).ToList().Count() < 1;
}
Expecting:
I'm expecting my driver to simply open and go to the page that I ask it to consistently. I only sort of understand what those chrome options are doing, so let me know if it looks out of place.
Found a fix. Super gimicky, but it works.
It definitely has to do with something getting slowed down and it times out. What I did is a took all of the resources that I need, created an HTML doc that contained nothing but
<html>
<head>
<script src="...the_guds..."></script> <!--could be for bootstrap, favicon, or whatever-->
</head>
</html>
for each resource that I needed to include and I systematically load each one after initialising the driver. After that, the resource gets cached, and it makes loading it super fast after that.
Ranorex 7.0.1, evaluation version, 28 days left
OS, Windows 10
Chrome driver, 2.29
Selenium webdriver standalone server, 3.4.0
I have set up Endpoint as Local Selenium WebDriver, its connection has been tested as "Connected".
WebDriver, WebDriver.Support have been added to References.
The code snippet is shown below:
namespace HoT
{
class Program
{
[STAThread]
public static int Main(string[] args)
{
// Uncomment the following 2 lines if you want to automate Windows apps
// by starting the test executable directly
//if (Util.IsRestartRequiredForWinAppAccess)
// return Util.RestartWithUiAccess();
Keyboard.AbortKey = System.Windows.Forms.Keys.Pause;
int error = 0;
var webDriver = new ChromeDriver(#"C:\Users\myName\Documents\Ranorex\RanorexStudio Projects\HoT\HoT\ExternalModules\chromedriver");
webDriver.Navigate().GoToUrl("http://www.google.com");
try
{
error = TestSuiteRunner.Run(typeof(Program), Environment.CommandLine);
}
catch (Exception e)
{
Report.Error("Unexpected exception occurred: " + e.ToString());
error = -1;
}
return error;
}
}
}
All I got is a console window and a pop up window, there is no Chrome browser instance or warning or error. Anyone knows why?
You need to start the browser with
Host.Current.OpenBrowser("http://www.google.com", "Chrome", "", false, false, false, false, false);
Im facing exactly the same issue as described here
but its a closed thread.
Im using selenium webdriver 2.48.2, on win7 IE 11.
The situation goes like this, I have a test that clicks on a button which is supposed to open a new experiment, this new experiment opens in new tab on chrome and in the same tab on firefox, but opens in new window on IE11 when it is run through selenium. But strange thing is that it does not open in new window when the browser was opened manually instead of through selenium script.
maybe the selenium script opens new webdriver? and script halts while searching for new page's elements. what the code does is, it checks if the new handle was opened or not, finds the new handle and then switches the window handle to newer one.
Here is the c# code snippet.
private static TResult TriggerAndWaitForNewWindow<TResult>(PageObject pageObject, Action action, int timeout = 30)
where TResult : PageObject, new()
{
IParent parent = pageObject.Driver;
List<String> existingHandles = pageObject.Driver.WindowHandles.ToList();
action();
string popupHandle = Wait.Until(() =>
{
string foundHandle = null;
List<string> currentHandles = pageObject.Driver.WindowHandles.ToList();
var differentHandles = GetDifference(existingHandles, currentHandles);
if (differentHandles.Count > 0)
{
Boolean hasSomeLength = differentHandles[differentHandles.Count-1].Length > 1;
if (hasSomeLength)
foundHandle = differentHandles[differentHandles.Count - 1];
}
return foundHandle;
}, "Waiting for new Window Handle to appear", timeout, 2000);
// Init the new page object but override the window handle
TResult page = PageObject.Init<TResult>(parent);
page.WindowHandle = popupHandle;
page.SwitchToMyWindow();
return page;
}
private static List<String> GetDifference(List<string> existingHandles, List<string> currentHandles)
{
System.Threading.Thread.Sleep(15000);
return currentHandles.Except(existingHandles).ToList();
}
Halts inside this function on IE11
public Boolean SwitchToMyWindow()
{
try
{
String windowHandle = this.WindowHandle; // must be the old handle
try
{
if (this.Driver.CurrentWindowHandle == windowHandle)
{
Log.Info("No need to cswitch window");
return true;
}
}
catch(Exception e)
{
Log.Warn("We have no current driver window, must have been closed");
}
Log.Info("Switching to Window Handle {0}", this.Driver.CurrentWindowHandle);
this.Driver.SwitchTo().Window(windowHandle); <---- Halts here on IE11
//Pause.milliSeconds(500);
Boolean switched = Wait.Until(() =>
this.Driver.CurrentWindowHandle == windowHandle, "Waiting for my window handle to be the active one", 5, 1000);
}
catch (OpenQA.Selenium.WebDriverTimeoutException tEx)
{
}
return true;
}
Did anyone else ever faced this issue? How can resolve it?
Can you verify if Selenium supports your target OS?
It is possible that Selenium is not fully supported on your Target OS.
Please check the following link for more details.
http://grokbase.com/t/gg/webdriver/1267fdkgaa/openqa-selenium-nosuchwindowexception-with-ie9-and-windows-2008
For a project I have to start an application in C#, rip out the AutomationElement tree related to the process, and then close the application and output the tree. I'm doing this by opening the application using Process.Start. Then I'm finding the AutomationElements related to the spawned process and walking the tree using a combination of TreeWalker and AutomationElement's FindFirst and FindAll methods.
This runs fine on my computer and runs correctly using NUnit locally. It also runs on the other people in my groups computers. The problem is that it never runs on our central testing server that's running Hudson. After some hours of debugging, I had a test on Hudson start the application and then print the first level of the AutomationTree. On my computer, this prints all of the windows I have on my desktop. On Hudson, this only prints the Desktop.
Thinking there might be multiple desktops, I tried using TreeWalker's GetNextSibling function on the RootElement. It still only reported one desktop.
Here's the code I'm using to start a process.
public bool connect(string[] args)
{
if (this.process != null) {
Console.WriteLine("ERROR: Process already connected");
return false;
}
if (!File.Exists(sApplicationPath)) {
Console.WriteLine(sApplicationPath + " does not exist");
return false;
}
// Turn the command arguments into a single string
string arguments = "";
foreach (string arg in args) {
arguments += arg + " ";
}
try {
// Start the application
ProcessStartInfo processStartInfo =
new ProcessStartInfo(sApplicationPath);
processStartInfo.Arguments = arguments;
this.process = Process.Start(processStartInfo);
// Must be a positive integer (non-zero)
if ( !( iInitialDelay > 0 ) ) {
Console.WriteLine("Invalid initial delay. " +
"Defaulting to 5 seconds.");
this.iInitialDelay = 5000;
}
Thread.Sleep(this.iInitialDelay);
} catch (Exception ex) {
Console.WriteLine("WGApplication.connect: " + ex.Message);
return false;
}
// Check if the process still exists
try {
/** This part does not return an error, so I think that means the process exists and is started */
Process check = Process.GetProcessById(process.Id);
} catch (ArgumentException ex) {
Console.WriteLine("The process expired before connection was complete");
Console.WriteLine("Make sure the process is not open anywhere else");
Console.WriteLine("and that it is able to execute on the host machine.");
return false;
}
// Check if the base automation element exists to verify open
AutomationElement rootWindow =
AutomationElement.RootElement.FindChildProcessById(process.Id);
/** This part returns null, so it can't find the window associated with this process id */
if (this.process == null) {
return false;
} else if (rootWindow == null) {
// A root window with this process id has not been found
Console.WriteLine("Cannot find the root window of the created " +
"process. Unknown error.");
return false;
} else {
// Everything is good to go
return true;
}
}
sApplicationPath is set to the absolute path of the executable. iInitialDelay is a delay to make sure the application has time to start. I'm running this on 'C:\Windows\System32\notepad.exe' on Windows Vista SP2 and compiling it with the v3.5 C# compiler.
FindChildProcessById is defined as follows:
public static AutomationElement FindChildProcessById(
this AutomationElement element, int processId)
{
var result = element.FindChildByCondition(
new PropertyCondition(AutomationElement.ProcessIdProperty,
processId));
return result;
}
Remember that this compiles and works on my computer. My test program on Hudson said that the RootElement had no children at all.
So I start the application, confirm it exists, and then I can't find any windows associated with the process. I can't find any windows associated with anything except the desktop.
Is this a problem with Hudson? Does Hudson work in some specific way that this code wouldn't work on it? Is it a problem with my code? The Hudson server is running on a Windows Server 2003 computer. Any help would be appreciated. I know this is a very specific problem which is a reason why I can't find any solutions online.
Is Hudson running as a service? If so, it may not have the necessary rights to show windows.