BASIS  r3148
testdriver.hxx
Go to the documentation of this file.
00001 /**
00002  * @file  testdriver.hxx
00003  * @brief Default test driver implementation.
00004  *
00005  * Copyright (c) 2011, 2012 University of Pennsylvania. All rights reserved.<br />
00006  * See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007  *
00008  * Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009  */
00010 
00011 #pragma once
00012 #ifndef _BASIS_TESTDRIVER_HXX
00013 #define _BASIS_TESTDRIVER_HXX
00014 
00015 
00016 #include <iterator>
00017 
00018 #if WINDOWS
00019 #  include <Winsock2.h> // gethostname()
00020 #  ifdef max
00021 #    undef max
00022 #  endif
00023 #  pragma comment(lib, "Ws2_32.lib")
00024 #else
00025 #  include <unistd.h>   // gethostname()
00026 #endif
00027 
00028 #ifdef ITK_VERSION
00029 #  include "testdriver-itk.hxx"
00030 #endif
00031 
00032 
00033 // ===========================================================================
00034 // initialization
00035 // ===========================================================================
00036 
00037 // ---------------------------------------------------------------------------
00038 void testdriversetup(int* argc, char** argv[])
00039 {
00040     try {
00041         string name;
00042         #ifdef TESTDRIVER_NAME
00043             name = TESTDRIVER_NAME;
00044         #else
00045             name = "testdriver";
00046         #endif
00047         #ifdef ITK_VERSION
00048             name += " build with ITK ";
00049             name += ITK_VERSION;
00050         #endif
00051 
00052         // -------------------------------------------------------------------
00053         // construct command-line
00054         CmdLine cmd(
00055                 // program identification
00056                 name, PROJECT,
00057                 // description
00058                 "This program alters the environment, runs a test and "
00059                 "compares the output image to one or more baseline images.",
00060                 // example usage
00061                 "EXENAME GaussFilter --compare output.nii baseline.nii"
00062                 "\n"
00063                 "Runs the test GaussFilter which presumably writes the"
00064                 " gaussian smoothed image to the image file output.nii."
00065                 " Compares the image produced by the test to the reference"
00066                 " image named baseline.nii with default intensity tolerance.",
00067                 // version information
00068                 RELEASE, "2011, 2012 University of Pennsylvania");
00069 
00070         cmd.add(add_before_libpath);
00071         cmd.add(add_before_env);
00072         cmd.add(clean_cwd_before_test);
00073         cmd.add(clean_cwd_after_test);
00074         cmd.add(diff);
00075         cmd.add(diff_lines);
00076         cmd.add(compare);
00077         cmd.add(max_number_of_differences);
00078         cmd.add(intensity_tolerance);
00079         cmd.add(tolerance_radius);
00080         cmd.add(orientation_insensitive);
00081         cmd.add(redirect_output);
00082         cmd.add(max_number_of_threads);
00083         cmd.add(full_output);
00084         cmd.add(verbose);
00085 
00086         #ifdef BASIS_STANDALONE_TESTDRIVER
00087         cmd.xorAdd(noprocess, testcmd);
00088         #else
00089         cmd.add(testcmd);
00090         #endif
00091 
00092         // -------------------------------------------------------------------
00093         // parse command-line
00094         cmd.parse(*argc, *argv);
00095 
00096         // -------------------------------------------------------------------
00097         // rearrange argc and argv of main()
00098         if (testcmd.isSet()) {
00099             for (unsigned int i = 0; i < testcmd.getValue().size(); i++) {
00100                 for (int j = 1; j < (*argc); j++) {
00101                     if (testcmd.getValue()[i] == (*argv)[j]) {
00102                         (*argv)[i + 1] = (*argv)[j];
00103                         break;
00104                     }
00105                 }
00106             }
00107             *argc = static_cast<int>(testcmd.getValue().size()) + 1;
00108             (*argv)[*argc] = NULL;
00109         } else {
00110             *argc = 1;
00111             (*argv)[1] = NULL;
00112         }
00113 
00114         // Reset ignoring flag of TCLAP library. Otherwise, when a test
00115         // uses the TCLAP library to parse its arguments, the labeled
00116         // arguments will be immediately ignored.
00117         // This required the addition of the stopIgnoring() method to TCLAP::Arg.
00118         TCLAP::Arg::stopIgnoring();
00119 
00120     // -----------------------------------------------------------------------
00121     // catch specification exceptions - parse errors are already taken care of
00122     } catch (CmdLineException& e) {
00123         cerr << e.error() << endl;
00124         exit(1);
00125     }
00126 
00127     // -----------------------------------------------------------------------
00128     // add host name as Dart/CDash measurement
00129     char hostname[256] = "unknown";
00130     #if WINDOWS
00131         WSADATA wsaData;
00132         WSAStartup(MAKEWORD(2, 2), &wsaData);
00133         gethostname(hostname, sizeof(hostname));
00134         WSACleanup();
00135     #else
00136         gethostname(hostname, sizeof(hostname));
00137     #endif
00138     hostname[255] = '\0';
00139 
00140     cout << "<DartMeasurement name=\"Host Name\" type=\"string\">";
00141     cout << hostname;
00142     cout <<  "</DartMeasurement>" << endl;
00143 
00144     cout << "<DartMeasurement name=\"Working Directory\" type=\"string\">";
00145     cout << os::getcwd();
00146     cout <<  "</DartMeasurement>" << endl;
00147 
00148     #ifdef ITK_VERSION
00149     cout << "<DartMeasurement name=\"ITK Version\" type=\"string\">";
00150     cout << ITK_VERSION;
00151     cout <<  "</DartMeasurement>" << endl;
00152     #endif
00153 
00154     // -----------------------------------------------------------------------
00155     // register ITK IO factories
00156     #ifdef ITK_VERSION
00157         RegisterRequiredFactories();
00158     #endif
00159 }
00160 
00161 // ===========================================================================
00162 // low-level file comparison
00163 // ===========================================================================
00164 
00165 // ---------------------------------------------------------------------------
00166 void BinaryDiffVisitor::visit()
00167 {
00168     assert(diff.getValue().size() != 0);
00169     assert((diff.getValue().size() % 2) == 0);
00170 
00171     RegressionTest regression_test;
00172 
00173     regression_test.test_file                 = diff.getValue()[diff.getValue().size() - 2];
00174     regression_test.baseline_file             = diff.getValue()[diff.getValue().size() - 1];
00175     regression_test.intensity_tolerance       = 0.0f;
00176     regression_test.max_number_of_differences = 0;
00177     regression_test.tolerance_radius          = 0;
00178     regression_test.orientation_insensitive   = false;
00179     regression_test.method                    = BINARY_DIFF;
00180 
00181     regression_tests.push_back(regression_test);
00182 }
00183 
00184 // ---------------------------------------------------------------------------
00185 int binary_diff(const char* testfile, const char* baseline)
00186 {
00187     int retval = 0;
00188     ifstream ift(testfile, ios::binary);
00189     ifstream ifb(baseline, ios::binary);
00190     if (!ift) return -1;
00191     if (!ifb) return -2;
00192     istream_iterator<unsigned char> eos; // end-of-stream
00193     istream_iterator<unsigned char> it(ift);
00194     istream_iterator<unsigned char> ib(ifb);
00195     while (it != eos && ib != eos) {
00196         if (*it != *ib) break;
00197         ++it;
00198         ++ib;
00199     }
00200     if (it != eos || ib != eos) retval = 1;
00201     ift.close();
00202     ifb.close();
00203     return retval;
00204 }
00205 
00206 // ---------------------------------------------------------------------------
00207 void LineDiffVisitor::visit()
00208 {
00209     assert(diff_lines.getValue().size() != 0);
00210     assert((diff_lines.getValue().size() % 2) == 0);
00211 
00212     RegressionTest regression_test;
00213 
00214     regression_test.test_file                 = diff_lines.getValue()[diff_lines.getValue().size() - 2];
00215     regression_test.baseline_file             = diff_lines.getValue()[diff_lines.getValue().size() - 1];
00216     regression_test.intensity_tolerance       = 0.0f;
00217     regression_test.max_number_of_differences = max_number_of_differences.getValue();
00218     regression_test.tolerance_radius          = 0;
00219     regression_test.orientation_insensitive   = false;
00220     regression_test.method                    = DIFF_LINES;
00221 
00222     regression_tests.push_back(regression_test);
00223 }
00224 
00225 // ---------------------------------------------------------------------------
00226 int text_diff_lines(const char* testfile, const char* baseline, unsigned int max_number_of_differences)
00227 {
00228     int retval = 0;
00229     ifstream ift(testfile);
00230     ifstream ifb(baseline);
00231     if (!ift) return -1;
00232     if (!ifb) return -2;
00233     string tline, bline;
00234     while (getline(ift, tline) && getline(ifb, bline)) {
00235         if (tline != bline) retval++;
00236     }
00237     if (static_cast<unsigned int>(retval) <= max_number_of_differences) retval = 0;
00238     if (getline(ift, tline) || getline(ifb, bline)) retval = 1000;
00239     ift.close();
00240     ifb.close();
00241     return retval;
00242 }
00243 
00244 // ===========================================================================
00245 // image regression testing
00246 // ===========================================================================
00247 
00248 // ---------------------------------------------------------------------------
00249 void CompareVisitor::visit()
00250 {
00251     assert(compare.getValue().size() != 0);
00252     assert((compare.getValue().size() % 2) == 0);
00253 
00254     RegressionTest regression_test;
00255 
00256     regression_test.test_file                 = compare.getValue()[compare.getValue().size() - 2];
00257     regression_test.baseline_file             = compare.getValue()[compare.getValue().size() - 1];
00258     regression_test.intensity_tolerance       = intensity_tolerance.getValue();
00259     regression_test.max_number_of_differences = max_number_of_differences.getValue();
00260     regression_test.tolerance_radius          = tolerance_radius.getValue();
00261     regression_test.orientation_insensitive   = orientation_insensitive.getValue();
00262     regression_test.method                    = COMPARE_IMAGES;
00263 
00264     regression_tests.push_back(regression_test);
00265 }
00266 
00267 // ---------------------------------------------------------------------------
00268 vector<string> get_baseline_filenames(string filename_template)
00269 {
00270     vector<string> baselines;
00271 
00272     ifstream ifs(filename_template.c_str());
00273     if (ifs) baselines.push_back(filename_template);
00274 
00275     int               x   = 0;
00276     string::size_type pos = filename_template.rfind(".");
00277     string            suffix;
00278 
00279     if (pos != string::npos) {
00280         suffix = filename_template.substr(pos);
00281         filename_template.erase(pos);
00282     }
00283     while (++x) {
00284         ostringstream filename;
00285         filename << filename_template << '.' << x << suffix;
00286         ifstream ifs(filename.str().c_str());
00287         if (!ifs) break;
00288         ifs.close();
00289         baselines.push_back(filename.str());
00290     }
00291     return baselines;
00292 }
00293 
00294 // ---------------------------------------------------------------------------
00295 int image_regression_test(const char*  imagefile,
00296                           const char*  baseline,
00297                           double       intensity_tolerance,
00298                           unsigned int max_number_of_differences,
00299                           unsigned int tolerance_radius,
00300                           bool         orientation_insensitive,
00301                           int          report)
00302 {
00303     #ifdef ITK_VERSION
00304         return RegressionTestImage(imagefile,
00305                                    baseline,
00306                                    report,
00307                                    intensity_tolerance,
00308                                    max_number_of_differences,
00309                                    tolerance_radius,
00310                                    orientation_insensitive);
00311     #else
00312         BASIS_THROW(runtime_error,
00313                     "Not implemented yet! Use ITK implementation instead, i.e.,"
00314                     << " install ITK 3.14 or greater (including versions after 4.0)"
00315                     << " and reconfigure the build tree of " << PROJECT << ". Ensure that"
00316                     << " the ITK_DIR variable is set to the directory of the ITKConfig.cmake file"
00317                     << " and that the variable USE_ITK is set to ON. Then rebuild " << PROJECT
00318                     << " and optionally install it again.");
00319     #endif
00320 }
00321 
00322 
00323 #endif // _BASIS_TESTDRIVER_HXX