blob: 257a49daf41c13b117355afeb78f39abde0b6601 [file] [log] [blame]
# Copyright 2012 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.
"""A factory test to interactively check a sequence of shell commands on DUT.
Description
-----------
This test is very similar to ``exec_shell`` test, except that
``line_check_item`` has the ability to get operator confirmation no matter if
the shell command success or not.
If you simply want to run few commands (and fail if the return value is
non-zero), especially if you need to run commands on host or station, use
``exec_shell``.
If you simply want to run few commands and don't care if the commands success
or not, use ``exec_shell`` and add ``'|| true'`` for all commands.
If you are running some commands and some (at least one) commands need operator
to judge if that passed or not manually (for example checking if some LED has
lightened properly), use ``line_check_item``.
``line_check_item`` evaluates and executes the given ``items`` argument, where
each item is a sequence with 3 elements (instruction, command, judge_to_pass):
1. ``instruction``: A string passed to ``i18n.Translated`` to display on UI.
2. ``command``: A sequence or str as shell command to be passed to dut.Popen.
3. ``judge_to_pass``: A boolean value to indicate if these commands need user to
judge pass or failure, even if the command returns zero (success).
Test Procedure
--------------
The test will go through each item and:
1. Display instruction on UI.
2. Execute command.
3. If judge_to_pass is True, wait for operator to confirm if passed or not.
Dependency
----------
The commands specified in items must be available on DUT.
Examples
--------
To turn on a 'lightbar' component and wait for confirmation, add this to test
list::
{
"pytest_name": "line_check_item",
"args": {
"items": [
["i18n! Initialization", "lightbar init", false],
["i18n! Turn on lightbar", "lightbar enable", true],
["i18n! Clean up", "lightbar reset", false]
],
"title": "i18n! LED Test"
}
}
"""
import collections
import subprocess
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test import event_log # TODO(chuntsen): Deprecate event log.
from cros.factory.test import session
from cros.factory.test import i18n
from cros.factory.test.i18n import arg_utils as i18n_arg_utils
from cros.factory.test import test_case
from cros.factory.test import test_ui
from cros.factory.testlog import testlog
from cros.factory.utils.arg_utils import Arg
CheckItem = collections.namedtuple('CheckItem',
'instruction command judge_to_pass')
class LineCheckItemTest(test_case.TestCase):
"""Test a sequence of commands are successful or not.
Properties:
_items: A sequence of CheckItem.
"""
ARGS = [
i18n_arg_utils.I18nArg('title', 'test title.'),
Arg('items', list,
('A sequence of items to check. Each item is a sequence: '
' [instruction, command, judge_to_pass].')),
Arg('is_station', bool,
('Run the given commands on station (usually local host) instead of '
'DUT, for example preparing connection configuration.'),
default=False),
]
def setUp(self):
"""Initializes _items"""
self._dut = (device_utils.CreateStationInterface()
if self.args.is_station else
device_utils.CreateDUTInterface())
self._items = []
for item in self.args.items:
if isinstance(item, list) and len(item) == 3:
check_item = CheckItem(i18n.Translated(item[0], translate=False),
item[1], item[2])
else:
raise ValueError('Unknown item %r in args.items.' % item)
self._items.append(check_item)
if not any(item.judge_to_pass for item in self._items):
raise ValueError('If judge_to_pass is not needed, use `exec_shell` test.')
# Group checker for Testlog.
self.group_checker = testlog.GroupParam(
'checked_item', ['command', 'retcode', 'stdout', 'stderr'])
testlog.UpdateParam('command', param_type=testlog.PARAM_TYPE.argument)
def runTest(self):
"""Main entrance of the test."""
self.ui.SetTitle(self.args.title)
for item in self._items:
command = item.command
self.ui.SetState(item.instruction)
process = self._dut.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
retcode = process.returncode
event_log.Log('checked_item', command=command, retcode=retcode,
stdout=stdout, stderr=stderr)
with self.group_checker:
testlog.LogParam('command', command)
testlog.LogParam('retcode', retcode)
testlog.LogParam('stdout', stdout)
testlog.LogParam('stderr', stderr)
if retcode:
session.console.info('%s: Exit code %d\nstdout: %s\nstderr: %s',
command, retcode, stdout, stderr)
self.FailTask('%s: Exit code %d\nstdout: %s\nstderr: %s' %
(command, retcode, stdout, stderr))
session.console.info('%s: stdout: %s\n', command, stdout)
if stderr:
session.console.info('stderr: %s', stderr)
if item.judge_to_pass:
self.ui.SetState(test_ui.PASS_FAIL_KEY_LABEL, append=True)
key = self.ui.WaitKeysOnce([test_ui.ENTER_KEY, test_ui.ESCAPE_KEY])
if key == test_ui.ESCAPE_KEY:
self.FailTask('Judged as fail by operator.')