faft: add a Cr50Test class
Many cr50 tests need to be able to update cr50. Add a cr50 test class
with utilities to update cr50 and download cr50 images using gsutil.
BUG=none
BRANCH=none
TEST=none
Change-Id: I83bdc3fc41aaeb15fced04be851b5d1737326950
Signed-off-by: Mary Ruthven <mruthven@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/558650
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
diff --git a/server/cros/faft/cr50_test.py b/server/cros/faft/cr50_test.py
new file mode 100644
index 0000000..c0a268c
--- /dev/null
+++ b/server/cros/faft/cr50_test.py
@@ -0,0 +1,195 @@
+# Copyright 2017 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 os
+import time
+
+from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib.cros import cr50_utils
+from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
+from autotest_lib.server.cros import gsutil_wrapper
+
+
+class Cr50Test(FirmwareTest):
+ """
+ Base class that sets up helper objects/functions for cr50 tests.
+ """
+ version = 1
+
+ CR50_GS_URL = 'gs://chromeos-localmirror-private/distfiles/chromeos-cr50-%s/'
+ CR50_DEBUG_FILE = 'cr50_dbg_%s.bin'
+ CR50_PROD_FILE = 'cr50.%s.bin.prod'
+
+ def initialize(self, host, cmdline_args):
+ super(Cr50Test, self).initialize(host, cmdline_args)
+
+ if not hasattr(self, 'cr50'):
+ raise error.TestNAError('Test can only be run on devices with '
+ 'access to the Cr50 console')
+
+ self._original_cr50_ver = self.cr50.get_version()
+ self.host = host
+
+
+ def cleanup(self):
+ """Make sure cr50 is running the same image"""
+ running_ver = self.cr50.get_version()
+ if (hasattr(self, '_original_cr50_ver') and
+ running_ver != self._original_cr50_ver):
+ raise error.TestError('Running %s not the original cr50 version '
+ '%s' % (running_ver,
+ self._original_cr50_ver))
+
+ super(Cr50Test, self).cleanup()
+
+
+ def find_cr50_gs_image(self, filename, image_type=None):
+ """Find the cr50 gs image name
+
+ Args:
+ filename: the cr50 filename to match to
+ image_type: release or debug. If it is not specified we will search
+ both the release and debug directories
+ Returns:
+ a tuple of the gsutil bucket, filename
+ """
+ gs_url = self.CR50_GS_URL % (image_type if image_type else '*')
+ gs_filename = os.path.join(gs_url, filename)
+ bucket, gs_filename = utils.gs_ls(gs_filename)[0].rsplit('/', 1)
+ return bucket, gs_filename
+
+
+ def download_cr50_gs_image(self, filename, bucket=None, image_type=None):
+ """Get the image from gs and save it in the autotest dir
+
+ Returns:
+ the local path
+ """
+ if not bucket:
+ bucket, filename = self.find_cr50_gs_image(filename, image_type)
+
+ remote_temp_dir = '/tmp/'
+ src = os.path.join(remote_temp_dir, filename)
+ dest = os.path.join(self.resultsdir, filename)
+
+ # Copy the image to the dut
+ gsutil_wrapper.copy_private_bucket(host=self.host,
+ bucket=bucket,
+ filename=filename,
+ destination=remote_temp_dir)
+
+ self.host.get_file(src, dest)
+ return dest
+
+
+ def download_cr50_debug_image(self, devid, board_id_info=None):
+ """download the cr50 debug file
+
+ Get the file with the matching devid and board id info
+
+ Args:
+ devid: the cr50_devid string '${DEVID0} ${DEVID1}'
+ board_id_info: a list of [board id, board id mask, board id
+ flags]
+ Returns:
+ the local path to the debug image
+ """
+ filename = self.CR50_DEBUG_FILE % (devid.replace(' ', '_'))
+ if board_id_info:
+ filename += '.' + '.'.join(board_id_info)
+ return self.download_cr50_gs_image(filename, image_type='debug')
+
+
+ def download_cr50_release_image(self, rw_ver, board_id_info=None):
+ """download the cr50 release file
+
+ Get the file with the matching version and board id info
+
+ Args:
+ rw_ver: the rw version string
+ board_id_info: a list of strings [board id, board id mask, board id
+ flags]
+ Returns:
+ the local path to the release image
+ """
+ filename = self.CR50_PROD_FILE % rw_ver
+ if board_id_info:
+ filename += '.' + '.'.join(board_id_info)
+ return self.download_cr50_gs_image(filename, image_type='release')
+
+
+ def _cr50_verify_update(self, expected_ver, expect_rollback):
+ """Verify the expected version is running on cr50
+
+ Args:
+ expect_ver: The RW version string we expect to be running
+ expect_rollback: True if cr50 should have rolled back during the
+ update
+
+ Raises:
+ TestFail if there is any unexpected update state
+ """
+ running_ver = self.cr50.get_version()
+ if expected_ver != running_ver:
+ raise error.TestFail('Unexpected update ver running %s not %s' %
+ (running_ver, expected_ver))
+
+ if expect_rollback != self.cr50.rolledback():
+ raise error.TestFail('Unexpected rollback behavior: %srollback '
+ 'detected' % 'no ' if expect_rollback else '')
+
+ logging.info('RUNNING %s after %s', expected_ver,
+ 'rollback' if expect_rollback else 'update')
+
+
+ def _cr50_run_update(self, path):
+ """Install the image at path onto cr50
+
+ Args:
+ path: the location of the image to update to
+
+ Returns:
+ the rw version of the image
+ """
+ tmp_dest = '/tmp/' + os.path.basename(path)
+
+ dest, image_ver = cr50_utils.InstallImage(self.host, path, tmp_dest)
+ cr50_utils.UsbUpdater(self.host, ['-s', dest])
+ return image_ver[1]
+
+
+ def cr50_update(self, path, rollback=False, erase_nvmem=False,
+ expect_rollback=False):
+ """Attempt to update to the given image.
+
+ If rollback is True, we assume that cr50 is already running an image
+ that can rollback.
+
+ Args:
+ path: the location of the update image
+ rollback: True if we need to force cr50 to rollback to update to
+ the given image
+ erase_nvmem: True if we need to erase nvmem during rollback
+ expect_rollback: True if cr50 should rollback on its own
+
+ Raises:
+ TestFail if the update failed
+ """
+ original_ver = self.cr50.get_version()
+
+ rw_ver = self._cr50_run_update(path)
+
+ if erase_nvmem and rollback:
+ self.cr50.erase_nvmem()
+
+ # Don't erase flashinfo during rollback. That would mess with the board
+ # id
+ if rollback:
+ self.cr50.rollback()
+
+ expected_ver = original_ver if expect_rollback else rw_ver
+ # If we expect a rollback, the version should remain unchanged
+ self._cr50_verify_update(expected_ver, rollback or expect_rollback)
diff --git a/server/cros/servo/chrome_cr50.py b/server/cros/servo/chrome_cr50.py
index da94cb0..26594c0 100644
--- a/server/cros/servo/chrome_cr50.py
+++ b/server/cros/servo/chrome_cr50.py
@@ -153,6 +153,11 @@
return self.get_version_info(self.ACTIVE)
+ def get_version(self):
+ """Get the RW version"""
+ return self.get_active_version_info()[1].strip()
+
+
def using_servo_v4(self):
"""Returns true if the console is being served using servo v4"""
return 'servo_v4' in self._servo.get_servo_version()