This is Unofficial EPICS BASE Doxygen Site
epicsUnitTest.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * EPICS BASE is distributed subject to a Software License Agreement found
5 * in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7 
8 /*
9  * Author: Andrew Johnson
10  *
11  * Unit test module which generates output in the Test Anything Protocol
12  * format. See perldoc Test::Harness for details of output format.
13  *
14  */
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #ifdef _WIN32
21 # include <crtdbg.h>
22 #endif
23 
24 #include "epicsThread.h"
25 #include "epicsMutex.h"
26 #include "epicsUnitTest.h"
27 #include "epicsExit.h"
28 #include "epicsTime.h"
29 #include "ellLib.h"
30 #include "errlog.h"
31 #include "cantProceed.h"
32 #include "epicsStackTrace.h"
33 
34 typedef struct {
36  const char *name;
37  int tests;
38  int failures;
39  int skips;
40 } testFailure;
41 
42 static epicsMutexId testLock = 0;
43 static int perlHarness;
44 static int planned;
45 static int tested;
46 static int passed;
47 static int failed;
48 static int skipped;
49 static int bonus;
50 static const char *todo;
51 
53 static int Harness;
54 static int Programs;
55 static int Tests;
56 
58 const char *testing = NULL;
59 
61 
62 #ifdef _WIN32
63 /*
64  * if we return FALSE, _CrtDbgReport is called to print to file etc
65  * if we return TRUE, we are the only function called
66  */
67 static int testReportHook(int reportType, char *message, int *returnValue)
68 {
69  int nRet = 0;
70  switch (reportType)
71  {
72  case _CRT_ASSERT:
73  case _CRT_ERROR:
75  break;
76 
77  default:
78  break;
79  }
80  if (returnValue)
81  {
82  *returnValue = 0;
83  }
84  return nRet;
85 }
86 #endif
87 
88 static void testOnce(void *dummy) {
89  testLock = epicsMutexMustCreate();
90  perlHarness = (getenv("HARNESS_ACTIVE") != NULL);
91 #ifdef _WIN32
92  _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE |_CRTDBG_MODE_DEBUG );
93  _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
94  _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE |_CRTDBG_MODE_DEBUG );
95  _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
96  _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE |_CRTDBG_MODE_DEBUG );
97  _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
98  _CrtSetReportHook2( _CRT_RPTHOOK_INSTALL, testReportHook );
99 #endif
100 }
101 
102 void testPlan(int plan) {
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 }
111 
112 int testOkV(int pass, const char *fmt, va_list pvar) {
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 }
136 
137 int testOk(int pass, const char *fmt, ...) {
138  va_list pvar;
139  va_start(pvar, fmt);
140  testOkV(pass, fmt, pvar);
141  va_end(pvar);
142  return pass;
143 }
144 
145 void testPass(const char *fmt, ...) {
146  va_list pvar;
147  va_start(pvar, fmt);
148  testOkV(1, fmt, pvar);
149  va_end(pvar);
150 }
151 
152 void testFail(const char *fmt, ...) {
153  va_list pvar;
154  va_start(pvar, fmt);
155  testOkV(0, fmt, pvar);
156  va_end(pvar);
157 }
158 
159 void testSkip(int skip, const char *why) {
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 }
170 
171 void testTodoBegin(const char *why) {
172  epicsMutexMustLock(testLock);
173  todo = why;
174  epicsMutexUnlock(testLock);
175 }
176 
177 void testTodoEnd(void) {
179 }
180 
181 int testDiag(const char *fmt, ...) {
182  va_list pvar;
183  va_start(pvar, fmt);
184  epicsMutexMustLock(testLock);
185  printf("# ");
186  vprintf(fmt, pvar);
187  putchar('\n');
188  fflush(stdout);
189  epicsMutexUnlock(testLock);
190  va_end(pvar);
191  return 0;
192 }
193 
194 void testAbort(const char *fmt, ...) {
195  va_list pvar;
196  va_start(pvar, fmt);
197  printf("Bail out! ");
198  vprintf(fmt, pvar);
199  putchar('\n');
200  fflush(stdout);
201  va_end(pvar);
202  abort();
203 }
204 
205 static void testResult(const char *result, int count) {
206  printf("%12.12s: %3d = %5.2f%%\n", result, count, 100.0 * count / tested);
207 }
208 
209 int testDone(void) {
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 }
250 
251 static int impreciseTiming;
252 
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 }
262 
263 /* Our test harness, for RTEMS and vxWorks */
264 
265 void testHarnessExit(void *dummy) {
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 }
299 
300 void testHarness(void) {
301  epicsThreadOnce(&onceFlag, testOnce, NULL);
303  Harness = 1;
304  Programs = 0;
305  Tests = 0;
306  ellInit(&faults);
307  epicsTimeGetCurrent(&started);
308 }
309 
310 void runTestFunc(const char *name, TESTFUNC func) {
311  printf("\n***** %s *****\n", name);
312  testing = name;
313  func(); /* May not return */
314  epicsThreadSleep(1.0);
315 }
pvac::PutEvent result
Definition: clientSync.cpp:117
#define ellCount(PLIST)
Report the number of nodes in a list.
Definition: ellLib.h:84
void testPlan(int plan)
Declare the test plan, required.
pvd::Status status
void testTodoBegin(const char *why)
Mark the start of a group of tests that are expected to fail.
void testHarnessExit(void *dummy)
void testTodoEnd(void)
Mark the end of a failing test group.
void testPass(const char *fmt,...)
#define printf
Definition: epicsStdio.h:41
ELLNODE * ellGet(ELLLIST *pList)
Deletes and returns the first node from a list.
Definition: ellLib.c:147
#define epicsMutexMustCreate()
Create an epicsMutex semaphore for use from C code.
Definition: epicsMutex.h:179
int testDiag(const char *fmt,...)
#define NULL
Definition: catime.c:38
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 testSkip(int skip, const char *why)
Place-holders for tests that can&#39;t be run.
A doubly-linked list library.
void ellAdd(ELLLIST *pList, ELLNODE *pNode)
Adds a node to the end of a list.
Definition: ellLib.c:24
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
void testAbort(const char *fmt,...)
int testDone(void)
Mark the end of testing.
APIs for the epicsMutex mutual exclusion semaphore.
void testHarness(void)
Initialize test harness.
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
#define putchar
Definition: epicsStdio.h:51
int epicsStdCall epicsTimeGetCurrent(epicsTimeStamp *pDest)
Get current time into *pDest.
int testImpreciseTiming(void)
Return non-zero in shared/oversubscribed testing envrionments.
Extended replacement for the Posix exit and atexit routines.
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
Definition: cantProceed.c:22
#define stdout
Definition: epicsStdio.h:30
List node type.
Definition: ellLib.h:45
void epicsStackTrace(void)
epics::pvData::PVStructurePtr dummy
Definition: pvAccess.cpp:72
Unit test routines.
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
#define ellInit(PLIST)
Initialize a list type.
Definition: ellLib.h:76
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
void testFail(const char *fmt,...)
LIBCOM_API double epicsStdCall epicsTimeDiffInSeconds(const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
Time difference between left and right in seconds.
Definition: epicsTime.cpp:1048
Routines for code that can&#39;t continue or return after an error.
EPICS time-stamps (epicsTimeStamp), epicsTime C++ class and C functions for handling wall-clock times...
#define epicsAtExit(F, A)
Convenience macro to register a function and context value to be run when the process exits...
Definition: epicsExit.h:70
List header type.
Definition: ellLib.h:56
C++ and C descriptions for a thread.
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
epicsTimeStamp started
Definition: epicsUnitTest.c:52
int(* TESTFUNC)(void)
int testOk(int pass, const char *fmt,...)
void runTestFunc(const char *name, TESTFUNC func)
ELLNODE node
Definition: epicsUnitTest.c:35
int testOkV(int pass, const char *fmt, va_list pvar)
Test result with var-args description.
const char * testing
Definition: epicsUnitTest.c:58