Table of Contents
This package consists of a library called
librobotester.a
. With this library you build a
program called tester which you use to test the
functions of your application. A second program called
roborun.pl uses tester to
run all the individual tests and show the results.
ROBOTester can be installed as follows:
./configure make
This build a library called librobotester.a.
To illustrate the use of this we build a small example tester. If you impatient and want to try it out right away run the following:
cd Example make -f makefile.plain test
We start with a hello-world example.
The most simple tester you can build is the following.
In the robotester directory create a new directory.
In this directory create a file called tester.c
with the following content.
#include <robotester.h> int main( int argc, char** argv ) { return RBT_Testit( argc, argv ); }
You can compile this as follows.
gcc tester.c -I ../Source/ -L ../Source/ -lrobotester -o tester
If you run this
./tester
it reports
Usage: tester list tester run <testsuite name> <testcase name>
This tester of course does not test anything. Lets extend it so it test
something. Say you recently created a function for wildcards. Lets
assume it is stored in two files: namely wildcard.c
and
wildcard.h
. The function has the following
prototype.
int RB_Match( char* target, char* wildcard_expression );
It takes a string called target and matches it agains a wildcard_expression. It supports both '*' which matches any string of 0 or more charaters, and '?' which matches a single character.
If there is a match is returns TRUE, if not it returns FALSE. An example of its use is:
if ( RB_Match( filename, "*.c" ) ) { /* Do something with a c-source file */ }
You of cource want to make sure that this function works for any wildcard expression. To test this you design a number of testcases. Each testcase tests a particular scenarion.
Lets considder the scenario where wildcard expression that combinations of the wildcard characters '?' and '*'. To test it we write following testcase:
void Test_RB_Match_1 ( void ) { r_ok( RB_Match( "aapjes", "*?" ) ); r_ok( RB_Match( "aapjes", "?*" ) ); r_ok( RB_Match( "aapjes", "?a*jes" ) ); r_ok( RB_Match( "aaaapjes", "?a*jes" ) ); r_notok( RB_Match( "aaaapjes", "?a*bjes" ) ); r_notok( RB_Match( "aaaapjes", "?a*jes?" ) ); }
Basically a testcase is nothing more that a number
of calls to the function you are testing. Each time
with different values. You check the result using
r_ok
or r_notok
.
We store it in a file called
test_wildcard.c
.
No we need to add this testcase to the testsuite. This is done by adding the following function:
void Create_Wildcard_TestSuite( void ) { RBT_TestSuite* suite = RBT_CreateSuite( "WildcardTestSuite" ); RBT_AddCase( suite, Test_RB_Match_1 ); }
The prototype of this function we put in
test_wildcard.h
.
The only thing left now is to inform ROBOTester of the existence of this testsuite. This is done by adding a call to the function that creates the teststuite.
#include <robotester.h> #include "test_wildcard.h" int main( int argc, char** argv ) { Create_Wildcard_TestSuite(); return RBT_Testit( argc, argv ); }
We compile tester.c
,
test_wildcard.c
and
wildcard.c
.
This result in a tester application that can
carry out our a single testcase.
Lets test this. When run as:
./tester list
it reports:
WildcardTestSuite Test_RB_Match_1
We can run the test using:
./tester run WildcardTestSuite Test_RB_Match_1
and it reports:
OK: RB_Match( "aapjes", "*?" ) OK: RB_Match( "aapjes", "?*" ) OK: RB_Match( "aapjes", "?a*jes" ) OK: RB_Match( "aaaapjes", "?a*jes" ) OK: RB_Match( "aaaapjes", "?a*bjes" ) OK: RB_Match( "aaaapjes", "?a*jes?" ) COMPLETE:
We can now add a second testcase to test a different scenario. Say a wildcard expression with no wildcard symbols at all.
void Test_RB_Match_2 ( void ) { r_ok( RB_Match( "test", "test" ) ); r_notok( RB_Match( "xest", "test" ) ); r_notok( RB_Match( "tesx", "test" ) ); r_notok( RB_Match( "txxt", "test" ) ); r_notok( RB_Match( "testo", "test" ) ); }
And add this case to the testsuite.
void Create_Wildcard_TestSuite( void ) { RBT_TestSuite* suite = RBT_CreateSuite( "WildcardTestSuite" ); RBT_AddCase( suite, Test_RB_Match_1 ); RBT_AddCase( suite, Test_RB_Match_2 ); }
A call to
./tester list
now reports:
WildcardTestSuite Test_RB_Match_1 WildcardTestSuite Test_RB_Match_2
We could now run both testcases by hand, however there is a program that can do this for us:
perl ../Source/roborun.pl
It runs both testcases and reports the results.
--------------------------------------------------------------------------- WildcardTestSuite Test_RB_Match_1 OK --------------------------------------------------------------------------- OK: RB_Match( "aapjes", "*?" ) OK: RB_Match( "aapjes", "?*" ) OK: RB_Match( "aapjes", "?a*jes" ) OK: RB_Match( "aaaapjes", "?a*jes" ) OK: RB_Match( "aaaapjes", "?a*bjes" ) OK: RB_Match( "aaaapjes", "?a*jes?" ) COMPLETE: --------------------------------------------------------------------------- WildcardTestSuite Test_RB_Match_2 OK --------------------------------------------------------------------------- OK: RB_Match( "test", "test" ) OK: RB_Match( "xest", "test" ) OK: RB_Match( "tesx", "test" ) OK: RB_Match( "txxt", "test" ) OK: RB_Match( "testo", "test" ) COMPLETE:
This is all that there is to it.
The following sections document the library functions themself.
FUNCTION.
librobotester is a library that you use to create a program called tester. This tester creates and executes testcases.
The library can be divided in three main parts:
a part to create testcases
a part to register testcases and testsuites.
The part responsible for running and list testcase.
You create testcases with a number of assertion functions. A testcase will consist of a number of assertions. Each assertion must be true for a testcase to succeed. Currently the following assertions are supported:
Once you have created testcases you can register and collect them in a testsuite. The following functions are available for this:
Once you have a number of testsuites, you run them with:
FUNCTION.
This module contains assert function that can be used to create testcases.
TODO.
Implement:
r_is -- assert two interger values are equal
r_iss -- assert two strings are equal
r_isf -- assert two floats are equal
FUNCTION.
Assert that an expression has a zero value. You use this function to write your testcases. This function is a macro that points to RBT_Assert_Func().
INPUTS.
expression -- the expression to test
SOURCE.
#define r_notok( expression ) RBT_Assert_Func( __LINE__, __FILE__, #expression, !expression )
FUNCTION.
Assert that an expression has a non-zero value. You use this function to write your testcases. This function is a macro that points to RBT_Assert_Func().
INPUTS.
expression -- the expression to test
SOURCE.
#define r_ok( expression ) RBT_Assert_Func( __LINE__, __FILE__, #expression, expression )
FUNCTION.
Provide a commandline interface to list en run the testcases.
SOURCE.
int RBT_Testit( int argc, char** argv ) { if ( argc == 2 ) { if ( strcmp( argv[ 1 ], "list" ) == 0 ) { RBT_ListCases(); } else { usage(); } } else if ( argc == 4 ) { if ( strcmp( argv[ 1 ], "run" ) == 0 ) { RBT_RunById( argv[2], argv[3] ); } else { usage(); } } else { usage(); } return EXIT_SUCCESS; }
FUNCTION.
This module contains functions to create and run testcases. A testcase usually tests one or two functions of your application.
Testcases are stored in RBT_TestCase structures.
FUNCTION.
Information about a testcase. Testcases are stored in a testsuite ( RBT_TestSuite ). A testcase consists of upto three functions:
a setup function, that sets up the environment (variables, files, etc) necessary to carry out a test.
a test function, this is the function that carries out the actual test.
a teardown function, this releases any resources claimed in your setup function and/or testfunction.
ATTRIBUTES.
next -- pointer to the next testcase, this is used to store the testcases as a linked list in a RBT_TestSuite.
id -- a unique ID for this testcase.
test_function
setup_function
teardown_function
SOURCE.
typedef struct RBT_TestCase { struct RBT_TestCase* next; char* id; TestFunction test_function; SetupFunction setup_function; TestFunction teardown_function; } RBT_TestCase;
FUNCTION.
The type of the setup function. See TestFunction.
SOURCE.
typedef void (*SetupFunction)(void);
FUNCTION.
The type of the teardown function. See TestFunction.
SOURCE.
typedef void (*TearDownFunction)(void);
FUNCTION.
The type of the testfunction. All testfunctions and teardown and setup functions should be functions that take no parameters and return nothing.
This is a function pointer. See http://www.function-pointer.org/ for a nice tutorial on function pointers.
SOURCE.
typedef void (*TestFunction)(void);
FUNCTION.
The module contains functions to create and run testsuites. For an explanation of what a testsuite is see RBT_TestSuite.
To test your application you create a number of testsuites and add testcases to these.
FUNCTION.
Create a new testcase and add it to a testsuite. In this case the testcase only has a testfunction and no setup and teardown function.
This is a macro that points to RBT_AddCase_Func().
INPUTS.
suite -- the testsuite the testcase is to be added to.
test_function -- the testfunction to create the testcase with.
SOURCE.
#define RBT_AddCase( suite, test_function ) \ RBT_AddCase_Func( suite, #test_function, 0, test_function, 0 )
FUNCTION.
Create a new testcase and add it to a testsuite. In this case the testcase is a full testcase with a teardown and setup function. To add a testcase without a teardown and setup function use RBT_AddCase().
This is a macro that points to RBT_AddCase_Func().
INPUTS.
suite -- the testsuite the testcase is to be added to.
setup_function -- the setup function to create the testcase with.
test_function -- the testfunction
teardown_function -- the teardown function
SOURCE.
#define RBT_AddFullCase( suite, setup_function, test_function, teardown_function ) \ RBT_AddCase_Func( suite, #test_function, setup_function, test_function, teardown_function )
FUNCTION.
Create a new testsuite to which testcases can be added.
INPUTS.
id -- a unique ID for this testsuite.
RETURN VALUE.
A pointer to a newly created testsuite.
SOURCE.
RBT_TestSuite* RBT_CreateSuite( char* id ) { RBT_TestSuite* new_testsuite = malloc( sizeof( RBT_TestSuite ) ); new_testsuite->id = strdup( id ); new_testsuite->first_testcase = 0; new_testsuite->last_testcase = 0; new_testsuite->next = 0; if( first_testsuite ) { last_testsuite->next = new_testsuite; last_testsuite = new_testsuite; } else { first_testsuite = new_testsuite; last_testsuite = new_testsuite; } return new_testsuite; }
FUNCTION.
A testsuite is a collection of testcases. Usually you have one of more testsuites to test your application.
ATTRIBUTES.
first_testcase -- the first testcase in a linked list of testcases.
last_testcase -- the last testcase in a linked list of testcases. Having this makes it easier to store the testcases in the same order they are added.
SOURCE.
typedef struct RBT_TestSuite { struct RBT_TestSuite* next; char* id; RBT_TestCase* first_testcase; RBT_TestCase* last_testcase; } RBT_TestSuite;