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 <pvinci@vinciconsulting.com>
This commit is contained in:
Paul Vinciguerra
2020-12-01 02:00:35 -05:00
committed by Ole Tr�an
parent 41f15ae1df
commit 46d6864b9d
3 changed files with 133 additions and 23 deletions

View File

@ -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)

View File

@ -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 = []

View File

@ -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)")