diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/testcase/tstIprtMiniString.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/testcase/tstIprtMiniString.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstIprtMiniString.cpp | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstIprtMiniString.cpp b/src/VBox/Runtime/testcase/tstIprtMiniString.cpp new file mode 100644 index 00000000..8e70bcbb --- /dev/null +++ b/src/VBox/Runtime/testcase/tstIprtMiniString.cpp @@ -0,0 +1,656 @@ +/* $Id: tstIprtMiniString.cpp $ */ +/** @file + * IPRT Testcase - RTCString. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/cpp/ministring.h> + +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/test.h> +#include <iprt/uni.h> + + +static void test1Hlp1(const char *pszExpect, const char *pszFormat, ...) +{ +#if 0 + va_list va; + va_start(va, pszFormat); + RTCString strTst(pszFormat, va); + va_end(va); + RTTESTI_CHECK_MSG(strTst.equals(pszExpect), ("strTst='%s' expected='%s'\n", strTst.c_str(), pszExpect)); +#else + RT_NOREF_PV(pszExpect); + RT_NOREF_PV(pszFormat); +#endif +} + +static void test1(RTTEST hTest) +{ + RTTestSub(hTest, "Basics"); + +#define CHECK(expr) RTTESTI_CHECK(expr) +#define CHECK_DUMP(expr, value) \ + do { \ + if (!(expr)) \ + RTTestFailed(hTest, "%d: FAILED %s, got \"%s\"", __LINE__, #expr, value); \ + } while (0) + +#define CHECK_DUMP_I(expr) \ + do { \ + if (!(expr)) \ + RTTestFailed(hTest, "%d: FAILED %s, got \"%d\"", __LINE__, #expr, expr); \ + } while (0) +#define CHECK_EQUAL(Str, szExpect) \ + do { \ + if (!(Str).equals(szExpect)) \ + RTTestIFailed("line %u: expected \"%s\" got \"%s\"", __LINE__, szExpect, (Str).c_str()); \ + } while (0) +#define CHECK_EQUAL_I(iRes, iExpect) \ + do { \ + if (iRes != iExpect) \ + RTTestIFailed("line %u: expected \"%zd\" got \"%zd\"", __LINE__, iExpect, iRes); \ + } while (0) + + RTCString empty; + CHECK(empty.length() == 0); + CHECK(empty.capacity() == 0); + + empty.reserve(1); + CHECK(empty.length() == 0); + CHECK(empty.capacity() == 1); + char *pszEmpty = empty.mutableRaw(); + CHECK(pszEmpty != NULL); + + RTCString sixbytes("12345"); + CHECK(sixbytes.length() == 5); + CHECK(sixbytes.capacity() == 6); + + sixbytes.append(RTCString("678")); + CHECK(sixbytes.length() == 8); + CHECK(sixbytes.capacity() >= 9); + + sixbytes.append("9a"); + CHECK(sixbytes.length() == 10); + CHECK(sixbytes.capacity() >= 11); + + char *psz = sixbytes.mutableRaw(); + // 123456789a + // ^ + // 0123456 + psz[6] = '\0'; + sixbytes.jolt(); + CHECK(sixbytes.length() == 6); + CHECK(sixbytes.capacity() == 7); + + RTCString morebytes("tobereplaced"); + morebytes = "newstring "; + morebytes.append(sixbytes); + + CHECK_DUMP(morebytes == "newstring 123456", morebytes.c_str()); + + RTCString third(morebytes); + third.reserve(100 * 1024); // 100 KB + CHECK_DUMP(third == "newstring 123456", morebytes.c_str() ); + CHECK(third.capacity() == 100 * 1024); + CHECK(third.length() == morebytes.length()); // must not have changed + + RTCString copy1(morebytes); + RTCString copy2 = morebytes; + CHECK(copy1 == copy2); + + copy1 = NULL; + CHECK(copy1.length() == 0); + + copy1 = ""; + CHECK(copy1.length() == 0); + + CHECK(RTCString("abc") < RTCString("def")); + CHECK(RTCString("") < RTCString("def")); + CHECK(RTCString("abc") > RTCString("")); + CHECK(RTCString("abc") != RTCString("def")); + CHECK_DUMP_I(RTCString("def") > RTCString("abc")); + CHECK(RTCString("abc") == RTCString("abc")); + CHECK(RTCString("").compare("") == 0); + CHECK(RTCString("").compare(NULL) == 0); + CHECK(RTCString("").compare("a") < 0); + CHECK(RTCString("a").compare("") > 0); + CHECK(RTCString("a").compare(NULL) > 0); + + CHECK(RTCString("abc") < "def"); + CHECK(RTCString("abc") != "def"); + CHECK_DUMP_I(RTCString("def") > "abc"); + CHECK(RTCString("abc") == "abc"); + + CHECK(RTCString("abc").equals("abc")); + CHECK(!RTCString("abc").equals("def")); + CHECK(RTCString("abc").equalsIgnoreCase("Abc")); + CHECK(RTCString("abc").equalsIgnoreCase("ABc")); + CHECK(RTCString("abc").equalsIgnoreCase("ABC")); + CHECK(!RTCString("abc").equalsIgnoreCase("dBC")); + CHECK(RTCString("").equals("")); + CHECK(RTCString("").equals(NULL)); + CHECK(!RTCString("").equals("a")); + CHECK(!RTCString("a").equals("")); + CHECK(!RTCString("a").equals(NULL)); + CHECK(RTCString("").equalsIgnoreCase("")); + CHECK(RTCString("").equalsIgnoreCase(NULL)); + CHECK(!RTCString("").equalsIgnoreCase("a")); + CHECK(!RTCString("a").equalsIgnoreCase("")); + + copy2.setNull(); + for (int i = 0; i < 100; ++i) + { + copy2.reserve(50); // should be ignored after 50 loops + copy2.append("1"); + } + CHECK(copy2.length() == 100); + + copy2.setNull(); + for (int i = 0; i < 100; ++i) + { + copy2.reserve(50); // should be ignored after 50 loops + copy2.append('1'); + } + CHECK(copy2.length() == 100); + + /* printf */ + RTCString StrFmt; + CHECK(StrFmt.printf("%s-%s-%d", "abc", "def", 42).equals("abc-def-42")); + test1Hlp1("abc-42-def", "%s-%d-%s", "abc", 42, "def"); + test1Hlp1("", ""); + test1Hlp1("1", "1"); + test1Hlp1("foobar", "%s", "foobar"); + + /* substring constructors */ + RTCString SubStr1("", (size_t)0); + CHECK_EQUAL(SubStr1, ""); + + RTCString SubStr2("abcdef", 2); + CHECK_EQUAL(SubStr2, "ab"); + + RTCString SubStr3("abcdef", 1); + CHECK_EQUAL(SubStr3, "a"); + + RTCString SubStr4("abcdef", 6); + CHECK_EQUAL(SubStr4, "abcdef"); + + RTCString SubStr5("abcdef", 7); + CHECK_EQUAL(SubStr5, "abcdef"); + + + RTCString SubStrBase("abcdef"); + + RTCString SubStr10(SubStrBase, 0); + CHECK_EQUAL(SubStr10, "abcdef"); + + RTCString SubStr11(SubStrBase, 1); + CHECK_EQUAL(SubStr11, "bcdef"); + + RTCString SubStr12(SubStrBase, 1, 1); + CHECK_EQUAL(SubStr12, "b"); + + RTCString SubStr13(SubStrBase, 2, 3); + CHECK_EQUAL(SubStr13, "cde"); + + RTCString SubStr14(SubStrBase, 2, 4); + CHECK_EQUAL(SubStr14, "cdef"); + + RTCString SubStr15(SubStrBase, 2, 5); + CHECK_EQUAL(SubStr15, "cdef"); + + /* substr() and substrCP() functions */ + RTCString strTest(""); + CHECK_EQUAL(strTest.substr(0), ""); + CHECK_EQUAL(strTest.substrCP(0), ""); + CHECK_EQUAL(strTest.substr(1), ""); + CHECK_EQUAL(strTest.substrCP(1), ""); + + /* now let's have some non-ASCII to chew on */ + strTest = "abcdefßäbcdef"; + // 13 codepoints, but 15 bytes (excluding null terminator); + // "ß" and "ä" consume two bytes each + CHECK_EQUAL(strTest.substr(0), strTest.c_str()); + CHECK_EQUAL(strTest.substrCP(0), strTest.c_str()); + + CHECK_EQUAL(strTest.substr(2), "cdefßäbcdef"); + CHECK_EQUAL(strTest.substrCP(2), "cdefßäbcdef"); + + CHECK_EQUAL(strTest.substr(2, 2), "cd"); + CHECK_EQUAL(strTest.substrCP(2, 2), "cd"); + + CHECK_EQUAL(strTest.substr(6), "ßäbcdef"); + CHECK_EQUAL(strTest.substrCP(6), "ßäbcdef"); + + CHECK_EQUAL(strTest.substr(6, 2), "ß"); // UTF-8 "ß" consumes two bytes + CHECK_EQUAL(strTest.substrCP(6, 1), "ß"); + + CHECK_EQUAL(strTest.substr(8), "äbcdef"); // UTF-8 "ß" consumes two bytes + CHECK_EQUAL(strTest.substrCP(7), "äbcdef"); + + CHECK_EQUAL(strTest.substr(8, 3), "äb"); // UTF-8 "ä" consumes two bytes + CHECK_EQUAL(strTest.substrCP(7, 2), "äb"); + + CHECK_EQUAL(strTest.substr(14, 1), "f"); + CHECK_EQUAL(strTest.substrCP(12, 1), "f"); + + CHECK_EQUAL(strTest.substr(15, 1), ""); + CHECK_EQUAL(strTest.substrCP(13, 1), ""); + + CHECK_EQUAL(strTest.substr(16, 1), ""); + CHECK_EQUAL(strTest.substrCP(15, 1), ""); + + /* and check cooperation with find() */ + size_t pos = strTest.find("ß"); + CHECK_EQUAL(strTest.substr(pos), "ßäbcdef"); + + /* check find() */ + CHECK_EQUAL_I(strTest.find("f"), 5); + CHECK_EQUAL_I(strTest.find("f", 0), 5); + CHECK_EQUAL_I(strTest.find("f", 3), 5); + CHECK_EQUAL_I(strTest.find("f", 6), 14); + CHECK_EQUAL_I(strTest.find("f", 9), 14); + CHECK_EQUAL_I(strTest.substr(pos).find("d"), 6); + + /* split */ + RTCList<RTCString> spList1 = RTCString("##abcdef##abcdef####abcdef##").split("##", RTCString::RemoveEmptyParts); + RTTESTI_CHECK(spList1.size() == 3); + for (size_t i = 0; i < spList1.size(); ++i) + RTTESTI_CHECK(spList1.at(i) == "abcdef"); + RTCList<RTCString> spList2 = RTCString("##abcdef##abcdef####abcdef##").split("##", RTCString::KeepEmptyParts); + RTTESTI_CHECK_RETV(spList2.size() == 5); + RTTESTI_CHECK(spList2.at(0) == ""); + RTTESTI_CHECK(spList2.at(1) == "abcdef"); + RTTESTI_CHECK(spList2.at(2) == "abcdef"); + RTTESTI_CHECK(spList2.at(3) == ""); + RTTESTI_CHECK(spList2.at(4) == "abcdef"); + RTCList<RTCString> spList3 = RTCString().split("##", RTCString::KeepEmptyParts); + RTTESTI_CHECK(spList3.size() == 0); + RTCList<RTCString> spList4 = RTCString().split(""); + RTTESTI_CHECK(spList4.size() == 0); + RTCList<RTCString> spList5 = RTCString("abcdef").split(""); + RTTESTI_CHECK_RETV(spList5.size() == 1); + RTTESTI_CHECK(spList5.at(0) == "abcdef"); + + /* join */ + RTCList<RTCString> jnList; + strTest = RTCString::join(jnList); + RTTESTI_CHECK(strTest == ""); + strTest = RTCString::join(jnList, "##"); + RTTESTI_CHECK(strTest == ""); + + jnList.append("abcdef"); + strTest = RTCString::join(jnList, "##"); + RTTESTI_CHECK(strTest == "abcdef"); + + jnList.append("abcdef"); + strTest = RTCString::join(jnList, ";"); + RTTESTI_CHECK(strTest == "abcdef;abcdef"); + + for (size_t i = 0; i < 3; ++i) + jnList.append("abcdef"); + strTest = RTCString::join(jnList); + RTTESTI_CHECK(strTest == "abcdefabcdefabcdefabcdefabcdef"); + strTest = RTCString::join(jnList, "##"); + RTTESTI_CHECK(strTest == "abcdef##abcdef##abcdef##abcdef##abcdef"); + + /* special constructor and assignment arguments */ + RTCString StrCtor1(""); + RTTESTI_CHECK(StrCtor1.isEmpty()); + RTTESTI_CHECK(StrCtor1.length() == 0); + + RTCString StrCtor2(NULL); + RTTESTI_CHECK(StrCtor2.isEmpty()); + RTTESTI_CHECK(StrCtor2.length() == 0); + + RTCString StrCtor1d(StrCtor1); + RTTESTI_CHECK(StrCtor1d.isEmpty()); + RTTESTI_CHECK(StrCtor1d.length() == 0); + + RTCString StrCtor2d(StrCtor2); + RTTESTI_CHECK(StrCtor2d.isEmpty()); + RTTESTI_CHECK(StrCtor2d.length() == 0); + + for (unsigned i = 0; i < 2; i++) + { + RTCString StrAssign; + if (i) StrAssign = "abcdef"; + StrAssign = (char *)NULL; + RTTESTI_CHECK(StrAssign.isEmpty()); + RTTESTI_CHECK(StrAssign.length() == 0); + + if (i) StrAssign = "abcdef"; + StrAssign = ""; + RTTESTI_CHECK(StrAssign.isEmpty()); + RTTESTI_CHECK(StrAssign.length() == 0); + + if (i) StrAssign = "abcdef"; + StrAssign = StrCtor1; + RTTESTI_CHECK(StrAssign.isEmpty()); + RTTESTI_CHECK(StrAssign.length() == 0); + + if (i) StrAssign = "abcdef"; + StrAssign = StrCtor2; + RTTESTI_CHECK(StrAssign.isEmpty()); + RTTESTI_CHECK(StrAssign.length() == 0); + } + + /* truncation */ + RTCString StrTruncate1("abcdef"); + RTTESTI_CHECK(StrTruncate1.length() == 6); + for (int i = 5; i >= 0; i--) + { + StrTruncate1.truncate(i); + RTTESTI_CHECK(StrTruncate1.length() == (size_t)i); + } + + RTCString StrTruncate2("01ßä6"); + CHECK_EQUAL(StrTruncate2, "01ßä6"); + StrTruncate2.truncate(6); + CHECK_EQUAL(StrTruncate2, "01ßä"); + StrTruncate2.truncate(5); + CHECK_EQUAL(StrTruncate2, "01ß"); + StrTruncate2.truncate(10); + CHECK_EQUAL(StrTruncate2, "01ß"); + StrTruncate2.truncate(4); + CHECK_EQUAL(StrTruncate2, "01ß"); + StrTruncate2.truncate(3); + CHECK_EQUAL(StrTruncate2, "01"); + StrTruncate2.truncate(1); + CHECK_EQUAL(StrTruncate2, "0"); + StrTruncate2.truncate(0); + CHECK_EQUAL(StrTruncate2, ""); + +#undef CHECK +#undef CHECK_DUMP +#undef CHECK_DUMP_I +#undef CHECK_EQUAL +} + + +static int mymemcmp(const char *psz1, const char *psz2, size_t cch) +{ + for (size_t off = 0; off < cch; off++) + if (psz1[off] != psz2[off]) + { + RTTestIFailed("off=%#x psz1=%.*Rhxs psz2=%.*Rhxs\n", off, + RT_MIN(cch - off, 8), &psz1[off], + RT_MIN(cch - off, 8), &psz2[off]); + return psz1[off] > psz2[off] ? 1 : -1; + } + return 0; +} + +#if 0 +/** + * Detects a few annoying unicode points with unstable case folding for UTF-8. + * + * Unicode 4.01, I think, introduces a few codepoints with lower/upper mappings + * that has a different length when encoded as UTF-8. This breaks some + * assumptions we used to make. Since it's just a handful codepoints, we'll + * detect them and ignore them here. The actual case folding functions in + * IPRT will of course deal with this in a more robust manner. + * + * @returns true if problematic, false if not. + * @param uc The codepoints. + */ +static bool isUnevenUtf8FoldingCp(RTUNICP uc) +{ + RTUNICP ucLower = RTUniCpToLower(uc); + RTUNICP ucUpper = RTUniCpToUpper(uc); + //return RTUniCpCalcUtf8Len(ucLower) != RTUniCpCalcUtf8Len(ucUpper); + return false; +} +#endif + +static void test2(RTTEST hTest) +{ + RTTestSub(hTest, "UTF-8 upper/lower encoding assumption"); + +#define CHECK_EQUAL(str1, str2) \ + do \ + { \ + RTTESTI_CHECK(strlen((str1).c_str()) == (str1).length()); \ + RTTESTI_CHECK((str1).length() == (str2).length()); \ + RTTESTI_CHECK(mymemcmp((str1).c_str(), (str2).c_str(), (str2).length() + 1) == 0); \ + } while (0) + + RTCString strTmp, strExpect; + char szDst[16]; + + /* Some simple ascii stuff. */ + strTmp = "abcdefghijklmnopqrstuvwxyz0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ;-+/\\"; + strExpect = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ;-+/\\"; + strTmp.toUpper(); + CHECK_EQUAL(strTmp, strExpect); + + strTmp.toLower(); + strExpect = "abcdefghijklmnopqrstuvwxyz0123456abcdefghijklmnopqrstuvwxyz;-+/\\"; + CHECK_EQUAL(strTmp, strExpect); + + strTmp = "abcdefghijklmnopqrstuvwxyz0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ;-+/\\"; + strTmp.toLower(); + CHECK_EQUAL(strTmp, strExpect); + + /* Collect all upper and lower case code points. */ + RTCString strLower(""); + strLower.reserve(_4M); + + RTCString strUpper(""); + strUpper.reserve(_4M); + + for (RTUNICP uc = 1; uc <= 0x10fffd; uc++) + { + /* Unicode 4.01, I think, introduced a few codepoints with lower/upper mappings + that aren't up for roundtrips and which case folding has a different UTF-8 + length. We'll just skip them here as there are very few: + - Dotless small i and dotless capital I folds into ASCII I and i. + - The small letter long s folds to ASCII S. + - Greek prosgegrammeni folds to iota, which is a letter with both upper + and lower case foldings of its own. */ +#if 0 /** @todo totally busted testcase, plz figure out how to fix. */ + if ( uc == 0x131 + || uc == 0x130 + || uc == 0x17f + || uc == 0x1fbe + ) + continue; + + if (RTUniCpIsLower(uc)) + { + RTTESTI_CHECK_MSG(uc < 0xd800 || (uc > 0xdfff && uc != 0xfffe && uc != 0xffff), ("%#x\n", uc)); + strLower.appendCodePoint(uc); + } + if (RTUniCpIsUpper(uc)) + { + RTTESTI_CHECK_MSG(uc < 0xd800 || (uc > 0xdfff && uc != 0xfffe && uc != 0xffff), ("%#x\n", uc)); + strUpper.appendCodePoint(uc); + } +#else + continue; +#endif + } + RTTESTI_CHECK(strlen(strLower.c_str()) == strLower.length()); + RTTESTI_CHECK(strlen(strUpper.c_str()) == strUpper.length()); + + /* Fold each code point in the lower case string and check that it encodes + into the same or less number of bytes. */ + size_t cch = 0; + const char *pszCur = strLower.c_str(); + RTCString strUpper2(""); + strUpper2.reserve(strLower.length() + 64); + for (;;) + { + RTUNICP ucLower; + const char * const pszPrev = pszCur; + RTTESTI_CHECK_RC_BREAK(RTStrGetCpEx(&pszCur, &ucLower), VINF_SUCCESS); + size_t const cchSrc = pszCur - pszPrev; + if (!ucLower) + break; + + RTUNICP const ucUpper = RTUniCpToUpper(ucLower); + const char *pszDstEnd = RTStrPutCp(szDst, ucUpper); + size_t const cchDst = pszDstEnd - &szDst[0]; + RTTESTI_CHECK_MSG(cchSrc >= cchDst, + ("ucLower=%#x %u bytes; ucUpper=%#x %u bytes\n", + ucLower, cchSrc, ucUpper, cchDst)); + cch += cchDst; + strUpper2.appendCodePoint(ucUpper); + + /* roundtrip stability */ + RTUNICP const ucUpper2 = RTUniCpToUpper(ucUpper); + RTTESTI_CHECK_MSG(ucUpper2 == ucUpper, ("ucUpper2=%#x ucUpper=%#x\n", ucUpper2, ucUpper)); + + RTUNICP const ucLower2 = RTUniCpToLower(ucUpper); + RTTESTI_CHECK_MSG(ucLower2 == ucLower, ("ucLower2=%#x ucLower=%#x\n", ucLower2, ucLower)); + RTUNICP const ucUpper3 = RTUniCpToUpper(ucLower2); + RTTESTI_CHECK_MSG(ucUpper3 == ucUpper, ("ucUpper3=%#x ucUpper=%#x\n", ucUpper3, ucUpper)); + + pszDstEnd = RTStrPutCp(szDst, ucLower2); + size_t const cchLower2 = pszDstEnd - &szDst[0]; + RTTESTI_CHECK_MSG(cchDst == cchLower2, + ("ucLower2=%#x %u bytes; ucUpper=%#x %u bytes; ucLower=%#x\n", + ucLower2, cchLower2, ucUpper, cchDst, ucLower)); + } + RTTESTI_CHECK(strlen(strUpper2.c_str()) == strUpper2.length()); + RTTESTI_CHECK_MSG(cch == strUpper2.length(), ("cch=%u length()=%u\n", cch, strUpper2.length())); + + /* the toUpper method shall do the same thing. */ + strTmp = strLower; CHECK_EQUAL(strTmp, strLower); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); + + /* Ditto for the upper case string. */ + cch = 0; + pszCur = strUpper.c_str(); + RTCString strLower2(""); + strLower2.reserve(strUpper.length() + 64); + for (;;) + { + RTUNICP ucUpper; + const char * const pszPrev = pszCur; + RTTESTI_CHECK_RC_BREAK(RTStrGetCpEx(&pszCur, &ucUpper), VINF_SUCCESS); + size_t const cchSrc = pszCur - pszPrev; + if (!ucUpper) + break; + + RTUNICP const ucLower = RTUniCpToLower(ucUpper); + const char *pszDstEnd = RTStrPutCp(szDst, ucLower); + size_t const cchDst = pszDstEnd - &szDst[0]; + RTTESTI_CHECK_MSG(cchSrc >= cchDst, + ("ucUpper=%#x %u bytes; ucLower=%#x %u bytes\n", + ucUpper, cchSrc, ucLower, cchDst)); + + cch += cchDst; + strLower2.appendCodePoint(ucLower); + + /* roundtrip stability */ + RTUNICP const ucLower2 = RTUniCpToLower(ucLower); + RTTESTI_CHECK_MSG(ucLower2 == ucLower, ("ucLower2=%#x ucLower=%#x\n", ucLower2, ucLower)); + + RTUNICP const ucUpper2 = RTUniCpToUpper(ucLower); + RTTESTI_CHECK_MSG(ucUpper2 == ucUpper, ("ucUpper2=%#x ucUpper=%#x\n", ucUpper2, ucUpper)); + RTUNICP const ucLower3 = RTUniCpToLower(ucUpper2); + RTTESTI_CHECK_MSG(ucLower3 == ucLower, ("ucLower3=%#x ucLower=%#x\n", ucLower3, ucLower)); + + pszDstEnd = RTStrPutCp(szDst, ucUpper2); + size_t const cchUpper2 = pszDstEnd - &szDst[0]; + RTTESTI_CHECK_MSG(cchDst == cchUpper2, + ("ucUpper2=%#x %u bytes; ucLower=%#x %u bytes\n", + ucUpper2, cchUpper2, ucLower, cchDst)); + } + RTTESTI_CHECK(strlen(strLower2.c_str()) == strLower2.length()); + RTTESTI_CHECK_MSG(cch == strLower2.length(), ("cch=%u length()=%u\n", cch, strLower2.length())); + + strTmp = strUpper; CHECK_EQUAL(strTmp, strUpper); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + + /* Checks of folding stability when nothing shall change. */ + strTmp = strUpper; CHECK_EQUAL(strTmp, strUpper); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper); + + strTmp = strUpper2; CHECK_EQUAL(strTmp, strUpper2); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); + + strTmp = strLower; CHECK_EQUAL(strTmp, strLower); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower); + + strTmp = strLower2; CHECK_EQUAL(strTmp, strLower2); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + + /* Check folding stability for roundtrips. */ + strTmp = strUpper; CHECK_EQUAL(strTmp, strUpper); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + strTmp.toUpper(); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + strTmp.toUpper(); + strTmp.toLower(); CHECK_EQUAL(strTmp, strLower2); + + strTmp = strLower; CHECK_EQUAL(strTmp, strLower); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); + strTmp.toLower(); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); + strTmp.toLower(); + strTmp.toUpper(); CHECK_EQUAL(strTmp, strUpper2); +} + + +int main() +{ + RTTEST hTest; + RTEXITCODE rcExit = RTTestInitAndCreate("tstIprtMiniString", &hTest); + if (rcExit == RTEXITCODE_SUCCESS) + { + RTTestBanner(hTest); + + test1(hTest); + test2(hTest); + + rcExit = RTTestSummaryAndDestroy(hTest); + } + return rcExit; +} + |