aboutsummaryrefslogtreecommitdiff
path: root/client/site_tests/policy_PowerManagementIdleSettings/policy_PowerManagementIdleSettings.py
blob: fa2bf21314f1df6f24b2adc2cc7e5ebf7494d026 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import logging
import time

from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import cryptohome
from autotest_lib.client.cros.enterprise import enterprise_policy_base
from autotest_lib.client.cros.graphics import graphics_utils
from autotest_lib.client.cros.power import power_status, power_utils


class policy_PowerManagementIdleSettings(
          enterprise_policy_base.EnterprisePolicyTest):
    """
    Test effect of PowerManagementIdleSettings policy on Chrome OS behavior.

    This test verifies the effect of the PowerManagementIdleSettings user
    policy on specific Chrome OS client behaviors. It tests two valid values
    for the IdleAction property: 'DoNothing' and Not set, with three test
    cases: DoNothing_Continue, NotSet_Sleep, and Logout_EndSession. It also
    verifies that the screen dims after ScreenDim delay, and then turns off
    after ScreenOff delay (both delays in milliseconds).

    Note: Valid IdleAction values are 'DoNothing', 'Suspend', 'Logout', and
    'Shutdown'. This test exercises only the DoNothing and Logout actions.
    Suspend is tested by enterprise_PowerManager.py. Shutdown can be tested
    only using a Server-side AutoTest.

    Chrome reports user activity to the power manager at most every 5 seconds.
    To accomodate potential delays, the test pads the idle-action delay with
    a 5 second activity report interval.

    Several supporting policies are necessary to facillitate testing, or to
    make testing more reliable. These policies are listed below with a brief
    description of the set value.
    - WaitForInitialUserActivity=False so idle timer starts immediately after
      session starts.
    - UserActivityScreenDimDelayScale=100 to prevent increase delays when
      user activity occurs after screen dim.
    - ChromeOsLockOnIdleSuspend=False to prevent screen lock upon suspend.
    - AllowScreenLock=False to prevent manual screen lock. Will not affect
      this test, but is the safest setting.
    - AllowScreenWakeLocks=False to ignore 'keep awake' requests. Since wake
      locks are not requested during this test, ignoring them is unnecessary.
      But for safety we ignore them when testing suspend.
    - LidCloseAction=3 to invoke no action upon (accidental) lid closure.
    - ResoreOnStartup* polices are set to display the settings and policy
      pages. This is useful when debugging failures.

    """
    version = 1

    def initialize(self, **kwargs):
        """Set up local variables and ensure device is on AC power."""
        self._initialize_test_constants()
        self._power_status = power_status.get_status()
        if not self._power_status.on_ac():
            raise error.TestNAError('Test must be run with DUT on AC power.')
        self._backlight = power_utils.Backlight()
        super(policy_PowerManagementIdleSettings, self).initialize(**kwargs)

    def _initialize_test_constants(self):
        self.POLICY_NAME = 'PowerManagementIdleSettings'
        self.SCREEN_SETTLE_TIME = 0.3
        self.SCREEN_DIM_DELAY = 4
        self.IDLE_WARNING_DELAY = 6
        self.SCREEN_OFF_DELAY = 8
        self.IDLE_ACTION_DELAY = 10
        self.ACTIVITY_REPORT_INTERVAL = 5
        self.IDLE_ACTION_NOTSET = {
            'AC': {
                'Delays': {
                    'ScreenDim': (self.SCREEN_DIM_DELAY * 1000),
                    'IdleWarning': (self.IDLE_WARNING_DELAY * 1000),
                    'ScreenOff': (self.SCREEN_OFF_DELAY * 1000),
                    'Idle': (self.IDLE_ACTION_DELAY * 1000)
                }
            }
        }
        self.IDLE_ACTION_DONOTHING = {
            'AC': {
                'Delays': {
                    'ScreenDim': (self.SCREEN_DIM_DELAY * 1000),
                    'IdleWarning': (self.IDLE_WARNING_DELAY * 1000),
                    'ScreenOff': (self.SCREEN_OFF_DELAY * 1000),
                    'Idle': (self.IDLE_ACTION_DELAY * 1000)
                },
                'IdleAction': 'DoNothing'
            }
        }
        self.IDLE_ACTION_LOGOUT = {
            'AC': {
                'Delays': {
                    'ScreenDim': (self.SCREEN_DIM_DELAY * 1000),
                    'IdleWarning': (self.IDLE_WARNING_DELAY * 1000),
                    'ScreenOff': (self.SCREEN_OFF_DELAY * 1000),
                    'Idle': (self.IDLE_ACTION_DELAY * 1000)
                },
                'IdleAction': 'Logout'
            }
        }
        self.TEST_CASES = {
            'NotSet_Sleep': self.IDLE_ACTION_NOTSET,
            'DoNothing_Continue': self.IDLE_ACTION_DONOTHING,
            'Logout_EndSession': self.IDLE_ACTION_LOGOUT
        }
        self.STARTUP_URLS = ['chrome://settings', 'chrome://policy']
        self.SUPPORTING_POLICIES = {
            'WaitForInitialUserActivity': True,
            'UserActivityScreenDimDelayScale': 100,
            'ChromeOsLockOnIdleSuspend': False,
            'AllowScreenLock': False,
            'AllowScreenWakeLocks': False,
            'LidCloseAction': 3,
            'RestoreOnStartup': 4,
            'RestoreOnStartupURLs': self.STARTUP_URLS
        }


    def elapsed_time(self, start_time):
        """Get time elapsed since |start_time|.

        @param start_time: clock time from which elapsed time is measured.
        @returns time elapsed since the start time.
        """
        return time.time() - start_time


    def _simulate_user_activity(self):
        """Inject user activity via D-bus to restart idle timer.

        Note that if the screen has gone black, these use activities will
        wake up the display again. However, they will not wake up a screen
        that has merely been dimmed.

        """
        graphics_utils.click_mouse()  # Note: Duration is 0.4 seconds.
        graphics_utils.press_keys(['KEY_LEFTCTRL'])


    def _wait_for_login_status(self, attribute, value, timeout):
        """Return when attribute has value, or its current value on timeout.

        Login_status is a dictionary of attributes that describe the login
        status of the current session. It contains values for the following
        attributes: isLoggedIn, isRegularUser, isOwner, isKiosk, isGuest,
        isScreenLocked, userImage, email, and displayEmail.

        @param attribute: String attribute key to be measured.
        @param value: Boolean attribute value expected.
        @param timeout: integer seconds till timeout.
        @returns dict of login status.

        """
        attribute_value = utils.wait_for_value(
            lambda: self.cr.login_status[attribute],
            expected_value=value,
            timeout_sec=timeout)
        return attribute_value


    def _poll_until_user_is_logged_out(self, timeout):
        """Return True when user logs out, False when user remains logged in.

        @returns boolean of user logged out status.

        """
        my_result = utils.poll_for_condition(
            lambda: not cryptohome.is_vault_mounted(user=self.username,
                                                    allow_fail=True),
            exception=None,
            timeout=timeout,
            sleep_interval=2,
            desc='Polling for user to be logged out.')
        return my_result


    def _set_brightness_to_maximum(self):
        """Set screen to maximum brightness."""
        max_level = self._backlight.get_max_level()
        self._backlight.set_level(max_level)


    def _wait_for_brightness_change(self, timeout):
        """Return screen brightness on update, or current value on timeout.

        @returns float of screen brightness percentage.

        """
        initial_brightness = self._backlight.get_percent()
        current_brightness = utils.wait_for_value_changed(
            lambda: self._backlight.get_percent(),
            old_value=initial_brightness,
            timeout_sec=timeout)
        if current_brightness != initial_brightness:
            time.sleep(self.SCREEN_SETTLE_TIME)
            current_brightness = self._backlight.get_percent()
        return current_brightness


    def _test_idle_action(self, policy_value):
        """
        Verify CrOS enforces PowerManagementIdleSettings policy value.

        @param policy_value: policy value for this case.
        @raises: TestFail if idle actions are not performed after their
                 specified delays.

        """
        logging.info('Running _test_idle_action(%s)', policy_value)

        # Wait until UI settles down with user logged in.
        user_is_logged_in = self._wait_for_login_status(
            'isLoggedIn', True, self.IDLE_ACTION_DELAY)
        if not user_is_logged_in:
            raise error.TestFail('User must be logged in at start.')

        # Set screen to maxiumum brightness.
        self._set_brightness_to_maximum()
        max_brightness = self._backlight.get_percent()
        logging.info('Brightness maximized to: %.2f', max_brightness)

        # Induce user activity to start idle timer.
        self._simulate_user_activity()
        start_time = time.time()

        # Verify screen is dimmed after expected delay.
        seconds_to_dim = (
            self.SCREEN_DIM_DELAY - self.elapsed_time(start_time))
        dim_brightness = self._wait_for_brightness_change(seconds_to_dim)
        dim_elapsed_time = self.elapsed_time(start_time)
        logging.info('  Brightness dimmed to: %.2f, ', dim_brightness)
        logging.info('  after %s seconds.', dim_elapsed_time)
        if not (dim_brightness < max_brightness and dim_brightness > 0.0):
            raise error.TestFail('Screen did not dim on delay.')

        # Verify screen is turned off after expected delay.
        seconds_to_off = (
            self.SCREEN_OFF_DELAY - self.elapsed_time(start_time))
        off_brightness = self._wait_for_brightness_change(seconds_to_off)
        off_elapsed_time = self.elapsed_time(start_time)
        logging.info('  Brightness off to: %.2f, ', off_brightness)
        logging.info('  after %s seconds.', off_elapsed_time)
        if not off_brightness < dim_brightness:
            raise error.TestFail('Screen did not turn off on delay.')

        # Verify user is still logged in before IdleAction.
        user_is_logged_in = self.cr.login_status['isLoggedIn']
        if not user_is_logged_in:
            raise error.TestFail('User must be logged in before idle action.')

        # Get user logged in state after IdleAction.
        seconds_to_action = (
            self.IDLE_ACTION_DELAY + self.ACTIVITY_REPORT_INTERVAL
            - self.elapsed_time(start_time))
        try:
            user_is_logged_in = not self._poll_until_user_is_logged_out(
                seconds_to_action)
        except utils.TimeoutError:
            pass
        action_elapsed_time = self.elapsed_time(start_time)
        logging.info('  User logged out: %r, ', not user_is_logged_in)
        logging.info('  after %s seconds.', action_elapsed_time)

        # Verify user status against expected result, based on case.
        if self.case == 'NotSet_Sleep' or self.case == 'DoNothing_Continue':
            if not user_is_logged_in:
                raise error.TestFail('User should be logged in.')
        elif self.case == 'Logout_EndSession':
            if user_is_logged_in:
                raise error.TestFail('User should be logged out.')


    def run_once(self, case):
        """
        Setup and run the test configured for the specified test case.

        @param case: Name of the test case to run.

        """
        case_value = self.TEST_CASES[case]
        self.SUPPORTING_POLICIES[self.POLICY_NAME] = case_value
        self.setup_case(user_policies=self.SUPPORTING_POLICIES)
        self._test_idle_action(case_value)