From e3a0d805e6e4e54835e0c6587b6a4695b855c0bf Mon Sep 17 00:00:00 2001 From: Ben Sartori <149951068+bensartori@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:25:02 +0200 Subject: [PATCH] Some more unittests, small restructuring for tests with mocked python modules --- tests/abstract_witre_method_test.py | 85 +++++++++++++++++ tests/test_lednamebadge_api.py | 57 ++++++++++++ tests/test_lednamebadge_select_method.py | 112 +---------------------- 3 files changed, 146 insertions(+), 108 deletions(-) create mode 100644 tests/abstract_witre_method_test.py create mode 100644 tests/test_lednamebadge_api.py diff --git a/tests/abstract_witre_method_test.py b/tests/abstract_witre_method_test.py new file mode 100644 index 0000000..9504e33 --- /dev/null +++ b/tests/abstract_witre_method_test.py @@ -0,0 +1,85 @@ +import sys +from unittest import TestCase +from unittest.mock import patch, MagicMock +from io import StringIO + + +class USBError(BaseException): + pass + + +class AbstractWriteMethodTest(TestCase): + def setUp(self): + print("Real platform: " + sys.platform) + + + # ------------------------------------------------------------------------- + + + def print_test_conditions(self, pyusb_available, pyhidapi_available, device_available, method, device_id): + print("Test condition: os=%s pyusb=%s pyhidapi=%s device=%s method=%s device_id=%s" % ( + sys.platform, + 'yes' if pyusb_available else 'no', + 'yes' if pyhidapi_available else 'no', + 'yes' if device_available else 'no', + method, + device_id)) + + def prepare_modules(self, pyusb_available, pyhidapi_available, device_available, func): + result = None + output = None + mocks = None + with self.do_import_patch(pyusb_available, pyhidapi_available, device_available) as module_mocks: + with patch('sys.stdout', new_callable=StringIO) as stdio_mock: + import lednamebadge + try: + result = func(lednamebadge.LedNameBadge) + mocks = {'pyhidapi': module_mocks['pyhidapi'], 'usb': module_mocks['usb']} + except(SystemExit): + pass + output = stdio_mock.getvalue() + print(output) + return result, output, mocks + + def do_import_patch(self, pyusb_available, pyhidapi_available, device_available): + patch_obj = patch.dict('sys.modules', { + 'pyhidapi': self.create_hid_mock(device_available) if pyhidapi_available else None, + 'usb': self.create_usb_mock(device_available) if pyusb_available else None, + 'usb.core': MagicMock() if pyusb_available else None, + 'usb.core.USBError': USBError if pyusb_available else None, + 'usb.util': MagicMock() if pyusb_available else None}) + # Assure fresh reimport of lednamebadge with current mocks + if 'lednamebadge' in sys.modules: + del sys.modules['lednamebadge'] + return patch_obj + + + def create_hid_mock(self, device_available): + device = MagicMock() + device.path = b'3-4:5-6' + device.manufacturer_string = 'HidApi Test Manufacturer' + device.product_string = 'HidApi Test Product' + device.interface_number = 0 + + mock = MagicMock() + mock.hid_enumerate.return_value = [device] if device_available else [] + mock.hid_open_path.return_value = 123456 if device_available else [] + return mock + + + def create_usb_mock(self, device_available): + device = MagicMock() + device.manufacturer = 'LibUsb Test Manufacturer' + device.product = 'LibUsb Test Product' + device.bus = 3 + device.address = 4 + + ep = MagicMock() + ep.bEndpointAddress = 2 + + mock = MagicMock() + mock.core = MagicMock() + mock.core.find.return_value = [device] if device_available else [] + mock.core.USBError = USBError + mock.util.find_descriptor.return_value = [ep] if device_available else [] + return mock diff --git a/tests/test_lednamebadge_api.py b/tests/test_lednamebadge_api.py new file mode 100644 index 0000000..271eed3 --- /dev/null +++ b/tests/test_lednamebadge_api.py @@ -0,0 +1,57 @@ +import sys +from array import array + +import abstract_witre_method_test + + +class Test(abstract_witre_method_test.AbstractWriteMethodTest): + def test_get_methods(self): + methods, output = self.call_info_methods() + self.assertDictEqual({ + 'hidapi': ('Program a device connected via USB using the pyhidapi package and libhidapi.', True), + 'libusb': ('Program a device connected via USB using the pyusb package and libusb.', True)}, + methods) + + def test_get_device_ids(self): + device_ids, output = self.call_info_ids('libusb') + self.assertDictEqual({ + '3:4:2': 'LibUsb Test Manufacturer - LibUsb Test Product (bus=3 dev=4 endpoint=2)'}, + device_ids) + + device_ids, output = self.call_info_ids('hidapi') + self.assertDictEqual({ + '3-4:5-6': 'HidApi Test Manufacturer - HidApi Test Product (if=0)'}, + device_ids) + + + def test_write(self): + device_ids, output, mocks = self.call_write('auto') + mocks['pyhidapi'].hid_write.assert_called_once() + + device_ids, output, mocks = self.call_write('hidapi') + mocks['pyhidapi'].hid_write.assert_called_once() + + device_ids, output, mocks = self.call_write('libusb') + mocks['usb'].util.find_descriptor.assert_called_once() + mocks['usb'].util.find_descriptor.return_value[0].write.assert_called_once() + + + # ------------------------------------------------------------------------- + + + def call_info_methods(self): + self.print_test_conditions(True, True, True, '-', '-') + method_obj, output, _ = self.prepare_modules(True, True, True, + lambda m: m.get_available_methods()) + return method_obj, output + + def call_info_ids(self, method): + self.print_test_conditions(True, True, True, '-', '-') + method_obj, output, _ = self.prepare_modules(True, True, True, + lambda m: m.get_available_device_ids(method)) + return method_obj, output + + def call_write(self, method): + self.print_test_conditions(True, True, True, 'auto', 'auto') + return self.prepare_modules(True, True, True, + lambda m: m.write(array('B', [1, 2, 3]), method)) diff --git a/tests/test_lednamebadge_select_method.py b/tests/test_lednamebadge_select_method.py index 0acd963..c9bfd1f 100644 --- a/tests/test_lednamebadge_select_method.py +++ b/tests/test_lednamebadge_select_method.py @@ -1,17 +1,9 @@ -import sys -from unittest import TestCase -from unittest.mock import patch, MagicMock -from io import StringIO +from unittest.mock import patch +import abstract_witre_method_test -class USBError(BaseException): - pass - - -class Test(TestCase): - def setUp(self): - print("Real platform: " + sys.platform) +class Test(abstract_witre_method_test.AbstractWriteMethodTest): @patch('sys.platform', new='linux') def test_list(self): method, output = self.call_find(True, True, True, 'list', 'auto') @@ -156,107 +148,11 @@ class Test(TestCase): # ------------------------------------------------------------------------- - def test_get_methods(self): - methods, output = self.call_info_methods() - self.assertDictEqual({ - 'hidapi': True, - 'libusb': True}, methods) - - def test_get_device_ids(self): - device_ids, output = self.call_info_ids('libusb') - self.assertDictEqual({ - '3:4:2': 'LibUsb Test Manufacturer - LibUsb Test Product (bus=3 dev=4 endpoint=2)'}, - device_ids) - - device_ids, output = self.call_info_ids('hidapi') - self.assertDictEqual({ - '3-4:5-6': 'HidApi Test Manufacturer - HidApi Test Product (if=0)'}, - device_ids) - - - # ------------------------------------------------------------------------- - - def call_find(self, pyusb_available, pyhidapi_available, device_available, method, device_id): self.print_test_conditions(pyusb_available, pyhidapi_available, device_available, method, device_id) - method_obj, output = self.prepare_modules(pyusb_available, pyhidapi_available, device_available, + method_obj, output, _ = self.prepare_modules(pyusb_available, pyhidapi_available, device_available, lambda m: m._find_write_method(method, device_id)) self.assertEqual(pyusb_available, 'usb.core detected' in output) self.assertEqual(pyhidapi_available, 'pyhidapi detected' in output) return method_obj, output - def call_info_methods(self): - self.print_test_conditions(True, True, True, '-', '-') - return self.prepare_modules(True, True, True, - lambda m: m.get_available_methods()) - - def call_info_ids(self, method): - self.print_test_conditions(True, True, True, '-', '-') - return self.prepare_modules(True, True, True, - lambda m: m.get_available_device_ids(method)) - - def prepare_modules(self, pyusb_available, pyhidapi_available, device_available, func): - result = None - output = None - with self.do_import_patch(pyusb_available, pyhidapi_available, device_available) as mock: - with patch('sys.stdout', new_callable=StringIO) as stdio_mock: - import lednamebadge - try: - result = func(lednamebadge.LedNameBadge) - except(SystemExit): - pass - output = stdio_mock.getvalue() - print(output) - return result, output - - def print_test_conditions(self, pyusb_available, pyhidapi_available, device_available, method, device_id): - print("Test condition: os=%s pyusb=%s pyhidapi=%s device=%s method=%s device_id=%s" % ( - sys.platform, - 'yes' if pyusb_available else 'no', - 'yes' if pyhidapi_available else 'no', - 'yes' if device_available else 'no', - method, - device_id)) - - def do_import_patch(self, pyusb_available, pyhidapi_available, device_available): - patch_obj = patch.dict('sys.modules', { - 'pyhidapi': self.create_hid_mock(device_available) if pyhidapi_available else None, - 'usb': self.create_usb_mock(device_available) if pyusb_available else None, - 'usb.core': MagicMock() if pyusb_available else None, - 'usb.core.USBError': USBError if pyusb_available else None, - 'usb.util': MagicMock() if pyusb_available else None}) - # Assure fresh reimport of lednamebadge with current mocks - if 'lednamebadge' in sys.modules: - del sys.modules['lednamebadge'] - return patch_obj - - - def create_hid_mock(self, device_available): - device = MagicMock() - device.path = b'3-4:5-6' - device.manufacturer_string = 'HidApi Test Manufacturer' - device.product_string = 'HidApi Test Product' - device.interface_number = 0 - - mock = MagicMock() - mock.hid_enumerate.return_value = [device] if device_available else [] - mock.hid_open_path.return_value = 123456 if device_available else [] - return mock - - - def create_usb_mock(self, device_available): - device = MagicMock() - device.manufacturer = 'LibUsb Test Manufacturer' - device.product = 'LibUsb Test Product' - device.bus = 3 - device.address = 4 - - ep = MagicMock() - ep.bEndpointAddress = 2 - - mock = MagicMock() - mock.core = MagicMock() - mock.core.find.return_value = [device] if device_available else [] - mock.core.USBError = USBError - mock.util.find_descriptor.return_value = [ep] if device_available else [] - return mock