1////////////////////////////////////////////////////////////////////////////// 2// regress.ipp 3// 4// (C) Copyright Eric Niebler 2004. 5// Use, modification and distribution are subject to the 6// Boost Software License, Version 1.0. (See accompanying file 7// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 9/* 10 Revision history: 11 7 March 2004 : Initial version. 12*/ 13 14#include <locale> 15#include <vector> 16#include <string> 17#include <fstream> 18#include <iostream> 19#include <boost/lexical_cast.hpp> 20#include <boost/xpressive/xpressive.hpp> 21#include <boost/test/unit_test.hpp> 22 23#if defined(_MSC_VER) && defined(_DEBUG) 24# define _CRTDBG_MAP_ALLOC 25# include <crtdbg.h> 26#endif 27 28#if defined(BOOST_XPRESSIVE_TEST_WREGEX) && !defined(BOOST_XPRESSIVE_NO_WREGEX) 29namespace std 30{ 31 inline std::ostream &operator <<(std::ostream &sout, std::wstring const &wstr) 32 { 33 for(std::size_t n = 0; n < wstr.size(); ++n) 34 sout.put(BOOST_USE_FACET(std::ctype<wchar_t>, std::locale()).narrow(wstr[n], '?')); 35 return sout; 36 } 37} 38#endif 39 40#define BOOST_XPR_CHECK(pred) \ 41 if(pred) {} else { BOOST_ERROR(case_ << #pred); } 42 43using namespace boost::unit_test; 44using namespace boost::xpressive; 45 46////////////////////////////////////////////////////////////////////////////// 47// xpr_test_case 48template<typename Char> 49struct xpr_test_case 50{ 51 typedef std::basic_string<Char> string_type; 52 std::string section; 53 string_type str; 54 string_type pat; 55 string_type sub; 56 string_type res; 57 regex_constants::syntax_option_type syntax_flags; 58 regex_constants::match_flag_type match_flags; 59 std::vector<string_type> br; 60 61 xpr_test_case() 62 { 63 this->reset(); 64 } 65 66 void reset() 67 { 68 this->section.clear(); 69 this->str.clear(); 70 this->pat.clear(); 71 this->sub.clear(); 72 this->res.clear(); 73 this->br.clear(); 74 this->syntax_flags = regex_constants::ECMAScript; 75 this->match_flags = regex_constants::match_default | regex_constants::format_first_only; 76 } 77}; 78 79////////////////////////////////////////////////////////////////////////////// 80// globals 81std::ifstream in; 82unsigned int test_count = 0; 83 84// The global object that contains the current test case 85xpr_test_case<char> test; 86 87struct test_case_formatter 88{ 89 friend std::ostream &operator <<(std::ostream &sout, test_case_formatter) 90 { 91 sout << test.section << " /" << test.pat << "/ : "; 92 return sout; 93 } 94}; 95 96test_case_formatter const case_ = {}; 97 98#if defined(BOOST_XPRESSIVE_TEST_WREGEX) && !defined(BOOST_XPRESSIVE_NO_WREGEX) 99/////////////////////////////////////////////////////////////////////////////// 100// widen 101// make a std::wstring from a std::string by widening according to the 102// current ctype<char> facet 103inline std::wstring widen(std::string const &str) 104{ 105 std::ctype<char> const &ct = BOOST_USE_FACET(std::ctype<char>, std::locale()); 106 std::wstring res; 107 for(size_t i=0; i<str.size(); ++i) 108 { 109 res += ct.widen(str[i]); 110 } 111 return res; 112} 113 114/////////////////////////////////////////////////////////////////////////////// 115// widen 116// widens an entire test case 117xpr_test_case<wchar_t> widen(xpr_test_case<char> const &test) 118{ 119 xpr_test_case<wchar_t> wtest; 120 wtest.section = test.section; 121 wtest.str = ::widen(test.str); 122 wtest.pat = ::widen(test.pat); 123 wtest.sub = ::widen(test.sub); 124 wtest.res = ::widen(test.res); 125 wtest.syntax_flags = test.syntax_flags; 126 wtest.match_flags = test.match_flags; 127 wtest.br.reserve(test.br.size()); 128 for(std::size_t i = 0; i < test.br.size(); ++i) 129 { 130 wtest.br.push_back(::widen(test.br[i])); 131 } 132 return wtest; 133} 134#endif // BOOST_XPRESSIVE_NO_WREGEX 135 136std::string escape(std::string str) 137{ 138 for(std::string::size_type pos = 0; std::string::npos != (pos = str.find('\\', pos)); ++pos) 139 { 140 if(pos + 1 == str.size()) 141 break; 142 143 switch(str[pos + 1]) 144 { 145 case '\\': str.replace(pos, 2, "\\"); break; 146 case 'n': str.replace(pos, 2, "\n"); break; 147 case 'r': str.replace(pos, 2, "\r"); break; 148 } 149 } 150 return str; 151} 152 153/////////////////////////////////////////////////////////////////////////////// 154// get_test 155// read the next section out of the input file, and fill out 156// the global variables 157bool get_test() 158{ 159 test.reset(); 160 bool first = true; 161 std::string line; 162 smatch what; 163 164 sregex const rx_sec = '[' >> (s1= +_) >> ']'; 165 sregex const rx_str = "str=" >> (s1= *_); 166 sregex const rx_pat = "pat=" >> (s1= *_); 167 sregex const rx_flg = "flg=" >> (s1= *_); 168 sregex const rx_sub = "sub=" >> (s1= *_); 169 sregex const rx_res = "res=" >> (s1= *_); 170 sregex const rx_br = "br" >> (s1= +digit) >> '=' >> (s2= *_); 171 172 while(in.good()) 173 { 174 std::getline(in, line); 175 176 if(!line.empty() && '\r' == line[line.size()-1]) 177 { 178 line.erase(line.size()-1); 179 } 180 181 if(regex_match(line, what, rx_sec)) 182 { 183 if(!first) 184 { 185 if(what[1] != "end") 186 { 187 BOOST_FAIL(("invalid input : " + line).c_str()); 188 } 189 break; 190 } 191 192 first = false; 193 test.section = what[1].str(); 194 } 195 else if(regex_match(line, what, rx_str)) 196 { 197 test.str = escape(what[1].str()); 198 } 199 else if(regex_match(line, what, rx_pat)) 200 { 201 test.pat = what[1].str(); 202 } 203 else if(regex_match(line, what, rx_sub)) 204 { 205 test.sub = what[1].str(); 206 } 207 else if(regex_match(line, what, rx_res)) 208 { 209 test.res = escape(what[1].str()); 210 } 211 else if(regex_match(line, what, rx_flg)) 212 { 213 std::string flg = what[1].str(); 214 215 if(std::string::npos != flg.find('i')) 216 { 217 test.syntax_flags = test.syntax_flags | regex_constants::icase; 218 } 219 if(std::string::npos == flg.find('m')) 220 { 221 test.syntax_flags = test.syntax_flags | regex_constants::single_line; 222 } 223 if(std::string::npos == flg.find('s')) 224 { 225 test.syntax_flags = test.syntax_flags | regex_constants::not_dot_newline; 226 } 227 if(std::string::npos != flg.find('x')) 228 { 229 test.syntax_flags = test.syntax_flags | regex_constants::ignore_white_space; 230 } 231 if(std::string::npos != flg.find('g')) 232 { 233 test.match_flags = test.match_flags & ~regex_constants::format_first_only; 234 } 235 if(std::string::npos != flg.find('a')) 236 { 237 test.match_flags = test.match_flags | regex_constants::format_all; 238 } 239 if(std::string::npos != flg.find('p')) 240 { 241 test.match_flags = test.match_flags | regex_constants::format_perl; 242 } 243 if(std::string::npos != flg.find('d')) 244 { 245 test.match_flags = test.match_flags | regex_constants::format_sed; 246 } 247 } 248 else if(regex_match(line, what, rx_br)) 249 { 250 std::size_t nbr = boost::lexical_cast<std::size_t>(what[1].str()); 251 252 if(nbr >= test.br.size()) 253 { 254 test.br.resize(nbr + 1); 255 } 256 257 test.br[nbr] = escape(what[2].str()); 258 } 259 else if(!line.empty() && ';' != line[0]) 260 { 261 BOOST_FAIL((std::string("invalid input : ") + line).c_str()); 262 } 263 } 264 265 return !first; 266} 267 268/////////////////////////////////////////////////////////////////////////////// 269// run_test_impl 270// run the test 271template<typename Char> 272void run_test_impl(xpr_test_case<Char> const &test) 273{ 274 try 275 { 276 Char const empty[] = {0}; 277 typedef typename std::basic_string<Char>::const_iterator iterator; 278 basic_regex<iterator> rx = basic_regex<iterator>::compile(test.pat, test.syntax_flags); 279 280 // Build the same regex for use with C strings 281 basic_regex<Char const *> c_rx = basic_regex<Char const *>::compile(test.pat, test.syntax_flags); 282 283 if(!test.res.empty()) 284 { 285 // test regex_replace 286 std::basic_string<Char> res = regex_replace(test.str, rx, test.sub, test.match_flags); 287 BOOST_CHECK_MESSAGE(res == test.res, case_ << res << " != " << test.res ); 288 289 // test regex_replace with NTBS format string 290 std::basic_string<Char> res2 = regex_replace(test.str, rx, test.sub.c_str(), test.match_flags); 291 BOOST_CHECK_MESSAGE(res2 == test.res, case_ << res2 << " != " << test.res ); 292 293 // test regex_replace with NTBS input string 294 std::basic_string<Char> res3 = regex_replace(test.str.c_str(), c_rx, test.sub, test.match_flags); 295 BOOST_CHECK_MESSAGE(res3 == test.res, case_ << res3 << " != " << test.res ); 296 297 // test regex_replace with NTBS input string and NTBS format string 298 std::basic_string<Char> res4 = regex_replace(test.str.c_str(), c_rx, test.sub.c_str(), test.match_flags); 299 BOOST_CHECK_MESSAGE(res4 == test.res, case_ << res4 << " != " << test.res ); 300 } 301 302 if(0 == (test.match_flags & regex_constants::format_first_only)) 303 { 304 { 305 // global search, use regex_iterator 306 std::vector<sub_match<iterator> > br; 307 regex_iterator<iterator> begin(test.str.begin(), test.str.end(), rx, test.match_flags), end; 308 for(; begin != end; ++begin) 309 { 310 match_results<iterator> const &what = *begin; 311 br.insert(br.end(), what.begin(), what.end()); 312 } 313 314 // match succeeded: was it expected to succeed? 315 BOOST_XPR_CHECK(br.size() == test.br.size()); 316 317 for(std::size_t i = 0; i < br.size() && i < test.br.size(); ++i) 318 { 319 BOOST_XPR_CHECK((!br[i].matched && test.br[i] == empty) || test.br[i] == br[i].str()); 320 } 321 } 322 323 { 324 // global search, use regex_token_iterator 325 std::vector<typename sub_match<iterator>::string_type> br2; 326 std::vector<int> subs(rx.mark_count() + 1, 0); 327 // regex_token_iterator will extract all sub_matches, in order: 328 for(std::size_t i = 0; i < subs.size(); ++i) 329 { 330 subs[i] = static_cast<int>(i); 331 } 332 regex_token_iterator<iterator> begin2(test.str.begin(), test.str.end(), rx, subs, test.match_flags), end2; 333 for(; begin2 != end2; ++begin2) 334 { 335 br2.push_back(*begin2); 336 } 337 338 // match succeeded: was it expected to succeed? 339 BOOST_XPR_CHECK(br2.size() == test.br.size()); 340 341 for(std::size_t i = 0; i < br2.size() && i < test.br.size(); ++i) 342 { 343 BOOST_XPR_CHECK(test.br[i] == br2[i]); 344 } 345 } 346 } 347 else 348 { 349 // test regex_search 350 match_results<iterator> what; 351 if(regex_search(test.str, what, rx, test.match_flags)) 352 { 353 // match succeeded: was it expected to succeed? 354 BOOST_XPR_CHECK(what.size() == test.br.size()); 355 356 for(std::size_t i = 0; i < what.size() && i < test.br.size(); ++i) 357 { 358 BOOST_XPR_CHECK((!what[i].matched && test.br[i] == empty) || test.br[i] == what[i].str()); 359 } 360 } 361 else 362 { 363 // match failed: was it expected to fail? 364 BOOST_XPR_CHECK(0 == test.br.size()); 365 } 366 } 367 } 368 catch(regex_error const &e) 369 { 370 BOOST_ERROR(case_ << e.what()); 371 } 372} 373 374/////////////////////////////////////////////////////////////////////////////// 375// run_test_impl 376// run the current test 377void run_test() 378{ 379 #if defined(BOOST_XPRESSIVE_TEST_WREGEX) && !defined(BOOST_XPRESSIVE_NO_WREGEX) 380 xpr_test_case<wchar_t> wtest = ::widen(test); 381 run_test_impl(wtest); 382 #else 383 run_test_impl(test); 384 #endif 385} 386 387static char const * s_argv1; 388 389/////////////////////////////////////////////////////////////////////////////// 390// open_test 391bool open_test() 392{ 393 in.open( s_argv1? s_argv1: "regress.txt" ); 394 return in.good(); 395} 396 397/////////////////////////////////////////////////////////////////////////////// 398// test_main 399// read the tests from the input file and execute them 400void test_main() 401{ 402 #if !defined(BOOST_XPRESSIVE_TEST_WREGEX) || !defined(BOOST_XPRESSIVE_NO_WREGEX) 403 if(!open_test()) 404 { 405 BOOST_ERROR("Error: unable to open input file."); 406 } 407 408 while(get_test()) 409 { 410 run_test(); 411 ++test_count; 412 } 413 #endif 414 415 std::cout << test_count << " tests completed." << std::endl; 416} 417 418/////////////////////////////////////////////////////////////////////////////// 419// init_unit_test_suite 420// 421test_suite* init_unit_test_suite( int argc, char* argv[] ) 422{ 423 s_argv1 = argv[1]; 424 425 test_suite *test = BOOST_TEST_SUITE("basic regression test"); 426 test->add(BOOST_TEST_CASE(&test_main)); 427 return test; 428} 429 430/////////////////////////////////////////////////////////////////////////////// 431// debug_init 432static const struct debug_init 433{ 434 debug_init() 435 { 436 #if defined(_MSC_VER) && defined(_DEBUG) 437 // Send warnings, errors and asserts to STDERR 438 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 439 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 440 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 441 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 442 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 443 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); 444 445 // Check for leaks at program termination 446 _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)); 447 448 //_CrtSetBreakAlloc(221); 449 #endif 450 } 451} g_debug_init; 452