ROBOTester 0.0.6 User Manual

Frans Slothouber

July 2007


Table of Contents

1. Preface
2. Installing ROBOTester
3. Example
3.1. Building your tester
3.2. Creating testcases
3.3. Running your tests
4. Aplication Programming Interface Documentation
4.1. ROBOTester/librobotester

1. Preface

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.

2. Installing ROBOTester

ROBOTester can be installed as follows:

./configure
make

This build a library called librobotester.a.

3. Example

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

3.1. Building your tester

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>

3.2. Creating testcases

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.

3.3. Running your tests

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.

4. Aplication Programming Interface Documentation

The following sections document the library functions themself.

4.1. ROBOTester/librobotester

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:

  • r_ok() -- assert an expression is non-zero.

  • r_notok() -- assert an expression is zero.

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:

4.1.1. librobotester/Assert

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

4.1.1.1. Assert/r_notok

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 )
4.1.1.2. Assert/r_ok

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 )

4.1.2. librobotester/RBT_Testit

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;
}

4.1.3. librobotester/Testcase

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.

4.1.3.1. Testcase/RBT_TestCase

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;
4.1.3.2. Testcase/SetupFunction

FUNCTION. 

The type of the setup function. See TestFunction.

SOURCE. 

typedef void (*SetupFunction)(void);
4.1.3.3. Testcase/TearDownFunction

FUNCTION. 

The type of the teardown function. See TestFunction.

SOURCE. 

typedef void (*TearDownFunction)(void);
4.1.3.4. Testcase/TestFunction

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);

4.1.4. librobotester/TestSuite

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.

4.1.4.1. TestSuite/RBT_AddCase

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 )
4.1.4.2. TestSuite/RBT_AddFullCase

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 )
4.1.4.3. TestSuite/RBT_CreateSuite

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;
}
4.1.4.4. TestSuite/RBT_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;