tests: preload api files 71/39871/11
authorMaxime Peim <mpeim@cisco.com>
Tue, 14 Nov 2023 14:26:41 +0000 (15:26 +0100)
committerDave Wallace <dwallacelf@gmail.com>
Thu, 18 Jan 2024 20:14:54 +0000 (20:14 +0000)
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 <mpeim@cisco.com>
src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
src/vpp-api/python/vpp_papi/vpp_papi.py
test/Makefile
test/config.py
test/run_tests.py
test/vpp_papi_provider.py

index 2b21c83..51c024a 100644 (file)
@@ -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)
index a9edfed..5c08964 100644 (file)
@@ -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:
index 281c7bd..7ea83ac 100644 (file)
@@ -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)
index 2fa93dd..32cc4ca 100644 (file)
@@ -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",
index 19ab905..f20c43a 100644 (file)
@@ -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:
index 6c3cd7f..d1c40a9 100644 (file)
@@ -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,