diff --git a/os/test/ftl/libs/libutils.ftl b/os/test/ftl/libs/libutils.ftl new file mode 100755 index 000000000..513d1e389 --- /dev/null +++ b/os/test/ftl/libs/libutils.ftl @@ -0,0 +1,131 @@ +[#ftl] +[#-- + ChibiOS - Copyright (C) 2006,2007,2008,2009,2010, + 2011,2012,2013,2014,2015,2016,2017,2018, + 2019,2020 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + --] + +[#-- + -- Returns the trimmed text "s" making sure it is terminated by a dot. + -- The empty string is always returned as an empty string, the dot is not + -- added. + --] +[#function WithDot s] + [#local s = s?trim /] + [#if s == ""] + [#return s /] + [/#if] + [#if s?ends_with(".")] + [#return s /] + [/#if] + [#return s + "." /] +[/#function] + +[#-- + -- Returns the trimmed text "s" making sure it is not terminated by a dot. + --] +[#function WithoutDot s] + [#local s = s?trim /] + [#if s?ends_with(".")] + [#return s?substring(0, s?length - 1) /] + [/#if] + [#return s /] +[/#function] + +[#-- + -- Returns the trimmed text "s" making sure it is terminated by a dot if the + -- text is composed of multiple phrases, if the text is composed of a single + -- phrase then makes sure it is *not* terminated by a dot. + -- A phrase is recognized by the pattern ". " into the text. + -- The empty string is always returned as an empty string, the dot is never + -- added. + --] +[#function IntelligentDot s] + [#local s = s?trim /] + [#if s?contains(". ")] + [#return WithDot(s) /] + [/#if] + [#return WithoutDot(s) /] +[/#function] + +[#-- + -- Formats a text string in a sequence of strings no longer than "len" (first + -- line) or "lenn" (subsequent lines). + -- White spaces are normalized between words, sequences of white spaces become + -- a single space. + --] +[#function StringToText len1 lenn s] + [#local words=s?word_list /] + [#local line="" /] + [#local lines=[] /] + [#list words as word] + [#if lines?size == 0] + [#local len = len1 /] + [#else] + [#local len = lenn /] + [/#if] + [#if (line?length + word?length + 1 > len)] + [#local lines = lines + [line?trim] /] + [#local line = word + " " /] + [#else] + [#local line = line + word + " " /] + [/#if] + [/#list] + [#if line != ""] + [#local lines = lines + [line?trim] /] + [/#if] + [#return lines /] +[/#function] + +[#-- + -- Emits a string "s" as a formatted text, the first line is prefixed by the + -- "p1" parameter, subsequent lines are prefixed by the "pn" paramenter. + -- Emitted lines are no longer than the "len" parameter. + -- White spaces are normalized between words. + --] +[#macro FormatStringAsText p1 pn s len] + [#local lines = StringToText(len - p1?length, len - pn?length, s) /] + [#list lines as line] + [#if line_index == 0] +${p1}${line} + [#else] +${pn}${line} + [/#if] + [/#list] +[/#macro] + +[#-- + -- Emits a C function body code reformatting the indentation using the + -- specified tab size and line prefix. + --] +[#macro EmitIndentedCCode start tab ccode] + [#assign lines = ccode?string?split("^", "rm") /] + [#list lines as line] + [#if (line_index > 0) || (line?trim?length > 0)] + [#if line?trim?length > 0] + [#if line[0] == "#"] +${line?chop_linebreak} + [#else] +${start + line?chop_linebreak} + [/#if] + [#else] + + [/#if] + [/#if] + [/#list] +[/#macro] diff --git a/os/test/ftl/processors/unittest/test/test_root.c.ftl b/os/test/ftl/processors/unittest/test/test_root.c.ftl new file mode 100755 index 000000000..825a72f71 --- /dev/null +++ b/os/test/ftl/processors/unittest/test/test_root.c.ftl @@ -0,0 +1,103 @@ +[#ftl] +[#-- + ChibiOS/RT - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --] +[#import "/@ftllibs/libutils.ftl" as utils /] +[#if xml.instance[0]??] + [#assign instance = xml.instance /] +[#else] + [#list xml.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] + [/#list] +[/#if] +[#assign conf = {"instance":instance} /] +[#assign prefix_lower = conf.instance.global_data_and_code.code_prefix.value[0]?trim?lower_case /] +[#assign prefix_upper = conf.instance.global_data_and_code.code_prefix.value[0]?trim?upper_case /] +[@pp.dropOutputFile /] +[@pp.changeOutputFile name=prefix_lower+"test_root.c" /] +[@utils.EmitIndentedCCode "" 2 conf.instance.description.copyright.value[0] /] + +/** + * @mainpage Test Suite Specification +[#if conf.instance.description.introduction.value[0]?trim != ""] +[@utils.FormatStringAsText " * " + " * " + utils.WithDot(conf.instance.description.introduction.value[0]?trim?cap_first) + 72 /] +[#else] + * No introduction. +[/#if] + * + *

Test Sequences

+[#if conf.instance.sequences.sequence?size > 0] + [#list conf.instance.sequences.sequence as sequence] + * - @subpage ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")} + [/#list] + * . +[#else] + * No test sequences defined in the test suite. +[/#if] + */ + +/** + * @file ${prefix_lower}test_root.c + * @brief Test Suite root structures code. + */ + +#include "hal.h" +#include "${prefix_lower}test_root.h" + +#if !defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/** + * @brief Array of test sequences. + */ +const testsequence_t * const ${prefix_lower}test_suite_array[] = { +[#list conf.instance.sequences.sequence as sequence] + [#if sequence.condition.value[0]?trim?length > 0] +#if (${sequence.condition.value[0]}) || defined(__DOXYGEN__) + [/#if] + &${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}, + [#if sequence.condition.value[0]?trim?length > 0] +#endif + [/#if] +[/#list] + NULL +}; + +/** + * @brief Test suite root structure. + */ +const testsuite_t ${prefix_lower}test_suite = { + "${utils.WithoutDot(conf.instance.description.brief.value[0]?trim)}", + ${prefix_lower}test_suite_array +}; + +/*===========================================================================*/ +/* Shared code. */ +/*===========================================================================*/ + +[#if conf.instance.global_data_and_code.global_code.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode "" 2 conf.instance.global_data_and_code.global_code.value[0] /] + +[/#if] +#endif /* !defined(__DOXYGEN__) */ diff --git a/os/test/ftl/processors/unittest/test/test_root.h.ftl b/os/test/ftl/processors/unittest/test/test_root.h.ftl new file mode 100755 index 000000000..45e391c40 --- /dev/null +++ b/os/test/ftl/processors/unittest/test/test_root.h.ftl @@ -0,0 +1,74 @@ +[#ftl] +[#-- + ChibiOS/RT - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --] +[#import "/@ftllibs/libutils.ftl" as utils /] +[#if xml.instance[0]??] + [#assign instance = xml.instance /] +[#else] + [#list xml.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] + [/#list] +[/#if] +[#assign conf = {"instance":instance} /] +[#assign prefix_lower = conf.instance.global_data_and_code.code_prefix.value[0]?trim?lower_case /] +[#assign prefix_upper = conf.instance.global_data_and_code.code_prefix.value[0]?trim?upper_case /] +[@pp.dropOutputFile /] +[@pp.changeOutputFile name=prefix_lower+"test_root.h" /] +[@utils.EmitIndentedCCode "" 2 conf.instance.description.copyright.value[0] /] + +/** + * @file ${prefix_lower}test_root.h + * @brief Test Suite root structures header. + */ + +#ifndef ${prefix_upper}TEST_ROOT_H +#define ${prefix_upper}TEST_ROOT_H + +#include "ch_test.h" + +[#list conf.instance.sequences.sequence as sequence] +#include "${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}.h" +[/#list] + +#if !defined(__DOXYGEN__) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +extern const testsuite_t ${prefix_lower}test_suite; + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif + +/*===========================================================================*/ +/* Shared definitions. */ +/*===========================================================================*/ + +[#if conf.instance.global_data_and_code.global_definitions.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode "" 2 conf.instance.global_data_and_code.global_definitions.value[0] /] + +[/#if] +#endif /* !defined(__DOXYGEN__) */ + +#endif /* ${prefix_upper}TEST_ROOT_H */ diff --git a/os/test/ftl/processors/unittest/test/test_sequence.c.ftl b/os/test/ftl/processors/unittest/test/test_sequence.c.ftl new file mode 100755 index 000000000..cd0940e1e --- /dev/null +++ b/os/test/ftl/processors/unittest/test/test_sequence.c.ftl @@ -0,0 +1,240 @@ +[#ftl] +[#-- + ChibiOS/RT - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --] +[#import "/@ftllibs/libutils.ftl" as utils /] +[@pp.dropOutputFile /] +[#if xml.instance[0]??] + [#assign instance = xml.instance /] +[#else] + [#list xml.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] + [/#list] +[/#if] +[#assign conf = {"instance":instance} /] +[#assign prefix_lower = conf.instance.global_data_and_code.code_prefix.value[0]?trim?lower_case /] +[#assign prefix_upper = conf.instance.global_data_and_code.code_prefix.value[0]?trim?upper_case /] +[#list conf.instance.sequences.sequence as sequence] + [@pp.changeOutputFile name=prefix_lower+"test_sequence_" + (sequence_index + 1)?string("000") + ".c" /] +[@utils.EmitIndentedCCode "" 2 conf.instance.description.copyright.value[0] /] + +#include "hal.h" +#include "${prefix_lower}test_root.h" + +/** + * @file ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}.c + * @brief Test Sequence ${(sequence_index + 1)?string("000")} code. + * + * @page ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")} [${(sequence_index + 1)?string}] ${utils.WithoutDot(sequence.brief.value[0]?string)} + * + * File: @ref ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}.c + * + *

Description

+[@utils.FormatStringAsText " * " + " * " + utils.WithDot(sequence.description.value[0]?string) + 72 /] + * + [#if sequence.condition.value[0]?trim?length > 0] + *

Conditions

+ * This sequence is only executed if the following preprocessor condition + * evaluates to true: + * - ${sequence.condition.value[0]} + * . + * + [/#if] + *

Test Cases

+ [#if sequence.cases.case?size > 0] + [#list sequence.cases.case as case] + * - @subpage ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")} + [/#list] + * . + [#else] + * No test cases defined in the test sequence. + [/#if] + */ + + [#if sequence.condition.value[0]?trim?length > 0] +#if (${sequence.condition.value[0]}) || defined(__DOXYGEN__) + + [/#if] +/**************************************************************************** + * Shared code. + ****************************************************************************/ + + [#if sequence.shared_code.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode "" 2 sequence.shared_code.value[0] /] + [/#if] + +/**************************************************************************** + * Test cases. + ****************************************************************************/ + + [#list sequence.cases.case as case] + [#-- Building the sequence of the requirements covered by + this test case.--] + [#assign reqseq = [] /] + [#list case.steps.step as step] + [#assign reqseq = reqseq + step.tags.value[0]?string?trim?word_list /] + [/#list] + [#assign reqseq = reqseq?sort /] + [#-- Checking if a condition should be generated.--] + [#if case.condition.value[0]?trim?length > 0] +#if (${case.condition.value[0]?trim}) || defined(__DOXYGEN__) + [/#if] + [#-- Header generation.--] +/** + * @page ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")} [${(sequence_index + 1)?string}.${(case_index + 1)?string}] ${utils.WithoutDot(case.brief.value[0])} + * + *

Description

+[@utils.FormatStringAsText " * " + " * " + utils.WithDot(case.description.value[0]?string) + 72 /] + * + [#if case.condition.value[0]?trim?length > 0] + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - ${case.condition.value[0]} + * . + * + [/#if] + *

Test Steps

+ [#list case.steps.step as step] +[@utils.FormatStringAsText " * - " + " * " + utils.WithDot("[" + (sequence_index + 1)?string + "." + (case_index + 1)?string + "." + (step_index + 1)?string + "] " + step.description.value[0]?string) + 72 /] + [/#list] + [#if case.steps.step?size > 0] + * . + [/#if] + [#if reqseq?size > 0] + *

Covered Requirements

+ [#assign reqs = "" /] + [#list reqseq as r] + [#assign reqs = reqs + r /] + [#if r_has_next] + [#assign reqs = reqs + ", " /] + [/#if] + [/#list] +[@utils.FormatStringAsText " * " + " * " + utils.WithDot(reqs) + 72 /] + [/#if] + */ + + [#if case.various_code.setup_code.value[0]?trim?length > 0] +static void ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_setup(void) { +[@utils.EmitIndentedCCode " " 2 case.various_code.setup_code.value[0] /] +} + + [/#if] + [#if case.various_code.teardown_code.value[0]?trim?length > 0] +static void ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_teardown(void) { +[@utils.EmitIndentedCCode " " 2 case.various_code.teardown_code.value[0] /] +} + + [/#if] +static void ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_execute(void) { + [#if case.various_code.local_variables.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode " " 2 case.various_code.local_variables.value[0] /] + [/#if] + [#list case.steps.step as step] + +[@utils.FormatStringAsText " /* " + " " + utils.WithDot("[" + (sequence_index + 1)?string + "." + (case_index + 1)?string + "." + (step_index + 1)?string + "] " + step.description.value[0]?string) + "*/" + 72 /] + test_set_step(${(step_index + 1)?string}); + { + [#if step.tags.value[0]?string?trim != ""] + [#assign reqseq = step.tags.value[0]?string?trim?word_list?sort /] + [#assign reqs = "" /] + [#list reqseq as r] + [#assign reqs = reqs + r /] + [#if r_has_next] + [#assign reqs = reqs + ", " /] + [/#if] + [/#list] +[@utils.FormatStringAsText " /* @covers " + " " + utils.WithDot(reqs) + "*/" + 72 /] + [/#if] + [#if step.code.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode " " 2 step.code.value[0] /] + [/#if] + } + test_end_step(${(step_index + 1)?string}); + [/#list] +} + +static const testcase_t ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")} = { + "${utils.WithoutDot(case.brief.value[0]?string)}", + [#if case.various_code.setup_code.value[0]?trim?length > 0] + ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_setup, + [#else] + NULL, + [/#if] + [#if case.various_code.teardown_code.value[0]?trim?length > 0] + ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_teardown, + [#else] + NULL, + [/#if] + ${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_execute +}; + [#if case.condition.value[0]?trim?length > 0] +#endif /* ${case.condition.value[0]?trim} */ + [/#if] + + [/#list] +/**************************************************************************** + * Exported data. + ****************************************************************************/ + +/** + * @brief Array of test cases. + */ +const testcase_t * const ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}_array[] = { + [#list sequence.cases.case as case] + [#if case.condition.value[0]?trim?length > 0] +#if (${case.condition.value[0]?trim}) || defined(__DOXYGEN__) + [/#if] + &${prefix_lower}test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}, + [#if case.condition.value[0]?trim?length > 0] +#endif + [/#if] + [/#list] + NULL +}; + +/** + * @brief ${utils.WithDot(sequence.brief.value[0]?string)} + */ +const testsequence_t ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")} = { + "${utils.WithoutDot(sequence.brief.value[0]?string)}", + ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}_array +}; + [#if sequence.condition.value[0]?trim?length > 0] + +#endif /* ${sequence.condition.value[0]} */ + [/#if] +[/#list] diff --git a/os/test/ftl/processors/unittest/test/test_sequence.h.ftl b/os/test/ftl/processors/unittest/test/test_sequence.h.ftl new file mode 100755 index 000000000..c8d85a04b --- /dev/null +++ b/os/test/ftl/processors/unittest/test/test_sequence.h.ftl @@ -0,0 +1,47 @@ +[#ftl] +[#-- + ChibiOS/RT - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --] +[#import "/@ftllibs/libutils.ftl" as utils /] +[@pp.dropOutputFile /] +[#if xml.instance[0]??] + [#assign instance = xml.instance /] +[#else] + [#list xml.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] + [/#list] +[/#if] +[#assign conf = {"instance":instance} /] +[#assign prefix_lower = conf.instance.global_data_and_code.code_prefix.value[0]?trim?lower_case /] +[#assign prefix_upper =conf. instance.global_data_and_code.code_prefix.value[0]?trim?upper_case /] +[#list conf.instance.sequences.sequence as sequence] + [@pp.changeOutputFile name=prefix_lower+"test_sequence_" + (sequence_index + 1)?string("000") + ".h" /] +[@utils.EmitIndentedCCode "" 2 conf.instance.description.copyright.value[0] /] + +/** + * @file ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}.h + * @brief Test Sequence ${(sequence_index + 1)?string("000")} header. + */ + +#ifndef ${prefix_upper}TEST_SEQUENCE_${(sequence_index + 1)?string("000")}_H +#define ${prefix_upper}TEST_SEQUENCE_${(sequence_index + 1)?string("000")}_H + +extern const testsequence_t ${prefix_lower}test_sequence_${(sequence_index + 1)?string("000")}; + +#endif /* ${prefix_upper}TEST_SEQUENCE_${(sequence_index + 1)?string("000")}_H */ +[/#list] diff --git a/os/test/ftl/schema/unittest.xsd b/os/test/ftl/schema/unittest.xsd index 05738e25d..4a7641461 100644 --- a/os/test/ftl/schema/unittest.xsd +++ b/os/test/ftl/schema/unittest.xsd @@ -59,7 +59,7 @@ - + + diff --git a/os/test/ftl/xml/configuration.xml b/os/test/ftl/xml/configuration.xml new file mode 100644 index 000000000..abccc72ae --- /dev/null +++ b/os/test/ftl/xml/configuration.xml @@ -0,0 +1,124 @@ + + + + + Example Test Suite. + + + . +*/]]> + + + Test suite for EXAMPLE. The purpose of this suite is to + perform unit tests on the EXAMPLE modules. + + + + + example_ + + + + + + + + + + + + Internal Tests + + + This is a sequence. + +
+ A sequence is a collection of test cases executed in sequence +sharing static code and data. +
+ + + + + + + + + + This is a test case. + +
+ A test case is a small program testing a specific feature +of the system under test. Test cases should not depend on things done in +previous test cases because those could have failed leaving an unexpected +state. +
+ + + + + + + + + + + + + + + + +
+ This is a test case step. A step is an elementary +piece of code part of a test case. +
+ + + + + + +
+
+
+
+
+
+