diff --git a/testing/mochitest/runjunit.py b/testing/mochitest/runjunit.py index ea50d4923284..8361f6aa79e3 100644 --- a/testing/mochitest/runjunit.py +++ b/testing/mochitest/runjunit.py @@ -3,7 +3,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import argparse -import json import os import posixpath import re @@ -17,7 +16,7 @@ import mozinfo import mozlog import moznetwork from mozdevice import ADBAndroid -from mozprofile import Profile, Preferences, DEFAULT_PORTS +from mozprofile import Profile, DEFAULT_PORTS from mozprofile.permissions import ServerLocations from runtests import MochitestDesktop, update_mozinfo @@ -99,29 +98,14 @@ class JUnitTestRunner(MochitestDesktop): Create a local profile with test prefs and proxy definitions and push it to the remote device. """ - preferences = [os.path.join(here, 'profile_data', 'common', 'user.js')] - prefs = {} - for path in preferences: - prefs.update(Preferences.read_prefs(path)) - interpolation = { - "server": "%s:%s" % - (self.options.webServer, self.options.httpPort)} - - prefs = json.loads(json.dumps(prefs).format(**interpolation)) - for pref in prefs: - prefs[pref] = Preferences.cast(prefs[pref]) - - proxy = {'remote': self.options.webServer, - 'http': self.options.httpPort, - 'https': self.options.sslPort, - 'ws': self.options.sslPort - } - - self.profile = Profile(locations=self.locations, preferences=prefs, - proxy=proxy) + self.profile = Profile(locations=self.locations, proxy=self.proxy(self.options)) self.options.profilePath = self.profile.profile + # Set preferences + self.merge_base_profiles(self.options) + self.profile.set_preferences(self.extraPrefs(self.options.extraPrefs)) + if self.fillCertificateDB(self.options): self.log.error("Certificate integration failed") diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index 15b7ad97578b..bbf24fbdab0b 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -52,7 +52,6 @@ from manifestparser.filters import ( subsuite, tags, ) -from six import string_types try: from marionette_driver.addons import Addons @@ -852,6 +851,7 @@ class MochitestDesktop(object): mediaDevices = None patternFiles = {} + base_profiles = ('common',) # XXX use automation.py for test name to avoid breaking legacy # TODO: replace this with 'runtests.py' or 'mochitest' or the like @@ -910,15 +910,20 @@ class MochitestDesktop(object): kwargs['log'] = self.log return test_environment(**kwargs) - def extraPrefs(self, extraPrefs): - """interpolate extra preferences from option strings""" + def extraPrefs(self, prefs): + """Interpolate extra preferences from option strings""" try: - return dict(parseKeyValue(extraPrefs, context='--setpref=')) + prefs = dict(parseKeyValue(prefs, context='--setpref=')) except KeyValueParseError as e: print(str(e)) sys.exit(1) + for pref, value in prefs.items(): + value = Preferences.cast(value) + prefs[pref] = value + return prefs + def getFullPath(self, path): " Get an absolute path relative to self.oldcwd." return os.path.normpath( @@ -1820,57 +1825,46 @@ toolbar#nav-bar { os.unlink(pwfilePath) return 0 + def proxy(self, options): + # proxy + # use SSL port for legacy compatibility; see + # - https://bugzilla.mozilla.org/show_bug.cgi?id=688667#c66 + # - https://bugzilla.mozilla.org/show_bug.cgi?id=899221 + # - https://github.com/mozilla/mozbase/commit/43f9510e3d58bfed32790c82a57edac5f928474d + # 'ws': str(self.webSocketPort) + return { + 'remote': options.webServer, + 'http': options.httpPort, + 'https': options.sslPort, + 'ws': options.sslPort, + } + + def merge_base_profiles(self, options): + """Merge extra profile data from testing/profiles.""" + 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 + # /extensions directory. + if build_obj: + path = os.path.join(build_obj.topsrcdir, 'testing', 'profiles') + if os.path.isdir(path): + profile_data_dir = path + + # values to use when interpolating preferences + interpolation = { + "server": "%s:%s" % (options.webServer, options.httpPort), + } + + for profile in self.base_profiles: + path = os.path.join(profile_data_dir, profile) + self.profile.merge(path, interpolation=interpolation) + def buildProfile(self, options): """ create the profile and add optional chrome bits and files if requested """ - if options.flavor == 'browser' and options.timeout: - options.extraPrefs.append( - "testing.browserTestHarness.timeout=%d" % - options.timeout) - # browser-chrome tests use a fairly short default timeout of 45 seconds; - # this is sometimes too short on asan and debug, where we expect reduced - # performance. - if (mozinfo.info["asan"] or mozinfo.info["debug"]) and \ - options.flavor == 'browser' and options.timeout is None: - self.log.info("Increasing default timeout to 90 seconds") - options.extraPrefs.append("testing.browserTestHarness.timeout=90") - - options.extraPrefs.append( - "browser.tabs.remote.autostart=%s" % - ('true' if options.e10s else 'false')) - - options.extraPrefs.append( - "dom.ipc.tabs.nested.enabled=%s" % - ('true' if options.nested_oop else 'false')) - - options.extraPrefs.append( - "idle.lastDailyNotification=%d" % - int(time.time())) - - # Enable tracing output for detailed failures in case of - # failing connection attempts, and hangs (bug 1397201) - options.extraPrefs.append("marionette.log.level=%s" % "TRACE") - - if getattr(self, 'testRootAbs', None): - options.extraPrefs.append( - "mochitest.testRoot=%s" % - self.testRootAbs) - # get extensions to install extensions = self.getExtensionsToInstall(options) - # preferences - preferences = [os.path.join(SCRIPT_DIR, 'profile_data', 'common', 'user.js')] - prefs = {} - for path in preferences: - prefs.update(Preferences.read_prefs(path)) - - prefs.update(self.extraPrefs(options.extraPrefs)) - - # Bug 1262954: For windows XP + e10s disable acceleration - if platform.system() in ("Windows", "Microsoft") and \ - '5.1' in platform.version() and options.e10s: - prefs['layers.acceleration.disabled'] = True - # Whitelist the _tests directory (../..) so that TESTING_JS_MODULES work tests_dir = os.path.dirname(os.path.dirname(SCRIPT_DIR)) sandbox_whitelist_paths = [tests_dir] + options.sandboxReadWhitelist @@ -1880,42 +1874,12 @@ toolbar#nav-bar { sandbox_whitelist_paths = map(lambda p: os.path.join(p, ""), sandbox_whitelist_paths) - # interpolate preferences - interpolation = { - "server": "%s:%s" % - (options.webServer, options.httpPort)} - - for pref in prefs: - if isinstance(prefs[pref], string_types): - prefs[pref] = prefs[pref].format(**interpolation) - prefs[pref] = Preferences.cast(prefs[pref]) - # TODO: make this less hacky - # https://bugzilla.mozilla.org/show_bug.cgi?id=913152 - - # proxy - # use SSL port for legacy compatibility; see - # - https://bugzilla.mozilla.org/show_bug.cgi?id=688667#c66 - # - https://bugzilla.mozilla.org/show_bug.cgi?id=899221 - # - https://github.com/mozilla/mozbase/commit/43f9510e3d58bfed32790c82a57edac5f928474d - # 'ws': str(self.webSocketPort) - proxy = {'remote': options.webServer, - 'http': options.httpPort, - 'https': options.sslPort, - 'ws': options.sslPort - } - - # See if we should use fake media devices. - if options.useTestMediaDevices: - prefs['media.audio_loopback_dev'] = self.mediaDevices['audio'] - prefs['media.video_loopback_dev'] = self.mediaDevices['video'] - - # create a profile + # Create the profile self.profile = Profile(profile=options.profilePath, addons=extensions, locations=self.locations, - preferences=prefs, - proxy=proxy, - whitelistpaths=sandbox_whitelist_paths + proxy=self.proxy(options), + whitelistpaths=sandbox_whitelist_paths, ) # Fix options.profilePath for legacy consumers. @@ -1932,6 +1896,47 @@ toolbar#nav-bar { "TEST-UNEXPECTED-FAIL | runtests.py | Certificate integration failed") return None + # Set preferences in the following order (latter overrides former): + # 1) Preferences from base profile (e.g from testing/profiles) + # 2) Prefs hardcoded in this function + # 3) Prefs from --setpref + + # Prefs from base profiles + self.merge_base_profiles(options) + + # Hardcoded prefs (TODO move these into a base profile) + prefs = { + "browser.tabs.remote.autostart": options.e10s, + "dom.ipc.tabs.nested.enabled": options.nested_oop, + "idle.lastDailyNotification": int(time.time()), + # Enable tracing output for detailed failures in case of + # failing connection attempts, and hangs (bug 1397201) + "marionette.log.level": "TRACE", + } + + if options.flavor == 'browser' and options.timeout: + prefs["testing.browserTestHarness.timeout"] = options.timeout + + # browser-chrome tests use a fairly short default timeout of 45 seconds; + # this is sometimes too short on asan and debug, where we expect reduced + # performance. + if (mozinfo.info["asan"] or mozinfo.info["debug"]) and \ + options.flavor == 'browser' and options.timeout is None: + self.log.info("Increasing default timeout to 90 seconds") + prefs["testing.browserTestHarness.timeout"] = 90 + + if getattr(self, 'testRootAbs', None): + prefs['mochitest.testRoot'] = self.testRootAbs + + # See if we should use fake media devices. + if options.useTestMediaDevices: + prefs['media.audio_loopback_dev'] = self.mediaDevices['audio'] + prefs['media.video_loopback_dev'] = self.mediaDevices['video'] + + self.profile.set_preferences(prefs) + + # Extra prefs from --setpref + self.profile.set_preferences(self.extraPrefs(options.extraPrefs)) return manifest def getGMPPluginPath(self, options): diff --git a/testing/mochitest/tests/python/python.ini b/testing/mochitest/tests/python/python.ini index b7a86d2cb700..690ae7798387 100644 --- a/testing/mochitest/tests/python/python.ini +++ b/testing/mochitest/tests/python/python.ini @@ -1,6 +1,7 @@ [DEFAULT] subsuite = mochitest -sequential = true [test_basic_mochitest_plain.py] +sequential = true [test_get_active_tests.py] +[test_build_profile.py] diff --git a/testing/mochitest/tests/python/test_build_profile.py b/testing/mochitest/tests/python/test_build_profile.py new file mode 100644 index 000000000000..09f9b10bd643 --- /dev/null +++ b/testing/mochitest/tests/python/test_build_profile.py @@ -0,0 +1,77 @@ +# 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 __future__ import print_function, unicode_literals + +import os +from argparse import Namespace + +from mozprofile.prefs import Preferences +from mozprofile import Profile +from six import string_types + +import mozunit +import pytest +from conftest import setup_args + + +@pytest.fixture +def build_profile(setup_test_harness, parser): + setup_test_harness(*setup_args) + runtests = pytest.importorskip('runtests') + md = runtests.MochitestDesktop('plain', {'log_tbpl': '-'}) + + options = parser.parse_args([]) + parser.validate(options) + options = vars(options) + + def inner(**kwargs): + opts = options.copy() + opts.update(kwargs) + + return md, md.buildProfile(Namespace(**opts)) + + return inner + + +@pytest.fixture +def profile_data_dir(build_obj): + return os.path.join(build_obj.topsrcdir, 'testing', 'profiles') + + +def test_common_prefs_are_all_set(build_profile, profile_data_dir): + # We set e10s=False here because MochitestDesktop.buildProfile overwrites + # the value defined in the base profile. + # TODO stop setting browser.tabs.remote.autostart in the base profile + md, result = build_profile(e10s=False) + + # build the expected prefs + expected_prefs = {} + for profile in md.base_profiles: + for name in Profile.preference_file_names: + path = os.path.join(profile_data_dir, profile, name) + if os.path.isfile(path): + expected_prefs.update(Preferences.read_prefs(path)) + + # read the actual prefs + actual_prefs = {} + for name in Profile.preference_file_names: + path = os.path.join(md.profile.profile, name) + if os.path.isfile(path): + actual_prefs.update(Preferences.read_prefs(path)) + + # keep this in sync with the values in MochitestDesktop.merge_base_profiles + interpolation = { + 'server': '127.0.0.1:8888', + } + for k, v in expected_prefs.items(): + if isinstance(v, string_types): + v = v.format(**interpolation) + + assert k in actual_prefs + assert k and actual_prefs[k] == v + + +if __name__ == '__main__': + mozunit.main()