wxWidgets/tests/strings/strings.cpp
Michael Wetherell b26ed94e4f The cyrillic characters used in the CaseChanges() test have different values
on different platforms depending on the cyrillic character set used. So try
generating these from the Unicode instead of using fixed values.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@37613 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2006-02-15 21:16:45 +00:00

647 lines
18 KiB
C++
Raw Blame History

///////////////////////////////////////////////////////////////////////////////
// Name: tests/strings/strings.cpp
// Purpose: wxString unit test
// Author: Vadim Zeitlin, Wlodzimierz ABX Skiba
// Created: 2004-04-19
// RCS-ID: $Id$
// Copyright: (c) 2004 Vadim Zeitlin, Wlodzimierz Skiba
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif // WX_PRECOMP
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class StringTestCase : public CppUnit::TestCase
{
public:
StringTestCase();
private:
CPPUNIT_TEST_SUITE( StringTestCase );
CPPUNIT_TEST( String );
CPPUNIT_TEST( PChar );
CPPUNIT_TEST( Format );
CPPUNIT_TEST( Constructors );
#if wxUSE_WCHAR_T
CPPUNIT_TEST( ConstructorsWithConversion );
CPPUNIT_TEST( Conversion );
CPPUNIT_TEST( ConversionUTF7 );
CPPUNIT_TEST( ConversionUTF8 );
#endif // wxUSE_WCHAR_T
CPPUNIT_TEST( Extraction );
CPPUNIT_TEST( Find );
CPPUNIT_TEST( Replace );
CPPUNIT_TEST( Match );
CPPUNIT_TEST( CaseChanges );
CPPUNIT_TEST( Compare );
CPPUNIT_TEST( CompareNoCase );
CPPUNIT_TEST( ToLong );
CPPUNIT_TEST( ToULong );
CPPUNIT_TEST( ToDouble );
CPPUNIT_TEST_SUITE_END();
void String();
void PChar();
void Format();
void Constructors();
#if wxUSE_WCHAR_T
void ConstructorsWithConversion();
void Conversion();
void ConversionUTF7();
void ConversionUTF8();
#endif // wxUSE_WCHAR_T
void Extraction();
void Find();
void Replace();
void Match();
void CaseChanges();
void Compare();
void CompareNoCase();
void ToLong();
void ToULong();
void ToDouble();
#if wxUSE_WCHAR_T
// test if converting s using the given encoding gives ws and vice versa
//
// if either of the first 2 arguments is NULL, the conversion is supposed
// to fail
void DoTestConversion(const char *s, const wchar_t *w, wxCSConv& conv);
#endif // wxUSE_WCHAR_T
DECLARE_NO_COPY_CLASS(StringTestCase)
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( StringTestCase );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StringTestCase, "StringTestCase" );
StringTestCase::StringTestCase()
{
}
void StringTestCase::String()
{
wxString a, b, c;
a.reserve (128);
b.reserve (128);
c.reserve (128);
for (int i = 0; i < 2; ++i)
{
a = _T("Hello");
b = _T(" world");
c = _T("! How'ya doin'?");
a += b;
a += c;
c = _T("Hello world! What's up?");
CPPUNIT_ASSERT( c != a );
}
}
void StringTestCase::PChar()
{
wxChar a [128];
wxChar b [128];
wxChar c [128];
for (int i = 0; i < 2; ++i)
{
wxStrcpy (a, _T("Hello"));
wxStrcpy (b, _T(" world"));
wxStrcpy (c, _T("! How'ya doin'?"));
wxStrcat (a, b);
wxStrcat (a, c);
wxStrcpy (c, _T("Hello world! What's up?"));
CPPUNIT_ASSERT( wxStrcmp (c, a) != 0 );
}
}
void StringTestCase::Format()
{
wxString s1,s2;
s1.Printf(_T("%03d"), 18);
CPPUNIT_ASSERT( s1 == wxString::Format(_T("%03d"), 18) );
s2.Printf(_T("Number 18: %s\n"), s1.c_str());
CPPUNIT_ASSERT( s2 == wxString::Format(_T("Number 18: %s\n"), s1.c_str()) );
static const size_t lengths[] = { 1, 512, 1024, 1025, 2048, 4096, 4097 };
for ( size_t n = 0; n < WXSIZEOF(lengths); n++ )
{
const size_t len = lengths[n];
wxString s(_T('Z'), len);
CPPUNIT_ASSERT_EQUAL( len, wxString::Format(_T("%s"), s.c_str()).length());
}
}
void StringTestCase::Constructors()
{
#define TEST_CTOR(args, res) \
{ \
wxString s args ; \
CPPUNIT_ASSERT( s == res ); \
}
TEST_CTOR((_T('Z'), 4), _T("ZZZZ"));
TEST_CTOR((_T("Hello"), 4), _T("Hell"));
TEST_CTOR((_T("Hello"), 5), _T("Hello"));
static const wxChar *s = _T("?really!");
const wxChar *start = wxStrchr(s, _T('r'));
const wxChar *end = wxStrchr(s, _T('!'));
TEST_CTOR((start, end), _T("really"));
}
#if wxUSE_WCHAR_T
void StringTestCase::ConstructorsWithConversion()
{
// the string "D<>j<EFBFBD>" in UTF-8 and wchar_t:
const unsigned char utf8Buf[] = {0x44,0xC3,0xA9,0x6A,0xC3,0xA0,0};
const wchar_t wchar[] = {0x44,0xE9,0x6A,0xE0,0};
const unsigned char utf8subBuf[] = {0x44,0xC3,0xA9,0x6A,0}; // just "D<>j"
const char *utf8 = (char *)utf8Buf;
const char *utf8sub = (char *)utf8subBuf;
wxString s1(utf8, wxConvUTF8);
wxString s2(wchar, wxConvUTF8);
#if wxUSE_UNICODE
CPPUNIT_ASSERT( s1 == wchar );
CPPUNIT_ASSERT( s2 == wchar );
#else
CPPUNIT_ASSERT( s1 == utf8 );
CPPUNIT_ASSERT( s2 == utf8 );
#endif
wxString sub(utf8sub, wxConvUTF8); // "Dej" substring
wxString s3(utf8, wxConvUTF8, 4);
wxString s4(wchar, wxConvUTF8, 3);
CPPUNIT_ASSERT( s3 == sub );
CPPUNIT_ASSERT( s4 == sub );
#if wxUSE_UNICODE
CPPUNIT_ASSERT ( wxString("\t[pl]open.format.Sformatuj dyskietk<74>=gfloppy %f",
wxConvUTF8) == wxT("") ); //should stop at pos 35
#endif
}
void StringTestCase::Conversion()
{
#if wxUSE_UNICODE
wxString szTheString(L"The\0String", wxConvLibc, 10);
wxCharBuffer theBuffer = szTheString.mb_str();
CPPUNIT_ASSERT( memcmp(theBuffer.data(), "The\0String", 11) == 0 );
wxString szTheString2("The\0String", wxConvLocal, 10);
CPPUNIT_ASSERT( szTheString2.length() == 11 );
CPPUNIT_ASSERT( wxTmemcmp(szTheString2.c_str(), L"The\0String", 11) == 0 );
#else
wxString szTheString(wxT("TheString"));
szTheString.insert(3, 1, '\0');
wxWCharBuffer theBuffer = szTheString.wc_str(wxConvLibc);
CPPUNIT_ASSERT( memcmp(theBuffer.data(), L"The\0String", 11 * sizeof(wchar_t)) == 0 );
wxString szLocalTheString(wxT("TheString"));
szLocalTheString.insert(3, 1, '\0');
wxWCharBuffer theLocalBuffer = szLocalTheString.wc_str(wxConvLocal);
CPPUNIT_ASSERT( memcmp(theLocalBuffer.data(), L"The\0String", 11 * sizeof(wchar_t)) == 0 );
#endif
}
#if !wxUSE_UNICODE
// in case wcscmp is missing
//
static int wx_wcscmp(const wchar_t *s1, const wchar_t *s2)
{
while (*s1 == *s2 && *s1 != 0)
{
s1++;
s2++;
}
return *s1 - *s2;
}
#endif
void
StringTestCase::DoTestConversion(const char *s,
const wchar_t *ws,
wxCSConv& conv)
{
#if wxUSE_UNICODE
if ( ws )
{
wxCharBuffer buf(wxString(ws).mb_str(conv));
CPPUNIT_ASSERT( strcmp(buf, s) == 0 );
}
#else // wxUSE_UNICODE
if ( s )
{
wxWCharBuffer wbuf(wxString(s).wc_str(conv));
if ( ws )
CPPUNIT_ASSERT( wx_wcscmp(wbuf, ws) == 0 );
else
CPPUNIT_ASSERT( !*wbuf );
}
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
}
struct StringConversionData
{
const char *str;
const wchar_t *wcs;
};
void StringTestCase::ConversionUTF7()
{
static const StringConversionData utf7data[] =
{
{ "+-", L"+" },
{ "+--", L"+-" },
//\u isn't recognized on MSVC 6
#if !defined(_MSC_VER)
#if !defined(__GNUC__) || (__GNUC__ >= 3)
{ "+AKM-", L"\u00a3" },
#endif
#endif
// Windows accepts invalid UTF-7 strings and so does our UTF-7
// conversion code -- this is wrong IMO but the way it is for now
//
// notice that converting "+" still behaves as expected because the
// result is just an empty string, i.e. the same as if there were an
// error, but converting "a+" results in "a" while it really should
// fail
{ "+", NULL },
{ "a+", L"a" },
};
wxCSConv conv(_T("utf-7"));
for ( size_t n = 0; n < WXSIZEOF(utf7data); n++ )
{
const StringConversionData& d = utf7data[n];
DoTestConversion(d.str, d.wcs, conv);
}
}
void StringTestCase::ConversionUTF8()
{
static const StringConversionData utf8data[] =
{
//\u isn't recognized on MSVC 6
#if !defined(_MSC_VER)
#if !defined(__GNUC__) || (__GNUC__ >= 3)
{ "\xc2\xa3", L"\u00a3" },
#endif
#endif
{ "\xc2", NULL },
};
wxCSConv conv(_T("utf-8"));
for ( size_t n = 0; n < WXSIZEOF(utf8data); n++ )
{
const StringConversionData& d = utf8data[n];
DoTestConversion(d.str, d.wcs, conv);
}
}
#endif // wxUSE_WCHAR_T
void StringTestCase::Extraction()
{
wxString s(_T("Hello, world!"));
CPPUNIT_ASSERT( wxStrcmp( s.c_str() , _T("Hello, world!") ) == 0 );
CPPUNIT_ASSERT( wxStrcmp( s.Left(5).c_str() , _T("Hello") ) == 0 );
CPPUNIT_ASSERT( wxStrcmp( s.Right(6).c_str() , _T("world!") ) == 0 );
CPPUNIT_ASSERT( wxStrcmp( s(3, 5).c_str() , _T("lo, w") ) == 0 );
CPPUNIT_ASSERT( wxStrcmp( s.Mid(3).c_str() , _T("lo, world!") ) == 0 );
CPPUNIT_ASSERT( wxStrcmp( s.substr(3, 5).c_str() , _T("lo, w") ) == 0 );
CPPUNIT_ASSERT( wxStrcmp( s.substr(3).c_str() , _T("lo, world!") ) == 0 );
wxString rest;
#define TEST_STARTS_WITH( prefix , correct_rest, result ) \
CPPUNIT_ASSERT( \
( s.StartsWith( prefix, &rest ) == result ) && \
( ( result == false ) || ( wxStrcmp( correct_rest , rest ) == 0 ) ) \
)
TEST_STARTS_WITH( _T("Hello"), _T(", world!"), true );
TEST_STARTS_WITH( _T("Hello, "), _T("world!"), true );
TEST_STARTS_WITH( _T("Hello, world!"), _T(""), true );
TEST_STARTS_WITH( _T("Hello, world!!!"), _T(""), false );
TEST_STARTS_WITH( _T(""), _T("Hello, world!"), true );
TEST_STARTS_WITH( _T("Goodbye"), _T(""), false );
TEST_STARTS_WITH( _T("Hi"), _T(""), false );
#undef TEST_STARTS_WITH
}
void StringTestCase::Find()
{
#define TEST_FIND( str , start , result ) \
CPPUNIT_ASSERT( wxString(str).find(_T("ell"), start) == result );
TEST_FIND( _T("Well, hello world"), 0, 1 );
TEST_FIND( _T("Well, hello world"), 6, 7 );
TEST_FIND( _T("Well, hello world"), 9, wxString::npos );
#undef TEST_FIND
}
void StringTestCase::Replace()
{
#define TEST_REPLACE( original , pos , len , replacement , result ) \
{ \
wxString s = original; \
s.replace( pos , len , replacement ); \
CPPUNIT_ASSERT( s == result ); \
}
TEST_REPLACE( _T("012-AWORD-XYZ"), 4, 5, _T("BWORD"), _T("012-BWORD-XYZ") );
TEST_REPLACE( _T("increase"), 0, 2, _T("de"), _T("decrease") );
TEST_REPLACE( _T("wxWindow"), 8, 0, _T("s"), _T("wxWindows") );
TEST_REPLACE( _T("foobar"), 3, 0, _T("-"), _T("foo-bar") );
TEST_REPLACE( _T("barfoo"), 0, 6, _T("foobar"), _T("foobar") );
#define TEST_NULLCHARREPLACE( o , olen, pos , len , replacement , r, rlen ) \
{ \
wxString s(o,olen); \
s.replace( pos , len , replacement ); \
CPPUNIT_ASSERT( s == wxString(r,rlen) ); \
}
TEST_NULLCHARREPLACE( _T("null\0char"), 9, 5, 1, _T("d"),
_T("null\0dhar"), 9 );
#define TEST_WXREPLACE( o , olen, olds, news, all, r, rlen ) \
{ \
wxString s(o,olen); \
s.Replace( olds, news, all ); \
CPPUNIT_ASSERT( s == wxString(r,rlen) ); \
}
TEST_WXREPLACE( _T("null\0char"), 9, _T("c"), _T("de"), true,
_T("null\0dehar"), 10 );
TEST_WXREPLACE( _T("null\0dehar"), 10, _T("de"), _T("c"), true,
_T("null\0char"), 9 );
#undef TEST_WXREPLACE
#undef TEST_NULLCHARREPLACE
#undef TEST_REPLACE
}
void StringTestCase::Match()
{
#define TEST_MATCH( s1 , s2 , result ) \
CPPUNIT_ASSERT( wxString(s1).Matches(s2) == result )
TEST_MATCH( _T("foobar"), _T("foo*"), true );
TEST_MATCH( _T("foobar"), _T("*oo*"), true );
TEST_MATCH( _T("foobar"), _T("*bar"), true );
TEST_MATCH( _T("foobar"), _T("??????"), true );
TEST_MATCH( _T("foobar"), _T("f??b*"), true );
TEST_MATCH( _T("foobar"), _T("f?b*"), false );
TEST_MATCH( _T("foobar"), _T("*goo*"), false );
TEST_MATCH( _T("foobar"), _T("*foo"), false );
TEST_MATCH( _T("foobarfoo"), _T("*foo"), true );
TEST_MATCH( _T(""), _T("*"), true );
TEST_MATCH( _T(""), _T("?"), false );
#undef TEST_MATCH
}
void StringTestCase::CaseChanges()
{
wxString s1(_T("Hello!"));
wxString s1u(s1);
wxString s1l(s1);
s1u.MakeUpper();
s1l.MakeLower();
wxString s2u, s2l;
s2u.MakeUpper();
s2l.MakeLower();
CPPUNIT_ASSERT( s1u == _T("HELLO!") );
CPPUNIT_ASSERT( s1l == _T("hello!") );
CPPUNIT_ASSERT( s2u == wxEmptyString );
CPPUNIT_ASSERT( s2l == wxEmptyString );
#if !wxUSE_UNICODE
wxLocale locRu(wxLANGUAGE_RUSSIAN, 0 /* flags */);
if ( locRu.IsOk() )
{
// try upper casing 8bit strings
const wchar_t capital_ya[] = { 0x42f, 0 },
small_ya[] = { 0x44f, 0 };
wxString sUpper(wxConvLibc.cWC2MB(capital_ya)),
sLower(wxConvLibc.cWC2MB(small_ya));
CPPUNIT_ASSERT( sUpper.Lower() == sLower );
CPPUNIT_ASSERT( sLower.Upper() == sUpper );
}
#endif // !wxUSE_UNICODE
}
void StringTestCase::Compare()
{
wxString s1 = wxT("AHH");
wxString eq = wxT("AHH");
wxString neq1 = wxT("HAH");
wxString neq2 = wxT("AH");
wxString neq3 = wxT("AHHH");
wxString neq4 = wxT("AhH");
CPPUNIT_ASSERT( s1 == eq );
CPPUNIT_ASSERT( s1 != neq1 );
CPPUNIT_ASSERT( s1 != neq2 );
CPPUNIT_ASSERT( s1 != neq3 );
CPPUNIT_ASSERT( s1 != neq4 );
// wxString _s1 = wxT("A\0HH");
// wxString _eq = wxT("A\0HH");
// wxString _neq1 = wxT("H\0AH");
// wxString _neq2 = wxT("A\0H");
// wxString _neq3 = wxT("A\0HHH");
// wxString _neq4 = wxT("A\0hH");
s1.insert(1,1,'\0');
eq.insert(1,1,'\0');
neq1.insert(1,1,'\0');
neq2.insert(1,1,'\0');
neq3.insert(1,1,'\0');
neq4.insert(1,1,'\0');
CPPUNIT_ASSERT( s1 == eq );
CPPUNIT_ASSERT( s1 != neq1 );
CPPUNIT_ASSERT( s1 != neq2 );
CPPUNIT_ASSERT( s1 != neq3 );
CPPUNIT_ASSERT( s1 != neq4 );
}
void StringTestCase::CompareNoCase()
{
wxString s1 = wxT("AHH");
wxString eq = wxT("AHH");
wxString eq2 = wxT("AhH");
wxString eq3 = wxT("ahh");
wxString neq = wxT("HAH");
wxString neq2 = wxT("AH");
wxString neq3 = wxT("AHHH");
#define CPPUNIT_CNCEQ_ASSERT(s1, s2) CPPUNIT_ASSERT( s1.CmpNoCase(s2) == 0)
#define CPPUNIT_CNCNEQ_ASSERT(s1, s2) CPPUNIT_ASSERT( s1.CmpNoCase(s2) != 0)
CPPUNIT_CNCEQ_ASSERT( s1, eq );
CPPUNIT_CNCEQ_ASSERT( s1, eq2 );
CPPUNIT_CNCEQ_ASSERT( s1, eq3 );
CPPUNIT_CNCNEQ_ASSERT( s1, neq );
CPPUNIT_CNCNEQ_ASSERT( s1, neq2 );
CPPUNIT_CNCNEQ_ASSERT( s1, neq3 );
// wxString _s1 = wxT("A\0HH");
// wxString _eq = wxT("A\0HH");
// wxString _eq2 = wxT("A\0hH");
// wxString _eq3 = wxT("a\0hh");
// wxString _neq = wxT("H\0AH");
// wxString _neq2 = wxT("A\0H");
// wxString _neq3 = wxT("A\0HHH");
s1.insert(1,1,'\0');
eq.insert(1,1,'\0');
eq2.insert(1,1,'\0');
eq3.insert(1,1,'\0');
neq.insert(1,1,'\0');
neq2.insert(1,1,'\0');
neq3.insert(1,1,'\0');
CPPUNIT_CNCEQ_ASSERT( s1, eq );
CPPUNIT_CNCEQ_ASSERT( s1, eq2 );
CPPUNIT_CNCEQ_ASSERT( s1, eq3 );
CPPUNIT_CNCNEQ_ASSERT( s1, neq );
CPPUNIT_CNCNEQ_ASSERT( s1, neq2 );
CPPUNIT_CNCNEQ_ASSERT( s1, neq3 );
}
void StringTestCase::ToLong()
{
long l;
static const struct ToLongData
{
const wxChar *str;
long value;
bool ok;
} longData[] =
{
{ _T("1"), 1, true },
{ _T("0"), 0, true },
{ _T("a"), 0, false },
{ _T("12345"), 12345, true },
{ _T("-1"), -1, true },
{ _T("--1"), 0, false },
};
size_t n;
for ( n = 0; n < WXSIZEOF(longData); n++ )
{
const ToLongData& ld = longData[n];
CPPUNIT_ASSERT_EQUAL( ld.ok, wxString(ld.str).ToLong(&l) );
if ( ld.ok )
CPPUNIT_ASSERT_EQUAL( ld.value, l );
}
}
void StringTestCase::ToULong()
{
unsigned long ul;
static const struct ToULongData
{
const wxChar *str;
unsigned long value;
bool ok;
} ulongData[] =
{
{ _T("1"), 1, true },
{ _T("0"), 0, true },
{ _T("a"), 0, false },
{ _T("12345"), 12345, true },
// this is surprizing but consistent with strtoul() behaviour
{ _T("-1"), ULONG_MAX, true },
};
size_t n;
for ( n = 0; n < WXSIZEOF(ulongData); n++ )
{
const ToULongData& uld = ulongData[n];
CPPUNIT_ASSERT_EQUAL( uld.ok, wxString(uld.str).ToULong(&ul) );
if ( uld.ok )
CPPUNIT_ASSERT_EQUAL( uld.value, ul );
}
}
void StringTestCase::ToDouble()
{
double d;
static const struct ToDoubleData
{
const wxChar *str;
double value;
bool ok;
} doubleData[] =
{
{ _T("1"), 1, true },
{ _T("1.23"), 1.23, true },
{ _T(".1"), .1, true },
{ _T("1."), 1, true },
{ _T("1.."), 0, false },
{ _T("0"), 0, true },
{ _T("a"), 0, false },
{ _T("12345"), 12345, true },
{ _T("-1"), -1, true },
{ _T("--1"), 0, false },
};
// we need to use decimal point, not comma or whatever is its value for the
// current locale
wxSetlocale(LC_ALL, _T("C"));
size_t n;
for ( n = 0; n < WXSIZEOF(doubleData); n++ )
{
const ToDoubleData& ld = doubleData[n];
CPPUNIT_ASSERT_EQUAL( ld.ok, wxString(ld.str).ToDouble(&d) );
if ( ld.ok )
CPPUNIT_ASSERT_EQUAL( ld.value, d );
}
}