From 3e9b695d3b638fa0072dbed9ddd2d8d3482161e4 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 14 Aug 2020 08:13:22 +0200 Subject: 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(-) create mode 100644 rtemsspec/tests/spec-validation/tc5.yml create mode 100644 rtemsspec/tests/spec-validation/tc6.yml diff --git a/rtemsspec/tests/spec-validation/tc5.yml b/rtemsspec/tests/spec-validation/tc5.yml new file mode 100644 index 00000000..efd6fa54 --- /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 00000000..02c100b8 --- /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 ca7668a3..708a0ea2 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 + +#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 3e557020..3a344745 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) -- cgit v1.2.3