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