/* * * Copyright (c) 1998-2002 * Dr John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * * FILE tests.cpp * VERSION see * * the actual tests conducted by regress. * */ #include #include #include "regress.h" #ifndef BOOST_REGEX_NO_TEST # ifdef BOOST_MSVC # pragma warning(disable: 4244 4267) #endif using namespace boost; template bool compare_result(const M1& sm, const M2& m) { if(sm.size() != m.size()) return false; #ifdef BOOST_REGEX_V3 if(sm.line() != m.line()) return false; #endif for(unsigned int i = 0; i < sm.size(); ++i) { if(sm.position(i) != m.position(i)) return false; if(sm.length(i) != m.length(i)) return false; } return true; } template bool compare_result(const M1& sm, const M1& m) { return sm == m; } template void cpp_eh_tests(const basic_regex& ) { #ifndef __GNUC__ bool thrown = false; // try set_expression form first: #ifndef BOOST_NO_EXCEPTIONS try { #endif A a; basic_regex e(a); e.set_expression(expression.c_str(), flags[2] | regex::use_except); #ifndef BOOST_NO_EXCEPTIONS } catch(const boost::bad_expression&) { thrown = true; } catch(...){} if(!thrown) { begin_error(); cout << "Error: expected exception not thrown" << endl; } #endif // now try constructor form: thrown = false; #ifndef BOOST_NO_EXCEPTIONS try #endif { A a; basic_regex e(expression.c_str(), flags[2] | regex::use_except, a); } #ifndef BOOST_NO_EXCEPTIONS catch(const boost::bad_expression&) { thrown = true; } catch(...){} if(!thrown) { begin_error(); cout << "Error: expected exception not thrown" << endl; } #endif #endif } template iterator find_last_line(iterator start, iterator end) { iterator result = start; while(start != end) { if(*start == '\n') { ++start; result = start; } else ++start; } return result; } template unsigned int count_lines(iterator start, iterator end) { unsigned int result = 0; while(start != end) { if(*start == '\n') ++result; ++start; } return result; } template class grep_test_predicate { int match_id; iterator base, term; // rebind allocator to correct type: #ifdef BOOST_REGEX_V3 typedef Alloc alloc_type; #else typedef typename detail::rebind_allocator, Alloc>::type alloc_type; #endif public: grep_test_predicate(iterator i, iterator j) : base(i), term(j) { match_id = 0; } ~grep_test_predicate(){} bool operator()(const boost::match_results< iterator, alloc_type >& m); }; template bool grep_test_predicate::operator()(const boost::match_results< iterator, alloc_type >& m) { std::ptrdiff_t start, end; start = m[0].first - base; end = m[0].second - base; if((matches[match_id] != start) || (matches[match_id + 1] != end)) { begin_error(); cout << "regex++ grep match error: found [" << start << "," << end << "] expected [" << matches[match_id] << "," << matches[match_id+1] << "]" << endl; } // // check $`: start = m[-1].first - base; end = m[-1].second - base; if(match_id && ( (end != matches[match_id]) || (start != matches[match_id - 1]) ) ) { begin_error(); cout << "regex++ grep error in $`: found [" << start << "," << end << "] expected [" << matches[match_id-1] << "," << matches[match_id] << "]" << endl; } else if((!match_id) && ((start != 0) || (end != matches[0]))) { begin_error(); cout << "regex++ grep error in $`: found [" << start << "," << end << "] expected [" << 0 << "," << matches[0] << "]" << endl; } // // check $': start = m[-2].first - base; end = m[-2].second - base; if((start != matches[match_id + 1]) || (end != (term-base))) { begin_error(); cout << "regex++ grep error in $': found [" << start << "," << end << "] expected [" << matches[match_id + 1] << "," << (term-base) << "]" << endl; } // // now check line() /* don't check this, it's not supported in the new algorithm.... start = m.line(); end = count_lines(base, iterator(m[0].first)) + 1; if(start != end) { begin_error(); cout << "regex++ grep error in line(): found " << start << " expected " << end << endl; } */ // // now check line_start() /* don't check this, it's not supported in the new algorithm.... start = m.line_start() - base; end = find_last_line(base, iterator(m[0].first)) - base; if(start != end) { begin_error(); cout << "regex++ grep error in line_start(): found " << start << " expected " << end << endl; } */ match_id += 2; return true; } template void cpp_tests(const basic_regex& e, bool recurse = true) { typedef typename basic_regex::allocator_type base_allocator_type; #ifdef BOOST_REGEX_V3 typedef base_allocator_type allocator_type; #else typedef typename detail::rebind_allocator >, base_allocator_type>::type allocator_type; #endif if(flags[4] & REG_MERGE) { // // test merge code: // string_type s; s = regex_merge(search_text, e, format_string.c_str(), static_cast(flags[3])); if(s != merge_string) { begin_error(); cout << "merge result mismatch: found \"" << make_narrow(s).c_str() << "\" expected \"" << make_narrow(merge_string.c_str()) << "\"" << endl; } return; } if(recurse) { // copy and assign test: basic_regex e2(e); cpp_tests(e2, false); e2 = e; cpp_tests(e2, false); basic_regex e3; #ifndef BOOST_REGEX_V3 e3.swap(e2); cpp_tests(e3, false); #endif } if(e.error_code()) { // verify that we don't expect expression to compile if(search_text != BOOST_RE_STR("!")) { begin_error(); cout << "Expression did not compile using regex++ API" << endl; } else if((recurse) && ((flags[3] & match_partial) == 0)) cpp_eh_tests(e); } else if(flags[4] & REG_GREP) { // try to do grep: debug_iterator x(search_text.begin(), search_text.begin(), search_text.end()); debug_iterator y(search_text.end(), search_text.begin(), search_text.end()); grep_test_predicate, allocator_type> oi(x, y); regex_grep(oi, x, y, e, static_cast(flags[3])); #if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING) if(!recurse) { unsigned len = search_text.size(); const std::basic_string& s = search_text; grep_test_predicate::const_iterator, base_allocator_type> oi2(s.begin(), s.end()); regex_grep(oi2, s, e, static_cast(flags[3])); grep_test_predicate oi3(s.c_str(), s.c_str()+s.size()); regex_grep(oi3, s.c_str(), e, static_cast(flags[3])); assert(s.size() == len); assert(s.end() - s.begin() == (std::basic_string::difference_type)len); } #endif } else { // try to find match match_results< debug_iterator, allocator_type> m; debug_iterator x(search_text.begin(), search_text.begin(), search_text.end()); debug_iterator y(search_text.end(), search_text.begin(), search_text.end()); if(regex_search(x, y, m, e, static_cast(flags[3]))) { // special case for partial matches: if(flags[4] & REG_PARTIAL_MATCH) { if(m[0].matched) { begin_error(); cout << "regex++ API result mismatch, found full match when partial match was expected: found (" << (m[0].first - x) << "," << (m[0].second - x) << ")" << endl; } else if(((m[0].first - x) != matches[0]) || ((m[0].second - x) != matches[1])) { begin_error(); cout << "regex++ API result mismatch in sub-expression " << 0 << ", found (" << (m[0].first - x) << "," << (m[0].second - x) << ") expected (" << matches[0] << "," << matches[1] << ")" << endl; } return; // don't bother testing anything else for partial matches } // match found compare what matched with what we expect: int j = 0; for(unsigned int i = 0; i < m.size(); ++i, j += 2) { if(m[i].matched == false) { if(matches[j] != -1) { begin_error(); cout << "regex++ API result mismatch in sub-expression " << i << ", found (" << (m[i].first - x) << "," << (m[i].second - x) << ") expected (" << matches[j] << "," << matches[j+1] << ")" << endl; } } else if(((m[i].first - x) != matches[j]) || ((m[i].second - x) != matches[j+1])) { begin_error(); cout << "regex++ API result mismatch in sub-expression " << i << ", found (" << (m[i].first - x) << "," << (m[i].second - x) << ") expected (" << matches[j] << "," << matches[j+1] << ")" << endl; } } // // now check $` and $': // if((m[-1].first != x) || (m[-1].second != m[0].first)) { begin_error(); cout << "regex++ API result mismatch in $` (match -1), found (" << (m[-1].first - x) << "," << (m[-1].second - x) << ") expected (0" << "," << matches[0] << ")" << endl; } if(((flags[3] & match_partial) == 0) && ((m[-2].first != m[0].second) || (m[-2].second != y))) { begin_error(); cout << "regex++ API result mismatch in $' (match -2), found (" << (m[-2].first - x) << "," << (m[-2].second - x) << ") expected (" << matches[1] << "," << (y-x) << ")" << endl; } #if !BOOST_WORKAROUND(BOOST_MSVC, <= 1300) && !defined(BOOST_REGEX_V3) && !BOOST_WORKAROUND(__HP_aCC, BOOST_TESTED_AT(55500)) // // now try comparison operators: string_type s(m[0]); if((s != m[0]) || (m[0] != s) || !(s == m[0]) || !(m[0] == s) || (s < m[0]) || (m[0] < s) || (s > m[0]) || (m[0] > s) || !(s <= m[0]) || !(m[0] <= s) || !(s >= m[0]) || !(m[0] >= s)) { begin_error(); cout << "string comparison failed for result" << std::endl; } if(s.find_first_of((string_type::value_type)0) == string_type::npos) { if((m[0] != s.c_str()) || (s.c_str() != m[0]) || !(m[0] == s.c_str()) || !(s.c_str() == m[0]) || (m[0] > s.c_str()) || (s.c_str() > m[0]) || (m[0] < s.c_str()) || (s.c_str() < m[0]) || !(m[0] >= s.c_str()) || !(s.c_str() >= m[0]) || !(m[0] <= s.c_str()) || !(s.c_str() <= m[0])) { begin_error(); cout << "string comparison failed for result" << std::endl; } } // // now try addition operators: string_type sa(1, (string_type::value_type)'a'); if( ((m[0] + sa) != (s + sa)) || (sa + m[0]) != (sa + s) || ((m[0] + m[0]) != (s + s)) || ((m[0] + m[0]) != (s + s)) || ((m[0] + sa[0]) != (s + sa[0])) || ((sa[0] + m[0]) != (sa[0] + s)) ) { begin_error(); cout << "string addition failed for result" << std::endl; } if(s.find_first_of((string_type::value_type)0) == string_type::npos) { if( ((m[0] + sa.c_str()) != (s + sa)) || ((sa.c_str() + m[0]) != (sa + s)) ) { begin_error(); cout << "string addition failed for result" << std::endl; } } #endif // // now try alternative forms of regex_search if available: #if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING) if(!recurse) { std::basic_string s2(search_text.begin(), search_text.end()); match_results::const_iterator> sm; if(regex_search(s2, sm, e, static_cast(flags[3]))) { if(compare_result(sm, m) == false) { begin_error(); cout << "regex++ API result mismatch in regex_search(const std::string&, match_results&, const basic_regex&, int)" << endl; } } else { begin_error(); cout << "regex++ API result mismatch in regex_search(const std::string&, match_results&, const basic_regex&, int)" << endl; } #ifndef BOOST_REGEX_V3 if(!regex_search(s2, e, static_cast(flags[3])) || !regex_search(s2.begin(), s2.end(), e, static_cast(flags[3]))) { begin_error(); cout << "regex++ API result mismatch in regex_search(const std::string&, const basic_regex&, int)" << endl; } #endif // // partial match should give same result as full match // provided a full match is expected: // if(matches[0] > 0) { if(regex_search(x, y, m, e, static_cast(flags[3]) | boost::match_partial)) { if(compare_result(sm, m) == false) { begin_error(); cout << "regex++ API result mismatch in regex_search when enabling match_partial" << endl; } } else { begin_error(); cout << "regex++ API result: match not found when match_partial specified" << endl; } } if(s2.find(char_t(0)) == std::basic_string::npos) { match_results ssm; if(regex_search(search_text.c_str(), ssm, e, static_cast(flags[3]))) { if(compare_result(ssm, m) == false) { begin_error(); cout << "regex++ API result mismatch in regex_search(const char_t*, match_results&, const basic_regex&, int)" << endl; } } else { begin_error(); cout << "regex++ API result mismatch in regex_search(const char_t*, match_results&, const basic_regex&, int)" << endl; } #ifndef BOOST_REGEX_V3 if(!regex_search(search_text.c_str(), e, static_cast(flags[3]))) { begin_error(); cout << "regex++ API result mismatch in regex_search(const char_t*, const basic_regex&, int)" << endl; } #endif } } if((false == recurse) && (matches[0] == 0) && (matches[1] == static_cast(search_text.size()))) { // // match expected on whole string, so all versions // of regex_match should also succeed: // match_results< debug_iterator, allocator_type> m1; debug_iterator x1(search_text.begin(), search_text.begin(), search_text.end()); debug_iterator y1(search_text.end(), search_text.begin(), search_text.end()); if(regex_match(x1, y1, m1, e, static_cast(flags[3]))) { if(compare_result(m1, m) == false) { begin_error(); cout << "regex++ API result mismatch in regex_match(iterator, iterator, match_results&, const basic_regex&, int)" << endl; } } else { begin_error(); cout << "regex++ API result mismatch in regex_match(iterator, iterator, match_results&, const basic_regex&, int)" << endl; } std::basic_string s3(search_text.begin(), search_text.end()); match_results::const_iterator> sm; if(regex_match(s3, sm, e, static_cast(flags[3]))) { if(compare_result(sm, m) == false) { begin_error(); cout << "regex++ API result mismatch in regex_match(const std::string&, match_results&, const basic_regex&, int)" << endl; } } else { begin_error(); cout << "regex++ API result mismatch in regex_match(const std::string&, match_results&, const basic_regex&, int)" << endl; } if(s3.find(char_t(0)) == std::basic_string::npos) { match_results ssm; if(regex_match(search_text.c_str(), ssm, e, static_cast(flags[3]))) { if(compare_result(ssm, m) == false) { begin_error(); cout << "regex++ API result mismatch in regex_match(const char_t*, match_results&, const basic_regex&, int)" << endl; } } else { begin_error(); cout << "regex++ API result mismatch in regex_match(const char_t*, match_results&, const basic_regex&, int)" << endl; } } } #endif } else { // match not found if(matches[0] != -1) { begin_error(); cout << "Match expected but not found using regex++ API" << endl; } } } } #if !defined(TEST_UNICODE) unsigned int hl_match_id; bool #if (defined(__BORLANDC__) || defined(BOOST_MSVC)) && !defined(BOOST_DISABLE_WIN32) __cdecl #endif hl_grep_test_proc(const RegEx& e) { std::ptrdiff_t start, end; start = e.Position(0); end = start + e.Length(); if((matches[hl_match_id] != start) || (matches[hl_match_id + 1] != end)) { begin_error(); cout << "class RegEx grep match error: found [" << start << "," << end << "] expected [" << matches[hl_match_id] << "," << matches[hl_match_id+1] << "]" << endl; } if(0 == (flags[4] & REG_GREP)) { for(unsigned int sub = 1; sub < e.Marks(); ++sub) { start = e.Position(sub); end = start + e.Length(sub); if((matches[2*sub] != start) || (matches[2*sub + 1] != end)) { begin_error(); cout << "class RegEx grep match error: found in sub " << sub << " [" << start << "," << end << "] expected [" << matches[2*sub] << "," << matches[2*sub+1] << "]" << endl; } } } // // check $`: start = e.Position(-1); end = start + e.Length(-1); if(start == boost::RegEx::npos) { if(hl_match_id && ( matches[hl_match_id] != matches[hl_match_id - 1] ) ) { begin_error(); cout << "class RegEx grep error in $`: found [" << start << "," << end << "] expected [" << matches[hl_match_id-1] << "," << matches[hl_match_id] << "]" << endl; } else if((!hl_match_id) && (0 != matches[0])) { begin_error(); cout << "class RegEx grep error in $`: found [" << start << "," << end << "] expected [" << 0 << "," << matches[0] << "]" << endl; } } else { if(hl_match_id && ( (end != matches[hl_match_id]) || (start != matches[hl_match_id - 1]) ) ) { begin_error(); cout << "class RegEx grep error in $`: found [" << start << "," << end << "] expected [" << matches[hl_match_id-1] << "," << matches[hl_match_id] << "]" << endl; } else if((!hl_match_id) && ((start != 0) || (end != matches[0]))) { begin_error(); cout << "class RegEx grep error in $`: found [" << start << "," << end << "] expected [" << 0 << "," << matches[0] << "]" << endl; } } // // check $': start = e.Position(-2); end = start + e.Length(-2); if(start == boost::RegEx::npos) { if(matches[hl_match_id + 1] != (int)search_text.size()) { begin_error(); cout << "class RegEx grep error in $': found [" << start << "," << end << "] expected [" << matches[hl_match_id + 1] << "," << (search_text.size()) << "]" << endl; } } else if((start != matches[hl_match_id + 1]) || (end != (int)search_text.size())) { begin_error(); cout << "class RegEx grep error in $': found [" << start << "," << end << "] expected [" << matches[hl_match_id + 1] << "," << (search_text.size()) << "]" << endl; } hl_match_id += 2; return true; } void cpp_hl_tests(RegEx& e, bool recurse = true) { if(flags[4] & REG_MERGE) return; if(e.error_code()) { if(search_text != BOOST_RE_STR("!")) { begin_error(); cout << "Expression did not compile with class RegEx" << endl; } return; } if(recurse) { // copy and assign test: RegEx e2(e); cpp_hl_tests(e2, false); e2 = e; cpp_hl_tests(e2, false); } if(flags[4] & REG_GREP) { // try to do grep: hl_match_id = 0; GrepCallback cb = hl_grep_test_proc; e.Grep(cb, search_text.c_str(), static_cast(flags[3])); } else { if(e.Search(search_text.c_str(), static_cast(flags[3]))) { unsigned int i = 0; unsigned int j = 0; while(matches[j] != -2) { if( (matches[j] != (int)e.Position(i)) || ((matches[j] != -1) && ((matches[j+1] - matches[j] != (int)e.Length(i)))) ) { begin_error(); cout << "RegEx::Search error in subexpression " << i << ": found [" << e.Position(i) << "," << (e.Position(i) + e.Length(i)) << "] expected [" << matches[j] << "," << matches[j+1] << "]" << endl; } ++i; j += 2; } } else { if(matches[0] != -1) { begin_error(); cout << "match expected but not found with RexEx::Search" << endl; } } // // test RegEx::Match only if we expect to match all of the input: // if((matches[0] == 0) && (matches[1] == (int)search_text.size())) { if(e.Match(search_text.c_str(), static_cast(flags[3]))) { unsigned int i = 0; unsigned int j = 0; while(matches[j] != -2) { if( (matches[j] != (int)e.Position(i)) || ((matches[j] != -1) && ((matches[j+1] - matches[j] != (int)e.Length(i)))) ) { begin_error(); cout << "RegEx::Match error in subexpression " << i << ": found [" << e.Position(i) << "," << (e.Position(i) + e.Length(i)) << "] expected [" << matches[j] << "," << matches[j+1] << "]" << endl; } ++i; j += 2; } } else { begin_error(); cout << "Match expected but not found with RegEx::Match" << endl; } } } } #endif template bool has_nulls(iterator i, iterator j) { while(i != j) { if(*i == 0) return true; ++i; } return false; } #ifdef TEST_UNICODE #undef regcomp #undef regerror #undef regexec #undef regfree #undef regex_t #define regcomp regcompW #define regerror regerrorW #define regexec regexecW #define regfree regfreeW #define regex_t regex_tW #endif void run_tests() { #ifndef TEST_UNICODE if(flags[4] & REG_UNICODE_ONLY) return; #endif #ifndef NO_CPP_TEST #ifndef BOOST_NO_EXCEPTIONS try { #endif unsigned int f = flags[2] & ~regex::use_except; if(flags[0] & REG_ICASE) f |= regex::icase; re_type e(expression.c_str(), f); cpp_tests(e, true); #ifndef BOOST_NO_EXCEPTIONS } catch(const std::exception& e) { // // this is only an error if the expression is expected to be valid: if(search_text != BOOST_RE_STR("!")) { begin_error(); cout << "Unexpected exception thrown from C++ library: " << e.what() << endl; } } catch(...) { begin_error(); cout << "Unexpected exception thrown from C++ library" << endl; } #endif // BOOST_NO_EXCEPTIONS #endif #if !defined(TEST_UNICODE) #ifndef BOOST_NO_EXCEPTIONS try { #endif if(((flags[3] & match_partial) == 0) && (flags[2] == regex::normal) && (has_nulls(search_text.begin(), search_text.end()) == false)) { RegEx e; e.SetExpression(expression.c_str(), flags[0] & REG_ICASE); cpp_hl_tests(e, true); } #ifndef BOOST_NO_EXCEPTIONS } catch(const std::exception& ) { if(search_text != BOOST_RE_STR("!")) { begin_error(); cout << "Expression did not compile with class RegEx" << endl; } } catch(...) { begin_error(); cout << "Unexpected exception thrown from RegEx::SetExpression" << endl; } #endif // BOOST_NO_EXCEPTIONS #endif if(flags[4] & (REG_NO_POSIX_TEST | REG_GREP | REG_MERGE | REG_MERGE_COPY)) return; regex_t posix_expression; regmatch_t m[MAX_MATCHES]; if(regcomp(&posix_expression, expression.c_str(), flags[0]) == 0) { if(flags[1] & REG_STARTEND) { m[0].rm_so = 0; m[0].rm_eo = search_text.size(); } if(regexec(&posix_expression, search_text.c_str(), MAX_MATCHES, m, flags[1])) { // match not found if(matches[0] != -1) { begin_error(); cout << "Match expected but not found using POSIX API" << endl; } } else { // match found compare what matched with what we expect: int j = 0; for(unsigned int i = 0; i <= posix_expression.re_nsub; ++i, j += 2) { if((m[i].rm_so != matches[j]) || (m[i].rm_eo != matches[j+1])) { begin_error(); cout << "POSIX API result mismatch in sub-expression " << i << ", found (" << m[i].rm_so << "," << m[i].rm_eo << ") expected (" << matches[j] << "," << matches[j+1] << ")" << endl; } } } regfree(&posix_expression); } else { // expression did not compile if(search_text != BOOST_RE_STR("!")) { begin_error(); cout << "Expression did not compile using POSIX API" << endl; } } } // // error reporting: int last_line = 0; void begin_error() { if(line != last_line) { cout << "Error in line " << line << " of file " << file << endl; cout << "Expression: " << make_narrow(expression).c_str() << endl; cout << "Search text: " << make_narrow(search_text).c_str() << endl; cout << "Flags: "; bool started = false; unsigned int id = 0; while(flag_data[id].len != 0) { if(flags[flag_data[id].id] & flag_data[id].value) { if(started) cout << " | "; cout << make_narrow(flag_data[id].name).c_str(); started = true; } ++id; } cout << endl; last_line = line; ++error_count; } } void reset_error() { last_line = 0; } #if defined(__BORLANDC__) && (__BORLANDC__ >= 0x550) && (__BORLANDC__ <= 0x551) && !defined(_RWSTD_COMPILE_INSTANTIATE) // // this is an ugly hack to work around an ugly problem: // by default this file will produce unresolved externals during // linking unless _RWSTD_COMPILE_INSTANTIATE is defined (Borland bug). // However if _RWSTD_COMPILE_INSTANTIATE is defined then we get separate // copies of basic_string's static data in the RTL and this DLL, this messes // with basic_string's memory management and results in run-time crashes, // Oh sweet joy of Catch 22.... // namespace std{ template<> template<> basic_string& basic_string ::replace >( string_type::iterator f1, string_type::iterator f2, debug_iterator i1, debug_iterator i2) { unsigned insert_pos = f1 - begin(); unsigned remove_len = f2 - f1; unsigned insert_len = i2 - i1; unsigned org_size = size(); if(insert_len > remove_len) { append(insert_len-remove_len, ' '); std::copy_backward(begin() + insert_pos + remove_len, begin() + org_size, end()); std::copy(i1, i2, begin() + insert_pos); } else { std::copy(begin() + insert_pos + remove_len, begin() + org_size, begin() + insert_pos + insert_len); std::copy(i1, i2, begin() + insert_pos); erase(size() + insert_len - remove_len); } return *this; } } #endif #endif