]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * | |
3 | * Copyright (c) 2004 | |
4 | * John Maddock | |
5 | * | |
6 | * Use, modification and distribution are subject to the | |
7 | * Boost Software License, Version 1.0. (See accompanying file | |
8 | * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | * | |
10 | */ | |
11 | ||
12 | /* | |
13 | * LOCATION: see http://www.boost.org for most recent version. | |
14 | * FILE test_regex_search.hpp | |
15 | * VERSION see <boost/version.hpp> | |
16 | * DESCRIPTION: Declares tests for regex search and iteration. | |
17 | */ | |
18 | ||
19 | #ifndef BOOST_REGEX_REGRESS_REGEX_SEARCH_HPP | |
20 | #define BOOST_REGEX_REGRESS_REGEX_SEARCH_HPP | |
21 | #include "info.hpp" | |
22 | #ifdef TEST_ROPE | |
23 | #include <rope> | |
24 | #endif | |
25 | // | |
26 | // this file implements a test for a regular expression that should compile, | |
27 | // followed by a search for that expression: | |
28 | // | |
29 | struct test_regex_search_tag{}; | |
30 | ||
31 | template <class BidirectionalIterator> | |
32 | void test_sub_match(const boost::sub_match<BidirectionalIterator>& sub, BidirectionalIterator base, const int* answer_table, int i, bool recurse = true) | |
33 | { | |
34 | #ifdef BOOST_MSVC | |
35 | #pragma warning(push) | |
36 | #pragma warning(disable:4244) | |
37 | #endif | |
38 | if(recurse) | |
39 | { | |
40 | boost::sub_match<BidirectionalIterator> copy(sub); | |
41 | test_sub_match(copy, base, answer_table, i, false); | |
42 | } | |
43 | typedef typename boost::sub_match<BidirectionalIterator>::value_type charT; | |
44 | if((sub.matched == 0) | |
45 | && | |
46 | !((i == 0) | |
47 | && (test_info<charT>::match_options() & boost::match_partial)) ) | |
48 | { | |
49 | if(answer_table[2*i] >= 0) | |
50 | { | |
51 | BOOST_REGEX_TEST_ERROR( | |
52 | "Sub-expression " << i | |
53 | << " was not matched when it should have been.", charT); | |
54 | } | |
55 | } | |
56 | else | |
57 | { | |
1e59de90 | 58 | if(std::distance(base, sub.first) != answer_table[2*i]) |
7c673cae FG |
59 | { |
60 | BOOST_REGEX_TEST_ERROR( | |
61 | "Error in start location of sub-expression " | |
1e59de90 | 62 | << i << ", found " << std::distance(base, sub.first) |
7c673cae FG |
63 | << ", expected " << answer_table[2*i] << ".", charT); |
64 | } | |
1e59de90 | 65 | if(std::distance(base, sub.second) != answer_table[1+ 2*i]) |
7c673cae FG |
66 | { |
67 | BOOST_REGEX_TEST_ERROR( | |
68 | "Error in end location of sub-expression " | |
1e59de90 | 69 | << i << ", found " << std::distance(base, sub.second) |
7c673cae FG |
70 | << ", expected " << answer_table[1 + 2*i] << ".", charT); |
71 | } | |
72 | } | |
73 | #ifdef BOOST_MSVC | |
74 | #pragma warning(pop) | |
75 | #endif | |
76 | } | |
77 | ||
78 | template <class BidirectionalIterator, class Allocator> | |
79 | void test_result(const boost::match_results<BidirectionalIterator, Allocator>& what, BidirectionalIterator base, const int* answer_table, bool recurse = true) | |
80 | { | |
81 | if(recurse) | |
82 | { | |
83 | boost::match_results<BidirectionalIterator, Allocator> copy(what); | |
84 | test_result(copy, base, answer_table, false); | |
85 | boost::match_results<BidirectionalIterator, Allocator> s; | |
86 | s.swap(copy); | |
87 | test_result(s, base, answer_table, false); | |
88 | boost::match_results<BidirectionalIterator, Allocator> s2; | |
89 | s2 = what; | |
90 | test_result(s2, base, answer_table, false); | |
91 | } | |
92 | for(unsigned i = 0; i < what.size(); ++i) | |
93 | { | |
94 | test_sub_match(what[i], base, answer_table, i); | |
95 | } | |
96 | } | |
97 | ||
98 | template<class charT, class traits> | |
99 | void test_simple_search(boost::basic_regex<charT, traits>& r) | |
100 | { | |
101 | typedef typename std::basic_string<charT>::const_iterator const_iterator; | |
102 | const std::basic_string<charT>& search_text = test_info<charT>::search_text(); | |
103 | boost::regex_constants::match_flag_type opts = test_info<charT>::match_options(); | |
104 | const int* answer_table = test_info<charT>::answer_table(); | |
105 | boost::match_results<const_iterator> what; | |
106 | if(boost::regex_search( | |
107 | search_text.begin(), | |
108 | search_text.end(), | |
109 | what, | |
110 | r, | |
111 | opts)) | |
112 | { | |
113 | test_result(what, search_text.begin(), answer_table); | |
114 | // setting match_any should have no effect on the result returned: | |
115 | if(!boost::regex_search( | |
116 | search_text.begin(), | |
117 | search_text.end(), | |
118 | r, | |
119 | opts|boost::regex_constants::match_any)) | |
120 | { | |
121 | BOOST_REGEX_TEST_ERROR("Expected match was not found when using the match_any flag.", charT); | |
122 | } | |
123 | } | |
124 | else | |
125 | { | |
126 | if(answer_table[0] >= 0) | |
127 | { | |
128 | // we should have had a match but didn't: | |
129 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
130 | } | |
131 | // setting match_any should have no effect on the result returned: | |
132 | else if(boost::regex_search( | |
133 | search_text.begin(), | |
134 | search_text.end(), | |
135 | r, | |
136 | opts|boost::regex_constants::match_any)) | |
137 | { | |
138 | BOOST_REGEX_TEST_ERROR("Unexpected match was found when using the match_any flag.", charT); | |
139 | } | |
140 | } | |
141 | #ifdef TEST_ROPE | |
142 | std::rope<charT> rsearch_text; | |
143 | for(unsigned i = 0; i < search_text.size(); ++i) | |
144 | { | |
145 | std::rope<charT> c(search_text[i]); | |
146 | if(++i != search_text.size()) | |
147 | { | |
148 | c.append(search_text[i]); | |
149 | if(++i != search_text.size()) | |
150 | { | |
151 | c.append(search_text[i]); | |
152 | } | |
153 | } | |
154 | rsearch_text.append(c); | |
155 | } | |
156 | boost::match_results<std::rope<charT>::const_iterator> rwhat; | |
157 | if(boost::regex_search( | |
158 | rsearch_text.begin(), | |
159 | rsearch_text.end(), | |
160 | rwhat, | |
161 | r, | |
162 | opts)) | |
163 | { | |
164 | test_result(rwhat, rsearch_text.begin(), answer_table); | |
165 | } | |
166 | else | |
167 | { | |
168 | if(answer_table[0] >= 0) | |
169 | { | |
170 | // we should have had a match but didn't: | |
171 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
172 | } | |
173 | } | |
174 | #endif | |
175 | } | |
176 | ||
177 | template<class charT, class traits> | |
178 | void test_regex_iterator(boost::basic_regex<charT, traits>& r) | |
179 | { | |
180 | typedef typename std::basic_string<charT>::const_iterator const_iterator; | |
181 | typedef boost::regex_iterator<const_iterator, charT, traits> test_iterator; | |
182 | const std::basic_string<charT>& search_text = test_info<charT>::search_text(); | |
183 | boost::regex_constants::match_flag_type opts = test_info<charT>::match_options(); | |
184 | const int* answer_table = test_info<charT>::answer_table(); | |
185 | test_iterator start(search_text.begin(), search_text.end(), r, opts), end; | |
186 | test_iterator copy(start); | |
187 | const_iterator last_end = search_text.begin(); | |
188 | while(start != end) | |
189 | { | |
190 | if(start != copy) | |
191 | { | |
192 | BOOST_REGEX_TEST_ERROR("Failed iterator != comparison.", charT); | |
193 | } | |
194 | if(!(start == copy)) | |
195 | { | |
196 | BOOST_REGEX_TEST_ERROR("Failed iterator == comparison.", charT); | |
197 | } | |
198 | test_result(*start, search_text.begin(), answer_table); | |
199 | // test $` and $' : | |
200 | if(start->prefix().first != last_end) | |
201 | { | |
202 | BOOST_REGEX_TEST_ERROR("Incorrect position for start of $`", charT); | |
203 | } | |
204 | if(start->prefix().second != (*start)[0].first) | |
205 | { | |
206 | BOOST_REGEX_TEST_ERROR("Incorrect position for end of $`", charT); | |
207 | } | |
208 | if(start->prefix().matched != (start->prefix().first != start->prefix().second)) | |
209 | { | |
210 | BOOST_REGEX_TEST_ERROR("Incorrect position for matched member of $`", charT); | |
211 | } | |
212 | if(start->suffix().first != (*start)[0].second) | |
213 | { | |
214 | BOOST_REGEX_TEST_ERROR("Incorrect position for start of $'", charT); | |
215 | } | |
216 | if(start->suffix().second != search_text.end()) | |
217 | { | |
218 | BOOST_REGEX_TEST_ERROR("Incorrect position for end of $'", charT); | |
219 | } | |
220 | if(start->suffix().matched != (start->suffix().first != start->suffix().second)) | |
221 | { | |
222 | BOOST_REGEX_TEST_ERROR("Incorrect position for matched member of $'", charT); | |
223 | } | |
224 | last_end = (*start)[0].second; | |
225 | ++start; | |
226 | ++copy; | |
227 | // move on the answer table to next set of answers; | |
228 | if(*answer_table != -2) | |
229 | while(*answer_table++ != -2){} | |
230 | } | |
231 | if(answer_table[0] >= 0) | |
232 | { | |
233 | // we should have had a match but didn't: | |
234 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
235 | } | |
236 | } | |
237 | ||
238 | template<class charT, class traits> | |
239 | void test_regex_token_iterator(boost::basic_regex<charT, traits>& r) | |
240 | { | |
241 | typedef typename std::basic_string<charT>::const_iterator const_iterator; | |
242 | typedef boost::regex_token_iterator<const_iterator, charT, traits> test_iterator; | |
243 | const std::basic_string<charT>& search_text = test_info<charT>::search_text(); | |
244 | boost::regex_constants::match_flag_type opts = test_info<charT>::match_options(); | |
245 | const int* answer_table = test_info<charT>::answer_table(); | |
246 | // | |
247 | // we start by testing sub-expression 0: | |
248 | // | |
249 | test_iterator start(search_text.begin(), search_text.end(), r, 0, opts), end; | |
250 | test_iterator copy(start); | |
251 | while(start != end) | |
252 | { | |
253 | if(start != copy) | |
254 | { | |
255 | BOOST_REGEX_TEST_ERROR("Failed iterator != comparison.", charT); | |
256 | } | |
257 | if(!(start == copy)) | |
258 | { | |
259 | BOOST_REGEX_TEST_ERROR("Failed iterator == comparison.", charT); | |
260 | } | |
261 | test_sub_match(*start, search_text.begin(), answer_table, 0); | |
262 | ++start; | |
263 | ++copy; | |
264 | // move on the answer table to next set of answers; | |
265 | if(*answer_table != -2) | |
266 | while(*answer_table++ != -2){} | |
267 | } | |
268 | if(answer_table[0] >= 0) | |
269 | { | |
270 | // we should have had a match but didn't: | |
271 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
272 | } | |
273 | // | |
274 | // and now field spitting: | |
275 | // | |
276 | test_iterator start2(search_text.begin(), search_text.end(), r, -1, opts), end2; | |
277 | test_iterator copy2(start2); | |
278 | int last_end2 = 0; | |
279 | answer_table = test_info<charT>::answer_table(); | |
280 | while(start2 != end2) | |
281 | { | |
282 | if(start2 != copy2) | |
283 | { | |
284 | BOOST_REGEX_TEST_ERROR("Failed iterator != comparison.", charT); | |
285 | } | |
286 | if(!(start2 == copy2)) | |
287 | { | |
288 | BOOST_REGEX_TEST_ERROR("Failed iterator == comparison.", charT); | |
289 | } | |
290 | #ifdef BOOST_MSVC | |
291 | #pragma warning(push) | |
292 | #pragma warning(disable:4244) | |
293 | #endif | |
1e59de90 | 294 | if(std::distance(search_text.begin(), start2->first) != last_end2) |
7c673cae FG |
295 | { |
296 | BOOST_REGEX_TEST_ERROR( | |
297 | "Error in location of start of field split, found: " | |
1e59de90 | 298 | << std::distance(search_text.begin(), start2->first) |
7c673cae FG |
299 | << ", expected: " |
300 | << last_end2 | |
301 | << ".", charT); | |
302 | } | |
303 | int expected_end = static_cast<int>(answer_table[0] < 0 ? search_text.size() : answer_table[0]); | |
1e59de90 | 304 | if(std::distance(search_text.begin(), start2->second) != expected_end) |
7c673cae FG |
305 | { |
306 | BOOST_REGEX_TEST_ERROR( | |
307 | "Error in location of end2 of field split, found: " | |
1e59de90 | 308 | << std::distance(search_text.begin(), start2->second) |
7c673cae FG |
309 | << ", expected: " |
310 | << expected_end | |
311 | << ".", charT); | |
312 | } | |
313 | #ifdef BOOST_MSVC | |
314 | #pragma warning(pop) | |
315 | #endif | |
316 | last_end2 = answer_table[1]; | |
317 | ++start2; | |
318 | ++copy2; | |
319 | // move on the answer table to next set of answers; | |
320 | if(*answer_table != -2) | |
321 | while(*answer_table++ != -2){} | |
322 | } | |
323 | if(answer_table[0] >= 0) | |
324 | { | |
325 | // we should have had a match but didn't: | |
326 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
327 | } | |
328 | #if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) | |
329 | // | |
330 | // and now both field splitting and $0: | |
331 | // | |
332 | std::vector<int> subs; | |
333 | subs.push_back(-1); | |
334 | subs.push_back(0); | |
335 | start2 = test_iterator(search_text.begin(), search_text.end(), r, subs, opts); | |
336 | copy2 = start2; | |
337 | last_end2 = 0; | |
338 | answer_table = test_info<charT>::answer_table(); | |
339 | while(start2 != end2) | |
340 | { | |
341 | if(start2 != copy2) | |
342 | { | |
343 | BOOST_REGEX_TEST_ERROR("Failed iterator != comparison.", charT); | |
344 | } | |
345 | if(!(start2 == copy2)) | |
346 | { | |
347 | BOOST_REGEX_TEST_ERROR("Failed iterator == comparison.", charT); | |
348 | } | |
349 | #ifdef BOOST_MSVC | |
350 | #pragma warning(push) | |
351 | #pragma warning(disable:4244) | |
352 | #endif | |
1e59de90 | 353 | if(std::distance(search_text.begin(), start2->first) != last_end2) |
7c673cae FG |
354 | { |
355 | BOOST_REGEX_TEST_ERROR( | |
356 | "Error in location of start of field split, found: " | |
1e59de90 | 357 | << std::distance(search_text.begin(), start2->first) |
7c673cae FG |
358 | << ", expected: " |
359 | << last_end2 | |
360 | << ".", charT); | |
361 | } | |
362 | int expected_end = static_cast<int>(answer_table[0] < 0 ? search_text.size() : answer_table[0]); | |
1e59de90 | 363 | if(std::distance(search_text.begin(), start2->second) != expected_end) |
7c673cae FG |
364 | { |
365 | BOOST_REGEX_TEST_ERROR( | |
366 | "Error in location of end2 of field split, found: " | |
1e59de90 | 367 | << std::distance(search_text.begin(), start2->second) |
7c673cae FG |
368 | << ", expected: " |
369 | << expected_end | |
370 | << ".", charT); | |
371 | } | |
372 | #ifdef BOOST_MSVC | |
373 | #pragma warning(pop) | |
374 | #endif | |
375 | last_end2 = answer_table[1]; | |
376 | ++start2; | |
377 | ++copy2; | |
378 | if((start2 == end2) && (answer_table[0] >= 0)) | |
379 | { | |
380 | BOOST_REGEX_TEST_ERROR( | |
381 | "Expected $0 match not found", charT); | |
382 | } | |
383 | if(start2 != end2) | |
384 | { | |
385 | test_sub_match(*start2, search_text.begin(), answer_table, 0); | |
386 | ++start2; | |
387 | ++copy2; | |
388 | } | |
389 | // move on the answer table to next set of answers; | |
390 | if(*answer_table != -2) | |
391 | while(*answer_table++ != -2){} | |
392 | } | |
393 | if(answer_table[0] >= 0) | |
394 | { | |
395 | // we should have had a match but didn't: | |
396 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
397 | } | |
398 | #endif | |
399 | } | |
400 | ||
401 | template <class charT, class traits> | |
402 | struct grep_test_predicate | |
403 | { | |
404 | typedef typename std::basic_string<charT>::const_iterator test_iter; | |
405 | ||
406 | grep_test_predicate(test_iter b, const int* a) | |
407 | : m_base(b), m_table(a) | |
408 | {} | |
409 | bool operator()(const boost::match_results<test_iter>& what) | |
410 | { | |
411 | test_result(what, m_base, m_table); | |
412 | // move on the answer table to next set of answers; | |
413 | if(*m_table != -2) | |
414 | while(*m_table++ != -2){} | |
415 | return true; | |
416 | } | |
417 | private: | |
418 | test_iter m_base; | |
419 | const int* m_table; | |
420 | }; | |
421 | ||
422 | template<class charT, class traits> | |
423 | void test_regex_grep(boost::basic_regex<charT, traits>& r) | |
424 | { | |
425 | //typedef typename std::basic_string<charT>::const_iterator const_iterator; | |
426 | const std::basic_string<charT>& search_text = test_info<charT>::search_text(); | |
427 | boost::regex_constants::match_flag_type opts = test_info<charT>::match_options(); | |
428 | const int* answer_table = test_info<charT>::answer_table(); | |
429 | grep_test_predicate<charT, traits> pred(search_text.begin(), answer_table); | |
430 | boost::regex_grep(pred, search_text.begin(), search_text.end(), r, opts); | |
431 | } | |
432 | ||
433 | template<class charT, class traits> | |
434 | void test_regex_match(boost::basic_regex<charT, traits>& r) | |
435 | { | |
436 | typedef typename std::basic_string<charT>::const_iterator const_iterator; | |
437 | const std::basic_string<charT>& search_text = test_info<charT>::search_text(); | |
438 | boost::regex_constants::match_flag_type opts = test_info<charT>::match_options(); | |
439 | const int* answer_table = test_info<charT>::answer_table(); | |
440 | boost::match_results<const_iterator> what; | |
441 | if(answer_table[0] < 0) | |
442 | { | |
443 | if(boost::regex_match(search_text, r, opts)) | |
444 | { | |
445 | BOOST_REGEX_TEST_ERROR("boost::regex_match found a match when it should not have done so.", charT); | |
446 | } | |
447 | } | |
448 | else | |
449 | { | |
450 | if((answer_table[0] > 0) && boost::regex_match(search_text, r, opts)) | |
451 | { | |
452 | BOOST_REGEX_TEST_ERROR("boost::regex_match found a match when it should not have done so.", charT); | |
453 | } | |
454 | else if((answer_table[0] == 0) && (answer_table[1] == static_cast<int>(search_text.size()))) | |
455 | { | |
456 | if(boost::regex_match( | |
457 | search_text.begin(), | |
458 | search_text.end(), | |
459 | what, | |
460 | r, | |
461 | opts)) | |
462 | { | |
463 | test_result(what, search_text.begin(), answer_table); | |
464 | } | |
465 | else if(answer_table[0] >= 0) | |
466 | { | |
467 | // we should have had a match but didn't: | |
468 | BOOST_REGEX_TEST_ERROR("Expected match was not found.", charT); | |
469 | } | |
470 | } | |
471 | } | |
472 | } | |
473 | ||
474 | template<class charT, class traits> | |
475 | void test(boost::basic_regex<charT, traits>& r, const test_regex_search_tag&) | |
476 | { | |
477 | const std::basic_string<charT>& expression = test_info<charT>::expression(); | |
478 | boost::regex_constants::syntax_option_type syntax_options = test_info<charT>::syntax_options(); | |
479 | #ifndef BOOST_NO_EXCEPTIONS | |
480 | try | |
481 | #endif | |
482 | { | |
483 | r.assign(expression, syntax_options); | |
484 | if(r.status()) | |
485 | { | |
486 | BOOST_REGEX_TEST_ERROR("Expression did not compile when it should have done, error code = " << r.status(), charT); | |
487 | } | |
488 | if(expression != std::basic_string<charT>(r.begin(), r.end())) | |
489 | { | |
490 | BOOST_REGEX_TEST_ERROR("Stored expression string was incorrect", charT); | |
491 | } | |
492 | test_simple_search(r); | |
493 | test_regex_iterator(r); | |
494 | test_regex_token_iterator(r); | |
495 | test_regex_grep(r); | |
496 | test_regex_match(r); | |
497 | // | |
498 | // Verify sub-expression locations: | |
499 | // | |
500 | #ifndef BOOST_NO_EXCEPTIONS | |
501 | if((syntax_options & boost::regbase::save_subexpression_location) == 0) | |
502 | { | |
503 | bool have_except = false; | |
504 | try | |
505 | { | |
506 | r.subexpression(1); | |
507 | } | |
508 | catch(const std::out_of_range&) | |
509 | { | |
510 | have_except = true; | |
511 | } | |
512 | if(!have_except) | |
513 | { | |
514 | BOOST_REGEX_TEST_ERROR("Expected std::out_of_range error was not found.", charT); | |
515 | } | |
516 | } | |
517 | #endif | |
518 | r.assign(expression, syntax_options | boost::regbase::save_subexpression_location); | |
519 | for(std::size_t i = 0; i < r.mark_count(); ++i) | |
520 | { | |
521 | std::pair<const charT*, const charT*> p = r.subexpression(i); | |
522 | if(*p.first != '(') | |
523 | { | |
524 | BOOST_REGEX_TEST_ERROR("Starting location of sub-expression " << i << " iterator was invalid.", charT); | |
525 | } | |
526 | if(*p.second != ')') | |
527 | { | |
528 | BOOST_REGEX_TEST_ERROR("Ending location of sub-expression " << i << " iterator was invalid.", charT); | |
529 | } | |
530 | } | |
531 | } | |
532 | #ifndef BOOST_NO_EXCEPTIONS | |
533 | catch(const boost::bad_expression& e) | |
534 | { | |
535 | BOOST_REGEX_TEST_ERROR("Expression did not compile when it should have done: " << e.what(), charT); | |
536 | } | |
537 | catch(const std::runtime_error& e) | |
538 | { | |
539 | BOOST_REGEX_TEST_ERROR("Received an unexpected std::runtime_error: " << e.what(), charT); | |
540 | } | |
541 | catch(const std::exception& e) | |
542 | { | |
543 | BOOST_REGEX_TEST_ERROR("Received an unexpected std::exception: " << e.what(), charT); | |
544 | } | |
545 | catch(...) | |
546 | { | |
547 | BOOST_REGEX_TEST_ERROR("Received an unexpected exception of unknown type", charT); | |
548 | } | |
549 | #endif | |
550 | } | |
551 | ||
552 | ||
553 | ||
554 | #endif |