Preparing Testcases
See https://gcc.gnu.org/onlinedocs/gccint/Test-Directives.html#Test-Directives for official documentation. See also TestCaseWriting for another test-case howto.
Every patch submitted for approval should always come with a testcase to be added to the testsuite (there are a few exceptions that are listed below). The testcase should be able to test the functionality of the new features you add or to reproduce the bug on all affected platforms, and it is used to make sure that the bug stays fixed.
There are some basic guidelines you should follow while preparing a testcase:
The testcase should be as small as possible. Most bugs can be minimized to a handful of lines of code, if you try hard enough. Usually, volunteers take care of the minimization if the bug is reported in the https://gcc.gnu.org/bugzilla/ bug database. A_guide_to_testcase_reduction is available, too.
- testcases for new features however should err on the side of being long; covering as many cases as possible that might be relevant to the issue, even if with the present code they follow identical paths.
- If your patch fixes a bug which can be reproduced with different code sequences (or a family of slightly similar bugs), it is better to prepare a single testcases with all the tests in it instead of several different testcases containing a single test each.
If the testcase is for a bug from our bug database, the specific PR must be named with the usual syntax, together with a good summary (e.g. PR c++/2204: Check abstractness of function parameters ) within a comment for easier future reference.
- If the testcase fixes a bug which was not submitted to the bug database, include a comment with a short description of the bug, that is what the testcase is meant to check. In some situations, the testcase itself is a crystal clear explanation of the bug (for instance, checking that a specific error message be emitted), so the description can be omitted.
- The testcase should not rely on external files (such as headers, even standard headers), if possible. It is better to manually bring the declarations of the needed functions into scope. For instance, instead of:
#include <stdlib.h>
void foo(void)
{
abort();
}
it is better to write:
extern void abort(void);
void foo(void)
{
abort();
}
C++ testcases can prepend extern "C" while declaring such functions.
Exceptions are the C headers that GCC includes, such as <limits.h> and <stddef.h>. For feature testcases these are fine to include. Testcases including <limits.h> and then using macros such as INT_MAX are arguably more readable than ones using the predefined _INT_MAX_ directly.
There are a few excuses for not preparing a testcase.
- It is not strictly necessary for a bug which occurred during GCC bootstrap, as GCC itself is considered a big testcase.
- There are situations where it is very hard to minimize a bug (a weird garbage collector crash, a very complex C++ testcase causing a failure somewhere deep into RTL optimizers, etc.).
A testcase could obviously be already present and just XFAIL-ed (see below), in which case it is sufficient to remove the xfail mark.
- The testcase may not be writable in the present framework (there seems to be no simple way to test for duplicated error messages).
- It might be from a proprietary testsuite (so if you've seen the case, already small, writing one that isn't a copy may be hard).
Choose carefully which directory to place the test in.
Each language has its own directory, and often two directories: one for torture tests, and the other for normal tests. A torture test is a testcase which is compiled and optionally executed with many different optimization levels, and it is meant to stress GCC optimizers. Normal tests instead are meant to test for frontend features (which are not affected by optimization levels), or tests which need specific command line options and thus are not suited for the torture testsuite.
- The other directories are obsoleted and frozen, so new tests must not go there.
Active directories in the testsuite
gcc/testsuite/gcc.c-torture/
C torture tests.
gcc/testsuite/gcc.dg/
C normal tests.
gcc/testsuite/g++.dg/
C++ tests.
gcc/testsuite/gfortran.dg/
F95 tests.
gcc/testsuite/gfortran.fortran-torture/
F95 torture tests.
gcc/testsuite/gnat.dg/
Ada tests.
gcc/testsuite/objc/
Objective C torture tests.
gcc/testsuite/objc.dg/
Objective C normal tests.
lib*/testsuite/*/
tests for the target libraries.
NOTES (these apply only to C testcases):
- Do not put non-portable testcases in gcc.c-torture.
- Any target specific testcase must have appropriate code to prevent it from causing a `failure' on unsupported platforms. The "torture" tests are meant to be generic tests that can run on any target. So you have to be careful about endianness, assumptions about sizes of datatypes, etc etc.
- For tests that merely need to compile, put them in the "compile" subdirectory.
- For tests which should give an error, put them in the "noncompile" subdirectory and update noncompile.exp appropriately (see examples in noncompile.exp).
- For IEEE FP specific tests, put them in execute/ieee.
- For execution tests, put them in execute.
- If a test does not fit into the torture framework, use the dg framework.
To add and test your testcase, it is sufficient to drop it into one of the above directories, and then run one of the below commands from the build directory
# Generic version. make check RUNTESTFLAGS=testsuitename.exp=testcasename.ext # C++ only make -C gcc check-g++ RUNTESTFLAGS=testsuitename.exp=testcasename.ext
testsuitename.exp is the name of the testsuite module (look for a file with extension .exp which is the closest, directory-wise, to the testcase you added). If the testcase does not work as expected, you can inspect the testsuite log, which is generated somewhere deep in the build directory (the logs for the GCC core can be found in <build-dir>/gcc/testsuite/*.log, while the logs for multilib libraries are at <build-dir>/<target>/<library>/testsuite/*.log, for instance build/i686-pc-linux-gnu/libstdc++-v3/testsuite/libstdc++.log).
The testsuite uses DejaGnu as its framework. The framework parses the testcases looking for special commands, written in comments, which describe the tests that must be performed. Each testcase might have several different tests performed on it, each one described by a command.
Each command issues a specific test to be done. The possible outcomes are the following
PASS
The test was sucessful.
FAIL
The test failed.
XFAIL
The test is marked as an expected failure (see below), and it failed indeed.
XPASS
The test is marked as an expected failure, but passed so it is not a failure anymore.
The testsuite summary lists only the tests that exited with FAIL or XPASS status, since the others just behaved as expected. In the former case, it means that the test failed, so your patch probably broke it. In the latter case, it means that your patch just fixed a bug which was already present in the testcase but marked as an expected failure. This means that a new testcase is probably not necessary, and your patch can be updated so that it also removes the xfail modifier from the existing testcase.
Test commands
The main commands that can be used are the following:
{ dg-do <action> }
- Specifies the basic compilation action to be performed on the testcase.
Valid actions for dg-do
preprocess
only run the preprocessor on the file.
compile
compile the file and emit the assembly code.
assemble
compile the file and assemble the assembly code, producing an object binary.
link
compile, assemble and link the testcase, producing an executable.
run
compile, assemble and link the testcase, and run the generated executable. The executable must then return with exit code 0 for the test to be successful.
For instance, the following testcase uses a run action:
/* { dg-do run } */ extern void abort(void); static const int a = 3 + 3; int main(void) { if (a != 6) abort(); return 0; }
Technically, a testcase which exits with non-zero exit code will be treated as a failure, but the recommended way is to use abort() to make testcase fail, as shown in the example.
By default, when using the assemble or compile actions, the framework expects the testcase to compile correctly, without any warning or error. If any diagnostic is expected, it must be handled with the dg-error / dg-warning commands (see below). Each of these commands match one or more error messages emitted during compilation. If all the error messages are matched (no excess errors), the compilation is considered a success (even the compiler technically failed on it). Otherwise, if there are excess (unmatched) errors, the test fails. The use of actions link and run is inappropriate for testcases which contain dg-error / dg-warning , because they are expected to not compile at all.
{ dg-options <opts> }
- Specifies command line options to be passed to GCC while compiling the testcase. This is useful to enable optimizations, test the behaviour of specific options like warnings or attributes, etc. The following testcase shows an example of this:
/* { dg-do link } */ /* { dg-options "-O2" } */ void link_error(void); int main(void) { /* We make sure this check is optimized away, otherwise a call to link_error is performed and there would be an error while linking the testcase (link_error has no definition). */ if (5+6) link_error(); return 0; }
{ dg-error "<match-regexp>" "<comment>" <{ target *-*-* }> <line-spec> }
Checks for the presence of an error message emitted by the compiler for a line matching the <line-spec>. The error text is matched with a regular expression which allows a certain degree of freedom in the match. The <line-spec> is either an integer that refers to the corresponding line of the test case, or a pattern that matches the ERE \.([-+][0-9]?)? such as a sole period (.) or .+1 or .-2. The pattern specifies reference to a line whose number is obtained by adding the signed integer to the number of the line the reference is on. (See Extend dg-{error,warning,message,bogus} line specification to allow relative line number for more.) The <line-spec> is usually omitted, and in that case the compiler error is expected on the line in which the dg-error command is defined. The (optional) error description is just a human-readable description of the error the command is checking for, and in case of failure it is emitted in the testsuite summary. Usually, the regular expression pattern is just a significant substring of the error message that it is meant to match. For instance, consider the following testcase:
/* { dg-do compile } */ struct A { int y : 0; /* { dg-error "zero width" } */ };
test.c:4: error: zero width for bit-field y
The dg-error command just matches a significant substring of the error message, which is enough for the purpose.
In some situations, the compiler emits two (or more) error messages for the same line of code, or a different message with different compiler options (for example, a different error may be issued when -std=c89 is specified than when -std=c11 is). There are two ways to handle this. When it doesn't matter which of the alternative messages is emitted, it is suggested to use a single dg-error command, with two different substring patterns (using the | operator for regular expressions, for instance "foo|bar"). This has the effect of the dg-error directive succeeding when either of the alternatives or both are seen. To verify that two or more distinct errors are issued for the same line, one dg-error directive must be used for each error. In such cases, the first directive is usually on the same line as the tested code and the other directives follow on subsequent lines. Since they are on different lines than the tested code, the subsequent directives must provide a <line-spec>. Using relative line references is preferable to hardcoding absolute line numbers because it avoids test failures when line numbers change. Since the line number is the last argument, all the preceding and otherwise optional arguments such as the target selector must be provided as well. The following block copied from the gcc.dg/init-bad-1.c test shows an example of this usage:
struct s x = { 0 }; /* { dg-error "variable 'x' has initializer but incomplete type" } */ /* { dg-warning "excess elements|near init" "excess" { target *-*-* } .-1 } */ /* { dg-error "storage size" "size" { target *-*-* } .-2 } */
Using the empty string ("") as the regexp matches any error message on the given line. This technique though is strongly discouraged: while it still makes the testcase valid, checking that the compiler does emit a diagnostic on the line, it does not enforce that the error message is indeed the correct one. It is in fact common for the compiler to regress in diagnostic quality, and to suddenly change a user-friendly error message into a cryptic syntax error, if not a totally wrong message. Such situations can be caught immediately only if the dg-error command contains a matching pattern for the error text.
{ dg-warning "<match-regexp>" "<comment>" <{ target *-*-* }> <line-spec> }
Behaves exactly like dg-error , but checks for a warning instead of an error.
{ dg-excess-errors "<match-regexp>" }
- Matches excess errors. This command basically checks for matching errors emitted on any line of the testcase.
It is not meant to be a lazy-man version of dg-error to avoid indicating the line where the error should happen. Instead, it can be useful for situations where there are too many fallout errors (bad error recovery) that would not make sense to match (as they could change or even be fixed in the future, and we do not want that to be reported as a failure in the testsuite). Moreover, this command can be useful to take care of all the additional information notes that the compiler emits to help the user fixing the problem: since they all look like:
foo.c:26: note: blah , it is sufficient to use {dg-excess-errors "note"} to match all of them.
Some dejagnu version treat "<match-regexp>" as comment.
{ dg-bogus "<match-regexp>" "<comment>" <{ target *-*-* }> <line-spec> }
Behaves like dg-error but with inverted logic: it tests for an error matching a certain regular expression not to be emitted on the line matching the <line-spec>. This is useful for situations where we want to make sure that a certain error message or warning is not emitted any more.
One could ask why to bother at all with dg-bogus . In fact, if we want an error not to be emitted while compiling the testcase, it is sufficient to do nothing at all, were the error emitted, the testcase compilation (as specified by dg-do ) would fail for excess errors. 'dg-bogus is mostly useful in combination with the xfail modifier (see below for an example).
{ dg-final { <final-action> } }
This family of commands executes an action *after* the main action (specified by dg-do) is done.
{ dg-final { scan-assembler "<match-regexp>" } }
- Scans the assembly output emitted by GCC during the compilation file, trying to match the given regular expression. The test succeeds if a match is found. This command can be useful to check if a specific machine instruction is present in the assembly listing (for instance, generated by an optimization), or if a (possibly mangled) symbol was emitted. The following example highlights one of these usages
/* Verify that 387 fsincos instruction is generated. */ /* { dg-do compile { target "i?86-*-*" } } */ /* { dg-options "-O -ffast-math -march=i686" } */ /* { dg-final { scan-assembler "fsincos" } } */ double f1(double x) { return sin(x) + cos (x); }
{ dg-final { scan-assembler-not "<match-regexp>" } }
Similar to scan-assembler but with inverted logic: the test succeeds if no match is found while scanning the assembly output.
{ dg-final { scan-file "<filename>" "<match-regexp>" } }
Generalized version of scan-assembler, but scans a specific file whose name is specified. This can be used for instance while preparing testcases for the preprocessor to parse the preprocessed output (assuming that dg-do preprocess was used). See the testcases in testsuite/gcc.dg/cpp/ for some examples.
{ dg-final { scan-file-not "<filename>" "<match-regexp>" } }
Similar to scan-file but with inverted logic: the test succeedes if no match is found while scanning the specified file.
{ dg-final { scan-tree-dump-times "<match-regexp>" <num-expected-matches> "<dump-name>" } }
- Scans a tree dump looking for a certain regular expression pattern, and checks if it matches exactly the expected number of times. This is the main test used to check the functionality of the tree optimizers (you can find a number of tests using this command in testsuite/gcc.dg/tree-ssa/). Each tree optimizer pass has a specific name and can be instructed to emit a tree dump after it runs. By scanning its dump, we can check if it did what we expected it to do.
For instance, the following testcase checks if the optimizer pass called dce3 was able to remove all the occurrences of if conditionals, as we expect it to do
/* { dg-do compile } */ /* { dg-options "-O1 -fdump-tree-dce3" } */ int t() __attribute__ ((const)); q() { int i = t(); if (!i) i = t(); } /* There should be no IF conditionals. */ /* { dg-final { scan-tree-dump-times "if " 0 "dce3"} } */
Notice that it is necessary to tell GCC to generate the specific tree dump manually, through the dg-options command, otherwise there will be no dump to scan, and scan-tree-dump-times will fail.
{ dg-final { scan-tree-dump "<match-regexp>" "<dump-name>" } }
Equivalent to { scan-tree-dump-times "<match-regexp>" 1 "<dump-name>" }
{ dg-final { scan-tree-dump-not "<match-regexp>" "<dump-name>" } }
Equivalent to { scan-tree-dump-times "<match-regexp>" 0 "<dump-name>" }
{ dg-output "<match-regexp>" }
- Verifies that the compiled executable generates output matching the regular expression once executed.
{ <modifier> }
- Modifiers can be combined to any of the above commands, and modify the behaviour of the test performed by the commands.
To apply a modifier to a command, use the following syntax: { command options { <modifier> } } .
Common modifiers used in the testsuite
{ target "<triplets>" }
Modifies the command so that the test is executed only on a specific target triplet, or a family of targets (through standard wildcards, ? and *). If needed, more than one triplet can be specified. This modifier is useful because some tests are not meant to be executed on all targets, as they test specific features of the backends. Note that applying this modifier on the main action means that the whole testcase is conditionally executed. The following test is an example of this modifier: it is meant to check for a peephole bug which used to exist in the rs6000 backend, and thus can be triggered on both PowerPC- and POWER-based systems
/* { dg-do compile { target powerpc-*-* rs6000-*-* } } */ /* { dg-options "-O3 -mcpu=power2 -fno-schedule-insns -w -mhard-float" } */ /* This used to ICE as the peephole was not checking to see if the register is a floating point one (I think this cannot happen in real life except in this example). */ register double t1 __asm__("r14"); register double t2 __asm__("r15"); register double t3 __asm__("r16"), t4 __asm__("r17"); void t(double *a, double *b) { t1 = a[-1]; t2 = a[0]; t3 = a[1]; t4 = a[2]; b[-1] = t1; b[0] = t2; b[1] = t3; b[2] = t4; }
{ xfail "<triplets>" }
- Marks the test as an expected failure in the testsuite. An expected failure is a bug which cannot be fixed at the moment for several reasons, but for which we still need a testcase. Expected failures are not reported in the testsuite summary. Only release managers and experienced developers should mark tests with the XFAIL modifier. In fact, XFAIL-ing a testcase hides it in the summary, so it does not immediately catch the attention of people. If your patch breaks an existing test, it usually means that there is a bug in your patch. If you believe it is not so, you should:
- Check if the testcase that is now failing is invalid.
- For instance, a new patch to the frontend could have caught a bug in an ill-formed testcase that was erroneously submitted. In this case, fix the testcase (or, in the worst case, even remove it).
- Check if the testcase is failing due to an existing bug exposed by your patch.
- In this case, you can either try to fix it in your patch (assuming it is related) or cope with it at a later time.
In either case, explain your reasoning in your mail to the mailing list.
Each testcase which is XFAIL-ed must have a corresponding PR open in the bug database, and the PR number must be referenced in a comment in the testcase itself. This makes sure that the failure is not forgotten.
Do not use XFAIL to disable tests which cannot be executed on specific targets: this is not what XFAIL is meant for. Use the target modifier for this, as explained above. The following example shows an example with a legit use of XFAIL: a bogus error message diagnosed by the compiler which cannot be currently fixed
/* { dg-do compile } */ /* { dg-options "-Wtraditional" } */ struct bar { int i; long j; }; union foo { struct bar b; int i; long l; }; struct baz { int a; double b; union foo c; }; static struct baz f1 = { 1, 2, {{1,1}} }; /* { dg-warning "traditional C rejects" "initialization of unions" } */ static struct baz f2 = { 1, 2, {{0,0}} }; /* { dg-bogus "traditional C rejects" "initialization of unions" { xfail *-*-* } } */
This testcase checks if a specific warning is emitted when -Wtraditional is used. This is the case of the initialization of f1, which correctly emits a warning that dg-warning checks for. Instead, the initialization f2 is not supposed to trigger a warning, but it does at the moment: it was agreed that GCC could live with this spurious warning for the time being, so a dg-bogus test was added ( dg-bogus checks that a specific warning/error is not emitted) and marked with xfail (since right now the warning is incorrectly emitted). When the bug is fixed and the spurious warning is not emitted anymore, the xfail modifier will be removed from the testcase.
- Check if the testcase that is now failing is invalid.
PS:
This text was taken from a patch from GiovanniBajo and a README, the comments from Joseph S. Myers and formatted for this Wiki by MichaelCieslinski.
TODO
The testsuite cannot detect duplicate error/warnings PR 30612, you need to use the syntax:
/* { dg-bogus "message.*message" } */ /* { dg-warning "message" "" { target *-*-* } 1 } */