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