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