Bug 1142805 - Set update channel via default branch. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 22 Nov 2016 13:15:33 +0100
changeset 442515 dfe0444ea3d544e802ce962e87f962fd6fa71b40
parent 442068 0534254e9a40b4bade2577c631fe4cfa0b5db41d
child 442516 ad492ecc3fec8a4413ced19c606701651fe79262
child 442522 20ce1f710f82e2e0c6f3aecb76caaef9515ad511
push id36712
push userbmo:hskupin@gmail.com
push dateTue, 22 Nov 2016 16:34:20 +0000
bugs1142805
milestone53.0a1
Bug 1142805 - Set update channel via default branch. MozReview-Commit-ID: 5iHnNhLRCK4
testing/firefox-ui/harness/firefox_ui_harness/testcases.py
testing/firefox-ui/tests/puppeteer/test_software_update.py
testing/puppeteer/firefox/firefox_puppeteer/api/software_update.py
--- a/testing/firefox-ui/harness/firefox_ui_harness/testcases.py
+++ b/testing/firefox-ui/harness/firefox_ui_harness/testcases.py
@@ -31,24 +31,22 @@ class UpdateTestCase(PuppeteerMixin, Mar
     # raised. See:
     # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/update/nsUpdateService.js?rev=a9240b1eb2fb#4813
     # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/update/nsUpdateService.js?rev=a9240b1eb2fb#4756
     PREF_APP_UPDATE_ALTWINDOWTYPE = 'app.update.altwindowtype'
 
     def __init__(self, *args, **kwargs):
         super(UpdateTestCase, self).__init__(*args, **kwargs)
 
+        self.update_channel = kwargs.pop('update_channel')
         self.update_url = kwargs.pop('update_url')
 
         self.target_buildid = kwargs.pop('update_target_buildid')
         self.target_version = kwargs.pop('update_target_version')
 
-        self.update_channel = kwargs.pop('update_channel')
-        self.default_update_channel = None
-
         self.update_mar_channels = set(kwargs.pop('update_mar_channels'))
         self.default_mar_channels = None
 
         self.updates = []
 
     def setUp(self, is_fallback=False):
         super(UpdateTestCase, self).setUp()
 
@@ -58,27 +56,16 @@ class UpdateTestCase(PuppeteerMixin, Mar
         # Bug 604364 - Preparation to test multiple update steps
         self.current_update_index = 0
 
         # Ensure that there exists no already partially downloaded update
         self.remove_downloaded_update()
 
         self.set_preferences_defaults()
 
-        # If requested modify the default update channel. It will be active
-        # after the next restart of the application
-        # Bug 1142805 - Modify file via Python directly
-        if self.update_channel:
-            # Backup the original content and the path of the channel-prefs.js file
-            self.default_update_channel = {
-                'content': self.software_update.update_channel.file_contents,
-                'path': self.software_update.update_channel.file_path,
-            }
-            self.software_update.update_channel.default_channel = self.update_channel
-
         # If requested modify the list of allowed MAR channels
         # Bug 1142805 - Modify file via Python directly
         if self.update_mar_channels:
             # Backup the original content and the path of the update-settings.ini file
             self.default_mar_channels = {
                 'content': self.software_update.mar_channels.config_file_contents,
                 'path': self.software_update.mar_channels.config_file_path,
             }
@@ -93,19 +80,16 @@ class UpdateTestCase(PuppeteerMixin, Mar
         self.updates = [{
             'build_pre': self.software_update.build_info,
             'build_post': None,
             'fallback': is_fallback,
             'patch': {},
             'success': False,
         }]
 
-        self.assertEqual(self.software_update.update_channel.default_channel,
-                         self.software_update.update_channel.channel)
-
         self.assertTrue(self.update_mar_channels.issubset(
                         self.software_update.mar_channels.channels),
                         'Allowed MAR channels have been set: expected "{}" in "{}"'.format(
                             ', '.join(self.update_mar_channels),
                             ', '.join(self.software_update.mar_channels.channels)))
 
         # Check if the user has permissions to run the update
         self.assertTrue(self.software_update.allowed,
@@ -377,40 +361,31 @@ class UpdateTestCase(PuppeteerMixin, Mar
     def restart(self, *args, **kwargs):
         super(UpdateTestCase, self).restart(*args, **kwargs)
 
         # After a restart default preference values as set in the former session are lost.
         # Make sure that any of those are getting restored.
         self.set_preferences_defaults()
 
     def restore_config_files(self):
-        # Reset channel-prefs.js file if modified
-        try:
-            if self.default_update_channel:
-                path = self.default_update_channel['path']
-                self.logger.info('Restoring channel defaults for: {}'.format(path))
-                with open(path, 'w') as f:
-                    f.write(self.default_update_channel['content'])
-        except IOError:
-            self.logger.error('Failed to reset the default update channel.',
-                              exc_info=True)
-
         # Reset update-settings.ini file if modified
         try:
             if self.default_mar_channels:
                 path = self.default_mar_channels['path']
                 self.logger.info('Restoring mar channel defaults for: {}'.format(path))
                 with open(path, 'w') as f:
                     f.write(self.default_mar_channels['content'])
         except IOError:
             self.logger.error('Failed to reset the default mar channels.',
                               exc_info=True)
 
     def set_preferences_defaults(self):
         """Set the default value for specific preferences to force its usage."""
+        if self.update_channel:
+            self.software_update.update_channel = self.update_channel
         if self.update_url:
             self.puppeteer.prefs.set_pref("app.update.url", self.update_url,
                                           default_branch=True)
 
     def wait_for_download_finished(self, window, timeout=TIMEOUT_UPDATE_DOWNLOAD):
         """ Waits until download is completed.
 
         :param window: Instance of :class:`AboutWindow` or :class:`UpdateWizardDialog`.
--- a/testing/firefox-ui/tests/puppeteer/test_software_update.py
+++ b/testing/firefox-ui/tests/puppeteer/test_software_update.py
@@ -12,21 +12,24 @@ from marionette import MarionetteTestCas
 class TestSoftwareUpdate(PuppeteerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(TestSoftwareUpdate, self).setUp()
 
         self.software_update = SoftwareUpdate(self.marionette)
 
         self.saved_mar_channels = self.software_update.mar_channels.channels
+        self.saved_update_channel = self.software_update.update_channel
+
         self.software_update.mar_channels.channels = set(['expected', 'channels'])
 
     def tearDown(self):
         try:
             self.software_update.mar_channels.channels = self.saved_mar_channels
+            self.software_update.update_channel = self.saved_update_channel
         finally:
             super(TestSoftwareUpdate, self).tearDown()
 
     def test_abi(self):
         self.assertTrue(self.software_update.ABI)
 
     def test_allowed(self):
         self.assertTrue(self.software_update.allowed)
@@ -36,17 +39,17 @@ class TestSoftwareUpdate(PuppeteerMixin,
         self.assertEqual(build_info['disabled_addons'], None)
         self.assertIn('Mozilla/', build_info['user_agent'])
         self.assertEqual(build_info['mar_channels'], set(['expected', 'channels']))
         self.assertTrue(build_info['version'])
         self.assertTrue(build_info['buildid'].isdigit())
         self.assertTrue(build_info['locale'])
         self.assertIn('force=1', build_info['update_url'])
         self.assertIn('xml', build_info['update_snippet'])
-        self.assertEqual(build_info['channel'], self.software_update.update_channel.channel)
+        self.assertEqual(build_info['channel'], self.software_update.update_channel)
 
     def test_force_fallback(self):
         status_file = os.path.join(self.software_update.staging_directory, 'update.status')
 
         try:
             self.software_update.force_fallback()
             with open(status_file, 'r') as f:
                 content = f.read()
@@ -63,42 +66,20 @@ class TestSoftwareUpdate(PuppeteerMixin,
         self.assertIn('force=1', update_url)
 
     def test_os_version(self):
         self.assertTrue(self.software_update.os_version)
 
     def test_staging_directory(self):
         self.assertTrue(self.software_update.staging_directory)
 
-
-class TestUpdateChannel(PuppeteerMixin, MarionetteTestCase):
-
-    def setUp(self):
-        super(TestUpdateChannel, self).setUp()
-
-        self.software_update = SoftwareUpdate(self.marionette)
-
-        self.saved_channel = self.software_update.update_channel.default_channel
-        self.software_update.update_channel.default_channel = 'expected_channel'
-
-    def tearDown(self):
-        try:
-            self.software_update.update_channel.default_channel = self.saved_channel
-        finally:
-            super(TestUpdateChannel, self).tearDown()
-
-    def test_update_channel_channel(self):
-        self.assertEqual(self.software_update.update_channel.channel, self.saved_channel)
-
-    def test_update_channel_default_channel(self):
-        self.assertEqual(self.software_update.update_channel.default_channel, 'expected_channel')
-
-    def test_update_channel_set_default_channel(self):
-        self.software_update.update_channel.default_channel = 'new_channel'
-        self.assertEqual(self.software_update.update_channel.default_channel, 'new_channel')
+    def test_set_update_channel(self):
+        self.software_update.update_channel = 'new_channel'
+        self.assertEqual(self.puppeteer.prefs.get_pref('app.update.channel', default_branch=True),
+                         'new_channel')
 
 
 class TestMARChannels(PuppeteerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(TestMARChannels, self).setUp()
 
         self.software_update = SoftwareUpdate(self.marionette)
--- a/testing/puppeteer/firefox/firefox_puppeteer/api/software_update.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/api/software_update.py
@@ -1,14 +1,13 @@
 # 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 os
-import re
 
 import mozinfo
 
 from firefox_puppeteer.base import BaseLib
 from firefox_puppeteer.api.appinfo import AppInfo
 from firefox_puppeteer.api.prefs import Preferences
 
 
@@ -159,27 +158,27 @@ class MARChannels(BaseLib):
         """
         self.channels = self.channels - set(channels)
 
 
 class SoftwareUpdate(BaseLib):
     """The SoftwareUpdate API adds support for an easy access to the update process."""
     PREF_APP_DISTRIBUTION = 'distribution.id'
     PREF_APP_DISTRIBUTION_VERSION = 'distribution.version'
+    PREF_APP_UPDATE_CHANNEL = 'app.update.channel'
     PREF_APP_UPDATE_URL = 'app.update.url'
     PREF_APP_UPDATE_URL_OVERRIDE = 'app.update.url.override'
     PREF_DISABLED_ADDONS = 'extensions.disabledAddons'
 
     def __init__(self, marionette):
         BaseLib.__init__(self, marionette)
 
         self.app_info = AppInfo(marionette)
         self.prefs = Preferences(marionette)
 
-        self._update_channel = UpdateChannel(marionette)
         self._mar_channels = MARChannels(marionette)
         self._active_update = ActiveUpdate(marionette)
 
     @property
     def ABI(self):
         """Get the customized ABI for the update service.
 
         :returns: ABI version
@@ -219,17 +218,17 @@ class SoftwareUpdate(BaseLib):
         """Return information of the current build version
 
         :returns: A dictionary of build information
         """
         update_url = self.get_update_url(True)
 
         return {
             'buildid': self.app_info.appBuildID,
-            'channel': self.update_channel.channel,
+            'channel': self.update_channel,
             'disabled_addons': self.prefs.get_pref(self.PREF_DISABLED_ADDONS),
             'locale': self.app_info.locale,
             'mar_channels': self.mar_channels.channels,
             'update_url': update_url,
             'update_snippet': self.get_update_snippet(update_url),
             'user_agent': self.app_info.user_agent,
             'version': self.app_info.version
         }
@@ -290,17 +289,17 @@ class SoftwareUpdate(BaseLib):
             osVersion = encodeURIComponent(osVersion);
           }
           return osVersion;
         """)
 
     @property
     def patch_info(self):
         """ Returns information of the active update in the queue."""
-        info = {'channel': self.update_channel.channel}
+        info = {'channel': self.update_channel}
 
         if (self.active_update.exists):
             info['buildid'] = self.active_update.buildID
             info['is_complete'] = self.is_complete_update
             info['size'] = self.active_update.selected_patch['size']
             info['type'] = self.update_type
             info['url_mirror'] = \
                 self.active_update.selected_patch['finalURL'] or 'n/a'
@@ -314,18 +313,27 @@ class SoftwareUpdate(BaseLib):
         return self.marionette.execute_script("""
           let aus = Components.classes['@mozilla.org/updates/update-service;1']
                     .getService(Components.interfaces.nsIApplicationUpdateService);
           return aus.getUpdatesDirectory().path;
         """)
 
     @property
     def update_channel(self):
-        """ Holds a reference to an :class:`UpdateChannel` object."""
-        return self._update_channel
+        """Return the currently used update channel."""
+        return self.prefs.get_pref(self.PREF_APP_UPDATE_CHANNEL, default_branch=True)
+
+    @update_channel.setter
+    def update_channel(self, channel):
+        """Set the update channel to be used for update checks.
+
+        :param channel: New update channel to use
+
+        """
+        self.prefs.set_pref(self.PREF_APP_UPDATE_CHANNEL, channel, default_branch=True)
 
     @property
     def update_type(self):
         """Returns the type of the active update."""
         return self.active_update.type
 
     def force_fallback(self):
         """Update the update.status file and set the status to 'failed:6'"""
@@ -368,61 +376,8 @@ class SoftwareUpdate(BaseLib):
             if '?' in url:
                 url += '&'
             else:
                 url += '?'
             url += 'force=1'
 
         return url
 
-
-class UpdateChannel(BaseLib):
-    """Class to handle the update channel as listed in channel-prefs.js"""
-    REGEX_UPDATE_CHANNEL = re.compile(r'("app\.update\.channel", ")([^"].*)(?=")')
-
-    def __init__(self, marionette):
-        BaseLib.__init__(self, marionette)
-
-        self.prefs = Preferences(marionette)
-
-        self.file_path = self.marionette.execute_script("""
-          Components.utils.import('resource://gre/modules/Services.jsm');
-
-          let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile);
-          file.append('channel-prefs.js');
-
-          return file.path;
-        """)
-
-    @property
-    def file_contents(self):
-        """The contents of the channel-prefs.js file."""
-        with open(self.file_path) as f:
-            return f.read()
-
-    @property
-    def channel(self):
-        """The name of the update channel as stored in the
-        app.update.channel pref."""
-        return self.prefs.get_pref('app.update.channel', True)
-
-    @property
-    def default_channel(self):
-        """Get the default update channel
-
-        :returns: Current default update channel
-        """
-        matches = re.search(self.REGEX_UPDATE_CHANNEL, self.file_contents).groups()
-        assert len(matches) == 2, 'Update channel value has been found'
-
-        return matches[1]
-
-    @default_channel.setter
-    def default_channel(self, channel):
-        """Set default update channel.
-
-        :param channel: New default update channel
-        """
-        assert channel, 'Update channel has been specified'
-        new_content = re.sub(
-            self.REGEX_UPDATE_CHANNEL, r'\g<1>' + channel, self.file_contents)
-        with open(self.file_path, 'w') as f:
-            f.write(new_content)