| // Copyright 2006 The RE2 Authors. All Rights Reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Test parse.cc, dump.cc, and tostring.cc. |
| |
| #include <string> |
| |
| #include "util/test.h" |
| #include "util/logging.h" |
| #include "re2/regexp.h" |
| |
| namespace re2 { |
| |
| // In the past, we used 1<<30 here and zeroed the bit later, but that |
| // has undefined behaviour, so now we use an internal-only flag because |
| // otherwise we would have to introduce a new flag value just for this. |
| static const Regexp::ParseFlags TestZeroFlags = Regexp::WasDollar; |
| |
| struct Test { |
| const char* regexp; |
| const char* parse; |
| Regexp::ParseFlags flags; |
| }; |
| |
| static Regexp::ParseFlags kTestFlags = Regexp::MatchNL | |
| Regexp::PerlX | |
| Regexp::PerlClasses | |
| Regexp::UnicodeGroups; |
| |
| static Test tests[] = { |
| // Base cases |
| { "a", "lit{a}" }, |
| { "a.", "cat{lit{a}dot{}}" }, |
| { "a.b", "cat{lit{a}dot{}lit{b}}" }, |
| { "ab", "str{ab}" }, |
| { "a.b.c", "cat{lit{a}dot{}lit{b}dot{}lit{c}}" }, |
| { "abc", "str{abc}" }, |
| { "a|^", "alt{lit{a}bol{}}" }, |
| { "a|b", "cc{0x61-0x62}" }, |
| { "(a)", "cap{lit{a}}" }, |
| { "(a)|b", "alt{cap{lit{a}}lit{b}}" }, |
| { "a*", "star{lit{a}}" }, |
| { "a+", "plus{lit{a}}" }, |
| { "a?", "que{lit{a}}" }, |
| { "a{2}", "rep{2,2 lit{a}}" }, |
| { "a{2,3}", "rep{2,3 lit{a}}" }, |
| { "a{2,}", "rep{2,-1 lit{a}}" }, |
| { "a*?", "nstar{lit{a}}" }, |
| { "a+?", "nplus{lit{a}}" }, |
| { "a??", "nque{lit{a}}" }, |
| { "a{2}?", "nrep{2,2 lit{a}}" }, |
| { "a{2,3}?", "nrep{2,3 lit{a}}" }, |
| { "a{2,}?", "nrep{2,-1 lit{a}}" }, |
| { "", "emp{}" }, |
| { "|", "alt{emp{}emp{}}" }, |
| { "|x|", "alt{emp{}lit{x}emp{}}" }, |
| { ".", "dot{}" }, |
| { "^", "bol{}" }, |
| { "$", "eol{}" }, |
| { "\\|", "lit{|}" }, |
| { "\\(", "lit{(}" }, |
| { "\\)", "lit{)}" }, |
| { "\\*", "lit{*}" }, |
| { "\\+", "lit{+}" }, |
| { "\\?", "lit{?}" }, |
| { "{", "lit{{}" }, |
| { "}", "lit{}}" }, |
| { "\\.", "lit{.}" }, |
| { "\\^", "lit{^}" }, |
| { "\\$", "lit{$}" }, |
| { "\\\\", "lit{\\}" }, |
| { "[ace]", "cc{0x61 0x63 0x65}" }, |
| { "[abc]", "cc{0x61-0x63}" }, |
| { "[a-z]", "cc{0x61-0x7a}" }, |
| { "[a]", "lit{a}" }, |
| { "\\-", "lit{-}" }, |
| { "-", "lit{-}" }, |
| { "\\_", "lit{_}" }, |
| |
| // Posix and Perl extensions |
| { "[[:lower:]]", "cc{0x61-0x7a}" }, |
| { "[a-z]", "cc{0x61-0x7a}" }, |
| { "[^[:lower:]]", "cc{0-0x60 0x7b-0x10ffff}" }, |
| { "[[:^lower:]]", "cc{0-0x60 0x7b-0x10ffff}" }, |
| { "(?i)[[:lower:]]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" }, |
| { "(?i)[a-z]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" }, |
| { "(?i)[^[:lower:]]", "cc{0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" }, |
| { "(?i)[[:^lower:]]", "cc{0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" }, |
| { "\\d", "cc{0x30-0x39}" }, |
| { "\\D", "cc{0-0x2f 0x3a-0x10ffff}" }, |
| { "\\s", "cc{0x9-0xa 0xc-0xd 0x20}" }, |
| { "\\S", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}" }, |
| { "\\w", "cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}" }, |
| { "\\W", "cc{0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}" }, |
| { "(?i)\\w", "cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}" }, |
| { "(?i)\\W", "cc{0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" }, |
| { "[^\\\\]", "cc{0-0x5b 0x5d-0x10ffff}" }, |
| { "\\C", "byte{}" }, |
| |
| // Unicode, negatives, and a double negative. |
| { "\\p{Braille}", "cc{0x2800-0x28ff}" }, |
| { "\\P{Braille}", "cc{0-0x27ff 0x2900-0x10ffff}" }, |
| { "\\p{^Braille}", "cc{0-0x27ff 0x2900-0x10ffff}" }, |
| { "\\P{^Braille}", "cc{0x2800-0x28ff}" }, |
| |
| // More interesting regular expressions. |
| { "a{,2}", "str{a{,2}}" }, |
| { "\\.\\^\\$\\\\", "str{.^$\\}" }, |
| { "[a-zABC]", "cc{0x41-0x43 0x61-0x7a}" }, |
| { "[^a]", "cc{0-0x60 0x62-0x10ffff}" }, |
| { "[\xce\xb1-\xce\xb5\xe2\x98\xba]", "cc{0x3b1-0x3b5 0x263a}" }, // utf-8 |
| { "a*{", "cat{star{lit{a}}lit{{}}" }, |
| |
| // Test precedences |
| { "(?:ab)*", "star{str{ab}}" }, |
| { "(ab)*", "star{cap{str{ab}}}" }, |
| { "ab|cd", "alt{str{ab}str{cd}}" }, |
| { "a(b|c)d", "cat{lit{a}cap{cc{0x62-0x63}}lit{d}}" }, |
| |
| // Test squashing of **, ++, ?? et cetera. |
| { "(?:(?:a)*)*", "star{lit{a}}" }, |
| { "(?:(?:a)+)+", "plus{lit{a}}" }, |
| { "(?:(?:a)?)?", "que{lit{a}}" }, |
| { "(?:(?:a)*)+", "star{lit{a}}" }, |
| { "(?:(?:a)*)?", "star{lit{a}}" }, |
| { "(?:(?:a)+)*", "star{lit{a}}" }, |
| { "(?:(?:a)+)?", "star{lit{a}}" }, |
| { "(?:(?:a)?)*", "star{lit{a}}" }, |
| { "(?:(?:a)?)+", "star{lit{a}}" }, |
| |
| // Test flattening. |
| { "(?:a)", "lit{a}" }, |
| { "(?:ab)(?:cd)", "str{abcd}" }, |
| { "(?:a|b)|(?:c|d)", "cc{0x61-0x64}" }, |
| { "a|c", "cc{0x61 0x63}" }, |
| { "a|[cd]", "cc{0x61 0x63-0x64}" }, |
| { "a|.", "dot{}" }, |
| { "[ab]|c", "cc{0x61-0x63}" }, |
| { "[ab]|[cd]", "cc{0x61-0x64}" }, |
| { "[ab]|.", "dot{}" }, |
| { ".|c", "dot{}" }, |
| { ".|[cd]", "dot{}" }, |
| { ".|.", "dot{}" }, |
| |
| // Test Perl quoted literals |
| { "\\Q+|*?{[\\E", "str{+|*?{[}" }, |
| { "\\Q+\\E+", "plus{lit{+}}" }, |
| { "\\Q\\\\E", "lit{\\}" }, |
| { "\\Q\\\\\\E", "str{\\\\}" }, |
| { "\\Qa\\E*", "star{lit{a}}" }, |
| { "\\Qab\\E*", "cat{lit{a}star{lit{b}}}" }, |
| { "\\Qabc\\E*", "cat{str{ab}star{lit{c}}}" }, |
| |
| // Test Perl \A and \z |
| { "(?m)^", "bol{}" }, |
| { "(?m)$", "eol{}" }, |
| { "(?-m)^", "bot{}" }, |
| { "(?-m)$", "eot{}" }, |
| { "(?m)\\A", "bot{}" }, |
| { "(?m)\\z", "eot{\\z}" }, |
| { "(?-m)\\A", "bot{}" }, |
| { "(?-m)\\z", "eot{\\z}" }, |
| |
| // Test named captures |
| { "(?P<name>a)", "cap{name:lit{a}}" }, |
| |
| // Case-folded literals |
| { "[Aa]", "litfold{a}" }, |
| |
| // Strings |
| { "abcde", "str{abcde}" }, |
| { "[Aa][Bb]cd", "cat{strfold{ab}str{cd}}" }, |
| |
| // Reported bug involving \n leaking in despite use of NeverNL. |
| { "[^ ]", "cc{0-0x9 0xb-0x1f 0x21-0x10ffff}", TestZeroFlags }, |
| { "[^ ]", "cc{0-0x9 0xb-0x1f 0x21-0x10ffff}", Regexp::FoldCase }, |
| { "[^ ]", "cc{0-0x9 0xb-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ ]", "cc{0-0x9 0xb-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \f]", "cc{0-0x9 0xb 0xd-0x1f 0x21-0x10ffff}", TestZeroFlags }, |
| { "[^ \f]", "cc{0-0x9 0xb 0xd-0x1f 0x21-0x10ffff}", Regexp::FoldCase }, |
| { "[^ \f]", "cc{0-0x9 0xb 0xd-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \f]", "cc{0-0x9 0xb 0xd-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \r]", "cc{0-0x9 0xb-0xc 0xe-0x1f 0x21-0x10ffff}", TestZeroFlags }, |
| { "[^ \r]", "cc{0-0x9 0xb-0xc 0xe-0x1f 0x21-0x10ffff}", Regexp::FoldCase }, |
| { "[^ \r]", "cc{0-0x9 0xb-0xc 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \r]", "cc{0-0x9 0xb-0xc 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \v]", "cc{0-0x9 0xc-0x1f 0x21-0x10ffff}", TestZeroFlags }, |
| { "[^ \v]", "cc{0-0x9 0xc-0x1f 0x21-0x10ffff}", Regexp::FoldCase }, |
| { "[^ \v]", "cc{0-0x9 0xc-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \v]", "cc{0-0x9 0xc-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \t]", "cc{0-0x8 0xb-0x1f 0x21-0x10ffff}", TestZeroFlags }, |
| { "[^ \t]", "cc{0-0x8 0xb-0x1f 0x21-0x10ffff}", Regexp::FoldCase }, |
| { "[^ \t]", "cc{0-0x8 0xb-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \t]", "cc{0-0x8 0xb-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \r\f\v]", "cc{0-0x9 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \r\f\v]", "cc{0-0x9 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \r\f\t\v]", "cc{0-0x8 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \r\f\t\v]", "cc{0-0x8 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \r\n\f\t\v]", "cc{0-0x8 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \r\n\f\t\v]", "cc{0-0x8 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^ \r\n\f\t]", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL }, |
| { "[^ \r\n\f\t]", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", Regexp::NeverNL | Regexp::FoldCase }, |
| { "[^\t-\n\f-\r ]", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses }, |
| { "[^\t-\n\f-\r ]", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses | Regexp::FoldCase }, |
| { "[^\t-\n\f-\r ]", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses | Regexp::NeverNL }, |
| { "[^\t-\n\f-\r ]", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses | Regexp::NeverNL | Regexp::FoldCase }, |
| { "\\S", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses }, |
| { "\\S", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses | Regexp::FoldCase }, |
| { "\\S", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses | Regexp::NeverNL }, |
| { "\\S", "cc{0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}", |
| Regexp::PerlClasses | Regexp::NeverNL | Regexp::FoldCase }, |
| }; |
| |
| bool RegexpEqualTestingOnly(Regexp* a, Regexp* b) { |
| return Regexp::Equal(a, b); |
| } |
| |
| void TestParse(const Test* tests, int ntests, Regexp::ParseFlags flags, |
| const string& title) { |
| Regexp** re = new Regexp*[ntests]; |
| for (int i = 0; i < ntests; i++) { |
| RegexpStatus status; |
| Regexp::ParseFlags f = flags; |
| if (tests[i].flags != 0) { |
| f = tests[i].flags & ~TestZeroFlags; |
| } |
| re[i] = Regexp::Parse(tests[i].regexp, f, &status); |
| ASSERT_TRUE(re[i] != NULL) |
| << " " << tests[i].regexp << " " << status.Text(); |
| string s = re[i]->Dump(); |
| EXPECT_EQ(string(tests[i].parse), s) << "Regexp: " << tests[i].regexp |
| << "\nparse: " << string(tests[i].parse) << " s: " << s << " flag=" << f; |
| } |
| |
| for (int i = 0; i < ntests; i++) { |
| for (int j = 0; j < ntests; j++) { |
| EXPECT_EQ(string(tests[i].parse) == string(tests[j].parse), |
| RegexpEqualTestingOnly(re[i], re[j])) |
| << "Regexp: " << tests[i].regexp << " " << tests[j].regexp; |
| } |
| } |
| |
| for (int i = 0; i < ntests; i++) |
| re[i]->Decref(); |
| delete[] re; |
| } |
| |
| // Test that regexps parse to expected structures. |
| TEST(TestParse, SimpleRegexps) { |
| TestParse(tests, arraysize(tests), kTestFlags, "simple"); |
| } |
| |
| Test foldcase_tests[] = { |
| { "AbCdE", "strfold{abcde}" }, |
| { "[Aa]", "litfold{a}" }, |
| { "a", "litfold{a}" }, |
| |
| // 0x17F is an old English long s (looks like an f) and folds to s. |
| // 0x212A is the Kelvin symbol and folds to k. |
| { "A[F-g]", "cat{litfold{a}cc{0x41-0x7a 0x17f 0x212a}}" }, // [Aa][A-z...] |
| { "[[:upper:]]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" }, |
| { "[[:lower:]]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" }, |
| }; |
| |
| // Test that parsing with FoldCase works. |
| TEST(TestParse, FoldCase) { |
| TestParse(foldcase_tests, arraysize(foldcase_tests), Regexp::FoldCase, "foldcase"); |
| } |
| |
| Test literal_tests[] = { |
| { "(|)^$.[*+?]{5,10},\\", "str{(|)^$.[*+?]{5,10},\\}" }, |
| }; |
| |
| // Test that parsing with Literal works. |
| TEST(TestParse, Literal) { |
| TestParse(literal_tests, arraysize(literal_tests), Regexp::Literal, "literal"); |
| } |
| |
| Test matchnl_tests[] = { |
| { ".", "dot{}" }, |
| { "\n", "lit{\n}" }, |
| { "[^a]", "cc{0-0x60 0x62-0x10ffff}" }, |
| { "[a\\n]", "cc{0xa 0x61}" }, |
| }; |
| |
| // Test that parsing with MatchNL works. |
| // (Also tested above during simple cases.) |
| TEST(TestParse, MatchNL) { |
| TestParse(matchnl_tests, arraysize(matchnl_tests), Regexp::MatchNL, "with MatchNL"); |
| } |
| |
| Test nomatchnl_tests[] = { |
| { ".", "cc{0-0x9 0xb-0x10ffff}" }, |
| { "\n", "lit{\n}" }, |
| { "[^a]", "cc{0-0x9 0xb-0x60 0x62-0x10ffff}" }, |
| { "[a\\n]", "cc{0xa 0x61}" }, |
| }; |
| |
| // Test that parsing without MatchNL works. |
| TEST(TestParse, NoMatchNL) { |
| TestParse(nomatchnl_tests, arraysize(nomatchnl_tests), Regexp::NoParseFlags, "without MatchNL"); |
| } |
| |
| Test prefix_tests[] = { |
| { "abc|abd", "cat{str{ab}cc{0x63-0x64}}" }, |
| { "a(?:b)c|abd", "cat{str{ab}cc{0x63-0x64}}" }, |
| { "abc|abd|aef|bcx|bcy", |
| "alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}" |
| "cat{str{bc}cc{0x78-0x79}}}" }, |
| { "abc|x|abd", "alt{str{abc}lit{x}str{abd}}" }, |
| { "(?i)abc|ABD", "cat{strfold{ab}cc{0x43-0x44 0x63-0x64}}" }, |
| { "[ab]c|[ab]d", "cat{cc{0x61-0x62}cc{0x63-0x64}}" }, |
| { ".c|.d", "cat{cc{0-0x9 0xb-0x10ffff}cc{0x63-0x64}}" }, |
| { "\\Cc|\\Cd", "cat{byte{}cc{0x63-0x64}}" }, |
| { "x{2}|x{2}[0-9]", |
| "cat{rep{2,2 lit{x}}alt{emp{}cc{0x30-0x39}}}" }, |
| { "x{2}y|x{2}[0-9]y", |
| "cat{rep{2,2 lit{x}}alt{lit{y}cat{cc{0x30-0x39}lit{y}}}}" }, |
| { "n|r|rs", |
| "alt{lit{n}cat{lit{r}alt{emp{}lit{s}}}}" }, |
| { "n|rs|r", |
| "alt{lit{n}cat{lit{r}alt{lit{s}emp{}}}}" }, |
| { "r|rs|n", |
| "alt{cat{lit{r}alt{emp{}lit{s}}}lit{n}}" }, |
| { "rs|r|n", |
| "alt{cat{lit{r}alt{lit{s}emp{}}}lit{n}}" }, |
| { "a\\C*?c|a\\C*?b", |
| "cat{lit{a}alt{cat{nstar{byte{}}lit{c}}cat{nstar{byte{}}lit{b}}}}" }, |
| { "^/a/bc|^/a/de", |
| "cat{bol{}cat{str{/a/}alt{str{bc}str{de}}}}" }, |
| // In the past, factoring was limited to kFactorAlternationMaxDepth (8). |
| { "a|aa|aaa|aaaa|aaaaa|aaaaaa|aaaaaaa|aaaaaaaa|aaaaaaaaa|aaaaaaaaaa", |
| "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" |
| "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" |
| "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" "cat{lit{a}alt{emp{}" |
| "lit{a}}}}}}}}}}}}}}}}}}}" }, |
| { "a|aardvark|aardvarks|abaci|aback|abacus|abacuses|abaft|abalone|abalones", |
| "cat{lit{a}alt{emp{}cat{str{ardvark}alt{emp{}lit{s}}}" |
| "cat{str{ba}alt{cat{lit{c}alt{cc{0x69 0x6b}cat{str{us}alt{emp{}str{es}}}}}" |
| "str{ft}cat{str{lone}alt{emp{}lit{s}}}}}}}" }, |
| }; |
| |
| // Test that prefix factoring works. |
| TEST(TestParse, Prefix) { |
| TestParse(prefix_tests, arraysize(prefix_tests), Regexp::PerlX, "prefix"); |
| } |
| |
| Test nested_tests[] = { |
| { "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))", |
| "cap{cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 lit{x}}}}}}}}}}}}}}}}}}}}" }, |
| { "((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})", |
| "cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{1,1 lit{x}}}}}}}}}}}}}}}}}}}}}" }, |
| { "((((((((((x{0}){2}){2}){2}){2}){2}){2}){2}){2}){2})", |
| "cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 cap{rep{0,0 lit{x}}}}}}}}}}}}}}}}}}}}}" }, |
| { "((((((x{2}){2}){2}){5}){5}){5})", |
| "cap{rep{5,5 cap{rep{5,5 cap{rep{5,5 cap{rep{2,2 cap{rep{2,2 cap{rep{2,2 lit{x}}}}}}}}}}}}}" }, |
| }; |
| |
| // Test that nested repetition works. |
| TEST(TestParse, Nested) { |
| TestParse(nested_tests, arraysize(nested_tests), Regexp::PerlX, "nested"); |
| } |
| |
| // Invalid regular expressions |
| const char* badtests[] = { |
| "(", |
| ")", |
| "(a", |
| "(a|b|", |
| "(a|b", |
| "[a-z", |
| "([a-z)", |
| "x{1001}", |
| "\xff", // Invalid UTF-8 |
| "[\xff]", |
| "[\\\xff]", |
| "\\\xff", |
| "(?P<name>a", |
| "(?P<name>", |
| "(?P<name", |
| "(?P<x y>a)", |
| "(?P<>a)", |
| "[a-Z]", |
| "(?i)[a-Z]", |
| "a{100000}", |
| "a{100000,}", |
| "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", |
| "(((x{7}){11}){13})", |
| "\\Q\\E*", |
| }; |
| |
| // Valid in Perl, bad in POSIX |
| const char* only_perl[] = { |
| "[a-b-c]", |
| "\\Qabc\\E", |
| "\\Q*+?{[\\E", |
| "\\Q\\\\E", |
| "\\Q\\\\\\E", |
| "\\Q\\\\\\\\E", |
| "\\Q\\\\\\\\\\E", |
| "(?:a)", |
| "(?P<name>a)", |
| }; |
| |
| // Valid in POSIX, bad in Perl. |
| const char* only_posix[] = { |
| "a++", |
| "a**", |
| "a?*", |
| "a+*", |
| "a{1}*", |
| }; |
| |
| // Test that parser rejects bad regexps. |
| TEST(TestParse, InvalidRegexps) { |
| for (int i = 0; i < arraysize(badtests); i++) { |
| ASSERT_TRUE(Regexp::Parse(badtests[i], Regexp::PerlX, NULL) == NULL) |
| << " " << badtests[i]; |
| ASSERT_TRUE(Regexp::Parse(badtests[i], Regexp::NoParseFlags, NULL) == NULL) |
| << " " << badtests[i]; |
| } |
| for (int i = 0; i < arraysize(only_posix); i++) { |
| ASSERT_TRUE(Regexp::Parse(only_posix[i], Regexp::PerlX, NULL) == NULL) |
| << " " << only_posix[i]; |
| Regexp* re = Regexp::Parse(only_posix[i], Regexp::NoParseFlags, NULL); |
| ASSERT_TRUE(re != NULL) << " " << only_posix[i]; |
| re->Decref(); |
| } |
| for (int i = 0; i < arraysize(only_perl); i++) { |
| ASSERT_TRUE(Regexp::Parse(only_perl[i], Regexp::NoParseFlags, NULL) == NULL) |
| << " " << only_perl[i]; |
| Regexp* re = Regexp::Parse(only_perl[i], Regexp::PerlX, NULL); |
| ASSERT_TRUE(re != NULL) << " " << only_perl[i]; |
| re->Decref(); |
| } |
| } |
| |
| // Test that ToString produces original regexp or equivalent one. |
| TEST(TestToString, EquivalentParse) { |
| for (int i = 0; i < arraysize(tests); i++) { |
| RegexpStatus status; |
| Regexp::ParseFlags f = kTestFlags; |
| if (tests[i].flags != 0) { |
| f = tests[i].flags & ~TestZeroFlags; |
| } |
| Regexp* re = Regexp::Parse(tests[i].regexp, f, &status); |
| ASSERT_TRUE(re != NULL) << " " << tests[i].regexp << " " << status.Text(); |
| string s = re->Dump(); |
| EXPECT_EQ(string(tests[i].parse), s) << " " << tests[i].regexp << " " << string(tests[i].parse) << " " << s; |
| string t = re->ToString(); |
| if (t != tests[i].regexp) { |
| // If ToString didn't return the original regexp, |
| // it must have found one with fewer parens. |
| // Unfortunately we can't check the length here, because |
| // ToString produces "\\{" for a literal brace, |
| // but "{" is a shorter equivalent. |
| // ASSERT_LT(t.size(), strlen(tests[i].regexp)) |
| // << " t=" << t << " regexp=" << tests[i].regexp; |
| |
| // Test that if we parse the new regexp we get the same structure. |
| Regexp* nre = Regexp::Parse(t, Regexp::MatchNL | Regexp::PerlX, &status); |
| ASSERT_TRUE(nre != NULL) << " reparse " << t << " " << status.Text(); |
| string ss = nre->Dump(); |
| string tt = nre->ToString(); |
| if (s != ss || t != tt) |
| LOG(INFO) << "ToString(" << tests[i].regexp << ") = " << t; |
| EXPECT_EQ(s, ss); |
| EXPECT_EQ(t, tt); |
| nre->Decref(); |
| } |
| re->Decref(); |
| } |
| } |
| |
| // Test that capture error args are correct. |
| TEST(NamedCaptures, ErrorArgs) { |
| RegexpStatus status; |
| Regexp* re; |
| |
| re = Regexp::Parse("test(?P<name", Regexp::LikePerl, &status); |
| EXPECT_TRUE(re == NULL); |
| EXPECT_EQ(status.code(), kRegexpBadNamedCapture); |
| EXPECT_EQ(status.error_arg(), "(?P<name"); |
| |
| re = Regexp::Parse("test(?P<space bar>z)", Regexp::LikePerl, &status); |
| EXPECT_TRUE(re == NULL); |
| EXPECT_EQ(status.code(), kRegexpBadNamedCapture); |
| EXPECT_EQ(status.error_arg(), "(?P<space bar>"); |
| } |
| |
| } // namespace re2 |