A FAFT test of booting an invalid USB image.

This test requires a USB disk plugged-in, which contains a Chrome OS test
image (built by "build_image --test"). On runtime, this test corrupts the
USB image and tries to boot into it. A failure is expected. It then
restores the USB image and boots into it again.

BUG=chromium-os:19710
TEST=run_remote_tests.sh --remote=$REMOTE_IP -a "xml_config=$OVERLAY_XML \
         servo_vid=0x18d1 servo_pid=0x5001" InvalidUSB

Change-Id: I661d357275627b4234e5a88f63a7e0bb133e46bf
Reviewed-on: https://gerrit.chromium.org/gerrit/13492
Reviewed-by: Todd Broch <tbroch@chromium.org>
Tested-by: Tom Wai-Hong Tam <waihong@chromium.org>
Commit-Ready: Tom Wai-Hong Tam <waihong@chromium.org>
diff --git a/server/site_tests/firmware_InvalidUSB/control b/server/site_tests/firmware_InvalidUSB/control
new file mode 100644
index 0000000..0550950
--- /dev/null
+++ b/server/site_tests/firmware_InvalidUSB/control
@@ -0,0 +1,26 @@
+# Copyright (c) 2011 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 = "Chrome OS Team"
+NAME = "firmware_InvalidUSB"
+PURPOSE = "Servo based booting an invalid USB image test"
+CRITERIA = "This test will fail if the invalid USB boots successfully"
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "firmware"
+TEST_TYPE = "server"
+
+DOC = """
+This test requires a USB disk plugged-in, which contains a Chrome OS test
+image (built by "build_image --test"). On runtime, this test corrupts the
+USB image and tries to boot into it. A failure is expected. It then
+restores the USB image and boots into it again.
+"""
+
+def run_invalidusb(machine):
+    host = hosts.create_host(machine)
+    job.run_test("firmware_InvalidUSB", host=host, cmdline_args=args,
+                 use_faft=True, disable_sysinfo=True)
+
+parallel_simple(run_invalidusb, machines)
diff --git a/server/site_tests/firmware_InvalidUSB/firmware_InvalidUSB.py b/server/site_tests/firmware_InvalidUSB/firmware_InvalidUSB.py
new file mode 100644
index 0000000..4a08ee0
--- /dev/null
+++ b/server/site_tests/firmware_InvalidUSB/firmware_InvalidUSB.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2011 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.server.cros.faftsequence import FAFTSequence
+
+
+class firmware_InvalidUSB(FAFTSequence):
+    """
+    Servo based booting an invalid USB image test.
+
+    This test requires a USB disk plugged-in, which contains a Chrome OS test
+    image (built by "build_image --test"). On runtime, this test corrupts the
+    USB image and tries to boot into it. A failure is expected. It then
+    restores the USB image and boots into it again.
+    """
+    version = 1
+
+
+    def corrupt_usb(self):
+        """Corrupt the USB image. USB plugs/unplugs happen in this method."""
+        self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
+        usb_dev = self.servo.probe_host_usb_dev()
+        self.corrupt_usb_kernel(usb_dev)
+
+
+    def restore_usb(self):
+        """Restore the USB image. USB plugs/unplugs happen in this method."""
+        self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
+        usb_dev = self.servo.probe_host_usb_dev()
+        self.restore_usb_kernel(usb_dev)
+
+
+    def insert_corrupted_usb_and_restore(self):
+        """Insert the corrupted USB on firmware screen. Then restore it."""
+        self.wait_fw_screen_and_plug_usb()
+        logging.info('Wait to ensure the USB image is unable to boot...')
+        try:
+            self.wait_for_client()
+            raise error.TestFail('Should not boot from the invalid USB image.')
+        except AssertionError:
+            logging.info(
+                'The USB image is surely unable to boot. Restore it and try...')
+
+        self.restore_usb()
+        time.sleep(self.SYNC_DELAY)
+        self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
+
+
+    def setup(self):
+        super(firmware_InvalidUSB, self).setup()
+        self.setup_dev_mode(dev_mode=False)
+        self.assert_test_image_in_usb_disk()
+        self.corrupt_usb()
+        self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
+
+
+    def cleanup(self):
+        self.restore_usb()
+        super(firmware_InvalidUSB, self).cleanup()
+
+
+    def run_once(self, host=None):
+        self.register_faft_sequence((
+            {   # Step 1, turn on the recovery boot. Remove and insert the
+                # corrupted USB stick, a boot failure is expected.
+                # Restore the USB image and boot it again.
+                'state_checker': (self.crossystem_checker, {
+                    'devsw_boot': '0',
+                    'mainfw_type': 'normal',
+                    'recoverysw_boot': '0',
+                }),
+                'userspace_action': self.faft_client.request_recovery_boot,
+                'firmware_action': self.insert_corrupted_usb_and_restore,
+                'install_deps_after_boot': True,
+            },
+            {   # Step 2, expected to boot the restored USB image and reboot.
+                'state_checker': (self.crossystem_checker, {
+                    'mainfw_type': 'recovery',
+                    'recovery_reason' : self.RECOVERY_REASON['US_TEST'],
+                    'recoverysw_boot': '0',
+                }),
+            },
+            {   # Step 3, expected to normal boot and done.
+                'state_checker': (self.crossystem_checker, {
+                    'devsw_boot': '0',
+                    'mainfw_type': 'normal',
+                    'recoverysw_boot': '0',
+                }),
+            },
+        ))
+        self.run_faft_sequence()
diff --git a/server/site_tests/suites/control.faft b/server/site_tests/suites/control.faft
index 8a3f3f5..3d2d82b 100644
--- a/server/site_tests/suites/control.faft
+++ b/server/site_tests/suites/control.faft
@@ -18,6 +18,7 @@
 TESTS_ON_NORMAL_MODE = [
     ('firmware_DevMode', {}),
     ('firmware_DevTriggerRecovery', {}),
+    ('firmware_InvalidUSB', {}),
     ('firmware_FwScreenPressPower', {}),
     ('firmware_FwScreenCloseLid', {}),
 ]