This is Unofficial EPICS BASE Doxygen Site
epicsUnitTest.h File Reference

Unit test routines. More...

#include <stdarg.h>
#include "compilerDependencies.h"
#include "libComAPI.h"
+ Include dependency graph for epicsUnitTest.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

LIBCOM_API void testPlan (int tests)
 Declare the test plan, required. More...
 
LIBCOM_API void LIBCOM_API int testDiag (const char *fmt,...) EPICS_PRINTF_STYLE(1
 Output additional diagnostics. More...
 
LIBCOM_API void LIBCOM_API int LIBCOM_API int testDone (void)
 Mark the end of testing. More...
 
LIBCOM_API int testImpreciseTiming (void)
 Return non-zero in shared/oversubscribed testing envrionments. More...
 
Missing or Failing Tests

Routines for handling special situations.

LIBCOM_API void testSkip (int skip, const char *why)
 Place-holders for tests that can't be run. More...
 
LIBCOM_API void testTodoBegin (const char *why)
 Mark the start of a group of tests that are expected to fail. More...
 
LIBCOM_API void testTodoEnd (void)
 Mark the end of a failing test group. More...
 
LIBCOM_API void testAbort (const char *fmt,...) EPICS_PRINTF_STYLE(1
 Stop testing, program cannot continue. More...
 

Announcing Test Results

Routines that declare individual test results.

#define testOk1(cond)   testOk(cond, "%s", #cond)
 Test result using condition as description. More...
 
LIBCOM_API int testOk (int pass, const char *fmt,...) EPICS_PRINTF_STYLE(2
 Test result with printf-style description. More...
 
LIBCOM_API int testOkV (int pass, const char *fmt, va_list pvar)
 Test result with var-args description. More...
 
LIBCOM_API void testPass (const char *fmt,...) EPICS_PRINTF_STYLE(1
 Passing test result with printf-style description. More...
 
LIBCOM_API void LIBCOM_API void testFail (const char *fmt,...) EPICS_PRINTF_STYLE(1
 Failing test result with printf-style description. More...
 

Test Harness for Embedded OSs

These routines are used to create a test-harness that can run multiple test programs, collect their names and results, then display a summary at the end of testing.

#define runTest(func)   runTestFunc(#func, func)
 Run a test program. More...
 
#define testHarnessDone()   testHarnessExit(0)
 Declare all test programs finished. More...
 
typedef int(* TESTFUNC) (void)
 
LIBCOM_API void testHarness (void)
 Initialize test harness. More...
 
LIBCOM_API void testHarnessExit (void *dummy)
 
LIBCOM_API void runTestFunc (const char *name, TESTFUNC func)
 

Detailed Description

Unit test routines.

Author
Andrew Johnson

The unit test routines make it easy for a test program to generate output that is compatible with the Test Anything Protocol and can thus be used with Perl's automated Test::Harness as well as generating human-readable output. The routines detect whether they are being run automatically and print a summary of the results at the end if not.

A test program starts with a call to testPlan(), announcing how many tests are to be conducted. If this number is not known a value of zero can be used during development, but it is recommended that the correct value be substituted after the test program has been completed.

Individual test results are reported using any of testOk(), testOk1(), testOkV(), testPass() or testFail(). The testOk() call takes and also returns a logical pass/fail result (zero means failure, any other value is success) and a printf-like format string and arguments which describe the test. The convenience macro testOk1() is provided which stringifies its single condition argument, reducing the effort needed when writing test programs. The individual testPass() and testFail() routines can be used when the test program takes a different path on success than on failure, but one or other must always be called for any particular test. The testOkV() routine is a varargs form of testOk() included for internal purposes which may prove useful in some cases.

If some program condition or failure makes it impossible to run some tests, the testSkip() routine can be used to indicate how many tests are being omitted from the run, thus keeping the test counts correct; the constant string why is displayed as an explanation to the user (this string is not printf-like).

If some tests are expected to fail because functionality in the module under test has not yet been fully implemented, these tests may still be executed, wrapped between calls to testTodoBegin() and testTodoEnd(). testTodoBegin() takes a constant string indicating why these tests are not expected to succeed. This modifies the counting of the results so the wrapped tests will not be recorded as failures.

Additional information can be supplied using the testDiag() routine, which displays the relevent information as a comment in the result output. None of the printable strings passed to any testXxx() routine should contain a newline '
' character, newlines will be added by the test routines as part of the Test Anything Protocol. For multiple lines of diagnostic output, call testDiag() as many times as necessary.

If at any time the test program is unable to continue for some catastrophic reason, calling testAbort() with an appropriate message will ensure that the test harness understands this. testAbort() does not return, but calls the ANSI C routine abort() to cause the program to stop immediately.

After all of the tests have been completed, the return value from testDone() can be used as the return status code from the program's main() routine.

On vxWorks and RTEMS, an alternative test harness can be used to run a series of tests in order and summarize the results from them all at the end just like the Perl harness does. The routine testHarness() is called once at the beginning of the test harness program. Each test program is run by passing its main routine name to the runTest() macro which expands into a call to the runTestFunc() routine. The last test program or the harness program itself must finish by calling testHarnessDone() which triggers the summary mechanism to generate its result outputs (from an epicsAtExit() callback routine).

IOC Testing

Some tests require the context of an IOC to be run. This conflicts with the idea of running multiple tests within a test harness, as iocInit() is only allowed to be called once, and some parts of the full IOC (e.g. the rsrv CA server) can not be shut down cleanly. The function iocBuildIsolated() allows to start an IOC without its Channel Access parts, so that it can be shutdown quite cleanly using iocShutdown(). This feature is only intended to be used from test programs, do not use it on productional IOCs. After building the IOC using iocBuildIsolated() or iocBuild(), it has to be started by calling iocRun(). The suggested call sequence in a test program that needs to run the IOC without Channel Access is:

#include "iocInit.h"
MAIN(iocTest)
{
testdbPrepare();
testdbReadDatabase("<dbdname>.dbd", 0, 0);
<dbdname>_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("some.db", 0, 0);
... test code before iocInit(). eg. dbGetString() ...
testIocInitOk();
... test code with IOC running. eg. dbGet()
testIocShutdownOk();
testdbCleanup();
return testDone();
}

The part from iocBuildIsolated() to iocShutdown() can be repeated to execute multiple tests within one executable or harness.

To make it easier to create a single test program that can be built for both the embedded and workstation operating system harnesses, the header file testMain.h provides a convenience macro MAIN() that adjusts the name of the test program according to the platform it is running on: main() on workstations and a regular function name on embedded systems.

Example

The following is a simple example of a test program using the epicsUnitTest routines:

#include <math.h>
#include "epicsUnitTest.h"
#include "testMain.h"
MAIN(mathTest)
{
testOk(sin(0.0) == 0.0, "Sine starts");
testOk(cos(0.0) == 1.0, "Cosine continues");
if (!testOk1(M_PI == 4.0*atan(1.0)))
testDiag("4 * atan(1) = %g", 4.0 * atan(1.0));
return testDone();
}

The output from running the above program looks like this:

1..3
ok 1 - Sine starts
ok 2 - Cosine continues
ok 3 - M_PI == 4.0*atan(1.0)
Results
=======
Tests: 3
Passed: 3 = 100%

Definition in file epicsUnitTest.h.

Macro Definition Documentation

#define runTest (   func)    runTestFunc(#func, func)

Run a test program.

Parameters
funcName of the test program.

Definition at line 269 of file epicsUnitTest.h.

#define testHarnessDone ( )    testHarnessExit(0)

Declare all test programs finished.

Definition at line 272 of file epicsUnitTest.h.

#define testOk1 (   cond)    testOk(cond, "%s", #cond)

Test result using condition as description.

Parameters
condCondition to be evaluated and displayed.
Returns
The value of cond.

Definition at line 182 of file epicsUnitTest.h.

Typedef Documentation

typedef int(* TESTFUNC) (void)

Definition at line 259 of file epicsUnitTest.h.

Function Documentation

LIBCOM_API void runTestFunc ( const char *  name,
TESTFUNC  func 
)

Definition at line 310 of file epicsUnitTest.c.

310  {
311  printf("\n***** %s *****\n", name);
312  testing = name;
313  func(); /* May not return */
314  epicsThreadSleep(1.0);
315 }
#define printf
Definition: epicsStdio.h:41
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
const char * testing
Definition: epicsUnitTest.c:58
LIBCOM_API void testAbort ( const char *  fmt,
  ... 
)

Stop testing, program cannot continue.

Parameters
fmtA printf-style format string giving the reason for stopping.
...Any parameters required for the format string.
LIBCOM_API void LIBCOM_API int testDiag ( const char *  fmt,
  ... 
)

Output additional diagnostics.

Parameters
fmtA printf-style format string containing diagnostic information.
...Any parameters required for the format string.
LIBCOM_API void LIBCOM_API int LIBCOM_API int testDone ( void  )

Mark the end of testing.

Definition at line 209 of file epicsUnitTest.c.

209  {
210  int status = 0;
211 
212  epicsMutexMustLock(testLock);
213  if (perlHarness) {
214  if (!planned) printf("1..%d\n", tested);
215  } else {
216  if (planned && tested > planned) {
217  printf("\nRan %d tests but only planned for %d!\n", tested, planned);
218  status = 2;
219  } else if (planned && tested < planned) {
220  printf("\nPlanned %d tests but only ran %d\n", planned, tested);
221  status = 2;
222  }
223  printf("\n Results\n =======\n Tests: %-3d\n", tested);
224  if (tested) {
225  testResult("Passed", passed);
226  if (bonus) testResult("Todo Passes", bonus);
227  if (failed) {
228  testResult("Failed", failed);
229  status = 1;
230  }
231  if (skipped) testResult("Skipped", skipped);
232  }
233  }
234  if (Harness) {
235  if (failed) {
236  testFailure *fault = callocMustSucceed(1, sizeof(testFailure),
237  "testDone calloc");
238  fault->name = testing;
239  fault->tests = tested;
240  fault->failures = failed;
241  fault->skips = skipped;
242  ellAdd(&faults, &fault->node);
243  }
244  Programs++;
245  Tests += tested;
246  }
247  epicsMutexUnlock(testLock);
248  return (status);
249 }
pvd::Status status
#define printf
Definition: epicsStdio.h:41
const char * name
Definition: epicsUnitTest.c:36
ELLLIST faults
Definition: epicsUnitTest.c:57
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
void ellAdd(ELLLIST *pList, ELLNODE *pNode)
Adds a node to the end of a list.
Definition: ellLib.c:24
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
Definition: cantProceed.c:22
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
ELLNODE node
Definition: epicsUnitTest.c:35
const char * testing
Definition: epicsUnitTest.c:58
LIBCOM_API void LIBCOM_API void testFail ( const char *  fmt,
  ... 
)

Failing test result with printf-style description.

Parameters
fmtA printf-style format string describing the test.
...Any parameters required for the format string.
LIBCOM_API void testHarness ( void  )

Initialize test harness.

Definition at line 300 of file epicsUnitTest.c.

300  {
301  epicsThreadOnce(&onceFlag, testOnce, NULL);
303  Harness = 1;
304  Programs = 0;
305  Tests = 0;
306  ellInit(&faults);
308 }
void testHarnessExit(void *dummy)
#define NULL
Definition: catime.c:38
ELLLIST faults
Definition: epicsUnitTest.c:57
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
int epicsStdCall epicsTimeGetCurrent(epicsTimeStamp *pDest)
Get current time into *pDest.
#define ellInit(PLIST)
Initialize a list type.
Definition: ellLib.h:76
#define epicsAtExit(F, A)
Convenience macro to register a function and context value to be run when the process exits...
Definition: epicsExit.h:70
epicsTimeStamp started
Definition: epicsUnitTest.c:52
LIBCOM_API void testHarnessExit ( void *  dummy)

Definition at line 265 of file epicsUnitTest.c.

265  {
266  epicsTimeStamp ended;
267  int Faulty;
268 
269  if (!Harness) return;
270 
271  epicsTimeGetCurrent(&ended);
272 
273  printf("\n\n EPICS Test Harness Results"
274  "\n ==========================\n\n");
275 
276  Faulty = ellCount(&faults);
277  if (!Faulty)
278  printf("All tests successful.\n");
279  else {
280  int Failures = 0;
281  testFailure *f;
282 
283  printf("Failing Program Tests Faults\n"
284  "---------------------------------------\n");
285  while ((f = (testFailure *)ellGet(&faults))) {
286  Failures += f->failures;
287  printf("%-25s %5d %5d\n", f->name, f->tests, f->failures);
288  if (f->skips)
289  printf("%d subtests skipped\n", f->skips);
290  free(f);
291  }
292  printf("\nFailed %d/%d test programs. %d/%d subtests failed.\n",
293  Faulty, Programs, Failures, Tests);
294  }
295 
296  printf("Programs=%d, Tests=%d, %.0f wallclock secs\n\n",
297  Programs, Tests, epicsTimeDiffInSeconds(&ended, &started));
298 }
#define ellCount(PLIST)
Report the number of nodes in a list.
Definition: ellLib.h:84
#define printf
Definition: epicsStdio.h:41
ELLNODE * ellGet(ELLLIST *pList)
Deletes and returns the first node from a list.
Definition: ellLib.c:147
const char * name
Definition: epicsUnitTest.c:36
ELLLIST faults
Definition: epicsUnitTest.c:57
int epicsStdCall epicsTimeGetCurrent(epicsTimeStamp *pDest)
Get current time into *pDest.
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
LIBCOM_API double epicsStdCall epicsTimeDiffInSeconds(const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
Time difference between left and right in seconds.
Definition: epicsTime.cpp:1048
epicsTimeStamp started
Definition: epicsUnitTest.c:52
LIBCOM_API int testImpreciseTiming ( void  )

Return non-zero in shared/oversubscribed testing envrionments.

May be used to testSkip(), or select longer timeouts, for some cases when the test process may be preempted for arbitrarily long times. This is common in shared CI environments.

The environment variable $EPICS_TEST_IMPRECISE_TIMING=YES should be set in by such testing environments.

Definition at line 253 of file epicsUnitTest.c.

254 {
255  if(impreciseTiming==0) {
256  const char* env = getenv("EPICS_TEST_IMPRECISE_TIMING");
257 
258  impreciseTiming = (env && strcmp(env, "YES")==0) ? 1 : -1;
259  }
260  return impreciseTiming>0;
261 }
LIBCOM_API int testOk ( int  pass,
const char *  fmt,
  ... 
)

Test result with printf-style description.

Parameters
passTrue/False value indicating result.
fmtA printf-style format string describing the test.
...Any parameters required for the format string.
Returns
The value of pass.
LIBCOM_API int testOkV ( int  pass,
const char *  fmt,
va_list  pvar 
)

Test result with var-args description.

Parameters
passTrue/False value indicating result.
fmtA printf-style format string describing the test.
pvarA var-args pointer to any parameters for the format string.
Returns
The value of pass.

Definition at line 112 of file epicsUnitTest.c.

112  {
113  const char *result = "not ok";
114  epicsMutexMustLock(testLock);
115  tested++;
116  if (pass) {
117  result += 4; /* skip "not " */
118  passed++;
119  if (todo)
120  bonus++;
121  } else {
122  if (todo)
123  passed++;
124  else
125  failed++;
126  }
127  printf("%s %2d - ", result, tested);
128  vprintf(fmt, pvar);
129  if (todo)
130  printf(" # TODO %s", todo);
131  putchar('\n');
132  fflush(stdout);
133  epicsMutexUnlock(testLock);
134  return pass;
135 }
pvac::PutEvent result
Definition: clientSync.cpp:117
#define printf
Definition: epicsStdio.h:41
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
#define putchar
Definition: epicsStdio.h:51
#define stdout
Definition: epicsStdio.h:30
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
LIBCOM_API void testPass ( const char *  fmt,
  ... 
)

Passing test result with printf-style description.

Parameters
fmtA printf-style format string describing the test.
...Any parameters required for the format string.
Returns
The value of pass.
LIBCOM_API void testPlan ( int  tests)

Declare the test plan, required.

Parameters
testsNumber of tests to be run. May be zero if not known but the test harness then can't tell if the program dies prematurely.

Definition at line 102 of file epicsUnitTest.c.

102  {
103  epicsThreadOnce(&onceFlag, testOnce, NULL);
104  epicsMutexMustLock(testLock);
105  planned = plan;
106  tested = passed = failed = skipped = bonus = 0;
107  todo = NULL;
108  if (plan) printf("1..%d\n", plan);
109  epicsMutexUnlock(testLock);
110 }
#define printf
Definition: epicsStdio.h:41
#define NULL
Definition: catime.c:38
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
LIBCOM_API void testSkip ( int  skip,
const char *  why 
)

Place-holders for tests that can't be run.

Parameters
skipHow many tests are being skipped.
whyReason for skipping these tests.

Definition at line 159 of file epicsUnitTest.c.

159  {
160  epicsMutexMustLock(testLock);
161  while (skip-- > 0) {
162  tested++;
163  passed++;
164  skipped++;
165  printf("ok %2d # SKIP %s\n", tested, why);
166  }
167  fflush(stdout);
168  epicsMutexUnlock(testLock);
169 }
#define printf
Definition: epicsStdio.h:41
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
#define stdout
Definition: epicsStdio.h:30
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
LIBCOM_API void testTodoBegin ( const char *  why)

Mark the start of a group of tests that are expected to fail.

Parameters
whyReason for expected failures.

Definition at line 171 of file epicsUnitTest.c.

171  {
172  epicsMutexMustLock(testLock);
173  todo = why;
174  epicsMutexUnlock(testLock);
175 }
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
LIBCOM_API void testTodoEnd ( void  )

Mark the end of a failing test group.

Definition at line 177 of file epicsUnitTest.c.

177  {
179 }
void testTodoBegin(const char *why)
Mark the start of a group of tests that are expected to fail.
#define NULL
Definition: catime.c:38