/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019-2023 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include #include #include using namespace Rml; TEST_CASE("StringUtilities::TrimTrailingDotZeros") { auto RunTrimTrailingDotZeros = [](String string) { StringUtilities::TrimTrailingDotZeros(string); return string; }; CHECK(RunTrimTrailingDotZeros("0.1") == "0.1"); CHECK(RunTrimTrailingDotZeros("0.10") == "0.1"); CHECK(RunTrimTrailingDotZeros("0.1000") == "0.1"); CHECK(RunTrimTrailingDotZeros("0.01") == "0.01"); CHECK(RunTrimTrailingDotZeros("0.") == "0"); CHECK(RunTrimTrailingDotZeros("5.") == "5"); CHECK(RunTrimTrailingDotZeros("5.5") == "5.5"); CHECK(RunTrimTrailingDotZeros("5.50") == "5.5"); CHECK(RunTrimTrailingDotZeros("5.501") == "5.501"); CHECK(RunTrimTrailingDotZeros("10.0") == "10"); CHECK(RunTrimTrailingDotZeros("11.0") == "11"); // Some test cases for behavior that are probably not what you want. WARN(RunTrimTrailingDotZeros("test0") == "test"); WARN(RunTrimTrailingDotZeros("1000") == "1"); WARN(RunTrimTrailingDotZeros(".") == ""); WARN(RunTrimTrailingDotZeros("0") == ""); WARN(RunTrimTrailingDotZeros(".0") == ""); WARN(RunTrimTrailingDotZeros(" 11 2121 3.00") == " 11 2121 3"); WARN(RunTrimTrailingDotZeros("11") == "11"); } TEST_CASE("StringUtilities::StartsWith") { using namespace Rml::StringUtilities; CHECK(StartsWith("abc", "abc")); CHECK(StartsWith("abc", "ab")); CHECK(StartsWith("abc", "a")); CHECK(StartsWith("abc", "")); CHECK(!StartsWith("abc", "abcd")); CHECK(!StartsWith("abc", "abd")); CHECK(!StartsWith("abc", "bbc")); CHECK(!StartsWith("abc", "bc")); CHECK(!StartsWith("abc", "x")); } TEST_CASE("StringView") { const char abc[] = "abc"; const String str_abc = abc; CHECK(StringView("abc") == StringView("abc")); CHECK(StringView("abc") == String("abc")); CHECK(StringView("abc") == "abc"); CHECK(StringView("abc") == abc); CHECK(StringView(String(abc)) == abc); CHECK(StringView(abc) == abc); CHECK(StringView(abc, abc + 3) == abc); CHECK(StringView(str_abc, 1) == "bc"); CHECK(StringView(str_abc, 1, 1) == "b"); CHECK(StringView("abcd") != abc); CHECK(StringView("ab") != abc); CHECK(StringView() != abc); CHECK(StringView() == String()); CHECK(StringView() == ""); } TEST_CASE("StringUtilities::ConvertByteOffsetToCharacterOffset") { using namespace Rml::StringUtilities; // clang-format off CHECK(ConvertByteOffsetToCharacterOffset("", 0) == 0); CHECK(ConvertByteOffsetToCharacterOffset("", 1) == 0); CHECK(ConvertByteOffsetToCharacterOffset("a", 0) == 0); CHECK(ConvertByteOffsetToCharacterOffset("a", 1) == 1); CHECK(ConvertByteOffsetToCharacterOffset("ab", 1) == 1); CHECK(ConvertByteOffsetToCharacterOffset("ab", 2) == 2); CHECK(ConvertByteOffsetToCharacterOffset("a\xC2\xA3" "b", 1) == 1); CHECK(ConvertByteOffsetToCharacterOffset("a\xC2\xA3" "b", 2) == 2); CHECK(ConvertByteOffsetToCharacterOffset("a\xC2\xA3" "b", 3) == 2); CHECK(ConvertByteOffsetToCharacterOffset("a\xC2\xA3" "b", 4) == 3); CHECK(ConvertByteOffsetToCharacterOffset("a\xE2\x82\xAC" "b", 2) == 2); CHECK(ConvertByteOffsetToCharacterOffset("a\xE2\x82\xAC" "b", 3) == 2); CHECK(ConvertByteOffsetToCharacterOffset("a\xE2\x82\xAC" "b", 4) == 2); CHECK(ConvertByteOffsetToCharacterOffset("a\xE2\x82\xAC" "b", 5) == 3); // clang-format on } TEST_CASE("StringUtilities::ConvertCharacterOffsetToByteOffset") { using namespace Rml::StringUtilities; // clang-format off CHECK(ConvertCharacterOffsetToByteOffset("", 0) == 0); CHECK(ConvertCharacterOffsetToByteOffset("", 1) == 0); CHECK(ConvertCharacterOffsetToByteOffset("a", 0) == 0); CHECK(ConvertCharacterOffsetToByteOffset("a", 1) == 1); CHECK(ConvertCharacterOffsetToByteOffset("ab", 1) == 1); CHECK(ConvertCharacterOffsetToByteOffset("ab", 2) == 2); CHECK(ConvertCharacterOffsetToByteOffset("a\xC2\xA3" "b", 1) == 1); CHECK(ConvertCharacterOffsetToByteOffset("a\xC2\xA3" "b", 2) == 3); CHECK(ConvertCharacterOffsetToByteOffset("a\xC2\xA3" "b", 3) == 4); CHECK(ConvertCharacterOffsetToByteOffset("a\xC2\xA3" "b", 4) == 4); CHECK(ConvertCharacterOffsetToByteOffset("a\xE2\x82\xAC" "b", 1) == 1); CHECK(ConvertCharacterOffsetToByteOffset("a\xE2\x82\xAC" "b", 2) == 4); CHECK(ConvertCharacterOffsetToByteOffset("a\xE2\x82\xAC" "b", 3) == 5); CHECK(ConvertCharacterOffsetToByteOffset("a\xE2\x82\xAC" "b", 4) == 5); // clang-format on } TEST_CASE("CreateString") { CHECK(Rml::CreateString("Hello %s!", "world") == "Hello world!"); CHECK(Rml::CreateString("%g, %d, %.2f", 0.5f, 5, 2.f) == "0.5, 5, 2.00"); constexpr int InternalBufferSize = 256; for (int string_size : {InternalBufferSize - 1, InternalBufferSize, InternalBufferSize + 1}) { Rml::String large_string(string_size, 'x'); CHECK(Rml::CreateString("%s", large_string.c_str()) == large_string); } } TEST_CASE("FormatString") { { Rml::String result; int length = Rml::FormatString(result, "Hello %s!", "world"); CHECK(result == "Hello world!"); CHECK(length == 12); } constexpr int InternalBufferSize = 256; for (int string_size : {InternalBufferSize - 1, InternalBufferSize, InternalBufferSize + 1}) { const Rml::String large_string(string_size, 'x'); Rml::String result; int length = Rml::FormatString(result, "%s", large_string.c_str()); CHECK(result == large_string); CHECK(length == string_size); } }