]> git.saurik.com Git - wxWidgets.git/blame - tests/regex/regextest.cpp
include wx/arrstr.h as it's needed by wxImageHandler and may not be implicitly includ...
[wxWidgets.git] / tests / regex / regextest.cpp
CommitLineData
e70833fb
VS
1///////////////////////////////////////////////////////////////////////////////
2// Name: tests/regex/regex.cpp
3// Purpose: Test the built-in regex lib and wxRegEx
4// Author: Mike Wetherell
5// RCS-ID: $Id$
6// Copyright: (c) 2004 Mike Wetherell
7// Licence: wxWidgets licence
8///////////////////////////////////////////////////////////////////////////////
9
10//
11// Notes:
12//
13// To run just one section, say wx_1, do this:
14// test regex.wx_1
15//
16// To run all the regex tests:
17// test regex
3e5f6c1c 18//
e70833fb
VS
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
3e5f6c1c 23//
e70833fb 24// The tests here are for the builtin library, tests for wxRegEx in general
bc10103e 25// should go in wxregex.cpp
e70833fb
VS
26//
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'.
3e5f6c1c 30//
e70833fb 31
e70833fb 32// For compilers that support precompilation, includes "wx/wx.h".
8899b155 33#include "testprec.h"
e70833fb
VS
34
35#ifdef __BORLANDC__
36 #pragma hdrstop
37#endif
38
39// for all others, include the necessary headers
40#ifndef WX_PRECOMP
41 #include "wx/wx.h"
42#endif
43
56863b16
RN
44
45// many of the tests are specific to the builtin regex lib, so only attempts
46// to do them when using the builtin regex lib.
47//
48#ifdef wxHAS_REGEX_ADVANCED
49
e70833fb 50#include "wx/regex.h"
3e5f6c1c
WS
51#include <string>
52#include <vector>
e70833fb 53
3e5f6c1c
WS
54using CppUnit::Test;
55using CppUnit::TestCase;
56using CppUnit::TestSuite;
57using CppUnit::Exception;
58
0868a030
RN
59using std::string;
60using std::vector;
e70833fb
VS
61
62///////////////////////////////////////////////////////////////////////////////
63// The test case - an instance represents a single test
64
65class RegExTestCase : public TestCase
66{
67public:
68 // constructor - create a single testcase
69 RegExTestCase(
70 const string& name,
71 const char *mode,
72 const char *id,
73 const char *flags,
74 const char *pattern,
75 const char *data,
76 const vector<const char *>& expected);
77
78protected:
79 // run this testcase
80 void runTest();
81
82private:
83 // workers
84 wxString Conv(const char *str);
85 void parseFlags(const wxString& flags);
86 void doTest(int flavor);
e70833fb
VS
87 static wxString quote(const wxString& arg);
88 const wxChar *convError() const { return _T("<cannot convert>"); }
89
90 // assertions - adds some information about the test that failed
91 void fail(const wxString& msg) const;
92 void failIf(bool condition, const wxString& msg) const
93 { if (condition) fail(msg); }
94
95 // mode, id, flags, pattern, test data, expected results...
96 int m_mode;
97 wxString m_id;
98 wxString m_flags;
99 wxString m_pattern;
100 wxString m_data;
101 wxArrayString m_expected;
102
103 // the flag decoded
104 int m_compileFlags;
105 int m_matchFlags;
106 bool m_basic;
107 bool m_extended;
108 bool m_advanced;
109};
110
111// constructor - throws Exception on failure
112//
113RegExTestCase::RegExTestCase(
114 const string& name,
115 const char *mode,
116 const char *id,
117 const char *flags,
118 const char *pattern,
119 const char *data,
120 const vector<const char *>& expected)
121 :
122 TestCase(name),
123 m_mode(mode[0]),
124 m_id(Conv(id)),
125 m_flags(Conv(flags)),
126 m_pattern(Conv(pattern)),
127 m_data(Conv(data)),
128 m_compileFlags(0),
129 m_matchFlags(0),
130 m_basic(false),
131 m_extended(false),
132 m_advanced(false)
133{
134 bool badconv = m_pattern == convError() || m_data == convError();
8899b155 135 //RN: Removing the std:: here will break MSVC6 compilation
0868a030 136 std::vector<const char *>::const_iterator it;
e70833fb
VS
137
138 for (it = expected.begin(); it != expected.end(); ++it) {
139 m_expected.push_back(Conv(*it));
140 badconv = badconv || *m_expected.rbegin() == convError();
141 }
142
143 failIf(badconv, _T("cannot convert to default character encoding"));
3e5f6c1c 144
e70833fb
VS
145 // the flags need further parsing...
146 parseFlags(m_flags);
147
148#ifndef wxHAS_REGEX_ADVANCED
149 failIf(!m_basic && !m_extended, _T("advanced regexs not available"));
150#endif
151}
152
30261041
RN
153int wxWcscmp(const wchar_t* s1, const wchar_t* s2)
154{
155 size_t nLen1 = wxWcslen(s1);
156 size_t nLen2 = wxWcslen(s2);
3e5f6c1c 157
30261041
RN
158 if (nLen1 != nLen2)
159 return nLen1 - nLen2;
3e5f6c1c 160
3d68e52c 161 return memcmp(s1, s2, nLen1*sizeof(wchar_t));
30261041
RN
162}
163
e70833fb
VS
164// convert a string from UTF8 to the internal encoding
165//
166wxString RegExTestCase::Conv(const char *str)
167{
168 const wxWCharBuffer wstr = wxConvUTF8.cMB2WC(str);
169 const wxWC2WXbuf buf = wxConvCurrent->cWC2WX(wstr);
170
30261041 171 if (!buf || wxWcscmp(wxConvCurrent->cWX2WC(buf), wstr) != 0)
e70833fb 172 return convError();
895cae46
VZ
173
174 return buf;
e70833fb
VS
175}
176
177// Parse flags
178//
179void RegExTestCase::parseFlags(const wxString& flags)
180{
86501081
VS
181 for ( wxString::const_iterator p = flags.begin(); p != flags.end(); ++p )
182 {
183 switch ( (*p).GetValue() ) {
e70833fb
VS
184 // noop
185 case '-': break;
186
187 // we don't fully support these flags, but they don't stop us
188 // checking for success of failure of the match, so treat as noop
189 case 'A': case 'B': case 'E': case 'H':
190 case 'I': case 'L': case 'M': case 'N':
191 case 'P': case 'Q': case 'R': case 'S':
192 case 'T': case 'U': case '%':
193 break;
194
195 // match options
196 case '^': m_matchFlags |= wxRE_NOTBOL; break;
197 case '$': m_matchFlags |= wxRE_NOTEOL; break;
198#if wxUSE_UNICODE
199 case '*': break;
200#endif
201 // compile options
202 case '&': m_advanced = m_basic = true; break;
203 case 'b': m_basic = true; break;
204 case 'e': m_extended = true; break;
205 case 'i': m_compileFlags |= wxRE_ICASE; break;
206 case 'o': m_compileFlags |= wxRE_NOSUB; break;
207 case 'n': m_compileFlags |= wxRE_NEWLINE; break;
208 case 't': if (strchr("ep", m_mode)) break; // else fall through...
209
210 // anything else we must skip the test
211 default:
212 fail(wxString::Format(
213 _T("requires unsupported flag '%c'"), *p));
214 }
215 }
216}
217
218// Try test for all flavours of expression specified
219//
220void RegExTestCase::runTest()
221{
222 if (m_basic)
223 doTest(wxRE_BASIC);
224 if (m_extended)
225 doTest(wxRE_EXTENDED);
226#ifdef wxHAS_REGEX_ADVANCED
227 if (m_advanced || (!m_basic && !m_extended))
228 doTest(wxRE_ADVANCED);
229#endif
230}
3e5f6c1c 231
e70833fb
VS
232// Try the test for a single flavour of expression
233//
234void RegExTestCase::doTest(int flavor)
235{
236 wxRegEx re(m_pattern, m_compileFlags | flavor);
237
238 // 'e' - test that the pattern fails to compile
bc10103e
VS
239 if (m_mode == 'e') {
240 failIf(re.IsValid(), _T("compile succeeded (should fail)"));
238fb020
VS
241 return;
242 }
e70833fb
VS
243 failIf(!re.IsValid(), _T("compile failed"));
244
245 bool matches = re.Matches(m_data, m_matchFlags);
246
247 // 'f' or 'p' - test that the pattern does not match
bc10103e
VS
248 if (m_mode == 'f' || m_mode == 'p') {
249 failIf(matches, _T("match succeeded (should fail)"));
238fb020
VS
250 return;
251 }
e70833fb
VS
252
253 // otherwise 'm' or 'i' - test the pattern does match
254 failIf(!matches, _T("match failed"));
255
bc10103e
VS
256 if (m_compileFlags & wxRE_NOSUB)
257 return;
258
259 // check wxRegEx has correctly counted the number of subexpressions
0519aac9
MW
260 wxString msg;
261 msg << _T("GetMatchCount() == ") << re.GetMatchCount()
262 << _T(", expected ") << m_expected.size();
263 failIf(m_expected.size() != re.GetMatchCount(), msg);
e70833fb
VS
264
265 for (size_t i = 0; i < m_expected.size(); i++) {
0519aac9
MW
266 wxString result;
267 size_t start, len;
268
269 msg.clear();
270 msg << _T("wxRegEx::GetMatch failed for match ") << i;
271 failIf(!re.GetMatch(&start, &len, i), msg);
e70833fb
VS
272
273 // m - check the match returns the strings given
274 if (m_mode == 'm')
fe26031e 275 {
e70833fb
VS
276 if (start < INT_MAX)
277 result = m_data.substr(start, len);
278 else
279 result = _T("");
fe26031e 280 }
e70833fb
VS
281
282 // i - check the match returns the offsets given
283 else if (m_mode == 'i')
fe26031e 284 {
0519aac9 285 if (start > INT_MAX)
e70833fb 286 result = _T("-1 -1");
0519aac9
MW
287 else if (start + len > 0)
288 result << start << _T(" ") << start + len - 1;
289 else
290 result << start << _T(" -1");
fe26031e 291 }
e70833fb 292
0519aac9
MW
293 msg.clear();
294 msg << _T("match(") << i << _T(") == ") << quote(result)
295 << _T(", expected == ") << quote(m_expected[i]);
296 failIf(result != m_expected[i], msg);
e70833fb
VS
297 }
298}
299
300// assertion - adds some information about the test that failed
301//
302void RegExTestCase::fail(const wxString& msg) const
303{
304 wxString str;
305 wxArrayString::const_iterator it;
306
307 str << (wxChar)m_mode << _T(" ") << m_id << _T(" ") << m_flags << _T(" ")
308 << quote(m_pattern) << _T(" ") << quote(m_data);
309
310 for (it = m_expected.begin(); it != m_expected.end(); ++it)
311 str << _T(" ") << quote(*it);
3e5f6c1c 312
e70833fb
VS
313 if (str.length() > 77)
314 str = str.substr(0, 74) + _T("...");
315
316 str << _T("\n ") << msg;
317
318 // no lossy convs so using utf8
319 CPPUNIT_FAIL(string(str.mb_str(wxConvUTF8)));
320}
321
322// quote a string so that it can be displayed (static)
323//
324wxString RegExTestCase::quote(const wxString& arg)
325{
326 const wxChar *needEscape = _T("\a\b\t\n\v\f\r\"\\");
327 const wxChar *escapes = _T("abtnvfr\"\\");
328 wxString str;
329
330 for (size_t i = 0; i < arg.length(); i++) {
3d68e52c 331 wxChar ch = (wxChar)arg[i];
e70833fb 332 const wxChar *p = wxStrchr(needEscape, ch);
3e5f6c1c 333
e70833fb
VS
334 if (p)
335 str += wxString::Format(_T("\\%c"), escapes[p - needEscape]);
336 else if (wxIscntrl(ch))
337 str += wxString::Format(_T("\\%03o"), ch);
338 else
fa3b08ca 339 str += (wxChar)ch;
e70833fb
VS
340 }
341
342 return str.length() == arg.length() && str.find(' ') == wxString::npos ?
343 str : _T("\"") + str + _T("\"");
344}
345
e70833fb
VS
346
347///////////////////////////////////////////////////////////////////////////////
348// Test suite
e70833fb
VS
349
350class RegExTestSuite : public TestSuite
351{
352public:
0d95d20c 353 RegExTestSuite(string name) : TestSuite(name) { }
e70833fb
VS
354 void add(const char *mode, const char *id, const char *flags,
355 const char *pattern, const char *data, const char *expected, ...);
356};
357
e70833fb
VS
358// Add a testcase to the suite
359//
360void RegExTestSuite::add(
361 const char *mode,
362 const char *id,
363 const char *flags,
364 const char *pattern,
365 const char *data,
366 const char *expected, ...)
367{
368 string name = getName() + "." + id;
369
370 vector<const char *> expected_results;
371 va_list ap;
372
373 for (va_start(ap, expected); expected; expected = va_arg(ap, const char *))
374 expected_results.push_back(expected);
375
376 va_end(ap);
3e5f6c1c 377
e70833fb
VS
378 try {
379 addTest(new RegExTestCase(
380 name, mode, id, flags, pattern, data, expected_results));
381 }
382 catch (Exception& e) {
383 wxLogInfo(wxString::Format(_T("skipping: %s\n %s\n"),
384 wxString(name.c_str(), wxConvUTF8).c_str(),
385 wxString(e.what(), wxConvUTF8).c_str()));
386 }
387}
388
389
390// Include the generated tests
391//
392#include "regex.inc"
393
394
395#endif // wxHAS_REGEX_ADVANCED