Debian: Adding sw-patch unit tests
Adding unit tests for: - patch_client (sw-patch) CLI - help - query -Deprecation and Syntax warnings are now ignored by tox. These are caused by netaddr and other 3rdparty components. Those components are outside of the control of this repo. - Pylint error codes are now suppressed individually. Previously all (C)onvention and (R)efactor error checks were being suppressed, including those that were passing. - All the python3 enable checks are removed, since sw-patch pylint runs in python3, and that was only needed for running in python2. - SafeConfigParser is renamed ConfigParser since python 3.2 and this is now fixed to satisfy the latest pylint. Story: 2009969 Task: 45542 Signed-off-by: Al Bailey <al.bailey@windriver.com> Change-Id: I60a4340ea6f2c2303d0baa17e303938d55e8d278
This commit is contained in:
parent
519f0d3c4b
commit
ee8a4edf4b
@ -49,7 +49,7 @@ def read_config():
|
||||
global controller_port
|
||||
global agent_port
|
||||
|
||||
config = configparser.SafeConfigParser(defaults)
|
||||
config = configparser.ConfigParser(defaults)
|
||||
|
||||
config.read(patching_conf)
|
||||
patching_conf_mtime = os.stat(patching_conf).st_mtime
|
||||
@ -105,7 +105,7 @@ def get_mgmt_iface():
|
||||
# so return the cached value.
|
||||
return mgmt_if
|
||||
|
||||
config = configparser.SafeConfigParser()
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# The platform.conf file has no section headers, which causes problems
|
||||
# for ConfigParser. So we'll fake it out.
|
||||
|
195
sw-patch/cgcs-patch/cgcs_patch/tests/test_patch_client.py
Normal file
195
sw-patch/cgcs-patch/cgcs_patch/tests/test_patch_client.py
Normal file
@ -0,0 +1,195 @@
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright (c) 2019-2022 Wind River Systems, Inc.
|
||||
#
|
||||
|
||||
import json
|
||||
import mock
|
||||
import os
|
||||
import sys
|
||||
import testtools
|
||||
|
||||
from cgcs_patch import patch_client
|
||||
|
||||
|
||||
FAKE_SW_VERSION = "1.2.3"
|
||||
PATCH_FLAG_NO = "N"
|
||||
PATCH_FLAG_YES = "Y"
|
||||
STATE_APPLIED = "Applied"
|
||||
STATE_AVAILABLE = "Available"
|
||||
STATE_NA = "n/a"
|
||||
STATUS_DEV = "DEV"
|
||||
|
||||
FAKE_PATCH_ID_1 = "PATCH_1"
|
||||
FAKE_PATCH_1_META = {
|
||||
"apply_active_release_only": "",
|
||||
"description": "Patch 1 description",
|
||||
"install_instructions": "Patch 1 instructions",
|
||||
"patchstate": STATE_NA,
|
||||
"reboot_required": PATCH_FLAG_YES,
|
||||
"repostate": STATE_APPLIED,
|
||||
"requires": [],
|
||||
"status": STATUS_DEV,
|
||||
"summary": "Patch 1 summary",
|
||||
"sw_version": FAKE_SW_VERSION,
|
||||
"unremovable": PATCH_FLAG_NO,
|
||||
"warnings": "Patch 1 warnings",
|
||||
}
|
||||
|
||||
FAKE_PATCH_ID_2 = "PATCH_2"
|
||||
FAKE_PATCH_2_META = {
|
||||
"apply_active_release_only": "",
|
||||
"description": "Patch 2 description",
|
||||
"install_instructions": "Patch 2 instructions",
|
||||
"patchstate": STATE_AVAILABLE,
|
||||
"reboot_required": PATCH_FLAG_NO,
|
||||
"repostate": STATE_AVAILABLE,
|
||||
"requires": [FAKE_PATCH_ID_1],
|
||||
"status": STATUS_DEV,
|
||||
"summary": "Patch 2 summary",
|
||||
"sw_version": FAKE_SW_VERSION,
|
||||
"unremovable": PATCH_FLAG_NO,
|
||||
"warnings": "Patch 2 warnings",
|
||||
}
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
"""This is used to mock a requests.get result"""
|
||||
def __init__(self, json_data, status_code):
|
||||
self.json_data = json_data
|
||||
self.status_code = status_code
|
||||
self.text = json.dumps(json_data)
|
||||
|
||||
def json(self):
|
||||
return self.json_data
|
||||
|
||||
|
||||
class PatchClientTestCase(testtools.TestCase):
|
||||
PROG = "sw-patch"
|
||||
|
||||
MOCK_ENV = {
|
||||
'OS_AUTH_URL': 'FAKE_OS_AUTH_URL',
|
||||
'OS_PROJECT_NAME': 'FAKE_OS_PROJECT_NAME',
|
||||
'OS_PROJECT_DOMAIN_NAME': 'FAKE_OS_PROJECT_DOMAIN_NAME',
|
||||
'OS_USERNAME': 'FAKE_OS_USERNAME',
|
||||
'OS_PASSWORD': 'FAKE_OS_PASSWORD',
|
||||
'OS_USER_DOMAIN_NAME': 'FAKE_OS_USER_DOMAIN_NAME',
|
||||
'OS_REGION_NAME': 'FAKE_OS_REGION_NAME',
|
||||
'OS_INTERFACE': 'FAKE_OS_INTERFACE'
|
||||
}
|
||||
|
||||
# mock_map is populated by the setUp method
|
||||
mock_map = {}
|
||||
|
||||
def setUp(self):
|
||||
super(PatchClientTestCase, self).setUp()
|
||||
|
||||
def _mock_requests_get(*args, **kwargs):
|
||||
key = args[0]
|
||||
_ = kwargs # kwargs is unused
|
||||
# if the key is not found in the mock_map
|
||||
# we return a 404 (not found)
|
||||
return self.mock_map.get(key,
|
||||
FakeResponse(None, 404))
|
||||
|
||||
patcher = mock.patch(
|
||||
'requests.get',
|
||||
side_effect=_mock_requests_get)
|
||||
self.mock_requests_get = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
|
||||
class PatchClientHelpTestCase(PatchClientTestCase):
|
||||
"""Test the sw-patch CLI calls that invoke 'help'
|
||||
|
||||
'check_for_os_region_name' is mocked to help determine
|
||||
which code path is used since many code paths can short
|
||||
circuit and invoke 'help' in failure cases.
|
||||
"""
|
||||
|
||||
def _test_print_help(self, shell_args=None):
|
||||
with mock.patch.dict(os.environ, self.MOCK_ENV):
|
||||
with mock.patch.object(sys, 'argv',
|
||||
shell_args):
|
||||
# mock 'print' so running unit tests will
|
||||
# not print help usage to the tox output
|
||||
with mock.patch('builtins.print'):
|
||||
# Every client invocation invokes exit
|
||||
# which raises SystemExit
|
||||
self.assertRaises(SystemExit,
|
||||
patch_client.main)
|
||||
|
||||
@mock.patch('cgcs_patch.patch_client.check_for_os_region_name')
|
||||
def test_main_no_args_calls_help(self, mock_check):
|
||||
"""When no arguments are called, this should invoke print_help"""
|
||||
shell_args = [self.PROG, ]
|
||||
self._test_print_help(shell_args=shell_args)
|
||||
mock_check.assert_not_called()
|
||||
|
||||
@mock.patch('cgcs_patch.patch_client.check_for_os_region_name')
|
||||
def test_main_help(self, mock_check):
|
||||
"""When no arguments are called, this should invoke print_help"""
|
||||
shell_args = [self.PROG, "--help"]
|
||||
self._test_print_help(shell_args=shell_args)
|
||||
mock_check.assert_called()
|
||||
|
||||
@mock.patch('cgcs_patch.patch_client.check_for_os_region_name')
|
||||
def test_main_invalid_action_calls_help(self, mock_check):
|
||||
"""invalid args should invoke print_help"""
|
||||
shell_args = [self.PROG, "invalid_arg"]
|
||||
self._test_print_help(shell_args=shell_args)
|
||||
mock_check.assert_called()
|
||||
|
||||
|
||||
class PatchClientQueryTestCase(PatchClientTestCase):
|
||||
"""Test the sw-patch CLI calls that invoke 'query'"""
|
||||
|
||||
TEST_URL_ALL = "http://127.0.0.1:5487/patch/query?show=all"
|
||||
TEST_PATCH_DATA_SHOW_ALL = {
|
||||
"pd": {
|
||||
FAKE_PATCH_ID_1: FAKE_PATCH_1_META,
|
||||
FAKE_PATCH_ID_2: FAKE_PATCH_2_META,
|
||||
}
|
||||
}
|
||||
|
||||
TEST_URL_APPLIED = "http://127.0.0.1:5487/patch/query?show=applied"
|
||||
TEST_PATCH_DATA_SHOW_APPLIED = {
|
||||
"pd": {
|
||||
FAKE_PATCH_ID_1: FAKE_PATCH_1_META,
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(PatchClientQueryTestCase, self).setUp()
|
||||
# update the mock_map with a query result
|
||||
self.mock_map[self.TEST_URL_ALL] = FakeResponse(
|
||||
self.TEST_PATCH_DATA_SHOW_ALL, 200)
|
||||
self.mock_map[self.TEST_URL_APPLIED] = FakeResponse(
|
||||
self.TEST_PATCH_DATA_SHOW_APPLIED, 200)
|
||||
|
||||
def _test_query(self, shell_args=None):
|
||||
with mock.patch.dict(os.environ, self.MOCK_ENV):
|
||||
with mock.patch.object(sys, 'argv',
|
||||
shell_args):
|
||||
# mock 'print' so running unit tests will
|
||||
# not print to the tox output
|
||||
with mock.patch('builtins.print'):
|
||||
# Every client invocation invokes exit
|
||||
# which raises SystemExit
|
||||
self.assertRaises(SystemExit,
|
||||
patch_client.main)
|
||||
|
||||
def test_query(self):
|
||||
shell_args = [self.PROG, "query"]
|
||||
self._test_query(shell_args=shell_args)
|
||||
self.mock_requests_get.assert_called_with(
|
||||
self.TEST_URL_ALL,
|
||||
headers=mock.ANY)
|
||||
|
||||
def test_query_patch(self):
|
||||
shell_args = [self.PROG, "query", "applied"]
|
||||
self._test_query(shell_args=shell_args)
|
||||
self.mock_requests_get.assert_called_with(
|
||||
self.TEST_URL_APPLIED,
|
||||
headers=mock.ANY)
|
@ -25,12 +25,10 @@ class CgcsPatchUtilsTestCase(testtools.TestCase):
|
||||
|
||||
def test_gethostbyname(self):
|
||||
result = cgcs_patch.utils.gethostbyname('localhost')
|
||||
print("gethostbyname returned %s for localhost" % result)
|
||||
self.assertIn(result, ['127.0.0.1', '::1'])
|
||||
|
||||
def test_gethostbyname_failure(self):
|
||||
result = cgcs_patch.utils.gethostbyname('xfakehostx')
|
||||
print("gethostbyname returned %s for xfakehostx" % result)
|
||||
self.assertIsNone(result)
|
||||
|
||||
@mock.patch('cgcs_patch.utils.gethostbyname')
|
||||
|
@ -36,87 +36,6 @@ extension-pkg-whitelist=lxml
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time. See also the "--disable" option for examples.
|
||||
#
|
||||
# Python3 checker:
|
||||
#
|
||||
# E1601: print-statement
|
||||
# E1602: parameter-unpacking
|
||||
# E1603: unpacking-in-except
|
||||
# E1604: old-raise-syntax
|
||||
# E1605: backtick
|
||||
# E1606: long-suffix
|
||||
# E1607: old-ne-operator
|
||||
# E1608: old-octal-literal
|
||||
# E1609: import-star-module-level
|
||||
# E1610: non-ascii-bytes-literal
|
||||
# E1611: invalid-unicode-literal
|
||||
# W1601: apply-builtin
|
||||
# W1602: basestring-builtin
|
||||
# W1603: buffer-builtin
|
||||
# W1604: cmp-builtin
|
||||
# W1605: coerce-builtin
|
||||
# W1606: execfile-builtin
|
||||
# W1607: file-builtin
|
||||
# W1608: long-builtin
|
||||
# W1609: raw_input-builtin
|
||||
# W1610: reduce-builtin
|
||||
# W1611: standarderror-builtin
|
||||
# W1612: unicode-builtin
|
||||
# W1613: xrange-builtin
|
||||
# W1614: coerce-method
|
||||
# W1615: delslice-method
|
||||
# W1616: getslice-method
|
||||
# W1617: setslice-method
|
||||
# W1618: no-absolute-import
|
||||
# W1619: old-division
|
||||
# W1620: dict-iter-method
|
||||
# W1621: dict-view-method
|
||||
# W1622: next-method-called
|
||||
# W1623: metaclass-assignment
|
||||
# W1624: indexing-exception
|
||||
# W1625: raising-string
|
||||
# W1626: reload-builtin
|
||||
# W1627: oct-method
|
||||
# W1628: hex-method
|
||||
# W1629: nonzero-method
|
||||
# W1630: cmp-method
|
||||
# W1632: input-builtin
|
||||
# W1633: round-builtin
|
||||
# W1634: intern-builtin
|
||||
# W1635: unichr-builtin
|
||||
# W1636: map-builtin-not-iterating
|
||||
# W1637: zip-builtin-not-iterating
|
||||
# W1638: range-builtin-not-iterating
|
||||
# W1639: filter-builtin-not-iterating
|
||||
# W1640: using-cmp-argument
|
||||
# W1641: eq-without-hash
|
||||
# W1642: div-method
|
||||
# W1643: idiv-method
|
||||
# W1644: rdiv-method
|
||||
# W1645: exception-message-attribute
|
||||
# W1646: invalid-str-codec
|
||||
# W1647: sys-max-int
|
||||
# W1648: bad-python3-import
|
||||
# W1649: deprecated-string-function
|
||||
# W1650: deprecated-str-translate-call
|
||||
# W1651: deprecated-itertools-function
|
||||
# W1652: deprecated-types-field
|
||||
# W1653: next-method-defined
|
||||
# W1654: dict-items-not-iterating
|
||||
# W1655: dict-keys-not-iterating
|
||||
# W1656: dict-values-not-iterating
|
||||
# W1657: deprecated-operator-function
|
||||
# W1658: deprecated-urllib-function
|
||||
# W1659: xreadlines-attribute
|
||||
# W1660: deprecated-sys-function
|
||||
# W1661: exception-escape
|
||||
# W1662: comprehension-escape
|
||||
enable=E1603,E1609,E1610,E1602,E1606,E1608,E1607,E1605,E1604,E1601,E1611,W1652,
|
||||
W1651,W1649,W1657,W1660,W1658,W1659,W1623,W1622,W1620,W1621,W1645,W1641,
|
||||
W1624,W1648,W1625,W1611,W1662,W1661,W1650,W1640,W1630,W1614,W1615,W1642,
|
||||
W1616,W1628,W1643,W1629,W1627,W1644,W1617,W1601,W1602,W1603,W1604,W1605,
|
||||
W1654,W1655,W1656,W1619,W1606,W1607,W1639,W1618,W1632,W1634,W1608,W1636,
|
||||
W1653,W1646,W1638,W1609,W1610,W1626,W1633,W1647,W1635,W1612,W1613,W1637
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
@ -126,6 +45,43 @@ enable=E1603,E1609,E1610,E1602,E1606,E1608,E1607,E1605,E1604,E1601,E1611,W1652,
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
# -Conventions-
|
||||
# C0103 invalid-name
|
||||
# C0114 missing-module-docstring
|
||||
# C0115 missing-class-docstring
|
||||
# C0116 missing-function-docstring
|
||||
# C0201 consider-iterating-dictionary
|
||||
# C0206 consider-using-dict-items
|
||||
# C0209 consider-using-f-string
|
||||
# C2801 unnecessary-dunder-call
|
||||
# C0301 line-too-long
|
||||
# C0302 too-many-lines
|
||||
# C0325 superfluous-parens
|
||||
# C0411 wrong-import-order
|
||||
# C0415 import-outside-toplevel
|
||||
# -Refactoring-
|
||||
# R0205 useless-object-inheritance
|
||||
# R0402 consider-using-from-import
|
||||
# R0801 Similar lines in x files
|
||||
# R0902 too-many-instance-attributes
|
||||
# R0903 too-few-public-methods
|
||||
# R0904 too-many-public-methods
|
||||
# R0911 too-many-return-statements
|
||||
# R0912 too-many-branches
|
||||
# R0913 too-many-arguments
|
||||
# R0914 too-many-locals
|
||||
# R0915 too-many-statements
|
||||
# R1702 too-many-nested-blocks
|
||||
# R1705 no-else-return
|
||||
# R1710 inconsistent-return-statements
|
||||
# R1714 consider-using-in
|
||||
# R1715 consider-using-get
|
||||
# R1722 consider-using-sys-exit
|
||||
# R1724 no-else-continue
|
||||
# R1725 super-with-arguments
|
||||
# R1732 consider-using-with
|
||||
# R1735 use-dict-literal
|
||||
# -Warnings-
|
||||
# W0107 unnecessary-pass
|
||||
# W0602 global-variable-not-assigned
|
||||
# W0603 global-statement
|
||||
@ -133,7 +89,12 @@ enable=E1603,E1609,E1610,E1602,E1606,E1608,E1607,E1605,E1604,E1601,E1611,W1652,
|
||||
# W0707 raise-missing-from
|
||||
# W1505 deprecated-method
|
||||
# W1514 unspecified-encoding
|
||||
disable=C,R,W0107,W0602,W0603,W0703,W0707,W1505,W1514
|
||||
disable= C0103,C0114,C0115,C0116,C0201,C0206,C0209,C2801,
|
||||
C0301,C0302,C0325,C0411,C0415,
|
||||
R0205,R0402,R0801,R0902,R0903,R0904,R0911,
|
||||
R0912,R0913,R0914,R0915,R1702,R1705,R1710,R1714,
|
||||
R1715,R1722,R1724,R1725,R1732,R1735,
|
||||
W0107,W0602,W0603,W0703,W0707,W1505,W1514
|
||||
|
||||
[REPORTS]
|
||||
|
||||
|
@ -38,7 +38,7 @@ setenv = VIRTUAL_ENV={envdir}
|
||||
OS_TEST_TIMEOUT=60
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
PYTHONHASHSEED=0
|
||||
PYTHONWARNINGS=default::DeprecationWarning
|
||||
PYTHONWARNINGS=ignore::DeprecationWarning,ignore::SyntaxWarning
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
sitepackages = False
|
||||
@ -98,7 +98,7 @@ ignore = H306,H401,H404,H405,W504,E501
|
||||
exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,release-tag-*
|
||||
# H106: Don't put vim configuration in source files (off by default).
|
||||
# H203: Use assertIs(Not)None to check for None (off by default).
|
||||
# (todo) enable H904 Delay string interpolations at logging calls (off by default)
|
||||
# H904 Delay string interpolations at logging calls (off by default)
|
||||
enable-extensions = H106 H203,H904
|
||||
max-line-length = 80
|
||||
|
||||
@ -113,6 +113,7 @@ commands = pylint cgcs_patch --rcfile=./pylint.rc
|
||||
|
||||
[testenv:cover]
|
||||
setenv =
|
||||
PYTHONWARNINGS=ignore::DeprecationWarning,ignore::SyntaxWarning
|
||||
PYTHON=coverage run --parallel-mode
|
||||
PYTHONDONTWRITEBYTECODE=True
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user