Simbody
|
00001 #ifndef SimTK_SimTKCOMMON_TESTING_H_ 00002 #define SimTK_SimTKCOMMON_TESTING_H_ 00003 00004 /* -------------------------------------------------------------------------- * 00005 * SimTK Core: SimTKcommon * 00006 * -------------------------------------------------------------------------- * 00007 * This is part of the SimTK Core biosimulation toolkit originating from * 00008 * Simbios, the NIH National Center for Physics-Based Simulation of * 00009 * Biological Structures at Stanford, funded under the NIH Roadmap for * 00010 * Medical Research, grant U54 GM072970. See https://simtk.org. * 00011 * * 00012 * Portions copyright (c) 2009 Stanford University and the Authors. * 00013 * Authors: Michael Sherman * 00014 * Contributors: * 00015 * * 00016 * Permission is hereby granted, free of charge, to any person obtaining a * 00017 * copy of this software and associated documentation files (the "Software"), * 00018 * to deal in the Software without restriction, including without limitation * 00019 * the rights to use, copy, modify, merge, publish, distribute, sublicense, * 00020 * and/or sell copies of the Software, and to permit persons to whom the * 00021 * Software is furnished to do so, subject to the following conditions: * 00022 * * 00023 * The above copyright notice and this permission notice shall be included in * 00024 * all copies or substantial portions of the Software. * 00025 * * 00026 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 00027 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 00028 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * 00029 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 00030 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 00031 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * 00032 * USE OR OTHER DEALINGS IN THE SOFTWARE. * 00033 * -------------------------------------------------------------------------- */ 00034 00035 #include "SimTKcommon/basics.h" 00036 #include "SimTKcommon/Simmatrix.h" 00037 #include "SimTKcommon/internal/Random.h" 00038 00039 #include <cmath> 00040 #include <ctime> 00041 #include <algorithm> 00042 #include <iostream> 00043 00049 namespace SimTK { 00050 00161 00162 00163 00164 00165 class Test { 00166 public: 00167 class Subtest; 00168 Test(const std::string& name) : testName(name) 00169 { 00170 std::clog << "Starting test " << testName << " ...\n"; 00171 startTime = std::clock(); 00172 } 00173 ~Test() { 00174 std::clog << "Done. " << testName << " time: " 00175 << 1000*(std::clock()-startTime)/CLOCKS_PER_SEC << "ms.\n"; 00176 } 00177 00178 template <class T> 00179 static double defTol() {return (double)NTraits<typename CNT<T>::Precision>::getSignificant();} 00180 00181 // For dissimilar types, the default tolerance is the narrowest of the two. 00182 template <class T1, class T2> 00183 static double defTol2() {return std::max(defTol<T1>(), defTol<T2>());} 00184 00185 // Scale by the magnitude of the quantities being compared, so that we don't 00186 // ask for unreasonable precision. For magnitudes near zero, we'll be satisfied 00187 // if both are very small without demanding that they must also be relatively 00188 // close. That is, we use a relative tolerance for big numbers and an absolute 00189 // tolerance for small ones. 00190 static bool numericallyEqual(float v1, float v2, int n, double tol=defTol<float>()) { 00191 const float scale = n*std::max(std::max(std::abs(v1), std::abs(v2)), 1.0f); 00192 return std::abs(v1-v2) < scale*(float)tol; 00193 } 00194 static bool numericallyEqual(double v1, double v2, int n, double tol=defTol<double>()) { 00195 const double scale = n*std::max(std::max(std::abs(v1), std::abs(v2)), 1.0); 00196 return std::abs(v1-v2) < scale*(double)tol; 00197 } 00198 static bool numericallyEqual(long double v1, long double v2, int n, double tol=defTol<long double>()) { 00199 const long double scale = n*std::max(std::max(std::abs(v1), std::abs(v2)), 1.0l); 00200 return std::abs(v1-v2) < scale*(long double)tol; 00201 } 00202 00203 // For integers we ignore tolerance. 00204 static bool numericallyEqual(int i1, int i2, int n, double tol=0) {return i1==i2;} 00205 static bool numericallyEqual(unsigned u1, unsigned u2, int n, double tol=0) {return u1==u2;} 00206 00207 // Mixed floating types use default tolerance for the narrower type. 00208 static bool numericallyEqual(float v1, double v2, int n, double tol=defTol<float>()) 00209 { return numericallyEqual((double)v1, v2, n, tol); } 00210 static bool numericallyEqual(double v1, float v2, int n, double tol=defTol<float>()) 00211 { return numericallyEqual(v1, (double)v2, n, tol); } 00212 static bool numericallyEqual(float v1, long double v2, int n, double tol=defTol<float>()) 00213 { return numericallyEqual((long double)v1, v2, n, tol); } 00214 static bool numericallyEqual(long double v1, float v2, int n, double tol=defTol<float>()) 00215 { return numericallyEqual(v1, (long double)v2, n, tol); } 00216 static bool numericallyEqual(double v1, long double v2, int n, double tol=defTol<double>()) 00217 { return numericallyEqual((long double)v1, v2, n, tol); } 00218 static bool numericallyEqual(long double v1, double v2, int n, double tol=defTol<double>()) 00219 { return numericallyEqual(v1, (long double)v2, n, tol); } 00220 00221 // Mixed int/floating just upgrades int to floating type. 00222 static bool numericallyEqual(int i1, float f2, int n, double tol=defTol<float>()) 00223 { return numericallyEqual((float)i1,f2,n,tol); } 00224 static bool numericallyEqual(float f1, int i2, int n, double tol=defTol<float>()) 00225 { return numericallyEqual(f1,(float)i2,n,tol); } 00226 static bool numericallyEqual(unsigned i1, float f2, int n, double tol=defTol<float>()) 00227 { return numericallyEqual((float)i1,f2,n,tol); } 00228 static bool numericallyEqual(float f1, unsigned i2, int n, double tol=defTol<float>()) 00229 { return numericallyEqual(f1,(float)i2,n,tol); } 00230 static bool numericallyEqual(int i1, double f2, int n, double tol=defTol<double>()) 00231 { return numericallyEqual((double)i1,f2,n,tol); } 00232 static bool numericallyEqual(double f1, int i2, int n, double tol=defTol<double>()) 00233 { return numericallyEqual(f1,(double)i2,n,tol); } 00234 static bool numericallyEqual(unsigned i1, double f2, int n, double tol=defTol<double>()) 00235 { return numericallyEqual((double)i1,f2,n,tol); } 00236 static bool numericallyEqual(double f1, unsigned i2, int n, double tol=defTol<double>()) 00237 { return numericallyEqual(f1,(double)i2,n,tol); } 00238 static bool numericallyEqual(int i1, long double f2, int n, double tol=defTol<long double>()) 00239 { return numericallyEqual((long double)i1,f2,n,tol); } 00240 static bool numericallyEqual(long double f1, int i2, int n, double tol=defTol<long double>()) 00241 { return numericallyEqual(f1,(long double)i2,n,tol); } 00242 static bool numericallyEqual(unsigned i1, long double f2, int n, double tol=defTol<long double>()) 00243 { return numericallyEqual((long double)i1,f2,n,tol); } 00244 static bool numericallyEqual(long double f1, unsigned i2, int n, double tol=defTol<long double>()) 00245 { return numericallyEqual(f1,(long double)i2,n,tol); } 00246 00247 template <class P> 00248 static bool numericallyEqual(const std::complex<P>& v1, const std::complex<P>& v2, int n, double tol=defTol<P>()) { 00249 return numericallyEqual(v1.real(), v2.real(), n, tol) 00250 && numericallyEqual(v1.imag(), v2.imag(), n, tol); 00251 } 00252 template <class P> 00253 static bool numericallyEqual(const conjugate<P>& v1, const conjugate<P>& v2, int n, double tol=defTol<P>()) { 00254 return numericallyEqual(v1.real(), v2.real(), n, tol) 00255 && numericallyEqual(v1.imag(), v2.imag(), n, tol); 00256 } 00257 template <class P> 00258 static bool numericallyEqual(const std::complex<P>& v1, const conjugate<P>& v2, int n, double tol=defTol<P>()) { 00259 return numericallyEqual(v1.real(), v2.real(), n, tol) 00260 && numericallyEqual(v1.imag(), v2.imag(), n, tol); 00261 } 00262 template <class P> 00263 static bool numericallyEqual(const conjugate<P>& v1, const std::complex<P>& v2, int n, double tol=defTol<P>()) { 00264 return numericallyEqual(v1.real(), v2.real(), n, tol) 00265 && numericallyEqual(v1.imag(), v2.imag(), n, tol); 00266 } 00267 template <class P> 00268 static bool numericallyEqual(const negator<P>& v1, const negator<P>& v2, int n, double tol=defTol<P>()) { 00269 return numericallyEqual(-v1, -v2, n, tol); // P, P 00270 } 00271 template <class P> 00272 static bool numericallyEqual(const P& v1, const negator<P>& v2, int n, double tol=defTol<P>()) { 00273 return numericallyEqual(-v1, -v2, n, tol); // P, P 00274 } 00275 template <class P> 00276 static bool numericallyEqual(const negator<P>& v1, const P& v2, int n, double tol=defTol<P>()) { 00277 return numericallyEqual(-v1, -v2, n, tol); // P, P 00278 } 00279 template <class P> 00280 static bool numericallyEqual(const negator<std::complex<P> >& v1, const conjugate<P>& v2, int n, double tol=defTol<P>()) { 00281 return numericallyEqual(-v1, -v2, n, tol); // complex, conjugate 00282 } 00283 template <class P> 00284 static bool numericallyEqual(const negator<conjugate<P> >& v1, const std::complex<P>& v2, int n, double tol=defTol<P>()) { 00285 return numericallyEqual(-v1, -v2, n, tol); // conjugate, complex 00286 } 00287 template <class P> 00288 static bool numericallyEqual(const std::complex<P>& v1, const negator<conjugate<P> >& v2, int n, double tol=defTol<P>()) { 00289 return numericallyEqual(-v1, -v2, n, tol); // complex, conjugate 00290 } 00291 template <class P> 00292 static bool numericallyEqual(const conjugate<P>& v1, const negator<std::complex<P> >& v2, int n, double tol=defTol<P>()) { 00293 return numericallyEqual(-v1, -v2, n, tol); // conjugate, complex 00294 } 00295 template <int M, class E1, int S1, class E2, int S2> 00296 static bool numericallyEqual(const Vec<M,E1,S1>& v1, const Vec<M,E2,S2>& v2, int n, double tol=(defTol2<E1,E2>())) { 00297 for (int i=0; i<M; ++i) if (!numericallyEqual(v1[i],v2[i], n, tol)) return false; 00298 return true; 00299 } 00300 template <int N, class E1, int S1, class E2, int S2> 00301 static bool numericallyEqual(const Row<N,E1,S1>& v1, const Row<N,E2,S2>& v2, int n, double tol=(defTol2<E1,E2>())) { 00302 for (int j=0; j<N; ++j) if (!numericallyEqual(v1[j],v2[j], n, tol)) return false; 00303 return true; 00304 } 00305 template <int M, int N, class E1, int CS1, int RS1, class E2, int CS2, int RS2> 00306 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>())) { 00307 for (int j=0; j<N; ++j) if (!numericallyEqual(v1(j),v2(j), n, tol)) return false; 00308 return true; 00309 } 00310 template <int N, class E1, int S1, class E2, int S2> 00311 static bool numericallyEqual(const SymMat<N,E1,S1>& v1, const SymMat<N,E2,S2>& v2, int n, double tol=(defTol2<E1,E2>())) { 00312 return numericallyEqual(v1.getAsVec(), v2.getAsVec(), n, tol); 00313 } 00314 template <class E1, class E2> 00315 static bool numericallyEqual(const VectorView_<E1>& v1, const VectorView_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) { 00316 if (v1.size() != v2.size()) return false; 00317 for (int i=0; i < v1.size(); ++i) 00318 if (!numericallyEqual(v1[i], v2[i], n, tol)) return false; 00319 return true; 00320 } 00321 template <class E1, class E2> 00322 static bool numericallyEqual(const Vector_<E1>& v1, const Vector_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) 00323 { return numericallyEqual((const VectorView_<E1>&)v1, (const VectorView_<E2>&)v2, n, tol); } 00324 00325 template <class E1, class E2> 00326 static bool numericallyEqual(const RowVectorView_<E1>& v1, const RowVectorView_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) { 00327 if (v1.size() != v2.size()) return false; 00328 for (int i=0; i < v1.size(); ++i) 00329 if (!numericallyEqual(v1[i], v2[i], n, tol)) return false; 00330 return true; 00331 } 00332 template <class E1, class E2> 00333 static bool numericallyEqual(const RowVector_<E1>& v1, const RowVector_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) 00334 { return numericallyEqual((const RowVectorView_<E1>&)v1, (const RowVectorView_<E2>&)v2, n, tol); } 00335 00336 template <class E1, class E2> 00337 static bool numericallyEqual(const MatrixView_<E1>& v1, const MatrixView_<E2>& v2, int n, double tol=(defTol2<E1,E2>())) { 00338 if (v1.nrow() != v2.nrow() || v1.ncol() != v2.ncol()) return false; 00339 for (int j=0; j < v1.ncol(); ++j) 00340 if (!numericallyEqual(v1(j), v2(j), n, tol)) return false; 00341 return true; 00342 } 00343 template <class E1, class E2> 00344 static bool numericallyEqual(const Matrix_<E1>& m1, const Matrix_<E2>& m2, int n, double tol=(defTol2<E1,E2>())) 00345 { return numericallyEqual((const MatrixView_<E1>&)m1, (const MatrixView_<E2>&)m2, n, tol); } 00346 00347 template <class P> 00348 static bool numericallyEqual(const Rotation_<P>& R1, const Rotation_<P>& R2, int n, double tol=defTol<P>()) { 00349 return R1.isSameRotationToWithinAngle(R2, (Real)(n*tol)); 00350 } 00351 00352 template <class P> 00353 static bool numericallyEqual(const Transform_<P>& T1, const Transform_<P>& T2, int n, double tol=defTol<P>()) { 00354 return numericallyEqual(T1.R(), T2.R(), n, tol) 00355 && numericallyEqual(T1.p(), T2.p(), n, tol); 00356 } 00357 00358 template <class P> 00359 static bool numericallyEqual(const UnitInertia_<P>& G1, const UnitInertia_<P>& G2, int n, double tol=defTol<P>()) { 00360 return numericallyEqual(G1.asSymMat33(),G2.asSymMat33(), n, tol); 00361 } 00362 00363 template <class P> 00364 static bool numericallyEqual(const Inertia_<P>& I1, const Inertia_<P>& I2, int n, double tol=defTol<P>()) { 00365 return numericallyEqual(I1.asSymMat33(),I2.asSymMat33(), n, tol); 00366 } 00367 00368 // Random numbers 00369 static Real randReal() { 00370 static Random::Uniform rand(-1,1); 00371 return rand.getValue(); 00372 } 00373 static Complex randComplex() {return Complex(randReal(),randReal());} 00374 static Conjugate randConjugate() {return Conjugate(randReal(),randReal());} 00375 static float randFloat() {return (float)randReal();} 00376 static double randDouble() {return (double)randReal();} 00377 00378 template <int M> static Vec<M> randVec() 00379 { Vec<M> v; for (int i=0; i<M; ++i) v[i]=randReal(); return v;} 00380 template <int N> static Row<N> randRow() {return ~randVec<N>();} 00381 template <int M, int N> static Mat<M,N> randMat() 00382 { Mat<M,N> m; for (int j=0; j<N; ++j) m(j)=randVec<M>(); return m;} 00383 template <int N> static SymMat<N> randSymMat() 00384 { SymMat<N> s; s.updAsVec() = randVec<N*(N+1)/2>(); return s; } 00385 00386 static Vector randVector(int m) 00387 { Vector v(m); for (int i=0; i<m; ++i) v[i]=randReal(); return v;} 00388 static Matrix randMatrix(int m, int n) 00389 { Matrix M(m,n); for (int j=0; j<n; ++j) M(j)=randVector(m); return M;} 00390 00391 static Vec3 randVec3() {return randVec<3>();} 00392 static Mat33 randMat33() {return randMat<3,3>();} 00393 static SymMat33 randSymMat33() {return randSymMat<3>();} 00394 static SpatialVec randSpatialVec() { 00395 return SpatialVec(randVec3(), randVec3()); 00396 } 00397 static SpatialMat randSpatialMat() { 00398 return SpatialMat(randMat33(), randMat33(), 00399 randMat33(), randMat33()); 00400 } 00401 static Rotation randRotation() { 00402 // Generate random angle and random axis to rotate around. 00403 return Rotation((Pi/2)*randReal(), randVec3()); 00404 } 00405 static Transform randTransform() { 00406 return Transform(randRotation(), randVec3()); 00407 } 00408 private: 00409 std::clock_t startTime; 00410 std::string testName; 00411 }; 00412 00414 class Test::Subtest { 00415 public: 00416 Subtest(const std::string& name) : subtestName(name) 00417 { 00418 char padded[128]; 00419 sprintf(padded, "%-20s", name.c_str()); 00420 paddedName = std::string(padded); 00421 std::clog << " " << paddedName << " ... " << std::flush; 00422 startTime = std::clock(); 00423 } 00424 ~Subtest() { 00425 std::clog << "done. " << paddedName << " time: " 00426 << 1000*(std::clock()-startTime)/CLOCKS_PER_SEC << "ms.\n"; 00427 } 00428 private: 00429 std::clock_t startTime; 00430 std::string subtestName; 00431 std::string paddedName; // name plus some blanks 00432 }; 00433 00434 } // namespace SimTK 00435 00437 #define SimTK_START_TEST(testName) \ 00438 SimTK::Test simtk_test_(testName); \ 00439 try { 00440 00442 #define SimTK_END_TEST() \ 00443 } catch(const std::exception& e) { \ 00444 std::cerr << "Test failed due to exception: " \ 00445 << e.what() << std::endl; \ 00446 return 1; \ 00447 } catch(...) { \ 00448 std::cerr << "Test failed due to unrecognized exception.\n"; \ 00449 return 1; \ 00450 } \ 00451 return 0; 00452 00455 #define SimTK_SUBTEST(testFunction) \ 00456 do {SimTK::Test::Subtest sub(#testFunction); (testFunction)();} while(false) 00457 00458 00459 #define SimTK_SUBTEST1(testFunction,arg1) \ 00460 do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1);} while(false) 00461 00462 00463 #define SimTK_SUBTEST2(testFunction,arg1,arg2) \ 00464 do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1,arg2);} while(false) 00465 00466 00467 #define SimTK_SUBTEST3(testFunction,arg1,arg2,arg3) \ 00468 do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1,arg2,arg3);} while(false) 00469 00470 00471 #define SimTK_SUBTEST4(testFunction,arg1,arg2,arg3,arg4) \ 00472 do {SimTK::Test::Subtest sub(#testFunction); (testFunction)(arg1,arg2,arg3,arg4);} while(false) 00473 00475 #define SimTK_TEST(cond) {SimTK_ASSERT_ALWAYS((cond), "Test condition failed.");} 00476 00479 #define SimTK_TEST_FAILED(msg) {SimTK_ASSERT_ALWAYS(!"Test case failed.", msg);} 00480 00484 #define SimTK_TEST_FAILED1(fmt,a1) {SimTK_ASSERT1_ALWAYS(!"Test case failed.",fmt,a1);} 00485 00489 #define SimTK_TEST_FAILED2(fmt,a1,a2) {SimTK_ASSERT2_ALWAYS(!"Test case failed.",fmt,a1,a2);} 00490 00494 #define SimTK_TEST_EQ(v1,v2) \ 00495 {SimTK_ASSERT_ALWAYS(SimTK::Test::numericallyEqual((v1),(v2),1), \ 00496 "Test values should have been numerically equivalent at default tolerance.");} 00497 00500 #define SimTK_TEST_EQ_SIZE(v1,v2,n) \ 00501 {SimTK_ASSERT1_ALWAYS(SimTK::Test::numericallyEqual((v1),(v2),(n)), \ 00502 "Test values should have been numerically equivalent at size=%d times default tolerance.",(n));} 00503 00507 #define SimTK_TEST_EQ_TOL(v1,v2,tol) \ 00508 {SimTK_ASSERT1_ALWAYS(SimTK::Test::numericallyEqual((v1),(v2),1,(tol)), \ 00509 "Test values should have been numerically equivalent at tolerance=%g.",(tol));} 00510 00514 #define SimTK_TEST_NOTEQ(v1,v2) \ 00515 {SimTK_ASSERT_ALWAYS(!SimTK::Test::numericallyEqual((v1),(v2),1), \ 00516 "Test values should NOT have been numerically equivalent (at default tolerance).");} 00517 00521 #define SimTK_TEST_NOTEQ_SIZE(v1,v2,n) \ 00522 {SimTK_ASSERT1_ALWAYS(!SimTK::Test::numericallyEqual((v1),(v2),(n)), \ 00523 "Test values should NOT have been numerically equivalent at size=%d times default tolerance.",(n));} 00524 00528 #define SimTK_TEST_NOTEQ_TOL(v1,v2,tol) \ 00529 {SimTK_ASSERT1_ALWAYS(!SimTK::Test::numericallyEqual((v1),(v2),1,(tol)), \ 00530 "Test values should NOT have been numerically equivalent at tolerance=%g.",(tol));} 00531 00533 #define SimTK_TEST_MUST_THROW(stmt) \ 00534 do {int threw=0; try {stmt;} \ 00535 catch(const std::exception&){threw=1;} \ 00536 catch(...){threw=2;} \ 00537 if (threw==0) SimTK_TEST_FAILED1("Expected statement\n----\n%s\n----\n to throw an exception but it did not.",#stmt); \ 00538 if (threw==2) SimTK_TEST_FAILED1("Expected statement\n%s\n to throw an std::exception but it threw something else.",#stmt); \ 00539 }while(false) 00540 00542 #define SimTK_TEST_MUST_THROW_EXC(stmt,exc) \ 00543 do {int threw=0; try {stmt;} \ 00544 catch(const exc&){threw=1;} \ 00545 catch(...){threw=2;} \ 00546 if (threw==0) SimTK_TEST_FAILED1("Expected statement\n----\n%s\n----\n to throw an exception but it did not.",#stmt); \ 00547 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); \ 00548 }while(false) 00549 00551 #define SimTK_TEST_MAY_THROW(stmt) \ 00552 do {int threw=0; try {stmt;} \ 00553 catch(const std::exception&){threw=1;} \ 00554 catch(...){threw=2;} \ 00555 if (threw==2) SimTK_TEST_FAILED1("Expected statement\n%s\n to throw an std::exception but it threw something else.",#stmt); \ 00556 }while(false) 00557 00559 #define SimTK_TEST_MAY_THROW_EXC(stmt,exc) \ 00560 do {int threw=0; try {stmt;} \ 00561 catch(const exc&){threw=1;} \ 00562 catch(...){threw=2;} \ 00563 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); \ 00564 }while(false) 00565 00566 // When we're only required to throw in Debug, we have to suppress the 00567 // test case altogether in Release because it may cause damage. 00568 #if defined(NDEBUG) 00569 00570 00571 #define SimTK_TEST_MUST_THROW_DEBUG(stmt) 00572 00573 00574 #define SimTK_TEST_MUST_THROW_EXC_DEBUG(stmt,exc) 00575 #else 00576 00577 00578 #define SimTK_TEST_MUST_THROW_DEBUG(stmt) SimTK_TEST_MUST_THROW(stmt) 00579 00580 00581 #define SimTK_TEST_MUST_THROW_EXC_DEBUG(stmt,exc) \ 00582 SimTK_TEST_MUST_THROW_EXC(stmt,exc) 00583 #endif 00584 00585 00586 00587 00588 // End of Regression testing group. 00590 00591 #endif // SimTK_SimTKCOMMON_TESTING_H_