summaryrefslogtreecommitdiffstats
path: root/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py')
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py550
1 files changed, 550 insertions, 0 deletions
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
new file mode 100644
index 0000000000..f41b374896
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
@@ -0,0 +1,550 @@
+# 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
+import unittest
+from urllib.parse import quote
+
+from marionette_driver import errors
+from marionette_driver.by import By
+from marionette_harness import MarionetteTestCase
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(quote(doc))
+
+
+class TestServerQuitApplication(MarionetteTestCase):
+ def tearDown(self):
+ if self.marionette.session is None:
+ self.marionette.start_session()
+
+ def quit(self, flags=None, safe_mode=False):
+ body = {}
+ if flags is not None:
+ body["flags"] = list(
+ flags,
+ )
+ if safe_mode:
+ body["safeMode"] = safe_mode
+
+ resp = self.marionette._send_message("Marionette:Quit", body)
+ self.marionette.session_id = None
+ self.marionette.session = None
+ self.marionette.process_id = None
+ self.marionette.profile = None
+ self.marionette.window = None
+
+ self.assertIn("cause", resp)
+
+ self.marionette.client.close()
+ self.marionette.instance.runner.wait()
+
+ return resp["cause"]
+
+ def test_types(self):
+ for typ in [42, True, "foo", []]:
+ print("testing type {}".format(type(typ)))
+ with self.assertRaises(errors.InvalidArgumentException):
+ self.marionette._send_message("Marionette:Quit", typ)
+
+ with self.assertRaises(errors.InvalidArgumentException):
+ self.quit("foo")
+
+ def test_undefined_default(self):
+ cause = self.quit()
+ self.assertEqual("shutdown", cause)
+
+ def test_empty_default(self):
+ cause = self.quit(())
+ self.assertEqual("shutdown", cause)
+
+ def test_incompatible_quit_flags(self):
+ with self.assertRaises(errors.InvalidArgumentException):
+ self.quit(("eAttemptQuit", "eForceQuit"))
+
+ def test_attempt_quit(self):
+ cause = self.quit(("eAttemptQuit",))
+ self.assertEqual("shutdown", cause)
+
+ def test_force_quit(self):
+ cause = self.quit(("eForceQuit",))
+ self.assertEqual("shutdown", cause)
+
+ def test_safe_mode_requires_restart(self):
+ with self.assertRaises(errors.InvalidArgumentException):
+ self.quit(("eAttemptQuit",), True)
+
+ @unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
+ def test_silent_quit_missing_windowless_capability(self):
+ with self.assertRaises(errors.UnsupportedOperationException):
+ self.quit(("eSilently",))
+
+
+class TestQuitRestart(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+
+ self.pid = self.marionette.process_id
+ self.profile = self.marionette.profile
+ self.session_id = self.marionette.session_id
+
+ # Use a preference to check that the restart was successful. If its
+ # value has not been forced, a restart will cause a reset of it.
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+ self.marionette.set_pref("startup.homepage_welcome_url", "about:about")
+
+ def tearDown(self):
+ # Ensure to restart a session if none exist for clean-up
+ if self.marionette.session is None:
+ self.marionette.start_session()
+
+ self.marionette.clear_pref("startup.homepage_welcome_url")
+
+ MarionetteTestCase.tearDown(self)
+
+ @property
+ def is_safe_mode(self):
+ with self.marionette.using_context("chrome"):
+ return self.marionette.execute_script(
+ """
+ return Services.appinfo.inSafeMode;
+ """
+ )
+
+ def shutdown(self, restart=False):
+ self.marionette.set_context("chrome")
+ self.marionette.execute_script(
+ """
+ let flags = Ci.nsIAppStartup.eAttemptQuit;
+ if (arguments[0]) {
+ flags |= Ci.nsIAppStartup.eRestart;
+ }
+ Services.startup.quit(flags);
+ """,
+ script_args=(restart,),
+ )
+
+ def test_force_restart(self):
+ self.marionette.restart(in_app=False)
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+
+ # A forced restart will cause a new process id
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_force_clean_restart(self):
+ self.marionette.restart(in_app=False, clean=True)
+ self.assertNotEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ # A forced restart will cause a new process id
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_force_quit(self):
+ self.marionette.quit(in_app=False)
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(
+ errors.InvalidSessionIdException, "Please start a session"
+ ):
+ self.marionette.get_url()
+
+ def test_force_clean_quit(self):
+ self.marionette.quit(in_app=False, clean=True)
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(
+ errors.InvalidSessionIdException, "Please start a session"
+ ):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_quit_no_in_app_and_clean(self):
+ # Test that in_app and clean cannot be used in combination
+ with self.assertRaisesRegexp(
+ ValueError, "cannot be triggered with the clean flag set"
+ ):
+ self.marionette.quit(in_app=True, clean=True)
+
+ def test_restart_no_in_app_and_clean(self):
+ # Test that in_app and clean cannot be used in combination
+ with self.assertRaisesRegexp(
+ ValueError, "cannot be triggered with the clean flag set"
+ ):
+ self.marionette.restart(in_app=True, clean=True)
+
+ def test_restart_preserves_requested_capabilities(self):
+ self.marionette.delete_session()
+ self.marionette.start_session(capabilities={"test:fooBar": True})
+
+ self.marionette.restart(in_app=False)
+ self.assertEqual(self.marionette.session.get("test:fooBar"), True)
+
+ def test_restart_safe_mode(self):
+ try:
+ self.assertFalse(self.is_safe_mode, "Safe Mode is unexpectedly enabled")
+ self.marionette.restart(safe_mode=True)
+ self.assertTrue(self.is_safe_mode, "Safe Mode is not enabled")
+ finally:
+ self.marionette.quit(in_app=False, clean=True)
+
+ def test_restart_safe_mode_requires_in_app(self):
+ self.assertFalse(self.is_safe_mode, "Safe Mode is unexpectedly enabled")
+
+ with self.assertRaisesRegexp(ValueError, "in_app restart is required"):
+ self.marionette.restart(in_app=False, safe_mode=True)
+
+ self.assertFalse(self.is_safe_mode, "Safe Mode is unexpectedly enabled")
+ self.marionette.quit(in_app=False, clean=True)
+
+ def test_in_app_restart(self):
+ details = self.marionette.restart()
+ self.assertTrue(details["in_app"], "Expected in_app restart")
+ self.assertFalse(details["forced"], "Expected non-forced shutdown")
+
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_in_app_restart_component_prevents_shutdown(self):
+ with self.marionette.using_context("chrome"):
+ self.marionette.execute_script(
+ """
+ Services.obs.addObserver(subject => {
+ let cancelQuit = subject.QueryInterface(Ci.nsISupportsPRBool);
+ cancelQuit.data = true;
+ }, "quit-application-requested");
+ """
+ )
+
+ details = self.marionette.restart()
+ self.assertTrue(details["in_app"], "Expected in_app restart")
+ self.assertTrue(details["forced"], "Expected forced shutdown")
+
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_in_app_restart_with_callback(self):
+ details = self.marionette.restart(callback=lambda: self.shutdown(restart=True))
+ self.assertTrue(details["in_app"], "Expected in_app restart")
+
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_in_app_restart_with_non_callable_callback(self):
+ with self.assertRaisesRegexp(ValueError, "is not callable"):
+ self.marionette.restart(callback=4)
+
+ self.assertEqual(self.marionette.instance.runner.returncode, None)
+ self.assertEqual(self.marionette.is_shutting_down, False)
+
+ @unittest.skipIf(sys.platform.startswith("win"), "Bug 1493796")
+ def test_in_app_restart_with_callback_but_process_quits_instead(self):
+ try:
+ timeout_shutdown = self.marionette.shutdown_timeout
+ timeout_startup = self.marionette.startup_timeout
+ self.marionette.shutdown_timeout = 5
+ self.marionette.startup_timeout = 0
+
+ with self.assertRaisesRegexp(
+ IOError, "Process unexpectedly quit without restarting"
+ ):
+ self.marionette.restart(callback=lambda: self.shutdown(restart=False))
+ finally:
+ self.marionette.shutdown_timeout = timeout_shutdown
+ self.marionette.startup_timeout = timeout_startup
+
+ @unittest.skipIf(sys.platform.startswith("win"), "Bug 1493796")
+ def test_in_app_restart_with_callback_missing_shutdown(self):
+ try:
+ timeout_shutdown = self.marionette.shutdown_timeout
+ timeout_startup = self.marionette.startup_timeout
+ self.marionette.shutdown_timeout = 5
+ self.marionette.startup_timeout = 0
+
+ with self.assertRaisesRegexp(
+ IOError, "the connection to Marionette server is lost"
+ ):
+ self.marionette.restart(in_app=True, callback=lambda: False)
+ finally:
+ self.marionette.shutdown_timeout = timeout_shutdown
+ self.marionette.startup_timeout = timeout_startup
+
+ def test_in_app_restart_preserves_requested_capabilities(self):
+ self.marionette.delete_session()
+ self.marionette.start_session(capabilities={"test:fooBar": True})
+
+ details = self.marionette.restart()
+ self.assertTrue(details["in_app"], "Expected in_app restart")
+ self.assertEqual(self.marionette.session.get("test:fooBar"), True)
+
+ @unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
+ def test_in_app_silent_restart_fails_without_windowless_flag_on_mac_os(self):
+ self.marionette.delete_session()
+ self.marionette.start_session()
+
+ with self.assertRaises(errors.UnsupportedOperationException):
+ self.marionette.restart(silent=True)
+
+ @unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
+ def test_in_app_silent_restart_windowless_flag_on_mac_os(self):
+ self.marionette.delete_session()
+ self.marionette.start_session(capabilities={"moz:windowless": True})
+
+ self.marionette.restart(silent=True)
+ self.assertTrue(self.marionette.session_capabilities["moz:windowless"])
+
+ self.marionette.restart()
+ self.assertTrue(self.marionette.session_capabilities["moz:windowless"])
+
+ self.marionette.delete_session()
+
+ @unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
+ def test_in_app_silent_restart_requires_in_app(self):
+ self.marionette.delete_session()
+ self.marionette.start_session(capabilities={"moz:windowless": True})
+
+ with self.assertRaisesRegexp(ValueError, "in_app restart is required"):
+ self.marionette.restart(in_app=False, silent=True)
+
+ self.marionette.delete_session()
+
+ @unittest.skipIf(
+ sys.platform.startswith("darwin"), "Not supported on other platforms than MacOS"
+ )
+ def test_in_app_silent_restart_windowless_flag_unsupported_platforms(self):
+ self.marionette.delete_session()
+
+ with self.assertRaises(errors.SessionNotCreatedException):
+ self.marionette.start_session(capabilities={"moz:windowless": True})
+
+ def test_in_app_quit(self):
+ details = self.marionette.quit()
+ self.assertTrue(details["in_app"], "Expected in_app shutdown")
+ self.assertFalse(details["forced"], "Expected non-forced shutdown")
+ self.assertEqual(self.marionette.instance.runner.returncode, 0)
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(
+ errors.InvalidSessionIdException, "Please start a session"
+ ):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_in_app_quit_forced_because_component_prevents_shutdown(self):
+ with self.marionette.using_context("chrome"):
+ self.marionette.execute_script(
+ """
+ Services.obs.addObserver(subject => {
+ let cancelQuit = subject.QueryInterface(Ci.nsISupportsPRBool);
+ cancelQuit.data = true;
+ }, "quit-application-requested");
+ """
+ )
+
+ details = self.marionette.quit()
+ self.assertTrue(details["in_app"], "Expected in_app shutdown")
+ self.assertTrue(details["forced"], "Expected forced shutdown")
+ self.assertEqual(self.marionette.instance.runner.returncode, 0)
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(
+ errors.InvalidSessionIdException, "Please start a session"
+ ):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_in_app_quit_with_callback(self):
+ details = self.marionette.quit(callback=self.shutdown)
+ self.assertTrue(details["in_app"], "Expected in_app shutdown")
+ self.assertFalse(details["forced"], "Expected non-forced shutdown")
+
+ self.assertEqual(self.marionette.instance.runner.returncode, 0)
+ self.assertEqual(self.marionette.is_shutting_down, False)
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(
+ errors.InvalidSessionIdException, "Please start a session"
+ ):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertEqual(self.marionette.profile, self.profile)
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(
+ self.marionette.get_pref("startup.homepage_welcome_url"), "about:about"
+ )
+
+ def test_in_app_quit_with_non_callable_callback(self):
+ with self.assertRaisesRegexp(ValueError, "is not callable"):
+ self.marionette.quit(callback=4)
+ self.assertEqual(self.marionette.instance.runner.returncode, None)
+ self.assertEqual(self.marionette.is_shutting_down, False)
+
+ def test_in_app_quit_forced_because_callback_does_not_shutdown(self):
+ try:
+ timeout = self.marionette.shutdown_timeout
+ self.marionette.shutdown_timeout = 5
+
+ with self.assertRaisesRegexp(IOError, "Process still running"):
+ self.marionette.quit(in_app=True, callback=lambda: False)
+
+ self.assertNotEqual(self.marionette.instance.runner.returncode, None)
+ self.assertEqual(self.marionette.is_shutting_down, False)
+ finally:
+ self.marionette.shutdown_timeout = timeout
+
+ self.marionette.start_session()
+
+ def test_in_app_quit_with_callback_that_raises_an_exception(self):
+ def errorneous_callback():
+ raise Exception("foo")
+
+ with self.assertRaisesRegexp(Exception, "foo"):
+ self.marionette.quit(in_app=True, callback=errorneous_callback)
+ self.assertEqual(self.marionette.instance.runner.returncode, None)
+ self.assertEqual(self.marionette.is_shutting_down, False)
+
+ self.assertIsNotNone(self.marionette.session)
+ self.marionette.current_window_handle
+
+ def test_in_app_quit_with_dismissed_beforeunload_prompt(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <input type="text">
+ <script>
+ window.addEventListener("beforeunload", function (event) {
+ event.preventDefault();
+ });
+ </script>
+ """
+ )
+ )
+
+ self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
+ self.marionette.quit()
+ self.assertNotEqual(self.marionette.instance.runner.returncode, None)
+ self.marionette.start_session()
+
+ def test_reset_context_after_quit_by_set_context(self):
+ # Check that we are in content context which is used by default in
+ # Marionette
+ self.assertNotIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Context does not default to content",
+ )
+
+ self.marionette.set_context("chrome")
+ self.marionette.quit()
+ self.assertEqual(self.marionette.session, None)
+ self.marionette.start_session()
+ self.assertNotIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Not in content context after quit with using_context",
+ )
+
+ def test_reset_context_after_quit_by_using_context(self):
+ # Check that we are in content context which is used by default in
+ # Marionette
+ self.assertNotIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Context does not default to content",
+ )
+
+ with self.marionette.using_context("chrome"):
+ self.marionette.quit()
+ self.assertEqual(self.marionette.session, None)
+ self.marionette.start_session()
+ self.assertNotIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Not in content context after quit with using_context",
+ )
+
+ def test_keep_context_after_restart_by_set_context(self):
+ # Check that we are in content context which is used by default in
+ # Marionette
+ self.assertNotIn(
+ "chrome://", self.marionette.get_url(), "Context doesn't default to content"
+ )
+
+ # restart while we are in chrome context
+ self.marionette.set_context("chrome")
+ self.marionette.restart()
+
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Not in chrome context after a restart with set_context",
+ )
+
+ def test_keep_context_after_restart_by_using_context(self):
+ # Check that we are in content context which is used by default in
+ # Marionette
+ self.assertNotIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Context does not default to content",
+ )
+
+ # restart while we are in chrome context
+ with self.marionette.using_context("chrome"):
+ self.marionette.restart()
+
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertIn(
+ "chrome://",
+ self.marionette.get_url(),
+ "Not in chrome context after a restart with using_context",
+ )