author | Andrew Halberstadt <ahalberstadt@mozilla.com> |
Thu, 19 Apr 2018 15:31:43 -0400 | |
changeset 416925 | 05cf749971979f41084cd6e1d501035e329d9d9d |
parent 416924 | 2a5a941c2db9c64d18a7b56cb7112ae7ac14fc52 |
child 416926 | 9a2ba1c7b0ec32f81e067acd2610e47b9be7d764 |
push id | 33943 |
push user | csabou@mozilla.com |
push date | Fri, 04 May 2018 17:19:55 +0000 |
treeherder | mozilla-central@ef1db4e8bf06 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gbrown |
bugs | 1451159 |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/testing/mozbase/mozprofile/mozprofile/profile.py +++ b/testing/mozbase/mozprofile/mozprofile/profile.py @@ -5,17 +5,17 @@ from __future__ import absolute_import import json import os import platform import tempfile import time import uuid -from abc import ABCMeta, abstractmethod +from abc import ABCMeta, abstractmethod, abstractproperty from shutil import copytree import mozfile from six import string_types from .addons import AddonManager from .permissions import Permissions from .prefs import Preferences @@ -27,17 +27,28 @@ from .prefs import Preferences 'ThunderbirdProfile', 'create_profile'] class BaseProfile(object): __metaclass__ = ABCMeta def __init__(self, profile=None, addons=None, preferences=None, restore=True): - self._addons = addons + """Create a new Profile. + + All arguments are optional. + + :param profile: Path to a profile. If not specified, a new profile + directory will be created. + :param addons: List of paths to addons which should be installed in the profile. + :param preferences: Dict of preferences to set in the profile. + :param restore: Whether or not to clean up any modifications made to this profile + (default True). + """ + self._addons = addons or [] # Prepare additional preferences if preferences: if isinstance(preferences, dict): # unordered preferences = preferences.items() # sanity check @@ -78,16 +89,50 @@ class BaseProfile(object): def reset(self): """ reset the profile to the beginning state """ self.cleanup() self._reset() + @abstractmethod + def set_preferences(self, preferences, filename='user.js'): + pass + + @abstractproperty + def preference_file_names(self): + """A tuple of file basenames expected to contain preferences.""" + + def merge(self, other, interpolation=None): + """Merges another profile into this one. + + This will handle pref files matching the profile's + `preference_file_names` property, and any addons in the + other/extensions directory. + """ + for basename in os.listdir(other): + if basename not in self.preference_file_names: + continue + + path = os.path.join(other, basename) + try: + prefs = Preferences.read_json(path) + except ValueError: + prefs = Preferences.read_prefs(path, interpolation=interpolation) + self.set_preferences(prefs, filename=basename) + + extension_dir = os.path.join(other, 'extensions') + for basename in os.listdir(extension_dir): + path = os.path.join(extension_dir, basename) + + if self.addons.is_addon(path): + self._addons.append(path) + self.addons.install(path) + @classmethod def clone(cls, path_from, path_to=None, ignore=None, **kwargs): """Instantiate a temporary profile via cloning - path: path of the basis to clone - ignore: callable passed to shutil.copytree - kwargs: arguments to the profile constructor """ if not path_to: @@ -123,31 +168,32 @@ class Profile(BaseProfile): can ensure this method is called (even in the case of exception) by using the profile as a context manager: :: with Profile() as profile: # do things with the profile pass # profile.cleanup() has been called here """ + preference_file_names = ('user.js', 'prefs.js') def __init__(self, profile=None, addons=None, preferences=None, locations=None, - proxy=None, restore=True, whitelistpaths=None): + proxy=None, restore=True, whitelistpaths=None, **kwargs): """ :param profile: Path to the profile :param addons: String of one or list of addons to install :param preferences: Dictionary or class of preferences :param locations: ServerLocations object :param proxy: Setup a proxy :param restore: Flag for removing all custom settings during cleanup :param whitelistpaths: List of paths to pass to Firefox to allow read access to from the content process sandbox. """ super(Profile, self).__init__( - profile=profile, addons=addons, preferences=preferences, restore=restore) + profile=profile, addons=addons, preferences=preferences, restore=restore, **kwargs) self._locations = locations self._proxy = proxy self._whitelistpaths = whitelistpaths # Initialize all class members self._reset() @@ -221,37 +267,32 @@ class Profile(BaseProfile): while True: if not self.pop_preferences(filename): break # methods for preferences def set_preferences(self, preferences, filename='user.js'): """Adds preferences dict to profile preferences""" - - # append to the file prefs_file = os.path.join(self.profile, filename) - f = open(prefs_file, 'a') - - if preferences: + with open(prefs_file, 'a') as f: + if not preferences: + return # note what files we've touched self.written_prefs.add(filename) # opening delimeter f.write('\n%s\n' % self.delimeters[0]) - # write the preferences Preferences.write(f, preferences) # closing delimeter f.write('%s\n' % self.delimeters[1]) - f.close() - def set_persistent_preferences(self, preferences): """ Adds preferences dict to profile preferences and save them during a profile reset """ # this is a dict sometimes, convert if isinstance(preferences, dict): @@ -440,49 +481,61 @@ class ThunderbirdProfile(Profile): 'browser.warnOnQuit': False, 'browser.sessionstore.resume_from_crash': False, # prevents the 'new e-mail address' wizard on new profile 'mail.provider.enabled': False, } class ChromeProfile(BaseProfile): + preference_file_names = ('Preferences',) + class AddonManager(list): def install(self, addons): if isinstance(addons, string_types): addons = [addons] self.extend(addons) + @classmethod + def is_addon(self, addon): + # TODO Implement this properly + return os.path.exists(addon) + def __init__(self, **kwargs): super(ChromeProfile, self).__init__(**kwargs) if self.create_new: self.profile = os.path.join(self.profile, 'Default') self._reset() def _reset(self): if not os.path.isdir(self.profile): os.makedirs(self.profile) if self._preferences: - pref_file = os.path.join(self.profile, 'Preferences') - - prefs = {} - if os.path.isfile(pref_file): - with open(pref_file, 'r') as fh: - prefs.update(json.load(fh)) - - prefs.update(self._preferences) - with open(pref_file, 'w') as fh: - json.dump(prefs, fh) + self.set_preferences(self._preferences) self.addons = self.AddonManager() if self._addons: self.addons.install(self._addons) + def set_preferences(self, preferences, filename='Preferences', **values): + pref_file = os.path.join(self.profile, filename) + + prefs = {} + if os.path.isfile(pref_file): + with open(pref_file, 'r') as fh: + prefs.update(json.load(fh)) + + prefs.update(preferences) + with open(pref_file, 'w') as fh: + prefstr = json.dumps(prefs) + prefstr % values # interpolate prefs with values + fh.write(prefstr) + profile_class = { 'chrome': ChromeProfile, 'firefox': FirefoxProfile, 'thunderbird': ThunderbirdProfile, }
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/files/dummy-profile/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + globals: { + user_pref: true, + } +};
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/files/dummy-profile/Preferences @@ -0,0 +1,1 @@ +{"Preferences": 1}
new file mode 100644 index 0000000000000000000000000000000000000000..26f28f099d24bec509e3abd991ff453c667543d6 GIT binary patch literal 530 zc$^FHW@Zs#U|`^2a4J``_nh^LHI0#hVG9!j12=;VLuOuaNn%cpUQtR~Xb2|*^KFSV zzmq^*TEWf0$nq7a60Es&lD~hmfyA-%pER}Nt}NMdpu{QSs!RE$=I*NZA5zU0vU;og zJT9L;^<1u7{@c%g=IyJ$^k&m!)hhP89bUeF3>N%T{k-YsS&^@8S!(}Q8<u)`+}`FW z%|115f`O-yld_O&#D0+-k(!6C*UVC`Ke0sNd(x&~0;wl+%@&ooIIL@SxBJYuNjjrQ zH|z3r_nJ7}vq$2yrtb353Q4YrOgcGLV}ar8GmdOVqWWjPl=EydTfKU_s(x$a+i9mx zI9T56J&_?0m0n!F?UB%p<o^o0-tw&2+S>Q_wz}B%eaXeVcS2^}<v)7i@K#r|O^f5S zIZfjaTz=EHGxg8)r+Tk0rG+wgugUJe7iE7$!nd_;(JO&t_8Ql@4X&5QcFmDCzkDxg z?S`AT)Srl(EITUkT<~c{iJew~(ej4FGmhIdrpUzi>?_-5uCg@Xd|ql{f!Lg=1ot~l zdv0g&-QwD2_-Xa##1r;yH`E0`8D0AC{j8Qbz?+dtju}^ENicu_kjv1}2x6f`9V;a2 W(4sBCo0ScsiIE`?NUsLzW&i+p&C!|w
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/files/dummy-profile/prefs.js @@ -0,0 +1,1 @@ +user_pref("prefs.js", 1);
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/files/dummy-profile/user.js @@ -0,0 +1,1 @@ +user_pref("user.js", 1);
--- a/testing/mozbase/mozprofile/tests/test_profile.py +++ b/testing/mozbase/mozprofile/tests/test_profile.py @@ -6,25 +6,28 @@ from __future__ import absolute_import import os import mozunit import pytest +from mozprofile.prefs import Preferences from mozprofile import ( BaseProfile, Profile, ChromeProfile, FirefoxProfile, ThunderbirdProfile, create_profile, ) +here = os.path.abspath(os.path.dirname(__file__)) + def test_with_profile_should_cleanup(): with Profile() as profile: assert os.path.exists(profile.profile) # profile is cleaned assert not os.path.exists(profile.profile) @@ -54,10 +57,42 @@ def test_create_profile(tmpdir, app, cls return profile = create_profile(app, profile=path) assert isinstance(profile, BaseProfile) assert profile.__class__ == cls assert profile.profile == path +@pytest.mark.parametrize('cls', [ + Profile, + ChromeProfile, +]) +def test_merge_profile(cls): + profile = cls(preferences={'foo': 'bar'}) + assert profile._addons == [] + assert os.path.isfile(os.path.join(profile.profile, profile.preference_file_names[0])) + + other_profile = os.path.join(here, 'files', 'dummy-profile') + profile.merge(other_profile) + + # make sure to add a pref file for each preference_file_names in the dummy-profile + prefs = {} + for name in profile.preference_file_names: + path = os.path.join(profile.profile, name) + assert os.path.isfile(path) + + try: + prefs.update(Preferences.read_json(path)) + except ValueError: + prefs.update(Preferences.read_prefs(path)) + + assert 'foo' in prefs + assert len(prefs) == len(profile.preference_file_names) + 1 + assert all(name in prefs for name in profile.preference_file_names) + + assert len(profile._addons) == 1 + assert profile._addons[0].endswith('empty.xpi') + assert os.path.exists(profile._addons[0]) + + if __name__ == '__main__': mozunit.main()