| 1 | /////////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: tests/regex/wxregex.cpp |
| 3 | // Purpose: Test wxRegEx |
| 4 | // Author: Vadim Zeitlin, Mike Wetherell |
| 5 | // RCS-ID: $Id$ |
| 6 | // Copyright: Vadim Zeitlin, Mike Wetherell |
| 7 | // Licence: wxWidgets licence |
| 8 | /////////////////////////////////////////////////////////////////////////////// |
| 9 | |
| 10 | #include "testprec.h" |
| 11 | |
| 12 | #ifdef __BORLANDC__ |
| 13 | # pragma hdrstop |
| 14 | #endif |
| 15 | |
| 16 | #ifndef WX_PRECOMP |
| 17 | # include "wx/wx.h" |
| 18 | #endif |
| 19 | |
| 20 | #if wxUSE_REGEX |
| 21 | |
| 22 | #include "wx/regex.h" |
| 23 | #include "wx/tokenzr.h" |
| 24 | #include <string> |
| 25 | |
| 26 | using CppUnit::Test; |
| 27 | using CppUnit::TestCase; |
| 28 | using CppUnit::TestSuite; |
| 29 | using std::string; |
| 30 | |
| 31 | |
| 32 | /////////////////////////////////////////////////////////////////////////////// |
| 33 | // Compile Test |
| 34 | |
| 35 | class RegExCompileTestCase : public TestCase |
| 36 | { |
| 37 | public: |
| 38 | RegExCompileTestCase(const char *name, const wxString& pattern, |
| 39 | bool correct, int flags) |
| 40 | : TestCase(name), |
| 41 | m_pattern(pattern), |
| 42 | m_correct(correct), |
| 43 | m_flags(flags) |
| 44 | { } |
| 45 | |
| 46 | protected: |
| 47 | void runTest(); |
| 48 | |
| 49 | private: |
| 50 | wxString m_pattern; |
| 51 | bool m_correct; |
| 52 | int m_flags; |
| 53 | }; |
| 54 | |
| 55 | void RegExCompileTestCase::runTest() |
| 56 | { |
| 57 | wxRegEx re; |
| 58 | bool ok = re.Compile(m_pattern, m_flags); |
| 59 | |
| 60 | if (m_correct) |
| 61 | CPPUNIT_ASSERT_MESSAGE("compile failed", ok); |
| 62 | else |
| 63 | CPPUNIT_ASSERT_MESSAGE("compile succeeded (should fail)", !ok); |
| 64 | } |
| 65 | |
| 66 | |
| 67 | /////////////////////////////////////////////////////////////////////////////// |
| 68 | // Match Test |
| 69 | |
| 70 | class RegExMatchTestCase : public TestCase |
| 71 | { |
| 72 | public: |
| 73 | RegExMatchTestCase(const char *name, const wxString& pattern, |
| 74 | const wxString& text, const char *expected, |
| 75 | int flags) |
| 76 | : TestCase(name), |
| 77 | m_pattern(pattern), |
| 78 | m_text(text), |
| 79 | m_expected(expected), |
| 80 | m_flags(flags) |
| 81 | { } |
| 82 | |
| 83 | protected: |
| 84 | void runTest(); |
| 85 | |
| 86 | private: |
| 87 | wxString m_pattern; |
| 88 | wxString m_text; |
| 89 | const char *m_expected; |
| 90 | int m_flags; |
| 91 | }; |
| 92 | |
| 93 | void RegExMatchTestCase::runTest() |
| 94 | { |
| 95 | int compileFlags = m_flags & ~(wxRE_NOTBOL | wxRE_NOTEOL); |
| 96 | int matchFlags = m_flags & (wxRE_NOTBOL | wxRE_NOTEOL); |
| 97 | |
| 98 | wxRegEx re(m_pattern, compileFlags); |
| 99 | CPPUNIT_ASSERT_MESSAGE("compile failed", re.IsValid()); |
| 100 | |
| 101 | bool ok = re.Matches(m_text, matchFlags); |
| 102 | |
| 103 | if (m_expected) { |
| 104 | CPPUNIT_ASSERT_MESSAGE("match failed", ok); |
| 105 | |
| 106 | wxStringTokenizer tkz(wxString(m_expected, *wxConvCurrent), |
| 107 | _T("\t"), wxTOKEN_RET_EMPTY); |
| 108 | size_t i; |
| 109 | |
| 110 | for (i = 0; i < re.GetMatchCount() && tkz.HasMoreTokens(); i++) { |
| 111 | wxString expected = tkz.GetNextToken(); |
| 112 | wxString result = re.GetMatch(m_text, i); |
| 113 | |
| 114 | wxString msgstr; |
| 115 | msgstr.Printf(_T("\\%d == '%s' (expected '%s')"), |
| 116 | (int)i, result.c_str(), expected.c_str()); |
| 117 | |
| 118 | CPPUNIT_ASSERT_MESSAGE((const char*)msgstr.mb_str(), |
| 119 | result == expected); |
| 120 | } |
| 121 | |
| 122 | if ((m_flags & wxRE_NOSUB) == 0) |
| 123 | CPPUNIT_ASSERT(re.GetMatchCount() == i); |
| 124 | } |
| 125 | else { |
| 126 | CPPUNIT_ASSERT_MESSAGE("match succeeded (should fail)", !ok); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | |
| 131 | /////////////////////////////////////////////////////////////////////////////// |
| 132 | // Replacement Test |
| 133 | |
| 134 | class RegExReplaceTestCase : public TestCase |
| 135 | { |
| 136 | public: |
| 137 | RegExReplaceTestCase(const char *name, const wxString& pattern, |
| 138 | const wxString& text, const wxString& repl, |
| 139 | const wxString& expected, size_t count, int flags) |
| 140 | : TestCase(name), |
| 141 | m_pattern(pattern), |
| 142 | m_text(text), |
| 143 | m_repl(repl), |
| 144 | m_expected(expected), |
| 145 | m_count(count), |
| 146 | m_flags(flags) |
| 147 | { } |
| 148 | |
| 149 | protected: |
| 150 | void runTest(); |
| 151 | |
| 152 | private: |
| 153 | wxString m_pattern; |
| 154 | wxString m_text; |
| 155 | wxString m_repl; |
| 156 | wxString m_expected; |
| 157 | size_t m_count; |
| 158 | int m_flags; |
| 159 | }; |
| 160 | |
| 161 | void RegExReplaceTestCase::runTest() |
| 162 | { |
| 163 | wxRegEx re(m_pattern, m_flags); |
| 164 | |
| 165 | wxString text(m_text); |
| 166 | size_t nRepl = re.Replace(&text, m_repl); |
| 167 | |
| 168 | wxString msgstr; |
| 169 | msgstr.Printf(_T("returns '%s' (expected '%s')"), text.c_str(), m_expected.c_str()); |
| 170 | CPPUNIT_ASSERT_MESSAGE((const char*)msgstr.mb_str(), text == m_expected); |
| 171 | |
| 172 | msgstr.Printf(_T("matches %d times (expected %d)"), (int)nRepl, (int)m_count); |
| 173 | CPPUNIT_ASSERT_MESSAGE((const char*)msgstr.mb_str(), nRepl == m_count); |
| 174 | } |
| 175 | |
| 176 | |
| 177 | /////////////////////////////////////////////////////////////////////////////// |
| 178 | // The suite |
| 179 | |
| 180 | class wxRegExTestSuite : public TestSuite |
| 181 | { |
| 182 | public: |
| 183 | wxRegExTestSuite() : TestSuite("wxRegExTestSuite") { } |
| 184 | static Test *suite(); |
| 185 | |
| 186 | private: |
| 187 | void add(const char *pattern, bool correct, int flags = wxRE_DEFAULT); |
| 188 | void add(const char *pattern, const char *text, |
| 189 | const char *expected = NULL, int flags = wxRE_DEFAULT); |
| 190 | void add(const char *pattern, const char *text, const char *replacement, |
| 191 | const char *expected, size_t count, int flags = wxRE_DEFAULT); |
| 192 | |
| 193 | static wxString FlagStr(int flags); |
| 194 | static wxString Conv(const char *str) { return wxString(str, *wxConvCurrent); } |
| 195 | }; |
| 196 | |
| 197 | // Build the suite (static) |
| 198 | // |
| 199 | Test *wxRegExTestSuite::suite() |
| 200 | { |
| 201 | wxRegExTestSuite *suite = new wxRegExTestSuite; |
| 202 | |
| 203 | // Compile tests |
| 204 | // pattern, expected result |
| 205 | suite->add("foo", true); |
| 206 | suite->add("foo(", false); |
| 207 | suite->add("foo(bar", false); |
| 208 | suite->add("foo(bar)", true); |
| 209 | suite->add("foo[", false); |
| 210 | suite->add("foo[bar", false); |
| 211 | suite->add("foo[bar]", true); |
| 212 | suite->add("foo{1", false); |
| 213 | suite->add("foo{1}", true); |
| 214 | suite->add("foo{1,2}", true); |
| 215 | suite->add("foo*", true); |
| 216 | suite->add("foo+", true); |
| 217 | suite->add("foo?", true); |
| 218 | |
| 219 | // Match tests |
| 220 | // pattern, text, expected results (match, followed by submatches |
| 221 | // tab separated, or NULL for no match expected) |
| 222 | suite->add("foo", "bar"); |
| 223 | suite->add("foo", "foobar", "foo"); |
| 224 | suite->add("^foo", "foobar", "foo"); |
| 225 | suite->add("^foo", "barfoo"); |
| 226 | suite->add("bar$", "barbar", "bar"); |
| 227 | suite->add("bar$", "barbar "); |
| 228 | suite->add("OoBa", "FoObAr", "oObA", wxRE_ICASE); |
| 229 | suite->add("^[A-Z].*$", "AA\nbb\nCC", "AA\nbb\nCC"); |
| 230 | suite->add("^[A-Z].*$", "AA\nbb\nCC", "AA", wxRE_NEWLINE); |
| 231 | suite->add("^[a-z].*$", "AA\nbb\nCC", "bb", wxRE_NEWLINE); |
| 232 | suite->add("^[A-Z].*$", "AA\nbb\nCC", "CC", wxRE_NEWLINE | wxRE_NOTBOL); |
| 233 | suite->add("^[A-Z].*$", "AA\nbb\nCC", NULL, wxRE_NEWLINE | wxRE_NOTBOL | wxRE_NOTEOL); |
| 234 | suite->add("([[:alpha:]]+) ([[:alpha:]]+) ([[:digit:]]+).* ([[:digit:]]+)$", |
| 235 | "Fri Jul 13 18:37:52 CEST 2001", |
| 236 | "Fri Jul 13 18:37:52 CEST 2001\tFri\tJul\t13\t2001"); |
| 237 | |
| 238 | // Replace tests |
| 239 | // pattern, text, replacement, expected result and number of matches |
| 240 | const char *patn = "([a-z]+)[^0-9]*([0-9]+)"; |
| 241 | suite->add(patn, "foo123", "bar", "bar", 1); |
| 242 | suite->add(patn, "foo123", "\\2\\1", "123foo", 1); |
| 243 | suite->add(patn, "foo_123", "\\2\\1", "123foo", 1); |
| 244 | suite->add(patn, "123foo", "bar", "123foo", 0); |
| 245 | suite->add(patn, "123foo456foo", "&&", "123foo456foo456foo", 1); |
| 246 | suite->add(patn, "123foo456foo", "\\0\\0", "123foo456foo456foo", 1); |
| 247 | suite->add(patn, "foo123foo123", "bar", "barbar", 2); |
| 248 | suite->add(patn, "foo123_foo456_foo789", "bar", "bar_bar_bar", 3); |
| 249 | |
| 250 | return suite; |
| 251 | } |
| 252 | |
| 253 | // Add a compile test |
| 254 | // |
| 255 | void wxRegExTestSuite::add( |
| 256 | const char *pattern, |
| 257 | bool correct, |
| 258 | int flags /*=wxRE_DEFAULT*/) |
| 259 | { |
| 260 | addTest(new RegExCompileTestCase( |
| 261 | (_T("/") + Conv(pattern) + _T("/") + FlagStr(flags)).mb_str(), |
| 262 | Conv(pattern), correct, flags)); |
| 263 | } |
| 264 | |
| 265 | // Add a match test |
| 266 | // |
| 267 | void wxRegExTestSuite::add( |
| 268 | const char *pattern, |
| 269 | const char *text, |
| 270 | const char *expected /*=NULL*/, |
| 271 | int flags /*=wxRE_DEFAULT*/) |
| 272 | { |
| 273 | wxString name; |
| 274 | |
| 275 | name << _T("'") << Conv(text) << _T("' =~ /") << Conv(pattern) << _T("/") |
| 276 | << FlagStr(flags); |
| 277 | name.Replace(_T("\n"), _T("\\n")); |
| 278 | |
| 279 | addTest(new RegExMatchTestCase(name.mb_str(), Conv(pattern), |
| 280 | Conv(text), expected, flags)); |
| 281 | } |
| 282 | |
| 283 | // Add a replace test |
| 284 | // |
| 285 | void wxRegExTestSuite::add( |
| 286 | const char *pattern, |
| 287 | const char *text, |
| 288 | const char *replacement, |
| 289 | const char *expected, |
| 290 | size_t count, |
| 291 | int flags /*=wxRE_DEFAULT*/) |
| 292 | { |
| 293 | wxString name; |
| 294 | |
| 295 | name << _T("'") << Conv(text) << _T("' =~ s/") << Conv(pattern) << _T("/") |
| 296 | << Conv(replacement) << _T("/g") << FlagStr(flags); |
| 297 | name.Replace(_T("\n"), _T("\\n")); |
| 298 | |
| 299 | addTest(new RegExReplaceTestCase( |
| 300 | name.mb_str(), Conv(pattern), Conv(text), |
| 301 | Conv(replacement), Conv(expected), count, flags)); |
| 302 | } |
| 303 | |
| 304 | // Display string for the flags |
| 305 | // |
| 306 | wxString wxRegExTestSuite::FlagStr(int flags) |
| 307 | { |
| 308 | wxString str; |
| 309 | |
| 310 | if (!flags) |
| 311 | return str; |
| 312 | |
| 313 | for (int i = 0; (unsigned)flags >> i; i++) { |
| 314 | switch (flags & (1 << i)) { |
| 315 | case 0: break; |
| 316 | #ifdef wxHAS_REGEX_ADVANCED |
| 317 | case wxRE_ADVANCED: str += _T(" | wxRE_ADVANCED"); break; |
| 318 | #endif |
| 319 | case wxRE_BASIC: str += _T(" | wxRE_BASIC"); break; |
| 320 | case wxRE_ICASE: str += _T(" | wxRE_ICASE"); break; |
| 321 | case wxRE_NOSUB: str += _T(" | wxRE_NOSUB"); break; |
| 322 | case wxRE_NEWLINE: str += _T(" | wxRE_NEWLINE"); break; |
| 323 | case wxRE_NOTBOL: str += _T(" | wxRE_NOTBOL"); break; |
| 324 | case wxRE_NOTEOL: str += _T(" | wxRE_NOTEOL"); break; |
| 325 | default: wxFAIL; break; |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | return _T(" (") + str.Mid(3) + _T(")"); |
| 330 | } |
| 331 | |
| 332 | // register in the unnamed registry so that these tests are run by default |
| 333 | CPPUNIT_TEST_SUITE_REGISTRATION(wxRegExTestSuite); |
| 334 | |
| 335 | // also include in it's own registry so that these tests can be run alone |
| 336 | CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(wxRegExTestSuite, "wxRegExTestSuite"); |
| 337 | |
| 338 | |
| 339 | #endif // wxUSE_REGEX |