Added a test suite generator tool written in FTL.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/stable_17.6.x@10616 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
Giovanni Di Sirio 2017-09-17 15:42:23 +00:00
parent 218295a815
commit ebd93bd85e
6 changed files with 486 additions and 0 deletions

View File

@ -91,6 +91,7 @@
*** 17.6.1 ***
- NEW: Improved RT and NIL test suite to report version numbers and
configuration settings.
- NEW: Added a test suite generator tool written in FTL.
- NEW: Added to the HAL USB driver a new function usbWakeupHost() for
standby exit.
- HAL: Fixed USB GET_DESCRIPTOR not handled for Interface Recipients (bug #885).

126
tools/ftl/libs/libutils.ftl Executable file
View File

@ -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]

View File

@ -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]
*
* <h2>Test Sequences</h2>
[#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__) */

View File

@ -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 */

View File

@ -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
*
* <h2>Description</h2>
[@utils.FormatStringAsText " * "
" * "
utils.WithDot(sequence.description.value[0]?string)
72 /]
*
[#if sequence.condition.value[0]?trim?length > 0]
* <h2>Conditions</h2>
* This sequence is only executed if the following preprocessor condition
* evaluates to true:
* - ${sequence.condition.value[0]}
* .
*
[/#if]
* <h2>Test Cases</h2>
[#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])}
*
* <h2>Description</h2>
[@utils.FormatStringAsText " * "
" * "
utils.WithDot(case.description.value[0]?string)
72 /]
*
[#if case.condition.value[0]?trim?length > 0]
* <h2>Conditions</h2>
* This test is only executed if the following preprocessor condition
* evaluates to true:
* - ${case.condition.value[0]}
* .
*
[/#if]
* <h2>Test Steps</h2>
[#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]
* <h2>Covered Requirements</h2>
[#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]

View File

@ -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]