[rtems-central commit] validation: Add test header for test cases

Sebastian Huber sebh at rtems.org
Thu Aug 20 14:13:13 UTC 2020


Module:    rtems-central
Branch:    master
Commit:    3e9b695d3b638fa0072dbed9ddd2d8d3482161e4
Changeset: http://git.rtems.org/rtems-central/commit/?id=3e9b695d3b638fa0072dbed9ddd2d8d3482161e4

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Fri Aug 14 08:13:22 2020 +0200

validation: Add test header for test cases

---

 rtemsspec/tests/spec-validation/tc5.yml |  73 ++++++++++++
 rtemsspec/tests/spec-validation/tc6.yml |  22 ++++
 rtemsspec/tests/test_validation.py      | 205 +++++++++++++++++++++++++++++++-
 rtemsspec/validation.py                 | 159 ++++++++++++++++---------
 4 files changed, 400 insertions(+), 59 deletions(-)

diff --git a/rtemsspec/tests/spec-validation/tc5.yml b/rtemsspec/tests/spec-validation/tc5.yml
new file mode 100644
index 0000000..efd6fa5
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tc5.yml
@@ -0,0 +1,73 @@
+SPDX-License-Identifier: CC-BY-SA-5.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+test-actions:
+- action: |
+    /* Test case action 0 code */
+  checks:
+  - check: |
+      /* Test case action 0 check 0 code */
+    description: |
+      Test case action 0 check 0 description.
+    links: []
+  - check: |
+      /* Test case action 0 check 1 code; step ${step} */
+    description: |
+      Test case action 0 check 1 description.
+    links: []
+  description: |
+    Test case action 0 description.
+  links: []
+- action: |
+    /* Test case action 1 code */
+  checks:
+  - check: |
+      /* Test case action 1 check 0 code; step ${step} */
+    description: |
+      Test case action 1 check 0 description.
+    links: []
+  - check: |
+      /* Test case action 1 check 1 code */
+    description: |
+      Test case action 1 check 1 description.
+    links: []
+  description: |
+    Test case action 1 description.
+  links: []
+test-brief: Test case 5 brief description.
+test-description: Test case 5 description.
+test-epilogue: |
+  /* Test case 5 epilogue code */
+test-fixture: null
+test-header:
+  code: |
+    /* Header code for ${.:/test-run}() */
+  includes:
+  - d.h
+  local-includes:
+  - e.h
+  run-params:
+  - description: |
+      Parameter A description.
+    dir: in
+    name: a
+    specifier: int *${.:name}
+  - description: |
+      Parameter B description.
+    dir: null
+    name: b
+    specifier: int ${.:name}
+  - description: |
+      Parameter C description.
+    dir: out
+    name: c
+    specifier: int *${.:name}
+  target: tc5.h
+test-includes: []
+test-local-includes: []
+test-prologue: null
+test-support: null
+test-target: tc34.c
+type: test-case
diff --git a/rtemsspec/tests/spec-validation/tc6.yml b/rtemsspec/tests/spec-validation/tc6.yml
new file mode 100644
index 0000000..02c100b
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tc6.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-6.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+test-actions: []
+test-brief: null
+test-description: null
+test-epilogue: null
+test-fixture: null
+test-header:
+  code: null
+  includes: []
+  local-includes: []
+  run-params: []
+  target: tc6.h
+test-includes: []
+test-local-includes: []
+test-prologue: null
+test-support: null
+test-target: tc34.c
+type: test-case
diff --git a/rtemsspec/tests/test_validation.py b/rtemsspec/tests/test_validation.py
index ca7668a..708a0ea 100644
--- a/rtemsspec/tests/test_validation.py
+++ b/rtemsspec/tests/test_validation.py
@@ -104,7 +104,6 @@ def test_validation(tmpdir):
 /** @} */
 """
         assert content == src.read()
-
     with open(os.path.join(base_directory, "tc12.c"), "r") as src:
         content = """/* SPDX-License-Identifier: BSD-2-Clause */
 
@@ -894,7 +893,6 @@ T_TEST_CASE_FIXTURE( Tc2, &test_case_2_fixture )
 /** @} */
 """
         assert content == src.read()
-
     with open(os.path.join(base_directory, "tc34.c"), "r") as src:
         content = """/* SPDX-License-Identifier: BSD-2-Clause */
 
@@ -903,6 +901,8 @@ T_TEST_CASE_FIXTURE( Tc2, &test_case_2_fixture )
  *
  * @ingroup RTEMSTestCaseTc3
  * @ingroup RTEMSTestCaseTc4
+ * @ingroup RTEMSTestCaseTc5
+ * @ingroup RTEMSTestCaseTc6
  */
 
 /*
@@ -996,6 +996,207 @@ T_TEST_CASE( Tc4 )
 }
 
 /** @} */
+
+/**
+ * @defgroup RTEMSTestCaseTc5 spec:/tc5
+ *
+ * @ingroup RTEMSTestSuiteTs
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+static void Tc5_Wrap( int *a, int b, int *c )
+{
+  T_plan(2);
+
+  /* Test case action 0 code */
+  /* Test case action 0 check 0 code */
+  /* Test case action 0 check 1 code; step 0 */
+
+  /* Test case action 1 code */
+  /* Test case action 1 check 0 code; step 1 */
+  /* Test case action 1 check 1 code */
+
+  /* Test case 5 epilogue code */
+}
+
+static T_fixture_node Tc5_Node;
+
+void Tc5_Run( int *a, int b, int *c )
+{
+  T_push_fixture( &Tc5_Node, &T_empty_fixture );
+  Tc5_Wrap( a, b, c );
+  T_pop_fixture();
+}
+
+/** @} */
+
+/**
+ * @defgroup RTEMSTestCaseTc6 spec:/tc6
+ *
+ * @ingroup RTEMSTestSuiteTs
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+void Tc6_Run( void )
+{
+}
+
+/** @} */
+"""
+        assert content == src.read()
+    with open(os.path.join(base_directory, "tc5.h"), "r") as src:
+        content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestCaseTc5
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TC5_H
+#define _TC5_H
+
+#include <d.h>
+
+#include "e.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup RTEMSTestCaseTc5
+ *
+ * @{
+ */
+
+/* Header code for Tc5_Run() */
+
+/**
+ * @brief Test case 5 brief description.
+ *
+ * Test case 5 description.
+ *
+ * This test case performs the following actions:
+ *
+ * - Test case action 0 description.
+ *
+ *   - Test case action 0 check 0 description.
+ *
+ *   - Test case action 0 check 1 description.
+ *
+ * - Test case action 1 description.
+ *
+ *   - Test case action 1 check 0 description.
+ *
+ *   - Test case action 1 check 1 description.
+ *
+ * @param[in] a Parameter A description.
+ *
+ * @param b Parameter B description.
+ *
+ * @param[out] c Parameter C description.
+ */
+void Tc5_Run( int *a, int b, int *c );
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TC5_H */
+"""
+        assert content == src.read()
+    with open(os.path.join(base_directory, "tc6.h"), "r") as src:
+        content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestCaseTc6
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TC6_H
+#define _TC6_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup RTEMSTestCaseTc6
+ *
+ * @{
+ */
+
+/**
+ */
+void Tc6_Run( void );
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TC6_H */
 """
         assert content == src.read()
     with open(os.path.join(base_directory, "action2.h"), "r") as src:
diff --git a/rtemsspec/validation.py b/rtemsspec/validation.py
index 3e55702..3a34474 100644
--- a/rtemsspec/validation.py
+++ b/rtemsspec/validation.py
@@ -36,11 +36,16 @@ from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper
 ItemMap = Dict[str, Item]
 
 
+def _get_test_run(ctx: ItemGetValueContext) -> Any:
+    return f"{to_camel_case(ctx.item.uid[1:]).replace(' ', '')}_Run"
+
+
 class _CodeMapper(ItemMapper):
-    def get_value(self, ctx: ItemGetValueContext) -> Any:
-        if ctx.type_path_key == "requirement/functional/action:/test-run":
-            return f"{to_camel_case(ctx.item.uid[1:]).replace(' ', '')}_Run"
-        raise KeyError
+    def __init__(self, item: Item):
+        super().__init__(item)
+        self.add_get_value("requirement/functional/action:/test-run",
+                           _get_test_run)
+        self.add_get_value("test-case:/test-run", _get_test_run)
 
 
 class _TextMapper(ItemMapper):
@@ -147,7 +152,8 @@ class _TestItem:
             _add_ingroup(content, test_case_to_suites[self.uid])
             content.add(["@brief Test Case", "", "@{"])
 
-    def _add_test_case_action_description(self, content: CContent) -> None:
+    def add_test_case_action_description(self, content: CContent) -> None:
+        """ Adds the test case action description. """
         actions = self["test-actions"]
         if actions:
             content.add("This test case performs the following actions:")
@@ -167,31 +173,100 @@ class _TestItem:
                 content.append(self.substitute_text(check["check"]))
         return content
 
-    def generate(self, content: CContent, _base_directory: str,
+    def _get_run_params(self, header: Optional[Dict[str, Any]]) -> List[str]:
+        if not header:
+            return []
+        return [
+            self.substitute_text(param["specifier"],
+                                 f"test-header/run-params[{index}]")
+            for index, param in enumerate(header["run-params"])
+        ]
+
+    def add_header_body(self, content: CContent, header: Dict[str,
+                                                              Any]) -> None:
+        """ Adds the test header body. """
+        content.add(self.substitute_code(header["code"]))
+        with content.doxygen_block():
+            content.add_brief_description(self.substitute_text(self.brief))
+            content.wrap(self.substitute_text(self.description))
+            self.add_test_case_action_description(content)
+            content.add_param_description(header["run-params"])
+        content.gap = False
+        content.declare_function("void", f"{self.ident}_Run",
+                                 self._get_run_params(header))
+
+    def generate_header(self, base_directory: str, header: Dict[str,
+                                                                Any]) -> None:
+        """ Generates the test header. """
+        content = CContent()
+        content.register_license_and_copyrights_of_item(self._item)
+        content.prepend_spdx_license_identifier()
+        with content.file_block():
+            content.add_ingroup([self.group_identifier])
+        content.add_copyrights_and_licenses()
+        with content.header_guard(os.path.basename(header["target"])):
+            content.add_includes(list(map(CInclude, header["includes"])))
+            content.add_includes(list(map(CInclude, header["local-includes"])),
+                                 local=True)
+            with content.extern_c():
+                with content.add_to_group(self.group_identifier):
+                    self.add_header_body(content, header)
+        content.write(os.path.join(base_directory, header["target"]))
+
+    def generate(self, content: CContent, base_directory: str,
                  test_case_to_suites: Dict[str, List["_TestItem"]]) -> None:
         """ Generates the content. """
         self.add_test_case_description(content, test_case_to_suites)
-        content.add(self.substitute_code(self["test-support"]))
-        with content.function_block(f"void T_case_body_{self.ident}( void )"):
-            content.add_brief_description(self.brief)
-            content.wrap(self.description)
-            self._add_test_case_action_description(content)
-        content.gap = False
-        params = [f"{self.ident}"]
+        self._text_mapper.reset_step()
+        actions = self._generate_test_case_actions()
         fixture = self["test-fixture"]
-        if fixture:
-            params.append(f"&{fixture}")
-            name = "T_TEST_CASE_FIXTURE"
+        header = self["test-header"]
+        if header:
+            self.generate_header(base_directory, header)
+            if self._text_mapper.steps > 0 and not fixture:
+                fixture = "T_empty_fixture"
+        content.add(self.substitute_code(self["test-support"]))
+        if header:
+            params = self._get_run_params(header)
+            if fixture:
+                ret = "static void"
+                name = f"{self.ident}_Wrap"
+            else:
+                ret = "void"
+                name = f"{self.ident}_Run"
+            align = True
         else:
-            name = "T_TEST_CASE"
-        with content.function("", name, params):
+            ret = ""
+            params = [f"{self.ident}"]
+            if fixture:
+                params.append(f"&{fixture}")
+                name = "T_TEST_CASE_FIXTURE"
+            else:
+                name = "T_TEST_CASE"
+            align = False
+            with content.function_block(
+                    f"void T_case_body_{self.ident}( void )"):
+                content.add_brief_description(self.brief)
+                content.wrap(self.description)
+                self.add_test_case_action_description(content)
+            content.gap = False
+        with content.function(ret, name, params, align=align):
             content.add(self.substitute_code(self["test-prologue"]))
-            self._text_mapper.reset_step()
-            action_content = self._generate_test_case_actions()
             if self._text_mapper.steps > 0:
                 content.add(f"T_plan({self._text_mapper.steps});")
-            content.add(action_content)
+            content.add(actions)
             content.add(self.substitute_code(self["test-epilogue"]))
+        if header and fixture:
+            run = f"{self.ident}_Run"
+            content.add(f"static T_fixture_node {self.ident}_Node;")
+            with content.function("void", run, params, align=align):
+                content.call_function(None, "T_push_fixture",
+                                      [f"&{self.ident}_Node", f"&{fixture}"])
+                content.gap = False
+                content.call_function(
+                    None, name,
+                    [param["name"] for param in header["run-params"]])
+                content.append("T_pop_fixture();")
         content.add("/** @} */")
 
 
@@ -579,51 +654,21 @@ class _TestDirectiveItem(_TestItem):
                 content.add("}")
                 content.add(self.substitute_code(condition["test-epilogue"]))
 
-    def _get_run_params(self, header: Optional[Dict[str, Any]]) -> List[str]:
-        if not header:
-            return []
-        return [
-            self.substitute_text(param["specifier"],
-                                 f"test-header/run-params[{index}]")
-            for index, param in enumerate(header["run-params"])
-        ]
+    def add_test_case_action_description(self, _content: CContent) -> None:
+        pass
 
-    def _generate_header_body(self, content: CContent,
-                              header: Dict[str, Any]) -> None:
+    def add_header_body(self, content: CContent, header: Dict[str,
+                                                              Any]) -> None:
         _directive_add_enum(content, self._pre_index_to_enum)
         _directive_add_enum(content, self._post_index_to_enum)
-        content.add(self.substitute_code(header["code"]))
-        with content.doxygen_block():
-            content.add_brief_description(self.brief)
-            content.wrap(self.description)
-            content.add_param_description(header["run-params"])
-        content.gap = False
-        content.declare_function("void", f"{self.ident}_Run",
-                                 self._get_run_params(header))
-
-    def _generate_header(self, base_directory: str, header: Dict[str,
-                                                                 Any]) -> None:
-        content = CContent()
-        content.register_license_and_copyrights_of_item(self._item)
-        content.prepend_spdx_license_identifier()
-        with content.file_block():
-            content.add_ingroup([self.group_identifier])
-        content.add_copyrights_and_licenses()
-        with content.header_guard(os.path.basename(header["target"])):
-            content.add_includes(list(map(CInclude, header["includes"])))
-            content.add_includes(list(map(CInclude, header["local-includes"])),
-                                 local=True)
-            with content.extern_c():
-                with content.add_to_group(self.group_identifier):
-                    self._generate_header_body(content, header)
-        content.write(os.path.join(base_directory, header["target"]))
+        super().add_header_body(content, header)
 
     def generate(self, content: CContent, base_directory: str,
                  test_case_to_suites: Dict[str, List[_TestItem]]) -> None:
         self.add_test_case_description(content, test_case_to_suites)
         header = self["test-header"]
         if header:
-            self._generate_header(base_directory, header)
+            self.generate_header(base_directory, header)
         else:
             _directive_add_enum(content, self._pre_index_to_enum)
             _directive_add_enum(content, self._post_index_to_enum)



More information about the vc mailing list