policy_DeviceScheduledCharging: AdvancedBatteryChargeMode

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

BUG=b:143173004
TEST=test_that -b sarien $DUT policy_DeviceChargingServer.AdvancedBatteryChargeMode --iterations=10

Change-Id: Id91a246ed72e81ace6da8e8e74f06e283145d51e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1968749
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/device_policy_lookup.py b/client/cros/enterprise/device_policy_lookup.py
index 9d14b4d..b320bf8 100644
--- a/client/cros/enterprise/device_policy_lookup.py
+++ b/client/cros/enterprise/device_policy_lookup.py
@@ -121,10 +121,10 @@
     'DeviceDockMacAddressSource': 'device_dock_mac_address_source.source',
     'DeviceUsbPowerShareEnabled': 'device_usb_power_share.enabled',
     'DeviceAdvancedBatteryChargeModeEnabled': 'device_advanced_battery_charge_mode.enabled',
-    'DeviceAdvancedBatteryChargeModeDayConfig': 'device_advanced_battery_charge_mode.day_config',
+    'DeviceAdvancedBatteryChargeModeDayConfig': 'device_advanced_battery_charge_mode.day_configs',
     'DeviceBatteryChargeMode': 'device_battery_charge_mode.battery_charge_mode',
     'DeviceBatteryChargeCustomStartCharging': 'device_battery_charge_mode.custom_charge_start',
     'DeviceBatteryChargeCustomStopCharging': 'device_battery_charge_mode.custom_charge_stop',
     'DeviceScheduledUpdateCheck': 'device_scheduled_update_check.device_scheduled_update_check_settings',
     'DevicePowerwashAllowed': 'device_powerwash_allowed.device_powerwash_allowed'
-  }
\ No newline at end of file
+  }
diff --git a/client/site_tests/policy_DeviceScheduledCharging/control b/client/site_tests/policy_DeviceScheduledCharging/control
new file mode 100644
index 0000000..06eb219
--- /dev/null
+++ b/client/site_tests/policy_DeviceScheduledCharging/control
@@ -0,0 +1,25 @@
+# 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.
+
+AUTHOR = 'ncrews'
+NAME = 'policy_DeviceScheduledCharging'
+TIME = 'LONG'
+TEST_CATEGORY = 'General'
+TEST_CLASS = 'enterprise'
+TEST_TYPE = 'client'
+
+DOC = '''
+Verifies that the DeviceAdvancedBatteryChargeMode and DevicePowerPeakShift
+polices work.
+
+This test is kicked off via policy_DeviceChargingServer server test. It requires
+a Servo v4 and Servo Micro attached to the DUT. Also, it requires that the
+battery is not full, and that the battery is above |MIN_BATTERY_LEVEL|, so that
+the policies can get fully tested. The server test should take care of this
+setup.
+'''
+
+args_dict = utils.args_to_dict(args)
+
+job.run_test('policy_DeviceScheduledCharging', **args_dict)
diff --git a/client/site_tests/policy_DeviceScheduledCharging/policy_DeviceScheduledCharging.py b/client/site_tests/policy_DeviceScheduledCharging/policy_DeviceScheduledCharging.py
new file mode 100644
index 0000000..810de77
--- /dev/null
+++ b/client/site_tests/policy_DeviceScheduledCharging/policy_DeviceScheduledCharging.py
@@ -0,0 +1,50 @@
+# 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.bin import utils
+from autotest_lib.client.cros import upstart
+from autotest_lib.client.cros.enterprise import charging_policy_tests
+
+
+class policy_DeviceScheduledCharging(charging_policy_tests.ChargingPolicyTest):
+    """
+    Variant of ChargingPolicyTest for schedule-based charging policies. As of
+    this writing, these features are only present on the Wilco platform.
+
+    This variation of ChargingPolicyTest only has to do a bit of warmup and
+    cleanup before and after each call to run_once(). Users should assume that
+    the EC thinks the time is |MOCK_TIME|.
+    """
+    version = 1
+
+    SYNC_EC_RTC_UPSTART_JOB = 'wilco_sync_ec_rtc'
+    # Noon on a Monday.
+    MOCK_TIME = '1/1/01 12:00:00'
+
+    def warmup(self):
+        """
+        For the first step in the test we set the EC's RTC to a consistent,
+        mock time. The EC, or Embedded Controller, is a microcontroller
+        separate from the main system-on-a-chip. The EC controls charge
+        scheduling, among other things. Setting the EC's RTC allows us to
+        use a hardcoded list of schedules as our test cases. We also need to
+        disable the upstart job that keeps the EC's RTC in sync with the
+        local time of the DUT.
+        """
+        super(policy_DeviceScheduledCharging, self).warmup()
+        upstart.stop_job(self.SYNC_EC_RTC_UPSTART_JOB)
+        utils.set_hwclock(
+                time=self.MOCK_TIME,
+                utc=False,
+                rtc='/dev/rtc1',
+                noadjfile=True)
+
+    def cleanup(self):
+        """
+        Get the DUT back to a clean state after messing with it in warmup().
+        """
+        utils.set_hwclock(
+                time='system', utc=False, rtc='/dev/rtc1', noadjfile=True)
+        upstart.restart_job(self.SYNC_EC_RTC_UPSTART_JOB)
+        super(policy_DeviceScheduledCharging, self).cleanup()
diff --git a/server/site_tests/policy_DeviceChargingServer/control.AdvancedBatteryChargeMode b/server/site_tests/policy_DeviceChargingServer/control.AdvancedBatteryChargeMode
new file mode 100644
index 0000000..afd74b6
--- /dev/null
+++ b/server/site_tests/policy_DeviceChargingServer/control.AdvancedBatteryChargeMode
@@ -0,0 +1,106 @@
+# 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.AdvancedBatteryChargeMode'
+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'
+
+# When AdvancedBatteryChargeMode is enabled, and the time is outside of the work
+# period, the battery should not charge above 90%. Therefore we need to be above
+# 90% so we can test this.
+MIN_BATTERY_LEVEL = 91
+
+# Various interesting day_config values of the policy, assuming the time
+# is noon on a Monday.
+BEFORE_START_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'charge_start_time':{'hour':22, 'minute':0},
+                 'charge_end_time':{'hour':23, 'minute':0},
+                }]
+}
+START_THRU_END_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'charge_start_time':{'hour':1, 'minute':0},
+                 'charge_end_time':{'hour':23, 'minute':0},
+                }]
+}
+AFTER_END_DAY_CONFIG = {
+    'entries': [{'day': 'MONDAY',
+                 'charge_start_time':{'hour':1, 'minute':0},
+                 'charge_end_time':{'hour':2, 'minute':0},
+                }]
+}
+
+# A test case consists of the policies, plus the expected power behavior.
+TEST_CASES = [
+    ({'DeviceAdvancedBatteryChargeModeEnabled': False,
+      'DeviceAdvancedBatteryChargeModeDayConfig': BEFORE_START_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+    ({'DeviceAdvancedBatteryChargeModeEnabled': False,
+      'DeviceAdvancedBatteryChargeModeDayConfig': START_THRU_END_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+    ({'DeviceAdvancedBatteryChargeModeEnabled': False,
+      'DeviceAdvancedBatteryChargeModeDayConfig': AFTER_END_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+
+    ({'DeviceAdvancedBatteryChargeModeEnabled': True,
+      'DeviceAdvancedBatteryChargeModeDayConfig': BEFORE_START_DAY_CONFIG},
+     'ON_AC_AND_NOT_CHARGING'),
+    ({'DeviceAdvancedBatteryChargeModeEnabled': True,
+      'DeviceAdvancedBatteryChargeModeDayConfig': START_THRU_END_DAY_CONFIG},
+     'ON_AC_AND_CHARGING'),
+    ({'DeviceAdvancedBatteryChargeModeEnabled': True,
+      'DeviceAdvancedBatteryChargeModeDayConfig': AFTER_END_DAY_CONFIG},
+     'ON_AC_AND_NOT_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_CHARGING_POLICIES = {
+    'DeviceAdvancedBatteryChargeModeEnabled': False,
+    'DeviceAdvancedBatteryChargeModeDayConfig': BEFORE_START_DAY_CONFIG
+}
+ON_AC_AND_NOT_CHARGING_POLICIES = {
+    'DeviceAdvancedBatteryChargeModeEnabled': True,
+    'DeviceAdvancedBatteryChargeModeDayConfig': BEFORE_START_DAY_CONFIG
+}
+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)