policy_DeviceScheduledCharging: PowerPeakShift

Add a client and server tests for the PowerPeakShift device
policies.

BUG=b:138940522
TEST=test_that -b sarien $DUT policy_DeviceChargingServer.PowerPeakShift --iterations=10

Change-Id: I21a9b0e549b5bee1d23c5121e2ef3c2132446df0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1968750
Commit-Queue: Daniel Campello <campello@chromium.org>
Tested-by: Daniel Campello <campello@chromium.org>
Reviewed-by: Oleh Lamzin <lamzin@google.com>
Reviewed-by: Derek Beckett <dbeckett@chromium.org>
diff --git a/client/cros/enterprise/charging_policy_tests.py b/client/cros/enterprise/charging_policy_tests.py
index 8caa546..135b4da 100644
--- a/client/cros/enterprise/charging_policy_tests.py
+++ b/client/cros/enterprise/charging_policy_tests.py
@@ -6,20 +6,18 @@
 from autotest_lib.client.cros.enterprise import enterprise_policy_base
 from autotest_lib.client.cros.power import power_status
 
+
 class ChargingPolicyTest(enterprise_policy_base.EnterprisePolicyTest):
     """
     A Client test that verifies that AC usage and battery charging is consistent
     with policy settings. As of this writing, these features are only present on
     the Wilco platform.
     """
-    # The Wilco EC updates it's charging behavior every 10 seconds,
-    # so give ourselves 15 seconds to notice a change in behavior.
-    POLICY_CHANGE_TIMEOUT = 15
+    # The Wilco EC updates it's charging behavior every 60 seconds,
+    # so give ourselves 120 seconds to notice a change in behavior.
+    POLICY_CHANGE_TIMEOUT = 120
 
-    def run_once(self,
-                 test_cases,
-                 min_battery_level,
-                 prep_policies):
+    def run_once(self, test_cases, min_battery_level, prep_policies):
         """
         Test a collection of cases.
 
@@ -53,7 +51,7 @@
                 failures.append(err)
         if failures:
             raise error.TestFail('Failed the following cases: {}'.format(
-                str(failures)))
+                    str(failures)))
 
     def _test_policies(self, policies, expected_behavior, min_battery_level):
         self.update_policies(device_policies=policies)
@@ -63,7 +61,7 @@
                                                     self.POLICY_CHANGE_TIMEOUT)
         except BaseException as e:
             msg = ('Expected to be {} using policies {}. Got this instead: {}'.
-                format(expected_behavior, policies, str(e)))
+                   format(expected_behavior, policies, str(e)))
             return msg
         return None
 
diff --git a/server/site_tests/policy_DeviceChargingServer/control.PowerPeakShift b/server/site_tests/policy_DeviceChargingServer/control.PowerPeakShift
new file mode 100644
index 0000000..e502a7f
--- /dev/null
+++ b/server/site_tests/policy_DeviceChargingServer/control.PowerPeakShift
@@ -0,0 +1,134 @@
+# Copyright 2020 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.
+
+from autotest_lib.client.common_lib import utils
+from autotest_lib.server.hosts import cros_host
+
+AUTHOR = 'ncrews'
+DEPENDENCIES = "servo"
+NAME = 'policy_DeviceChargingServer.PowerPeakShift'
+TIME = 'LONG'
+TEST_CATEGORY = 'General'
+TEST_CLASS = 'enterprise'
+TEST_TYPE = 'server'
+ATTRIBUTES = 'suite:ent-nightly, suite:policy'
+
+DOC = """
+Ensures the DUT's battery level is in a testable range, clears the TPM if
+needed, and then runs the specified client test to verify charging behavior
+is consistent with policies.
+"""
+
+args_dict = utils.args_to_dict(args)
+servo_args = cros_host.CrosHost.get_servo_arguments(args_dict)
+
+client_test = 'policy_DeviceScheduledCharging'
+
+# The lowest |DevicePowerPeakShift.battery_threshold| we can set is 15%. We need
+# to be able to set it to below our current battery level.
+MIN_BATTERY_LEVEL = 16
+
+# Various interesting day_config values of the policy, assuming the time
+# is noon on a Monday.
+BEFORE_START_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'start_time':{'hour':21, 'minute':0},
+                 'end_time':{'hour':22, 'minute':0},
+                 'charge_start_time':{'hour':23, 'minute':0},
+                }]
+}
+START_THRU_END_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'start_time':{'hour':1, 'minute':0},
+                 'end_time':{'hour':22, 'minute':0},
+                 'charge_start_time':{'hour':23, 'minute':0},
+                }]
+}
+END_THRU_CHARGE_START_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'start_time':{'hour':1, 'minute':0},
+                 'end_time':{'hour':2, 'minute':0},
+                 'charge_start_time':{'hour':23, 'minute':0},
+                }]
+}
+AFTER_CHARGE_START_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'start_time':{'hour':1, 'minute':0},
+                 'end_time':{'hour':2, 'minute':0},
+                 'charge_start_time':{'hour':3, 'minute':0},
+                }]
+}
+
+# A test case consists of the policies, plus the expected power behavior.
+TEST_CASES = [
+    ({'DevicePowerPeakShiftEnabled': False,
+      'DevicePowerPeakShiftBatteryThreshold': 0,
+      'DevicePowerPeakShiftDayConfig':{}},
+     'ON_AC_AND_CHARGING'),
+
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 15,
+      'DevicePowerPeakShiftDayConfig': BEFORE_START_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 15,
+      'DevicePowerPeakShiftDayConfig': START_THRU_END_DAY_CONFIG},
+     'NOT_ON_AC_AND_NOT_CHARGING'),
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 15,
+      'DevicePowerPeakShiftDayConfig': END_THRU_CHARGE_START_DAY_CONFIG},
+     'ON_AC_AND_NOT_CHARGING'),
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 15,
+      'DevicePowerPeakShiftDayConfig': AFTER_CHARGE_START_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 100,
+      'DevicePowerPeakShiftDayConfig': BEFORE_START_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 100,
+      'DevicePowerPeakShiftDayConfig': START_THRU_END_DAY_CONFIG},
+     'ON_AC_AND_NOT_CHARGING'),
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 100,
+      'DevicePowerPeakShiftDayConfig': END_THRU_CHARGE_START_DAY_CONFIG},
+     'ON_AC_AND_NOT_CHARGING'),
+    ({'DevicePowerPeakShiftEnabled': True,
+      'DevicePowerPeakShiftBatteryThreshold': 100,
+      'DevicePowerPeakShiftDayConfig': AFTER_CHARGE_START_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+]
+
+# These are used to cleanup the DUT and to prep the DUT before each test case.
+# See the test for more info.
+ON_AC_AND_NOT_CHARGING_POLICIES = {
+    'DevicePowerPeakShiftEnabled': True,
+    'DevicePowerPeakShiftBatteryThreshold': 15,
+    'DevicePowerPeakShiftDayConfig': END_THRU_CHARGE_START_DAY_CONFIG
+}
+ON_AC_AND_CHARGING_POLICIES = {
+    'DevicePowerPeakShiftEnabled': False,
+    'DevicePowerPeakShiftBatteryThreshold': 0,
+    'DevicePowerPeakShiftDayConfig': {}
+}
+PREP_POLICIES = {
+    'ON_AC_AND_CHARGING'         : (ON_AC_AND_NOT_CHARGING_POLICIES,
+                                    'ON_AC_AND_NOT_CHARGING'),
+    'ON_AC_AND_NOT_CHARGING'     : (ON_AC_AND_CHARGING_POLICIES,
+                                    'ON_AC_AND_CHARGING'),
+    'NOT_ON_AC_AND_NOT_CHARGING' : (ON_AC_AND_CHARGING_POLICIES,
+                                    'ON_AC_AND_CHARGING'),
+}
+def run(machine):
+    host = hosts.create_host(machine, servo_args=servo_args)
+    job.run_test('policy_DeviceChargingServer',
+                 host=host,
+                 client_test=client_test,
+                 test_cases=TEST_CASES,
+                 min_battery_level=MIN_BATTERY_LEVEL,
+                 prep_policies=PREP_POLICIES)
+
+parallel_simple(run, machines)