Simbody  3.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Testing.h
Go to the documentation of this file.
1 #ifndef SimTK_SimTKCOMMON_TESTING_H_
2 #define SimTK_SimTKCOMMON_TESTING_H_
3 
4 /* -------------------------------------------------------------------------- *
5  * Simbody(tm): SimTKcommon *
6  * -------------------------------------------------------------------------- *
7  * This is part of the SimTK biosimulation toolkit originating from *
8  * Simbios, the NIH National Center for Physics-Based Simulation of *
9  * Biological Structures at Stanford, funded under the NIH Roadmap for *
10  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. *
11  * *
12  * Portions copyright (c) 2009-12 Stanford University and the Authors. *
13  * Authors: Michael Sherman *
14  * Contributors: *
15  * *
16  * Licensed under the Apache License, Version 2.0 (the "License"); you may *
17  * not use this file except in compliance with the License. You may obtain a *
18  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
19  * *
20  * Unless required by applicable law or agreed to in writing, software *
21  * distributed under the License is distributed on an "AS IS" BASIS, *
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
23  * See the License for the specific language governing permissions and *
24  * limitations under the License. *
25  * -------------------------------------------------------------------------- */
26 
27 #include "SimTKcommon/basics.h"
28 #include "SimTKcommon/Simmatrix.h"
30 
31 #include <cmath>
32 #include <ctime>
33 #include <algorithm>
34 #include <iostream>
35 
41 namespace SimTK {
42 
155 
156 
157 
158 
159 class Test {
160 public:
161  class Subtest;
162  Test(const std::string& name) : testName(name)
163  {
164  std::clog << "Starting test " << testName << " ...\n";
165  startTime = std::clock();
166  }
167  ~Test() {
168  std::clog << "Done. " << testName << " time: "
169  << 1000*(std::clock()-startTime)/CLOCKS_PER_SEC << "ms.\n";
170  }
171 
172  template <class T>
173  static double defTol() {return (double)NTraits<typename CNT<T>::Precision>::getSignificant();}
174 
175  // For dissimilar types, the default tolerance is the narrowest of the two.
176  template <class T1, class T2>
177  static double defTol2() {return std::max(defTol<T1>(), defTol<T2>());}
178 
179  // Scale by the magnitude of the quantities being compared, so that we don't
180  // ask for unreasonable precision. For magnitudes near zero, we'll be satisfied
181  // if both are very small without demanding that they must also be relatively
182  // close. That is, we use a relative tolerance for big numbers and an absolute
183  // tolerance for small ones.
184  static bool numericallyEqual(float v1, float v2, int n, double tol=defTol<float>()) {
185  const float scale = n*std::max(std::max(std::abs(v1), std::abs(v2)), 1.0f);
186  return std::abs(v1-v2) < scale*(float)tol;
187  }
188  static bool numericallyEqual(double v1, double v2, int n, double tol=defTol<double>()) {
189  const double scale = n*std::max(std::max(std::abs(v1), std::abs(v2)), 1.0);
190  return std::abs(v1-v2) < scale*(double)tol;
191  }
192  static bool numericallyEqual(long double v1, long double v2, int n, double tol=defTol<long double>()) {
193  const long double scale = n*std::max(std::max(std::abs(v1), std::abs(v2)), 1.0l);
194  return std::abs(v1-v2) < scale*(long double)tol;
195  }
196 
197  // For integers we ignore tolerance.
198  static bool numericallyEqual(int i1, int i2, int n, double tol=0) {return i1==i2;}
199  static bool numericallyEqual(unsigned u1, unsigned u2, int n, double tol=0) {return u1==u2;}
200 
201  // Mixed floating types use default tolerance for the narrower type.
202  static bool numericallyEqual(float v1, double v2, int n, double tol=defTol<float>())
203  { return numericallyEqual((double)v1, v2, n, tol); }
204  static bool numericallyEqual(double v1, float v2, int n, double tol=defTol<float>())
205  { return numericallyEqual(v1, (double)v2, n, tol); }
206  static bool numericallyEqual(float v1, long double v2, int n, double tol=defTol<float>())
207  { return numericallyEqual((long double)v1, v2, n, tol); }
208  static bool numericallyEqual(long double v1, float v2, int n, double tol=defTol<float>())
209  { return numericallyEqual(v1, (long double)v2, n, tol); }
210  static bool numericallyEqual(double v1, long double v2, int n, double tol=defTol<double>())
211  { return numericallyEqual((long double)v1, v2, n, tol); }
212  static bool numericallyEqual(long double v1, double v2, int n, double tol=defTol<double>())
213  { return numericallyEqual(v1, (long double)v2, n, tol); }
214 
215  // Mixed int/floating just upgrades int to floating type.
216  static bool numericallyEqual(int i1, float f2, int n, double tol=defTol<float>())
217  { return numericallyEqual((float)i1,f2,n,tol); }
218  static bool numericallyEqual(float f1, int i2, int n, double tol=defTol<float>())
219  { return numericallyEqual(f1,(float)i2,n,tol); }
220  static bool numericallyEqual(unsigned i1, float f2, int n, double tol=defTol<float>())
221  { return numericallyEqual((float)i1,f2,n,tol); }
222  static bool numericallyEqual(float f1, unsigned i2, int n, double tol=defTol<float>())
223  { return numericallyEqual(f1,(float)i2,n,tol); }
224  static bool numericallyEqual(int i1, double f2, int n, double tol=defTol<double>())
225  { return numericallyEqual((double)i1,f2,n,tol); }
226  static bool numericallyEqual(double f1, int i2, int n, double tol=defTol<double>())
227  { return numericallyEqual(f1,(double)i2,n,tol); }
228  static bool numericallyEqual(unsigned i1, double f2, int n, double tol=defTol<double>())
229  { return numericallyEqual((double)i1,f2,n,tol); }
230  static bool numericallyEqual(double f1, unsigned i2, int n, double tol=defTol<double>())
231  { return numericallyEqual(f1,(double)i2,n,tol); }
232  static bool numericallyEqual(int i1, long double f2, int n, double tol=defTol<long double>())
233  { return numericallyEqual((long double)i1,f2,n,tol); }
234  static bool numericallyEqual(long double f1, int i2, int n, double tol=defTol<long double>())
235  { return numericallyEqual(f1,(long double)i2,n,tol); }
236  static bool numericallyEqual(unsigned i1, long double f2, int n, double tol=defTol<long double>())
237  { return numericallyEqual((long double)i1,f2,n,tol); }
238  static bool numericallyEqual(long double f1, unsigned i2, int n, double tol=defTol<long double>())
239  { return numericallyEqual(f1,(long double)i2,n,tol); }
240 
241  template <class P>
242  static bool numericallyEqual(const std::complex<P>& v1, const std::complex<P>& v2, int n, double tol=defTol<P>()) {
243  return numericallyEqual(v1.real(), v2.real(), n, tol)
244  && numericallyEqual(v1.imag(), v2.imag(), n, tol);
245  }
246  template <class P>
247  static bool numericallyEqual(const conjugate<P>& v1, const conjugate<P>& v2, int n, double tol=defTol<P>()) {
248  return numericallyEqual(v1.real(), v2.real(), n, tol)
249  && numericallyEqual(v1.imag(), v2.imag(), n, tol);
250  }
251  template <class P>
252  static bool numericallyEqual(const std::complex<P>& v1, const conjugate<P>& v2, int n, double tol=defTol<P>()) {
253  return numericallyEqual(v1.real(), v2.real(), n, tol)
254  && numericallyEqual(v1.imag(), v2.imag(), n, tol);
255  }
256  template <class P>
257  static bool numericallyEqual(const conjugate<P>& v1, const std::complex<P>& v2, int n, double tol=defTol<P>()) {
258  return numericallyEqual(v1.real(), v2.real(), n, tol)
259  && numericallyEqual(v1.imag(), v2.imag(), n, tol);
260  }
261  template <class P>
262  static bool numericallyEqual(const negator<P>& v1, const negator<P>& v2, int n, double tol=defTol<P>()) {
263  return numericallyEqual(-v1, -v2, n, tol); // P, P
264  }
265  template <class P>
266  static bool numericallyEqual(const P& v1, const negator<P>& v2, int n, double tol=defTol<P>()) {
267  return numericallyEqual(-v1, -v2, n, tol); // P, P
268  }
269  template <class P>
270  static bool numericallyEqual(const negator<P>& v1, const P& v2, int n, double tol=defTol<P>()) {
271  return numericallyEqual(-v1, -v2, n, tol); // P, P
272  }
273  template <class P>
274  static bool numericallyEqual(const negator<std::complex<P> >& v1, const conjugate<P>& v2, int n, double tol=defTol<P>()) {
275  return numericallyEqual(-v1, -v2, n, tol); // complex, conjugate
276  }
277  template <class P>
278  static bool numericallyEqual(const negator<conjugate<P> >& v1, const std::complex<P>& v2, int n, double tol=defTol<P>()) {
279  return numericallyEqual(-v1, -v2, n, tol); // conjugate, complex
280  }
281  template <class P>
282  static bool numericallyEqual(const std::complex<P>& v1, const negator<conjugate<P> >& v2, int n, double tol=defTol<P>()) {
283  return numericallyEqual(-v1, -v2, n, tol); // complex, conjugate
284  }
285  template <class P>
286  static bool numericallyEqual(const conjugate<P>& v1, const negator<std::complex<P> >& v2, int n, double tol=defTol<P>()) {
287  return numericallyEqual(-v1, -v2, n, tol); // conjugate, complex
288  }
289  template <int M, class E1, int S1, class E2, int S2>
290  static bool numericallyEqual(const Vec<M,E1,S1>& v1, const Vec<M,E2,S2>& v2, int n, double tol=(defTol2<E1,E2>())) {
291  for (int i=0; i<M; ++i) if (!numericallyEqual(v1[i],v2[i], n, tol)) return false;
292  return true;
293  }
294  template <int N, class E1, int S1, class E2, int S2>
295  static bool numericallyEqual(const Row<N,E1,S1>& v1, const Row<N,E2,S2>& v2, int n, double tol=(defTol2<E1,E2>())) {
296  for (int j=0; j<N; ++j) if (!numericallyEqual(v1[j],v2[j], n, tol)) return false;
297  return true;
298  }
299  template <int M, int N, class E1, int CS1, int RS1, class E2, int CS2, int RS2>
300  static bool numericallyEqual(const Mat<M,N,E1,CS1,RS1>& v1, const Mat<M,N,E2,CS2,RS2>& v2, int n, double tol=(defTol2<E1,E2>())) {
301  for (int j=0; j<N; ++j) if (!numericallyEqual(v1(j),v2(j), n, tol)) return false;
302  return true;
303  }
304  template <int N, class E1, int S1, class E2, int S2>
305  static bool numericallyEqual(const SymMat<N,E1,S1>& v1, const SymMat<N,E2,S2>& v2, int n, double tol=(defTol2<E1,E2>())) {
306  return numericallyEqual(v1.getAsVec(), v2.getAsVec(), n, tol);
307  }
308  template <class E1, class E2>
309  static bool numericallyEqual(const VectorView_<E1>& v1, const VectorView_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) {
310  if (v1.size() != v2.size()) return false;
311  for (int i=0; i < v1.size(); ++i)
312  if (!numericallyEqual(v1[i], v2[i], n, tol)) return false;
313  return true;
314  }
315  template <class E1, class E2>
316  static bool numericallyEqual(const Vector_<E1>& v1, const Vector_<E2>& v2, int n, double tol=(defTol2<E1,E2>()))
317  { return numericallyEqual((const VectorView_<E1>&)v1, (const VectorView_<E2>&)v2, n, tol); }
318  template <class E1, class E2>
319  static bool numericallyEqual(const Vector_<E1>& v1, const VectorView_<E2>& v2, int n, double tol=(defTol2<E1,E2>()))
320  { return numericallyEqual((const VectorView_<E1>&)v1, (const VectorView_<E2>&)v2, n, tol); }
321  template <class E1, class E2>
322  static bool numericallyEqual(const VectorView_<E1>& v1, const Vector_<E2>& v2, int n, double tol=(defTol2<E1,E2>()))
323  { return numericallyEqual((const VectorView_<E1>&)v1, (const VectorView_<E2>&)v2, n, tol); }
324 
325  template <class E1, class E2>
326  static bool numericallyEqual(const RowVectorView_<E1>& v1, const RowVectorView_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) {
327  if (v1.size() != v2.size()) return false;
328  for (int i=0; i < v1.size(); ++i)
329  if (!numericallyEqual(v1[i], v2[i], n, tol)) return false;
330  return true;
331  }
332  template <class E1, class E2>
333  static bool numericallyEqual(const RowVector_<E1>& v1, const RowVector_<E2>& v2, int n, double tol=(defTol2<E1,E2>()))
334  { return numericallyEqual((const RowVectorView_<E1>&)v1, (const RowVectorView_<E2>&)v2, n, tol); }
335  template <class E1, class E2>
336  static bool numericallyEqual(const RowVector_<E1>& v1, const RowVectorView_<E2>& v2, int n, double tol=(defTol2<E1,E2>()))
337  { return numericallyEqual((const RowVectorView_<E1>&)v1, (const RowVectorView_<E2>&)v2, n, tol); }
338  template <class E1, class E2>
339  static bool numericallyEqual(const RowVectorView_<E1>& v1, const RowVector_<E2>& v2, int n, double tol=(defTol2<E1,E2>()))
340  { return numericallyEqual((const RowVectorView_<E1>&)v1, (const RowVectorView_<E2>&)v2, n, tol); }
341 
342  template <class E1, class E2>
343  static bool numericallyEqual(const MatrixView_<E1>& v1, const MatrixView_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) {
344  if (v1.nrow() != v2.nrow() || v1.ncol() != v2.ncol()) return false;
345  for (int j=0; j < v1.ncol(); ++j)
346  if (!numericallyEqual(v1(j), v2(j), n, tol)) return false;
347  return true;
348  }
349  template <class E1, class E2>
350  static bool numericallyEqual(const Matrix_<E1>& m1, const Matrix_<E2>& m2, int n, double tol=(defTol2<E1,E2>()))
351  { return numericallyEqual((const MatrixView_<E1>&)m1, (const MatrixView_<E2>&)m2, n, tol); }
352  template <class E1, class E2>
353  static bool numericallyEqual(const Matrix_<E1>& m1, const MatrixView_<E2>& m2, int n, double tol=(defTol2<E1,E2>()))
354  { return numericallyEqual((const MatrixView_<E1>&)m1, (const MatrixView_<E2>&)m2, n, tol); }
355  template <class E1, class E2>
356  static bool numericallyEqual(const MatrixView_<E1>& m1, const Matrix_<E2>& m2, int n, double tol=(defTol2<E1,E2>()))
357  { return numericallyEqual((const MatrixView_<E1>&)m1, (const MatrixView_<E2>&)m2, n, tol); }
358 
359  template <class P>
360  static bool numericallyEqual(const Rotation_<P>& R1, const Rotation_<P>& R2, int n, double tol=defTol<P>()) {
361  return R1.isSameRotationToWithinAngle(R2, (Real)(n*tol));
362  }
363 
364  template <class P>
365  static bool numericallyEqual(const Transform_<P>& T1, const Transform_<P>& T2, int n, double tol=defTol<P>()) {
366  return numericallyEqual(T1.R(), T2.R(), n, tol)
367  && numericallyEqual(T1.p(), T2.p(), n, tol);
368  }
369 
370  template <class P>
371  static bool numericallyEqual(const UnitInertia_<P>& G1, const UnitInertia_<P>& G2, int n, double tol=defTol<P>()) {
372  return numericallyEqual(G1.asSymMat33(),G2.asSymMat33(), n, tol);
373  }
374 
375  template <class P>
376  static bool numericallyEqual(const Inertia_<P>& I1, const Inertia_<P>& I2, int n, double tol=defTol<P>()) {
377  return numericallyEqual(I1.asSymMat33(),I2.asSymMat33(), n, tol);
378  }
379 
380  // Random numbers
381  static Real randReal() {
382  static Random::Uniform rand(-1,1);
383  return rand.getValue();
384  }
385  static Complex randComplex() {return Complex(randReal(),randReal());}
387  static float randFloat() {return (float)randReal();}
388  static double randDouble() {return (double)randReal();}
389 
390  template <int M> static Vec<M> randVec()
391  { Vec<M> v; for (int i=0; i<M; ++i) v[i]=randReal(); return v;}
392  template <int N> static Row<N> randRow() {return ~randVec<N>();}
393  template <int M, int N> static Mat<M,N> randMat()
394  { Mat<M,N> m; for (int j=0; j<N; ++j) m(j)=randVec<M>(); return m;}
395  template <int N> static SymMat<N> randSymMat()
396  { SymMat<N> s; s.updAsVec() = randVec<N*(N+1)/2>(); return s; }
397 
398  static Vector randVector(int m)
399  { Vector v(m); for (int i=0; i<m; ++i) v[i]=randReal(); return v;}
400  static Matrix randMatrix(int m, int n)
401  { Matrix M(m,n); for (int j=0; j<n; ++j) M(j)=randVector(m); return M;}
402 
403  static Vec3 randVec3() {return randVec<3>();}
404  static Mat33 randMat33() {return randMat<3,3>();}
405  static SymMat33 randSymMat33() {return randSymMat<3>();}
407  return SpatialVec(randVec3(), randVec3());
408  }
410  return SpatialMat(randMat33(), randMat33(),
411  randMat33(), randMat33());
412  }
414  // Generate random angle and random axis to rotate around.
415  return Rotation((Pi/2)*randReal(), randVec3());
416  }
418  return Transform(randRotation(), randVec3());
419  }
420 private:
421  std::clock_t startTime;
422  std::string testName;
423 };
424 
427 public:
428  Subtest(const std::string& name) : subtestName(name)
429  {
430  char padded[128];
431  sprintf(padded, "%-20s", name.c_str());
432  paddedName = std::string(padded);
433  std::clog << " " << paddedName << " ... " << std::flush;
434  startTime = std::clock();
435  }
437  std::clog << "done. " << paddedName << " time: "
438  << 1000*(std::clock()-startTime)/CLOCKS_PER_SEC << "ms.\n";
439  }
440 private:
441  std::clock_t startTime;
442  std::string subtestName;
443  std::string paddedName; // name plus some blanks
444 };
445 
446 } // namespace SimTK
447 
449 #define SimTK_START_TEST(testName) \
450  SimTK::Test simtk_test_(testName); \
451  try {
452 
454 #define SimTK_END_TEST() \
455  } catch(const std::exception& e) { \
456  std::cerr << "Test failed due to exception: " \
457  << e.what() << std::endl; \
458  return 1; \
459  } catch(...) { \
460  std::cerr << "Test failed due to unrecognized exception.\n"; \
461  return 1; \
462  } \
463  return 0;
464 
467 #define SimTK_SUBTEST(testFunction) \
468  do {SimTK::Test::Subtest sub(#testFunction); (testFunction)();} while(false)
469 
470 
471 #define SimTK_SUBTEST1(testFunction,arg1) \
472  do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1);} while(false)
473 
474 
475 #define SimTK_SUBTEST2(testFunction,arg1,arg2) \
476  do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1,arg2);} while(false)
477 
478 
479 #define SimTK_SUBTEST3(testFunction,arg1,arg2,arg3) \
480  do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1,arg2,arg3);} while(false)
481 
482 
483 #define SimTK_SUBTEST4(testFunction,arg1,arg2,arg3,arg4) \
484  do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1,arg2,arg3,arg4);} while(false)
485 
487 #define SimTK_TEST(cond) {SimTK_ASSERT_ALWAYS((cond), "Test condition failed.");}
488 
491 #define SimTK_TEST_FAILED(msg) {SimTK_ASSERT_ALWAYS(!"Test case failed.", msg);}
492 
496 #define SimTK_TEST_FAILED1(fmt,a1) {SimTK_ASSERT1_ALWAYS(!"Test case failed.",fmt,a1);}
497 
501 #define SimTK_TEST_FAILED2(fmt,a1,a2) {SimTK_ASSERT2_ALWAYS(!"Test case failed.",fmt,a1,a2);}
502 
506 #define SimTK_TEST_EQ(v1,v2) \
507  {SimTK_ASSERT_ALWAYS(SimTK::Test::numericallyEqual((v1),(v2),1), \
508  "Test values should have been numerically equivalent at default tolerance.");}
509 
512 #define SimTK_TEST_EQ_SIZE(v1,v2,n) \
513  {SimTK_ASSERT1_ALWAYS(SimTK::Test::numericallyEqual((v1),(v2),(n)), \
514  "Test values should have been numerically equivalent at size=%d times default tolerance.",(n));}
515 
519 #define SimTK_TEST_EQ_TOL(v1,v2,tol) \
520  {SimTK_ASSERT1_ALWAYS(SimTK::Test::numericallyEqual((v1),(v2),1,(tol)), \
521  "Test values should have been numerically equivalent at tolerance=%g.",(tol));}
522 
526 #define SimTK_TEST_NOTEQ(v1,v2) \
527  {SimTK_ASSERT_ALWAYS(!SimTK::Test::numericallyEqual((v1),(v2),1), \
528  "Test values should NOT have been numerically equivalent (at default tolerance).");}
529 
533 #define SimTK_TEST_NOTEQ_SIZE(v1,v2,n) \
534  {SimTK_ASSERT1_ALWAYS(!SimTK::Test::numericallyEqual((v1),(v2),(n)), \
535  "Test values should NOT have been numerically equivalent at size=%d times default tolerance.",(n));}
536 
540 #define SimTK_TEST_NOTEQ_TOL(v1,v2,tol) \
541  {SimTK_ASSERT1_ALWAYS(!SimTK::Test::numericallyEqual((v1),(v2),1,(tol)), \
542  "Test values should NOT have been numerically equivalent at tolerance=%g.",(tol));}
543 
545 #define SimTK_TEST_MUST_THROW(stmt) \
546  do {int threw=0; try {stmt;} \
547  catch(const std::exception&){threw=1;} \
548  catch(...){threw=2;} \
549  if (threw==0) SimTK_TEST_FAILED1("Expected statement\n----\n%s\n----\n to throw an exception but it did not.",#stmt); \
550  if (threw==2) SimTK_TEST_FAILED1("Expected statement\n%s\n to throw an std::exception but it threw something else.",#stmt); \
551  }while(false)
552 
554 #define SimTK_TEST_MUST_THROW_EXC(stmt,exc) \
555  do {int threw=0; try {stmt;} \
556  catch(const exc&){threw=1;} \
557  catch(...){threw=2;} \
558  if (threw==0) SimTK_TEST_FAILED1("Expected statement\n----\n%s\n----\n to throw an exception but it did not.",#stmt); \
559  if (threw==2) SimTK_TEST_FAILED2("Expected statement\n----\n%s\n----\n to throw exception type %s but it threw something else.",#stmt,#exc); \
560  }while(false)
561 
563 #define SimTK_TEST_MAY_THROW(stmt) \
564  do {int threw=0; try {stmt;} \
565  catch(const std::exception&){threw=1;} \
566  catch(...){threw=2;} \
567  if (threw==2) SimTK_TEST_FAILED1("Expected statement\n%s\n to throw an std::exception but it threw something else.",#stmt); \
568  }while(false)
569 
571 #define SimTK_TEST_MAY_THROW_EXC(stmt,exc) \
572  do {int threw=0; try {stmt;} \
573  catch(const exc&){threw=1;} \
574  catch(...){threw=2;} \
575  if (threw==2) SimTK_TEST_FAILED2("Expected statement\n----\n%s\n----\n to throw exception type %s but it threw something else.",#stmt,#exc); \
576  }while(false)
577 
578 // When we're only required to throw in Debug, we have to suppress the
579 // test case altogether in Release because it may cause damage.
580 #if defined(NDEBUG)
581 
582 
583  #define SimTK_TEST_MUST_THROW_DEBUG(stmt)
584 
585 
586  #define SimTK_TEST_MUST_THROW_EXC_DEBUG(stmt,exc)
587 #else
588 
589 
590  #define SimTK_TEST_MUST_THROW_DEBUG(stmt) SimTK_TEST_MUST_THROW(stmt)
591 
592 
593  #define SimTK_TEST_MUST_THROW_EXC_DEBUG(stmt,exc) \
594  SimTK_TEST_MUST_THROW_EXC(stmt,exc)
595 #endif
596 
597 
598 
599 
600 // End of Regression testing group.
602 
603 #endif // SimTK_SimTKCOMMON_TESTING_H_