PVData C++  8.0.6-dev
pvUnitTest.h
1 /*
2  * Copyright information and license terms for this software can be
3  * found in the file LICENSE that is included with the distribution
4  */
5 #ifndef PVUNITTEST_H
6 #define PVUNITTEST_H
7 
8 #include <sstream>
9 #include <typeinfo>
10 
11 #include <epicsUnitTest.h>
12 
13 #include <pv/sharedPtr.h>
14 #include <pv/epicsException.h>
15 #include <pv/pvData.h>
16 
17 namespace detail {
18 
19 template<class C, void (C::*M)()>
20 void test_method(const char *kname, const char *mname)
21 {
22  try {
23  testDiag("------- %s::%s --------", kname, mname);
24  C inst;
25  (inst.*M)();
26  } catch(std::exception& e) {
27  PRINT_EXCEPTION(e);
28  testAbort("unexpected exception: %s", e.what());
29  }
30 }
31 
32 class epicsShareClass testPassx
33 {
34  std::ostringstream strm;
35  const bool dotest, pass;
36  bool alive;
37 public:
38  testPassx() :dotest(false), pass(false), alive(true) {}
39  explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
40  ~testPassx();
41  template<typename T>
42  inline testPassx& operator<<(const T& v) {
43  strm<<v;
44  return *this;
45  }
46 
47  // allow testPassx to be returned
48  // move ctor masquerading as copy ctor
49  testPassx(testPassx& o);
50 private:
51  testPassx& operator=(const testPassx&);
52 };
53 
54 template<typename LHS, typename RHS>
55 inline testPassx testEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
56 {
57  return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
58 }
59 
60 template<typename LHS, typename RHS>
61 inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
62 {
63  return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
64 }
65 
66 }//namespace detail
67 
68 /** @defgroup testhelpers Unit testing helpers
69  *
70  * Helper functions for writing unit tests.
71  *
72  @include unittest.cpp
73  *
74  * @{
75  */
76 
77 /** Run a class method as a test.
78  *
79  * Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
80  * Thus constructor and destructor can be used for common test setup and tear down.
81  @code
82  namespace { // anon
83  struct MyTest {
84  MyTest() { } // setup
85  ~MyTest() { } // tear down
86  void test1() {}
87  void test2() {}
88  };
89  } // namespace anon
90  MAIN(somename) {
91  testPlan(0);
92  TEST_METHOD(MyTest, test1)
93  TEST_METHOD(MyTest, test2)
94  return testDone();
95  }
96  @endcode
97  */
98 #define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
99 
100 /** Compare equality. print left and right hand values and expression strings
101  *
102  @code
103  int x=5;
104  testEqual(x, 5);
105  // prints "ok 1 - x (5) == 5 (5)\n"
106  testEqual(x, 6)<<" oops";
107  // prints "not ok 1 - x (5) == 6 (6) oops\n"
108  @endcode
109  */
110 #define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
111 
112 #define testNotEqual(LHS, RHS) ::detail::testNotEqualx(#LHS, #RHS, LHS, RHS)
113 
114 /** Pass/fail from boolean
115  *
116  @code
117  bool y=true;
118  testTrue(y);
119  // prints "ok 1 - y\n"
120  testTrue(!y)<<" oops";
121  // prints "not ok 1 - !y oops\n"
122  @endcode
123  */
124 #define testTrue(B) ::detail::testPassx(!!(B))<<#B
125 
126 /** Test that a given block throws an exception
127  *
128  @code
129  testThrows(std::runtime_error, somefunc(5))
130  @endcode
131  */
132 #define testThrows(EXC, CODE) try{ CODE; testFail("unexpected success of " #CODE); }catch(EXC& e){testPass("catch expected exception: %s", e.what());}
133 
134 /** Print test output w/o testing
135  *
136  @code
137  testShow()<<"Foo";
138  @endcode
139  */
140 #define testShow() ::detail::testPassx()
141 
142 /** Compare value of PVStructure field
143  *
144  @code
145  PVStructurePtr x(.....);
146  testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
147  @endcode
148  */
149 template<typename PVD>
150 ::detail::testPassx
151 testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::value_type expect)
152 {
153  if(!val) {
154  return ::detail::testPassx(false)<<" null structure pointer";
155  }
156  typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
157  if(!fval) {
158  epics::pvData::PVUnion::const_shared_pointer uval(val->getSubField<epics::pvData::PVUnion>(name));
159  if(uval)
160  fval = uval->get<PVD>();
161  }
162  if(!fval) {
163  return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
164  } else {
165  typename PVD::value_type actual(fval->get());
166  return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
167  }
168 }
169 
170 template<typename PVD>
171 ::detail::testPassx
172 testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::const_svector expect)
173 {
174  if(!val) {
175  return ::detail::testPassx(false)<<" null structure pointer";
176  }
177  typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
178  if(!fval) {
179  return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
180  } else {
181  typename PVD::const_svector actual(fval->view());
182  return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
183  }
184 }
185 
186 /** @} */
187 
188 #endif // PVUNITTEST_H
#define PRINT_EXCEPTION(EI)
::detail::testPassx testFieldEqual(const std::tr1::shared_ptr< const epics::pvData::PVStructure > &val, const char *name, typename PVD::value_type expect)
Definition: pvUnitTest.h:151