5. Templates, Template Groups and File Generation with T2C

5.1. Templates for output files
5.1.1. Basic structure
5.1.2. Multi-valued parameters and templates
5.1.3. Joining values
5.1.4. Conditionals
5.2. Template groups and subtemplates
5.2.1. Files and directories
5.2.2. References in the templates
5.2.3. Naming rules
5.2.4. Evaluation of a template group
5.3. Parameters and configuration files
5.4. Global, suite-specific and test-specific parameters
5.5. Default, suite-specific and test-specific templates
5.6. Directory structure of the templates for a T2C target
5.7. Generating output files for the test suites
5.7.1. Generating test source files
5.7.2. Special parameters defined by the file generator

5.1. Templates for output files

5.1.1. Basic structure

The templates used to generate output files are little more than just documents with holes in the most simple case. They contain parts (string chunks) that will be placed verbatim to the output file and special named placeholders that may refer to other templates or parameters.

Note

The file generator uses special template engine (MiST Engine - Minimal String Template Engine) to process the templates and create the output files. The engine is statically linked into the generator, so there is no need to build and install it separately, if you have installed T2C. MiST Engine is also available as a separate package from the Template2Code project page at SourceForge.net.

Here is an example of a simple template:

Good morning, <$Title$> <$Name$>!
It is good to see you.

Let GoodMorning be the name of the template. This template has two placeholders: <$Title$> and <$Name$>. If parameter Name has value Freeman and Title has value Dr., the above template will evaluate to the following:

Good morning, Dr. Freeman!
It is good to see you.

As you can see from the example, newline characters in string chunks are also placed verbatim to the resulting text.

<$ and $> are the markers to denote the beginning and the end of a placeholder. If necessary, user-defined values can be used as the markers instead of the default ones. The new values can be specified in the configuration file of the appropriate template group and will affect the whole group (template groups are described in detail in Section 5.2, “Template groups and subtemplates”).

Whitespace characters (spaces, tabs and newlines) are ignored after the opening marker and before the closing marker of a placeholder. The following template is equivalent to GoodMorning template above:

Good morning, <$   Title   $> <$
Name
  $>!
It is good to see you.

A template may refer to other templates (from the group it belongs to) in the same way it refers to the parameters. That is, assume Name is not a parameter but there is rather a template with name Name and the following contents:

<$first_name$> <$last_name$>

If Title is Dr. as above, first_name is Gordon and last_name is Freeman, GoodMorning template will evaluate to the following:

Good morning, Dr. Gordon Freeman!
It is good to see you.

The placeholders referring to subtemplates and parameters have the same syntax (a parameter can actually be viewed as a special case of a subtemplate). How a placeholder is interpreted depends only on the presense of a template with the appropriate name in the corresponding template group. For example, <$Title$> is a reference to the template with name Title if there is such template in the group, but a reference to a parameter otherwise.

The value of each parameter is a string. There are no types of values here, like integer, float, etc. The file generator operates on strings only.

If a parameter has no value, its value is assumed to be an empty string.

5.1.2. Multi-valued parameters and templates

A parameter may have more than one value (multi-valued parameter). Consider the following GoodMorning template similar to the one from the examples above:

Good morning, <$Title$> <$Name$>!

Now let Name and Title parameters have 3 values each, Freeman, Vance, Grigory and Mr., Dr., Mr., respectively. The order of values of each particular parameter is important. Note that it is possible for a parameter to have two or more identical values, like Mr. in case of Title parameter.

GoodMorning template will have 3 values as a result:

Good morning, Mr. Freeman!
Good morning, Dr. Vance!
Good morning, Mr. Grigory!

As you can see, the first values of Name and Title are used to create the first resulting value, the second values of these parameters are used for the second one, etc.

Important

Note that it is exactly 3 values that the template will have as a result rather than a single string with these values concatenated. To concatenate these values into a single string, you need to use join() construct described below.

If the main (top-level) template in a group is multi-valued as a result, T2C file generator will report this as an error. Other templates can be multi-valued - as long as the top-level template has only one value.

The number of resulting values is the maximum number of values associated with the placeholders in the template. If there are less values associated with a particular placeholder than the number of the resulting values, the last value associated with this placeholder is used when creating the remaining resulting strings.

For example, assume that Name parameter has the 3 values described above but Title has only two: Doctor and Mr., in this order. GoodMorning template will have the following 3 values in this case:

Good morning, Doctor Freeman!
Good morning, Mr. Vance!
Good morning, Mr. Grigory!

Even if Title parameter had only a single value, Mr., the template would have 3 values as a result:

Good morning, Mr. Freeman!
Good morning, Mr. Vance!
Good morning, Mr. Grigory!

That is, the only value of Title parameter is used in conjunction with each of the three values of Name parameter to produce three resulting strings.

5.1.3. Joining values

Now assume we have two templates: GoodMorning and Person.

GoodMorning (note that it refers to Person subtemplate, see Section 5.2.2, “References in the templates” for details):

Good morning, <$Person$>!

Person:

<$Title$> <$Name$>

If Title or Name parameter is multi-valued, Person and GoodMorning templates will also have more than one value.

Sometimes it is necessary to join the values of a multi-valued template or parameter. join() construct in the placeholder can be used to do this. Suppose we rewrite GoodMorning template as follows:

Good morning, <$Person : join(, )$>!

Let Name and Title parameters have 3 values each, Freeman, Vance, Grigory and Dr., Mr., F. respectively. GoodMorning template will have a single value in this case:

Good morning, Dr. Freeman, Mr. Vance, F. Grigory!

The values of Person subtemplate are joined here into a single string with inbetween as a separator. This separator sequence was specified in the parentheses in join(). So, a construct like this allows to create various list-like structures in the resulting files.

The syntax of a placeholder with join() construct is as follows:

<begin_marker> <name> : join (<separator>) <end_marker>

Space, tab and newline characters are optional before and after the colon, before the opening parenthesis and after the closing parenthesis. These whitespace characters are ignored.

The whitespace characters are not ignored between the parentheses. That is, the sequence between the first '(' character after join and the last ')' character of the placeholder is taken as a whole as the separator string. This also allows to use '(' and ')' characters in the separator string without escaping.

If there is nothing between the parentheses in a join() construct, an empty string will be used as a separator as you could expect.

Note

If the subtemplate or parameter referred to in a placeholder with join() has only one value, the join() construct is ignored. As a result, the placeholder will have the same value as if there were no join() there.

The following character sequences have special meaning when used in the separator string in join(). They are interpreted in the similar way as in the strings in C programming language.

  • \\ - '\' character.

  • \n - LF character.

  • \r - CR character.

  • \t - TAB character.

These escaped sequences are supported to provide a convenient way to specify tabs and newline characters in the separator strings. Nevertheless, a newline can still be specified in the separator literally, for example:

Good morning, <$Person : join(,
)$>!

The following variant is often more convenient to use here instead:

Good morning, <$Person : join(,\n)$>!

5.1.4. Conditionals

If is sometimes needed to include one or another part in a template depending on whether some particlular parameter is defined or not. Conditional constructs in the templates allow to do this. There are two forms of conditional constructs (with and without else branch):

<$if cond$> then-branch [<$else$> else-branch] <$endif$>

and

<$if cond$> then-branch <$endif$>

<$if … $>, <$else$> and <$endif$> are placeholders of a special sort.

cond is a conditional expression which will be checked to select appropriate branch. It can be a name of a parameter or a subtemplate or it can be a concat-expression (see below).

then-branch and else-branch can be arbitrary sequences of string chunks and placeholders. These branches may also contain conditional constructs (nested conditionals).

Note

Note that join() constructs are not allowed in the conditional expression (cond). If you need to use a joined value as the condition, you should prepare a subtemplate, join the appropriate values there and use the name of the subtemplate as the conditional expression instead.

If you need the branches to be chosen depending on whether a parameter or a template has at least one non-empty value, you can also use a concat-expression as a conditional expression.

The most simple case is when the template or the parameter referred to by cond has the only value (if it is not defined at all, it is considered the same as if it was defined and had only one value, an empty string).

Consider the following extract from a template of a makefile for the group of tests:

TEST_EXT = <$if TEST_SOURCE_EXT$>.<$TEST_SOURCE_EXT$><$else$>.cpp<$endif$> 

In this example, TEST_SOURCE_EXT is a parameter than may be specified in the configuration file for the test suite or for the group of tests. It defines the extension of the file names used for the sources of the tests. If TEST_SOURCE_EXT is defined and is not empty, its value will be assigned to TEST_EXT variable in the makefile with a dot prepended. For example, if TEST_SOURCE_EXT has value dxx, the value of the conditional construct will be .dxx. If TEST_SOURCE_EXT is empty or is not defined, the value of the conditional construct will be .cpp.

If cond is multi-valued, things get more complicated. The template engine processes such conditional constructs as follows.

  1. First, for each value of cond, the template engine determines which branch should be evaluated and evaluates it (if it has not done that yet).

  2. Second, the number of values the conditional construct will have (nvals) is determined. It is the maximum among the numbers of values of cond and of the branches that are actually taken. That is, if cond has such values that one branch is never chosen, this branch is not evaluated and the number of its values is not taken into account as well.

  3. Finally, each of nvals resulting values of the conditional construct is prepared. To obtain the value #i, the value #i of cond and the value #i of the corresponding branch are taken. If the branch does not have value #i, its last value is used instead, same for cond.

To make it clearer, consider the following example. Suppose we have a template:

<$if PC$>[<$PA$>]<$else$>-<$PB$>-<$endif$>

Suppose the parameters PA, PB and PC have the following values:

  • PA: A1, A2, A3 (3 values)

  • PB: B1, B2, B3, B4 (4 values)

  • PC: C, , C, , , C (6 values, 3 of which are empty)

The template will have 6 values as a result: [A1], -B2-, [A3], -B4-, -B4-, [A3].

If PC had only one value, C, the template would have 3 values as a result: [A1], [A2], [A3].

If PC had 6 values, each of which was empty, the template would have 6 values as a result: -B1-, -B2-, -B3-, -B4-, -B4-, -B4-.

It can be helpful to be able to choose a branch depending on whether a parameter or a subtemplate has at least one non-empty value. This is equivalent to checking whether a string created by concatenating (gluing) the values of that parameter or subtemplate is non-empty. This can be achieved by using a subtemplate where join() is applied to these values but it is often more convenient to use a concat-expression instead.

A concat-expression has the following form: concat(name), where name is the name of the corresponding parameter or a subtemplate. The expression can only be used within <$if ...$> placeholder. The conditional construct is interpreted the same way in this case as if the conditional expression was a parameter having only one value which was non-empty if and only if name had at least one non-empty value.

Consider a modified version of the template from the example above:

<$if concat(PC)$>[<$PA$>]<$else$>-<$PB$>-<$endif$>

Suppose PA and PB have the same values as above and PC has 6 values: C, , C, , , C. The template will have 3 values as a result: [A1], [A2], [A3]. PC has some non-empty values, so only then-branch is used.

If PC had only one value, C, the result would be the same.

If PC had 6 values, each of which was empty, the template would have 4 values as a result: -B1-, -B2-, -B3-, -B4-. Note the difference from the example without a concat-expression. This is because concat(PC) is an expression having only one value even though PC has 6 values.

5.2. Template groups and subtemplates

The templates are often organized in groups (template groups). It is a template group rather than a single template that the template engine (and hence the file generator) usually operates on. Each group corresponds to a single file to be generated.

Each group has a main (top-level) template. This template usually defines the structure of the file to be generated from this group. Name of the group is the name of its main template.

5.2.1. Files and directories

A template group is represented in the file system by a directory with the same name as the group. This directory should contain at least two files:

  • The file containing the main template of the group. This file has .tpl extension and the same name as the template (and the group).

  • The configuration file for the group. This file has .cfg extension and the same name as the group. The contents of this file are described below.

Each template from a group is stored in a file with .tpl extension and the same name as the template in the directory of the group.

For example, the templates considered in Section 5.1.3, “Joining values” could be organized as GoodMorning template group as follows in the file system:

GoodMorning     // directory for "GoodMorning" template group
    |
    +- GoodMorning.cfg  // configuration file for the group
    +- GoodMorning.tpl  // main template ("GoodMorning")
    +- Person.tpl       // "Person" template

Note

Only the configuration file of a template group and .tpl files contained in the directory of this group are loaded by the template engine. All other files are ignored.

The configuration file for a template group has the same format as the configuration files defining the values of parameters referred to by the templates. The format is described in Section 5.3, “Parameters and configuration files”.

The following parameters are recognized by the template engine in the configuration file for a template group (all other parameters are ignored):

PH_BEGIN_MARKER

(optional) A character sequence that marks the beginning of each placeholder in the templates from the template group as well as in the template defined in FILE_PATH_TEMPLATE parameter (see below).

Default value: <$

PH_END_MARKER

(optional) A character sequence that marks the end of each placeholder in the templates from the template group as well as in the template defined in FILE_PATH_TEMPLATE parameter (see below).

Default value: $>

FILE_PATH_TEMPLATE

(required) Template of the path to the file to be generated. This template must not refer to subtemplates, but it may refer to the parameters of the tests (default parameters, parameters defined for the test suite or for a particular group of tests).

For example, here is a sample template of the path to the test source file:

FILE_PATH_TEMPLATE = <$SUITE_ROOT_DIR$>/tests/<$T2C_FILE_NAME$>.cpp

When the file generator is about to create the resulting file for a template group, it evaluates the template from FILE_PATH_TEMPLATE parameter to obtain the path to that file. If some directories in the path do not exist at the moment, the file generator will attempt to create them.

Sometimes (for example, for debugging purposes) it can be useful to define this parameter without any placeholders, like this:

FILE_PATH_TEMPLATE = output.txt

This instructs the file generator to place the result to output.txt file in the current working directory.

Note

FILE_PATH_TEMPLATE parameter must be defined in the configuration file for test_case template group too, even though it is not used there. You may specify any value for FILE_PATH_TEMPLATE in this case.

If a custom begin or end marker has been specified in the configuration file for the template group, it will affect not only the templates from this group but also the template in FILE_PATH_TEMPLATE parameter.

Example:

# Custom begin and end markers of placeholders in the templates.
PH_BEGIN_MARKER = $$
PH_END_MARKER = $$

# The template of the path to the file to be generated.
# Note the use of the custom markers ('$$') here.
FILE_PATH_TEMPLATE = some_dir/$$OUT_DIR$$/Good Morning.txt

5.2.2. References in the templates

A template may refer to other templates (subtemplates) from the same group. These templates may in turn refer to subtemplates of their own and so on. A template may refer to any number of subtemplates and any number of templates may refer to the same template. This allows to use the templates as the building blocks to factor out common parts of the generated file, etc. From this point of view, the templates are a bit similar to macros (preprocessor definitions, #define constructs) used in C source code, macro definitions processed by m4 tool, etc.

Currently, there is no way for a template to refer to a subtemplate from another group.

Important

Avoid creating recursion in the template references. Here are two common examples of recursion:

  1. Template A has a placeholder that refers to template A.

  2. Template A has a placeholder that refers to template B, template B (or some other template B refers to, etc.) has a placeholder that refers to template A.

Currently, the template engine does not detect the recursion in the template references. If there is a recursion, the following is guaranteed though:

  1. The template engine will neither hang nor crash, the evaluation of the group will complete (but it is undefined what the result will look like).

  2. When the group is evaluated, each template will be evaluated no more than once.

5.2.3. Naming rules

Each template in a group must have a unique name. It is the name that is used to refer to this template from other templates in the group. The names are case sensitive.

The name of a template is a string of single-byte characters. It must not begin with a dot or a space and must consist only of characters listed below:

  • latin letters: 'A'..'Z', 'a'..'z' (character codes 0x41-0x5A, 0x61-0x7A)

  • digits: '0'..'9' (character codes 0x30-0x39)

  • space: ' ' (character code 0x20)

  • hyphen: '-' (character code 0x2D)

  • dot: '.' (character code 0x2E)

  • underscore: '_' (character code 0x5F)

Note

If a template group is to be loaded from the file system, this may impose additional system-specific restrictions on the names of the templates. This is because the name of a template is the name of the corresponding file (without .tpl extension) in this case. In addition, the name of the main template of the group is the name of the directory where the group is located. For example, this may impose a restriction on the length of the name.

The following keywords have special meaning and must not be used as the names of templates or parameters:

  • if
  • else
  • endif

5.2.4. Evaluation of a template group

When the group is evaluated, the template engine starts with the main template. It looks for the templates it refers to, processes these templates and their subtemplates recursively in a similar way and so on. If the template engine encounters a template that does not refer to any other templates (it may refer to parameters, though), the values of the template are prepared, that is, evaluation of the template is complete. The template engine continues until all the necessary templates are evaluated and finally, the main template is evaluated.

This process can be viewed as traversing a tree (a hierarchy) of the templates. The main template is the root node, the parameters and subtemplates it directly refers to are its child nodes. A child node may have children of its own, etc. The nodes representing the parameters are the leaf nodes of this tree. The template engine goes deep to these leaf nodes first and then back to the root evaluating the nodes in its path.

Note

With this approach, only the templates needed to evaluate the main template are actually evaluated. Nevertheless, all the templates from the group are loaded when the group is loaded by the template engine including the ones that will not actually be used. In particular, syntax checking will be performed for each template in the group.

5.3. Parameters and configuration files

The values of parameters that can be referred to by the templates are often specified in configuration files (value files). Format of such files is described below. Note that the term whitespace characters refers only to spaces and tab characters in this section.

A configuration file has the following format. The file may contain blank lines (1), comments (2), and parameter definitions (3, 4).

(1) Blank lines, that is, the lines containing only whitespace charaters, are ignored.

(2) A comment is a line, the first non-whitespace character in which is '#'. Comments are also ignored.

Parameter definitions can be single-line and multiline.

(3) A definition of a single-line parameter has the following form:

    <Name> = <Value>

Whitespace characters surrounding <Name> and <Value> are ignored.

<Name> must not contain whitespace characters.

<Value> can be empty. It may also contain '=' characters. Example:

    CFLAGS = -std=c99 -O2

Here, the name of the parameter is CFLAGS and its value is -std=c99 -O2.

A backslash character ('\') can be used to split long <Value> strings into several logical lines (logical because all of them will be combined into a single line when the configuration file is loaded). Leading and trailing whitespace characters will be removed from each logical line. Single space characters will be inserted between the lines when combining.

For example, the above definition of CFLAGS can be written equivalently as follows:

    CFLAGS = \
        -std=c99 -O2

or as follows:

    CFLAGS = -std=c99 \ 
        -O2

A backslash must not occur between <Name> and '='. The following example is not allowed:

    CFLAGS \ 
        = -std=c99 -O2

Note that the logical lines that begin with '#' or contain only whitespace characters are considered to be the part of <Value> and hence are not ignored. Consider the following definition:

    CFLAGS = -std=c99 \ 
       \ 
    # foo 

It is is interpreted as follows:

    CFLAGS = -std=c99  # foo

The value of CFLAGS parameter is -std=c99 # foo in this case.

(4) A definition of a multiline parameter has the following form:

    <Name> =>> 
        <line1>
        <line2>
        ...
        <lineN>
    <<

The value of this parameter is the part of the file between <Name> =>> and << (excluding the newline characters before <line1> and after <lineN>).

These lines are taken as is: no trimming of whitespace characters, no comment and blank line recognition, no special meaning of the trailing backslash characters like in (3) above, etc.

The resulting value will contain newline characters between <line1>...<lineN>, so the value of the parameter can be multiline indeed.

Non-whitespace characters are not allowed after =>> marker at the same line.

If and only if a line contains just << and, optionally, whitespace characters, it is considered an end marker for the value of this parameter. That is, it is allowed to specify multiline parameters like this:

    CFLAGS =>>
        -O2
        -D"_FOO=<<"
        -g
    <<

<< in -D"_FOO=<<" is not considered an end marker for this value because apart from <<, there are non-whitespace characters on the line.

An example of a configuration file is shown below:

############################################################################
# mysuite.cfg 
############################################################################

# Name of the test suite
TEST_SUITE_NAME = mysuite-t2c

# Version of the test suite
TEST_SUITE_VERSION = 0.1.0_alpha2

# Bug report address of the test suite (typically, email or web address)
TEST_SUITE_BUGREPORT = http://bugtracker.somesite.org/sample-tests/

# Copyright holder
COPYRIGHT_HOLDER = Some Company Ltd

# Extra options for the compiler and linker common for all the tests in the 
# suite. 
COMMON_COMPILER_FLAGS = -DCHECK_EXT_REQS \
    `pkg-config --cflags MySuperLibrary-3.1 \
    gtk+-2.0`
COMMON_LINKER_FLAGS = 
COMMON_LIBS = `pkg-config --libs MySuperLibrary-3.1 gtk+-2.0`

# A boilerplate notice.
NOTICE =>>
This program 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 2
of the License, or (at your option) any later version.

This program 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, write to the Free Software
Foundation, Inc., 
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 
<<

If there are several Name = Value records with the same Name in the file, all the values defined this way will be associated with the corresponding parameter. The parameter will be multi-valued as a result. The order of its values will be the same as the order of the respective records in the file.

Note

Note that when T2C file generator processes the configuration files for the test suites and test groups, it will report an error if it encounters a multiple definition of a parameter. This makes sense in T2C because usually the very meaning of a configuration parameter requires that it has only one value. There are some multi-valued parameters used there too (TEST_MODULE, for example) but they always have special meaning and hence are specified in a special way. Custom multi-valued parameters are not allowed by T2C file generator.

On the other hand, MiST Engine allows to define multi-valued parameters in the configuration files (value files) as described above. Unlike T2C, MiST Engine does not make any assumptions about the meaning of parameters (except the parameters of template groups like FILE_PATH_TEMPLATE, etc.). So you can use parameters with MiST Engine if you want to.

5.4. Global, suite-specific and test-specific parameters

Parameters used by T2C file generator to create resulting files can be specified at three levels:

Global parameters

Several special parameters are set by the file generator (see Section 5.7.2, “Special parameters defined by the file generator”). The values of these parameters cannot be overridden at suite-specific or test-specific level (it usually makes no sense to try to do so, anyway).

Suite-specific parameters

These parameters are specified in the configuration file of the test suite. Among these are often custom compiler and linker options to be used when building the tests from the generated files, parameters controlling execution of the tests (for example, time limits), etc. Even a copyright notice that should appear at the top of the generated source files can often be made a parameter.

A configuration file should always be provided for the test suite. This file should be located in the main (root) directory of the suite. It has .cfg extension and the same name as that directory (without –t2c suffix if it was present). For example, if the main directory of the suite is foo, the file generator will look for foo.cfg in that directory. For baz-t2c directory, the configuration file should be named baz.cfg.

It is allowed to specify user-defined parameters at this level. The file generator makes no assumptions about the meaning of suite-specific parameters (except two special parameters described below) and just substitutes them to appropriate places in the templates.

Two special parameters can be specified at this level, both of which are optional:

Test-specific parameters

You can optionally specify parameters that will affect only the group of tests generated from a particular .t2c file. If a parameter is defined both at suite-specific and test-specific levels, its value from the test-specific level is used. Special parameters (see above) cannot be overridden this way.

It is also allowed to define the parameters at this level that are not specified at two other levels.

Using test-specific parameters can be helpful, for instance, in the following (if the file generation target supports appropriate features):

  • Set a different time limit on the execution of the tests for the group of tests than for the remaining groups.

  • Specify a particular copyright notice for the group of tests if it should be different than for the rest of the suite.

  • Specify custom compiler and linker options to be used for this group of tests.

  • Specify additional source files and other files necessary to build the tests from this group. This could also be achieved by overriding the appropriate templates at test-specific level (see Section 5.5, “Default, suite-specific and test-specific templates”) but using the parameters for that could sometimes be more convenient.

  • Specify a custom extension to use in the name of the generated source file for the group if it differs from the one used in this test suite by default.

The parameters defined at this level are set in a test-specific configuration file. This file should be located in the same directory as the corresponding .t2c file. The configuration file should have the same name as the .t2c file but its extension should be .cfg, for example, test.cfg for the group of tests defined in test.t2c, bar.foo.cfg for bar.foo.t2c, etc. If the T2C source file defining the group or tests does not have .t2c extension, .cfg extension is appended to get the name of the configuration file: baz.bz.cfg for bar.bz, etc.

5.5. Default, suite-specific and test-specific templates

Similar to parameters, there are default, suite-specific and test-specific templates (more exactly, template groups) used by the file generator.

New template groups can be added at suite-specific and test-specific levels which will result in creation of the corresponding output files by the file generator. For example, if you need an additional file to be created for each group of tests in the suite, you should provide appropriate template group for that file at the suite-specific level. The file generator will do the rest.

Some or even all of the template groups can be overridden (replaced by the groups with the same names but probably different contents) at the suite-specific or test-specific level. The file generator decides which templates to use individually for each template group. First, it creates a list of the groups provided by default for the file generation target currently used. Then, if there are suite-specific template groups for this target, the file generator updates the list, adding these groups to it. Each template group in the list that has the same name as a suite-specific group is replaced by the latter. Finally, the process is repeated for test-specific templates for the target: new template groups are added and the default and suite-specific ones are overridden by the test-specific ones. The resulting list may contain the template groups from one, two or even from all three levels. These groups are then evaluated by the file generator to create the required files.

Note

  1. When looking for appropriate templates, the file generator processes each template group as a whole. So, it is not possible to add or override only a part of a group and leave the remaining templates from the group unchanged. Only a template group as a whole can be added or overridden.

  2. Currently, there is no way to instruct the file generator to create less files at, say, suite-specific level than required by the template groups at the default level. That is, template groups cannot be turned off at suite-specific and test-specific levels, they can only be added or overridden.

It is not required to use suite-specific or test-specific templates.

Here is a description of how the template groups are defined at each level (see also Section 5.6, “Directory structure of the templates for a T2C target”).

Default templates

Default templates are loaded from <target_name> subdirectory of the main template directory (<target_name> is the name of the file generation target to be used). Main template directory is templates subdirectory of what is returned by t2c --print-data-dir unless t2c is executed with -d command line option (see Section 3, “T2C Command Line Tool” for details).

Suite-specific templates

If there is TEMPLATE_DIR parameter defined in the configuration file of the test suite, its value specifies the path to the suite-specific template directory. This directory must contain a subdirectory with appropriate name for each file generation target the suite-specific templates are provided for. The value of TEMPLATE_DIR parameter is interpreted as the path relative to the root directory of the suite.

For example, suppose makefile_am, test_group and test_case template groups are defined by default for test mode (see Section 3, “T2C Command Line Tool”) for a target named c_sample. The directory structure of the main template directory may look as follows:

<main_template_directory>
    |
    c_sample  // name of the target
        |
        +- common  // template groups used by t2c in "common mode"
        |   |
        |  ... 
        |
        +- test    // template groups used by t2c in "test mode"
            |
            +- makefile_am
            +- test_case
            +- test_group

Assume we want to add some_data template group and replace test_group with a suite-specific one in the test mode. To do so, we should create custom_templates subdirectory in the main directory of the suite. The subdirectory should contain the following:

custom_templates
    |
    c_sample  // name of the target
        |
        +- test    // template groups used by t2c in "test mode"
            |
            +- some_data  // another template group to be processed
            +- test_group // these templates override the default ones

Note that as we do not change anything in common mode, we do not need to provide common subdirectory.

Now we need to define TEMPLATE_DIR parameter in the configuration file of the suite as follows:

    TEMPLATE_DIR = custom_templates

After that, if we execute t2c in test mode, it will use default template groups makefile_am and test_case and suite-specific template groups test_group and some_data.

Test-specific templates

These templates affect only the group of tests created from a particular .t2c file. Main directory of the templates should be located in the same directory as the corresponding .t2c file. The name of this template directory should be the same as the full name of that file with .templates suffix appended. If the T2C source file ends with .t2c extension, this extension is omitted. For example, test-specific templates for the group of tests defined in test.t2c should be stored in test.templates directory, for bar.foo.t2c - in bar.foo.templates, for bar.bz - in bar.bz.templates, etc.

The directory structure for test-specific templates is similar to that of the suite-specific ones.

Let us extend the example above. Suppose we also want to replace the templates from makefile_am and some_data groups with test-specific ones for the group of tests to be created from buzz.t2c. To do this, we should create buzz.templates subdirectory in the directory where buzz.t2c is located. The subdirectory should contain the following:

buzz.templates
    |
    c_sample  // name of the target
        |
        +- test    // template groups used by t2c in "test mode"
            |
            +- makefile_am  // overrides the default templates
            +- some_data    // overrides the suite-specific templates

If we now execute t2c in test mode, it will use the default template group test_case, suite-specific template group test_group and test-specific template groups some_data and makefile_am.

Note

If you use test-specific templates, it does not mean that you should use suite-specific templates as well.

5.6. Directory structure of the templates for a T2C target

Main template directory for a T2C file generation target should have the same name as the target. It may contain two subdirectories:

  • common - this directory contains templates for the files specific to the test suite. t2c will use these templates when called in common mode (see Section 3, “T2C Command Line Tool”). If there are no such files to be generated, the directory could be absent.

  • test - this directory contains templates for the files specific to the group of tests defined in a particular .t2c file. t2c will use these templates when called in per-test mode (see Section 3, “T2C Command Line Tool”). This directory can also be absent but it usually makes little sense not to use it.

For each file to be generated in common or per-test mode, a template group should be prepared (see Section 5.2, “Template groups and subtemplates” for details). The templates from this group should be located in a subdirectory of common or test directory, respectively, that has the same name as the group.

Note

Name of a template group does not need to be the same as the name of the file to be created from it. The name (the path) of the resulting file should be specified in FILE_PATH_TEMPLATE parameter in the configuration file of the group. The configuration file of the group has .cfg extension and the same name as the group. This file is located in the directory containing the templates from this group.

There is one notable exception to One template group - one generated file rule. The file with source code of a group of tests is created using two template groups that must always be present in per-test mode: test_case and test_group. The former allows to create an individual test, the latter defines the structure of the source file where these individual tests will be placed. The path to the source file is specified in FILE_PATH_TEMPLATE parameter in the configuration file of test_group group. How these template groups are processed is described in Section 5.7, “Generating output files for the test suites”.

Consider, for example, directory structure of the templates for c_gtest_nested target:

c_gtest_nested
    |
    +- common
    |   |
    |   +- configure_file  // templates for top-level configure.ac file
    |   |   |
    |   |   +- configure_file.cfg   // configuration file
    |   |   +- configure_file.tpl   // main template
    |   |   +- makefile_item.tpl    // subtemplate
    |   |
    |   +- makefile_am_file // templates for top-level Makefile.am file
    |       |
    |       +- makefile_am_file.cfg  // configuration file
    |       +- makefile_am_file.tpl  // main template
    |       +- test_subdir.tpl       // subtemplate
    |
    +- test
        |
        +- makefile  // template for per-test makefiles
        |   |
        |   +- makefile.cfg   // configuration file
        |   +- makefile.tpl   // main template
        |
        +- test_case // templates for an individual test 
        |   |
        |   +- test_case.cfg   // configuration file
        |   +- test_case.tpl   // main template
        |   +- define.tpl      // subtemplate
        |   +- undef.tpl       // subtemplate
        |
        +- test_group // templates for a test source file
            |
            +- test_group.cfg       // configuration file
            +- test_group.tpl       // main template
            +- test_func_item.tpl   // subtemplate
            +- test_name_item.tpl   // subtemplate

5.7. Generating output files for the test suites

Apart from the sources of the tests, the output files (Makefiles, etc.) are generated simply by evaluating appropriate template groups. The process of generating the source files is more complicated, test_group and test_case template groups are handled in a special way by the file generator. The process is described below.

5.7.1. Generating test source files

Each individual test is generated from a <BLOCK> section for the corresponding set of parameter values defined in the <VALUES> (or <PURPOSE>) subsection of the <BLOCK>. The file generator uses test_case template group to do this. It is the only template group from which no file is created. It is rather used to prepare the parts of the test source file. After test_case template group is evaluated to obtain the source of an individual test, the resulting string is appended to the list of values of test_case parameter used by test_group template group. From the latter group, the file with the source code of the tests is created.

Technically, a file containing the source code of the tests is created by the file generator in three steps:

  1. An intermediate template of the source code of the tests from a <BLOCK> section is prepared by evaluating test_case template group with respect to <$…$>-parameters and subtemplates. That is, the templates from test_case group, the contents of <CODE> and <FINALLY> sections are assembled into a single template that may refer only to <%…%>-parameters.

  2. The intermediate template prepared at the previous step is now evaluated for each set of values of test parameters (<%…%>-parameters) to produce the source code of the corresponding individual tests. In particular, this is the step when the values from <VALUES> (<PURPOSE>) sections replace <%0%>, <%1%>, <%2%>, etc., in the source code of the tests.

    The resulting strings containing the source code of the individual tests created from the <BLOCK> section are appended to the list of values of test_case parameter that will be used by test_group template group.

  3. The previous two steps should now have been performed for each <BLOCK> section in the .t2c file. Finally, the test_group template group is evaluated to produce the file containing the source code of all the tests generated from the .t2c file.

    The main template of test_group template group often contains a construct like the following one to place the sources of the individual tests in the resulting test source file:

    <$test_case : join(\n)$>
    

    Here, the generated sources of the individual tests will be placed in the resulting source file separated by newline characters ('\n').

Because the file generator operates this way when creating source files, there are some restrictions on using template parameters in .t2c files.

For example, the parameters defined in the configuration file(s) for the tests as well as the special parameters defined by the file generator are substituted only when test_group template group is evaluated (as well as the template groups, from which other files are generated). This means, it is no good using these parameters in the sections of a .t2c file: they will be taken literally by the file generator. If you want these parameters to be used in the source files of the tests, you should appropriately change the templates from test_group group rather than refer to these parameters in the .t2c file.

Note

Begin and end markers of the placeholders in the templates (<$ and $> by default) can be changed for all template groups except <% and %> in test_case group.

User-defined markers can be specified in PH_BEGIN_MARKER and PH_END_MARKER parameters, respectively, in the configuration file of the template group of interest.

5.7.2. Special parameters defined by the file generator

T2C file generator defines several special parameters that can be referred to by the templates. The parameters are listed below.

T2C_DATADIR

The actual path to the data directory of T2C file generator.

This parameter should not be confused with T2C_DATADIR environment variable (see Section 3, “T2C Command Line Tool”). T2C_DATADIR parameter is rather what t2c --print-data-dir returns. It is always non-empty while T2C_DATADIR environment variable may or may not be defined.

SUITE_ROOT_DIR

The absolute path to the root directory of the test suite.

SUITE_NAME

The name of the test suite. It is the name of the main directory of the suite (without –t2c suffix if it was present).

T2C_FILE_NAME

The name of the .t2c file being processed (without .t2c extension if it was present).

YEAR

Current year in YYYY format (mostly for copyright notices).

TARGET

Name of the T2C target this file has been generated for (like c_minimal, c_standalone, etc.)

TEST_MODULE

If t2c is called with -f (--list-file) option, the values of this parameter are entries read from the file specified in this option. The entries may have any meaning but usually they somehow identify the groups of tests. For example, each entry can be the name of the .t2c file in the suite.

TEST_MODULE is commonly used when generating top-level files for a test suite: configure.ac, Makefile.am or the like. This parameter is often multi-valued, its values being the names of groups of tests involved. This allows to create the lists of files or directories depending on these names. Consider the following example from the template of configure.ac file (c_gtest_nested target):

AC_CONFIG_FILES([ 
<$if concat(TEST_MODULE)$><$makefile_item: join(\n)$><$endif$>
    t2c_api/<$TARGET$>/Makefile 
    testdata/Makefile
    Makefile
]) 

<$if concat(TEST_MODULE)$> is used to check if TEST_MODULE is defined. The values of TEST_MODULE are always non-empty strings, so if we created a concatenated string from these values, it would be non-empty if and only if TEST_MODULE had at least one value.

makefile_item subtemplate is defined in makefile_item.tpl as follows:

    tests/<$TEST_MODULE$>/Makefile

Assume t2c is called with --list-file=modules.list option and modules.list file contains the following:

    glib_arrays
    glib_date_and_time
    glib_file_utilities
    glib_i18n
    glib_string_utility

Then, the corresponding part of generated configure.ac file may look like this:

AC_CONFIG_FILES([ 
    tests/glib_arrays/Makefile
    tests/glib_date_and_time/Makefile
    tests/glib_file_utilities/Makefile
    tests/glib_i18n/Makefile
    tests/glib_string_utility/Makefile
    t2c_api/c_gtest_nested/Makefile 
    testdata/Makefile
    Makefile
]) 

All these parameters except T2C_FILE_NAME are available when the file generator is used in test mode or in common mode. T2C_FILE_NAME is available only when the file generator is used in test mode (because no .t2c file is processed in common mode anyway). See Section 3, “T2C Command Line Tool” for details on how the file generator operates in these modes.