]> git.saurik.com Git - wxWidgets.git/blob - tests/regex/regextest.cpp
don't use -single_module together with -bundle under Darwin (ld gives an error for...
[wxWidgets.git] / tests / regex / regextest.cpp
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
25 // should go in wxregex.cpp
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
32 // For compilers that support precompilation, includes "wx/wx.h".
33 #include "testprec.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
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
50 #include "wx/regex.h"
51 #include <string>
52 #include <vector>
53
54 using CppUnit::Test;
55 using CppUnit::TestCase;
56 using CppUnit::TestSuite;
57 using CppUnit::Exception;
58
59 using std::string;
60 using std::vector;
61
62 ///////////////////////////////////////////////////////////////////////////////
63 // The test case - an instance represents a single test
64
65 class RegExTestCase : public TestCase
66 {
67 public:
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
78 protected:
79 // run this testcase
80 void runTest();
81
82 private:
83 // workers
84 wxString Conv(const char *str);
85 void parseFlags(const wxString& flags);
86 void doTest(int flavor);
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 //
113 RegExTestCase::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();
135 //RN: Removing the std:: here will break MSVC6 compilation
136 std::vector<const char *>::const_iterator it;
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"));
144
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
153 int wxWcscmp(const wchar_t* s1, const wchar_t* s2)
154 {
155 size_t nLen1 = wxWcslen(s1);
156 size_t nLen2 = wxWcslen(s2);
157
158 if (nLen1 != nLen2)
159 return nLen1 - nLen2;
160
161 return wxTmemcmp(s1, s2, nLen1);
162 }
163
164 // convert a string from UTF8 to the internal encoding
165 //
166 wxString RegExTestCase::Conv(const char *str)
167 {
168 const wxWCharBuffer wstr = wxConvUTF8.cMB2WC(str);
169 const wxWC2WXbuf buf = wxConvCurrent->cWC2WX(wstr);
170
171 if (!buf || wxWcscmp(wxConvCurrent->cWX2WC(buf), wstr) != 0)
172 return convError();
173 else
174 return buf;
175 }
176
177 // Parse flags
178 //
179 void RegExTestCase::parseFlags(const wxString& flags)
180 {
181 for (const wxChar *p = flags; *p; p++) {
182 switch (*p) {
183 // noop
184 case '-': break;
185
186 // we don't fully support these flags, but they don't stop us
187 // checking for success of failure of the match, so treat as noop
188 case 'A': case 'B': case 'E': case 'H':
189 case 'I': case 'L': case 'M': case 'N':
190 case 'P': case 'Q': case 'R': case 'S':
191 case 'T': case 'U': case '%':
192 break;
193
194 // match options
195 case '^': m_matchFlags |= wxRE_NOTBOL; break;
196 case '$': m_matchFlags |= wxRE_NOTEOL; break;
197 #if wxUSE_UNICODE
198 case '*': break;
199 #endif
200 // compile options
201 case '&': m_advanced = m_basic = true; break;
202 case 'b': m_basic = true; break;
203 case 'e': m_extended = true; break;
204 case 'i': m_compileFlags |= wxRE_ICASE; break;
205 case 'o': m_compileFlags |= wxRE_NOSUB; break;
206 case 'n': m_compileFlags |= wxRE_NEWLINE; break;
207 case 't': if (strchr("ep", m_mode)) break; // else fall through...
208
209 // anything else we must skip the test
210 default:
211 fail(wxString::Format(
212 _T("requires unsupported flag '%c'"), *p));
213 }
214 }
215 }
216
217 // Try test for all flavours of expression specified
218 //
219 void RegExTestCase::runTest()
220 {
221 if (m_basic)
222 doTest(wxRE_BASIC);
223 if (m_extended)
224 doTest(wxRE_EXTENDED);
225 #ifdef wxHAS_REGEX_ADVANCED
226 if (m_advanced || (!m_basic && !m_extended))
227 doTest(wxRE_ADVANCED);
228 #endif
229 }
230
231 // Try the test for a single flavour of expression
232 //
233 void RegExTestCase::doTest(int flavor)
234 {
235 wxRegEx re(m_pattern, m_compileFlags | flavor);
236
237 // 'e' - test that the pattern fails to compile
238 if (m_mode == 'e') {
239 failIf(re.IsValid(), _T("compile succeeded (should fail)"));
240 return;
241 }
242 failIf(!re.IsValid(), _T("compile failed"));
243
244 bool matches = re.Matches(m_data, m_matchFlags);
245
246 // 'f' or 'p' - test that the pattern does not match
247 if (m_mode == 'f' || m_mode == 'p') {
248 failIf(matches, _T("match succeeded (should fail)"));
249 return;
250 }
251
252 // otherwise 'm' or 'i' - test the pattern does match
253 failIf(!matches, _T("match failed"));
254
255 if (m_compileFlags & wxRE_NOSUB)
256 return;
257
258 // check wxRegEx has correctly counted the number of subexpressions
259 failIf(m_expected.size() != re.GetMatchCount(),
260 wxString::Format(_T("GetMatchCount() == %d, expected %d"),
261 re.GetMatchCount(), m_expected.size()));
262
263 wxString result;
264 size_t start, len;
265
266 for (size_t i = 0; i < m_expected.size(); i++) {
267 failIf(!re.GetMatch(&start, &len, i), wxString::Format(
268 _T("wxRegEx::GetMatch failed for match %d"), i));
269
270 // m - check the match returns the strings given
271 if (m_mode == 'm')
272 if (start < INT_MAX)
273 result = m_data.substr(start, len);
274 else
275 result = _T("");
276
277 // i - check the match returns the offsets given
278 else if (m_mode == 'i')
279 if (start < INT_MAX)
280 result = wxString::Format(_T("%d %d"), start, start + len - 1);
281 else
282 result = _T("-1 -1");
283
284 failIf(result != m_expected[i], wxString::Format(
285 _T("match(%d) == %s, expected == %s"), i,
286 quote(result).c_str(), quote(m_expected[i]).c_str()));
287 }
288 }
289
290 // assertion - adds some information about the test that failed
291 //
292 void RegExTestCase::fail(const wxString& msg) const
293 {
294 wxString str;
295 wxArrayString::const_iterator it;
296
297 str << (wxChar)m_mode << _T(" ") << m_id << _T(" ") << m_flags << _T(" ")
298 << quote(m_pattern) << _T(" ") << quote(m_data);
299
300 for (it = m_expected.begin(); it != m_expected.end(); ++it)
301 str << _T(" ") << quote(*it);
302
303 if (str.length() > 77)
304 str = str.substr(0, 74) + _T("...");
305
306 str << _T("\n ") << msg;
307
308 // no lossy convs so using utf8
309 CPPUNIT_FAIL(string(str.mb_str(wxConvUTF8)));
310 }
311
312 // quote a string so that it can be displayed (static)
313 //
314 wxString RegExTestCase::quote(const wxString& arg)
315 {
316 const wxChar *needEscape = _T("\a\b\t\n\v\f\r\"\\");
317 const wxChar *escapes = _T("abtnvfr\"\\");
318 wxString str;
319
320 for (size_t i = 0; i < arg.length(); i++) {
321 wxUChar ch = arg[i];
322 const wxChar *p = wxStrchr(needEscape, ch);
323
324 if (p)
325 str += wxString::Format(_T("\\%c"), escapes[p - needEscape]);
326 else if (wxIscntrl(ch))
327 str += wxString::Format(_T("\\%03o"), ch);
328 else
329 str += ch;
330 }
331
332 return str.length() == arg.length() && str.find(' ') == wxString::npos ?
333 str : _T("\"") + str + _T("\"");
334 }
335
336
337 ///////////////////////////////////////////////////////////////////////////////
338 // Test suite
339
340 class RegExTestSuite : public TestSuite
341 {
342 public:
343 RegExTestSuite(string name) : TestSuite(name) { }
344 void add(const char *mode, const char *id, const char *flags,
345 const char *pattern, const char *data, const char *expected, ...);
346 };
347
348 // Add a testcase to the suite
349 //
350 void RegExTestSuite::add(
351 const char *mode,
352 const char *id,
353 const char *flags,
354 const char *pattern,
355 const char *data,
356 const char *expected, ...)
357 {
358 string name = getName() + "." + id;
359
360 vector<const char *> expected_results;
361 va_list ap;
362
363 for (va_start(ap, expected); expected; expected = va_arg(ap, const char *))
364 expected_results.push_back(expected);
365
366 va_end(ap);
367
368 try {
369 addTest(new RegExTestCase(
370 name, mode, id, flags, pattern, data, expected_results));
371 }
372 catch (Exception& e) {
373 wxLogInfo(wxString::Format(_T("skipping: %s\n %s\n"),
374 wxString(name.c_str(), wxConvUTF8).c_str(),
375 wxString(e.what(), wxConvUTF8).c_str()));
376 }
377 }
378
379
380 // Include the generated tests
381 //
382 #include "regex.inc"
383
384
385 #endif // wxHAS_REGEX_ADVANCED