Merge changes from 3.15 branch into 7.0
This commit is contained in:
210
modules/libcom/test/epicsThreadClassTest.cpp
Normal file
210
modules/libcom/test/epicsThreadClassTest.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2020 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* epicsThreadClassTest.cpp */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
/* Key to the char's that define the test case actions:
|
||||
*
|
||||
* Upper case letters are for parent thread actions
|
||||
* B - Parent calls thread->start() and waits for child to start
|
||||
* D - Parent deletes thread. This waits for child to return if it hasn't yet
|
||||
* E - Parent calls thread->exitWait(), this may wait for child to return
|
||||
* S - Parent sleeps for SLEEP_TIME seconds
|
||||
* T - Parent sends sync trigger to child (w)
|
||||
* W - Parent waits for sync trigger from child (t)
|
||||
* X - Parent calls thread->exitWait(0)
|
||||
*
|
||||
* Lower case letters are for child thread actions
|
||||
* d - Child deletes thread
|
||||
* e - Child calls thread->exitWait()
|
||||
* r - Child returns
|
||||
* s - Child sleeps for SLEEP_TIME seconds
|
||||
* t - Child sends sync trigger to parent (W)
|
||||
* w - Child waits for sync trigger from child (T)
|
||||
*
|
||||
* Note that it is possible to write test cases that can hang,
|
||||
* segfault, or that trigger errors from thread APIs.
|
||||
*/
|
||||
|
||||
// The test cases
|
||||
|
||||
const char * const cases[] = {
|
||||
// These cases don't start the thread:
|
||||
"D", // Parent deletes thread
|
||||
"ED", // Parent does exitWait(), deletes thread
|
||||
|
||||
// In these cases the parent deletes the thread
|
||||
"BrSD", // Child returns; parent deletes thread
|
||||
"BsDr", // Parent deletes thread; child returns
|
||||
"BrSED", // Child returns; parent does exitWait(), deletes thread
|
||||
"BsErD", // Parent does exitWait(); child returns; parent deletes thread
|
||||
"BsXDr", // Parent does exitWait(0); parent deletes thread; child returns
|
||||
"BwXTDsr", // Parent does exitWait(0); parent deletes thread; child returns
|
||||
// These are currently broken
|
||||
// "BetWSrD", // Child does exitWait(); sync; child returns; parent deletes thread
|
||||
// "BetWsDr", // Child does exitWait(); sync; parent deletes thread; child returns
|
||||
|
||||
// In these cases the child deletes the thread
|
||||
"BdrS", // Child deletes thread, returns
|
||||
"BedrS", // Child does exitWait(), deletes thread, returns
|
||||
"BwXTSdr", // Parent does exitWait(0); sync; child deletes thread, returns
|
||||
|
||||
NULL // Terminator
|
||||
};
|
||||
|
||||
// How long to sleep for while the other thread works
|
||||
#define SLEEP_TIME 1.0
|
||||
|
||||
class threadCase: public epicsThreadRunable {
|
||||
public:
|
||||
threadCase(const char * const tcase);
|
||||
virtual ~threadCase();
|
||||
virtual void run();
|
||||
epicsThread *pthread;
|
||||
epicsEvent startEvt;
|
||||
epicsEvent childEvt;
|
||||
epicsEvent parentEvt;
|
||||
private:
|
||||
const char * const name;
|
||||
};
|
||||
|
||||
threadCase::threadCase(const char * const tcase) :
|
||||
pthread(new epicsThread(*this, tcase,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall))),
|
||||
name(tcase)
|
||||
{
|
||||
testDiag("Constructing test case '%s'", name);
|
||||
}
|
||||
|
||||
threadCase::~threadCase()
|
||||
{
|
||||
testDiag("Destroying test case '%s'", name);
|
||||
}
|
||||
|
||||
void threadCase::run()
|
||||
{
|
||||
testDiag("Child running for '%s'", name);
|
||||
startEvt.signal();
|
||||
|
||||
for (const char * pdo = name;
|
||||
const char tdo = *pdo;
|
||||
pdo++)
|
||||
{
|
||||
switch (tdo)
|
||||
{
|
||||
case 'd':
|
||||
testDiag("'%c': Child deleting epicsThread", tdo);
|
||||
delete pthread;
|
||||
pthread = NULL;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
testDiag("'%c': Child calling exitWait()", tdo);
|
||||
assert(pthread);
|
||||
pthread->exitWait();
|
||||
break;
|
||||
|
||||
case 's':
|
||||
testDiag("'%c': Child sleeping", tdo);
|
||||
epicsThreadSleep(SLEEP_TIME);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
testDiag("'%c': Child sending trigger", tdo);
|
||||
parentEvt.signal();
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
testDiag("'%c': Child awaiting trigger", tdo);
|
||||
childEvt.wait();
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
testDiag("'%c': Child returning", tdo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
testFail("Test case '%s' is missing 'r'", name);
|
||||
}
|
||||
|
||||
MAIN(epicsThreadClassTest)
|
||||
{
|
||||
const int ntests = NELEMENTS(cases);
|
||||
testPlan(ntests - 1); // The last element is the NULL terminator
|
||||
|
||||
for (const char * const * pcase = cases;
|
||||
const char * const tcase = *pcase;
|
||||
pcase++)
|
||||
{
|
||||
testDiag("======= Test case '%s' =======", tcase);
|
||||
threadCase thrCase(tcase);
|
||||
|
||||
for (const char * pdo = tcase;
|
||||
const char tdo = *pdo;
|
||||
pdo++)
|
||||
{
|
||||
switch (tdo)
|
||||
{
|
||||
case 'B':
|
||||
testDiag("'%c': Parent starting child", tdo);
|
||||
assert(thrCase.pthread);
|
||||
thrCase.pthread->start();
|
||||
thrCase.startEvt.wait();
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
testDiag("'%c': Parent deleting epicsThread", tdo);
|
||||
assert(thrCase.pthread);
|
||||
delete thrCase.pthread;
|
||||
thrCase.pthread = NULL;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
testDiag("'%c': Parent calling exitWait()", tdo);
|
||||
assert(thrCase.pthread);
|
||||
thrCase.pthread->exitWait();
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
testDiag("'%c': Parent calling exitWait(0)", tdo);
|
||||
assert(thrCase.pthread);
|
||||
thrCase.pthread->exitWait(0);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
testDiag("'%c': Parent sleeping", tdo);
|
||||
epicsThreadSleep(SLEEP_TIME);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
testDiag("'%c': Parent sending trigger", tdo);
|
||||
thrCase.childEvt.signal();
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
testDiag("'%c': Parent awaiting trigger", tdo);
|
||||
thrCase.parentEvt.wait();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
testPass("Test case '%s' passed", tcase);
|
||||
}
|
||||
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user