From fb355909fa1e96f24087c1380daef19cc0eb0532 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Sun, 17 Sep 2017 13:28:22 +0000 Subject: Added an FTL unit-test generator tool independent from SPC5Studio, it uses the same configuration.xml file so SPC5Studio can still be used but it is not in the critical path. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@10612 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- tools/ftl/libs/libutils.ftl | 126 +++++++++++++ tools/ftl/processors/unittest/test/test_root.c.ftl | 74 ++++++++ tools/ftl/processors/unittest/test/test_root.h.ftl | 50 +++++ .../processors/unittest/test/test_sequence.c.ftl | 210 +++++++++++++++++++++ .../processors/unittest/test/test_sequence.h.ftl | 25 +++ 5 files changed, 485 insertions(+) create mode 100755 tools/ftl/libs/libutils.ftl create mode 100755 tools/ftl/processors/unittest/test/test_root.c.ftl create mode 100755 tools/ftl/processors/unittest/test/test_root.h.ftl create mode 100755 tools/ftl/processors/unittest/test/test_sequence.c.ftl create mode 100755 tools/ftl/processors/unittest/test/test_sequence.h.ftl (limited to 'tools') diff --git a/tools/ftl/libs/libutils.ftl b/tools/ftl/libs/libutils.ftl new file mode 100755 index 000000000..3fc825a00 --- /dev/null +++ b/tools/ftl/libs/libutils.ftl @@ -0,0 +1,126 @@ +[#ftl] +[#-- + ChibiOS/RT - Copyright (C) 2006-2017 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. + --] + +[#-- + -- 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/tools/ftl/processors/unittest/test/test_root.c.ftl b/tools/ftl/processors/unittest/test/test_root.c.ftl new file mode 100755 index 000000000..6751ef56d --- /dev/null +++ b/tools/ftl/processors/unittest/test/test_root.c.ftl @@ -0,0 +1,74 @@ +[#ftl] +[#import "/@ftllibs/libutils.ftl" as utils /] +[@pp.dropOutputFile /] +[@pp.changeOutputFile name="test_root.c" /] +[#list conf.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] +[/#list] +[@utils.EmitIndentedCCode "" 2 instance.description.copyright.value[0] /] + +/** + * @mainpage Test Suite Specification +[#if instance.description.introduction.value[0]?trim != ""] +[@utils.FormatStringAsText " * " + " * " + utils.WithDot(instance.description.introduction.value[0]?trim?cap_first) + 72 /] +[#else] + * No introduction. +[/#if] + * + *

Test Sequences

+[#if instance.sequences.sequence?size > 0] + [#list instance.sequences.sequence as sequence] + * - @subpage test_sequence_${(sequence_index + 1)?string("000")} + [/#list] + * . +[#else] + * No test sequences defined in the test suite. +[/#if] + */ + +/** + * @file test_root.c + * @brief Test Suite root structures code. + */ + +#include "hal.h" +#include "ch_test.h" +#include "test_root.h" + +#if !defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Module exported variables. */ +/*===========================================================================*/ + +/** + * @brief Array of all the test sequences. + */ +const testcase_t * const *test_suite[] = { +[#list instance.sequences.sequence as sequence] + [#if sequence.condition.value[0]?trim?length > 0] +#if (${sequence.condition.value[0]}) || defined(__DOXYGEN__) + [/#if] + test_sequence_${(sequence_index + 1)?string("000")}, + [#if sequence.condition.value[0]?trim?length > 0] +#endif + [/#if] +[/#list] + NULL +}; + +/*===========================================================================*/ +/* Shared code. */ +/*===========================================================================*/ + +[#if instance.global_data_and_code.global_code.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode "" 2 instance.global_data_and_code.global_code.value[0] /] + +[/#if] +#endif /* !defined(__DOXYGEN__) */ diff --git a/tools/ftl/processors/unittest/test/test_root.h.ftl b/tools/ftl/processors/unittest/test/test_root.h.ftl new file mode 100755 index 000000000..930a676cc --- /dev/null +++ b/tools/ftl/processors/unittest/test/test_root.h.ftl @@ -0,0 +1,50 @@ +[#ftl] +[#import "/@ftllibs/libutils.ftl" as utils /] +[@pp.dropOutputFile /] +[@pp.changeOutputFile name="test_root.h" /] +[#list conf.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] +[/#list] +[@utils.EmitIndentedCCode "" 2 instance.description.copyright.value[0] /] + +/** + * @file test_root.h + * @brief Test Suite root structures header. + */ + +#ifndef TEST_ROOT_H +#define TEST_ROOT_H + +[#list instance.sequences.sequence as sequence] +#include "test_sequence_${(sequence_index + 1)?string("000")}.h" +[/#list] + +#if !defined(__DOXYGEN__) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +extern const testcase_t * const *test_suite[]; + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif + +/*===========================================================================*/ +/* Shared definitions. */ +/*===========================================================================*/ + +[#if instance.global_data_and_code.global_definitions.value[0]?trim?length > 0] +[@utils.EmitIndentedCCode "" 2 instance.global_data_and_code.global_definitions.value[0] /] + +[/#if] +#endif /* !defined(__DOXYGEN__) */ + +#endif /* TEST_ROOT_H */ diff --git a/tools/ftl/processors/unittest/test/test_sequence.c.ftl b/tools/ftl/processors/unittest/test/test_sequence.c.ftl new file mode 100755 index 000000000..7be0c58f2 --- /dev/null +++ b/tools/ftl/processors/unittest/test/test_sequence.c.ftl @@ -0,0 +1,210 @@ +[#ftl] +[#import "/@ftllibs/libutils.ftl" as utils /] +[@pp.dropOutputFile /] +[#list conf.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] +[/#list] +[#list instance.sequences.sequence as sequence] + [@pp.changeOutputFile name="test_sequence_" + (sequence_index + 1)?string("000") + ".c" /] +[@utils.EmitIndentedCCode "" 2 instance.description.copyright.value[0] /] + +#include "hal.h" +#include "ch_test.h" +#include "test_root.h" + +/** + * @file test_sequence_${(sequence_index + 1)?string("000")}.c + * @brief Test Sequence ${(sequence_index + 1)?string("000")} code. + * + * @page test_sequence_${(sequence_index + 1)?string("000")} [${(sequence_index + 1)?string}] ${utils.WithoutDot(sequence.brief.value[0]?string)} + * + * File: @ref 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 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 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 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 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 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] + } + [/#list] +} + +static const testcase_t 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] + 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] + test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}_teardown, + [#else] + NULL, + [/#if] + 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 ${utils.WithDot(sequence.brief.value[0]?string)} + */ +const testcase_t * const test_sequence_${(sequence_index + 1)?string("000")}[] = { + [#list sequence.cases.case as case] + [#if case.condition.value[0]?trim?length > 0] +#if (${case.condition.value[0]?trim}) || defined(__DOXYGEN__) + [/#if] + &test_${(sequence_index + 1)?string("000")}_${(case_index + 1)?string("000")}, + [#if case.condition.value[0]?trim?length > 0] +#endif + [/#if] + [/#list] + NULL +}; + [#if sequence.condition.value[0]?trim?length > 0] + +#endif /* ${sequence.condition.value[0]} */ + [/#if] +[/#list] diff --git a/tools/ftl/processors/unittest/test/test_sequence.h.ftl b/tools/ftl/processors/unittest/test/test_sequence.h.ftl new file mode 100755 index 000000000..eb3911888 --- /dev/null +++ b/tools/ftl/processors/unittest/test/test_sequence.h.ftl @@ -0,0 +1,25 @@ +[#ftl] +[#import "/@ftllibs/libutils.ftl" as utils /] +[@pp.dropOutputFile /] +[#list conf.*.application.instances.instance as inst] + [#if inst.@id?string == "org.chibios.spc5.components.portable.chibios_unitary_tests_engine"] + [#assign instance = inst /] + [#break] + [/#if] +[/#list] +[#list instance.sequences.sequence as sequence] + [@pp.changeOutputFile name="test_sequence_" + (sequence_index + 1)?string("000") + ".h" /] +[@utils.EmitIndentedCCode "" 2 instance.description.copyright.value[0] /] + +/** + * @file test_sequence_${(sequence_index + 1)?string("000")}.h + * @brief Test Sequence ${(sequence_index + 1)?string("000")} header. + */ + +#ifndef TEST_SEQUENCE_${(sequence_index + 1)?string("000")}_H +#define TEST_SEQUENCE_${(sequence_index + 1)?string("000")}_H + +extern const testcase_t * const test_sequence_${(sequence_index + 1)?string("000")}[]; + +#endif /* TEST_SEQUENCE_${(sequence_index + 1)?string("000")}_H */ +[/#list] -- cgit v1.2.3