290 lines
10 KiB
Python
290 lines
10 KiB
Python
#!/usr/bin/python2
|
|
#
|
|
# Copyright 2019 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 unittest
|
|
|
|
import common
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.common_lib import utils
|
|
from autotest_lib.client.common_lib.cros import cros_config
|
|
|
|
# Lots of command-line mocking in this file.
|
|
# Mock cros_config results are based on the path and property provided.
|
|
# (Remember, cros_config's syntax is `cros_config path property`.)
|
|
# The path determines whether cros_config fails or succeeds.
|
|
# The property determines whether there is a fallback command, and if so,
|
|
# whether the fallback fails or succeeds.
|
|
|
|
SUCCEEDS = 0
|
|
FAILS = 1
|
|
DOES_NOT_EXIST = 2
|
|
|
|
# cros_config path determines the mock behavior of cros_config.
|
|
CC_PATHS = {SUCCEEDS: '/success', FAILS: '/error'}
|
|
|
|
# cros_config property determines the mock behavior of the fallback command.
|
|
CC_PROPERTIES = {
|
|
SUCCEEDS: 'fallback_succeeds',
|
|
FAILS: 'fallback_fails',
|
|
DOES_NOT_EXIST: 'no_fallback'
|
|
}
|
|
|
|
CROS_CONFIG_SUCCESS_RESPONSE = 'cros_config succeeded'
|
|
CROS_CONFIG_FALLBACK_RESPONSE = 'fallback succeeded'
|
|
|
|
|
|
def get_cros_config_args(cros_config_result, fallback_result):
|
|
"""Build cros_config_args based on the desired outcome."""
|
|
cros_config_path = CC_PATHS[cros_config_result]
|
|
cros_config_property = CC_PROPERTIES[fallback_result]
|
|
return '%s %s' % (cros_config_path, cros_config_property)
|
|
|
|
|
|
class _CrosConfigBaseTestCase(unittest.TestCase):
|
|
"""Base class which sets up mock fallback commands"""
|
|
|
|
def setUp(self):
|
|
"""Add mock fallback command(s) to cros_config.FALLBACKS"""
|
|
for path in CC_PATHS.values():
|
|
pass_args = '%s %s' % (path, CC_PROPERTIES[SUCCEEDS])
|
|
fail_args = '%s %s' % (path, CC_PROPERTIES[FAILS])
|
|
cros_config.FALLBACKS[pass_args] = \
|
|
'echo %s' % CROS_CONFIG_FALLBACK_RESPONSE
|
|
cros_config.FALLBACKS[fail_args] = 'this command does nothing'
|
|
|
|
def tearDown(self):
|
|
"""Remove mock fallback command(s) from cros_config.FALLBACKS"""
|
|
for path in CC_PATHS.values():
|
|
pass_args = '%s %s' % (path, CC_PROPERTIES[SUCCEEDS])
|
|
fail_args = '%s %s' % (path, CC_PROPERTIES[FAILS])
|
|
del cros_config.FALLBACKS[pass_args]
|
|
del cros_config.FALLBACKS[fail_args]
|
|
|
|
|
|
class GetFallbackTestCase(_CrosConfigBaseTestCase):
|
|
"""Verify cros_config.get_fallback"""
|
|
|
|
def runTest(self):
|
|
"""Check handling for commands with and without fallbacks"""
|
|
self.assertFalse(
|
|
cros_config.get_fallback(
|
|
'%s %s' % (CC_PATHS[SUCCEEDS],
|
|
CC_PROPERTIES[DOES_NOT_EXIST])))
|
|
self.assertEqual(
|
|
cros_config.get_fallback('%s %s' % (CC_PATHS[SUCCEEDS],
|
|
CC_PROPERTIES[SUCCEEDS])),
|
|
'echo %s' % CROS_CONFIG_FALLBACK_RESPONSE)
|
|
|
|
|
|
def _mock_cmd_runner(cmd, **kwargs):
|
|
"""
|
|
Mock running a DUT command, returning a CmdResult.
|
|
|
|
We handle a few mock functions here:
|
|
* cros_config $path $property: $path determines error or success.
|
|
$property is not used here.
|
|
* echo $text: Returns $text with a trailing newline.
|
|
|
|
Additionally, if the kwarg `ignore_status` is passed in as True,
|
|
then when cros_config would raise an error, it instead returns a
|
|
CmdResult with an exit_status of 1.
|
|
|
|
@param cmd: A command, as would be run on the DUT
|
|
@param **kwargs: Kwargs that might be passed into, say, utils.run()
|
|
@return: A mock response from the DUT
|
|
|
|
@type cmd: string
|
|
@rtype: client.common_lib.utils.CmdResult
|
|
|
|
@raise error.CmdError if cros_config should raise an exception.
|
|
@raise NotImplementedError if cros_config has an unexpected path
|
|
|
|
"""
|
|
result = utils.CmdResult(cmd)
|
|
if cmd.startswith('cros_config '):
|
|
_, path, _ = cmd.split()
|
|
if path == CC_PATHS[SUCCEEDS]:
|
|
result.stdout = CROS_CONFIG_SUCCESS_RESPONSE
|
|
elif path == CC_PATHS[FAILS]:
|
|
result.exit_status = 1
|
|
if not kwargs.get('ignore_status'):
|
|
raise error.CmdError(cmd, result)
|
|
else:
|
|
raise NotImplementedError('Bad cros_config path: %s' % path)
|
|
elif cmd.startswith('echo '):
|
|
result.stdout = cmd.lstrip('echo ') + '\n'
|
|
else:
|
|
result.exit_status = 2
|
|
if not kwargs.get('ignore_status'):
|
|
raise error.CmdError(cmd, result)
|
|
return result
|
|
|
|
|
|
class CallCrosConfigWithFallbackTestCase(_CrosConfigBaseTestCase):
|
|
"""Verify cros_config.call_cros_config_with_fallback"""
|
|
|
|
def run_cc_w_fallback(self, cros_config_result, fallback_result,
|
|
ignore_status=False):
|
|
"""
|
|
Helper function to call
|
|
cros_config.call_cros_config_with_fallback()
|
|
|
|
"""
|
|
cc_args = get_cros_config_args(cros_config_result, fallback_result)
|
|
if ignore_status:
|
|
return cros_config.call_cros_config_with_fallback(
|
|
cc_args, _mock_cmd_runner, ignore_status=True)
|
|
else:
|
|
return cros_config.call_cros_config_with_fallback(
|
|
cc_args, _mock_cmd_runner)
|
|
|
|
def test_cros_config_success(self):
|
|
"""
|
|
Verify that if cros_config is defined, we get the cros_config
|
|
result, regardless of whether there is a fallback command.
|
|
|
|
"""
|
|
for fallback_status in (SUCCEEDS, FAILS, DOES_NOT_EXIST):
|
|
for ignore_status in (True, False):
|
|
output = self.run_cc_w_fallback(SUCCEEDS, fallback_status,
|
|
ignore_status)
|
|
self.assertEqual(output.stdout, CROS_CONFIG_SUCCESS_RESPONSE)
|
|
self.assertFalse(output.exit_status)
|
|
|
|
def test_fallback_success(self):
|
|
"""
|
|
Verify that if cros_config is not defined but a fallback is,
|
|
we get the fallback result.
|
|
|
|
"""
|
|
for ignore_status in (True, False):
|
|
output = self.run_cc_w_fallback(FAILS, SUCCEEDS, ignore_status)
|
|
self.assertEqual(output.stdout, CROS_CONFIG_FALLBACK_RESPONSE)
|
|
self.assertFalse(output.exit_status)
|
|
|
|
def test_fallback_fails(self):
|
|
"""
|
|
Verify that if both cros_config and the fallback fail, a
|
|
CmdError is raised.
|
|
|
|
"""
|
|
with self.assertRaises(error.CmdError):
|
|
self.run_cc_w_fallback(FAILS, FAILS)
|
|
|
|
def test_fallback_dne(self):
|
|
"""
|
|
Verify that if cros_config fails and the fallback does not
|
|
exist, a CmdError is raised.
|
|
|
|
"""
|
|
with self.assertRaises(error.CmdError):
|
|
self.run_cc_w_fallback(FAILS, DOES_NOT_EXIST)
|
|
|
|
def test_fallback_fails_ignore_status(self):
|
|
"""
|
|
Verify that if both cros_config and the fallback fail, and the
|
|
ignore_status kwarg is passed in, we get a CmdResult with a
|
|
non-zero exit status.
|
|
|
|
"""
|
|
output = self.run_cc_w_fallback(FAILS, FAILS, True)
|
|
self.assertTrue(output.exit_status)
|
|
|
|
def test_fallback_dne_ignore_status(self):
|
|
"""
|
|
Verify that if cros_config fails and the fallback does not
|
|
exist, and the ignore_status kwarg is passed in, we get a
|
|
CmdResult with a non-zero exit status.
|
|
|
|
"""
|
|
output = self.run_cc_w_fallback(FAILS, DOES_NOT_EXIST, True)
|
|
self.assertTrue(output.exit_status)
|
|
|
|
|
|
class CallCrosConfigGetOutputTestCase(_CrosConfigBaseTestCase):
|
|
"""
|
|
Verify cros_config.call_cros_config_get_output.
|
|
Basically the same as CallCrosConfigWithFallbackTestCase, except
|
|
that the expected result is a string instead of a CmdResult, and
|
|
it shouldn't raise exceptions.
|
|
|
|
"""
|
|
|
|
def run_cc_get_output(self, cros_config_result, fallback_result,
|
|
ignore_status=False):
|
|
"""
|
|
Helper function to call
|
|
cros_config.call_cros_config_get_output()
|
|
|
|
"""
|
|
cc_args = get_cros_config_args(cros_config_result, fallback_result)
|
|
if ignore_status:
|
|
return cros_config.call_cros_config_get_output(
|
|
cc_args, _mock_cmd_runner, ignore_status=True)
|
|
else:
|
|
return cros_config.call_cros_config_get_output(
|
|
cc_args, _mock_cmd_runner)
|
|
|
|
def test_cros_config_success(self):
|
|
"""
|
|
Verify that if cros_config is defined, we get the cros_config
|
|
result, regardless of whether there is a fallback command.
|
|
|
|
"""
|
|
for fallback_status in (SUCCEEDS, FAILS, DOES_NOT_EXIST):
|
|
output = self.run_cc_get_output(SUCCEEDS, fallback_status)
|
|
self.assertEqual(output, CROS_CONFIG_SUCCESS_RESPONSE)
|
|
|
|
def test_fallback_success(self):
|
|
"""
|
|
Verify that if cros_config is not defined but a fallback is,
|
|
we get the fallback result.
|
|
|
|
"""
|
|
output = self.run_cc_get_output(FAILS, SUCCEEDS)
|
|
self.assertEqual(output, CROS_CONFIG_FALLBACK_RESPONSE)
|
|
|
|
def test_fallback_fails(self):
|
|
"""
|
|
Verify that if both cros_config and the fallback fail, we get
|
|
a falsey value.
|
|
|
|
"""
|
|
output = self.run_cc_get_output(FAILS, FAILS)
|
|
self.assertFalse(output)
|
|
|
|
def test_fallback_dne(self):
|
|
"""
|
|
Verify that if cros_config fails and the fallback does not
|
|
exist, we get a falsey value.
|
|
|
|
"""
|
|
output = self.run_cc_get_output(FAILS, DOES_NOT_EXIST)
|
|
self.assertFalse(output)
|
|
|
|
def test_fallback_fails_ignore_status(self):
|
|
"""
|
|
Verify that if both cros_config and the fallback fail, and the
|
|
ignore_status kwarg is passed in, we get a falsey value.
|
|
|
|
"""
|
|
output = self.run_cc_get_output(FAILS, FAILS, True)
|
|
self.assertFalse(output)
|
|
|
|
def test_fallback_dne_ignore_status(self):
|
|
"""
|
|
Verify that if cros_config fails and the fallback does not
|
|
exist, and the ignore_status kwarg is passed in, we get a
|
|
falsey value.
|
|
|
|
"""
|
|
output = self.run_cc_get_output(FAILS, DOES_NOT_EXIST, True)
|
|
self.assertFalse(output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|