]>
git.saurik.com Git - wxWidgets.git/blob - tests/regex/regex.cpp
733e5aed69e1bd6c46b590cc493e7acb4084a768
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/regex/regex.cpp
3 // Purpose: Test the built-in regex lib and wxRegEx
4 // Author: Mike Wetherell
6 // Copyright: (c) 2004 Mike Wetherell
7 // Licence: wxWidgets licence
8 ///////////////////////////////////////////////////////////////////////////////
13 // To run just one section, say wx_1, do this:
16 // To run all the regex tests:
19 // Some tests must be skipped since they use features which we do not make
20 // available through wxRegEx. To see the list of tests that have been skipped
21 // turn on verbose logging, e.g.:
22 // test --verbose regex
24 // The tests here are for the builtin library, tests for wxRegEx in general
25 // should go in another module.
27 // The tests are generated from Henry Spencer's reg.test, additional test
28 // can be added in wxreg.test. These test files are then turned into a C++
29 // include file 'regex.inc' (included below) using a script 'regex.pl'.
32 #if defined(__GNUG__) && !defined(__APPLE__)
33 #pragma implementation
37 // For compilers that support precompilation, includes "wx/wx.h".
38 #include "wx/wxprec.h"
44 // for all others, include the necessary headers
50 #include "wx/cppunit.h"
55 using namespace CppUnit
;
57 // many of the tests are specific to the builtin regex lib, so only attempts
58 // to do them when using the builtin regex lib.
60 #ifdef wxHAS_REGEX_ADVANCED
63 ///////////////////////////////////////////////////////////////////////////////
64 // The test case - an instance represents a single test
66 class RegExTestCase
: public TestCase
69 // constructor - create a single testcase
77 const vector
<const char *>& expected
);
85 wxString
Conv(const char *str
);
86 void parseFlags(const wxString
& flags
);
87 void doTest(int flavor
);
88 static size_t matchCount(const wxString
& expr
, int flags
);
89 static wxString
quote(const wxString
& arg
);
90 const wxChar
*convError() const { return _T("<cannot convert>"); }
92 // assertions - adds some information about the test that failed
93 void fail(const wxString
& msg
) const;
94 void failIf(bool condition
, const wxString
& msg
) const
95 { if (condition
) fail(msg
); }
97 // mode, id, flags, pattern, test data, expected results...
103 wxArrayString m_expected
;
113 // constructor - throws Exception on failure
115 RegExTestCase::RegExTestCase(
122 const vector
<const char *>& expected
)
127 m_flags(Conv(flags
)),
128 m_pattern(Conv(pattern
)),
136 bool badconv
= m_pattern
== convError() || m_data
== convError();
137 vector
<const char *>::const_iterator it
;
139 for (it
= expected
.begin(); it
!= expected
.end(); ++it
) {
140 m_expected
.push_back(Conv(*it
));
141 badconv
= badconv
|| *m_expected
.rbegin() == convError();
144 failIf(badconv
, _T("cannot convert to default character encoding"));
146 // the flags need further parsing...
149 #ifndef wxHAS_REGEX_ADVANCED
150 failIf(!m_basic
&& !m_extended
, _T("advanced regexs not available"));
154 // convert a string from UTF8 to the internal encoding
156 wxString
RegExTestCase::Conv(const char *str
)
158 const wxWCharBuffer wstr
= wxConvUTF8
.cMB2WC(str
);
159 const wxWC2WXbuf buf
= wxConvCurrent
->cWC2WX(wstr
);
161 if (!buf
|| wcscmp(wxConvCurrent
->cWX2WC(buf
), wstr
) != 0)
169 void RegExTestCase::parseFlags(const wxString
& flags
)
171 for (const wxChar
*p
= flags
; *p
; p
++) {
176 // we don't fully support these flags, but they don't stop us
177 // checking for success of failure of the match, so treat as noop
178 case 'A': case 'B': case 'E': case 'H':
179 case 'I': case 'L': case 'M': case 'N':
180 case 'P': case 'Q': case 'R': case 'S':
181 case 'T': case 'U': case '%':
185 case '^': m_matchFlags
|= wxRE_NOTBOL
; break;
186 case '$': m_matchFlags
|= wxRE_NOTEOL
; break;
191 case '&': m_advanced
= m_basic
= true; break;
192 case 'b': m_basic
= true; break;
193 case 'e': m_extended
= true; break;
194 case 'i': m_compileFlags
|= wxRE_ICASE
; break;
195 case 'o': m_compileFlags
|= wxRE_NOSUB
; break;
196 case 'n': m_compileFlags
|= wxRE_NEWLINE
; break;
197 case 't': if (strchr("ep", m_mode
)) break; // else fall through...
199 // anything else we must skip the test
201 fail(wxString::Format(
202 _T("requires unsupported flag '%c'"), *p
));
207 // Try test for all flavours of expression specified
209 void RegExTestCase::runTest()
214 doTest(wxRE_EXTENDED
);
215 #ifdef wxHAS_REGEX_ADVANCED
216 if (m_advanced
|| (!m_basic
&& !m_extended
))
217 doTest(wxRE_ADVANCED
);
221 // Try the test for a single flavour of expression
223 void RegExTestCase::doTest(int flavor
)
225 wxRegEx
re(m_pattern
, m_compileFlags
| flavor
);
227 // 'e' - test that the pattern fails to compile
229 return failIf(re
.IsValid(), _T("compile suceeded (should fail)"));
230 failIf(!re
.IsValid(), _T("compile failed"));
232 bool matches
= re
.Matches(m_data
, m_matchFlags
);
234 // 'f' or 'p' - test that the pattern does not match
235 if (m_mode
== 'f' || m_mode
== 'p')
236 return failIf(matches
, _T("match suceeded (should fail)"));
238 // otherwise 'm' or 'i' - test the pattern does match
239 failIf(!matches
, _T("match failed"));
241 // Check that wxRegEx is going to allocate a large enough array for the
242 // results we are supposed to get
243 failIf(m_expected
.size() > matchCount(m_pattern
, m_compileFlags
| flavor
),
244 _T("wxRegEx has not allocated a large enough array for the ")
245 _T("number of results expected"));
250 for (size_t i
= 0; i
< m_expected
.size(); i
++) {
251 failIf(!re
.GetMatch(&start
, &len
, i
), wxString::Format(
252 _T("wxRegEx::GetMatch failed for match %d"), i
));
254 // m - check the match returns the strings given
257 result
= m_data
.substr(start
, len
);
261 // i - check the match returns the offsets given
262 else if (m_mode
== 'i')
264 result
= wxString::Format(_T("%d %d"), start
, start
+ len
- 1);
266 result
= _T("-1 -1");
268 failIf(result
!= m_expected
[i
], wxString::Format(
269 _T("match(%d) == %s, expected == %s"), i
,
270 quote(result
).c_str(), quote(m_expected
[i
]).c_str()));
274 // assertion - adds some information about the test that failed
276 void RegExTestCase::fail(const wxString
& msg
) const
279 wxArrayString::const_iterator it
;
281 str
<< (wxChar
)m_mode
<< _T(" ") << m_id
<< _T(" ") << m_flags
<< _T(" ")
282 << quote(m_pattern
) << _T(" ") << quote(m_data
);
284 for (it
= m_expected
.begin(); it
!= m_expected
.end(); ++it
)
285 str
<< _T(" ") << quote(*it
);
287 if (str
.length() > 77)
288 str
= str
.substr(0, 74) + _T("...");
290 str
<< _T("\n ") << msg
;
292 // no lossy convs so using utf8
293 CPPUNIT_FAIL(string(str
.mb_str(wxConvUTF8
)));
296 // quote a string so that it can be displayed (static)
298 wxString
RegExTestCase::quote(const wxString
& arg
)
300 const wxChar
*needEscape
= _T("\a\b\t\n\v\f\r\"\\");
301 const wxChar
*escapes
= _T("abtnvfr\"\\");
304 for (size_t i
= 0; i
< arg
.length(); i
++) {
306 const wxChar
*p
= wxStrchr(needEscape
, ch
);
309 str
+= wxString::Format(_T("\\%c"), escapes
[p
- needEscape
]);
310 else if (wxIscntrl(ch
))
311 str
+= wxString::Format(_T("\\%03o"), ch
);
316 return str
.length() == arg
.length() && str
.find(' ') == wxString::npos
?
317 str
: _T("\"") + str
+ _T("\"");
320 // Count the number of subexpressions (taken from wxRegExImpl::Compile)
322 size_t RegExTestCase::matchCount(const wxString
& expr
, int flags
)
324 // there is always one for the whole expression
327 // and some more for bracketed subexperessions
328 for ( const wxChar
*cptr
= expr
; *cptr
; cptr
++ )
330 if ( *cptr
== _T('\\') )
332 // in basic RE syntax groups are inside \(...\)
333 if ( *++cptr
== _T('(') && (flags
& wxRE_BASIC
) )
338 else if ( *cptr
== _T('(') && !(flags
& wxRE_BASIC
) )
340 // we know that the previous character is not an unquoted
341 // backslash because it would have been eaten above, so we
342 // have a bar '(' and this indicates a group start for the
352 ///////////////////////////////////////////////////////////////////////////////
355 // In a non-unicode build the regex is affected by the current locale, so
356 // this derived TestSuite is used. It sets the locale in it's run() method
357 // for the duration of the regex tests.
359 class RegExTestSuite
: public TestSuite
362 RegExTestSuite(string name
);
363 void run(TestResult
*result
);
364 void add(const char *mode
, const char *id
, const char *flags
,
365 const char *pattern
, const char *data
, const char *expected
, ...);
368 // constructor, sets the locale so that it is set when the tests are added
370 RegExTestSuite::RegExTestSuite(string name
) : TestSuite(name
)
372 setlocale(LC_ALL
, "");
375 // run the test suite, sets the locale again since it may have been changed
376 // by another test since this suite was crated
378 void RegExTestSuite::run(TestResult
*result
)
380 setlocale(LC_ALL
, "");
381 TestSuite::run(result
);
384 // Add a testcase to the suite
386 void RegExTestSuite::add(
392 const char *expected
, ...)
394 string name
= getName() + "." + id
;
396 vector
<const char *> expected_results
;
399 for (va_start(ap
, expected
); expected
; expected
= va_arg(ap
, const char *))
400 expected_results
.push_back(expected
);
405 addTest(new RegExTestCase(
406 name
, mode
, id
, flags
, pattern
, data
, expected_results
));
408 catch (Exception
& e
) {
409 wxLogInfo(wxString::Format(_T("skipping: %s\n %s\n"),
410 wxString(name
.c_str(), wxConvUTF8
).c_str(),
411 wxString(e
.what(), wxConvUTF8
).c_str()));
416 // Include the generated tests
421 #endif // wxHAS_REGEX_ADVANCED