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