Bug 1312675 - Try harder to close existing windows after each testharness test, r=ato draft
authorJames Graham <james@hoppipolla.co.uk>
Tue, 18 Oct 2016 17:04:34 +0100
changeset 429126 e761e4f7fe7814d8f6a04f658ff5ca679bb12de2
parent 429125 1c02d9f3fddcc9593582def617e7a6b748b73ee3
child 429127 c0d1df782f831956ccf10a39a8f5c91a91a39db3
child 429131 8fe94e06879170b0f471f26fa65491fa75565249
push id33491
push userbmo:james@hoppipolla.co.uk
push dateTue, 25 Oct 2016 08:49:07 +0000
reviewersato
bugs1312675
milestone52.0a1
Bug 1312675 - Try harder to close existing windows after each testharness test, r=ato MozReview-Commit-ID: DVV5PAfRxII
testing/web-platform/harness/wptrunner/executors/executormarionette.py
--- a/testing/web-platform/harness/wptrunner/executors/executormarionette.py
+++ b/testing/web-platform/harness/wptrunner/executors/executormarionette.py
@@ -56,16 +56,18 @@ def do_delayed_imports():
 
 class MarionetteProtocol(Protocol):
     def __init__(self, executor, browser):
         do_delayed_imports()
 
         Protocol.__init__(self, executor, browser)
         self.marionette = None
         self.marionette_port = browser.marionette_port
+        self.timeout = None
+        self.runner_handle = None
 
     def setup(self, runner):
         """Connect to browser via Marionette."""
         Protocol.setup(self, runner)
 
         self.logger.debug("Connecting to Marionette on port %i" % self.marionette_port)
         self.marionette = marionette.Marionette(host='localhost',
                                                 port=self.marionette_port,
@@ -118,32 +120,56 @@ class MarionetteProtocol(Protocol):
             self.marionette.current_window_handle
         except Exception:
             return False
         return True
 
     def after_connect(self):
         self.load_runner("http")
 
+    def set_timeout(self, timeout):
+        self.marionette.set_script_timeout(timeout * 1000)
+        self.timeout = timeout
+
     def load_runner(self, protocol):
         # Check if we previously had a test window open, and if we did make sure it's closed
         self.marionette.execute_script("if (window.wrappedJSObject.win) {window.wrappedJSObject.win.close()}")
         url = urlparse.urljoin(self.executor.server_url(protocol), "/testharness_runner.html")
         self.logger.debug("Loading %s" % url)
+        self.runner_handle = self.marionette.current_window_handle
         try:
             self.marionette.navigate(url)
         except Exception as e:
             self.logger.critical(
                 "Loading initial page %s failed. Ensure that the "
                 "there are no other programs bound to this port and "
                 "that your firewall rules or network setup does not "
                 "prevent access.\e%s" % (url, traceback.format_exc(e)))
         self.marionette.execute_script(
             "document.title = '%s'" % threading.current_thread().name.replace("'", '"'))
 
+    def close_old_windows(self, protocol):
+        handles = self.marionette.window_handles
+        runner_handle = None
+        try:
+            handles.remove(self.runner_handle)
+            runner_handle = self.runner_handle
+        except ValueError:
+            # The runner window probably changed id but we can restore it
+            runner_handle = handles[0]
+            handles = handles[1:]
+
+        for handle in handles:
+            self.marionette.switch_to_window(handle)
+            self.marionette.close()
+
+        self.marionette.switch_to_window(runner_handle)
+        if runner_handle != self.runner_handle:
+            self.load_runner(protocol)
+
     def wait(self):
         socket_timeout = self.marionette.client.sock.gettimeout()
         if socket_timeout:
             self.marionette.set_script_timeout((socket_timeout / 2) * 1000)
 
         while True:
             try:
                 self.marionette.execute_async_script("")
@@ -285,36 +311,38 @@ class RemoteMarionetteProtocol(Protocol)
         """
         conn = httplib.HTTPConnection(self.server.host, self.server.port)
         conn.request("HEAD", self.server.base_path + "invalid")
         res = conn.getresponse()
         return res.status == 404
 
 
 class ExecuteAsyncScriptRun(object):
-    def __init__(self, logger, func, marionette, url, timeout):
+    def __init__(self, logger, func, protocol, url, timeout):
         self.logger = logger
         self.result = (None, None)
-        self.marionette = marionette
+        self.protocol = protocol
+        self.marionette = protocol.marionette
         self.func = func
         self.url = url
         self.timeout = timeout
         self.result_flag = threading.Event()
 
     def run(self):
         timeout = self.timeout
 
         try:
             if timeout is not None:
-                self.marionette.set_script_timeout((timeout + extra_timeout) * 1000)
+                if timeout + extra_timeout != self.protocol.timeout:
+                    self.protocol.set_timeout(timeout + extra_timeout)
             else:
                 # We just want it to never time out, really, but marionette doesn't
                 # make that possible. It also seems to time out immediately if the
                 # timeout is set too high. This works at least.
-                self.marionette.set_script_timeout(2**31 - 1)
+                self.protocol.set_timeout(2**28 - 1)
         except IOError:
             self.logger.error("Lost marionette connection before starting test")
             return Stop
 
         executor = threading.Thread(target = self._run)
         executor.start()
 
         if timeout is not None:
@@ -378,27 +406,28 @@ class MarionetteTestharnessExecutor(Test
             self.protocol.load_runner(new_environment["protocol"])
 
     def do_test(self, test):
         timeout = (test.timeout * self.timeout_multiplier if self.debug_info is None
                    else None)
 
         success, data = ExecuteAsyncScriptRun(self.logger,
                                               self.do_testharness,
-                                              self.protocol.marionette,
+                                              self.protocol,
                                               self.test_url(test),
                                               timeout).run()
         if success:
             return self.convert_result(test, data)
 
         return (test.result_cls(*data), [])
 
     def do_testharness(self, marionette, url, timeout):
         if self.close_after_done:
             marionette.execute_script("if (window.wrappedJSObject.win) {window.wrappedJSObject.win.close()}")
+            self.protocol.close_old_windows(self.protocol)
 
         if timeout is not None:
             timeout_ms = str(timeout * 1000)
         else:
             timeout_ms = "null"
 
         script = self.script % {"abs_url": url,
                                 "url": strip_server(url),
@@ -461,17 +490,17 @@ class MarionetteRefTestExecutor(RefTestE
         assert dpi is None
 
         timeout =  self.timeout_multiplier * test.timeout if self.debug_info is None else None
 
         test_url = self.test_url(test)
 
         return ExecuteAsyncScriptRun(self.logger,
                              self._screenshot,
-                             self.protocol.marionette,
+                             self.protocol,
                              test_url,
                              timeout).run()
 
     def _screenshot(self, marionette, url, timeout):
         marionette.navigate(url)
 
         marionette.execute_async_script(self.wait_script)