Bug 1460914 - [xpcshell] Use nsIPrefService.readUserPrefsFromFile to set prefs, r=ted
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Fri, 26 Oct 2018 17:46:03 +0000
changeset 443204 1ba93cd55330c825b10310473276996d4ae6caf8
parent 443203 1c2afde752e7eb78e6d4b4ebe10b1f1e3c3842f7
child 443205 20a550cfc08f4e8de5332209b9a56dbda2d6c6fd
push id34944
push userncsoregi@mozilla.com
push dateSat, 27 Oct 2018 09:49:55 +0000
treeherdermozilla-central@49d47a692ca4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs1460914
milestone65.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
Bug 1460914 - [xpcshell] Use nsIPrefService.readUserPrefsFromFile to set prefs, r=ted This uses nsIPrefService.readUserPrefsFromFile to set preferences from a user.js passed in via the python harness. This allows us to use the profiles under testing/profiles like all the other harnesses and will make setting prefs in xpcshell easier to use and understand. Differential Revision: https://phabricator.services.mozilla.com/D9716
python/mozbuild/mozbuild/action/test_archive.py
testing/mozbase/mozprofile/mozprofile/profile.py
testing/profiles/profiles.json
testing/profiles/xpcshell/user.js
testing/xpcshell/head.js
testing/xpcshell/remotexpcshelltests.py
testing/xpcshell/runxpcshelltests.py
--- a/python/mozbuild/mozbuild/action/test_archive.py
+++ b/python/mozbuild/mozbuild/action/test_archive.py
@@ -534,16 +534,22 @@ ARCHIVE_FILES = {
             'dest': 'xpcshell',
         },
         {
             'source': buildconfig.topobjdir,
             'base': 'build',
             'pattern': 'automation.py',
             'dest': 'xpcshell',
         },
+        {
+            'source': buildconfig.topsrcdir,
+            'base': 'testing/profiles',
+            'pattern': '**',
+            'dest': 'xpcshell/profile_data',
+        },
     ],
     'updater-dep': [
         {
             'source': buildconfig.topobjdir,
             'base': '_tests/updater-dep',
             'pattern': '**',
             'dest': 'updater-dep',
         },
--- a/testing/mozbase/mozprofile/mozprofile/profile.py
+++ b/testing/mozbase/mozprofile/mozprofile/profile.py
@@ -116,16 +116,19 @@ class BaseProfile(object):
             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')
+        if not os.path.isdir(extension_dir):
+            return
+
         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
--- a/testing/profiles/profiles.json
+++ b/testing/profiles/profiles.json
@@ -1,9 +1,10 @@
 {
     "mochitest": ["common", "unittest"],
     "profileserver": ["common", "unittest", "profileserver"],
     "raptor": ["common", "perf", "raptor"],
     "reftest": ["common", "reftest"],
     "talos": ["common", "perf"],
     "valgrind": ["common", "unittest"],
+    "xpcshell": ["xpcshell"],
     "web-platform-tests": ["common", "unittest", "web-platform"]
 }
new file mode 100644
--- /dev/null
+++ b/testing/profiles/xpcshell/user.js
@@ -0,0 +1,2 @@
+// Base preferences file used by the xpcshell harness
+/* globals user_pref */
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -7,17 +7,18 @@
 /*
  * This file contains common code that is loaded before each test file(s).
  * See http://developer.mozilla.org/en/docs/Writing_xpcshell-based_unit_tests
  * for more information.
  */
 
 /* defined by the harness */
 /* globals _HEAD_FILES, _HEAD_JS_PATH, _JSDEBUGGER_PORT, _JSCOV_DIR,
-    _MOZINFO_JS_PATH, _TEST_FILE, _TEST_NAME, _TESTING_MODULES_DIR:true */
+    _MOZINFO_JS_PATH, _TEST_FILE, _TEST_NAME, _TESTING_MODULES_DIR:true,
+    _PREFS_FILE */
 
 /* defined by XPCShellImpl.cpp */
 /* globals load, sendCommand */
 
 /* must be defined by tests using do_await_remote_message/do_send_remote_message */
 /* globals Cc, Ci */
 
 /* may be defined in test files */
@@ -1474,16 +1475,21 @@ function run_next_test() {
     // Close the previous test do_test_pending call.
     do_test_finished(_gRunningTest.name);
   }
 }
 
 try {
   // Set global preferences
   if (runningInParent) {
+    let prefsFile = Cc["@mozilla.org/file/local;1"]
+      .createInstance(Ci.nsIFile);
+    prefsFile.initWithPath(_PREFS_FILE);
+    _Services.prefs.readUserPrefsFromFile(prefsFile);
+
     // Always use network provider for geolocation tests
     // so we bypass the OSX dialog raised by the corelocation provider
     _Services.prefs.setBoolPref("geo.provider.testing", true);
 
     // We need to avoid hitting the network with certain components.
     _Services.prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
     _Services.prefs.setCharPref("media.gmp-manager.updateEnabled", false);
     _Services.prefs.setCharPref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -343,16 +343,25 @@ class XPCShellRemote(xpcshell.XPCShellTe
             "echo xpcw: xpcshell \"$@\"\n",
             "%s/xpcshell \"$@\"\n" % self.remoteBinDir])
         f.close()
         remoteWrapper = posixpath.join(self.remoteBinDir, "xpcw")
         self.device.push(localWrapper, remoteWrapper)
         self.device.chmod(remoteWrapper, root=True)
         os.remove(localWrapper)
 
+    def buildPrefsFile(self):
+        super(XPCShellRemote, self).buildPrefsFile()
+
+        remotePrefsFile = posixpath.join(self.remoteTestRoot, 'user.js')
+        self.device.push(self.prefsFile, remotePrefsFile)
+        self.device.chmod(remotePrefsFile, root=True)
+        os.remove(self.prefsFile)
+        self.prefsFile = remotePrefsFile
+
     def buildEnvironment(self):
         self.buildCoreEnvironment()
         self.setLD_LIBRARY_PATH()
         self.env["MOZ_LINKER_CACHE"] = self.remoteBinDir
         if self.options['localAPK'] and self.appRoot:
             self.env["GRE_HOME"] = self.appRoot
         self.env["XPCSHELL_TEST_PROFILE_DIR"] = self.profileDir
         self.env["TMPDIR"] = self.remoteTmpDir
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -71,16 +71,17 @@ if os.path.isdir(mozbase):
         sys.path.append(os.path.join(mozbase, package))
 
 from manifestparser import TestManifest
 from manifestparser.filters import chunk_by_slice, tags, pathprefix
 from mozlog import commandline
 import mozcrash
 import mozfile
 import mozinfo
+from mozprofile import Profile
 from mozrunner.utils import get_stack_fixer_function
 
 # --------------------------------------------------------------
 
 # TODO: perhaps this should be in a more generally shared location?
 # This regex matches all of the C0 and C1 control characters
 # (U+0000 through U+001F; U+007F; U+0080 through U+009F),
 # except TAB (U+0009), CR (U+000D), LF (U+000A) and backslash (U+005C).
@@ -151,16 +152,17 @@ class XPCShellTestThread(Thread):
         self._rootTempDir = kwargs.get('tempDir')
         self.cleanup_dir_list = kwargs.get('cleanup_dir_list')
         self.pStdout = kwargs.get('pStdout')
         self.pStderr = kwargs.get('pStderr')
         self.keep_going = kwargs.get('keep_going')
         self.log = kwargs.get('log')
         self.app_dir_key = kwargs.get('app_dir_key')
         self.interactive = kwargs.get('interactive')
+        self.prefsFile = kwargs.get('prefsFile')
 
         # only one of these will be set to 1. adding them to the totals in
         # the harness
         self.passCount = 0
         self.todoCount = 0
         self.failCount = 0
 
         # Context for output processing
@@ -397,17 +399,16 @@ class XPCShellTestThread(Thread):
         """
         headfiles = self.getHeadFiles(self.test_object)
         cmdH = ", ".join(['"' + f.replace('\\', '/') + '"'
                          for f in headfiles])
 
         dbgport = 0 if self.jsDebuggerInfo is None else self.jsDebuggerInfo.port
 
         return [
-            '-e', 'const _SERVER_ADDR = "localhost"',
             '-e', 'const _HEAD_FILES = [%s];' % cmdH,
             '-e', 'const _JSDEBUGGER_PORT = %d;' % dbgport,
         ]
 
     def getHeadFiles(self, test):
         """Obtain lists of head- files.  Returns a list of head files.
         """
         def sanitize_list(s, kind):
@@ -443,16 +444,17 @@ class XPCShellTestThread(Thread):
             self.xpcshell,
             '-g', self.xrePath,
             '-a', self.appPath,
             '-r', self.httpdManifest,
             '-m',
             '-s',
             '-e', 'const _HEAD_JS_PATH = "%s";' % self.headJSPath,
             '-e', 'const _MOZINFO_JS_PATH = "%s";' % self.mozInfoJSPath,
+            '-e', 'const _PREFS_FILE = "%s";' % self.prefsFile.replace('\\', '\\\\'),
         ]
 
         if self.testingModulesDir:
             # Escape backslashes in string literal.
             sanitized = self.testingModulesDir.replace('\\', '\\\\')
             xpcsCmd.extend([
                 '-e',
                 'const _TESTING_MODULES_DIR = "%s";' % sanitized
@@ -939,16 +941,43 @@ class XPCShellTests(object):
         self.httpdJSPath = self.httpdJSPath.replace('\\', '/')
 
         self.httpdManifest = os.path.join(self.xrePath, 'components', 'httpd.manifest')
         self.httpdManifest = self.httpdManifest.replace('\\', '/')
 
         if self.mozInfo is None:
             self.mozInfo = os.path.join(self.testharnessdir, "mozinfo.json")
 
+    def buildPrefsFile(self):
+        # Create the prefs.js file
+        profile_data_dir = os.path.join(SCRIPT_DIR, 'profile_data')
+
+        # If possible, read profile data from topsrcdir. This prevents us from
+        # requiring a re-build to pick up newly added extensions in the
+        # <profile>/extensions directory.
+        if build:
+            path = os.path.join(build.topsrcdir, 'testing', 'profiles')
+            if os.path.isdir(path):
+                profile_data_dir = path
+
+        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
+            base_profiles = json.load(fh)['xpcshell']
+
+        # values to use when interpolating preferences
+        interpolation = {
+            "server": "dummyserver",
+        }
+
+        profile = Profile(profile=self.tempDir, restore=False)
+        for name in base_profiles:
+            path = os.path.join(profile_data_dir, name)
+            profile.merge(path, interpolation=interpolation)
+
+        self.prefsFile = os.path.join(profile.profile, 'user.js')
+
     def buildCoreEnvironment(self):
         """
           Add environment variables likely to be used across all platforms, including
           remote systems.
         """
         # Make assertions fatal
         self.env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
         # Crash reporting interferes with debugging
@@ -1241,16 +1270,17 @@ class XPCShellTests(object):
         self.jscovdir = options.get('jscovdir')
 
         self.testCount = 0
         self.passCount = 0
         self.failCount = 0
         self.todoCount = 0
 
         self.setAbsPath()
+        self.buildPrefsFile()
         self.buildXpcsRunArgs()
 
         self.event = Event()
 
         if not self.updateMozinfo():
             return False
 
         self.stack_fixer_function = None
@@ -1311,17 +1341,18 @@ class XPCShellTests(object):
             'stack_fixer_function': self.stack_fixer_function,
             'event': self.event,
             'cleanup_dir_list': self.cleanup_dir_list,
             'pStdout': pStdout,
             'pStderr': pStderr,
             'keep_going': self.keepGoing,
             'log': self.log,
             'interactive': self.interactive,
-            'app_dir_key': appDirKey
+            'app_dir_key': appDirKey,
+            'prefsFile': self.prefsFile,
         }
 
         if self.sequential:
             # Allow user to kill hung xpcshell subprocess with SIGINT
             # when we are only running tests sequentially.
             signal.signal(signal.SIGINT, markGotSIGINT)
 
         if self.debuggerInfo: