From 684feb7418f02dda6fbeef04b71984338d208b1e Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Sat, 30 Jul 2016 19:07:43 +0000 Subject: [PATCH] [ATL][ATL_APITEST] Implement / Improve CString, based upon the code that was already there. CORE-11579 #resolve Add code + tests for: Conversion of A->W and W->A, equality operators, MakeLower, MakeUpper, Find, FindOneOf, ReverseFind, Compare, Mid, Left, Right, Format, Replace, Trim, TrimLeft, TrimRight. svn path=/trunk/; revision=72061 --- reactos/sdk/lib/atl/atlsimpstr.h | 24 +- reactos/sdk/lib/atl/atlstr.h | 6 + reactos/sdk/lib/atl/cstringt.h | 594 ++++++++++++++++++++++++++- rostests/apitests/atl/CMakeLists.txt | 3 +- rostests/apitests/atl/CString.cpp | 176 ++++++++ rostests/apitests/atl/CString.inl | 358 ++++++++++++++++ rostests/apitests/atl/testlist.c | 2 + 7 files changed, 1158 insertions(+), 5 deletions(-) create mode 100644 rostests/apitests/atl/CString.cpp create mode 100644 rostests/apitests/atl/CString.inl diff --git a/reactos/sdk/lib/atl/atlsimpstr.h b/reactos/sdk/lib/atl/atlsimpstr.h index f55dae5ec90..ea4434c9db2 100644 --- a/reactos/sdk/lib/atl/atlsimpstr.h +++ b/reactos/sdk/lib/atl/atlsimpstr.h @@ -200,6 +200,28 @@ public: return *this; } + CSimpleStringT& operator=(_In_ const CSimpleStringT& strSrc) + { + CStringData* pData = GetData(); + CStringData* pNewData = strSrc.GetData(); + + if (pNewData != pData) + { + if (!pData->IsLocked() && (pNewData->pStringMgr == pData->pStringMgr)) + { + pNewData = CloneData(pNewData); + pData->Release(); + Attach(pNewData); + } + else + { + SetString(strSrc.GetString(), strSrc.GetLength()); + } + } + + return *this; + } + CSimpleStringT& operator+=(_In_ const CSimpleStringT& strSrc) { Append(strSrc); @@ -495,7 +517,7 @@ private: if (pOldData->IsShared()) { Fork(nLength); - ATLASSERT(FALSE); + //ATLASSERT(FALSE); } else if (pOldData->nAllocLength < nLength) { diff --git a/reactos/sdk/lib/atl/atlstr.h b/reactos/sdk/lib/atl/atlstr.h index 45d03550dcf..549e7e0533c 100644 --- a/reactos/sdk/lib/atl/atlstr.h +++ b/reactos/sdk/lib/atl/atlstr.h @@ -124,6 +124,12 @@ public: typedef CStringT< wchar_t, StrTraitATL< wchar_t, ChTraitsCRT > > CAtlStringW; +typedef CStringT< char, StrTraitATL< char, ChTraitsCRT > > CAtlStringA; + + +typedef CAtlStringW CStringW; +typedef CAtlStringA CStringA; + } diff --git a/reactos/sdk/lib/atl/cstringt.h b/reactos/sdk/lib/atl/cstringt.h index c14eccc20a4..d895ca2187d 100644 --- a/reactos/sdk/lib/atl/cstringt.h +++ b/reactos/sdk/lib/atl/cstringt.h @@ -28,14 +28,28 @@ public: static int __cdecl GetBaseTypeLength(_In_z_ LPCWSTR pszSource) throw() { - return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL) - 1; + if (pszSource == NULL) return -1; + return static_cast(wcslen(pszSource)); + } + + static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSource) throw() + { + if (pszSource == NULL) return 0; + return ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSource, -1, NULL, 0) - 1; } static int __cdecl GetBaseTypeLength( _In_reads_(nLength) LPCWSTR pszSource, _In_ int nLength) throw() { - return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL); + return nLength; + } + + static int __cdecl GetBaseTypeLength( + _In_reads_(nLength) LPCSTR pszSource, + _In_ int nLength) throw() + { + return ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0); } static void __cdecl ConvertToBaseType( @@ -49,9 +63,198 @@ public: wmemcpy(pszDest, pszSrc, nSrcLength); } + + static void __cdecl ConvertToBaseType( + _Out_writes_(nDestLength) LPWSTR pszDest, + _In_ int nDestLength, + _In_ LPCSTR pszSrc, + _In_ int nSrcLength = -1) + { + if (nSrcLength == -1) + nSrcLength = 1 + GetBaseTypeLength(pszSrc); + + ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength); + } + + static void __cdecl MakeLower( + _Out_writes_(nSrcLength) LPWSTR pszSource, + _In_ int nSrcLength) + { + ::CharLowerBuffW(pszSource, nSrcLength); + } + + static void __cdecl MakeUpper( + _Out_writes_(nSrcLength) LPWSTR pszSource, + _In_ int nSrcLength) + { + ::CharUpperBuffW(pszSource, nSrcLength); + } + + static LPWSTR __cdecl FindString( + _In_z_ LPCWSTR pszSource, + _In_z_ LPCWSTR pszSub) + { + return ::wcsstr(pszSource, pszSub); + } + + static LPWSTR __cdecl FindChar( + _In_z_ LPCWSTR pszSource, + _In_ WCHAR ch) + { + return ::wcschr(pszSource, ch); + } + + static LPWSTR __cdecl FindCharReverse( + _In_z_ LPCWSTR pszSource, + _In_ WCHAR ch) + { + return ::wcsrchr(pszSource, ch); + } + + static LPWSTR __cdecl FindOneOf( + _In_z_ LPCWSTR pszSource, + _In_z_ LPCWSTR pszCharSet) + { + return ::wcspbrk(pszSource, pszCharSet); + } + + static int __cdecl Compare( + _In_z_ LPCWSTR psz1, + _In_z_ LPCWSTR psz2) + { + return ::wcscmp(psz1, psz2); + } + + static int __cdecl FormatV( + _In_opt_z_ LPWSTR pszDest, + _In_z_ LPCWSTR pszFormat, + _In_ va_list args) + { + if (pszDest == NULL) + return ::_vscwprintf(pszFormat, args); + return ::vswprintf(pszDest, pszFormat, args); + } + }; +// Template specialization + +template<> +class ChTraitsCRT : public ChTraitsBase +{ +public: + + static int __cdecl GetBaseTypeLength(_In_z_ LPCWSTR pszSource) throw() + { + return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL) - 1; + } + + static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSource) throw() + { + if (pszSource == NULL) return 0; + return static_cast(strlen(pszSource)); + } + + static int __cdecl GetBaseTypeLength( + _In_reads_(nLength) LPCWSTR pszSource, + _In_ int nLength) throw() + { + return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL); + } + + static int __cdecl GetBaseTypeLength( + _In_reads_(nLength) LPCSTR pszSource, + _In_ int nLength) throw() + { + return nLength; + } + + static void __cdecl ConvertToBaseType( + _Out_writes_(nDestLength) LPSTR pszDest, + _In_ int nDestLength, + _In_ LPCWSTR pszSrc, + _In_ int nSrcLength = -1) + { + if (nSrcLength == -1) + nSrcLength = 1 + GetBaseTypeLength(pszSrc); + + ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL); + } + + static void __cdecl ConvertToBaseType( + _Out_writes_(nDestLength) LPSTR pszDest, + _In_ int nDestLength, + _In_ LPCSTR pszSrc, + _In_ int nSrcLength = -1) + { + if (nSrcLength == -1) + nSrcLength = 1 + GetBaseTypeLength(pszSrc); + + memcpy(pszDest, pszSrc, nSrcLength); + } + + static void __cdecl MakeLower( + _Out_writes_(nSrcLength) LPSTR pszSource, + _In_ int nSrcLength) + { + ::CharLowerBuffA(pszSource, nSrcLength); + } + + static void __cdecl MakeUpper( + _Out_writes_(nSrcLength) LPSTR pszSource, + _In_ int nSrcLength) + { + ::CharUpperBuffA(pszSource, nSrcLength); + } + + static LPSTR __cdecl FindString( + _In_z_ LPCSTR pszSource, + _In_z_ LPCSTR pszSub) + { + return ::strstr(pszSource, pszSub); + } + + static LPSTR __cdecl FindChar( + _In_z_ LPCSTR pszSource, + _In_ CHAR ch) + { + return ::strchr(pszSource, ch); + } + + static LPSTR __cdecl FindCharReverse( + _In_z_ LPCSTR pszSource, + _In_ CHAR ch) + { + return ::strrchr(pszSource, ch); + } + + static LPSTR __cdecl FindOneOf( + _In_z_ LPCSTR pszSource, + _In_z_ LPCSTR pszCharSet) + { + return ::strpbrk(pszSource, pszCharSet); + } + + static int __cdecl Compare( + _In_z_ LPCSTR psz1, + _In_z_ LPCSTR psz2) + { + return ::strcmp(psz1, psz2); + } + + static int __cdecl FormatV( + _In_opt_z_ LPSTR pszDest, + _In_z_ LPCSTR pszFormat, + _In_ va_list args) + { + if (pszDest == NULL) + return ::_vscprintf(pszFormat, args); + return ::vsprintf(pszDest, pszFormat, args); + } + +}; + namespace _CSTRING_IMPL_ { @@ -63,6 +266,8 @@ namespace _CSTRING_IMPL_ } +// TODO: disable conversion functions when _CSTRING_DISABLE_NARROW_WIDE_CONVERSION is defined. + template class CStringT : public CSimpleStringT ::c_bIsMFCDLLTraits> @@ -90,7 +295,7 @@ public: static void __cdecl Construct(_In_ CStringT* pString) { - pString = new CStringT; + new(pString) CStringT; } CStringT(_In_ const CStringT& strSrc) : @@ -98,6 +303,13 @@ public: { } + template + CStringT(_In_ const CStringT & strSrc) : + CThisSimpleString(StringTraits::GetDefaultManager()) + { + *this = static_cast(strSrc); + } + CStringT(_In_opt_z_ const XCHAR* pszSrc) : CThisSimpleString( StringTraits::GetDefaultManager() ) { @@ -114,6 +326,36 @@ public: *this = pszSrc; } + CStringT(_In_opt_z_ const YCHAR* pszSrc) : + CThisSimpleString( StringTraits::GetDefaultManager() ) + { + // FIXME: Check whether pszSrc is not a resource string ID! + *this = pszSrc; + } + + CStringT( + _In_opt_z_ const YCHAR* pszSrc, + _In_ IAtlStringMgr* pStringMgr) : + CThisSimpleString( pStringMgr ) + { + // FIXME: Check whether pszSrc is not a resource string ID! + *this = pszSrc; + } + + CStringT( + _In_reads_z_(nLength) const XCHAR* pch, + _In_ int nLength) : + CThisSimpleString(pch, nLength, StringTraits::GetDefaultManager()) + { + } + + CStringT( + _In_reads_z_(nLength) const YCHAR* pch, + _In_ int nLength) : + CThisSimpleString(pch, nLength, StringTraits::GetDefaultManager()) + { + } + CStringT& operator=(_In_ const CStringT& strSrc) { CThisSimpleString::operator=(strSrc); @@ -126,6 +368,59 @@ public: return *this; } + CStringT& operator=(_In_opt_z_ PCYSTR pszSrc) + { + int length = pszSrc ? StringTraits::GetBaseTypeLength(pszSrc) : 0; + if (length > 0) + { + PXSTR result = CThisSimpleString::GetBuffer(length); + StringTraits::ConvertToBaseType(result, length, pszSrc); + CThisSimpleString::ReleaseBufferSetLength(length); + } + else + { + CThisSimpleString::Empty(); + } + return *this; + } + + friend bool operator==(const CStringT& str1, const CStringT& str2) throw() + { + return str1.Compare(str2) == 0; + } + + friend bool operator==(const CStringT& str1, PCXSTR psz2) throw() + { + return str1.Compare(psz2) == 0; + } + + friend bool operator==(const CStringT& str1, PCYSTR psz2) throw() + { + CStringT tmp(psz2, str1.GetManager()); + return tmp == str1; + } + + friend bool operator==(const CStringT& str1, XCHAR ch2) throw() + { + return str1.GetLength() == 1 && str1[0] == ch2; + } + + friend bool operator==(PCXSTR psz1, const CStringT& str2) throw() + { + return str2.Compare(psz1) == 0; + } + + friend bool operator==(PCYSTR psz1, const CStringT& str2) throw() + { + CStringT tmp(psz1, str2.GetManager()); + return tmp.Compare(str2) == 0; + } + + friend bool operator==(XCHAR ch1, const CStringT& str2) throw() + { + return str2.GetLength() == 1 && str2[0] == ch1; + } + CStringT& operator+=(_In_ const CThisSimpleString& str) { CThisSimpleString::operator+=(str); @@ -151,6 +446,299 @@ public: return TRUE; } + + CStringT& MakeLower() + { + int nLength = CThisSimpleString::GetLength(); + PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength); + + StringTraits::MakeLower(pszBuffer, nLength); + CThisSimpleString::ReleaseBufferSetLength(nLength); + + return *this; + } + + CStringT& MakeUpper() + { + int nLength = CThisSimpleString::GetLength(); + PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength); + + StringTraits::MakeUpper(pszBuffer, nLength); + CThisSimpleString::ReleaseBufferSetLength(nLength); + + return *this; + } + + int Find(_In_ PCXSTR pszSub, _In_opt_ int iStart = 0) const throw() + { + int nLength = CThisSimpleString::GetLength(); + + if (iStart >= nLength || iStart < 0) + return -1; + + PCXSTR pszString = CThisSimpleString::GetString(); + PCXSTR pszResult = StringTraits::FindString(pszString + iStart, pszSub); + + return pszResult ? ((int)(pszResult - pszString)) : -1; + } + + int Find(_In_ XCHAR ch, _In_opt_ int iStart = 0) const throw() + { + int nLength = CThisSimpleString::GetLength(); + + if (iStart >= nLength || iStart < 0) + return -1; + + PCXSTR pszString = CThisSimpleString::GetString(); + PCXSTR pszResult = StringTraits::FindChar(pszString + iStart, ch); + + return pszResult ? ((int)(pszResult - pszString)) : -1; + } + + int FindOneOf(_In_ PCXSTR pszCharSet) const throw() + { + PCXSTR pszString = CThisSimpleString::GetString(); + PCXSTR pszResult = StringTraits::FindOneOf(pszString, pszCharSet); + + return pszResult ? ((int)(pszResult - pszString)) : -1; + } + + int ReverseFind(_In_ XCHAR ch) const throw() + { + PCXSTR pszString = CThisSimpleString::GetString(); + PCXSTR pszResult = StringTraits::FindCharReverse(pszString, ch); + + return pszResult ? ((int)(pszResult - pszString)) : -1; + } + + int Compare(_In_z_ PCXSTR psz) const + { + return StringTraits::Compare(CThisSimpleString::GetString(), psz); + } + + + CStringT Mid(int iFirst, int nCount) const + { + int nLength = CThisSimpleString::GetLength(); + + if (iFirst < 0) + iFirst = 0; + if (nCount < 0) + nCount = 0; + if (iFirst > nLength) + iFirst = nLength; + if (iFirst + nCount > nLength) + nCount = nLength - iFirst; + + return CStringT(CThisSimpleString::GetString() + iFirst, nCount); + } + + CStringT Mid(int iFirst) const + { + int nLength = CThisSimpleString::GetLength(); + + if (iFirst < 0) + iFirst = 0; + if (iFirst > nLength) + iFirst = nLength; + + return CStringT(CThisSimpleString::GetString() + iFirst, nLength - iFirst); + } + + CStringT Left(int nCount) const + { + int nLength = CThisSimpleString::GetLength(); + + if (nCount < 0) + nCount = 0; + if (nCount > nLength) + nCount = nLength; + + return CStringT(CThisSimpleString::GetString(), nCount); + } + + CStringT Right(int nCount) const + { + int nLength = CThisSimpleString::GetLength(); + + if (nCount < 0) + nCount = 0; + if (nCount > nLength) + nCount = nLength; + + return CStringT(CThisSimpleString::GetString() + nLength - nCount, nCount); + } + + + //void __cdecl Format(UINT nFormatID, ...) + //{ + // va_list args; + // va_start(args, dwMessageId); + // CStringT formatString; + // formatString.LoadString(?????); + // FormatV(formatString, args); + // va_end(args); + //} + + void __cdecl Format(PCXSTR pszFormat, ...) + { + va_list args; + va_start(args, pszFormat); + FormatV(pszFormat, args); + va_end(args); + } + + void FormatV(PCXSTR pszFormat, va_list args) + { + int nLength = StringTraits::FormatV(NULL, pszFormat, args); + + PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength); + StringTraits::FormatV(pszBuffer, pszFormat, args); + CThisSimpleString::ReleaseBufferSetLength(nLength); + } + + + int Replace(PCXSTR pszOld, PCXSTR pszNew) + { + PCXSTR pszString = CThisSimpleString::GetString(); + + const int nLength = CThisSimpleString::GetLength(); + const int nOldLen = StringTraits::GetBaseTypeLength(pszOld); + const int nNewLen = StringTraits::GetBaseTypeLength(pszNew); + const int nDiff = nNewLen - nOldLen; + int nResultLength = nLength; + + PCXSTR pszFound; + while ((pszFound = StringTraits::FindString(pszString, pszOld))) + { + nResultLength += nDiff; + pszString = pszFound + nOldLen; + } + + if (pszString == CThisSimpleString::GetString()) + return 0; + + PXSTR pszResult = CThisSimpleString::GetBuffer(nResultLength); + PXSTR pszNext; + int nCount = 0, nRemaining = nLength; + while (nRemaining && (pszNext = StringTraits::FindString(pszResult, pszOld))) + { + nRemaining -= (pszNext - pszResult); + nRemaining -= nOldLen; + if (nRemaining > 0) + CThisSimpleString::CopyCharsOverlapped(pszNext + nNewLen, nRemaining + 1, pszNext + nOldLen, nRemaining + 1); + CThisSimpleString::CopyCharsOverlapped(pszNext, nNewLen, pszNew, nNewLen); + pszResult = pszNext + nNewLen; + nCount++; + } + + CThisSimpleString::ReleaseBufferSetLength(nResultLength); + + return nCount; + } + + int Replace(XCHAR chOld, XCHAR chNew) + { + PCXSTR pszString = CThisSimpleString::GetString(); + PXSTR pszFirst = StringTraits::FindChar(pszString, chOld); + if (!pszFirst) + return 0; + + int nLength = CThisSimpleString::GetLength(); + int nCount = 0; + + PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength); + pszFirst = pszBuffer + (pszFirst - pszString); + do { + *pszFirst = chNew; + ++nCount; + } while ((pszFirst = StringTraits::FindChar(pszFirst + 1, chOld))); + + CThisSimpleString::ReleaseBufferSetLength(nLength); + return nCount; + } + + + static PCXSTR DefaultTrimChars() + { + static XCHAR str[] = { ' ', '\t', '\r', '\n', 0 }; + return str; + } + + + CStringT& TrimLeft() + { + return TrimLeft(DefaultTrimChars()); + } + + CStringT& TrimLeft(XCHAR chTarget) + { + XCHAR str[2] = { chTarget, 0 }; + return TrimLeft(str); + } + + CStringT& TrimLeft(PCXSTR pszTargets) + { + int nLength = CThisSimpleString::GetLength(); + PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength); + int nCount = 0; + + while (nCount < nLength && StringTraits::FindChar(pszTargets, pszBuffer[nCount])) + nCount++; + + if (nCount > 0) + { + CThisSimpleString::CopyCharsOverlapped(pszBuffer, nLength - nCount, pszBuffer + nCount, nLength - nCount); + nLength -= nCount; + } + CThisSimpleString::ReleaseBufferSetLength(nLength); + + return *this; + } + + + CStringT& TrimRight() + { + return TrimRight(DefaultTrimChars()); + } + + CStringT& TrimRight(XCHAR chTarget) + { + XCHAR str[2] = { chTarget, 0 }; + return TrimRight(str); + } + + CStringT& TrimRight(PCXSTR pszTargets) + { + int nLength = CThisSimpleString::GetLength(); + PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength); + + while (nLength > 0 && StringTraits::FindChar(pszTargets, pszBuffer[nLength-1])) + nLength--; + + CThisSimpleString::ReleaseBufferSetLength(nLength); + + return *this; + } + + + CStringT& Trim() + { + return Trim(DefaultTrimChars()); + } + + CStringT& Trim(XCHAR chTarget) + { + XCHAR str[2] = { chTarget, 0 }; + return Trim(str); + } + + CStringT& Trim(PCXSTR pszTargets) + { + return TrimRight(pszTargets).TrimLeft(pszTargets); + } + + }; } //namespace ATL diff --git a/rostests/apitests/atl/CMakeLists.txt b/rostests/apitests/atl/CMakeLists.txt index 591c7ef31e0..f13b9ccbcf7 100644 --- a/rostests/apitests/atl/CMakeLists.txt +++ b/rostests/apitests/atl/CMakeLists.txt @@ -1,11 +1,12 @@ -set_cpp(WITH_RUNTIME) +set_cpp(WITH_RUNTIME WITH_EXCEPTIONS) include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/atl) add_executable(atl_apitest CComBSTR.cpp CComHeapPtr.cpp + CString.cpp testlist.c atl_apitest.rc) diff --git a/rostests/apitests/atl/CString.cpp b/rostests/apitests/atl/CString.cpp new file mode 100644 index 00000000000..cf8cba39fcd --- /dev/null +++ b/rostests/apitests/atl/CString.cpp @@ -0,0 +1,176 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test for CString + * PROGRAMMER: Mark Jansen + */ + +#include +#include + + +struct traits_test +{ + const char* strA; + const wchar_t* strW; + int str_len; + int exp_1, exp_2, exp_3, exp_4; +}; + +traits_test g_Tests[] = { + // inputs outputs + { NULL, NULL, 0, 0, 0, -1, 0 }, + { NULL, NULL, -1, 0, -1, -1, 0 }, + { NULL, NULL, 1, 0, 1, -1, 0 }, + + { "", L"", 0, 0, 0, 0, 0 }, + { "", L"", -1, 0, -1, 0, 1 }, + { "", L"", 1, 0, 1, 0, 1 }, + + { "AAABBB", L"AAABBB", 0, 6, 0, 6, 0 }, + { "AAABBB", L"AAABBB", 3, 6, 3, 6, 3 }, + { "AAABBB", L"AAABBB", -1, 6, -1, 6, 7 }, +}; + +static void test_basetypes() +{ + int len; + char bufA[10]; + wchar_t bufW[10]; + + for (size_t n = 0; n < _countof(g_Tests); ++n) + { + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strA); + ok(len == g_Tests[n].exp_1, "Expected len to be %i, was %i for %u (A)\n", g_Tests[n].exp_1, len, n); + + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strA, g_Tests[n].str_len); + ok(len == g_Tests[n].exp_2, "Expected len to be %i, was %i for %u (A,len)\n", g_Tests[n].exp_2, len, n); + + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strW); + ok(len == g_Tests[n].exp_3, "Expected len to be %i, was %i for %u (W)\n", g_Tests[n].exp_3, len, n); + + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strW, g_Tests[n].str_len); + ok(len == g_Tests[n].exp_4, "Expected len to be %i, was %i for %u (W,len)\n", g_Tests[n].exp_4, len, n); + + if (g_Tests[n].strA && g_Tests[n].strW) + { + memset(bufA, 'x', sizeof(bufA)); + ChTraitsCRT::ConvertToBaseType(bufA, g_Tests[n].exp_1+1, g_Tests[n].strA); + char ch = bufA[g_Tests[n].exp_1]; + ok(ch == '\0', "Expected %i to be \\0, was: %c (%i) for %u\n", g_Tests[n].exp_1, ch, (int)ch, n); + ok(!strcmp(bufA, g_Tests[n].strA), "Expected bufA to be %s, was: %s for %u\n", g_Tests[n].strA, bufA, n); + ch = bufA[g_Tests[n].exp_1+1]; + ok(ch == 'x', "Expected %i to be 'x', was: %c (%i) for %u\n", g_Tests[n].exp_1+1, ch, (int)ch, n); + } + + if (g_Tests[n].strA && g_Tests[n].strW) + { + memset(bufA, 'x', sizeof(bufA)); + ChTraitsCRT::ConvertToBaseType(bufA, g_Tests[n].exp_1+1, g_Tests[n].strW); + char ch = bufA[g_Tests[n].exp_1]; + ok(ch == '\0', "Expected %i to be \\0, was: %c (%i) for %u\n", g_Tests[n].exp_1, ch, (int)ch, n); + ok(!strcmp(bufA, g_Tests[n].strA), "Expected bufA to be %s, was: %s for %u\n", g_Tests[n].strA, bufA, n); + ch = bufA[g_Tests[n].exp_1+1]; + ok(ch == 'x', "Expected %i to be 'x', was: %c (%i) for %u\n", g_Tests[n].exp_1+1, ch, (int)ch, n); + } + + // wchar_t --> please note, swapped the expectations from 2 and 4 ! + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strA); + ok(len == g_Tests[n].exp_1, "Expected len to be %i, was %i for %u (A)\n", g_Tests[n].exp_1, len, n); + + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strA, g_Tests[n].str_len); + ok(len == g_Tests[n].exp_4, "Expected len to be %i, was %i for %u (A,len)\n", g_Tests[n].exp_4, len, n); + + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strW); + ok(len == g_Tests[n].exp_3, "Expected len to be %i, was %i for %u (W)\n", g_Tests[n].exp_3, len, n); + + len = ChTraitsCRT::GetBaseTypeLength(g_Tests[n].strW, g_Tests[n].str_len); + ok(len == g_Tests[n].exp_2, "Expected len to be %i, was %i for %u (W,len)\n", g_Tests[n].exp_2, len, n); + + if (g_Tests[n].strA && g_Tests[n].strW) + { + memset(bufW, 'x', sizeof(bufW)); + ChTraitsCRT::ConvertToBaseType(bufW, g_Tests[n].exp_1+1, g_Tests[n].strA); + wchar_t ch = bufW[g_Tests[n].exp_1]; + ok(ch == L'\0', "Expected %i to be \\0, was: %c (%i) for %u\n", g_Tests[n].exp_1, ch, (int)ch, n); + ok(!wcscmp(bufW, g_Tests[n].strW), "Expected bufW to be %s, was: %s for %u\n", wine_dbgstr_w(g_Tests[n].strW), wine_dbgstr_w(bufW), n); + ch = bufW[g_Tests[n].exp_1+1]; + ok(ch == 30840, "Expected %i to be %i for %u\n", g_Tests[n].exp_1+1, (int)ch, n); + } + + if (g_Tests[n].strA && g_Tests[n].strW) + { + memset(bufW, 'x', sizeof(bufW)); + ChTraitsCRT::ConvertToBaseType(bufW, g_Tests[n].exp_1+1, g_Tests[n].strW); + wchar_t ch = bufW[g_Tests[n].exp_1]; + ok(ch == '\0', "Expected %i to be \\0, was: %c (%i) for %u\n", g_Tests[n].exp_1, ch, (int)ch, n); + ok(!wcscmp(bufW, g_Tests[n].strW), "Expected bufW to be %s, was: %s for %u\n", wine_dbgstr_w(g_Tests[n].strW), wine_dbgstr_w(bufW), n); + ch = bufW[g_Tests[n].exp_1+1]; + ok(ch == 30840, "Expected %i to be %i for %u\n", g_Tests[n].exp_1+1, (int)ch, n); + } + } +} + +// Allocation strategy seems to differ a bit between us and MS's atl. +// if someone cares enough to find out why, feel free to change the macro below. +#define ALLOC_EXPECT(a, b) b + + +#undef ok +#undef _T + +#define TEST_NAMEX(name) void test_##name##W() +#define CStringX CStringW +#define _X(x) L ## x +#define XCHAR WCHAR +#define dbgstrx(x) wine_dbgstr_w(x) +#define ok ok_("CStringW:\n" __FILE__, __LINE__) +#include "CString.inl" + + +#undef CStringX +#undef TEST_NAMEX +#undef _X +#undef XCHAR +#undef dbgstrx +#undef ok + +#define TEST_NAMEX(name) void test_##name##A() +#define CStringX CStringA +#define _X(x) x +#define XCHAR CHAR +#define dbgstrx(x) (const char*)x +#define ok ok_("CStringA:\n" __FILE__, __LINE__) +#include "CString.inl" + + +START_TEST(CString) +{ + test_basetypes(); + + if ((ALLOC_EXPECT(1, 2)) == 2) + { + skip("Ignoring real GetAllocLength() lenght\n"); + } + + test_operators_initW(); + test_operators_initA(); + + test_compareW(); + test_compareA(); + + test_findW(); + test_findA(); + + test_formatW(); + test_formatA(); + + test_substrW(); + test_substrA(); + + test_replaceW(); + test_replaceA(); + + test_trimW(); + test_trimA(); +} diff --git a/rostests/apitests/atl/CString.inl b/rostests/apitests/atl/CString.inl new file mode 100644 index 00000000000..40176d66b51 --- /dev/null +++ b/rostests/apitests/atl/CString.inl @@ -0,0 +1,358 @@ + +TEST_NAMEX(operators_init) +{ + CStringX test; + ok(test.IsEmpty() == true, "Expected test to be empty\n"); + ok(test.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == 0, "Expected GetAllocLength() to be 0, was: %i\n", test.GetAllocLength()); + + // Operator + const XCHAR* cstring = (const XCHAR*)test; + ok(cstring != NULL, "Expected a valid pointer\n"); + if (cstring) + { + ok(cstring[0] == '\0', "Expected \\0, got: %c (%i)\n", cstring[0], (int)cstring[0]); + } + + CStringX first(_X("First ")); + ok(first.IsEmpty() != true, "Expected first to not be empty\n"); + ok(first.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", first.GetLength()); + ok(first.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(7, 6), first.GetAllocLength()); + + CStringX second(_X("Second")); + ok(second.IsEmpty() != true, "Expected second to not be empty\n"); + ok(second.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", second.GetLength()); + ok(second.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(7, 6), second.GetAllocLength()); + + test = first; + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(7, 6), test.GetAllocLength()); + + test.Empty(); + ok(test.IsEmpty() == true, "Expected test to be empty\n"); + ok(test.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == 0, "Expected GetAllocLength() to be 0, was: %i\n", test.GetAllocLength()); + + test = _X("First "); + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(7, 6), test.GetAllocLength()); + + test += second; + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 12, "Expected GetLength() to be 12, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(15, 12), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(15, 12), test.GetAllocLength()); + + test = first + second; + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 12, "Expected GetLength() to be 12, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(15, 12), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(15, 12), test.GetAllocLength()); + + test = first + second + _X("."); + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 13, "Expected GetLength() to be 13, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(15, 18), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(15, 18), test.GetAllocLength()); + + CStringX test2(test); + ok(test2.IsEmpty() != true, "Expected test2 to not be empty\n"); + ok(test2.GetLength() == 13, "Expected GetLength() to be 13, was: %i\n", test2.GetLength()); + ok(test2.GetAllocLength() == ALLOC_EXPECT(15, 18), "Expected GetAllocLength() to be %i, was: %i\n", ALLOC_EXPECT(15, 18), test2.GetAllocLength()); + + // Clear it again + test.Empty(); + ok(test.IsEmpty() == true, "Expected test to be empty\n"); + ok(test.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == 0, "Expected GetAllocLength() to be 0, was: %i\n", test.GetAllocLength()); + + // Assign string + test = "First "; + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be 7, was: %i\n", test.GetAllocLength()); + + CStringA testA = test; + ok(testA.IsEmpty() != true, "Expected testA to not be empty\n"); + ok(testA.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", testA.GetLength()); + ok(testA.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be 7, was: %i\n", testA.GetAllocLength()); + + CStringW testW = test; + ok(testW.IsEmpty() != true, "Expected testW to not be empty\n"); + ok(testW.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", testW.GetLength()); + ok(testW.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be 7, was: %i\n", testW.GetAllocLength()); + + // Assign wstring + test = L"First "; + ok(test.IsEmpty() != true, "Expected test to not be empty\n"); + ok(test.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", test.GetLength()); + ok(test.GetAllocLength() == ALLOC_EXPECT(7, 6), "Expected GetAllocLength() to be 7, was: %i\n", test.GetAllocLength()); +} + + +TEST_NAMEX(compare) +{ + CStringX s1, s2; + s1 = s2 = _X("some text 1!"); + + int c = s1.Compare(s2); + ok(c == 0, "Expected c to be 1, was: %i\n", c); + + c = s1.Compare(_X("r")); + ok(c == 1, "Expected c to be 1, was: %i\n", c); + + c = s1.Compare(_X("s")); + ok(c == 1, "Expected c to be 1, was: %i\n", c); + + c = s1.Compare(_X("t")); + ok(c == -1, "Expected c to be -1, was: %i\n", c); + + c = s1.Compare(_X("R")); + ok(c == 1, "Expected c to be 1, was: %i\n", c); + + c = s1.Compare(_X("S")); + ok(c == 1, "Expected c to be 1, was: %i\n", c); + + c = s1.Compare(_X("T")); + ok(c == 1, "Expected c to be 1, was: %i\n", c); + + ok(s1 == s2, "Expected s1 and s2 to be equal: '%s' == '%s'\n", dbgstrx(s1), dbgstrx(s2)); + + s1.MakeUpper(); // Does not modify s2 + ok(s1[0] == _X('S'), "Expected s1[0] to be S, was: %c\n", (char)s1[0]); + ok(s2[0] == _X('s'), "Expected s2[0] to be s, was: %c\n", (char)s2[0]); + + ok(s1 == _X("SOME TEXT 1!"), "Expected s1 to be 'SOME TEXT 1!', was: %s\n", dbgstrx(s1)); + + CStringX s3 = s1.MakeLower(); + ok(s1 == _X("some text 1!"), "Expected s1 to be 'some text 1!', was: %s\n", dbgstrx(s1)); + ok(s1 == s3, "Expected s1 and s3 to be equal: '%s' == '%s'\n", dbgstrx(s1), dbgstrx(s3)); +} + + +TEST_NAMEX(find) +{ + CStringX s(_X("adbcdef")); + int n = s.Find(_X('c')); + ok(n == 3, "Expected n to be 2, was %i\n", n); + n = s.Find(_X("de")); + ok(n == 4, "Expected n to be 4, was %i\n", n); + + CStringX str(_X("The waves are still")); + n = str.Find(_X('e'), 5); + ok(n == 7, "Expected n to be 7, was %i\n", n); + n = str.Find(_X('e'), 7); + ok(n == 7, "Expected n to be 7, was %i\n", n); + + s = _X("abcdefGHIJKLMNop"); + n = s.FindOneOf(_X("Nd")); + ok(n == 3, "Expected n to be 3, was %i\n", n); + n = s.FindOneOf(_X("Np")); + ok(n == 13, "Expected n to be 13, was %i\n", n); + + n = str.ReverseFind(_X('l')); + ok(n == 18, "Expected n to be 18, was %i\n", n); + + n = str.ReverseFind(_X('e')); + ok(n == 12, "Expected n to be 12, was %i\n", n); +} + + +void WriteString(const XCHAR* pstrFormat, ...) +{ + CStringX str; + + va_list args; + va_start(args, pstrFormat); + + str.FormatV(pstrFormat, args); + va_end(args); + + ok(str == _X("10e 1351l"), "Expected str to be '10e 1351l', was: %s\n", dbgstrx(str)); +} + +TEST_NAMEX(format) +{ + CStringX str; + + str.Format(_X("FP: %.2f"), 12345.12345); + ok(str == _X("FP: 12345.12"), "Expected str to be 'FP: 12345.12', was: %s\n", dbgstrx(str)); + + str.Format(_X("int: %.6d"), 35); + ok(str == _X("int: 000035"), "Expected str to be 'int: 000035', was: %s\n", dbgstrx(str)); + + WriteString(_X("%de %dl"), 10, 1351); +} + + +TEST_NAMEX(substr) +{ + CStringX s(_X("abcdef")); + + CStringX m = s.Mid(2, 3); + ok(m == _X("cde"), "Expected str to be 'cde', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 3, "Expected GetLength() to be 3, was: %i\n", m.GetLength()); + + m = s.Mid(-5, 3); + ok(m == _X("abc"), "Expected str to be 'abc', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 3, "Expected GetLength() to be 3, was: %i\n", m.GetLength()); + + m = s.Mid(3, 20); + ok(m == _X("def"), "Expected str to be 'def', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 3, "Expected GetLength() to be 3, was: %i\n", m.GetLength()); + + m = s.Mid(3, -1); + ok(m == _X(""), "Expected str to be '', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", m.GetLength()); + + m = s.Mid(2); + ok(m == _X("cdef"), "Expected str to be 'cdef', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 4, "Expected GetLength() to be 4, was: %i\n", m.GetLength()); + + m = s.Mid(-3); + ok(m == _X("abcdef"), "Expected str to be 'abcdef', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", m.GetLength()); + + m = s.Mid(20); + ok(m == _X(""), "Expected str to be '', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", m.GetLength()); + + m = s.Left(2); + ok(m == _X("ab"), "Expected str to be 'ab', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 2, "Expected GetLength() to be 2, was: %i\n", m.GetLength()); + + m = s.Left(40); + ok(m == _X("abcdef"), "Expected str to be 'abcdef', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", m.GetLength()); + + m = s.Left(-10); + ok(m == _X(""), "Expected str to be '', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", m.GetLength()); + + m = s.Right(2); + ok(m == _X("ef"), "Expected str to be 'ef', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 2, "Expected GetLength() to be 2, was: %i\n", m.GetLength()); + + m = s.Right(-40); + ok(m == _X(""), "Expected str to be '', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", m.GetLength()); + + m = s.Right(99); + ok(m == _X("abcdef"), "Expected str to be 'abcdef', was: %s\n", dbgstrx(m)); + ok(m.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", m.GetLength()); +} + + +TEST_NAMEX(replace) +{ + CStringX str(_X("abcde")); + int n = str.Replace(_X("b"), _X("bx")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(str == _X("abxcde"), "Expected str to be 'abxcde', was: %s\n", dbgstrx(str)); + ok(str.GetLength() == 6, "Expected GetLength() to be 6, was: %i\n", str.GetLength()); + + CStringX strBang(_X("The quick brown fox is lazy today of all days")); + + n = strBang.Replace(_X("is"), _X("was")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(strBang == _X("The quick brown fox was lazy today of all days"), + "Expected str to be 'The quick brown fox was lazy today of all days', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 46, "Expected GetLength() to be 46, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X("is"), _X("was")); + ok(n == 0, "Expected n to be 0, was %i\n", n); + ok(strBang == _X("The quick brown fox was lazy today of all days"), + "Expected str to be 'The quick brown fox was lazy today of all days', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 46, "Expected GetLength() to be 46, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X('o'), _X('0')); + ok(n == 4, "Expected n to be 4, was %i\n", n); + ok(strBang == _X("The quick br0wn f0x was lazy t0day 0f all days"), + "Expected str to be 'The quick br0wn f0x was lazy t0day 0f all days', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 46, "Expected GetLength() to be 46, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X('o'), _X('0')); + ok(n == 0, "Expected n to be 0, was %i\n", n); + ok(strBang == _X("The quick br0wn f0x was lazy t0day 0f all days"), + "Expected str to be 'The quick br0wn f0x was lazy t0day 0f all days', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 46, "Expected GetLength() to be 46, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X("y "), _X("y, ")); + ok(n == 2, "Expected n to be 2, was %i\n", n); + ok(strBang == _X("The quick br0wn f0x was lazy, t0day, 0f all days"), + "Expected str to be 'The quick br0wn f0x was lazy, t0day, 0f all days', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 48, "Expected GetLength() to be 48, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X(", 0f all days"), _X("")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(strBang == _X("The quick br0wn f0x was lazy, t0day"), + "Expected str to be 'The quick br0wn f0x was lazy, t0day', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 35, "Expected GetLength() to be 35, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X(" lazy, "), _X(" fast ")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(strBang == _X("The quick br0wn f0x was fast t0day"), + "Expected str to be 'The quick br0wn f0x was fast t0day', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 34, "Expected GetLength() to be 34, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X("The "), _X("")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(strBang == _X("quick br0wn f0x was fast t0day"), + "Expected str to be 'quick br0wn f0x was fast t0day', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 30, "Expected GetLength() to be 30, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X(" t0day"), _X("")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(strBang == _X("quick br0wn f0x was fast"), + "Expected str to be 'quick br0wn f0x was fast', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 24, "Expected GetLength() to be 24, was: %i\n", strBang.GetLength()); + + n = strBang.Replace(_X("quick"), _X("The fast, quick")); + ok(n == 1, "Expected n to be 1, was %i\n", n); + ok(strBang == _X("The fast, quick br0wn f0x was fast"), + "Expected str to be 'The fast, quick br0wn f0x was fast', was: %s\n", dbgstrx(strBang)); + ok(strBang.GetLength() == 34, "Expected GetLength() to be 34, was: %i\n", strBang.GetLength()); +} + + +TEST_NAMEX(trim) +{ + CStringX str; + str = _X(" \t\r\n******Trim some text!?!?!?!?!\n\r\t "); + + str.TrimLeft(); + ok(str == _X("******Trim some text!?!?!?!?!\n\r\t "), "Expected str to be '******Trim some text!?!?!?!?!\n\r\t ', was: %s\n", dbgstrx(str)); + ok(str.GetLength() == 33, "Expected GetLength() to be 33, was: %i\n", str.GetLength()); + + str.TrimRight(); + ok(str == _X("******Trim some text!?!?!?!?!"), "Expected str to be '******Trim some text!?!?!?!?!', was: %s\n", dbgstrx(str)); + ok(str.GetLength() == 29, "Expected GetLength() to be 29, was: %i\n", str.GetLength()); + + CStringX str2 = str.Trim(_X("?!*")); + ok(str2 == _X("Trim some text"), "Expected str to be 'Trim some text', was: %s\n", dbgstrx(str2)); + ok(str2.GetLength() == 14, "Expected GetLength() to be 14, was: %i\n", str2.GetLength()); + + str = _X("\t\t ****Trim some text!"); + str2 = str.TrimLeft(_X("\t *")); + ok(str2 == _X("Trim some text!"), "Expected str to be 'Trim some text!', was: %s\n", dbgstrx(str2)); + ok(str2.GetLength() == 15, "Expected GetLength() to be 15, was: %i\n", str2.GetLength()); + + str = _X("Trim some text!?!?!?!?!"); + str2 = str.TrimRight(_X("?!")); + ok(str2 == _X("Trim some text"), "Expected str to be 'Trim some text', was: %s\n", dbgstrx(str2)); + ok(str2.GetLength() == 14, "Expected GetLength() to be 14, was: %i\n", str2.GetLength()); + + str = _X("\t\t\t\t\t"); + str2 = str.TrimLeft(); + ok(str2 == _X(""), "Expected str2 to be '', was: %s\n", dbgstrx(str2)); + ok(str2.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", str2.GetLength()); + + str = _X("\t\t\t\t\t"); + str2 = str.TrimRight(); + ok(str2 == _X(""), "Expected str2 to be '', was: %s\n", dbgstrx(str2)); + ok(str2.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", str2.GetLength()); + + str = _X("\t\t\t\t\t"); + str2 = str.Trim(); + ok(str2 == _X(""), "Expected str2 to be '', was: %s\n", dbgstrx(str2)); + ok(str2.GetLength() == 0, "Expected GetLength() to be 0, was: %i\n", str2.GetLength()); +} diff --git a/rostests/apitests/atl/testlist.c b/rostests/apitests/atl/testlist.c index 5712c65c9e5..6be7f35aff1 100644 --- a/rostests/apitests/atl/testlist.c +++ b/rostests/apitests/atl/testlist.c @@ -3,10 +3,12 @@ extern void func_CComBSTR(void); extern void func_CComHeapPtr(void); +extern void func_CString(void); const struct test winetest_testlist[] = { { "CComBSTR", func_CComBSTR }, { "CComHeapPtr", func_CComHeapPtr }, + { "CString", func_CString }, { 0, 0 } }; -- 2.17.1