summaryrefslogtreecommitdiff
path: root/unittest/mytap
diff options
context:
space:
mode:
Diffstat (limited to 'unittest/mytap')
-rw-r--r--unittest/mytap/Doxyfile47
-rw-r--r--unittest/mytap/tap.c267
-rw-r--r--unittest/mytap/tap.h28
3 files changed, 290 insertions, 52 deletions
diff --git a/unittest/mytap/Doxyfile b/unittest/mytap/Doxyfile
index 8c23703e925..1b1c82b4c98 100644
--- a/unittest/mytap/Doxyfile
+++ b/unittest/mytap/Doxyfile
@@ -432,7 +432,7 @@ FILE_PATTERNS =
# subdirectories should be searched for input files as well. Possible
# values are YES and NO. If left blank NO is used.
-RECURSIVE = YES
+RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that
# should excluded from the INPUT source files. This way you can easily
@@ -457,14 +457,14 @@ EXCLUDE_PATTERNS =
# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH =
+EXAMPLE_PATH = e
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
-EXAMPLE_PATTERNS =
+EXAMPLE_PATTERNS = *.c
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
@@ -926,7 +926,7 @@ MACRO_EXPANSION = YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
-EXPAND_ONLY_PREDEF = NO
+EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
@@ -939,33 +939,34 @@ SEARCH_INCLUDES = YES
INCLUDE_PATH =
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more
+# wildcard patterns (like *.h and *.hpp) to filter out the
+# header-files in the directories. If left blank, the patterns
+# specified with FILE_PATTERNS will be used.
INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed.
+# The PREDEFINED tag can be used to specify one or more macro names
+# that are defined before the preprocessor is started (similar to the
+# -D option of gcc). The argument of the tag is a list of macros of
+# the form: name or name=definition (no spaces). If the definition and
+# the = are omitted =1 is assumed.
PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES
+# then this tag can be used to specify a list of macro names that
+# should be expanded. The macro definition that is found in the
+# sources will be used. Use the PREDEFINED tag if you want to use a
+# different macro definition.
-EXPAND_AS_DEFINED =
+EXPAND_AS_DEFINED = __attribute__
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse the
-# parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are
+# alone on a line, have an all uppercase name, and do not end with a
+# semicolon. Such function macros are typically used for boiler-plate
+# code, and will confuse the parser if not removed.
SKIP_FUNCTION_MACROS = YES
diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c
index 54292f3b828..29dc765950f 100644
--- a/unittest/mytap/tap.c
+++ b/unittest/mytap/tap.c
@@ -29,11 +29,17 @@
#include <signal.h>
/**
+ @defgroup MyTAP_Internal MyTAP Internals
+
+ Internal functions and data structures for the MyTAP implementation.
+*/
+
+/**
Test data structure.
Data structure containing all information about the test suite.
- @ingroup MyTAP
+ @ingroup MyTAP_Internal
*/
static TEST_DATA g_test = { 0, 0, 0, "" };
@@ -41,6 +47,8 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
Output stream for test report message.
The macro is just a temporary solution.
+
+ @ingroup MyTAP_Internal
*/
#define tapout stdout
@@ -50,7 +58,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
To emit the directive, use the emit_dir() function
- @ingroup MyTAP
+ @ingroup MyTAP_Internal
@see emit_dir
@@ -59,7 +67,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" };
@param ap Vararg list for the description string above.
*/
static void
-emit_tap(int pass, char const *fmt, va_list ap)
+vemit_tap(int pass, char const *fmt, va_list ap)
{
fprintf(tapout, "%sok %d%s",
pass ? "" : "not ",
@@ -80,18 +88,22 @@ emit_tap(int pass, char const *fmt, va_list ap)
not ok 2 # todo some text explaining what remains
@endcode
+ @ingroup MyTAP_Internal
+
@param dir Directive as a string
- @param exp Explanation string
+ @param why Explanation string
*/
static void
-emit_dir(const char *dir, const char *exp)
+emit_dir(const char *dir, const char *why)
{
- fprintf(tapout, " # %s %s", dir, exp);
+ fprintf(tapout, " # %s %s", dir, why);
}
/**
Emit a newline to the TAP output stream.
+
+ @ingroup MyTAP_Internal
*/
static void
emit_endl()
@@ -198,7 +210,7 @@ ok(int const pass, char const *fmt, ...)
if (!pass && *g_test.todo == '\0')
++g_test.failed;
- emit_tap(pass, fmt, ap);
+ vemit_tap(pass, fmt, ap);
va_end(ap);
if (*g_test.todo != '\0')
emit_dir("todo", g_test.todo);
@@ -223,7 +235,7 @@ skip(int how_many, char const *const fmt, ...)
while (how_many-- > 0)
{
va_list ap;
- emit_tap(1, NULL, ap);
+ vemit_tap(1, NULL, ap);
emit_dir("skip", reason);
emit_endl();
}
@@ -316,7 +328,7 @@ int exit_status() {
@section UnitTest Writing unit tests
The purpose of writing unit tests is to use them to drive component
- development towards a solution that the tests. This means that the
+ development towards a solution that passes the tests. This means that the
unit tests has to be as complete as possible, testing at least:
- Normal input
@@ -325,29 +337,240 @@ int exit_status() {
- Error handling
- Bad environment
- We will go over each case and explain it in more detail.
+ @subsection NormalSubSec Normal input
+
+ This is to test that the component have the expected behaviour.
+ This is just plain simple: test that it works. For example, test
+ that you can unpack what you packed, adding gives the sum, pincing
+ the duck makes it quack.
+
+ This is what everybody does when they write tests.
+
+
+ @subsection BorderlineTests Borderline cases
+
+ If you have a size anywhere for your component, does it work for
+ size 1? Size 0? Sizes close to <code>UINT_MAX</code>?
+
+ It might not be sensible to have a size 0, so in this case it is
+ not a borderline case, but rather a faulty input (see @ref
+ FaultyInputTests).
+
+
+ @subsection FaultyInputTests Faulty input
+
+ Does your bitmap handle 0 bits size? Well, it might not be designed
+ for it, but is should <em>not</em> crash the application, but
+ rather produce an error. This is called defensive programming.
- @subsection NormalSSec Normal input
+ Unfortunately, adding checks for values that should just not be
+ entered at all is not always practical: the checks cost cycles and
+ might cost more than it's worth. For example, some functions are
+ designed so that you may not give it a null pointer. In those
+ cases it's not sensible to pass it <code>NULL</code> just to see it
+ crash.
- @subsection BorderlineSSec Borderline cases
+ Since every experienced programmer add an <code>assert()</code> to
+ ensure that you get a proper failure for the debug builds when a
+ null pointer passed (you add asserts too, right?), you will in this
+ case instead have a controlled (early) crash in the debug build.
- @subsection FaultySSec Faulty input
- @subsection ErrorSSec Error handling
+ @subsection ErrorHandlingTests Error handling
- @subsection EnvironmentSSec Environment
+ This is testing that the errors your component is designed to give
+ actually are produced. For example, testing that trying to open a
+ non-existing file produces a sensible error code.
+
+
+ @subsection BadEnvironmentTests Environment
Sometimes, modules has to behave well even when the environment
- fails to work correctly. Typical examples are: out of dynamic
- memory, disk is full,
+ fails to work correctly. Typical examples are when the computer is
+ out of dynamic memory or when the disk is full. You can emulate
+ this by replacing, e.g., <code>malloc()</code> with your own
+ version that will work for a while, but then fail. Some things are
+ worth to keep in mind here:
+
+ - Make sure to make the function fail deterministically, so that
+ you really can repeat the test.
+
+ - Make sure that it doesn't just fail immediately. The unit might
+ have checks for the first case, but might actually fail some time
+ in the near future.
- @section UnitTestSec How to structure a unit test
+
+ @section UnitTest How to structure a unit test
In this section we will give some advice on how to structure the
- unit tests to make the development run smoothly.
+ unit tests to make the development run smoothly. The basic
+ structure of a test is:
+
+ - Plan
+ - Test
+ - Report
+
+
+ @subsection TestPlanning Plan the test
+
+ Planning the test means telling how many tests there are. In the
+ event that one of the tests causes a crash, it is then possible to
+ see that there are fewer tests than expected, and print a proper
+ error message.
+
+ To plan a test, use the @c plan() function in the following manner:
+
+ @code
+ int main(int argc, char *argv[])
+ {
+ plan(5);
+ .
+ .
+ .
+ }
+ @endcode
+
+ If you don't call the @c plan() function, the number of tests
+ executed will be printed at the end. This is intended to be used
+ while developing the unit and you are constantly adding tests. It
+ is not indented to be used after the unit has been released.
+
- @subsection PieceSec Test each piece separately
+ @subsection TestRunning Execute the test
+
+ To report the status of a test, the @c ok() function is used in the
+ following manner:
+
+ @code
+ int main(int argc, char *argv[])
+ {
+ plan(5);
+ ok(ducks == paddling_ducks,
+ "%d ducks did not paddle", ducks - paddling_ducks);
+ .
+ .
+ .
+ }
+ @endcode
+
+ This will print a test result line on the standard output in TAP
+ format, which allows TAP handling frameworks (like Test::Harness)
+ to parse the status of the test.
+
+ @subsection TestReport Report the result of the test
+
+ At the end, a complete test report should be written, with some
+ statistics. If the test returns EXIT_SUCCESS, all tests were
+ successfull, otherwise at least one test failed.
+
+ To get a TAP complient output and exit status, report the exit
+ status in the following manner:
+
+ @code
+ int main(int argc, char *argv[])
+ {
+ plan(5);
+ ok(ducks == paddling_ducks,
+ "%d ducks did not paddle", ducks - paddling_ducks);
+ .
+ .
+ .
+ return exit_status();
+ }
+ @endcode
+
+ @section DontDoThis Ways to not do unit testing
+
+ In this section, we'll go through some quite common ways to write
+ tests that are <em>not</em> a good idea.
+
+ @subsection BreadthFirstTests Doing breadth-first testing
+
+ If you're writing a library with several functions, don't test all
+ functions using size 1, then all functions using size 2, etc. If a
+ test for size 42 fails, you have no easy way of tracking down why
+ it failed.
+
+ It is better to concentrate on getting one function to work at a
+ time, which means that you test each function for all sizes that
+ you think is reasonable. Then you continue with the next function,
+ doing the same. This is usually also the way that a library is
+ developed (one function at a time) so stick to testing that is
+ appropriate for now the unit is developed.
+
+ @subsection JustToBeSafeTest Writing unnecessarily large tests
+
+ Don't write tests that use parameters in the range 1-1024 unless
+ you have a very good reason to belive that the component will
+ succeed for 562 but fail for 564 (the numbers picked are just
+ examples).
+
+ It is very common to write extensive tests "just to be safe."
+ Having a test suite with a lot of values might give you a warm
+ fuzzy feeling, but it doesn't really help you find the bugs. Good
+ tests fail; seriously, if you write a test that you expect to
+ succeed, you don't need to write it. If you think that it
+ <em>might</em> fail, <em>then</em> you should write it.
+
+ Don't take this as an excuse to avoid writing any tests at all
+ "since I make no mistakes" (when it comes to this, there are two
+ kinds of people: those who admit they make mistakes, and those who
+ don't); rather, this means that there is no reason to test that
+ using a buffer with size 100 works when you have a test for buffer
+ size 96.
+
+ The drawback is that the test suite takes longer to run, for little
+ or no benefit. It is acceptable to do a exhaustive test if it
+ doesn't take too long to run and it is quite common to do an
+ exhaustive test of a function for a small set of values.
+ Use your judgment to decide what is excessive: your milage may
+ vary.
+*/
+
+/**
+ @example simple.t.c
+
+ This is an simple example of how to write a test using the
+ library. The output of this program is:
+
+ @code
+ 1..1
+ # Testing basic functions
+ ok 1 - Testing gcs()
+ @endcode
+
+ The basic structure is: plan the number of test points using the
+ plan() function, perform the test and write out the result of each
+ test point using the ok() function, print out a diagnostics message
+ using diag(), and report the result of the test by calling the
+ exit_status() function. Observe that this test does excessive
+ testing (see @ref JustToBeSafeTest), but the test point doesn't
+ take very long time.
+*/
+
+/**
+ @example todo.t.c
+
+ This example demonstrates how to use the <code>todo_start()</code>
+ and <code>todo_end()</code> function to mark a sequence of tests to
+ be done. Observe that the tests are assumed to fail: if any test
+ succeeds, it is considered a "bonus".
+*/
+
+/**
+ @example skip.t.c
+
+ This is an example of how the <code>SKIP_BLOCK_IF</code> can be
+ used to skip a predetermined number of tests. Observe that the
+ macro actually skips the following statement, but it's not sensible
+ to use anything than a block.
+*/
+
+/**
+ @example skip_all.t.c
- Don't test all functions using size 1, then all functions using
- size 2, etc.
+ Sometimes, you skip an entire test because it's testing a feature
+ that doesn't exist on the system that you're testing. To skip an
+ entire test, use the <code>skip_all()</code> function according to
+ this example.
*/
diff --git a/unittest/mytap/tap.h b/unittest/mytap/tap.h
index 51b8c7df04d..a47060fa3cc 100644
--- a/unittest/mytap/tap.h
+++ b/unittest/mytap/tap.h
@@ -33,6 +33,8 @@
/**
Data about test plan.
+ @ingroup MyTAP_Internal
+
@internal We are using the "typedef struct X { ... } X" idiom to
create class/struct X both in C and C++.
*/
@@ -61,6 +63,14 @@ extern "C" {
#endif
/**
+ @defgroup MyTAP_API MyTAP API
+
+ MySQL support for performing unit tests according to TAP.
+
+ @{
+*/
+
+/**
Set number of tests that is planned to execute.
The function also accepts the predefined constant
@@ -101,11 +111,14 @@ void ok(int pass, char const *fmt, ...)
/**
Skip a determined number of tests.
- Function to print that <em>how_many</em> tests have been
- skipped. The reason is printed for each skipped test. Observe
- that this function does not do the actual skipping for you, it just
- prints information that tests have been skipped. It shall be used
- in the following manner:
+ Function to print that <em>how_many</em> tests have been skipped.
+ The reason is printed for each skipped test. Observe that this
+ function does not do the actual skipping for you, it just prints
+ information that tests have been skipped. This function is not
+ usually used, but rather the macro @c SKIP_BLOCK_IF, which does the
+ skipping for you.
+
+ It shall be used in the following manner:
@code
if (ducks == 0) {
@@ -192,8 +205,8 @@ void BAIL_OUT(char const *fmt, ...)
return exit_status();
@endcode
- @returns EXIT_SUCCESS if all tests passed, EXIT_FAILURE if one or
- more tests failed.
+ @returns @c EXIT_SUCCESS if all tests passed, @c EXIT_FAILURE if
+ one or more tests failed.
*/
int exit_status(void);
@@ -242,6 +255,7 @@ void todo_start(char const *message, ...)
void todo_end();
+/** @} */
#ifdef __cplusplus
}