1# Lint as: python2, python3
2# Copyright 2020 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging
7
8from autotest_lib.client.bin import utils
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib.cros import chrome
11from autotest_lib.client.cros.update_engine import nebraska_wrapper
12from autotest_lib.client.cros.update_engine import update_engine_test
13from telemetry.core import exceptions
14
15class autoupdate_UpdateFromUI(update_engine_test.UpdateEngineTest):
16    """Starts an update from the ChromeOS Settings app. """
17    version = 1
18
19    _NOTIFICATION_INTERVAL = 1
20    _NOTIFICATION_TIMEOUT = 10
21    _NOTIFICATION_TITLE = "Update available"
22
23
24    def initialize(self):
25        """Test setup."""
26        super(autoupdate_UpdateFromUI, self).initialize()
27        self._clear_custom_lsb_release()
28        self._cr = None
29
30
31    def cleanup(self):
32        """Test cleanup. Clears the custom lsb-release used by the test. """
33        self._clear_custom_lsb_release()
34        super(autoupdate_UpdateFromUI, self).cleanup()
35
36    def _wait_for_update_notification(self):
37        """Waits for the post-update notification to appear. """
38
39        def find_notification():
40            """Polls for visibility of the post-update notification. """
41            notifications = self._cr.get_visible_notifications()
42            if notifications is None:
43                return False
44            return any(n for n in notifications
45                       if self._NOTIFICATION_TITLE in n['title'])
46
47        utils.poll_for_condition(
48                condition=find_notification,
49                exception=error.TestFail('Post-update notification not found'),
50                timeout=self._NOTIFICATION_TIMEOUT,
51                sleep_interval=self._NOTIFICATION_INTERVAL)
52
53
54    def run_once(self, payload_url):
55        """
56        Tests that a ChromeOS software update can be completed from the UI,
57        and that the post-update notification appears when the update is
58        complete.
59
60        @param payload_url: The payload url to use.
61
62        """
63        with nebraska_wrapper.NebraskaWrapper(
64            log_dir=self.resultsdir, payload_url=payload_url) as nebraska:
65            # To check the post-update notification, we need to remain logged
66            # in after the update completes. However, the DUT will auto-reboot
67            # if we log out after completing an update. This will cause the
68            # server test to fail when returning from the client test. To avoid
69            # this, we stay logged in at the end of the client test by not
70            # using a context manager for the Chrome session.
71            try:
72                self._cr = chrome.Chrome(autotest_ext=True)
73
74                # Need to create a custom lsb-release file to point the UI
75                # update button to Nebraska instead of the default update
76                # server.
77                self._create_custom_lsb_release(
78                    nebraska.get_update_url(critical_update=True))
79
80                # Go to the OS settings page and check for an update.
81                tab = self._cr.browser.tabs[0]
82                tab.Navigate('chrome://os-settings/help')
83                tab.WaitForDocumentReadyStateToBeComplete()
84                self._take_screenshot('before_check_for_updates.png')
85                request_update_js = '''
86                    async function checkForUpdate() {
87                        return await import('chrome://os-settings/chromeos/os_settings.js').then(m =>
88                          m.AboutPageBrowserProxyImpl.getInstance().requestUpdate());
89                    }
90                    checkForUpdate();
91                '''
92                try:
93                    tab.EvaluateJavaScript(request_update_js)
94                except exceptions.EvaluateException:
95                    raise error.TestFail(
96                        'Failed to find and click Check For Updates button.')
97                self._take_screenshot('after_check_for_updates.png')
98                self._wait_for_update_to_complete()
99
100            except Exception as e:
101                # The update didn't complete, so we can close the Chrome
102                # session without worrying about auto-reboot.
103                logging.exception("Failed to perform the update: %s", e)
104                if self._cr:
105                    self._cr.close()
106                raise error.TestFail("Failed to perform the update: %s" % e)
107
108            self._wait_for_update_notification()
109