1 // Copyright 2008 The RE2 Authors. All Rights Reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Regular expression engine tester -- test all the implementations against each other.
6
7 #include "re2/testing/tester.h"
8
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12
13 #include <string>
14
15 #include "absl/base/macros.h"
16 #include "absl/flags/flag.h"
17 #include "absl/log/absl_check.h"
18 #include "absl/log/absl_log.h"
19 #include "absl/strings/escaping.h"
20 #include "absl/strings/str_format.h"
21 #include "absl/strings/string_view.h"
22 #include "re2/prog.h"
23 #include "re2/re2.h"
24 #include "re2/regexp.h"
25 #include "util/pcre.h"
26
27 ABSL_FLAG(bool, dump_prog, false, "dump regexp program");
28 ABSL_FLAG(bool, log_okay, false, "log successful runs");
29 ABSL_FLAG(bool, dump_rprog, false, "dump reversed regexp program");
30
31 ABSL_FLAG(int, max_regexp_failures, 100,
32 "maximum number of regexp test failures (-1 = unlimited)");
33
34 ABSL_FLAG(std::string, regexp_engines, "",
35 "pattern to select regexp engines to test");
36
37 namespace re2 {
38
39 enum {
40 kMaxSubmatch = 1+16, // $0...$16
41 };
42
43 const char* engine_names[kEngineMax] = {
44 "Backtrack",
45 "NFA",
46 "DFA",
47 "DFA1",
48 "OnePass",
49 "BitState",
50 "RE2",
51 "RE2a",
52 "RE2b",
53 "PCRE",
54 };
55
56 // Returns the name of the engine.
EngineName(Engine e)57 static const char* EngineName(Engine e) {
58 ABSL_CHECK_GE(e, 0);
59 ABSL_CHECK_LT(e, ABSL_ARRAYSIZE(engine_names));
60 ABSL_CHECK(engine_names[e] != NULL);
61 return engine_names[e];
62 }
63
64 // Returns bit mask of engines to use.
Engines()65 static uint32_t Engines() {
66 static bool did_parse = false;
67 static uint32_t cached_engines = 0;
68
69 if (did_parse)
70 return cached_engines;
71
72 if (absl::GetFlag(FLAGS_regexp_engines).empty()) {
73 cached_engines = ~0;
74 } else {
75 for (Engine i = static_cast<Engine>(0); i < kEngineMax; i++)
76 if (absl::GetFlag(FLAGS_regexp_engines).find(EngineName(i)) != std::string::npos)
77 cached_engines |= 1<<i;
78 }
79
80 if (cached_engines == 0)
81 ABSL_LOG(INFO) << "Warning: no engines enabled.";
82 if (!UsingPCRE)
83 cached_engines &= ~(1<<kEnginePCRE);
84 for (Engine i = static_cast<Engine>(0); i < kEngineMax; i++) {
85 if (cached_engines & (1<<i))
86 ABSL_LOG(INFO) << EngineName(i) << " enabled";
87 }
88
89 did_parse = true;
90 return cached_engines;
91 }
92
93 // The result of running a match.
94 struct TestInstance::Result {
Resultre2::TestInstance::Result95 Result()
96 : skipped(false),
97 matched(false),
98 untrusted(false),
99 have_submatch(false),
100 have_submatch0(false) {
101 ClearSubmatch();
102 }
103
ClearSubmatchre2::TestInstance::Result104 void ClearSubmatch() {
105 for (int i = 0; i < kMaxSubmatch; i++)
106 submatch[i] = absl::string_view();
107 }
108
109 bool skipped; // test skipped: wasn't applicable
110 bool matched; // found a match
111 bool untrusted; // don't really trust the answer
112 bool have_submatch; // computed all submatch info
113 bool have_submatch0; // computed just submatch[0]
114 absl::string_view submatch[kMaxSubmatch];
115 };
116
117 typedef TestInstance::Result Result;
118
119 // Formats a single capture range s in text in the form (a,b)
120 // where a and b are the starting and ending offsets of s in text.
FormatCapture(absl::string_view text,absl::string_view s)121 static std::string FormatCapture(absl::string_view text,
122 absl::string_view s) {
123 if (s.data() == NULL)
124 return "(?,?)";
125 return absl::StrFormat("(%d,%d)",
126 BeginPtr(s) - BeginPtr(text),
127 EndPtr(s) - BeginPtr(text));
128 }
129
130 // Returns whether text contains non-ASCII (>= 0x80) bytes.
NonASCII(absl::string_view text)131 static bool NonASCII(absl::string_view text) {
132 for (size_t i = 0; i < text.size(); i++)
133 if ((uint8_t)text[i] >= 0x80)
134 return true;
135 return false;
136 }
137
138 // Returns string representation of match kind.
FormatKind(Prog::MatchKind kind)139 static std::string FormatKind(Prog::MatchKind kind) {
140 switch (kind) {
141 case Prog::kFullMatch:
142 return "full match";
143 case Prog::kLongestMatch:
144 return "longest match";
145 case Prog::kFirstMatch:
146 return "first match";
147 case Prog::kManyMatch:
148 return "many match";
149 }
150 return "???";
151 }
152
153 // Returns string representation of anchor kind.
FormatAnchor(Prog::Anchor anchor)154 static std::string FormatAnchor(Prog::Anchor anchor) {
155 switch (anchor) {
156 case Prog::kAnchored:
157 return "anchored";
158 case Prog::kUnanchored:
159 return "unanchored";
160 }
161 return "???";
162 }
163
164 struct ParseMode {
165 Regexp::ParseFlags parse_flags;
166 std::string desc;
167 };
168
169 static const Regexp::ParseFlags single_line =
170 Regexp::LikePerl;
171 static const Regexp::ParseFlags multi_line =
172 static_cast<Regexp::ParseFlags>(Regexp::LikePerl & ~Regexp::OneLine);
173
174 static ParseMode parse_modes[] = {
175 { single_line, "single-line" },
176 { single_line|Regexp::Latin1, "single-line, latin1" },
177 { multi_line, "multiline" },
178 { multi_line|Regexp::NonGreedy, "multiline, nongreedy" },
179 { multi_line|Regexp::Latin1, "multiline, latin1" },
180 };
181
FormatMode(Regexp::ParseFlags flags)182 static std::string FormatMode(Regexp::ParseFlags flags) {
183 for (size_t i = 0; i < ABSL_ARRAYSIZE(parse_modes); i++)
184 if (parse_modes[i].parse_flags == flags)
185 return parse_modes[i].desc;
186 return absl::StrFormat("%#x", static_cast<uint32_t>(flags));
187 }
188
189 // Constructs and saves all the matching engines that
190 // will be required for the given tests.
TestInstance(absl::string_view regexp_str,Prog::MatchKind kind,Regexp::ParseFlags flags)191 TestInstance::TestInstance(absl::string_view regexp_str, Prog::MatchKind kind,
192 Regexp::ParseFlags flags)
193 : regexp_str_(regexp_str),
194 kind_(kind),
195 flags_(flags),
196 error_(false),
197 regexp_(NULL),
198 num_captures_(0),
199 prog_(NULL),
200 rprog_(NULL),
201 re_(NULL),
202 re2_(NULL) {
203
204 ABSL_VLOG(1) << absl::CEscape(regexp_str);
205
206 // Compile regexp to prog.
207 // Always required - needed for backtracking (reference implementation).
208 RegexpStatus status;
209 regexp_ = Regexp::Parse(regexp_str, flags, &status);
210 if (regexp_ == NULL) {
211 ABSL_LOG(INFO) << "Cannot parse: " << absl::CEscape(regexp_str_)
212 << " mode: " << FormatMode(flags);
213 error_ = true;
214 return;
215 }
216 num_captures_ = regexp_->NumCaptures();
217 prog_ = regexp_->CompileToProg(0);
218 if (prog_ == NULL) {
219 ABSL_LOG(INFO) << "Cannot compile: " << absl::CEscape(regexp_str_);
220 error_ = true;
221 return;
222 }
223 if (absl::GetFlag(FLAGS_dump_prog)) {
224 ABSL_LOG(INFO) << "Prog for "
225 << " regexp "
226 << absl::CEscape(regexp_str_)
227 << " (" << FormatKind(kind_)
228 << ", " << FormatMode(flags_)
229 << ")\n"
230 << prog_->Dump();
231 }
232
233 // Compile regexp to reversed prog. Only needed for DFA engines.
234 if (Engines() & ((1<<kEngineDFA)|(1<<kEngineDFA1))) {
235 rprog_ = regexp_->CompileToReverseProg(0);
236 if (rprog_ == NULL) {
237 ABSL_LOG(INFO) << "Cannot reverse compile: "
238 << absl::CEscape(regexp_str_);
239 error_ = true;
240 return;
241 }
242 if (absl::GetFlag(FLAGS_dump_rprog))
243 ABSL_LOG(INFO) << rprog_->Dump();
244 }
245
246 // Create re string that will be used for RE and RE2.
247 std::string re = std::string(regexp_str);
248 // Accomodate flags.
249 // Regexp::Latin1 will be accomodated below.
250 if (!(flags & Regexp::OneLine))
251 re = "(?m)" + re;
252 if (flags & Regexp::NonGreedy)
253 re = "(?U)" + re;
254 if (flags & Regexp::DotNL)
255 re = "(?s)" + re;
256
257 // Compile regexp to RE2.
258 if (Engines() & ((1<<kEngineRE2)|(1<<kEngineRE2a)|(1<<kEngineRE2b))) {
259 RE2::Options options;
260 if (flags & Regexp::Latin1)
261 options.set_encoding(RE2::Options::EncodingLatin1);
262 if (kind_ == Prog::kLongestMatch)
263 options.set_longest_match(true);
264 re2_ = new RE2(re, options);
265 if (!re2_->error().empty()) {
266 ABSL_LOG(INFO) << "Cannot RE2: " << absl::CEscape(re);
267 error_ = true;
268 return;
269 }
270 }
271
272 // Compile regexp to RE.
273 // PCRE as exposed by the RE interface isn't always usable.
274 // 1. It disagrees about handling of empty-string reptitions
275 // like matching (a*)* against "b". PCRE treats the (a*) as
276 // occurring once, while we treat it as occurring not at all.
277 // 2. It treats $ as this weird thing meaning end of string
278 // or before the \n at the end of the string.
279 // 3. It doesn't implement POSIX leftmost-longest matching.
280 // 4. It lets \s match vertical tab.
281 // MimicsPCRE() detects 1 and 2.
282 if ((Engines() & (1<<kEnginePCRE)) && regexp_->MimicsPCRE() &&
283 kind_ != Prog::kLongestMatch) {
284 PCRE_Options o;
285 o.set_option(PCRE::UTF8);
286 if (flags & Regexp::Latin1)
287 o.set_option(PCRE::None);
288 // PCRE has interface bug keeping us from finding $0, so
289 // add one more layer of parens.
290 re_ = new PCRE("("+re+")", o);
291 if (!re_->error().empty()) {
292 ABSL_LOG(INFO) << "Cannot PCRE: " << absl::CEscape(re);
293 error_ = true;
294 return;
295 }
296 }
297 }
298
~TestInstance()299 TestInstance::~TestInstance() {
300 if (regexp_)
301 regexp_->Decref();
302 delete prog_;
303 delete rprog_;
304 delete re_;
305 delete re2_;
306 }
307
308 // Runs a single search using the named engine type.
309 // This interface hides all the irregularities of the various
310 // engine interfaces from the rest of this file.
RunSearch(Engine type,absl::string_view orig_text,absl::string_view orig_context,Prog::Anchor anchor,Result * result)311 void TestInstance::RunSearch(Engine type, absl::string_view orig_text,
312 absl::string_view orig_context,
313 Prog::Anchor anchor, Result* result) {
314 if (regexp_ == NULL) {
315 result->skipped = true;
316 return;
317 }
318 int nsubmatch = 1 + num_captures_; // NumCaptures doesn't count $0
319 if (nsubmatch > kMaxSubmatch)
320 nsubmatch = kMaxSubmatch;
321
322 absl::string_view text = orig_text;
323 absl::string_view context = orig_context;
324
325 switch (type) {
326 default:
327 ABSL_LOG(FATAL) << "Bad RunSearch type: " << (int)type;
328
329 case kEngineBacktrack:
330 if (prog_ == NULL) {
331 result->skipped = true;
332 break;
333 }
334 result->matched =
335 prog_->UnsafeSearchBacktrack(text, context, anchor, kind_,
336 result->submatch, nsubmatch);
337 result->have_submatch = true;
338 break;
339
340 case kEngineNFA:
341 if (prog_ == NULL) {
342 result->skipped = true;
343 break;
344 }
345 result->matched =
346 prog_->SearchNFA(text, context, anchor, kind_,
347 result->submatch, nsubmatch);
348 result->have_submatch = true;
349 break;
350
351 case kEngineDFA:
352 if (prog_ == NULL) {
353 result->skipped = true;
354 break;
355 }
356 result->matched = prog_->SearchDFA(text, context, anchor, kind_, NULL,
357 &result->skipped, NULL);
358 break;
359
360 case kEngineDFA1:
361 if (prog_ == NULL || rprog_ == NULL) {
362 result->skipped = true;
363 break;
364 }
365 result->matched =
366 prog_->SearchDFA(text, context, anchor, kind_, result->submatch,
367 &result->skipped, NULL);
368 // If anchored, no need for second run,
369 // but do it anyway to find more bugs.
370 if (result->matched) {
371 if (!rprog_->SearchDFA(result->submatch[0], context,
372 Prog::kAnchored, Prog::kLongestMatch,
373 result->submatch,
374 &result->skipped, NULL)) {
375 ABSL_LOG(ERROR) << "Reverse DFA inconsistency: "
376 << absl::CEscape(regexp_str_)
377 << " on " << absl::CEscape(text);
378 result->matched = false;
379 }
380 }
381 result->have_submatch0 = true;
382 break;
383
384 case kEngineOnePass:
385 if (prog_ == NULL ||
386 !prog_->IsOnePass() ||
387 anchor == Prog::kUnanchored ||
388 nsubmatch > Prog::kMaxOnePassCapture) {
389 result->skipped = true;
390 break;
391 }
392 result->matched = prog_->SearchOnePass(text, context, anchor, kind_,
393 result->submatch, nsubmatch);
394 result->have_submatch = true;
395 break;
396
397 case kEngineBitState:
398 if (prog_ == NULL ||
399 !prog_->CanBitState()) {
400 result->skipped = true;
401 break;
402 }
403 result->matched = prog_->SearchBitState(text, context, anchor, kind_,
404 result->submatch, nsubmatch);
405 result->have_submatch = true;
406 break;
407
408 case kEngineRE2:
409 case kEngineRE2a:
410 case kEngineRE2b: {
411 if (!re2_ || EndPtr(text) != EndPtr(context)) {
412 result->skipped = true;
413 break;
414 }
415
416 RE2::Anchor re_anchor;
417 if (anchor == Prog::kAnchored)
418 re_anchor = RE2::ANCHOR_START;
419 else
420 re_anchor = RE2::UNANCHORED;
421 if (kind_ == Prog::kFullMatch)
422 re_anchor = RE2::ANCHOR_BOTH;
423
424 result->matched = re2_->Match(
425 context,
426 static_cast<size_t>(BeginPtr(text) - BeginPtr(context)),
427 static_cast<size_t>(EndPtr(text) - BeginPtr(context)),
428 re_anchor,
429 result->submatch,
430 nsubmatch);
431 result->have_submatch = nsubmatch > 0;
432 break;
433 }
434
435 case kEnginePCRE: {
436 if (!re_ || BeginPtr(text) != BeginPtr(context) ||
437 EndPtr(text) != EndPtr(context)) {
438 result->skipped = true;
439 break;
440 }
441
442 // In Perl/PCRE, \v matches any character considered vertical
443 // whitespace, not just vertical tab. Regexp::MimicsPCRE() is
444 // unable to handle all cases of this, unfortunately, so just
445 // catch them here. :(
446 if (regexp_str_.find("\\v") != absl::string_view::npos &&
447 (text.find('\n') != absl::string_view::npos ||
448 text.find('\f') != absl::string_view::npos ||
449 text.find('\r') != absl::string_view::npos)) {
450 result->skipped = true;
451 break;
452 }
453
454 // PCRE 8.34 or so started allowing vertical tab to match \s,
455 // following a change made in Perl 5.18. RE2 does not.
456 if ((regexp_str_.find("\\s") != absl::string_view::npos ||
457 regexp_str_.find("\\S") != absl::string_view::npos) &&
458 text.find('\v') != absl::string_view::npos) {
459 result->skipped = true;
460 break;
461 }
462
463 const PCRE::Arg **argptr = new const PCRE::Arg*[nsubmatch];
464 PCRE::Arg *a = new PCRE::Arg[nsubmatch];
465 for (int i = 0; i < nsubmatch; i++) {
466 a[i] = PCRE::Arg(&result->submatch[i]);
467 argptr[i] = &a[i];
468 }
469 size_t consumed;
470 PCRE::Anchor pcre_anchor;
471 if (anchor == Prog::kAnchored)
472 pcre_anchor = PCRE::ANCHOR_START;
473 else
474 pcre_anchor = PCRE::UNANCHORED;
475 if (kind_ == Prog::kFullMatch)
476 pcre_anchor = PCRE::ANCHOR_BOTH;
477 re_->ClearHitLimit();
478 result->matched =
479 re_->DoMatch(text,
480 pcre_anchor,
481 &consumed,
482 argptr, nsubmatch);
483 if (re_->HitLimit()) {
484 result->untrusted = true;
485 delete[] argptr;
486 delete[] a;
487 break;
488 }
489 result->have_submatch = true;
490 delete[] argptr;
491 delete[] a;
492 break;
493 }
494 }
495
496 if (!result->matched)
497 result->ClearSubmatch();
498 }
499
500 // Checks whether r is okay given that correct is the right answer.
501 // Specifically, r's answers have to match (but it doesn't have to
502 // claim to have all the answers).
ResultOkay(const Result & r,const Result & correct)503 static bool ResultOkay(const Result& r, const Result& correct) {
504 if (r.skipped)
505 return true;
506 if (r.matched != correct.matched)
507 return false;
508 if (r.have_submatch || r.have_submatch0) {
509 for (int i = 0; i < kMaxSubmatch; i++) {
510 if (correct.submatch[i].data() != r.submatch[i].data() ||
511 correct.submatch[i].size() != r.submatch[i].size())
512 return false;
513 if (!r.have_submatch)
514 break;
515 }
516 }
517 return true;
518 }
519
520 // Runs a single test.
RunCase(absl::string_view text,absl::string_view context,Prog::Anchor anchor)521 bool TestInstance::RunCase(absl::string_view text, absl::string_view context,
522 Prog::Anchor anchor) {
523 // Backtracking is the gold standard.
524 Result correct;
525 RunSearch(kEngineBacktrack, text, context, anchor, &correct);
526 if (correct.skipped) {
527 if (regexp_ == NULL)
528 return true;
529 ABSL_LOG(ERROR) << "Skipped backtracking! " << absl::CEscape(regexp_str_)
530 << " " << FormatMode(flags_);
531 return false;
532 }
533 ABSL_VLOG(1) << "Try: regexp " << absl::CEscape(regexp_str_)
534 << " text " << absl::CEscape(text)
535 << " (" << FormatKind(kind_)
536 << ", " << FormatAnchor(anchor)
537 << ", " << FormatMode(flags_)
538 << ")";
539
540 // Compare the others.
541 bool all_okay = true;
542 for (Engine i = kEngineBacktrack+1; i < kEngineMax; i++) {
543 if (!(Engines() & (1<<i)))
544 continue;
545
546 Result r;
547 RunSearch(i, text, context, anchor, &r);
548 if (ResultOkay(r, correct)) {
549 if (absl::GetFlag(FLAGS_log_okay))
550 LogMatch(r.skipped ? "Skipped: " : "Okay: ", i, text, context, anchor);
551 continue;
552 }
553
554 // We disagree with PCRE on the meaning of some Unicode matches.
555 // In particular, we treat non-ASCII UTF-8 as non-word characters.
556 // We also treat "empty" character sets like [^\w\W] as being
557 // impossible to match, while PCRE apparently excludes some code
558 // points (e.g., 0x0080) from both \w and \W.
559 if (i == kEnginePCRE && NonASCII(text))
560 continue;
561
562 if (!r.untrusted)
563 all_okay = false;
564
565 LogMatch(r.untrusted ? "(Untrusted) Mismatch: " : "Mismatch: ", i, text,
566 context, anchor);
567 if (r.matched != correct.matched) {
568 if (r.matched) {
569 ABSL_LOG(INFO) << " Should not match (but does).";
570 } else {
571 ABSL_LOG(INFO) << " Should match (but does not).";
572 continue;
573 }
574 }
575 for (int i = 0; i < 1+num_captures_; i++) {
576 if (r.submatch[i].data() != correct.submatch[i].data() ||
577 r.submatch[i].size() != correct.submatch[i].size()) {
578 ABSL_LOG(INFO) <<
579 absl::StrFormat(" $%d: should be %s is %s",
580 i,
581 FormatCapture(text, correct.submatch[i]),
582 FormatCapture(text, r.submatch[i]));
583 } else {
584 ABSL_LOG(INFO) <<
585 absl::StrFormat(" $%d: %s ok", i,
586 FormatCapture(text, r.submatch[i]));
587 }
588 }
589 }
590
591 if (!all_okay) {
592 // This will be initialised once (after flags have been initialised)
593 // and that is desirable because we want to enforce a global limit.
594 static int max_regexp_failures = absl::GetFlag(FLAGS_max_regexp_failures);
595 if (max_regexp_failures > 0 && --max_regexp_failures == 0)
596 ABSL_LOG(QFATAL) << "Too many regexp failures.";
597 }
598
599 return all_okay;
600 }
601
LogMatch(const char * prefix,Engine e,absl::string_view text,absl::string_view context,Prog::Anchor anchor)602 void TestInstance::LogMatch(const char* prefix, Engine e,
603 absl::string_view text, absl::string_view context,
604 Prog::Anchor anchor) {
605 ABSL_LOG(INFO) << prefix
606 << EngineName(e)
607 << " regexp "
608 << absl::CEscape(regexp_str_)
609 << " "
610 << absl::CEscape(regexp_->ToString())
611 << " text "
612 << absl::CEscape(text)
613 << " ("
614 << BeginPtr(text) - BeginPtr(context)
615 << ","
616 << EndPtr(text) - BeginPtr(context)
617 << ") of context "
618 << absl::CEscape(context)
619 << " (" << FormatKind(kind_)
620 << ", " << FormatAnchor(anchor)
621 << ", " << FormatMode(flags_)
622 << ")";
623 }
624
625 static Prog::MatchKind kinds[] = {
626 Prog::kFirstMatch,
627 Prog::kLongestMatch,
628 Prog::kFullMatch,
629 };
630
631 // Test all possible match kinds and parse modes.
Tester(absl::string_view regexp)632 Tester::Tester(absl::string_view regexp) {
633 error_ = false;
634 for (size_t i = 0; i < ABSL_ARRAYSIZE(kinds); i++) {
635 for (size_t j = 0; j < ABSL_ARRAYSIZE(parse_modes); j++) {
636 TestInstance* t = new TestInstance(regexp, kinds[i],
637 parse_modes[j].parse_flags);
638 error_ |= t->error();
639 v_.push_back(t);
640 }
641 }
642 }
643
~Tester()644 Tester::~Tester() {
645 for (size_t i = 0; i < v_.size(); i++)
646 delete v_[i];
647 }
648
TestCase(absl::string_view text,absl::string_view context,Prog::Anchor anchor)649 bool Tester::TestCase(absl::string_view text, absl::string_view context,
650 Prog::Anchor anchor) {
651 bool okay = true;
652 for (size_t i = 0; i < v_.size(); i++)
653 okay &= (!v_[i]->error() && v_[i]->RunCase(text, context, anchor));
654 return okay;
655 }
656
657 static Prog::Anchor anchors[] = {
658 Prog::kAnchored,
659 Prog::kUnanchored
660 };
661
TestInput(absl::string_view text)662 bool Tester::TestInput(absl::string_view text) {
663 bool okay = TestInputInContext(text, text);
664 if (!text.empty()) {
665 absl::string_view sp;
666 sp = text;
667 sp.remove_prefix(1);
668 okay &= TestInputInContext(sp, text);
669 sp = text;
670 sp.remove_suffix(1);
671 okay &= TestInputInContext(sp, text);
672 }
673 return okay;
674 }
675
TestInputInContext(absl::string_view text,absl::string_view context)676 bool Tester::TestInputInContext(absl::string_view text,
677 absl::string_view context) {
678 bool okay = true;
679 for (size_t i = 0; i < ABSL_ARRAYSIZE(anchors); i++)
680 okay &= TestCase(text, context, anchors[i]);
681 return okay;
682 }
683
TestRegexpOnText(absl::string_view regexp,absl::string_view text)684 bool TestRegexpOnText(absl::string_view regexp,
685 absl::string_view text) {
686 Tester t(regexp);
687 return t.TestInput(text);
688 }
689
690 } // namespace re2
691