Bug 1259055 - Use window management class for handling of new tabs. draft
authorHenrik Skupin <mail@hskupin.info>
Thu, 03 Nov 2016 17:58:41 +0100
changeset 433361 97f83ced176dcb4f097a61ce258e0b0f2b1b1ac9
parent 432808 fd09c7ed81edf542e6bc28ea720bdb6087e6f43e
child 433362 a18a737c15d2a02b0ba2fc6d1007b3e389c8b0ec
push id34562
push userbmo:hskupin@gmail.com
push dateThu, 03 Nov 2016 17:00:59 +0000
bugs1259055
milestone52.0a1
Bug 1259055 - Use window management class for handling of new tabs. MozReview-Commit-ID: Le8ltn1IbU4
testing/marionette/harness/marionette/__init__.py
testing/marionette/harness/marionette/runner/__init__.py
testing/marionette/harness/marionette/runner/mixins/__init__.py
testing/marionette/harness/marionette/runner/mixins/window_manager.py
testing/marionette/harness/marionette/tests/unit/test_about_pages.py
testing/marionette/harness/marionette/tests/unit/test_window_handles.py
testing/marionette/harness/marionette/tests/unit/test_window_management.py
--- a/testing/marionette/harness/marionette/__init__.py
+++ b/testing/marionette/harness/marionette/__init__.py
@@ -5,16 +5,17 @@
 __version__ = '3.2.0'
 
 from .marionette_test import (
     CommonTestCase,
     expectedFailure,
     MarionetteJSTestCase,
     MarionetteTestCase,
     skip,
+    skip_if_chrome,
     skip_if_desktop,
     skip_if_mobile,
     SkipTest,
     skip_unless_protocol,
 )
 from .runner import (
     BaseMarionetteArguments,
     BaseMarionetteTestRunner,
@@ -27,9 +28,10 @@ from .runner import (
     Marionette,
     MarionetteTest,
     MarionetteTestResult,
     MarionetteTextTestRunner,
     MemoryEnduranceTestCaseMixin,
     TestManifest,
     TestResult,
     TestResultCollection,
+    WindowManagerMixin,
 )
--- a/testing/marionette/harness/marionette/runner/__init__.py
+++ b/testing/marionette/harness/marionette/runner/__init__.py
@@ -19,9 +19,10 @@ from .mixins import (
     EnduranceTestCaseMixin,
     HTMLReportingArguments,
     HTMLReportingTestResultMixin,
     HTMLReportingTestRunnerMixin,
     MemoryEnduranceTestCaseMixin,
     BrowserMobProxyTestCaseMixin,
     BrowserMobProxyArguments,
     BrowserMobTestCase,
+    WindowManagerMixin,
 )
--- a/testing/marionette/harness/marionette/runner/mixins/__init__.py
+++ b/testing/marionette/harness/marionette/runner/mixins/__init__.py
@@ -14,8 +14,12 @@ from .reporting import (
     HTMLReportingTestRunnerMixin,
 )
 
 from .browsermob import (
     BrowserMobProxyTestCaseMixin,
     BrowserMobProxyArguments,
     BrowserMobTestCase,
 )
+
+from .window_manager import (
+    WindowManagerMixin,
+)
new file mode 100644
--- /dev/null
+++ b/testing/marionette/harness/marionette/runner/mixins/window_manager.py
@@ -0,0 +1,67 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+
+from marionette_driver import By, Wait
+
+
+class WindowManagerMixin(object):
+
+    _menu_item_new_tab = (By.ID, "menu_newNavigatorTab")
+
+    def setUp(self):
+        super(WindowManagerMixin, self).setUp()
+
+        self.start_tab = self.marionette.current_window_handle
+        self.start_tabs = self.marionette.window_handles
+
+    def tearDown(self):
+        try:
+            if len(self.marionette.window_handles) != len(self.start_tabs):
+                raise Exception("Not all tabs as opened by the test have been closed")
+        finally:
+            super(WindowManagerMixin, self).tearDown()
+
+    def close_all_tabs(self):
+        current_window_handles = self.marionette.window_handles
+
+        # If the start tab is not present anymore, use the next one of the list
+        if self.start_tab not in current_window_handles:
+            self.start_tab = current_window_handles[0]
+
+        current_window_handles.remove(self.start_tab)
+        for handle in current_window_handles:
+            self.marionette.switch_to_window(handle)
+            self.marionette.close()
+
+            # Bug 1311350 - close() doesn't wait for tab to be closed.
+            Wait(self.marionette).until(
+                lambda mn: handle not in mn.window_handles,
+                message="Failed to close tab with handle {}".format(handle)
+            )
+
+        self.marionette.switch_to_window(self.start_tab)
+
+    def open_tab(self, trigger="menu"):
+        current_tabs = self.marionette.window_handles
+
+        try:
+            if callable(trigger):
+                trigger()
+            elif trigger == 'menu':
+                with self.marionette.using_context("chrome"):
+                    self.marionette.find_element(*self._menu_item_new_tab).click()
+        except Exception:
+            exc, val, tb = sys.exc_info()
+            raise exc, 'Failed to trigger opening a new tab: {}'.format(val), tb
+        else:
+            Wait(self.marionette).until(
+                lambda mn: len(mn.window_handles) == len(current_tabs) + 1,
+                message="No new tab has been opened"
+            )
+
+            [new_tab] = list(set(self.marionette.window_handles) - set(current_tabs))
+
+            return new_tab
--- a/testing/marionette/harness/marionette/tests/unit/test_about_pages.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_about_pages.py
@@ -1,93 +1,107 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette import MarionetteTestCase
-from marionette.marionette_test import skip_if_e10s
+from marionette import MarionetteTestCase, WindowManagerMixin
 from marionette_driver.keys import Keys
 from marionette_driver.by import By
 
 
-class TestAboutPages(MarionetteTestCase):
+class TestAboutPages(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
-        MarionetteTestCase.setUp(self)
+        super(TestAboutPages, self).setUp()
+
         if self.marionette.session_capabilities['platformName'] == 'darwin':
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
         self.remote_uri = self.marionette.absolute_url("javascriptPage.html")
         self.marionette.navigate(self.remote_uri)
 
+    def tearDown(self):
+        self.close_all_tabs()
+
+        super(TestAboutPages, self).tearDown()
+
     def test_back_forward(self):
+        # Bug 1311041 - Prevent changing of window handle by forcing the test
+        # to be run in a new tab.
+        new_tab = self.open_tab(trigger='menu')
+        self.marionette.switch_to_window(new_tab)
+
         self.marionette.navigate("about:blank")
         self.marionette.navigate(self.remote_uri)
 
         self.marionette.navigate("about:preferences")
 
         self.marionette.go_back()
 
         self.wait_for_condition(
             lambda mn: mn.get_url() == self.remote_uri)
 
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
+
     def test_navigate_non_remote_about_pages(self):
+        # Bug 1311041 - Prevent changing of window handle by forcing the test
+        # to be run in a new tab.
+        new_tab = self.open_tab(trigger='menu')
+        self.marionette.switch_to_window(new_tab)
+
         self.marionette.navigate("about:blank")
         self.assertEqual(self.marionette.get_url(), "about:blank")
         self.marionette.navigate("about:preferences")
         self.assertEqual(self.marionette.get_url(), "about:preferences")
 
-    def test_navigate_shortcut_key(self):
-        self.marionette.navigate("about:preferences")
-        self.marionette.navigate(self.remote_uri)
-        self.marionette.navigate("about:blank")
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
 
-        start_win = self.marionette.current_window_handle
-        start_win_handles = self.marionette.window_handles
-        with self.marionette.using_context("chrome"):
-            main_win = self.marionette.find_element(By.ID, "main-window")
-            main_win.send_keys(self.mod_key, Keys.SHIFT, 'a')
+    def test_navigate_shortcut_key(self):
+        def open_with_shortcut():
+            with self.marionette.using_context("chrome"):
+                main_win = self.marionette.find_element(By.ID, "main-window")
+                main_win.send_keys(self.mod_key, Keys.SHIFT, 'a')
+
+        new_tab = self.open_tab(trigger=open_with_shortcut)
+        self.marionette.switch_to_window(new_tab)
 
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
-        self.assertEqual(start_win, self.marionette.current_window_handle)
-        [new_tab] = list(set(self.marionette.window_handles) - set(start_win_handles))
+        self.wait_for_condition(lambda mn: mn.get_url() == "about:addons")
+
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
+
+    def test_type_to_non_remote_tab(self):
+        # Bug 1311041 - Prevent changing of window handle by forcing the test
+        # to be run in a new tab.
+        new_tab = self.open_tab(trigger='menu')
         self.marionette.switch_to_window(new_tab)
-        self.wait_for_condition(lambda mn: mn.get_url() == "about:addons")
-        self.marionette.close()
-        self.marionette.switch_to_window(start_win)
 
-    @skip_if_e10s
-    def test_type_to_non_remote_tab(self):
         with self.marionette.using_context("chrome"):
             urlbar = self.marionette.find_element(By.ID, 'urlbar')
             urlbar.send_keys(self.mod_key + 'a')
             urlbar.send_keys(self.mod_key + 'x')
             urlbar.send_keys('about:preferences' + Keys.ENTER)
         self.wait_for_condition(lambda mn: mn.get_url() == "about:preferences")
 
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
+
     def test_type_to_remote_tab(self):
-        self.marionette.navigate("about:preferences")
+        # about:blank keeps remoteness from remote_uri
+        self.marionette.navigate("about:blank")
         with self.marionette.using_context("chrome"):
             urlbar = self.marionette.find_element(By.ID, 'urlbar')
-            urlbar.send_keys(self.mod_key + 'a')
-            urlbar.send_keys(self.mod_key + 'x')
             urlbar.send_keys(self.remote_uri + Keys.ENTER)
 
         self.wait_for_condition(lambda mn: mn.get_url() == self.remote_uri)
 
     def test_hang(self):
-        self.marionette.set_context('chrome')
-        initial_tab = self.marionette.window_handles[0]
-
         # Open a new tab and close the first one
-        new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
-        new_tab.click()
-
-        new_tab = [handle for handle in self.marionette.window_handles if
-                   handle != initial_tab][0]
+        new_tab = self.open_tab(trigger="menu")
 
         self.marionette.close()
         self.marionette.switch_to_window(new_tab)
 
-        with self.marionette.using_context('content'):
-            self.marionette.navigate(self.remote_uri)
+        self.marionette.navigate(self.remote_uri)
--- a/testing/marionette/harness/marionette/tests/unit/test_window_handles.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_window_handles.py
@@ -1,98 +1,99 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette import MarionetteTestCase
+from marionette import MarionetteTestCase, WindowManagerMixin
 from marionette_driver.keys import Keys
-from marionette_driver import By
+from marionette_driver import By, Wait
 
 
-class TestWindowHandles(MarionetteTestCase):
+class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
+
+    def tearDown(self):
+        try:
+            self.close_all_tabs()
+        finally:
+            super(TestWindowHandles, self).tearDown()
 
     def test_new_tab_window_handles(self):
-
         keys = []
         if self.marionette.session_capabilities['platformName'] == 'darwin':
             keys.append(Keys.META)
         else:
             keys.append(Keys.CONTROL)
         keys.append('t')
 
-        origin_win = self.marionette.current_window_handle
+        def open_with_shortcut():
+            with self.marionette.using_context("chrome"):
+                main_win = self.marionette.find_element(By.ID, "main-window")
+                main_win.send_keys(*keys)
 
-        with self.marionette.using_context("chrome"):
-            main_win = self.marionette.find_element(By.ID, "main-window")
-            main_win.send_keys(*keys)
-
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
-        handles = self.marionette.window_handles
-        handles.remove(origin_win)
-        new_tab = handles.pop()
+        new_tab = self.open_tab(trigger=open_with_shortcut)
+        self.assertEqual(self.marionette.current_window_handle, self.start_tab)
         self.marionette.switch_to_window(new_tab)
         self.assertEqual(self.marionette.get_url(), "about:newtab")
+
         self.marionette.close()
-
-        self.marionette.switch_to_window(origin_win)
-        self.assertEqual(self.marionette.get_url(), "about:blank")
+        self.marionette.switch_to_window(self.start_tab)
 
     def test_new_tab_window_handles_no_switch(self):
         """Regression test for bug 1294456.
         This test is testing the case where Marionette attempts to send a
         command to a window handle when the browser has opened and selected
         a new tab. Before bug 1294456 landed, the Marionette driver was getting
         confused about which window handle the client cared about, and assumed
         it was the window handle for the newly opened and selected tab.
 
         This caused Marionette to think that the browser needed to do a remoteness
         flip in the e10s case, since the tab opened by menu_newNavigatorTab is
         about:newtab (which is currently non-remote). This meant that commands
         sent to what should have been the original window handle would be
         queued and never sent, since the remoteness flip in the new tab was
         never going to happen.
         """
+        def open_with_menu():
+            with self.marionette.using_context("chrome"):
+                menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
+                menu_new_tab.click()
 
-        with self.marionette.using_context("chrome"):
-            menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
-            menu_new_tab.click()
-
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
+        new_tab = self.open_tab(trigger=open_with_menu)
+        self.assertEqual(self.marionette.current_window_handle, self.start_tab)
 
         # We still have the default tab set as our window handle. This
         # get_url command should be sent immediately, and not be forever-queued.
         self.assertEqual(self.marionette.get_url(), "about:blank")
 
-        self.marionette.switch_to_window(self.marionette.window_handles[1])
+        self.marionette.switch_to_window(new_tab)
         self.marionette.close()
-        self.marionette.switch_to_window(self.marionette.window_handles[0])
+        self.marionette.switch_to_window(self.start_tab)
 
     def test_link_opened_tab_window_handles(self):
         tab_testpage = self.marionette.absolute_url("windowHandles.html")
         self.marionette.navigate(tab_testpage)
-        start_win = self.marionette.current_window_handle
-        link = self.marionette.find_element(By.ID, "new-tab")
-        link.click()
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
+
+        def open_with_link():
+            link = self.marionette.find_element(By.ID, "new-tab")
+            link.click()
 
-        handles = self.marionette.window_handles
-        handles.remove(start_win)
-        dest_win = handles.pop()
+        new_tab = self.open_tab(trigger=open_with_link)
+        self.assertEqual(self.marionette.current_window_handle, self.start_tab)
 
-        self.marionette.switch_to_window(dest_win)
+        self.marionette.switch_to_window(new_tab)
         self.assertEqual(self.marionette.get_url(), "about:blank")
         self.assertEqual(self.marionette.title, "")
 
-        self.marionette.switch_to_window(start_win)
+        self.marionette.switch_to_window(self.start_tab)
 
-        self.assertIn('windowHandles.html', self.marionette.get_url())
+        self.assertEqual(self.marionette.get_url(), tab_testpage)
         self.assertEqual(self.marionette.title, "Marionette New Tab Link")
 
         self.marionette.close()
-        self.marionette.switch_to_window(dest_win)
+        self.marionette.switch_to_window(new_tab)
         self.assertEqual(self.marionette.get_url(), "about:blank")
 
     def test_chrome_windows(self):
         opener_page = self.marionette.absolute_url("windowHandles.html")
 
         self.marionette.navigate(opener_page)
 
         start_win = self.marionette.current_chrome_window_handle
@@ -146,68 +147,60 @@ class TestWindowHandles(MarionetteTestCa
 
         self.marionette.switch_to_window(start_tab)
 
         self.assertEqual(len(self.marionette.chrome_window_handles), 1)
         self.assertEqual(len(self.marionette.window_handles), 1)
         self.assertEqual(self.marionette.current_window_handle, start_tab)
 
     def test_tab_and_window_handles(self):
-        start_tab = self.marionette.current_window_handle
         start_chrome_window = self.marionette.current_chrome_window_handle
+
         tab_open_page = self.marionette.absolute_url("windowHandles.html")
         window_open_page = self.marionette.absolute_url("test_windows.html")
+        results_page = self.marionette.absolute_url("resultPage.html")
 
         # Open a new tab and switch to it.
-        self.marionette.navigate(tab_open_page)
-        link = self.marionette.find_element(By.ID, "new-tab")
-        link.click()
+        def open_tab_with_link():
+            self.marionette.navigate(tab_open_page)
+            link = self.marionette.find_element(By.ID, "new-tab")
+            link.click()
 
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
+        second_tab = self.open_tab(trigger=open_tab_with_link)
         self.assertEqual(len(self.marionette.chrome_window_handles), 1)
         self.assertEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
 
-        handles = self.marionette.window_handles
-        handles.remove(start_tab)
-
-        new_tab = handles.pop()
-        self.marionette.switch_to_window(new_tab)
+        self.marionette.switch_to_window(second_tab)
         self.assertEqual(self.marionette.get_url(), "about:blank")
 
-        # Open a new window from the new tab.
-        self.marionette.navigate(window_open_page)
+        # Open a new window from the new tab and only care about the second new tab
+        def open_win_with_link():
+            self.marionette.navigate(window_open_page)
+            link = self.marionette.find_element(By.LINK_TEXT, "Open new window")
+            link.click()
 
-        link = self.marionette.find_element(By.LINK_TEXT, "Open new window")
-        link.click()
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 3)
-
+        third_tab = self.open_tab(trigger=open_win_with_link)
         self.assertEqual(len(self.marionette.chrome_window_handles), 2)
         self.assertEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
 
-        # Find the new window and switch to it.
-        handles = self.marionette.window_handles
-        handles.remove(start_tab)
-        handles.remove(new_tab)
-        new_window = handles.pop()
-
-        self.marionette.switch_to_window(new_window)
-        results_page = self.marionette.absolute_url("resultPage.html")
+        # Check that the new tab has the correct page loaded
+        self.marionette.switch_to_window(third_tab)
         self.assertEqual(self.marionette.get_url(), results_page)
 
         self.assertEqual(len(self.marionette.chrome_window_handles), 2)
         self.assertNotEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
 
         # Return to our original tab and close it.
-        self.marionette.switch_to_window(start_tab)
+        self.marionette.switch_to_window(self.start_tab)
         self.marionette.close()
         self.assertEquals(len(self.marionette.window_handles), 2)
 
-        # Close the opened window and carry on in our new tab.
-        self.marionette.switch_to_window(new_window)
+        # Close the opened window and carry on in our second tab.
+        self.marionette.switch_to_window(third_tab)
         self.marionette.close()
         self.assertEqual(len(self.marionette.window_handles), 1)
 
-        self.marionette.switch_to_window(new_tab)
+        self.marionette.switch_to_window(second_tab)
         self.assertEqual(self.marionette.get_url(), results_page)
         self.marionette.navigate("about:blank")
 
         self.assertEqual(len(self.marionette.chrome_window_handles), 1)
         self.assertEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
--- a/testing/marionette/harness/marionette/tests/unit/test_window_management.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_window_management.py
@@ -4,38 +4,39 @@
 
 import time
 from marionette import MarionetteTestCase
 from marionette_driver import By, Wait
 
 
 class TestSwitchWindow(MarionetteTestCase):
     def open_new_window(self):
-        self.marionette.set_context("chrome")
-        self.marionette.set_script_timeout(5000)
-        self.marionette.execute_async_script("""
-var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                   .getService(Components.interfaces.nsIWindowWatcher);
-var win = ww.openWindow(null, "chrome://browser/content/browser.xul", "testWin", null, null);
-win.addEventListener("load", function() {
-  win.removeEventListener("load", arguments.callee, true);
-  marionetteScriptFinished();
-}, null);
-""")
-        self.marionette.set_context("content")
+        with self.marionette.using_context("chrome"):
+            print '**** %s' % self.marionette.execute_async_script("""
+              var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
+                                  getService(Components.interfaces.nsIWindowWatcher);
+              var win = ww.openWindow(null, "chrome://browser/content/browser.xul",
+                                      "testWin", null, null);
+              win.addEventListener("load", function() {
+                marionetteScriptFinished(true);
+              }, {once: true});
+              dump(win.readyState);
+              if (win.readyState == 'loaded')
+                marionetteScriptFinished(true);
+            """)
 
     def close_new_window(self):
         self.marionette.set_context("chrome")
         self.marionette.execute_script("""
-var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                   .getService(Components.interfaces.nsIWindowWatcher);
-var win = ww.getWindowByName("testWin", null);
-if (win != null)
-  win.close();
-""")
+          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
+                              getService(Components.interfaces.nsIWindowWatcher);
+          var win = ww.getWindowByName("testWin", null);
+          if (win != null)
+            win.close();
+        """)
         self.marionette.set_context("content")
 
     def test_windows(self):
         orig_win = self.marionette.current_chrome_window_handle
         orig_available = self.marionette.chrome_window_handles
         self.open_new_window()
         # assert we're still in the original window
         self.assertEqual(self.marionette.current_chrome_window_handle, orig_win)