Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

# 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
import mozinfo
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.assertRaisesRegex(
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.assertRaisesRegex(
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.assertRaisesRegex(
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.assertRaisesRegex(
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)
@unittest.skipIf(
mozinfo.info["tsan"] == True,
"Bug 1925308 - socket.timeout: Process unexpectedly quit without restarting",
)
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.assertRaisesRegex(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.assertRaisesRegex(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.assertRaisesRegex(
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.assertRaisesRegex(
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.assertRaisesRegex(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.assertRaisesRegex(
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.assertRaisesRegex(
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.assertRaisesRegex(
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.assertRaisesRegex(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.assertRaisesRegex(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.assertRaisesRegex(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",
)