add pvUnitTest.h

This commit is contained in:
Michael Davidsaver
2017-07-06 17:05:30 +02:00
parent a0210af5c6
commit 919bc0138a
8 changed files with 246 additions and 2 deletions

View File

@ -11,4 +11,7 @@ src_DEPEND_DIRS = configure
DIRS += testApp
testApp_DEPEND_DIRS = src
DIRS += examples
examples_DEPEND_DIRS = src
include $(TOP)/configure/RULES_TOP

View File

@ -835,14 +835,14 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH =
EXAMPLE_PATH = ../examples
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS = *
EXAMPLE_PATTERNS = *.cpp
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands

14
examples/Makefile Normal file
View File

@ -0,0 +1,14 @@
# Makefile for the examples
# make sure they compile
TOP = ..
include $(TOP)/configure/CONFIG
PROD_LIBS += pvData Com
TESTPROD_HOST += unittest
unittest_SRCS += unittest.cpp
include $(TOP)/configure/RULES

30
examples/unittest.cpp Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* c++ unittest skeleton */
#include <stdexcept>
#include <testMain.h>
#include <pv/pvUnitTest.h>
#include <pv/epicsException.h>
namespace {
void testCase1() {
testEqual(1+1, 2);
}
} // namespace
MAIN(testUnitTest)
{
testPlan(1);
try {
testCase1();
}catch(std::exception& e){
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
testAbort("Unhandled exception: %s", e.what());
}
return testDone();
}

View File

@ -25,6 +25,7 @@ INC += pv/typeCast.h
INC += pv/sharedVector.h
INC += pv/templateMeta.h
INC += pv/current_function.h
INC += pv/pvUnitTest.h
LIBSRCS += byteBuffer.cpp
LIBSRCS += bitSet.cpp

141
src/misc/pv/pvUnitTest.h Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef PVUNITTEST_H
#define PVUNITTEST_H
#include <sstream>
#include <typeinfo>
#include <epicsUnitTest.h>
#include <pv/sharedPtr.h>
#include <pv/epicsException.h>
#include <pv/pvData.h>
namespace detail {
template<class C, void (C::*M)()>
void test_method(const char *kname, const char *mname)
{
try {
testDiag("------- %s::%s --------", kname, mname);
C inst;
(inst.*M)();
} catch(std::exception& e) {
PRINT_EXCEPTION(e);
testAbort("unexpected exception: %s", e.what());
}
}
class testPassx
{
std::ostringstream strm;
bool pass, alive;
public:
explicit testPassx(bool r) :pass(r), alive(true) {}
~testPassx() {
if(alive)
testOk(pass, "%s", strm.str().c_str());
}
template<typename T>
inline testPassx& operator<<(T v) {
strm<<v;
return *this;
}
// move ctor masquerading as copy ctor
testPassx(testPassx& o) :strm(o.strm.str()), pass(o.pass), alive(o.alive) { strm.seekp(0, std::ios_base::end); o.alive = false; }
private:
testPassx& operator=(const testPassx&);
};
template<typename LHS, typename RHS>
inline testPassx testEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r)
{
return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
}
}//namespace detail
/** @defgroup testhelpers Unit testing helpers
*
* Helper functions for writing unit tests.
*
@include unittest.cpp
*
* @{
*/
/** Run a class method as a test.
*
* Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
* Thus constructor and destructor can be used for common test setup and tear down.
@code
struct MyTest {
MyTest() { } // setup
~MyTest() { } // tear down
void test1() {}
void test2() {}
};
MAIN(somename) {
testPlan(0);
TEST_METHOD(MyTest, test1)
TEST_METHOD(MyTest, test2)
return testDone();
}
@endcode
*/
#define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
/** Compare equality. print left and right hand values and expression strings
*
@code
int x=5;
testEqual(x, 5);
// prints "ok 1 - x (5) == 5 (5)\n"
testEqual(x, 6)<<" oops";
// prints "not ok 1 - x (5) == 6 (6) oops\n"
@endcode
*/
#define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
/** Pass/fail from boolean
*
@code
bool y=true;
testTrue(y);
// prints "ok 1 - y\n"
testTrue(!y)<<" oops";
// prints "not ok 1 - !y oops\n"
@endcode
*/
#define testTrue(B) ::detail::testPassx(!!(B))<<#B
/** Compare value of PVStructure field
*
@code
PVStructurePtr x(.....);
testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
@endcode
*/
template<typename PVD>
::detail::testPassx
testFieldEqual(const std::tr1::shared_ptr<epics::pvData::PVStructure>& val, const char *name, typename PVD::value_type expect)
{
if(!val) {
return ::detail::testPassx(false)<<" null structure pointer";
}
typename PVD::shared_pointer fval(val->getSubField<PVD>(name));
if(!fval) {
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
} else {
typename PVD::value_type actual(fval->get());
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
}
}
/** @} */
#endif // PVUNITTEST_H

View File

@ -73,3 +73,7 @@ TESTPROD_HOST += testTypeCast
testTypeCast_SRCS += testTypeCast.cpp
testHarness_SRCS += testTypeCast.cpp
TESTS += testTypeCast
TESTPROD_HOST += testUnitTest
testUnitTest_SRCS += testUnitTest.cpp
TESTS += testUnitTest

View File

@ -0,0 +1,51 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <stdexcept>
#include <testMain.h>
#include <pv/pvUnitTest.h>
#include <pv/valueBuilder.h>
namespace pvd = epics::pvData;
namespace {
struct MyTest {
MyTest() { testPass("MyTest::MyTest"); }
~MyTest() { testPass("MyTest::~MyTest"); }
void test1() { testPass("test1"); }
void test2() { testPass("test2"); }
};
}//namespace
MAIN(testUnitTest)
{
testPlan(0);
try {
TEST_METHOD(MyTest, test1);
TEST_METHOD(MyTest, test2);
{
std::string x("hello");
testEqual(x, "hello");
testEqual(x, "hello")<<" Extra";
}
pvd::PVStructurePtr S(pvd::ValueBuilder()
.addNested("alarm")
.add<pvd::pvInt>("severity", 1)
.add<pvd::pvString>("message", "hello")
.endNested()
.buildPVStructure());
testFieldEqual<pvd::PVInt>(S, "alarm.severity", 1);
testFieldEqual<pvd::PVString>(S, "alarm.message", "hello")<<" More";
}catch(std::exception& e){
testAbort("Unhandled exception: %s", e.what());
}
return testDone();
}