Bug 1316987 - Improve handling of preferences for default branch and complex values. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 15 Nov 2016 16:06:18 +0100
changeset 439178 b995c588ecf952c4ad61fd86446eacb003303d73
parent 439145 f91e37a3afc5fed2e5b859f9d71b91bda01b9aa0
child 537093 6bcf9088ebd707a778e3f6836bb518f48d222dbb
push id35924
push userbmo:hskupin@gmail.com
push dateTue, 15 Nov 2016 15:06:53 +0000
bugs1316987
milestone53.0a1
Bug 1316987 - Improve handling of preferences for default branch and complex values. MozReview-Commit-ID: 18BNm7YiKR5
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/harness/marionette/tests/unit/test_prefs.py
testing/marionette/harness/marionette/tests/unit/test_using_prefs.py
testing/marionette/harness/marionette/tests/unit/unit-tests.ini
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -970,89 +970,125 @@ class Marionette(object):
             self.push_permission(perm, perms[perm])
 
         try:
             yield
         finally:
             for perm in original_perms:
                 self.push_permission(perm, original_perms[perm])
 
-    def get_pref(self, pref):
-        """Gets the preference value.
+    def clear_pref(self, pref):
+        """Clear the user-defined value from the specified preference.
 
         :param pref: Name of the preference.
-
-        Usage example::
-
-            marionette.get_pref("browser.tabs.warnOnClose")
         """
-        with self.using_context(self.CONTEXT_CONTENT):
-            pref_value = self.execute_script("""
-                Components.utils.import("resource://gre/modules/Preferences.jsm");
-                return Preferences.get(arguments[0], null);
-                """, script_args=(pref,), sandbox="system")
-            return pref_value
-
-    def clear_pref(self, pref):
         with self.using_context(self.CONTEXT_CHROME):
             self.execute_script("""
                Components.utils.import("resource://gre/modules/Preferences.jsm");
                Preferences.reset(arguments[0]);
                """, script_args=(pref,))
 
-    def set_pref(self, pref, value):
+    def get_pref(self, pref, default_branch=False, value_type="nsISupportsString"):
+        """Get the value of the specified preference.
+
+        :param pref: Name of the preference.
+        :param default_branch: Optional, if `True` the preference value will be read
+                               from the default branch. Otherwise the user-defined
+                               value if set is returned. Defaults to `False`.
+        :param value_type: Optional, XPCOM interface of the pref's complex value.
+                           Defaults to `nsISupportsString`. Other possible values are:
+                           `nsILocalFile`, and `nsIPrefLocalizedString`.
+
+        Usage example::
+            marionette.get_pref("browser.tabs.warnOnClose")
+
+        """
+        with self.using_context(self.CONTEXT_CHROME):
+            pref_value = self.execute_script("""
+                Components.utils.import("resource://gre/modules/Preferences.jsm");
+
+                let [pref, defaultBranch, valueType] = arguments;
+
+                prefs = new Preferences({defaultBranch: defaultBranch});
+                return prefs.get(pref, null, valueType=Ci[valueType]);
+                """, script_args=(pref, default_branch, value_type))
+            return pref_value
+
+    def set_pref(self, pref, value, default_branch=False):
+        """Set the value of the specified preference.
+
+        :param pref: Name of the preference.
+        :param value: The value to set the preference to. If the value is None,
+                      reset the preference to its default value. If no default
+                      value exists, the preference will cease to exist.
+        :param default_branch: Optional, if `True` the preference value will
+                       be written to the default branch, and will remain until
+                       the application gets restarted. Otherwise a user-defined
+                       value is set. Defaults to `False`.
+
+        Usage example::
+            marionette.set_pref("browser.tabs.warnOnClose", True)
+
+        """
         with self.using_context(self.CONTEXT_CHROME):
             if value is None:
                 self.clear_pref(pref)
                 return
 
             self.execute_script("""
                 Components.utils.import("resource://gre/modules/Preferences.jsm");
-                Preferences.set(arguments[0], arguments[1]);
-                """, script_args=(pref, value,))
+
+                let [pref, value, defaultBranch] = arguments;
 
-    def set_prefs(self, prefs):
-        """Sets preferences.
+                prefs = new Preferences({defaultBranch: defaultBranch});
+                prefs.set(pref, value);
+                """, script_args=(pref, value, default_branch))
 
-        If the value of the preference to be set is None, reset the preference
-        to its default value. If no default value exists, the preference will
-        cease to exist.
+    def set_prefs(self, prefs, default_branch=False):
+        """Set the value of a list of preferences.
 
-        :param prefs: A dict containing one or more preferences and
-            their values to be set.
+        :param prefs: A dict containing one or more preferences and their values
+                      to be set. See `set_pref` for further details.
+        :param default_branch: Optional, if `True` the preference value will
+                       be written to the default branch, and will remain until
+                       the application gets restarted. Otherwise a user-defined
+                       value is set. Defaults to `False`.
 
         Usage example::
 
             marionette.set_prefs({"browser.tabs.warnOnClose": True})
 
         """
         for pref, value in prefs.items():
-            self.set_pref(pref, value)
+            self.set_pref(pref, value, default_branch=default_branch)
 
     @contextmanager
-    def using_prefs(self, prefs):
-        """Sets preferences for code being executed in a `with` block,
-        and restores them on exit.
+    def using_prefs(self, prefs, default_branch=False):
+        """Set preferences for code executed in a `with` block, and restores them on exit.
 
         :param prefs: A dict containing one or more preferences and their values
-        to be set.
+                      to be set. See `set_prefs` for further details.
+        :param default_branch: Optional, if `True` the preference value will
+                       be written to the default branch, and will remain until
+                       the application gets restarted. Otherwise a user-defined
+                       value is set. Defaults to `False`.
 
         Usage example::
 
             with marionette.using_prefs({"browser.tabs.warnOnClose": True}):
                 # ... do stuff ...
 
         """
         original_prefs = {p: self.get_pref(p) for p in prefs}
-        self.set_prefs(prefs)
+        self.set_prefs(prefs, default_branch=default_branch)
 
         try:
             yield
         finally:
-            self.set_prefs(original_prefs)
+            self.set_prefs(original_prefs, default_branch=default_branch)
 
     @do_process_check
     def enforce_gecko_prefs(self, prefs):
         """Checks if the running instance has the given prefs. If not,
         it will kill the currently running instance, and spawn a new
         instance with the requested preferences.
 
         : param prefs: A dictionary whose keys are preference names.
rename from testing/marionette/harness/marionette/tests/unit/test_using_prefs.py
rename to testing/marionette/harness/marionette/tests/unit/test_prefs.py
--- a/testing/marionette/harness/marionette/tests/unit/test_using_prefs.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_prefs.py
@@ -1,50 +1,166 @@
-# -*- coding: utf-8 -*-
 # 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_driver.errors import JavascriptException
 
 
-class TestUsingPrefs(MarionetteTestCase):
-    string_pref = 'marionette.test.string'
-    int_pref = 'marionette.test.int'
-    bool_pref = 'marionette.test.bool'
-    non_existent_pref = 'marionette.test.non_existent.string'
+class TestPreferences(MarionetteTestCase):
+    prefs = {
+        "bool": "marionette.test.bool",
+        "int": "marionette.test.int",
+        "string": "marionette.test.string",
+    }
+
+    def tearDown(self):
+        for pref in self.prefs.values():
+            self.marionette.clear_pref(pref)
+
+        super(TestPreferences, self).tearDown()
+
+    def test_clear_pref(self):
+        self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+
+        self.marionette.set_pref(self.prefs["bool"], True)
+        self.assertTrue(self.marionette.get_pref(self.prefs["bool"]))
+
+        self.marionette.clear_pref(self.prefs["bool"])
+        self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+
+    def test_get_and_set_pref(self):
+        # By default none of the preferences are set
+        self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+        self.assertIsNone(self.marionette.get_pref(self.prefs["int"]))
+        self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+        # Test boolean values
+        self.marionette.set_pref(self.prefs["bool"], True)
+        value = self.marionette.get_pref(self.prefs["bool"])
+        self.assertTrue(value)
+        self.assertEqual(type(value), bool)
+
+        # Test int values
+        self.marionette.set_pref(self.prefs["int"], 42)
+        value = self.marionette.get_pref(self.prefs["int"])
+        self.assertEqual(value, 42)
+        self.assertEqual(type(value), int)
+
+        # Test string values
+        self.marionette.set_pref(self.prefs["string"], "abc")
+        value = self.marionette.get_pref(self.prefs["string"])
+        self.assertEqual(value, "abc")
+        self.assertTrue(isinstance(value, basestring))
+
+        # Test reset value
+        self.marionette.set_pref(self.prefs["string"], None)
+        self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+    def test_get_set_pref_default_branch(self):
+        pref_default = "marionette.test.pref_default1"
+        self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+        self.marionette.set_pref(pref_default, "default_value", default_branch=True)
+        self.assertEqual(self.marionette.get_pref(pref_default), "default_value")
+        self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+                         "default_value")
+
+        self.marionette.set_pref(pref_default, "user_value")
+        self.assertEqual(self.marionette.get_pref(pref_default), "user_value")
+        self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+                         "default_value")
+
+        self.marionette.clear_pref(pref_default)
+        self.assertEqual(self.marionette.get_pref(pref_default), "default_value")
+
+    def test_get_pref_value_type(self):
+        # Without a given value type the properties URL will be returned only
+        pref_complex = "browser.startup.homepage"
+        properties_file = "chrome://branding/locale/browserconfig.properties"
+        self.assertEqual(self.marionette.get_pref(pref_complex, default_branch=True),
+                         properties_file)
+
+        # Otherwise the property named like the pref will be translated
+        value = self.marionette.get_pref(pref_complex, default_branch=True,
+                                         value_type="nsIPrefLocalizedString")
+        self.assertNotEqual(value, properties_file)
+
+    def test_set_prefs(self):
+        # By default none of the preferences are set
+        self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+        self.assertIsNone(self.marionette.get_pref(self.prefs["int"]))
+        self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+        # Set a value on the default branch first
+        pref_default = "marionette.test.pref_default2"
+        self.assertIsNone(self.marionette.get_pref(pref_default))
+        self.marionette.set_prefs({pref_default: "default_value"}, default_branch=True)
+
+        # Set user values
+        prefs = {self.prefs["bool"]: True, self.prefs["int"]: 42,
+                 self.prefs["string"]: "abc", pref_default: "user_value"}
+        self.marionette.set_prefs(prefs)
+
+        self.assertTrue(self.marionette.get_pref(self.prefs["bool"]))
+        self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42)
+        self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc")
+        self.assertEqual(self.marionette.get_pref(pref_default), "user_value")
+        self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+                         "default_value")
 
     def test_using_prefs(self):
-        # Test that multiple preferences can be set with 'using_prefs', and that
+        # Test that multiple preferences can be set with "using_prefs", and that
         # they are set correctly and unset correctly after leaving the context
         # manager.
-        self.marionette.set_prefs({self.string_pref: 'old',
-                                   self.int_pref: 42,
-                                   self.bool_pref: False})
+        pref_not_existent = "marionette.test.not_existent1"
+        pref_default = "marionette.test.pref_default3"
 
-        with self.marionette.using_prefs({self.string_pref: 'new',
-                                          self.int_pref: 10 ** 9,
-                                          self.bool_pref: True,
-                                          self.non_existent_pref: 'existent'}):
-            self.assertEquals(self.marionette.get_pref(self.string_pref), 'new')
-            self.assertEquals(self.marionette.get_pref(self.int_pref), 10 ** 9)
-            self.assertEquals(self.marionette.get_pref(self.bool_pref), True)
-            self.assertEquals(self.marionette.get_pref(self.non_existent_pref),
-                              'existent')
+        self.marionette.set_prefs({self.prefs["string"]: "abc",
+                                   self.prefs["int"]: 42,
+                                   self.prefs["bool"]: False,
+                                   })
+        self.assertFalse(self.marionette.get_pref(self.prefs["bool"]))
+        self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42)
+        self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc")
+        self.assertIsNone(self.marionette.get_pref(pref_not_existent))
+
+        with self.marionette.using_prefs({self.prefs["bool"]: True,
+                                          self.prefs["int"]: 24,
+                                          self.prefs["string"]: "def",
+                                          pref_not_existent: "existent"}):
+
+            self.assertTrue(self.marionette.get_pref(self.prefs["bool"]), True)
+            self.assertEquals(self.marionette.get_pref(self.prefs["int"]), 24)
+            self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "def")
+            self.assertEquals(self.marionette.get_pref(pref_not_existent), "existent")
 
-        self.assertEquals(self.marionette.get_pref(self.string_pref), 'old')
-        self.assertEquals(self.marionette.get_pref(self.int_pref), 42)
-        self.assertEquals(self.marionette.get_pref(self.bool_pref), False)
-        self.assertEquals(self.marionette.get_pref(self.non_existent_pref), None)
+        self.assertFalse(self.marionette.get_pref(self.prefs["bool"]))
+        self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42)
+        self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc")
+        self.assertIsNone(self.marionette.get_pref(pref_not_existent))
+
+        # Using context with default branch
+        self.marionette.set_pref(pref_default, "default_value", default_branch=True)
+        self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+                         "default_value")
 
-    def test_exception_using_prefs(self):
-        # Test that throwing an exception inside the context manager doesn't
+        with self.marionette.using_prefs({pref_default: "new_value"}, default_branch=True):
+            self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+                             "new_value")
+
+        self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+                         "default_value")
+
+    def test_using_prefs_exception(self):
+        # Test that throwing an exception inside the context manager doesn"t
         # prevent the preferences from being restored at context manager exit.
-        self.marionette.set_pref(self.string_pref, 'old')
+        self.marionette.set_pref(self.prefs["string"], "abc")
 
-        with self.marionette.using_prefs({self.string_pref: 'new'}):
-            self.assertEquals(self.marionette.get_pref(self.string_pref), 'new')
-            self.assertRaises(JavascriptException,
-                              self.marionette.execute_script,
-                              "return foo.bar.baz;")
+        try:
+            with self.marionette.using_prefs({self.prefs["string"]: "def"}):
+                self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "def")
+                self.marionette.execute_script("return foo.bar.baz;")
+        except JavascriptException:
+            pass
 
-        self.assertEquals(self.marionette.get_pref(self.string_pref), 'old')
+        self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "abc")
--- a/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
@@ -125,17 +125,17 @@ skip-if = buildapp == 'b2g' || appname =
 skip-if = appname == 'fennec'
 [test_teardown_context_preserved.py]
 skip-if = buildapp == 'b2g'
 [test_file_upload.py]
 skip-if = buildapp == 'b2g' || appname == 'fennec' || os == "win" # http://bugs.python.org/issue14574
 
 [test_execute_sandboxes.py]
 [test_using_permissions.py]
-[test_using_prefs.py]
+[test_prefs.py]
 
 [test_shadow_dom.py]
 
 [test_chrome.py]
 skip-if = buildapp == 'b2g' || appname == 'fennec'
 
 [test_addons.py]