00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 #ifndef __PION_TEST_UNIT_TEST_HEADER__
00011 #define __PION_TEST_UNIT_TEST_HEADER__
00012 
00013 #include <iostream>
00014 #include <fstream>
00015 #include <boost/version.hpp>
00016 #include <boost/thread/mutex.hpp>
00017 #include <boost/thread/condition.hpp>
00018 #include <boost/test/unit_test.hpp>
00019 #include <boost/test/unit_test_log.hpp>
00020 #include <boost/test/unit_test_log_formatter.hpp>
00021 #include <boost/test/test_case_template.hpp>
00022 #include <boost/test/utils/xml_printer.hpp>
00023 #include <pion/logger.hpp>
00024 
00025 #ifdef _MSC_VER
00026     #include <direct.h>
00027     #define CHANGE_DIRECTORY _chdir
00028     #define GET_DIRECTORY(a,b) _getcwd(a,b)
00029 #else
00030     #include <unistd.h>
00031     #define CHANGE_DIRECTORY chdir
00032     #define GET_DIRECTORY(a,b) getcwd(a,b)
00033 #endif
00034 
00035 #define DIRECTORY_MAX_SIZE 1000
00036 
00037 
00038 namespace pion {    
00039 namespace test {    
00040     
00042     class safe_xml_log_formatter
00043         : public boost::unit_test::unit_test_log_formatter
00044     {
00045     public:
00046         
00048         safe_xml_log_formatter()
00049             : m_entry_in_progress(false)
00050         {}
00051         
00053         virtual ~safe_xml_log_formatter() {}
00054     
00056         virtual void log_start(std::ostream& ostr,
00057                                boost::unit_test::counter_t test_cases_amount )
00058         {
00059             ostr << "<TestLog>" << std::endl;
00060         }
00061     
00063         virtual void log_finish(std::ostream& ostr)
00064         {
00065             ostr << "</TestLog>" << std::endl;
00066         }
00067     
00069         virtual void log_build_info(std::ostream& ostr)
00070         {
00071             ostr  << "<BuildInfo"
00072                 << " platform"  << attr_value() << BOOST_PLATFORM
00073                 << " compiler"  << attr_value() << BOOST_COMPILER
00074                 << " stl"       << attr_value() << BOOST_STDLIB
00075                 << " boost=\""  << BOOST_VERSION/100000     << "."
00076                 << BOOST_VERSION/100 % 1000 << "."
00077                 << BOOST_VERSION % 100      << '\"'
00078                 << "/>" << std::endl;
00079         }
00080     
00082         virtual void test_unit_start(std::ostream& ostr,
00083                                      boost::unit_test::test_unit const& tu )
00084         {
00085             ostr << "<" << tu_type_name( tu ) << " name" << attr_value() << tu.p_name.get() << ">" << std::endl;
00086         }
00087     
00089         virtual void test_unit_finish(std::ostream& ostr,
00090                                       boost::unit_test::test_unit const& tu,
00091                                       unsigned long elapsed )
00092         {
00093             if ( tu.p_type == boost::unit_test::tut_case )
00094                 ostr << "<TestingTime>" << elapsed << "</TestingTime>";
00095             ostr << "</" << tu_type_name( tu ) << ">" << std::endl;
00096         }
00097     
00099         virtual void test_unit_skipped(std::ostream& ostr,
00100                                        boost::unit_test::test_unit const& tu )
00101         {
00102             ostr << "<" << tu_type_name( tu )
00103                 << " name"    << attr_value() << tu.p_name.get()
00104                 << " skipped" << attr_value() << "yes"
00105                 << "/>" << std::endl;
00106         }
00107     
00109         virtual void log_exception(std::ostream& ostr,
00110                                    boost::unit_test::log_checkpoint_data const& checkpoint_data,
00111                                    boost::execution_exception const& ex )
00112         {
00113             boost::execution_exception::location const& loc = ex.where();
00114             
00115             ostr << "<Exception file" << attr_value() << loc.m_file_name
00116                 << " line" << attr_value() << loc.m_line_num;
00117             
00118             if( !loc.m_function.is_empty() )
00119                 ostr << " function"   << attr_value() << loc.m_function;
00120             
00121             ostr << ">" << boost::unit_test::cdata() << ex.what();
00122             
00123             if( !checkpoint_data.m_file_name.is_empty() ) {
00124                 ostr << "<LastCheckpoint file" << attr_value() << checkpoint_data.m_file_name
00125                     << " line"                << attr_value() << checkpoint_data.m_line_num
00126                     << ">"
00127                     << boost::unit_test::cdata() << checkpoint_data.m_message
00128                     << "</LastCheckpoint>";
00129             }
00130             
00131             ostr << "</Exception>" << std::endl;
00132         }
00133     
00135         virtual void log_entry_start( std::ostream& ostr,
00136                                      boost::unit_test::log_entry_data const& entry_data,
00137                                      log_entry_types let )
00138         {
00139             boost::mutex::scoped_lock entry_lock(m_mutex);
00140             while (m_entry_in_progress) {
00141                 m_entry_complete.wait(entry_lock);
00142             }
00143             m_entry_in_progress = true;
00144             
00145             static boost::unit_test::literal_string xml_tags[] = { "Info", "Message", "Warning", "Error", "FatalError" };
00146             m_curr_tag = xml_tags[let];
00147             ostr << '<' << m_curr_tag
00148                 << BOOST_TEST_L( " file" ) << attr_value() << entry_data.m_file_name
00149                 << BOOST_TEST_L( " line" ) << attr_value() << entry_data.m_line_num
00150                 << BOOST_TEST_L( "><![CDATA[" );
00151 
00152             ostr.flush();
00153         }
00154     
00157         virtual void log_entry_value( std::ostream& ostr, boost::unit_test::const_string value )
00158         {
00159             boost::mutex::scoped_lock entry_lock(m_mutex);
00160             if (m_entry_in_progress) {
00161                 ostr << value;
00162                 ostr.flush();
00163             }
00164         }
00165         
00168         virtual void log_entry_finish( std::ostream& ostr )
00169         {
00170             boost::mutex::scoped_lock entry_lock(m_mutex);
00171             if (m_entry_in_progress) {
00172                 ostr << BOOST_TEST_L( "]]></" ) << m_curr_tag << BOOST_TEST_L( ">" ) << std::endl;
00173                 m_curr_tag.clear();
00174                 m_entry_in_progress = false;
00175                 m_entry_complete.notify_one();
00176             }
00177         }
00178         
00179     private:
00180 
00182         static boost::unit_test::const_string tu_type_name( boost::unit_test::test_unit const& tu )
00183         {
00184             return tu.p_type == boost::unit_test::tut_case ? "TestCase" : "TestSuite";
00185         }
00186         
00188         typedef boost::unit_test::attr_value    attr_value;
00189         
00191         volatile bool       m_entry_in_progress;
00192         
00194         boost::condition    m_entry_complete;
00195         
00197         boost::mutex        m_mutex;
00198 
00200         boost::unit_test::const_string  m_curr_tag;
00201     };
00202     
00203     
00209     struct config {
00210         config() {
00211             std::cout << "global setup for all pion unit tests\n";
00212             
00213             
00214             int argc = boost::unit_test::framework::master_test_suite().argc;
00215             char** argv = boost::unit_test::framework::master_test_suite().argv;
00216             bool verbose = false;
00217 
00218             if (argc > 1) {
00219                 if (argv[1][0] == '-' && argv[1][1] == 'v') {
00220                     verbose = true;
00221                 } else if (strlen(argv[1]) > 13 && strncmp(argv[1], "--log_output=", 13) == 0) {
00222                     const char * const test_log_filename = argv[1] + 13;
00223                     m_test_log_file.open(test_log_filename);
00224                     if (m_test_log_file.is_open()) {
00225                         boost::unit_test::unit_test_log.set_stream(m_test_log_file);
00226                         boost::unit_test::unit_test_log.set_formatter(new safe_xml_log_formatter);
00227                     } else {
00228                         std::cerr << "unable to open " << test_log_filename << std::endl;
00229                     }
00230                 }
00231             }
00232     
00233             if (verbose) {
00234                 PION_LOG_CONFIG_BASIC;
00235             } else {
00236                 std::cout << "Use '-v' to enable logging of errors and warnings from pion.\n";
00237             }
00238 
00239             pion::logger log_ptr = PION_GET_LOGGER("pion");
00240             PION_LOG_SETLEVEL_WARN(log_ptr);
00241         }
00242         virtual ~config() {
00243             std::cout << "global teardown for all pion unit tests\n";
00244         }
00245 
00247         static std::ofstream    m_test_log_file;
00248     };
00249     
00250 
00251     
00252     static inline char* trim(char* str) {
00253         for (long len = strlen(str) - 1; len >= 0; len--) {
00254             if (str[len] == '\n' || str[len] == '\r')
00255                 str[len] = '\0';
00256             else
00257                 break;
00258         }
00259         return str;
00260     }
00261 
00262     
00263     
00264     static inline bool read_lines_from_file(const std::string& filename, std::list<std::string>& lines) {
00265         
00266         std::ifstream a_file(filename.c_str(), std::ios::in | std::ios::binary);
00267         if (! a_file.is_open())
00268             return false;
00269 
00270         
00271         static const unsigned int BUF_SIZE = 4096;
00272         char *ptr, buf[BUF_SIZE+1];
00273         buf[BUF_SIZE] = '\0';
00274         lines.clear();
00275 
00276         while (a_file.getline(buf, BUF_SIZE)) {
00277             ptr = trim(buf);
00278             if (*ptr != '\0' && *ptr != '#')
00279                 lines.push_back(ptr);
00280         }
00281 
00282         
00283         a_file.close();
00284 
00285         return true;
00286     }
00287 
00288     
00289     
00290     static inline bool check_files_match(const std::string& fileA, const std::string& fileB) {
00291         
00292         std::list<std::string> a_lines, b_lines;
00293         BOOST_REQUIRE(read_lines_from_file(fileA, a_lines));
00294         BOOST_REQUIRE(read_lines_from_file(fileB, b_lines));
00295 
00296         
00297         a_lines.sort();
00298         b_lines.sort();
00299 
00300         
00301         return (a_lines == b_lines);
00302     }
00303 
00304     static inline bool check_files_exact_match(const std::string& fileA, const std::string& fileB, bool ignore_line_endings = false) {
00305         
00306         std::ifstream a_file(fileA.c_str(), std::ios::in | std::ios::binary);
00307         BOOST_REQUIRE(a_file.is_open());
00308 
00309         std::ifstream b_file(fileB.c_str(), std::ios::in | std::ios::binary);
00310         BOOST_REQUIRE(b_file.is_open());
00311 
00312         
00313         static const unsigned int BUF_SIZE = 4096;
00314         char a_buf[BUF_SIZE];
00315         char b_buf[BUF_SIZE];
00316 
00317         if (ignore_line_endings) {
00318             while (a_file.getline(a_buf, BUF_SIZE)) {
00319                 if (! b_file.getline(b_buf, BUF_SIZE))
00320                     return false;
00321                 trim(a_buf);
00322                 trim(b_buf);
00323                 if (strlen(a_buf) != strlen(b_buf))
00324                     return false;
00325                 if (memcmp(a_buf, b_buf, strlen(a_buf)) != 0)
00326                     return false;
00327             }
00328             if (b_file.getline(b_buf, BUF_SIZE))
00329                 return false;
00330         } else {
00331             while (a_file.read(a_buf, BUF_SIZE)) {
00332                 if (! b_file.read(b_buf, BUF_SIZE))
00333                     return false;
00334                 if (memcmp(a_buf, b_buf, BUF_SIZE) != 0)
00335                     return false;
00336             }
00337             if (b_file.read(b_buf, BUF_SIZE))
00338                 return false;
00339         }
00340         if (a_file.gcount() != b_file.gcount())
00341             return false;
00342         if (memcmp(a_buf, b_buf, a_file.gcount()) != 0)
00343             return false;
00344 
00345         a_file.close();
00346         b_file.close();
00347 
00348         
00349         return true;
00350     }
00351 
00352 
00353 }   
00354 }   
00355 
00356 
00357 
00358 
00359 
00360 
00361 
00362 
00363 
00364 
00365 
00366 
00367 
00368 
00369 
00370 
00371 
00372 
00373 
00374 
00375 
00376 
00377 
00378 
00379 
00380 
00381 
00382 
00383 
00384 
00385 
00386 
00387 
00388 
00389 
00390 
00391 
00392 
00393 
00394 
00395 
00396 
00397 
00398 
00399 
00400 
00401 
00402 
00403 
00404 
00405 
00406 
00407 
00408 
00409 
00410 
00411 
00412 
00413 
00414 
00415 
00416 
00417 
00418 
00419 
00420 #define BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE(suite_name, fixture_types) \
00421 BOOST_AUTO_TEST_SUITE(suite_name)                                         \
00422 typedef fixture_types BOOST_AUTO_TEST_CASE_FIXTURE_TYPES;                 \
00423 
00424 
00425 #define BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(test_name)        \
00426 template<typename F>                                            \
00427 struct test_name : public F                                     \
00428 { void test_method(); };                                        \
00429                                                                 \
00430 struct BOOST_AUTO_TC_INVOKER( test_name ) {                     \
00431     template<typename TestType>                                 \
00432     static void run( boost::type<TestType>* = 0 )               \
00433     {                                                           \
00434         test_name<TestType> t;                                  \
00435         t.test_method();                                        \
00436     }                                                           \
00437 };                                                              \
00438                                                                 \
00439 BOOST_AUTO_TU_REGISTRAR( test_name )(                           \
00440     boost::unit_test::ut_detail::template_test_case_gen<        \
00441         BOOST_AUTO_TC_INVOKER( test_name ),                     \
00442         BOOST_AUTO_TEST_CASE_FIXTURE_TYPES >(                   \
00443             BOOST_STRINGIZE( test_name ) ) );                   \
00444                                                                 \
00445 template<typename F>                                            \
00446 void test_name<F>::test_method()                                \
00447 
00448 
00449 
00450 #endif