From 46d6864b9de6d722c4b6eb72d231918b251e267d Mon Sep 17 00:00:00 2001 From: Paul Vinciguerra Date: Tue, 1 Dec 2020 02:00:35 -0500 Subject: papi: improve unit testability refactor the code so that snippets of json can be used to test vpp_papi example unit test provided Type: improvement Change-Id: Ibec608fd2e5b12515aa4db17d85d4319134c22ea Signed-off-by: Paul Vinciguerra --- src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py | 63 ++++++++++++++++ src/vpp-api/python/vpp_papi/vpp_papi.py | 85 ++++++++++++++++------ src/vpp-api/python/vpp_papi/vpp_transport_shmem.py | 8 +- 3 files changed, 133 insertions(+), 23 deletions(-) (limited to 'src/vpp-api/python') diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py index 774b4e1092c..d0c72497bd0 100644 --- a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py +++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py @@ -14,8 +14,12 @@ import ctypes import multiprocessing as mp +import sys import unittest +from unittest import mock + from vpp_papi import vpp_papi +from vpp_papi import vpp_transport_shmem class TestVppPapiVPPApiClient(unittest.TestCase): @@ -51,3 +55,62 @@ class TestVppPapiVPPApiClientMp(unittest.TestCase): # AssertionError: 11 != 1 self.assertEqual(11, c.get_context()) + +class TestVppTypes(unittest.TestCase): + + def test_enum_from_json(self): + json_api = """\ +{ + "enums": [ + + [ + "address_family", + [ + "ADDRESS_IP4", + 0 + ], + [ + "ADDRESS_IP6", + 1 + ], + { + "enumtype": "u8" + } + ], + [ + "if_type", + [ + "IF_API_TYPE_HARDWARE", + 0 + ], + [ + "IF_API_TYPE_SUB", + 1 + ], + [ + "IF_API_TYPE_P2P", + 2 + ], + [ + "IF_API_TYPE_PIPE", + 3 + ], + { + "enumtype": "u32" + } + ] + ] +} +""" + processor = vpp_papi.VPPApiJSONFiles() + + # add the types to vpp_serializer + processor.process_json_str(json_api) + + vpp_transport_shmem.VppTransport = mock.MagicMock() + ac = vpp_papi.VPPApiClient(apifiles=[], testmode=True) + type_name = "vl_api_if_type_t" + t = ac.get_type(type_name) + self.assertTrue(str(t).startswith("VPPEnumType")) + self.assertEqual(t.name, type_name) + diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py index 192168772ec..1b4df069810 100644 --- a/src/vpp-api/python/vpp_papi/vpp_papi.py +++ b/src/vpp-api/python/vpp_papi/vpp_papi.py @@ -33,6 +33,19 @@ from . vpp_format import verify_enum_hint from . vpp_serializer import VPPType, VPPEnumType, VPPUnionType from . vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias +try: + import VppTransport +except ModuleNotFoundError: + class V: + """placeholder for VppTransport as the implementation is dependent on + VPPAPIClient's initialization values + """ + + VppTransport = V + +logger = logging.getLogger(__name__) +logger.addHandler(logging.NullHandler()) + if sys.version[0] == '2': import Queue as queue else: @@ -218,7 +231,7 @@ class VPPApiJSONFiles(object): return None @classmethod - def find_api_files(cls, api_dir=None, patterns='*'): + def find_api_files(cls, api_dir=None, patterns='*'): # -> list """Find API definition files from the given directory tree with the given pattern. If no directory is given then find_api_dir() is used to locate one. If no pattern is given then all definition files found @@ -260,21 +273,49 @@ class VPPApiJSONFiles(object): @classmethod def process_json_file(self, apidef_file): api = json.load(apidef_file) + return self._process_json(api) + + @classmethod + def process_json_str(self, json_str): + api = json.loads(json_str) + return self._process_json(api) + + @staticmethod + def _process_json(api): # -> Tuple[Dict, Dict] types = {} services = {} messages = {} - for t in api['enums']: - t[0] = 'vl_api_' + t[0] + '_t' - types[t[0]] = {'type': 'enum', 'data': t} - for t in api['unions']: - t[0] = 'vl_api_' + t[0] + '_t' - types[t[0]] = {'type': 'union', 'data': t} - for t in api['types']: - t[0] = 'vl_api_' + t[0] + '_t' - types[t[0]] = {'type': 'type', 'data': t} - for t, v in api['aliases'].items(): - types['vl_api_' + t + '_t'] = {'type': 'alias', 'data': v} - services.update(api['services']) + try: + for t in api['enums']: + t[0] = 'vl_api_' + t[0] + '_t' + types[t[0]] = {'type': 'enum', 'data': t} + except KeyError: + pass + + try: + for t in api['unions']: + t[0] = 'vl_api_' + t[0] + '_t' + types[t[0]] = {'type': 'union', 'data': t} + except KeyError: + pass + + try: + for t in api['types']: + t[0] = 'vl_api_' + t[0] + '_t' + types[t[0]] = {'type': 'type', 'data': t} + except KeyError: + pass + + try: + for t, v in api['aliases'].items(): + types['vl_api_' + t + '_t'] = {'type': 'alias', 'data': v} + except KeyError: + pass + + try: + services.update(api['services']) + except KeyError: + pass i = 0 while True: @@ -309,13 +350,15 @@ class VPPApiJSONFiles(object): .format(unresolved)) types = unresolved i += 1 - - for m in api['messages']: - try: - messages[m[0]] = VPPMessage(m[0], m[1:]) - except VPPNotImplementedError: - ### OLE FIXME - self.logger.error('Not implemented error for {}'.format(m[0])) + try: + for m in api['messages']: + try: + messages[m[0]] = VPPMessage(m[0], m[1:]) + except VPPNotImplementedError: + ### OLE FIXME + logger.error('Not implemented error for {}'.format(m[0])) + except KeyError: + pass return messages, services @@ -389,7 +432,7 @@ class VPPApiClient(object): # Pick up API definitions from default directory try: apifiles = VPPApiJSONFiles.find_api_files(self.apidir) - except RuntimeError: + except (RuntimeError, VPPApiError): # In test mode we don't care that we can't find the API files if testmode: apifiles = [] diff --git a/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py b/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py index fa8943fc119..a7ba26b4722 100644 --- a/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py +++ b/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py @@ -29,8 +29,12 @@ void vac_mem_init (size_t size); vpp_object = None -# Barfs on failure, no need to check success. -vpp_api = ffi.dlopen('libvppapiclient.so') +# allow file to be imported so it can be mocked in tests. +# If the shared library fails, VppTransport cannot be initialized. +try: + vpp_api = ffi.dlopen('libvppapiclient.so') +except OSError: + vpp_api = None @ffi.callback("void(unsigned char *, int)") -- cgit 1.2.3-korg