1 //============================================================================== 2 // 3 // This file is part of GPSTk, the GPS Toolkit. 4 // 5 // The GPSTk is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published 7 // by the Free Software Foundation; either version 3.0 of the License, or 8 // any later version. 9 // 10 // The GPSTk is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with GPSTk; if not, write to the Free Software Foundation, 17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 18 // 19 // This software was developed by Applied Research Laboratories at the 20 // University of Texas at Austin. 21 // Copyright 2004-2020, The Board of Regents of The University of Texas System 22 // 23 //============================================================================== 24 25 //============================================================================== 26 // 27 // This software was developed by Applied Research Laboratories at the 28 // University of Texas at Austin, under contract to an agency or agencies 29 // within the U.S. Department of Defense. The U.S. Government retains all 30 // rights to use, duplicate, distribute, disclose, or release this software. 31 // 32 // Pursuant to DoD Directive 523024 33 // 34 // DISTRIBUTION STATEMENT A: This software has been approved for public 35 // release, distribution is unlimited. 36 // 37 //============================================================================== 38 39 #include <typeinfo> 40 #include <iostream> 41 #include <fstream> 42 #include <string> 43 #include <iomanip> 44 #include <sstream> 45 #include <cmath> 46 #include <vector> 47 #include <float.h> 48 #include "build_config.h" 49 #include "StringUtils.hpp" 50 #include "Matrix.hpp" 51 52 // Define a TestUtil object named testFramework 53 #define TUDEF(CLASS,METHOD) gpstk::TestUtil testFramework(CLASS, METHOD, __FILE__, __LINE__) 54 55 // Macro to make test code look nice... 56 #define TUCSM(METHOD) testFramework.changeSourceMethod(METHOD) 57 58 59 // Macro to be short form of TestUtil::assert() 60 #define TUASSERT(EXPR) \ 61 try \ 62 { \ 63 testFramework.assert(EXPR, "Assertion failure: "#EXPR, __LINE__); \ 64 } \ 65 catch (gpstk::Exception &exc) \ 66 { \ 67 std::cerr << exc << std::endl; \ 68 testFramework.assert(false, "Exception during "#EXPR, __LINE__); \ 69 } \ 70 catch (...) \ 71 { \ 72 testFramework.assert(false, "Exception during "#EXPR, __LINE__); \ 73 } 74 75 76 // Basic macro for doing equality tests. Expects a TestUtil instance 77 // named testFramework. 78 #define TUASSERTE(TYPE,EXP,GOT) \ 79 try \ 80 { \ 81 testFramework.assert_equals<TYPE>(EXP, GOT, __LINE__); \ 82 } \ 83 catch (gpstk::Exception &exc) \ 84 { \ 85 std::cerr << exc << std::endl; \ 86 testFramework.assert(false, \ 87 "Exception evaluating " #EXP " or " #GOT, \ 88 __LINE__); \ 89 } \ 90 catch (...) \ 91 { \ 92 testFramework.assert(false, \ 93 "Exception evaluating " #EXP " or " #GOT, \ 94 __LINE__); \ 95 } 96 97 98 // Macro for doing equality tests of double/float values. Expects a 99 // TestUtil instance named testFramework. 100 #define TUASSERTFE(EXP,GOT) \ 101 try \ 102 { \ 103 testFramework.assert_equals(EXP, GOT, __LINE__); \ 104 } \ 105 catch (gpstk::Exception &exc) \ 106 { \ 107 std::cerr << exc << std::endl; \ 108 testFramework.assert(false, \ 109 "Exception evaluating " #EXP " or " #GOT, \ 110 __LINE__); \ 111 } \ 112 catch (...) \ 113 { \ 114 testFramework.assert(false, \ 115 "Exception evaluating " #EXP " or " #GOT, \ 116 __LINE__); \ 117 } 118 119 120 // Macro for doing equality tests of double/float values with a 121 // specified epsilon. Expects a TestUtil instance named 122 // testFramework. 123 #define TUASSERTFEPS(EXP,GOT,EPS) \ 124 try \ 125 { \ 126 testFramework.assert_equals(EXP, GOT, __LINE__, "", EPS); \ 127 } \ 128 catch (gpstk::Exception &exc) \ 129 { \ 130 std::cerr << exc << std::endl; \ 131 testFramework.assert(false, \ 132 "Exception evaluating " #EXP " or " #GOT, \ 133 __LINE__); \ 134 } \ 135 catch (...) \ 136 { \ 137 testFramework.assert(false, \ 138 "Exception evaluating " #EXP " or " #GOT, \ 139 __LINE__); \ 140 } 141 142 // Macro for doing comparisons of test files 143 #define TUCMPFILE(F1,F2,SKIP) \ 144 try \ 145 { \ 146 testFramework.assert_files_equal(__LINE__, F1, F2, \ 147 "File mismatch: "+F1+" "+F2, \ 148 SKIP); \ 149 } \ 150 catch (gpstk::Exception &exc) \ 151 { \ 152 std::cerr << exc << std::endl; \ 153 testFramework.assert(false, \ 154 "Exception comparing " #F1 " and " #F2, \ 155 __LINE__); \ 156 } \ 157 catch (...) \ 158 { \ 159 testFramework.assert(false, \ 160 "Exception comparing " #F1 " and " #F2, \ 161 __LINE__); \ 162 } 163 164 // Macro for executing a statement inside a try/catch block with PASS/FAIL. 165 // e.g. TUCATCH(object.methodShouldNotThrow()); 166 #define TUCATCH(STATEMENT) \ 167 try \ 168 { \ 169 STATEMENT; \ 170 TUPASS(#STATEMENT); \ 171 } \ 172 catch (gpstk::Exception &exc) \ 173 { \ 174 std::cerr << exc << std::endl; \ 175 TUFAIL("Exception"); \ 176 } \ 177 catch (...) \ 178 { \ 179 TUFAIL("Exception"); \ 180 } 181 182 // Macro for executing a statement inside a try/catch block with PASS/FAIL. 183 // e.g. TUTHROW(object.methodShouldDefinitelyThrow()); 184 #define TUTHROW(STATEMENT) \ 185 try \ 186 { \ 187 STATEMENT; \ 188 TUFAIL("Did not throw Exception"); \ 189 } \ 190 catch (gpstk::Exception &exc) \ 191 { \ 192 TUPASS(#STATEMENT); \ 193 } \ 194 catch (...) \ 195 { \ 196 TUPASS(#STATEMENT); \ 197 } 198 199 200 // Fail the test with a message. 201 #define TUFAIL(MSG) testFramework.assert(false, MSG, __LINE__) 202 // Pass the test with a (unprinted) message. 203 #define TUPASS(MSG) testFramework.assert(true, MSG, __LINE__) 204 // Usual return from a test function 205 #define TURETURN() return testFramework.countFails() 206 207 namespace gpstk 208 { 209 /// @return a string with the name of the type 210 template<typename T> typeString()211 std::string typeString() 212 { 213 if (typeid(T) == typeid(long double)) 214 return std::string("long double"); 215 else if (typeid(T) == typeid(double)) 216 return std::string("double"); 217 else if (typeid(T) == typeid(float)) 218 return std::string("float"); 219 else 220 return 0; 221 } 222 223 //============================================================ 224 // class: TestUtil 225 // purpose: TestUtil is a utility class (not parent class) 226 // for use with test classes and test methods in GPSTk. 227 // Example: Source usage for a test method with 4 sub-tests: 228 // 229 // TUDEF("SomeClass", "SomeMethod"); 230 // 231 // TUASSERTE(unsigned, 1, 2); 232 // which is equivalent to 233 // testFramework.assert( 1==2 ); 234 // TUASSERTE(unsigned, 1, 1); 235 // testFramework.changeSourceMethod("SomeOtherMethod"); 236 // 237 //============================================================ 238 class TestUtil 239 { 240 public: 241 /** Constructor to be called at the start of each test method. 242 * @param[in] sourceClassInput the name of the source class 243 * being tested 244 * @param[in] sourceMethodInput the name of the source method 245 * being tested 246 * @param[in] testFileInput the name of file containing the test 247 * code, e.g., __FILE__ 248 * @param[in] testLineInput the line number in the file where 249 * testing is done, e.g. __LINE__ 250 * @param[in] verbosityInput the level of verbosity in the print 251 * output, default=1, but set to 0 will supress fail messages 252 */ 253 TestUtil( const std::string& sourceClassInput = "Unknown", 254 const std::string& sourceMethodInput = "Unknown", 255 const std::string& testFileInput = "Unknown", 256 const int& testLineInput = 0, 257 const int& verbosityInput = 1 258 ); 259 260 /** Takes a boolean expression, passes or fails the test, 261 * depending on whether the assertion is true or false, and then 262 * prints the result. 263 * @param[in] testExpression Boolean value that is expected 264 * to be true. 265 * @param[in] testMsg A message to be printed on failure. 266 * @param[in] lineNumber The line of source in the test file 267 * where this assert is being performed, typically __LINE__. 268 */ 269 void assert( bool testExpression, 270 const std::string& testMsg, 271 const int lineNumber ); 272 273 /** Takes two values of the same type, compares them and passes 274 * the test if the values are equal. 275 * @param[in] expected The expected value to be compared against. 276 * @param[in] got The value produced by the method under test. 277 * @param[in] lineNumber The line of source in the test file 278 * where this assert is being performed, typically __LINE__. 279 * @param[in] testMsg A message to be printed on failure. 280 * A default message will simply say what was expected and 281 * what the value actually was when expected != got. 282 */ 283 template <class T> 284 void assert_equals( const T& expected, 285 const T& got, 286 int lineNumber, 287 const std::string& testMsg = std::string()); 288 289 /** Takes two floating point values, compares them and passes the test 290 * if the values are equal within an epsilon. 291 * @param[in] expected The expected value to be compared against. 292 * @param[in] got The value produced by the method under test. 293 * @param[in] lineNumber The line of source in the test file 294 * where this assert is being performed, typically __LINE__. 295 * @param[in] testMsg A message to be printed on failure. 296 * A default message will simply say what was expected and 297 * what the value actually was when expected != got. 298 * @param[in] epsilon The maximum difference between expected 299 * and got that will be considered "equal". If this number is 300 * less than zero, the type's epsilon is used. 301 */ assert_equals(double expected,double got,int lineNumber,const std::string & testMsg=std::string (),double epsilon=-1)302 void assert_equals( double expected, 303 double got, 304 int lineNumber, 305 const std::string& testMsg = std::string(), 306 double epsilon=-1) 307 {assert_equals_fp<double>(expected, got, lineNumber, testMsg, epsilon);} 308 assert_equals(long double expected,long double got,int lineNumber,const std::string & testMsg=std::string (),long double epsilon=-1)309 void assert_equals( long double expected, 310 long double got, 311 int lineNumber, 312 const std::string& testMsg = std::string(), 313 long double epsilon=-1) 314 {assert_equals_fp<double>(expected, got, lineNumber, testMsg, epsilon);} 315 assert_equals(float expected,float got,int lineNumber,const std::string & testMsg=std::string (),float epsilon=-1)316 void assert_equals( float expected, 317 float got, 318 int lineNumber, 319 const std::string& testMsg = std::string(), 320 float epsilon=-1) 321 {assert_equals_fp<double>(expected, got, lineNumber, testMsg, epsilon);} 322 323 template <typename T> 324 void assert_equals_fp( const T& expected, 325 const T& got, 326 int lineNumber, 327 const std::string& testMsg = std::string(), 328 T epsilon = -1); 329 330 331 /** Takes two matricies, compares them and passes the test 332 * if the values are equal within an epsilon. 333 * @param[in] expected The expected value to be compared against. 334 * @param[in] got The value produced by the method under test. 335 * @param[in] lineNumber The line of source in the test file 336 * where this assert is being performed, typically __LINE__. 337 * @param[in] testMsg A message to be printed on failure. 338 * A default message will simply say what was expected and 339 * what the value actually was when expected != got. 340 * @param[in] epsilon The maximum difference between expected 341 * and got that will be considered "equal". If this number is 342 * less than zero, the type's epsilon is used. 343 */ 344 template<typename T> 345 void assert_equals( const gpstk::Matrix<T>& expected, 346 const gpstk::Matrix<T>& got, 347 int lineNumber, 348 const std::string& testMsg = std::string(), 349 T epsilon = -1); 350 template<typename T> 351 void assert_equals( const gpstk::Vector<T>& expected, 352 const gpstk::Vector<T>& got, 353 int lineNumber, 354 const std::string& testMsg = std::string(), 355 T epsilon = -1); 356 357 /** Compare two text files, line-by-line. Test passes if there 358 * are no differences according to the rules set by parameters. 359 * @param[in] lineNumber The line of source in the test file 360 * where this assert is being performed, typically __LINE__. 361 * @param[in] file1Name The full path to the reference file 362 * being compared against. 363 * @param[in] file2Name The full path to the test output file to 364 * compare to the reference. 365 * @param[in] testMsg A message to be printed on failure. 366 * @param[in] numLinesSkip The number of lines to ignore in the 367 * two files, starting from the beginning. 368 * @param[in] ignoreLeadingSpaces If true, changes are ignored 369 * between the two files in white space at the beginning of a 370 * line. 371 * @param[in] ignoreTrailingSpaces If true, changes are ignored 372 * between the two files in white space at the end of a 373 * line. 374 * @param[in] ignoreRegex An optional vector of regular 375 * expression strings that, if matched in the SOURCE FILE 376 * (i.e. file1name), differences on that line will be ignored. 377 */ 378 void assert_files_equal( int lineNumber, 379 const std::string& file1Name, 380 const std::string& file2Name, 381 const std::string& testMsg, 382 int numLinesSkip=0, 383 bool ignoreLeadingSpaces = false, 384 bool ignoreTrailingSpaces = false, 385 std::vector<std::string> ignoreRegex = std::vector<std::string>(0) ); 386 387 /** Compare two binary files, byte-by-byte. Test passes if there 388 * are no differences in the data. 389 * @param[in] lineNumber The line of source in the test file 390 * where this assert is being performed, typically __LINE__. 391 * @param[in] file1Name The full path to the reference file 392 * being compared against. 393 * @param[in] file2Name The full path to the test output file to 394 * compare to the reference. 395 * @param[in] testMsg A message to be printed on failure. 396 * @param[in] from First byte to compare. 397 * @param[in] to Last byte to compare. 398 */ 399 void assert_binary_files_equal( int lineNumber, 400 const std::string& file1Name, 401 const std::string& file2Name, 402 const std::string& testMsg, 403 unsigned long long from = 0, 404 unsigned long long to = -1); 405 406 /// @return the number of tests that have failed so far. 407 int countFails( void ); 408 409 /// @return the number of tests that have been executed so far. 410 int countTests( void ); 411 412 /** Change the method, function, or feature of the source class 413 * under test in the test output stream. 414 * @param[in] newMethod the name of the method under test */ 415 void changeSourceMethod( const std::string& newMethod ); 416 417 /** Set the message text that is reported when print() is 418 * called, usually a fail message. 419 * @param[in] testMsg the text to be sent to a log, usually to 420 * describe what failed and why. */ 421 void setTestMessage( const std::string& testMsg ); 422 423 /** Set the message text that is reported when print() is 424 * called, usually a fail message. 425 * @param[in] testMsg The text to be sent to a log, usually to 426 * describe what failed and why. 427 * @param[in] lineNumber The line number in the test app where 428 * pass(), fail(), assert() was called */ 429 void setTestMessage( const std::string& testMsg, const int lineNumber ); 430 431 void setTestLine( const int lineNumber ); 432 433 /** Compare two text files for differences, line by line. 434 * @param[in] refFile The reference file to compare against. 435 * @param[in] checkFile The generated file being compared. 436 * @param[in] numLinesSkip Number of lines to ignore at the 437 * start of each file. 438 * @param[in] ignoreLeadingSpaces If true, ignore any changes in 439 * whitespace at the beginning of each line. 440 * @param[in] ignoreTrailingSpaces If true, ignore any changes in 441 * whitespace at the end of each line. 442 * @param[in] ignoreRegex A vector of POSIX regular expressions 443 * that, if matched in refFile, differences in those lines 444 * will be ignored. Regular expressions are not matched 445 * against checkFile. 446 * @return true if the files are equal, false if not. 447 */ 448 bool fileEqualTest( const std::string& refFile, 449 const std::string& checkFile, 450 int numLinesSkip=0, 451 bool ignoreLeadingSpaces = false, 452 bool ignoreTrailingSpaces = false, 453 std::vector<std::string> ignoreRegex = std::vector<std::string>(0) ); 454 455 /** Compare two binary files for differences, byte by byte. 456 * @param[in] refFile The reference file to compare against. 457 * @param[in] checkFile The generated file being compared. 458 * @param[in] from First byte to compare. 459 * @param[in] to Last byte to compare. */ 460 bool fileCompTest( const std::string& refFile, 461 const std::string& checkFile, 462 unsigned long long from = 0, 463 unsigned long long to = -1); 464 465 private: 466 467 // The following are all used as part of the output from 468 // TestUtil::print() to facilitate filtering of output that is 469 // thus printed to stdout 470 471 /// Identifies a stdout line as a test record from this class 472 std::string outputKeyword; 473 /// help locate source class causing a test failure 474 std::string sourceClass; 475 /// help locate source method causing a test failure 476 std::string sourceMethod; 477 /// help locate test file that discovered a failure 478 std::string testFileName; 479 /// help locate test line where the failure occured 480 std::string testFileLine; 481 482 /** if failBit==1 && verbosity>=1, print this string description 483 * of why the test failed to be set by the test app developer */ 484 std::string testMessage; 485 486 /// store the result of a test (0=pass, 1=fail) 487 int failBit; 488 /** if verbosity>=0, print summary line; if verbosity>=1, print 489 * testMessage when fail() is called. */ 490 int verbosity; 491 492 // since single test methods may contain multiple subtests. 493 494 int testCount; ///< Count of tests that have been run 495 int subtestID; ///< ID of the current sub-test, used in TestUtil::print() 496 int failCount; ///< Count of tests that have failed 497 498 499 500 /** Print test results and information on classes being tested 501 * to stdout in a common format that is both human-readable and 502 * easy to filter using tools like grep so as to help isolate 503 * where problems are happening. */ 504 void print( void ); 505 506 /** Pass the test! Record a pass by setting the failBit=0 and 507 * incrementing the testCount */ 508 void pass( void ); 509 510 /** Fail the test! Record a failure by setting the failBit and 511 * incrementing failCount. */ 512 void fail( void ); 513 514 /** Fail the test! Record a failure by setting the failBit and 515 * incrementing failCount. */ 516 void fail( const std::string& failMsg ); 517 518 /** Fail the test! Record a failure by setting the failBit and 519 * incrementing failCount. */ 520 void fail( const std::string& failMsg, const int lineNumber ); 521 522 /** Increment the failCount and reset subtestID based on current 523 * testCount. */ 524 void next( void ); 525 526 /** Undo the test! Undo a pass/fail by unsetting failBit and 527 * decrementing failCount (only if failed) and decrementing the 528 * testCount. */ 529 void undo( void ); 530 }; // class TestUtil 531 532 533 534 TestUtil :: TestUtil(const std::string & sourceClassInput,const std::string & sourceMethodInput,const std::string & testFileInput,const int & testLineInput,const int & verbosityInput)535 TestUtil( const std::string& sourceClassInput, 536 const std::string& sourceMethodInput, 537 const std::string& testFileInput, 538 const int& testLineInput, 539 const int& verbosityInput ) 540 : outputKeyword( "GPSTkTest" ), 541 sourceClass( sourceClassInput ), 542 sourceMethod( sourceMethodInput ), 543 testFileName( testFileInput ), 544 testFileLine( "0" ), 545 testMessage( "Developer is a lazy slacker" ), 546 failBit( 0 ), 547 verbosity( verbosityInput ), 548 testCount( 0 ), 549 subtestID( 1 ), 550 failCount( 0 ) 551 { 552 // convert int to string 553 setTestLine( testLineInput ); 554 555 // strip off the path from the full-path filename 556 // so that "/home/user/test.txt" becomes "test.txt" 557 std::string file_sep = gpstk::getFileSep(); 558 testFileName = testFileName.substr( 559 testFileName.find_last_of( file_sep ) + 1 ); 560 } 561 562 563 void TestUtil :: assert(bool testExpression,const std::string & testMsg,const int lineNumber)564 assert( bool testExpression, 565 const std::string& testMsg, 566 const int lineNumber ) 567 { 568 setTestMessage( testMsg ); 569 setTestLine( lineNumber ); 570 571 if( testExpression == false ) 572 { 573 fail(); 574 } 575 else 576 { 577 pass(); 578 } 579 580 print(); 581 next(); 582 } 583 584 585 template <class T> 586 void TestUtil :: assert_equals(const T & expected,const T & got,int lineNumber,const std::string & testMsg)587 assert_equals( const T& expected, 588 const T& got, 589 int lineNumber, 590 const std::string& testMsg) 591 { 592 std::string mess(testMsg); 593 if (testMsg.empty()) 594 { 595 std::ostringstream ostr; 596 ostr << "Expected:'" << expected << "' ,But got:'" << got << "'"; 597 mess = ostr.str(); 598 } 599 assert(expected == got, mess, lineNumber); 600 } 601 602 603 template<typename T> 604 void TestUtil :: assert_equals_fp(const T & expected,const T & got,int lineNumber,const std::string & testMsg,T epsilon)605 assert_equals_fp( const T& expected, 606 const T& got, 607 int lineNumber, 608 const std::string& testMsg, 609 T epsilon ) 610 { 611 T err = std::abs(expected - got); 612 if (epsilon < 0) 613 epsilon = std::numeric_limits<T>::epsilon(); 614 615 bool good = err < epsilon; 616 std::string mess(testMsg); 617 if (testMsg.empty()) 618 { 619 std::ostringstream ostr; 620 ostr << "abs(" << expected << " - " << got << ") = " << err; 621 if (good) 622 ostr << " <= "; 623 else 624 ostr << " > "; 625 ostr << epsilon; 626 mess = ostr.str(); 627 } 628 assert(good, mess, lineNumber); 629 } 630 631 632 template<typename T> 633 void TestUtil :: assert_equals(const gpstk::Matrix<T> & expected,const gpstk::Matrix<T> & got,int lineNumber,const std::string & testMsg,T epsilon)634 assert_equals( const gpstk::Matrix<T>& expected, 635 const gpstk::Matrix<T>& got, 636 int lineNumber, 637 const std::string& testMsg, 638 T epsilon ) 639 { 640 if (epsilon < 0) 641 epsilon = std::numeric_limits<T>::epsilon(); 642 643 std::string mess(testMsg); 644 T mag = maxabs(expected - got); 645 if (testMsg.empty()) 646 { 647 std::ostringstream ostr; 648 ostr << "maxabs(expected-computed) = " << mag; 649 mess = ostr.str(); 650 } 651 assert_equals(T(mag) , T(0.0), lineNumber, mess, epsilon); 652 } 653 654 655 template<typename T> 656 void TestUtil :: assert_equals(const gpstk::Vector<T> & expected,const gpstk::Vector<T> & got,int lineNumber,const std::string & testMsg,T epsilon)657 assert_equals( const gpstk::Vector<T>& expected, 658 const gpstk::Vector<T>& got, 659 int lineNumber, 660 const std::string& testMsg, 661 T epsilon ) 662 { 663 if (epsilon < 0) 664 epsilon = std::numeric_limits<T>::epsilon(); 665 std::string mess(testMsg); 666 T mag = maxabs(expected - got); 667 if (testMsg.empty()) 668 { 669 std::ostringstream ostr; 670 ostr << "absmag(expected-computed) = " << mag; 671 mess = ostr.str(); 672 } 673 assert_equals(T(mag) , T(0.0), lineNumber, mess, epsilon); 674 } 675 676 void TestUtil :: assert_files_equal(int lineNumber,const std::string & file1Name,const std::string & file2Name,const std::string & testMsg,int numLinesSkip,bool ignoreLeadingSpaces,bool ignoreTrailingSpaces,std::vector<std::string> ignoreRegex)677 assert_files_equal( int lineNumber, 678 const std::string& file1Name, 679 const std::string& file2Name, 680 const std::string& testMsg, 681 int numLinesSkip, 682 bool ignoreLeadingSpaces, 683 bool ignoreTrailingSpaces, 684 std::vector<std::string> ignoreRegex ) 685 { 686 bool eq = fileEqualTest( 687 file1Name, file2Name, numLinesSkip, ignoreLeadingSpaces, 688 ignoreTrailingSpaces, ignoreRegex ); 689 assert(eq, testMsg, lineNumber); 690 } 691 692 693 void TestUtil :: assert_binary_files_equal(int lineNumber,const std::string & file1Name,const std::string & file2Name,const std::string & testMsg,unsigned long long from,unsigned long long to)694 assert_binary_files_equal( int lineNumber, 695 const std::string& file1Name, 696 const std::string& file2Name, 697 const std::string& testMsg, 698 unsigned long long from, 699 unsigned long long to) 700 { 701 bool eq = fileCompTest(file1Name, file2Name, from, to); 702 assert(eq, testMsg, lineNumber); 703 } 704 705 706 int TestUtil :: countFails(void)707 countFails( void ) 708 { 709 return( failCount ); 710 } 711 712 713 int TestUtil :: countTests(void)714 countTests( void ) 715 { 716 return( testCount ); 717 } 718 719 720 void TestUtil :: changeSourceMethod(const std::string & newMethod)721 changeSourceMethod( const std::string& newMethod ) 722 { 723 sourceMethod = newMethod; 724 } 725 726 727 void TestUtil :: setTestMessage(const std::string & testMsg)728 setTestMessage( const std::string& testMsg ) 729 { 730 testMessage = testMsg; 731 } 732 733 void TestUtil :: setTestMessage(const std::string & testMsg,const int lineNumber)734 setTestMessage( const std::string& testMsg, const int lineNumber ) 735 { 736 setTestMessage( testMsg ); 737 setTestLine( lineNumber ); 738 } 739 740 741 void TestUtil :: setTestLine(const int lineNumber)742 setTestLine( const int lineNumber ) 743 { 744 std::ostringstream conversionStringStream; 745 conversionStringStream << lineNumber; 746 testFileLine = conversionStringStream.str(); 747 } 748 749 750 bool TestUtil :: fileEqualTest(const std::string & refFile,const std::string & checkFile,int numLinesSkip,bool ignoreLeadingSpaces,bool ignoreTrailingSpaces,std::vector<std::string> ignoreRegex)751 fileEqualTest( const std::string& refFile, 752 const std::string& checkFile, 753 int numLinesSkip, 754 bool ignoreLeadingSpaces, 755 bool ignoreTrailingSpaces, 756 std::vector<std::string> ignoreRegex ) 757 { 758 int lineNumber = 0; 759 bool filesEqual = false; 760 std::ifstream refStream; 761 std::ifstream checkStream; 762 std::string refLine; 763 std::string checkLine; 764 765 refStream.open( refFile.c_str() ); 766 checkStream.open( checkFile.c_str() ); 767 768 // Compare each line until you reach the end of Ref 769 while( !refStream.eof() ) 770 { 771 lineNumber++; 772 773 // If we reach the end of Check, but there is 774 // more left in Ref, then they are not equal 775 if( checkStream.eof() ) 776 { 777 filesEqual = false; 778 return( filesEqual ); 779 } 780 781 // get the next line and compare 782 getline( refStream, refLine ); 783 getline( checkStream, checkLine ); 784 785 if (lineNumber <= numLinesSkip) 786 continue; 787 788 if (ignoreLeadingSpaces) 789 { 790 std::size_t idx = refLine.find_first_not_of(" \t\r\n\f\v"); 791 if (idx != std::string::npos) 792 refLine.erase(0,idx-1); 793 idx = checkLine.find_first_not_of(" \t\r\n\f\v"); 794 if (idx != std::string::npos) 795 checkLine.erase(0,idx-1); 796 } 797 if (ignoreTrailingSpaces) 798 { 799 std::size_t idx = refLine.find_last_not_of(" \t\r\n\f\v"); 800 if (idx != std::string::npos) 801 refLine.erase(idx+1); 802 else 803 refLine.clear(); // all whitespace 804 idx = checkLine.find_last_not_of(" \t\r\n\f\v"); 805 if (idx != std::string::npos) 806 checkLine.erase(idx+1); 807 else 808 checkLine.clear(); // all whitespace 809 } 810 if (!ignoreRegex.empty()) 811 { 812 // check for regular expressions 813 // Use a flag because break/continue in C++ doesn't 814 // allow you to skip multiple levels. 815 bool ignore = false; 816 for (int i = 0; i < ignoreRegex.size(); i++) 817 { 818 if (gpstk::StringUtils::isLike(refLine, ignoreRegex[i])) 819 { 820 ignore = true; 821 break; 822 } 823 } 824 if (ignore) 825 continue; 826 } 827 828 // only fail if you find differences AFTER the skipped lines 829 if (refLine != checkLine) 830 { 831 filesEqual = false; 832 return( filesEqual ); 833 } 834 } 835 836 // If we reach the end of Ref, but there is 837 // more left in Check, then they are not equal 838 if( !checkStream.eof() ) 839 { 840 filesEqual = false; 841 if (verbosity>1) 842 std::cout << "refLine:" << refLine << std::endl 843 << "checkLine:" << checkLine << std::endl; 844 return( filesEqual ); 845 } 846 else 847 { 848 filesEqual = true; 849 return( filesEqual ); 850 } 851 } 852 853 854 bool TestUtil :: fileCompTest(const std::string & refFile,const std::string & checkFile,unsigned long long from,unsigned long long to)855 fileCompTest( const std::string& refFile, 856 const std::string& checkFile, 857 unsigned long long from, 858 unsigned long long to) 859 { 860 static const unsigned bufsize = 4096; 861 unsigned readsize = bufsize; 862 bool done = false; 863 std::vector<char> refBuf(bufsize, 0), checkBuf(bufsize, 0); 864 std::ifstream ref(refFile.c_str()), check(checkFile.c_str()); 865 if (!ref || !check) 866 return false; // missing or inaccessible file 867 // get the file sizes 868 unsigned long long refSize, checkSize, curPos; 869 ref.seekg(0, std::ios_base::end); 870 refSize = ref.tellg(); 871 check.seekg(0, std::ios_base::end); 872 checkSize = check.tellg(); 873 if (refSize != checkSize) 874 return false; // files not the same size 875 // set our limit to the smaller of the file size and "to" 876 to = std::min(to, refSize); 877 if (!ref.seekg(from, std::ios_base::beg)) 878 return false; // seek failure, usually file too short 879 if (!check.seekg(from, std::ios_base::beg)) 880 return false; // seek failure, usually file too short 881 882 while (!done) 883 { 884 curPos = ref.tellg(); 885 // stop where requested 886 if ((curPos + readsize) > to) 887 { 888 readsize = 1+(to - curPos); 889 done = true; 890 } 891 ref.read(&refBuf[0], readsize); 892 check.read(&checkBuf[0], readsize); 893 if (refBuf != checkBuf) 894 return false; 895 } 896 return true; 897 } 898 899 900 void TestUtil :: print(void)901 print( void ) 902 { 903 // print test summary description and result to stdout 904 if( failBit==1 && verbosity >=1 ) 905 { 906 std::cout 907 << outputKeyword << ", " 908 << "Class=" << sourceClass << ", " 909 << "Method=" << sourceMethod << ", " 910 << "testFile=" << testFileName << ", " 911 << "testLine=" << testFileLine << ", " 912 << "subtest=" << subtestID << ", " 913 << "failBit=" << failBit << ", " 914 << "testMsg=" << testMessage 915 << std::endl; // implicit conversion from int to string 916 } 917 else 918 { 919 std::cout 920 << outputKeyword << ", " 921 << "Class=" << sourceClass << ", " 922 << "Method=" << sourceMethod << ", " 923 << "testFile=" << testFileName << ", " 924 << "testLine=" << testFileLine << ", " 925 << "subtest=" << subtestID << ", " 926 << "failBit=" << failBit 927 << std::endl; // implicit conversion from int to string 928 } 929 } 930 931 932 void TestUtil :: pass(void)933 pass( void ) 934 { 935 failBit = 0; 936 testCount++; 937 } 938 939 940 void TestUtil :: fail(void)941 fail( void ) 942 { 943 failBit = 1; 944 failCount++; 945 testCount++; 946 } 947 948 949 void TestUtil :: fail(const std::string & failMsg)950 fail( const std::string& failMsg ) 951 { 952 setTestMessage( failMsg ); 953 fail(); 954 } 955 956 957 void TestUtil :: fail(const std::string & failMsg,const int lineNumber)958 fail( const std::string& failMsg, const int lineNumber ) 959 { 960 setTestMessage( failMsg ); 961 setTestLine( lineNumber ); 962 fail(); 963 } 964 965 966 void TestUtil :: next(void)967 next( void ) 968 { 969 // increment subtest counter/ID 970 subtestID = countTests() + 1; 971 972 // reset fail parameters for next/new subtest 973 failBit = 0; 974 testMessage = "Developer is a lazy slacker"; 975 } 976 977 978 void TestUtil :: undo(void)979 undo( void ) 980 { 981 if( failBit==1 ) 982 { 983 failBit = 0; 984 failCount--; 985 testCount--; 986 } 987 else 988 { 989 failBit = 0; 990 testCount--; 991 } 992 next(); 993 } 994 } 995