[rtems-central commit] validation: Add ${.:skip} for action requirements

Sebastian Huber sebh at rtems.org
Tue Aug 17 07:36:33 UTC 2021


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

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Fri Aug 13 20:08:29 2021 +0200

validation: Add ${.:skip} for action requirements

Add support to skip pre-condition states at runtime.

---

 rtemsspec/tests/spec-validation/action2.yml |  1 +
 rtemsspec/tests/test_validation.py          | 44 +++++++++++++++++
 rtemsspec/validation.py                     | 75 ++++++++++++++++++++++++-----
 3 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/rtemsspec/tests/spec-validation/action2.yml b/rtemsspec/tests/spec-validation/action2.yml
index d379a27..17b649a 100644
--- a/rtemsspec/tests/spec-validation/action2.yml
+++ b/rtemsspec/tests/spec-validation/action2.yml
@@ -63,6 +63,7 @@ pre-conditions:
   - name: A1
     test-code: |
       /* Pre A 1 */
+      ${.:skip}
     text: |
       Pre A 1.
   test-epilogue: |
diff --git a/rtemsspec/tests/test_validation.py b/rtemsspec/tests/test_validation.py
index 43a2cd7..5314050 100644
--- a/rtemsspec/tests/test_validation.py
+++ b/rtemsspec/tests/test_validation.py
@@ -310,6 +310,12 @@ typedef struct {
      * @brief This member contains the current transition map entry.
      */
     Directive_Entry entry;
+
+    /**
+     * @brief If this member is true, then the current transition variant
+     *   should be skipped.
+     */
+    bool skip;
   } Map;
 } Directive_Context;
 
@@ -1899,6 +1905,12 @@ typedef struct {
      * @brief This member contains the current transition map entry.
      */
     Action2_Entry entry;
+
+    /**
+     * @brief If this member is true, then the current transition variant
+     *   should be skipped.
+     */
+    bool skip;
   } Map;
 } Action2_Context;
 
@@ -1952,6 +1964,7 @@ static void Action2_Pre_A_Prepare( Action2_Context *ctx, Action2_Pre_A state )
        * Pre A 1.
        */
       /* Pre A 1 */
+      ctx->Map.skip = true;
       break;
     }
 
@@ -2223,12 +2236,43 @@ static inline Action2_Entry Action2_PopEntry( Action2_Context *ctx )
   ];
 }
 
+static void Action2_Skip( Action2_Context *ctx, size_t index )
+{
+  size_t increment;
+
+  ctx->Map.skip = false;
+  increment = 1;
+
+  switch ( index + 1 ) {
+    case 0:
+      increment *= Action2_Pre_A_NA;
+      ctx->Map.pcs[ 0 ] = Action2_Pre_A_NA - 1;
+      /* Fall through */
+    case 1:
+      increment *= Action2_Pre_B_NA;
+      ctx->Map.pcs[ 1 ] = Action2_Pre_B_NA - 1;
+      /* Fall through */
+    case 2:
+      increment *= Action2_Pre_C_NA;
+      ctx->Map.pcs[ 2 ] = Action2_Pre_C_NA - 1;
+      break;
+  }
+
+  ctx->Map.index += increment - 1;
+}
+
 static void Action2_TestVariant( Action2_Context *ctx )
 {
   Action2_Pre_A_Prepare(
     ctx,
     ctx->Map.entry.Pre_A_NA ? Action2_Pre_A_NA : ctx->Map.pcs[ 0 ]
   );
+
+  if ( ctx->Map.skip ) {
+    Action2_Skip( ctx, 0 );
+    return;
+  }
+
   Action2_Pre_B_Prepare( ctx, ctx->Map.pcs[ 1 ] );
   Action2_Pre_C_Prepare( ctx, ctx->Map.pcs[ 2 ] );
   Action2_Action( ctx );
diff --git a/rtemsspec/validation.py b/rtemsspec/validation.py
index c743b1c..306a0d9 100644
--- a/rtemsspec/validation.py
+++ b/rtemsspec/validation.py
@@ -48,7 +48,7 @@ def _get_test_run(ctx: ItemGetValueContext) -> Any:
 
 
 class _Mapper(ItemMapper):
-    def __init__(self, item: Item):
+    def __init__(self, item: Item, test_item: "_TestItem"):
         super().__init__(item)
         self._step = 0
         self.add_get_value("glossary/term:/plural", get_value_plural)
@@ -60,6 +60,9 @@ class _Mapper(ItemMapper):
         self.add_get_value("interface/macro:/params/name", get_value_params)
         self.add_get_value("requirement/functional/action:/test-run",
                            _get_test_run)
+        self.add_get_value(("requirement/functional/action:"
+                            "/pre-conditions/states/test-code/skip"),
+                           test_item.skip_pre_condition)
         self.add_get_value("test-case:/test-run", _get_test_run)
 
     @property
@@ -99,7 +102,7 @@ class _TestItem:
         self._item = item
         self._ident = to_camel_case(item.uid[1:])
         self._context = f"{self._ident}_Context"
-        self._mapper = _Mapper(item)
+        self._mapper = _Mapper(item, self)
 
     def __getitem__(self, key: str):
         return self._item[key]
@@ -154,9 +157,13 @@ class _TestItem:
         """ Returns the group identifier. """
         return f"RTEMSTestCase{self.ident}"
 
-    def substitute_code(self, text: Optional[str]) -> str:
-        """ Performs a variable substitution for code. """
-        return self._mapper.substitute(text)
+    def substitute_code(self,
+                        text: Optional[str],
+                        prefix: Optional[str] = None) -> str:
+        """
+        Performs a variable substitution for code with an optional prefix.
+        """
+        return self._mapper.substitute(text, prefix=prefix)
 
     def substitute_text(self,
                         text: Optional[str],
@@ -460,6 +467,10 @@ class _TestItem:
             content.add(epilogue)
         content.add("/** @} */")
 
+    def skip_pre_condition(self, _ctx: ItemGetValueContext) -> Any:
+        # pylint: disable=no-self-use
+        """ Adds code to skip the current pre-condition state. """
+
 
 class _TestSuiteItem(_TestItem):
     """ A test suite item. """
@@ -501,6 +512,7 @@ class _ActionRequirementTestItem(_TestItem):
     """ An action requirement test item. """
     def __init__(self, item: Item):
         super().__init__(item)
+        self._pre_co_skip = {}  # type: Dict[int, bool]
         self._pre_co_count = len(item["pre-conditions"])
         self._pre_co_idx_to_enum = _to_enum(f"{self.ident}_Pre",
                                             item["pre-conditions"])
@@ -544,6 +556,10 @@ class _ActionRequirementTestItem(_TestItem):
             content.add_description_block(
                 "This member contains the current transition map entry.", None)
             content.add(f"{self.ident}_Entry entry;")
+            content.add_description_block(
+                "If this member is true, then the current transition "
+                "variant should be skipped.", None)
+            content.add("bool skip;")
         content.append("} Map;")
 
     def _add_fixture_scope(self, content: CContent) -> None:
@@ -561,6 +577,27 @@ class _ActionRequirementTestItem(_TestItem):
             content.gap = False
             content.call_function(None, f"{self.ident}_{name}", ["ctx"])
 
+    def _add_skip(self, content: CContent) -> Any:
+        with content.function("static void", f"{self.ident}_Skip",
+                              [f"{self.context} *ctx", "size_t index"]):
+            content.append([
+                "size_t increment;", "", "ctx->Map.skip = false;",
+                "increment = 1;", "", "switch ( index + 1 ) {"
+            ])
+            fall_through = "/* Fall through */"
+            with content.indent():
+                for index, enum in enumerate(self._pre_co_idx_to_enum):
+                    content.add(f"case {index}:")
+                    with content.indent():
+                        content.append([
+                            f"increment *= {enum[-1]};",
+                            f"ctx->Map.pcs[ {index} ] = {enum[-1]} - 1;",
+                            fall_through
+                        ])
+                content.lines[-1] = content.lines[-1].replace(
+                    fall_through, "break;")
+            content.append(["}", "", "ctx->Map.index += increment - 1;"])
+
     def _add_test_variant(self, content: CContent,
                           transition_map: TransitionMap) -> None:
         entry = "ctx->Map.entry"
@@ -573,6 +610,12 @@ class _ActionRequirementTestItem(_TestItem):
                 state = f"{entry}.Pre_{name}_NA ? {enum_na} : {state}"
             prepare = f"{self._pre_co_idx_to_enum[index][0]}_Prepare"
             content.call_function(None, prepare, ["ctx", state])
+            if self._pre_co_skip.get(index, False):
+                with content.condition("ctx->Map.skip"):
+                    content.call_function(None, f"{self.ident}_Skip",
+                                          ["ctx", str(index)])
+                    content.append("return;")
+                content.add_blank_line()
         self._add_call(content, "test-action", "Action")
         for index, enum in enumerate(self._post_co_idx_to_enum):
             content.gap = False
@@ -616,6 +659,8 @@ class _ActionRequirementTestItem(_TestItem):
                 "ctx->Map.index = index + 1;", f"return {self.ident}_Entries[",
                 f"  {self.ident}_Map[ index ]", "];"
             ])
+        if self._pre_co_skip:
+            self._add_skip(content)
         with content.function("static void", f"{self.ident}_TestVariant",
                               [f"{self.context} *ctx"]):
             self._add_test_variant(content, transition_map)
@@ -653,9 +698,9 @@ class _ActionRequirementTestItem(_TestItem):
             self._add_for_loops(content, transition_map, 0)
             content.add(epilogue)
 
-    def _add_handler(self, content: CContent, conditions: List[Any],
+    def _add_handler(self, content: CContent, conditions: str,
                      co_idx_to_enum: _IdxToX, action: str) -> None:
-        for co_idx, condition in enumerate(conditions):
+        for co_idx, condition in enumerate(self[conditions]):
             enum = co_idx_to_enum[co_idx]
             handler = f"{enum[0]}_{action}"
             params = [f"{self.context} *ctx", f"{enum[0]} state"]
@@ -665,12 +710,15 @@ class _ActionRequirementTestItem(_TestItem):
                 with content.indent():
                     for state_index, state in enumerate(condition["states"]):
                         content.add(f"case {enum[state_index + 1]}: {{")
+                        prefix = (f"/{conditions}[{co_idx}]"
+                                  f"/states[{state_index}]/test-code")
                         with content.indent():
                             with content.comment_block():
                                 content.wrap(
                                     self.substitute_text(state["text"]))
                             content.append(
-                                self.substitute_code(state["test-code"]))
+                                self.substitute_code(state["test-code"],
+                                                     prefix))
                             content.append("break;")
                         content.add("}")
                     content.add(f"case {enum[-1]}:")
@@ -702,9 +750,9 @@ class _ActionRequirementTestItem(_TestItem):
         instance = self.add_context(content)
         self._add_pre_condition_descriptions(content)
         content.add(self.substitute_code(self["test-support"]))
-        self._add_handler(content, self["pre-conditions"],
-                          self._pre_co_idx_to_enum, "Prepare")
-        self._add_handler(content, self["post-conditions"],
+        self._add_handler(content, "pre-conditions", self._pre_co_idx_to_enum,
+                          "Prepare")
+        self._add_handler(content, "post-conditions",
                           self._post_co_idx_to_enum, "Check")
         optional_code = "ctx->Map.in_action_loop = false;"
         setup = self.add_support_method(content,
@@ -733,6 +781,11 @@ class _ActionRequirementTestItem(_TestItem):
         self._add_test_case(content, transition_map, header)
         content.add("/** @} */")
 
+    def skip_pre_condition(self, ctx: ItemGetValueContext) -> Any:
+        index = int(ctx.path.split("]")[0].split("[")[1])
+        self._pre_co_skip[index] = True
+        return "ctx->Map.skip = true;"
+
 
 class _RuntimeMeasurementRequestItem(_TestItem):
     """ A runtime measurement request item. """



More information about the vc mailing list