From ad0a5ade3ab208194df2f25e3373ef4822b54d8f Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Fri, 22 Jan 2021 11:59:41 -0800 Subject: [PATCH] [psud] Increase unit test coverage (#140) - Add 100% unit test coverage of `PsuStatus` class in psud. - Add skeleton of class to test `DaemonPsud` class - Add test case for `get_psu_key()` and `try_get()` helper functions - Add checks to import 'mock' from the 'unittest' package if running with Python 3 Overall psud unit test coverage increases from 39% to 51%. Previous unit test coverage: ``` ----------- coverage: platform linux, python 3.7.3-final-0 ----------- Name Stmts Miss Cover ---------------------------------- scripts/psud 381 233 39% Coverage HTML written to dir htmlcov Coverage XML written to file coverage.xml ``` Unit test coverage with this patch: ``` ----------- coverage: platform linux, python 3.7.3-final-0 ----------- Name Stmts Miss Cover ---------------------------------- scripts/psud 381 185 51% Coverage HTML written to dir htmlcov Coverage XML written to file coverage.xml ``` --- sonic-psud/setup.py | 2 +- sonic-psud/tests/mock_platform.py | 8 + sonic-psud/tests/test_PsuStatus.py | 236 +++++++++++++++++++++++++++++ sonic-psud/tests/test_psud.py | 90 ++++++++++- 4 files changed, 334 insertions(+), 2 deletions(-) create mode 100644 sonic-psud/tests/test_PsuStatus.py diff --git a/sonic-psud/setup.py b/sonic-psud/setup.py index 7b572c36a8fa..7329681239b2 100644 --- a/sonic-psud/setup.py +++ b/sonic-psud/setup.py @@ -21,8 +21,8 @@ 'wheel' ], tests_require=[ + 'mock>=2.0.0; python_version < "3.3"', 'pytest', - 'mock>=2.0.0', 'pytest-cov' ], classifiers=[ diff --git a/sonic-psud/tests/mock_platform.py b/sonic-psud/tests/mock_platform.py index a88a74370c68..878203037648 100644 --- a/sonic-psud/tests/mock_platform.py +++ b/sonic-psud/tests/mock_platform.py @@ -37,10 +37,18 @@ def __init__(self, psu_presence, psu_status, psu_name): self.name = psu_name self.presence = True self.psu_status = psu_status + self.status_led_color = self.STATUS_LED_COLOR_OFF def get_powergood_status(self): return self.psu_status + def set_status_led(self, color): + self.status_led_color = color + return True + + def get_status_led(self): + return self.status_led_color + def set_status(self, status): self.psu_status = status diff --git a/sonic-psud/tests/test_PsuStatus.py b/sonic-psud/tests/test_PsuStatus.py new file mode 100644 index 000000000000..e96da0c9398c --- /dev/null +++ b/sonic-psud/tests/test_PsuStatus.py @@ -0,0 +1,236 @@ +import os +import sys +from imp import load_source + +# TODO: Clean this up once we no longer need to support Python 2 +if sys.version_info.major == 3: + from unittest.mock import MagicMock +else: + from mock import MagicMock + +from .mock_platform import MockPsu + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, modules_path) + +os.environ["PSUD_UNIT_TESTING"] = "1" +load_source('psud', scripts_path + '/psud') +from psud import * + + +class TestPsuStatus(object): + """ + Test cases to cover functionality of PsuStatus class + """ + + def test_set_presence(self): + mock_logger = MagicMock() + mock_psu = MockPsu(True, True, "PSU 1") + + psu_status = PsuStatus(mock_logger, mock_psu) + assert psu_status.presence == False + + # Test toggling presence to True + ret = psu_status.set_presence(True) + assert ret == True + assert psu_status.presence == True + + # Test toggling presence to False + ret = psu_status.set_presence(False) + assert ret == True + assert psu_status.presence == False + + # Test attempting to set presence to the same as the current value + ret = psu_status.set_presence(False) + assert ret == False + assert psu_status.presence == False + + def test_set_power_good(self): + mock_logger = MagicMock() + mock_psu = MockPsu(True, True, "PSU 1") + + psu_status = PsuStatus(mock_logger, mock_psu) + assert psu_status.power_good == False + + # Test toggling power_good to True + ret = psu_status.set_power_good(True) + assert ret == True + assert psu_status.power_good == True + + # Test attempting to set power_good to the same as the current value (return value should be False) + ret = psu_status.set_power_good(True) + assert ret == False + assert psu_status.power_good == True + + # Test toggling power_good to False + ret = psu_status.set_power_good(False) + assert ret == True + assert psu_status.power_good == False + + # Test attempting to set power_good to the same as the current value (return value should be False) + ret = psu_status.set_power_good(False) + assert ret == False + assert psu_status.power_good == False + + def test_set_voltage(self): + mock_logger = MagicMock() + mock_psu = MockPsu(True, True, "PSU 1") + + psu_status = PsuStatus(mock_logger, mock_psu) + assert psu_status.voltage_good == False + + # Pass in a good voltage + ret = psu_status.set_voltage(12.0, 12.5, 11.5) + assert ret == True + assert psu_status.voltage_good == True + + # Pass in a another good voltage successively (return value should be False) + ret = psu_status.set_voltage(11.9, 12.5, 11.5) + assert ret == False + assert psu_status.voltage_good == True + + # Pass in a high voltage + ret = psu_status.set_voltage(12.6, 12.5, 11.5) + assert ret == True + assert psu_status.voltage_good == False + + # Pass in a another bad voltage successively (return value should be False) + ret = psu_status.set_voltage(12.7, 12.5, 11.5) + assert ret == False + assert psu_status.voltage_good == False + + # Pass in a good (high edge case) voltage + ret = psu_status.set_voltage(12.5, 12.5, 11.5) + assert ret == True + assert psu_status.voltage_good == True + + # Pass in a low voltage + ret = psu_status.set_voltage(11.4, 12.5, 11.5) + assert ret == True + assert psu_status.voltage_good == False + + # Pass in a good (low edge case) voltage + ret = psu_status.set_voltage(11.5, 12.5, 11.5) + assert ret == True + assert psu_status.voltage_good == True + + # Test passing parameters as None when voltage_good == True + ret = psu_status.set_voltage(None, 12.5, 11.5) + assert ret == False + assert psu_status.voltage_good == True + ret = psu_status.set_voltage(11.5, None, 11.5) + assert ret == False + assert psu_status.voltage_good == True + ret = psu_status.set_voltage(11.5, 12.5, None) + assert ret == False + assert psu_status.voltage_good == True + + # Test passing parameters as None when voltage_good == False + psu_status.voltage_good = False + ret = psu_status.set_voltage(None, 12.5, 11.5) + assert ret == False + assert psu_status.voltage_good == True + psu_status.voltage_good = False + ret = psu_status.set_voltage(11.5, None, 11.5) + assert ret == False + assert psu_status.voltage_good == True + psu_status.voltage_good = False + ret = psu_status.set_voltage(11.5, 12.5, None) + assert ret == False + assert psu_status.voltage_good == True + + def test_set_temperature(self): + mock_logger = MagicMock() + mock_psu = MockPsu(True, True, "PSU 1") + + psu_status = PsuStatus(mock_logger, mock_psu) + assert psu_status.temperature_good == False + + # Pass in a good temperature + ret = psu_status.set_temperature(20.123, 50.0) + assert ret == True + assert psu_status.temperature_good == True + + # Pass in a another good temperature successively (return value should be False) + ret = psu_status.set_temperature(31.456, 50.0) + assert ret == False + assert psu_status.temperature_good == True + + # Pass in a high temperature + ret = psu_status.set_temperature(50.001, 50.0) + assert ret == True + assert psu_status.temperature_good == False + + # Pass in a another bad temperature successively (return value should be False) + ret = psu_status.set_temperature(50.0, 50.0) + assert ret == False + assert psu_status.temperature_good == False + + # Pass in a good (high edge case) temperature + ret = psu_status.set_temperature(49.999, 50.0) + assert ret == True + assert psu_status.temperature_good == True + + # Test passing parameters as None when temperature_good == True + ret = psu_status.set_temperature(None, 50.0) + assert ret == False + assert psu_status.temperature_good == True + ret = psu_status.set_temperature(20.123, None) + assert ret == False + assert psu_status.temperature_good == True + + # Test passing parameters as None when temperature_good == False + psu_status.temperature_good = False + ret = psu_status.set_temperature(None, 50.0) + assert ret == False + assert psu_status.temperature_good == True + psu_status.temperature_good = False + ret = psu_status.set_temperature(20.123, None) + assert ret == False + assert psu_status.temperature_good == True + + def test_is_ok(self): + mock_logger = MagicMock() + mock_psu = MockPsu(True, True, "PSU 1") + + psu_status = PsuStatus(mock_logger, mock_psu) + psu_status.presence = True + psu_status.power_good = True + psu_status.voltage_good = True + psu_status.temperature_good = True + ret = psu_status.is_ok() + assert ret == True + + psu_status.presence = False + ret = psu_status.is_ok() + assert ret == False + + psu_status.presence = True + ret = psu_status.is_ok() + assert ret == True + + psu_status.power_good = False + ret = psu_status.is_ok() + assert ret == False + + psu_status.power_good = True + ret = psu_status.is_ok() + assert ret == True + + psu_status.voltage_good = False + ret = psu_status.is_ok() + assert ret == False + + psu_status.voltage_good = True + ret = psu_status.is_ok() + assert ret == True + + psu_status.temperature_good = False + ret = psu_status.is_ok() + assert ret == False + + psu_status.temperature_good = True + ret = psu_status.is_ok() + assert ret == True diff --git a/sonic-psud/tests/test_psud.py b/sonic-psud/tests/test_psud.py index 841eebbee4d8..d5ffa56a7a64 100644 --- a/sonic-psud/tests/test_psud.py +++ b/sonic-psud/tests/test_psud.py @@ -2,7 +2,11 @@ import sys from imp import load_source -from mock import Mock, MagicMock, patch +# TODO: Clean this up once we no longer need to support Python 2 +if sys.version_info.major == 3: + from unittest.mock import Mock, MagicMock, patch +else: + from mock import Mock, MagicMock, patch from sonic_py_common import daemon_base from .mock_platform import MockChassis, MockPsu, MockFanDrawer, MockModule @@ -169,3 +173,87 @@ def test_psuchassis_check_power_budget(): assert float(fvs[CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD]) > float(fvs[CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD]) assert chassis_info.master_status_good == True assert MockPsu.get_status_master_led() == MockPsu.STATUS_LED_COLOR_GREEN + + +def test_get_psu_key(): + assert get_psu_key(0) == PSU_INFO_KEY_TEMPLATE.format(0) + assert get_psu_key(1) == PSU_INFO_KEY_TEMPLATE.format(1) + + +def test_try_get(): + # Test a proper, working callback + GOOD_CALLBACK_RETURN_VALUE = "This is a test" + + def callback1(): + return GOOD_CALLBACK_RETURN_VALUE + + ret = try_get(callback1) + assert ret == GOOD_CALLBACK_RETURN_VALUE + + # Ensure try_get returns default value if callback returns None + DEFAULT_VALUE = "Default value" + + def callback2(): + return None + + ret = try_get(callback2, default=DEFAULT_VALUE) + assert ret == DEFAULT_VALUE + + # Ensure try_get returns default value if callback returns None + def callback3(): + raise NotImplementedError + + ret = try_get(callback3, default=DEFAULT_VALUE) + assert ret == DEFAULT_VALUE + + +class TestDaemonPsud(object): + """ + Test cases to cover functionality in DaemonPsud class + """ + + def test_set_psu_led(self): + mock_logger = MagicMock() + mock_psu = MockPsu(True, True, "PSU 1") + psu_status = PsuStatus(mock_logger, mock_psu) + + daemon_psud = DaemonPsud(SYSLOG_IDENTIFIER) + + psu_status.presence = True + psu_status.power_good = True + psu_status.voltage_good = True + psu_status.temperature_good = True + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_GREEN + + psu_status.presence = False + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_RED + + psu_status.presence = True + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_GREEN + + psu_status.power_good = False + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_RED + + psu_status.power_good = True + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_GREEN + + psu_status.voltage_good = False + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_RED + + psu_status.voltage_good = True + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_GREEN + + psu_status.temperature_good = False + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_RED + + psu_status.temperature_good = True + daemon_psud._set_psu_led(mock_psu, psu_status) + assert mock_psu.get_status_led() == mock_psu.STATUS_LED_COLOR_GREEN