From 77caeb1b193404e76beb27a1aa81321e8eb1cf1f Mon Sep 17 00:00:00 2001 From: Maxime Peim Date: Tue, 14 Nov 2023 15:26:41 +0100 Subject: tests: preload api files When sanity test is not done, API files are not loaded until the first test case is run. Hence, it is not possible to use enums, etc. outside of a test class. By preloading API files before running any tests, it prevents its issue. Type: fix Change-Id: I8730150374e6c5f8d6933ec037811372ac2a8da0 Signed-off-by: Maxime Peim --- src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py | 6 +- src/vpp-api/python/vpp_papi/vpp_papi.py | 68 +++++++++++++--------- test/Makefile | 8 ++- test/config.py | 1 + test/run_tests.py | 4 ++ test/vpp_papi_provider.py | 5 +- 6 files changed, 54 insertions(+), 38 deletions(-) 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 2b21c83966a..51c024aa3ab 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 @@ -24,8 +24,7 @@ from vpp_papi import vpp_transport_shmem class TestVppPapiVPPApiClient(unittest.TestCase): def test_getcontext(self): - vpp_papi.VPPApiClient.apidir = "." - c = vpp_papi.VPPApiClient(testmode=True, use_socket=True) + c = vpp_papi.VPPApiClient(apidir=".", testmode=True, use_socket=True) # reset initialization at module load time. c.get_context.context = mp.Value(ctypes.c_uint, 0) @@ -39,8 +38,7 @@ class TestVppPapiVPPApiClientMp(unittest.TestCase): # run_tests.py (eg. make test TEST_JOBS=10) def test_get_context_mp(self): - vpp_papi.VPPApiClient.apidir = "." - c = vpp_papi.VPPApiClient(testmode=True, use_socket=True) + c = vpp_papi.VPPApiClient(apidir=".", testmode=True, use_socket=True) # reset initialization at module load time. c.get_context.context = mp.Value(ctypes.c_uint, 0) diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py index a9edfed81be..5c089647e59 100644 --- a/src/vpp-api/python/vpp_papi/vpp_papi.py +++ b/src/vpp-api/python/vpp_papi/vpp_papi.py @@ -281,16 +281,15 @@ class VPPApiJSONFiles: @classmethod def process_json_file(self, apidef_file): - api = json.load(apidef_file) - return self._process_json(api) + return self._process_json(apidef_file.read()) @classmethod def process_json_str(self, json_str): - api = json.loads(json_str) - return self._process_json(api) + return self._process_json(json_str) @staticmethod - def _process_json(api): # -> Tuple[Dict, Dict] + def _process_json(json_str): # -> Tuple[Dict, Dict] + api = json.loads(json_str) types = {} services = {} messages = {} @@ -380,6 +379,30 @@ class VPPApiJSONFiles: pass return messages, services + @staticmethod + def load_api(apifiles=None, apidir=None): + messages = {} + services = {} + if not apifiles: + # Pick up API definitions from default directory + try: + if isinstance(apidir, list): + apifiles = [] + for d in apidir: + apifiles += VPPApiJSONFiles.find_api_files(d) + else: + apifiles = VPPApiJSONFiles.find_api_files(apidir) + except (RuntimeError, VPPApiError): + raise VPPRuntimeError + + for file in apifiles: + with open(file) as apidef_file: + m, s = VPPApiJSONFiles.process_json_file(apidef_file) + messages.update(m) + services.update(s) + + return apifiles, messages, services + class VPPApiClient: """VPP interface. @@ -394,7 +417,6 @@ class VPPApiClient: these messages in a background thread. """ - apidir = None VPPApiError = VPPApiError VPPRuntimeError = VPPRuntimeError VPPValueError = VPPValueError @@ -405,6 +427,7 @@ class VPPApiClient: self, *, apifiles=None, + apidir=None, testmode=False, async_thread=True, logger=None, @@ -439,6 +462,7 @@ class VPPApiClient: self.id_msgdef = [] self.header = VPPType("header", [["u16", "msgid"], ["u32", "client_index"]]) self.apifiles = [] + self.apidir = apidir self.event_callback = None self.message_queue = queue.Queue() self.read_timeout = read_timeout @@ -449,29 +473,15 @@ class VPPApiClient: self._apifiles = apifiles self.stats = {} - if not apifiles: - # Pick up API definitions from default directory - try: - if isinstance(self.apidir, list): - apifiles = [] - for d in self.apidir: - apifiles += VPPApiJSONFiles.find_api_files(d) - else: - apifiles = VPPApiJSONFiles.find_api_files(self.apidir) - except (RuntimeError, VPPApiError): - # In test mode we don't care that we can't find the API files - if testmode: - apifiles = [] - else: - raise VPPRuntimeError - - for file in apifiles: - with open(file) as apidef_file: - m, s = VPPApiJSONFiles.process_json_file(apidef_file) - self.messages.update(m) - self.services.update(s) - - self.apifiles = apifiles + try: + self.apifiles, self.messages, self.services = VPPApiJSONFiles.load_api( + apifiles, apidir + ) + except VPPRuntimeError as e: + if testmode: + self.apifiles = [] + else: + raise e # Basic sanity check if len(self.messages) == 0 and not testmode: diff --git a/test/Makefile b/test/Makefile index 281c7bd8b44..7ea83acfff7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -259,6 +259,12 @@ ifneq ($(findstring $(DECODE_PCAPS),1 y yes),) ARG18=--decode-pcaps endif +ifneq ($(findstring $(API_PRELOAD),1 y yes),) +ARG19=--api-preload +else +ARG19= +endif + EXC_PLUGINS_ARG= ifneq ($(VPP_EXCLUDED_PLUGINS),) # convert the comma-separated list into N invocations of the argument to exclude a plugin @@ -267,7 +273,7 @@ endif -EXTRA_ARGS=$(ARG0) $(ARG1) $(ARG2) $(ARG3) $(ARG4) $(ARG5) $(ARG6) $(ARG7) $(ARG8) $(ARG9) $(ARG10) $(ARG11) $(ARG12) $(ARG13) $(ARG14) $(ARG15) $(ARG16) $(ARG17) $(ARG18) +EXTRA_ARGS=$(ARG0) $(ARG1) $(ARG2) $(ARG3) $(ARG4) $(ARG5) $(ARG6) $(ARG7) $(ARG8) $(ARG9) $(ARG10) $(ARG11) $(ARG12) $(ARG13) $(ARG14) $(ARG15) $(ARG16) $(ARG17) $(ARG18) $(ARG19) RUN_TESTS_ARGS=--failed-dir=$(FAILED_DIR) --verbose=$(V) --jobs=$(TEST_JOBS) --filter=$(TEST) --retries=$(RETRIES) --venv-dir=$(VENV_PATH) --vpp-ws-dir=$(WS_ROOT) --vpp-tag=$(TAG) --rnd-seed=$(RND_SEED) --vpp-worker-count="$(VPP_WORKER_COUNT)" --keep-pcaps $(PLUGIN_PATH_ARGS) $(EXC_PLUGINS_ARG) $(TEST_PLUGIN_PATH_ARGS) $(EXTRA_ARGS) RUN_SCRIPT_ARGS=--python-opts=$(PYTHON_OPTS) diff --git a/test/config.py b/test/config.py index 2fa93ddef3b..32cc4cac5fa 100644 --- a/test/config.py +++ b/test/config.py @@ -201,6 +201,7 @@ parser.add_argument( parser.add_argument( "--sanity", action="store_true", help="perform sanity vpp run before running tests" ) +parser.add_argument("--api-preload", action="store_true", help="preload API files") parser.add_argument( "--force-foreground", diff --git a/test/run_tests.py b/test/run_tests.py index 19ab9052d5c..f20c43af7d7 100644 --- a/test/run_tests.py +++ b/test/run_tests.py @@ -14,6 +14,7 @@ from multiprocessing import Process, Pipe, get_context from multiprocessing.queues import Queue from multiprocessing.managers import BaseManager from config import config, num_cpus, available_cpus, max_vpp_cpus +from vpp_papi import VPPApiJSONFiles from asfframework import ( VppTestRunner, get_testcase_doc_name, @@ -906,6 +907,9 @@ def parse_results(results): if __name__ == "__main__": print(f"Config is: {config}") + if config.api_preload: + VPPApiJSONFiles.load_api(apidir=config.extern_apidir + [config.vpp_install_dir]) + if config.sanity: print("Running sanity test case.") try: diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 6c3cd7f83fa..d1c40a97bce 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -236,11 +236,8 @@ class VppPapiProvider(object): self._expect_api_retval = self._zero self._expect_stack = [] - # install_dir is a class attribute. We need to set it before - # calling the constructor. - VPPApiClient.apidir = config.extern_apidir + [config.vpp_install_dir] - self.vpp = VPPApiClient( + apidir=config.extern_apidir + [config.vpp_install_dir], logger=test_class.logger, read_timeout=read_timeout, use_socket=True, -- cgit 1.2.3-korg