Servo AutoTest framework and pwr_button test.
BUG=chromium-os:16588
TEST=manually tested with servo and partner device
Change-Id: I1a89b60203c511ff716ba16cdea986dcfbe53659
Reviewed-on: http://gerrit.chromium.org/gerrit/3098
Reviewed-by: <craigdh@google.com>
Tested-by: <craigdh@google.com>
Reviewed-by: Nirnimesh <nirnimesh@chromium.org>
diff --git a/server/cros/__init__.py b/server/cros/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/server/cros/__init__.py
diff --git a/server/cros/servo.py b/server/cros/servo.py
new file mode 100755
index 0000000..1bfeec0
--- /dev/null
+++ b/server/cros/servo.py
@@ -0,0 +1,233 @@
+# 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.
+#
+# Expects to be run in an environment with sudo and no interactive password
+# prompt, such as within the Chromium OS development chroot.
+
+
+import time
+import xmlrpclib
+import subprocess
+
+
+class Servo:
+ """Manages control of a Servo board.
+
+ Servo is a board developed by hardware group to aide in the debug and
+ control of various partner devices. Servo's features include the simulation
+ of pressing the power button, closing the lid, and pressing Ctrl-d. This
+ class manages setting up and communicating with a servo demon (servod)
+ process. It provides both high-level functions for common servo tasks and
+ low-level functions for directly setting and reading gpios.
+ """
+
+ _server = None
+ _servod = None
+
+
+ def __init__(self, servo_port, xml_config='servo.xml', cold_reset=False):
+ """Sets up the servo communication infrastructure.
+
+ Args:
+ servo_port: Port the servod process should listen on.
+ xml_config: Configuration XML file for servod.
+ cold_reset: If True, cold reset device and boot during init,
+ otherwise perform init with device running.
+ """
+ # launch servod
+ self._launch_servod(servo_port, xml_config)
+
+ # connect to servod
+ assert servo_port
+
+ self._connect_servod(servo_port)
+ if cold_reset:
+ self._init_seq_cold_reset_devmode()
+ else:
+ self._init_seq()
+
+
+
+ def power_long_press(self):
+ """Simulate a long (8 sec) power button press."""
+ self.power_key(8)
+
+
+ def power_normal_press(self):
+ """Simulate a normal (1 sec) power button press."""
+ self.power_key(1)
+
+
+ def power_short_press(self):
+ """Simulate a short (0.1 sec) power button press."""
+ self.power_key(0.1)
+
+
+ def power_key(self, secs=1):
+ """Simulate a power button press.
+
+ Args:
+ secs: Time in seconds to simulate the keypress.
+ """
+ self.set('pwr_button', 'press')
+ time.sleep(secs)
+ self.set('pwr_button', 'release')
+
+
+ def lid_open(self):
+ """Simulate opening the lid."""
+ self.set('lid_open', 'yes')
+
+
+ def lid_close(self):
+ """Simulate closing the lid."""
+ self.set('lid_open', 'no')
+
+
+ def ctrl_d(self, secs=0.5):
+ """Simulate Ctrl-d simultaneous button presses.
+
+ Args:
+ secs: Time in seconds to simulate the keypress.
+ """
+ self.set_nocheck('kbd_en', 'on')
+ self.set_nocheck('kbd_m1', 'r2_c2')
+ self.set_nocheck('kbd_m2', 'r1_c1')
+ time.sleep(secs)
+ self.set_nocheck('kbd_en', 'off')
+
+
+ def enter_key(self, secs=0.5):
+ """Simulate Enter key button press.
+
+ Args:
+ secs: Time in seconds to simulate the keypress.
+ """
+ self.set_nocheck('kbd_en', 'on')
+ self.set_nocheck('kbd_m1', 'r3_c2')
+ time.sleep(secs)
+ self.set_nocheck('kbd_en', 'off')
+
+
+ def refresh_key(self, secs=0.5):
+ """Simulate Refresh key (F3) button press.
+
+ Args:
+ secs: Time in seconds to simulate the keypress.
+ """
+ self.set_nocheck('kbd_en', 'on')
+ self.set_nocheck('kbd_m2', 'r2_c1')
+ time.sleep(secs)
+ self.set_nocheck('kbd_en', 'off')
+
+
+ def imaginary_key(self, secs=0.5):
+ """Simulate imaginary key button press.
+
+ Maps to a key that doesn't physically exist.
+
+ Args:
+ secs: Time in seconds to simulate the keypress.
+ """
+ self.set_nocheck('kbd_en', 'on')
+ self.set_nocheck('kbd_m2', 'r3_c1')
+ time.sleep(secs)
+ self.set_nocheck('kbd_en', 'off')
+
+
+ def boot_devmode(self):
+ """Boot a dev-mode device that is powered off."""
+ self.set('pwr_button', 'release')
+ time.sleep(1)
+ self.power_normal_press()
+ time.sleep(8)
+ self.ctrl_d()
+ time.sleep(15)
+
+ def _init_seq_cold_reset_devmode(self):
+ """Cold reset, init device, and boot in dev-mode."""
+ self._cold_reset()
+ self._init_seq()
+ self.set('dev_mode', 'on')
+ self.boot_devmode()
+
+
+ def get(self, gpio_name):
+ """Get the value of a gpio from Servod."""
+ assert gpio_name
+ return self._server.get(gpio_name)
+
+
+ def set(self, gpio_name, gpio_value):
+ """Set and check the value of a gpio using Servod."""
+ assert gpio_name and gpio_value
+ self._server.set(gpio_name, gpio_value)
+ assert gpio_value == self.get(gpio_name)
+
+
+ def set_nocheck(self, gpio_name, gpio_value):
+ """Set the value of a gpio using Servod."""
+ assert gpio_name and gpio_value
+ self._server.set(gpio_name, gpio_value)
+
+
+ def __del__(self):
+ """Kill the Servod process."""
+ assert self._servod
+ # kill servod one way or another
+ try:
+ # won't work without superuser privileges
+ self._servod.terminate()
+ except:
+ # should work without superuser privileges
+ assert subprocess.call(['sudo', 'kill', str(self._servod.pid)])
+
+
+ def _launch_servod(self, servo_port, xml_config='servo.xml'):
+ """Launch the servod process.
+
+ Args:
+ servo_port: Port to start servod listening on.
+ xml_config: XML configuration file for servod.
+ """
+ self._servod = subprocess.Popen(['sudo', 'servod', '-c',
+ str(xml_config),
+ '--host=localhost',
+ '--port=' + str(servo_port)],
+ 0, None, None, None, subprocess.PIPE)
+ # wait for servod to initialize
+ timeout = 10
+ while ("Listening" not in self._servod.stderr.readline() and
+ self._servod.returncode is None and timeout > 0):
+ time.sleep(1)
+ timeout -= 1
+ assert self._servod.returncode is None and timeout
+
+
+ def _init_seq(self):
+ """Initiate starting state for servo."""
+ self.set('tx_dir', 'input')
+ self.set('servo_dut_tx', 'off')
+ self.set('lid_open', 'yes')
+ self.set('rec_mode', 'off')
+
+
+ def _cold_reset(self):
+ """Perform a cold reset of the EC.
+
+ Has the side effect of shutting off the device.
+ """
+ self.set('cold_reset', 'on')
+ time.sleep(2)
+ self.set('cold_reset', 'off')
+
+
+ def _connect_servod(self, servo_port=''):
+ """Connect to the Servod process with XMLRPC.
+
+ Args:
+ servo_port: Port the Servod process is listening on.
+ """
+ remote = 'http://localhost:%s' % servo_port
+ self._server = xmlrpclib.ServerProxy(remote)
diff --git a/server/cros/servotest.py b/server/cros/servotest.py
new file mode 100755
index 0000000..8597cbf
--- /dev/null
+++ b/server/cros/servotest.py
@@ -0,0 +1,50 @@
+# 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 subprocess
+
+from autotest_lib.server import test, autotest
+import autotest_lib.server.cros.servo
+
+class ServoTest(test.test):
+ """AutoTest test class that creates and destroys a servo object.
+
+ Servo-based server side AutoTests can inherit from this object.
+ """
+ version = 1
+ servo = None
+ _ip = None
+
+
+ def initialize(self, host, servo_port, xml_config='servo.xml'):
+ """Create a Servo object."""
+ self.servo = autotest_lib.server.cros.servo.Servo(servo_port,
+ xml_config)
+ self._ip = host.ip
+
+
+ def assert_ping(self):
+ """Ping to assert that the device is up."""
+ assert self.ping_test(self._ip)
+
+
+ def assert_pingfail(self):
+ """Ping to assert that the device is down."""
+ assert not self.ping_test(self._ip)
+
+
+ def ping_test(self, hostname, timeout=5):
+ """Verify whether a host responds to a ping.
+
+ Args:
+ hostname: Hostname to ping.
+ timeout: Time in seconds to wait for a response.
+ """
+ return subprocess.call(['ping', '-c', '1', '-W',
+ str(timeout), hostname]) == 0
+
+
+ def cleanup(self):
+ """Delete the Servo object."""
+ del self.servo
diff --git a/server/site_tests/platform_LongPressPower/control b/server/site_tests/platform_LongPressPower/control
new file mode 100644
index 0000000..dc4393e
--- /dev/null
+++ b/server/site_tests/platform_LongPressPower/control
@@ -0,0 +1,32 @@
+# 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.
+#
+# Test expects to be run on a jailbroken device in developer mode.
+
+AUTHOR = "Chrome OS Team"
+NAME = "platform_LongPressPower"
+PURPOSE = "Servo based ChromeOS functional tests."
+CRITERIA = "This test will fail if ChromesOS does not respond to a ping."
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "platform"
+TEST_TYPE = "server"
+
+DOC = """
+This test uses servo to press and hold the device power button and uses ping
+to validate behavior.
+"""
+servo_port = 9999
+servo_xml_config = 'servo.xml'
+
+def run_longpresspower(machine):
+ global servo_port
+ global servo_xml_config
+ host = hosts.create_host(machine)
+ port = servo_port
+ servo_port += 1
+ job.run_test("platform_LongPressPower", host=host, servo_port=port,
+ xml_config=servo_xml_config)
+
+parallel_simple(run_longpresspower, machines)
diff --git a/server/site_tests/platform_LongPressPower/platform_LongPressPower.py b/server/site_tests/platform_LongPressPower/platform_LongPressPower.py
new file mode 100755
index 0000000..85e0221
--- /dev/null
+++ b/server/site_tests/platform_LongPressPower/platform_LongPressPower.py
@@ -0,0 +1,23 @@
+# 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 time
+
+import autotest_lib.server.cros.servotest
+
+class platform_LongPressPower(autotest_lib.server.cros.servotest.ServoTest):
+ """Uses servo pwr_button gpio to power the host off and back on.
+ """
+ version = 1
+
+ def run_once(self, host):
+ # ensure host starts in a good state
+ self.assert_ping()
+ # turn off device
+ self.servo.power_long_press()
+ # ensure host is now off
+ self.assert_pingfail()
+ # ensure host boots
+ self.servo.boot_devmode()
+ self.assert_ping()