| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- ///////////////////////////////////////////////////////////////////////////////
- // Name: tests/regex/regex.cpp
- // Purpose: Test the built-in regex lib and wxRegEx
- // Author: Mike Wetherell
- // Copyright: (c) 2004 Mike Wetherell
- // Licence: wxWindows licence
- ///////////////////////////////////////////////////////////////////////////////
- //
- // Notes:
- //
- // To run just one section, say wx_1, do this:
- // test regex.wx_1
- //
- // To run all the regex tests:
- // test regex
- //
- // Some tests must be skipped since they use features which we do not make
- // available through wxRegEx. To see the list of tests that have been skipped
- // turn on verbose logging, e.g.:
- // test --verbose regex
- //
- // The tests here are for the builtin library, tests for wxRegEx in general
- // should go in wxregex.cpp
- //
- // The tests are generated from Henry Spencer's reg.test, additional test
- // can be added in wxreg.test. These test files are then turned into a C++
- // include file 'regex.inc' (included below) using a script 'regex.pl'.
- //
- // For compilers that support precompilation, includes "wx/wx.h".
- #include "testprec.h"
- #ifdef __BORLANDC__
- #pragma hdrstop
- #endif
- #if wxUSE_REGEX
- // for all others, include the necessary headers
- #ifndef WX_PRECOMP
- #include "wx/wx.h"
- #endif
- // many of the tests are specific to the builtin regex lib, so only attempts
- // to do them when using the builtin regex lib.
- //
- #ifdef wxHAS_REGEX_ADVANCED
- #include "wx/regex.h"
- #include <string>
- #include <vector>
- using CppUnit::Test;
- using CppUnit::TestCase;
- using CppUnit::TestSuite;
- using CppUnit::Exception;
- using std::string;
- using std::vector;
- ///////////////////////////////////////////////////////////////////////////////
- // The test case - an instance represents a single test
- class RegExTestCase : public TestCase
- {
- public:
- // constructor - create a single testcase
- RegExTestCase(
- const string& name,
- const char *mode,
- const char *id,
- const char *flags,
- const char *pattern,
- const char *data,
- const vector<const char *>& expected);
- protected:
- // run this testcase
- void runTest();
- private:
- // workers
- wxString Conv(const char *str);
- void parseFlags(const wxString& flags);
- void doTest(int flavor);
- static wxString quote(const wxString& arg);
- const wxChar *convError() const { return wxT("<cannot convert>"); }
- // assertions - adds some information about the test that failed
- void fail(const wxString& msg) const;
- void failIf(bool condition, const wxString& msg) const
- { if (condition) fail(msg); }
- // mode, id, flags, pattern, test data, expected results...
- int m_mode;
- wxString m_id;
- wxString m_flags;
- wxString m_pattern;
- wxString m_data;
- wxArrayString m_expected;
- // the flag decoded
- int m_compileFlags;
- int m_matchFlags;
- bool m_basic;
- bool m_extended;
- bool m_advanced;
- };
- // constructor - throws Exception on failure
- //
- RegExTestCase::RegExTestCase(
- const string& name,
- const char *mode,
- const char *id,
- const char *flags,
- const char *pattern,
- const char *data,
- const vector<const char *>& expected)
- :
- TestCase(name),
- m_mode(mode[0]),
- m_id(Conv(id)),
- m_flags(Conv(flags)),
- m_pattern(Conv(pattern)),
- m_data(Conv(data)),
- m_compileFlags(0),
- m_matchFlags(0),
- m_basic(false),
- m_extended(false),
- m_advanced(false)
- {
- bool badconv = m_pattern == convError() || m_data == convError();
- //RN: Removing the std:: here will break MSVC6 compilation
- std::vector<const char *>::const_iterator it;
- for (it = expected.begin(); it != expected.end(); ++it) {
- m_expected.push_back(Conv(*it));
- badconv = badconv || *m_expected.rbegin() == convError();
- }
- failIf(badconv, wxT("cannot convert to default character encoding"));
- // the flags need further parsing...
- parseFlags(m_flags);
- #ifndef wxHAS_REGEX_ADVANCED
- failIf(!m_basic && !m_extended, wxT("advanced regexs not available"));
- #endif
- }
- int wxWcscmp(const wchar_t* s1, const wchar_t* s2)
- {
- size_t nLen1 = wxWcslen(s1);
- size_t nLen2 = wxWcslen(s2);
- if (nLen1 != nLen2)
- return nLen1 - nLen2;
- return memcmp(s1, s2, nLen1*sizeof(wchar_t));
- }
- // convert a string from UTF8 to the internal encoding
- //
- wxString RegExTestCase::Conv(const char *str)
- {
- const wxWCharBuffer wstr = wxConvUTF8.cMB2WC(str);
- const wxWC2WXbuf buf = wxConvCurrent->cWC2WX(wstr);
- if (!buf || wxWcscmp(wxConvCurrent->cWX2WC(buf), wstr) != 0)
- return convError();
- return buf;
- }
- // Parse flags
- //
- void RegExTestCase::parseFlags(const wxString& flags)
- {
- for ( wxString::const_iterator p = flags.begin(); p != flags.end(); ++p )
- {
- switch ( (*p).GetValue() ) {
- // noop
- case '-': break;
- // we don't fully support these flags, but they don't stop us
- // checking for success of failure of the match, so treat as noop
- case 'A': case 'B': case 'E': case 'H':
- case 'I': case 'L': case 'M': case 'N':
- case 'P': case 'Q': case 'R': case 'S':
- case 'T': case 'U': case '%':
- break;
- // match options
- case '^': m_matchFlags |= wxRE_NOTBOL; break;
- case '$': m_matchFlags |= wxRE_NOTEOL; break;
- #if wxUSE_UNICODE
- case '*': break;
- #endif
- // compile options
- case '&': m_advanced = m_basic = true; break;
- case 'b': m_basic = true; break;
- case 'e': m_extended = true; break;
- case 'i': m_compileFlags |= wxRE_ICASE; break;
- case 'o': m_compileFlags |= wxRE_NOSUB; break;
- case 'n': m_compileFlags |= wxRE_NEWLINE; break;
- case 't': if (strchr("ep", m_mode)) break; // else fall through...
- // anything else we must skip the test
- default:
- fail(wxString::Format(
- wxT("requires unsupported flag '%c'"), *p));
- }
- }
- }
- // Try test for all flavours of expression specified
- //
- void RegExTestCase::runTest()
- {
- if (m_basic)
- doTest(wxRE_BASIC);
- if (m_extended)
- doTest(wxRE_EXTENDED);
- #ifdef wxHAS_REGEX_ADVANCED
- if (m_advanced || (!m_basic && !m_extended))
- doTest(wxRE_ADVANCED);
- #endif
- }
- // Try the test for a single flavour of expression
- //
- void RegExTestCase::doTest(int flavor)
- {
- wxRegEx re(m_pattern, m_compileFlags | flavor);
- // 'e' - test that the pattern fails to compile
- if (m_mode == 'e') {
- failIf(re.IsValid(), wxT("compile succeeded (should fail)"));
- return;
- }
- failIf(!re.IsValid(), wxT("compile failed"));
- bool matches = re.Matches(m_data, m_matchFlags);
- // 'f' or 'p' - test that the pattern does not match
- if (m_mode == 'f' || m_mode == 'p') {
- failIf(matches, wxT("match succeeded (should fail)"));
- return;
- }
- // otherwise 'm' or 'i' - test the pattern does match
- failIf(!matches, wxT("match failed"));
- if (m_compileFlags & wxRE_NOSUB)
- return;
- // check wxRegEx has correctly counted the number of subexpressions
- wxString msg;
- msg << wxT("GetMatchCount() == ") << re.GetMatchCount()
- << wxT(", expected ") << m_expected.size();
- failIf(m_expected.size() != re.GetMatchCount(), msg);
- for (size_t i = 0; i < m_expected.size(); i++) {
- wxString result;
- size_t start, len;
- msg.clear();
- msg << wxT("wxRegEx::GetMatch failed for match ") << i;
- failIf(!re.GetMatch(&start, &len, i), msg);
- // m - check the match returns the strings given
- if (m_mode == 'm')
- {
- if (start < INT_MAX)
- result = m_data.substr(start, len);
- else
- result = wxT("");
- }
- // i - check the match returns the offsets given
- else if (m_mode == 'i')
- {
- if (start > INT_MAX)
- result = wxT("-1 -1");
- else if (start + len > 0)
- result << start << wxT(" ") << start + len - 1;
- else
- result << start << wxT(" -1");
- }
- msg.clear();
- msg << wxT("match(") << i << wxT(") == ") << quote(result)
- << wxT(", expected == ") << quote(m_expected[i]);
- failIf(result != m_expected[i], msg);
- }
- }
- // assertion - adds some information about the test that failed
- //
- void RegExTestCase::fail(const wxString& msg) const
- {
- wxString str;
- wxArrayString::const_iterator it;
- str << (wxChar)m_mode << wxT(" ") << m_id << wxT(" ") << m_flags << wxT(" ")
- << quote(m_pattern) << wxT(" ") << quote(m_data);
- for (it = m_expected.begin(); it != m_expected.end(); ++it)
- str << wxT(" ") << quote(*it);
- if (str.length() > 77)
- str = str.substr(0, 74) + wxT("...");
- str << wxT("\n ") << msg;
- // no lossy convs so using utf8
- CPPUNIT_FAIL(string(str.mb_str(wxConvUTF8)));
- }
- // quote a string so that it can be displayed (static)
- //
- wxString RegExTestCase::quote(const wxString& arg)
- {
- const wxChar *needEscape = wxT("\a\b\t\n\v\f\r\"\\");
- const wxChar *escapes = wxT("abtnvfr\"\\");
- wxString str;
- for (size_t i = 0; i < arg.length(); i++) {
- wxChar ch = (wxChar)arg[i];
- const wxChar *p = wxStrchr(needEscape, ch);
- if (p)
- str += wxString::Format(wxT("\\%c"), escapes[p - needEscape]);
- else if (wxIscntrl(ch))
- str += wxString::Format(wxT("\\%03o"), ch);
- else
- str += (wxChar)ch;
- }
- return str.length() == arg.length() && str.find(' ') == wxString::npos ?
- str : wxT("\"") + str + wxT("\"");
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Test suite
- class RegExTestSuite : public TestSuite
- {
- public:
- RegExTestSuite(string name) : TestSuite(name) { }
- void add(const char *mode, const char *id, const char *flags,
- const char *pattern, const char *data, const char *expected, ...);
- };
- // Add a testcase to the suite
- //
- void RegExTestSuite::add(
- const char *mode,
- const char *id,
- const char *flags,
- const char *pattern,
- const char *data,
- const char *expected, ...)
- {
- string name = getName() + "." + id;
- vector<const char *> expected_results;
- va_list ap;
- for (va_start(ap, expected); expected; expected = va_arg(ap, const char *))
- expected_results.push_back(expected);
- va_end(ap);
- try {
- addTest(new RegExTestCase(
- name, mode, id, flags, pattern, data, expected_results));
- }
- catch (Exception& e) {
- wxLogInfo(wxString::Format(wxT("skipping: %s\n %s\n"),
- wxString(name.c_str(), wxConvUTF8).c_str(),
- wxString(e.what(), wxConvUTF8).c_str()));
- }
- }
- // Include the generated tests
- //
- #include "regex.inc"
- #endif // wxHAS_REGEX_ADVANCED
- #endif // wxUSE_REGEX
|