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