xref: /aosp_15_r20/external/autotest/client/site_tests/autoupdate_EOL/autoupdate_EOL.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright 2018 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 datetime
7import logging
8
9from autotest_lib.client.bin import utils
10from autotest_lib.client.common_lib import error
11from autotest_lib.client.common_lib.cros import chrome
12from autotest_lib.client.cros.update_engine import nebraska_wrapper
13from autotest_lib.client.cros.update_engine import update_engine_test
14
15class autoupdate_EOL(update_engine_test.UpdateEngineTest):
16    """Tests end of life (EOL) behaviour."""
17    version = 1
18
19    _EXPECTED_FINAL_TITLE = 'Final software update'
20    _DAYS_BEFORE_EOL_START_WARNING = 180
21    # Value within {} expected to be number of days since epoch.
22    _EXPECTED_EOL_DATE_TEMPLATE = 'EOL_DATE={}'
23    # Value within {} expected to be the month and year.
24    _EXPECTED_WARNING_TITLE = 'Updates end {}'
25    _UNIX_EPOCH = datetime.datetime(1970, 1, 1)
26
27
28    def _get_expected_eol_date(self, eol_date):
29        """Figure out the expected eol date."""
30        return self._UNIX_EPOCH + datetime.timedelta(eol_date)
31
32
33    def _check_eol_info(self):
34        """Checks update_engines eol status."""
35        result = utils.run(
36            [self._UPDATE_ENGINE_CLIENT_CMD, '--eol_status']).stdout.strip()
37        if self._EXPECTED_EOL_DATE not in result:
38            raise error.TestFail('Expected date %s. Actual: %s' %
39                                 (self._EXPECTED_EOL_DATE, result))
40
41
42    def _check_eol_notification(self, eol_date):
43        """Checks that we are showing an EOL notification to the user."""
44        expected_eol_date = self._get_expected_eol_date(eol_date)
45        expected_warning_begins_date = (expected_eol_date
46                                        - datetime.timedelta(
47                                          self._DAYS_BEFORE_EOL_START_WARNING))
48
49        expected_final_title = self._EXPECTED_FINAL_TITLE
50        expected_warning_title = (self._EXPECTED_WARNING_TITLE.
51            format(expected_eol_date.strftime("%B %Y")))
52
53        def find_notification(expected_title):
54            """Helper to find notification."""
55            notifications = self._cr.get_visible_notifications()
56            return any([n['title'] == expected_title
57                        for n in (notifications or [])])
58
59        def check_eol_notifications():
60            """Checks if correct notification is shown."""
61            final_notification = find_notification(expected_final_title)
62            warning_notification = find_notification(expected_warning_title)
63
64            now = datetime.datetime.utcnow()
65            if expected_eol_date <= now:
66                return final_notification and not warning_notification
67            elif expected_warning_begins_date <= now:
68                return not final_notification and warning_notification
69            return not final_notification and not warning_notification
70
71        utils.poll_for_condition(condition=lambda: check_eol_notifications(),
72                                 desc='End of Life Notification UI passed',
73                                 timeout=5, sleep_interval=1)
74
75
76    def _check_eol_settings(self, eol_date):
77        """Check that the messages about EOL in Settings are correct."""
78        tab = self._cr.browser.tabs[0]
79        tab.Navigate('chrome://os-settings/help/details')
80        tab.WaitForDocumentReadyStateToBeComplete()
81        eol_js = '''
82            async function getEOL() {
83                return await import('chrome://os-settings/chromeos/os_settings.js').then(m =>
84                    m.AboutPageBrowserProxyImpl.getInstance().getEndOfLifeInfo());
85            }
86            getEOL();
87        '''
88        eol_promise = tab.EvaluateJavaScript(eol_js, promise=True)
89        expected_eol_date = self._get_expected_eol_date(eol_date)
90        eol_msg = ('This device will get automatic software and security '
91                   'updates until')
92        if expected_eol_date <= datetime.datetime.utcnow():
93            eol_msg = ('This device stopped getting automatic software and '
94                       'security updates in')
95        if eol_msg not in eol_promise['aboutPageEndOfLifeMessage']:
96            raise error.TestFail('"%s" not found in Settings.' % eol_msg)
97
98
99    def run_once(self, eol_date):
100        """
101        Checks that DUT behaves correctly in EOL scenarios.
102
103        @param eol_date: the days from epoch value passed along to
104                         NanoOmahaDevServer placed within the _eol_date tag
105                         in the Omaha response.
106
107        """
108        self._EXPECTED_EOL_DATE = \
109            self._EXPECTED_EOL_DATE_TEMPLATE.format(eol_date)
110
111        # Start a Nebraska server to return a response with eol entry.
112        with nebraska_wrapper.NebraskaWrapper(
113            log_dir=self.resultsdir) as nebraska:
114            # Try to update. It should fail with noupdate.
115            try:
116                self._check_for_update(
117                    nebraska.get_update_url(eol_date=eol_date, no_update=True),
118                    wait_for_completion=True)
119            except error.CmdError:
120                logging.info('Update failed as expected.')
121
122            self._check_eol_info()
123            with chrome.Chrome(autotest_ext=True, logged_in=True) as cr:
124                self._cr = cr
125                self._check_eol_notification(eol_date)
126                self._check_eol_settings(eol_date)
127