mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-06 17:06:31 +00:00
![Tim Schumacher](/assets/img/avatar_default.png)
Similar to POSIX read, the basic read and write functions of AK::Stream do not have a lower limit of how much data they read or write (apart from "none at all"). Rename the functions to "read some [data]" and "write some [data]" (with "data" being omitted, since everything here is reading and writing data) to make them sufficiently distinct from the functions that ensure to use the entire buffer (which should be the go-to function for most usages). No functional changes, just a lot of new FIXMEs.
890 lines
28 KiB
C++
890 lines
28 KiB
C++
/*
|
||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||
*
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
// This is included first on purpose. We specifically do not want LibTest to override VERIFY here so
|
||
// that we can actually test that some String factory methods cause a crash with invalid input.
|
||
#include <AK/String.h>
|
||
|
||
#include <LibTest/TestCase.h>
|
||
|
||
#include <AK/MemoryStream.h>
|
||
#include <AK/StringBuilder.h>
|
||
#include <AK/Try.h>
|
||
#include <AK/Utf8View.h>
|
||
#include <AK/Vector.h>
|
||
|
||
TEST_CASE(construct_empty)
|
||
{
|
||
String empty;
|
||
EXPECT(empty.is_empty());
|
||
EXPECT_EQ(empty.bytes().size(), 0u);
|
||
EXPECT_EQ(empty, ""sv);
|
||
|
||
auto empty2 = MUST(""_string);
|
||
EXPECT(empty2.is_empty());
|
||
EXPECT_EQ(empty, empty2);
|
||
|
||
auto empty3 = MUST(String::from_utf8(""sv));
|
||
EXPECT(empty3.is_empty());
|
||
EXPECT_EQ(empty, empty3);
|
||
}
|
||
|
||
TEST_CASE(move_assignment)
|
||
{
|
||
String string1 = MUST("hello"_string);
|
||
string1 = MUST("friends!"_string);
|
||
EXPECT_EQ(string1, "friends!"sv);
|
||
}
|
||
|
||
TEST_CASE(short_strings)
|
||
{
|
||
#ifdef AK_ARCH_64_BIT
|
||
auto string1 = MUST(String::from_utf8("abcdefg"sv));
|
||
EXPECT_EQ(string1.is_short_string(), true);
|
||
EXPECT_EQ(string1.bytes().size(), 7u);
|
||
EXPECT_EQ(string1.bytes_as_string_view(), "abcdefg"sv);
|
||
|
||
constexpr auto string2 = String::from_utf8_short_string("abcdefg"sv);
|
||
EXPECT_EQ(string2.is_short_string(), true);
|
||
EXPECT_EQ(string2.bytes().size(), 7u);
|
||
EXPECT_EQ(string2, string1);
|
||
|
||
auto string3 = MUST("abcdefg"_string);
|
||
EXPECT_EQ(string3.is_short_string(), true);
|
||
EXPECT_EQ(string3.bytes().size(), 7u);
|
||
EXPECT_EQ(string3, string1);
|
||
|
||
constexpr auto string4 = "abcdefg"_short_string;
|
||
EXPECT_EQ(string4.is_short_string(), true);
|
||
EXPECT_EQ(string4.bytes().size(), 7u);
|
||
EXPECT_EQ(string4, string1);
|
||
#else
|
||
auto string1 = MUST(String::from_utf8("abc"sv));
|
||
EXPECT_EQ(string1.is_short_string(), true);
|
||
EXPECT_EQ(string1.bytes().size(), 3u);
|
||
EXPECT_EQ(string1.bytes_as_string_view(), "abc"sv);
|
||
|
||
constexpr auto string2 = String::from_utf8_short_string("abc"sv);
|
||
EXPECT_EQ(string2.is_short_string(), true);
|
||
EXPECT_EQ(string2.bytes().size(), 3u);
|
||
EXPECT_EQ(string2, string1);
|
||
|
||
auto string3 = MUST("abc"_string);
|
||
EXPECT_EQ(string3.is_short_string(), true);
|
||
EXPECT_EQ(string3.bytes().size(), 3u);
|
||
EXPECT_EQ(string3, string1);
|
||
|
||
constexpr auto string4 = "abc"_short_string;
|
||
EXPECT_EQ(string4.is_short_string(), true);
|
||
EXPECT_EQ(string4.bytes().size(), 3u);
|
||
EXPECT_EQ(string4, string1);
|
||
#endif
|
||
}
|
||
|
||
TEST_CASE(long_strings)
|
||
{
|
||
auto string = MUST(String::from_utf8("abcdefgh"sv));
|
||
EXPECT_EQ(string.is_short_string(), false);
|
||
EXPECT_EQ(string.bytes().size(), 8u);
|
||
EXPECT_EQ(string.bytes_as_string_view(), "abcdefgh"sv);
|
||
}
|
||
|
||
TEST_CASE(long_streams)
|
||
{
|
||
{
|
||
u8 bytes[64] = {};
|
||
constexpr auto test_view = "Well, hello friends"sv;
|
||
FixedMemoryStream stream(Bytes { bytes, sizeof(bytes) });
|
||
// FIXME: This should write the entire span.
|
||
MUST(stream.write_some(test_view.bytes()));
|
||
MUST(stream.seek(0));
|
||
|
||
auto string = MUST(String::from_stream(stream, test_view.length()));
|
||
|
||
EXPECT_EQ(string.is_short_string(), false);
|
||
EXPECT_EQ(string.bytes().size(), 19u);
|
||
EXPECT_EQ(string.bytes_as_string_view(), test_view);
|
||
}
|
||
|
||
{
|
||
AllocatingMemoryStream stream;
|
||
// FIXME: This should write the entire span.
|
||
MUST(stream.write_some(("abc"sv).bytes()));
|
||
|
||
auto string = MUST(String::from_stream(stream, 3u));
|
||
|
||
EXPECT_EQ(string.is_short_string(), true);
|
||
EXPECT_EQ(string.bytes().size(), 3u);
|
||
EXPECT_EQ(string.bytes_as_string_view(), "abc"sv);
|
||
}
|
||
|
||
{
|
||
AllocatingMemoryStream stream;
|
||
// FIXME: This should write the entire span.
|
||
MUST(stream.write_some(("0123456789"sv).bytes()));
|
||
|
||
auto string = MUST(String::from_stream(stream, 9u));
|
||
|
||
EXPECT_EQ(string.is_short_string(), false);
|
||
EXPECT_EQ(string.bytes().size(), 9u);
|
||
EXPECT_EQ(string.bytes_as_string_view(), "012345678"sv);
|
||
}
|
||
|
||
{
|
||
AllocatingMemoryStream stream;
|
||
MUST(stream.write_value(0xffffffff));
|
||
MUST(stream.write_value(0xffffffff));
|
||
MUST(stream.write_value(0xffffffff));
|
||
auto error_or_string = String::from_stream(stream, stream.used_buffer_size());
|
||
EXPECT_EQ(error_or_string.is_error(), true);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(invalid_utf8)
|
||
{
|
||
auto string1 = String::from_utf8("long string \xf4\x8f\xbf\xc0"sv); // U+110000
|
||
EXPECT(string1.is_error());
|
||
EXPECT(string1.error().string_literal().contains("Input was not valid UTF-8"sv));
|
||
|
||
auto string2 = String::from_utf8("\xf4\xa1\xb0\xbd"sv); // U+121C3D
|
||
EXPECT(string2.is_error());
|
||
EXPECT(string2.error().string_literal().contains("Input was not valid UTF-8"sv));
|
||
|
||
AllocatingMemoryStream stream;
|
||
MUST(stream.write_value<u8>(0xf4));
|
||
MUST(stream.write_value<u8>(0xa1));
|
||
MUST(stream.write_value<u8>(0xb0));
|
||
MUST(stream.write_value<u8>(0xbd));
|
||
auto string3 = String::from_stream(stream, stream.used_buffer_size());
|
||
EXPECT_EQ(string3.is_error(), true);
|
||
EXPECT(string3.error().string_literal().contains("Input was not valid UTF-8"sv));
|
||
}
|
||
|
||
TEST_CASE(from_code_points)
|
||
{
|
||
for (u32 code_point = 0; code_point < 0x80; ++code_point) {
|
||
auto string = String::from_code_point(code_point);
|
||
|
||
auto ch = static_cast<char>(code_point);
|
||
StringView view { &ch, 1 };
|
||
|
||
EXPECT_EQ(string, view);
|
||
}
|
||
|
||
auto string = String::from_code_point(0x10ffff);
|
||
EXPECT_EQ(string, "\xF4\x8F\xBF\xBF"sv);
|
||
|
||
EXPECT_CRASH("Creating a string from an invalid code point", [] {
|
||
String::from_code_point(0xffffffff);
|
||
return Test::Crash::Failure::DidNotCrash;
|
||
});
|
||
}
|
||
|
||
TEST_CASE(substring)
|
||
{
|
||
auto superstring = MUST("Hello I am a long string"_string);
|
||
auto short_substring = MUST(superstring.substring_from_byte_offset(0, 5));
|
||
EXPECT_EQ(short_substring, "Hello"sv);
|
||
|
||
auto long_substring = MUST(superstring.substring_from_byte_offset(0, 10));
|
||
EXPECT_EQ(long_substring, "Hello I am"sv);
|
||
}
|
||
|
||
TEST_CASE(substring_with_shared_superstring)
|
||
{
|
||
auto superstring = MUST("Hello I am a long string"_string);
|
||
|
||
auto substring1 = MUST(superstring.substring_from_byte_offset_with_shared_superstring(0, 5));
|
||
EXPECT_EQ(substring1, "Hello"sv);
|
||
|
||
auto substring2 = MUST(superstring.substring_from_byte_offset_with_shared_superstring(0, 10));
|
||
EXPECT_EQ(substring2, "Hello I am"sv);
|
||
}
|
||
|
||
TEST_CASE(code_points)
|
||
{
|
||
auto string = MUST("🦬🪒"_string);
|
||
|
||
Vector<u32> code_points;
|
||
for (auto code_point : string.code_points())
|
||
code_points.append(code_point);
|
||
|
||
EXPECT_EQ(code_points[0], 0x1f9acu);
|
||
EXPECT_EQ(code_points[1], 0x1fa92u);
|
||
}
|
||
|
||
TEST_CASE(string_builder)
|
||
{
|
||
StringBuilder builder;
|
||
builder.append_code_point(0x1f9acu);
|
||
builder.append_code_point(0x1fa92u);
|
||
|
||
auto string = MUST(builder.to_string());
|
||
EXPECT_EQ(string, "🦬🪒"sv);
|
||
EXPECT_EQ(string.bytes().size(), 8u);
|
||
}
|
||
|
||
TEST_CASE(ak_format)
|
||
{
|
||
auto foo = MUST(String::formatted("Hello {}", MUST("friends"_string)));
|
||
EXPECT_EQ(foo, "Hello friends"sv);
|
||
}
|
||
|
||
TEST_CASE(replace)
|
||
{
|
||
{
|
||
auto haystack = MUST("Hello enemies"_string);
|
||
auto result = MUST(haystack.replace("enemies"sv, "friends"sv, ReplaceMode::All));
|
||
EXPECT_EQ(result, "Hello friends"sv);
|
||
}
|
||
|
||
{
|
||
auto base_title = MUST("anon@courage:~"_string);
|
||
auto result = MUST(base_title.replace("[*]"sv, "(*)"sv, ReplaceMode::FirstOnly));
|
||
EXPECT_EQ(result, "anon@courage:~"sv);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(reverse)
|
||
{
|
||
auto test_reverse = [](auto test, auto expected) {
|
||
auto string = MUST(String::from_utf8(test));
|
||
auto result = MUST(string.reverse());
|
||
|
||
EXPECT_EQ(result, expected);
|
||
};
|
||
|
||
test_reverse(""sv, ""sv);
|
||
test_reverse("a"sv, "a"sv);
|
||
test_reverse("ab"sv, "ba"sv);
|
||
test_reverse("ab cd ef"sv, "fe dc ba"sv);
|
||
test_reverse("😀"sv, "😀"sv);
|
||
test_reverse("ab😀cd"sv, "dc😀ba"sv);
|
||
}
|
||
|
||
TEST_CASE(to_lowercase)
|
||
{
|
||
{
|
||
auto string = MUST("Aa"_string);
|
||
auto result = MUST(string.to_lowercase());
|
||
EXPECT_EQ(result, "aa"sv);
|
||
}
|
||
{
|
||
auto string = MUST("Ωω"_string);
|
||
auto result = MUST(string.to_lowercase());
|
||
EXPECT_EQ(result, "ωω"sv);
|
||
}
|
||
{
|
||
auto string = MUST("İi̇"_string);
|
||
auto result = MUST(string.to_lowercase());
|
||
EXPECT_EQ(result, "i̇i̇"sv);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(to_uppercase)
|
||
{
|
||
{
|
||
auto string = MUST("Aa"_string);
|
||
auto result = MUST(string.to_uppercase());
|
||
EXPECT_EQ(result, "AA"sv);
|
||
}
|
||
{
|
||
auto string = MUST("Ωω"_string);
|
||
auto result = MUST(string.to_uppercase());
|
||
EXPECT_EQ(result, "ΩΩ"sv);
|
||
}
|
||
{
|
||
auto string = MUST("ʼn"_string);
|
||
auto result = MUST(string.to_uppercase());
|
||
EXPECT_EQ(result, "ʼN"sv);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(to_titlecase)
|
||
{
|
||
{
|
||
auto string = MUST("foo bar baz"_string);
|
||
auto result = MUST(string.to_titlecase());
|
||
EXPECT_EQ(result, "Foo Bar Baz"sv);
|
||
}
|
||
{
|
||
auto string = MUST("foo \n \r bar \t baz"_string);
|
||
auto result = MUST(string.to_titlecase());
|
||
EXPECT_EQ(result, "Foo \n \r Bar \t Baz"sv);
|
||
}
|
||
{
|
||
auto string = MUST("f\"oo\" b'ar'"_string);
|
||
auto result = MUST(string.to_titlecase());
|
||
EXPECT_EQ(result, "F\"Oo\" B'ar'"sv);
|
||
}
|
||
{
|
||
auto string = MUST("123dollars"_string);
|
||
auto result = MUST(string.to_titlecase());
|
||
EXPECT_EQ(result, "123Dollars"sv);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(equals_ignoring_case)
|
||
{
|
||
{
|
||
String string1 {};
|
||
String string2 {};
|
||
|
||
EXPECT(string1.equals_ignoring_case(string2));
|
||
}
|
||
{
|
||
auto string1 = MUST("abcd"_string);
|
||
auto string2 = MUST("ABCD"_string);
|
||
auto string3 = MUST("AbCd"_string);
|
||
auto string4 = MUST("dcba"_string);
|
||
auto string5 = MUST("abce"_string);
|
||
auto string6 = MUST("abc"_string);
|
||
|
||
EXPECT(string1.equals_ignoring_case(string2));
|
||
EXPECT(string1.equals_ignoring_case(string3));
|
||
EXPECT(!string1.equals_ignoring_case(string4));
|
||
EXPECT(!string1.equals_ignoring_case(string5));
|
||
EXPECT(!string1.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string2.equals_ignoring_case(string1));
|
||
EXPECT(string2.equals_ignoring_case(string3));
|
||
EXPECT(!string2.equals_ignoring_case(string4));
|
||
EXPECT(!string2.equals_ignoring_case(string5));
|
||
EXPECT(!string2.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string3.equals_ignoring_case(string1));
|
||
EXPECT(string3.equals_ignoring_case(string2));
|
||
EXPECT(!string3.equals_ignoring_case(string4));
|
||
EXPECT(!string3.equals_ignoring_case(string5));
|
||
EXPECT(!string3.equals_ignoring_case(string6));
|
||
}
|
||
{
|
||
auto string1 = MUST("\u00DF"_string); // LATIN SMALL LETTER SHARP S
|
||
auto string2 = MUST("SS"_string);
|
||
auto string3 = MUST("Ss"_string);
|
||
auto string4 = MUST("ss"_string);
|
||
auto string5 = MUST("S"_string);
|
||
auto string6 = MUST("s"_string);
|
||
|
||
EXPECT(string1.equals_ignoring_case(string2));
|
||
EXPECT(string1.equals_ignoring_case(string3));
|
||
EXPECT(string1.equals_ignoring_case(string4));
|
||
EXPECT(!string1.equals_ignoring_case(string5));
|
||
EXPECT(!string1.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string2.equals_ignoring_case(string1));
|
||
EXPECT(string2.equals_ignoring_case(string3));
|
||
EXPECT(string2.equals_ignoring_case(string4));
|
||
EXPECT(!string2.equals_ignoring_case(string5));
|
||
EXPECT(!string2.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string3.equals_ignoring_case(string1));
|
||
EXPECT(string3.equals_ignoring_case(string2));
|
||
EXPECT(string3.equals_ignoring_case(string4));
|
||
EXPECT(!string3.equals_ignoring_case(string5));
|
||
EXPECT(!string3.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string4.equals_ignoring_case(string1));
|
||
EXPECT(string4.equals_ignoring_case(string2));
|
||
EXPECT(string4.equals_ignoring_case(string3));
|
||
EXPECT(!string4.equals_ignoring_case(string5));
|
||
EXPECT(!string4.equals_ignoring_case(string6));
|
||
}
|
||
{
|
||
|
||
auto string1 = MUST("Ab\u00DFCd\u00DFeF"_string);
|
||
auto string2 = MUST("ABSSCDSSEF"_string);
|
||
auto string3 = MUST("absscdssef"_string);
|
||
auto string4 = MUST("aBSscDsSEf"_string);
|
||
auto string5 = MUST("Ab\u00DFCd\u00DFeg"_string);
|
||
auto string6 = MUST("Ab\u00DFCd\u00DFe"_string);
|
||
|
||
EXPECT(string1.equals_ignoring_case(string1));
|
||
EXPECT(string1.equals_ignoring_case(string2));
|
||
EXPECT(string1.equals_ignoring_case(string3));
|
||
EXPECT(string1.equals_ignoring_case(string4));
|
||
EXPECT(!string1.equals_ignoring_case(string5));
|
||
EXPECT(!string1.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string2.equals_ignoring_case(string1));
|
||
EXPECT(string2.equals_ignoring_case(string2));
|
||
EXPECT(string2.equals_ignoring_case(string3));
|
||
EXPECT(string2.equals_ignoring_case(string4));
|
||
EXPECT(!string2.equals_ignoring_case(string5));
|
||
EXPECT(!string2.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string3.equals_ignoring_case(string1));
|
||
EXPECT(string3.equals_ignoring_case(string2));
|
||
EXPECT(string3.equals_ignoring_case(string3));
|
||
EXPECT(string3.equals_ignoring_case(string4));
|
||
EXPECT(!string3.equals_ignoring_case(string5));
|
||
EXPECT(!string3.equals_ignoring_case(string6));
|
||
|
||
EXPECT(string4.equals_ignoring_case(string1));
|
||
EXPECT(string4.equals_ignoring_case(string2));
|
||
EXPECT(string4.equals_ignoring_case(string3));
|
||
EXPECT(string4.equals_ignoring_case(string4));
|
||
EXPECT(!string4.equals_ignoring_case(string5));
|
||
EXPECT(!string4.equals_ignoring_case(string6));
|
||
}
|
||
}
|
||
|
||
TEST_CASE(is_one_of)
|
||
{
|
||
auto foo = MUST("foo"_string);
|
||
auto bar = MUST("bar"_string);
|
||
|
||
EXPECT(foo.is_one_of(foo));
|
||
EXPECT(foo.is_one_of(foo, bar));
|
||
EXPECT(foo.is_one_of(bar, foo));
|
||
EXPECT(!foo.is_one_of(bar));
|
||
|
||
EXPECT(!bar.is_one_of("foo"sv));
|
||
EXPECT(bar.is_one_of("foo"sv, "bar"sv));
|
||
EXPECT(bar.is_one_of("bar"sv, "foo"sv));
|
||
EXPECT(bar.is_one_of("bar"sv));
|
||
}
|
||
|
||
TEST_CASE(split)
|
||
{
|
||
{
|
||
auto test = MUST("foo bar baz"_string);
|
||
auto parts = MUST(test.split(' '));
|
||
EXPECT_EQ(parts.size(), 3u);
|
||
EXPECT_EQ(parts[0], "foo");
|
||
EXPECT_EQ(parts[1], "bar");
|
||
EXPECT_EQ(parts[2], "baz");
|
||
}
|
||
{
|
||
auto test = MUST("ωΣ2ωΣω"_string);
|
||
auto parts = MUST(test.split(0x03A3u));
|
||
EXPECT_EQ(parts.size(), 3u);
|
||
EXPECT_EQ(parts[0], "ω"sv);
|
||
EXPECT_EQ(parts[1], "2ω"sv);
|
||
EXPECT_EQ(parts[2], "ω"sv);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(find_byte_offset)
|
||
{
|
||
{
|
||
String string {};
|
||
auto index1 = string.find_byte_offset(0);
|
||
EXPECT(!index1.has_value());
|
||
|
||
auto index2 = string.find_byte_offset(""sv);
|
||
EXPECT(!index2.has_value());
|
||
}
|
||
{
|
||
auto string = MUST("foo"_string);
|
||
|
||
auto index1 = string.find_byte_offset('f');
|
||
EXPECT_EQ(index1, 0u);
|
||
|
||
auto index2 = string.find_byte_offset('o');
|
||
EXPECT_EQ(index2, 1u);
|
||
|
||
auto index3 = string.find_byte_offset('o', *index2 + 1);
|
||
EXPECT_EQ(index3, 2u);
|
||
|
||
auto index4 = string.find_byte_offset('b');
|
||
EXPECT(!index4.has_value());
|
||
}
|
||
{
|
||
auto string = MUST("foo"_string);
|
||
|
||
auto index1 = string.find_byte_offset("fo"sv);
|
||
EXPECT_EQ(index1, 0u);
|
||
|
||
auto index2 = string.find_byte_offset("oo"sv);
|
||
EXPECT_EQ(index2, 1u);
|
||
|
||
auto index3 = string.find_byte_offset("o"sv, *index2 + 1);
|
||
EXPECT_EQ(index3, 2u);
|
||
|
||
auto index4 = string.find_byte_offset("fooo"sv);
|
||
EXPECT(!index4.has_value());
|
||
}
|
||
{
|
||
auto string = MUST("ωΣωΣω"_string);
|
||
|
||
auto index1 = string.find_byte_offset(0x03C9U);
|
||
EXPECT_EQ(index1, 0u);
|
||
|
||
auto index2 = string.find_byte_offset(0x03A3u);
|
||
EXPECT_EQ(index2, 2u);
|
||
|
||
auto index3 = string.find_byte_offset(0x03C9U, 2);
|
||
EXPECT_EQ(index3, 4u);
|
||
|
||
auto index4 = string.find_byte_offset(0x03A3u, 4);
|
||
EXPECT_EQ(index4, 6u);
|
||
|
||
auto index5 = string.find_byte_offset(0x03C9U, 6);
|
||
EXPECT_EQ(index5, 8u);
|
||
}
|
||
{
|
||
auto string = MUST("ωΣωΣω"_string);
|
||
|
||
auto index1 = string.find_byte_offset("ω"sv);
|
||
EXPECT_EQ(index1, 0u);
|
||
|
||
auto index2 = string.find_byte_offset("Σ"sv);
|
||
EXPECT_EQ(index2, 2u);
|
||
|
||
auto index3 = string.find_byte_offset("ω"sv, 2);
|
||
EXPECT_EQ(index3, 4u);
|
||
|
||
auto index4 = string.find_byte_offset("Σ"sv, 4);
|
||
EXPECT_EQ(index4, 6u);
|
||
|
||
auto index5 = string.find_byte_offset("ω"sv, 6);
|
||
EXPECT_EQ(index5, 8u);
|
||
}
|
||
}
|
||
|
||
TEST_CASE(repeated)
|
||
{
|
||
{
|
||
auto string1 = MUST(String::repeated('a', 0));
|
||
EXPECT(string1.is_short_string());
|
||
EXPECT(string1.is_empty());
|
||
|
||
auto string2 = MUST(String::repeated(0x03C9U, 0));
|
||
EXPECT(string2.is_short_string());
|
||
EXPECT(string2.is_empty());
|
||
|
||
auto string3 = MUST(String::repeated(0x10300, 0));
|
||
EXPECT(string3.is_short_string());
|
||
EXPECT(string3.is_empty());
|
||
}
|
||
{
|
||
auto string1 = MUST(String::repeated('a', 1));
|
||
EXPECT(string1.is_short_string());
|
||
EXPECT_EQ(string1.bytes_as_string_view().length(), 1u);
|
||
EXPECT_EQ(string1, "a"sv);
|
||
|
||
auto string2 = MUST(String::repeated(0x03C9U, 1));
|
||
EXPECT(string2.is_short_string());
|
||
EXPECT_EQ(string2.bytes_as_string_view().length(), 2u);
|
||
EXPECT_EQ(string2, "ω"sv);
|
||
|
||
auto string3 = MUST(String::repeated(0x10300, 1));
|
||
#ifdef AK_ARCH_64_BIT
|
||
EXPECT(string3.is_short_string());
|
||
#else
|
||
EXPECT(!string3.is_short_string());
|
||
#endif
|
||
EXPECT_EQ(string3.bytes_as_string_view().length(), 4u);
|
||
EXPECT_EQ(string3, "𐌀"sv);
|
||
}
|
||
{
|
||
auto string1 = MUST(String::repeated('a', 3));
|
||
EXPECT(string1.is_short_string());
|
||
EXPECT_EQ(string1.bytes_as_string_view().length(), 3u);
|
||
EXPECT_EQ(string1, "aaa"sv);
|
||
|
||
auto string2 = MUST(String::repeated(0x03C9U, 3));
|
||
#ifdef AK_ARCH_64_BIT
|
||
EXPECT(string2.is_short_string());
|
||
#else
|
||
EXPECT(!string2.is_short_string());
|
||
#endif
|
||
EXPECT_EQ(string2.bytes_as_string_view().length(), 6u);
|
||
EXPECT_EQ(string2, "ωωω"sv);
|
||
|
||
auto string3 = MUST(String::repeated(0x10300, 3));
|
||
EXPECT(!string3.is_short_string());
|
||
EXPECT_EQ(string3.bytes_as_string_view().length(), 12u);
|
||
EXPECT_EQ(string3, "𐌀𐌀𐌀"sv);
|
||
}
|
||
{
|
||
auto string1 = MUST(String::repeated('a', 10));
|
||
EXPECT(!string1.is_short_string());
|
||
EXPECT_EQ(string1.bytes_as_string_view().length(), 10u);
|
||
EXPECT_EQ(string1, "aaaaaaaaaa"sv);
|
||
|
||
auto string2 = MUST(String::repeated(0x03C9U, 10));
|
||
EXPECT(!string2.is_short_string());
|
||
EXPECT_EQ(string2.bytes_as_string_view().length(), 20u);
|
||
EXPECT_EQ(string2, "ωωωωωωωωωω"sv);
|
||
|
||
auto string3 = MUST(String::repeated(0x10300, 10));
|
||
EXPECT(!string3.is_short_string());
|
||
EXPECT_EQ(string3.bytes_as_string_view().length(), 40u);
|
||
EXPECT_EQ(string3, "𐌀𐌀𐌀𐌀𐌀𐌀𐌀𐌀𐌀𐌀"sv);
|
||
}
|
||
|
||
EXPECT_CRASH("Creating a string from an invalid code point", [] {
|
||
(void)String::repeated(0xffffffff, 1);
|
||
return Test::Crash::Failure::DidNotCrash;
|
||
});
|
||
}
|
||
|
||
TEST_CASE(join)
|
||
{
|
||
auto string1 = MUST(String::join(',', Vector<i32> {}));
|
||
EXPECT(string1.is_empty());
|
||
|
||
auto string2 = MUST(String::join(',', Array { 1 }));
|
||
EXPECT_EQ(string2, "1"sv);
|
||
|
||
auto string3 = MUST(String::join(':', Array { 1 }, "[{}]"sv));
|
||
EXPECT_EQ(string3, "[1]"sv);
|
||
|
||
auto string4 = MUST(String::join(',', Array { 1, 2, 3 }));
|
||
EXPECT_EQ(string4, "1,2,3"sv);
|
||
|
||
auto string5 = MUST(String::join(',', Array { 1, 2, 3 }, "[{}]"sv));
|
||
EXPECT_EQ(string5, "[1],[2],[3]"sv);
|
||
|
||
auto string6 = MUST(String::join("!!!"_short_string, Array { "foo"sv, "bar"sv, "baz"sv }));
|
||
EXPECT_EQ(string6, "foo!!!bar!!!baz"sv);
|
||
|
||
auto string7 = MUST(String::join(" - "sv, Array { 1, 16, 256, 4096 }, "[{:#04x}]"sv));
|
||
EXPECT_EQ(string7, "[0x0001] - [0x0010] - [0x0100] - [0x1000]"sv);
|
||
}
|
||
|
||
TEST_CASE(trim)
|
||
{
|
||
{
|
||
String string {};
|
||
|
||
auto result = MUST(string.trim(" "sv, TrimMode::Both));
|
||
EXPECT(result.is_empty());
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Left));
|
||
EXPECT(result.is_empty());
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Right));
|
||
EXPECT(result.is_empty());
|
||
}
|
||
{
|
||
auto string = MUST("word"_string);
|
||
|
||
auto result = MUST(string.trim(" "sv, TrimMode::Both));
|
||
EXPECT_EQ(result, "word"sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Left));
|
||
EXPECT_EQ(result, "word"sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Right));
|
||
EXPECT_EQ(result, "word"sv);
|
||
}
|
||
{
|
||
auto string = MUST(" word"_string);
|
||
|
||
auto result = MUST(string.trim(" "sv, TrimMode::Both));
|
||
EXPECT_EQ(result, "word"sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Left));
|
||
EXPECT_EQ(result, "word"sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Right));
|
||
EXPECT_EQ(result, " word"sv);
|
||
}
|
||
{
|
||
auto string = MUST("word "_string);
|
||
|
||
auto result = MUST(string.trim(" "sv, TrimMode::Both));
|
||
EXPECT_EQ(result, "word"sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Left));
|
||
EXPECT_EQ(result, "word "sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Right));
|
||
EXPECT_EQ(result, "word"sv);
|
||
}
|
||
{
|
||
auto string = MUST(" word "_string);
|
||
|
||
auto result = MUST(string.trim(" "sv, TrimMode::Both));
|
||
EXPECT_EQ(result, "word"sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Left));
|
||
EXPECT_EQ(result, "word "sv);
|
||
|
||
result = MUST(string.trim(" "sv, TrimMode::Right));
|
||
EXPECT_EQ(result, " word"sv);
|
||
}
|
||
{
|
||
auto string = MUST(" word "_string);
|
||
|
||
auto result = MUST(string.trim("\t"sv, TrimMode::Both));
|
||
EXPECT_EQ(result, " word "sv);
|
||
|
||
result = MUST(string.trim("\t"sv, TrimMode::Left));
|
||
EXPECT_EQ(result, " word "sv);
|
||
|
||
result = MUST(string.trim("\t"sv, TrimMode::Right));
|
||
EXPECT_EQ(result, " word "sv);
|
||
}
|
||
{
|
||
auto string = MUST("ωΣωΣω"_string);
|
||
|
||
auto result = MUST(string.trim("ω"sv, TrimMode::Both));
|
||
EXPECT_EQ(result, "ΣωΣ"sv);
|
||
|
||
result = MUST(string.trim("ω"sv, TrimMode::Left));
|
||
EXPECT_EQ(result, "ΣωΣω"sv);
|
||
|
||
result = MUST(string.trim("ω"sv, TrimMode::Right));
|
||
EXPECT_EQ(result, "ωΣωΣ"sv);
|
||
}
|
||
{
|
||
auto string = MUST("ωΣωΣω"_string);
|
||
|
||
auto result = MUST(string.trim("ωΣ"sv, TrimMode::Both));
|
||
EXPECT(result.is_empty());
|
||
|
||
result = MUST(string.trim("ωΣ"sv, TrimMode::Left));
|
||
EXPECT(result.is_empty());
|
||
|
||
result = MUST(string.trim("ωΣ"sv, TrimMode::Right));
|
||
EXPECT(result.is_empty());
|
||
}
|
||
{
|
||
auto string = MUST("ωΣωΣω"_string);
|
||
|
||
auto result = MUST(string.trim("Σω"sv, TrimMode::Both));
|
||
EXPECT(result.is_empty());
|
||
|
||
result = MUST(string.trim("Σω"sv, TrimMode::Left));
|
||
EXPECT(result.is_empty());
|
||
|
||
result = MUST(string.trim("Σω"sv, TrimMode::Right));
|
||
EXPECT(result.is_empty());
|
||
}
|
||
}
|
||
|
||
TEST_CASE(contains)
|
||
{
|
||
EXPECT(!String {}.contains({}));
|
||
EXPECT(!String {}.contains(" "sv));
|
||
EXPECT(!String {}.contains(0));
|
||
|
||
EXPECT("a"_short_string.contains("a"sv));
|
||
EXPECT(!"a"_short_string.contains({}));
|
||
EXPECT(!"a"_short_string.contains("b"sv));
|
||
EXPECT(!"a"_short_string.contains("ab"sv));
|
||
|
||
EXPECT("a"_short_string.contains(0x0061));
|
||
EXPECT(!"a"_short_string.contains(0x0062));
|
||
|
||
EXPECT("abc"_short_string.contains("a"sv));
|
||
EXPECT("abc"_short_string.contains("b"sv));
|
||
EXPECT("abc"_short_string.contains("c"sv));
|
||
EXPECT("abc"_short_string.contains("ab"sv));
|
||
EXPECT("abc"_short_string.contains("bc"sv));
|
||
EXPECT("abc"_short_string.contains("abc"sv));
|
||
EXPECT(!"abc"_short_string.contains({}));
|
||
EXPECT(!"abc"_short_string.contains("ac"sv));
|
||
EXPECT(!"abc"_short_string.contains("abcd"sv));
|
||
|
||
EXPECT("abc"_short_string.contains(0x0061));
|
||
EXPECT("abc"_short_string.contains(0x0062));
|
||
EXPECT("abc"_short_string.contains(0x0063));
|
||
EXPECT(!"abc"_short_string.contains(0x0064));
|
||
|
||
auto emoji = MUST("😀"_string);
|
||
EXPECT(emoji.contains("\xF0"sv));
|
||
EXPECT(emoji.contains("\x9F"sv));
|
||
EXPECT(emoji.contains("\x98"sv));
|
||
EXPECT(emoji.contains("\x80"sv));
|
||
EXPECT(emoji.contains("\xF0\x9F"sv));
|
||
EXPECT(emoji.contains("\xF0\x9F\x98"sv));
|
||
EXPECT(emoji.contains("\xF0\x9F\x98\x80"sv));
|
||
EXPECT(emoji.contains("\x9F\x98\x80"sv));
|
||
EXPECT(emoji.contains("\x98\x80"sv));
|
||
EXPECT(!emoji.contains("a"sv));
|
||
EXPECT(!emoji.contains("🙃"sv));
|
||
|
||
EXPECT(emoji.contains(0x1F600));
|
||
EXPECT(!emoji.contains(0x1F643));
|
||
}
|
||
|
||
TEST_CASE(starts_with)
|
||
{
|
||
EXPECT(String {}.starts_with_bytes({}));
|
||
EXPECT(!String {}.starts_with_bytes(" "sv));
|
||
EXPECT(!String {}.starts_with(0));
|
||
|
||
EXPECT("a"_short_string.starts_with_bytes({}));
|
||
EXPECT("a"_short_string.starts_with_bytes("a"sv));
|
||
EXPECT(!"a"_short_string.starts_with_bytes("b"sv));
|
||
EXPECT(!"a"_short_string.starts_with_bytes("ab"sv));
|
||
|
||
EXPECT("a"_short_string.starts_with(0x0061));
|
||
EXPECT(!"a"_short_string.starts_with(0x0062));
|
||
|
||
EXPECT("abc"_short_string.starts_with_bytes({}));
|
||
EXPECT("abc"_short_string.starts_with_bytes("a"sv));
|
||
EXPECT("abc"_short_string.starts_with_bytes("ab"sv));
|
||
EXPECT("abc"_short_string.starts_with_bytes("abc"sv));
|
||
EXPECT(!"abc"_short_string.starts_with_bytes("b"sv));
|
||
EXPECT(!"abc"_short_string.starts_with_bytes("bc"sv));
|
||
|
||
EXPECT("abc"_short_string.starts_with(0x0061));
|
||
EXPECT(!"abc"_short_string.starts_with(0x0062));
|
||
EXPECT(!"abc"_short_string.starts_with(0x0063));
|
||
|
||
auto emoji = MUST("😀🙃"_string);
|
||
EXPECT(emoji.starts_with_bytes("\xF0"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F\x98"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F\x98\x80"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F\x98\x80\xF0"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F\x98\x80\xF0\x9F"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F\x98\x80\xF0\x9F\x99"sv));
|
||
EXPECT(emoji.starts_with_bytes("\xF0\x9F\x98\x80\xF0\x9F\x99\x83"sv));
|
||
EXPECT(!emoji.starts_with_bytes("a"sv));
|
||
EXPECT(!emoji.starts_with_bytes("🙃"sv));
|
||
|
||
EXPECT(emoji.starts_with(0x1F600));
|
||
EXPECT(!emoji.starts_with(0x1F643));
|
||
}
|
||
|
||
TEST_CASE(ends_with)
|
||
{
|
||
EXPECT(String {}.ends_with_bytes({}));
|
||
EXPECT(!String {}.ends_with_bytes(" "sv));
|
||
EXPECT(!String {}.ends_with(0));
|
||
|
||
EXPECT("a"_short_string.ends_with_bytes({}));
|
||
EXPECT("a"_short_string.ends_with_bytes("a"sv));
|
||
EXPECT(!"a"_short_string.ends_with_bytes("b"sv));
|
||
EXPECT(!"a"_short_string.ends_with_bytes("ba"sv));
|
||
|
||
EXPECT("a"_short_string.ends_with(0x0061));
|
||
EXPECT(!"a"_short_string.ends_with(0x0062));
|
||
|
||
EXPECT("abc"_short_string.ends_with_bytes({}));
|
||
EXPECT("abc"_short_string.ends_with_bytes("c"sv));
|
||
EXPECT("abc"_short_string.ends_with_bytes("bc"sv));
|
||
EXPECT("abc"_short_string.ends_with_bytes("abc"sv));
|
||
EXPECT(!"abc"_short_string.ends_with_bytes("b"sv));
|
||
EXPECT(!"abc"_short_string.ends_with_bytes("ab"sv));
|
||
|
||
EXPECT("abc"_short_string.ends_with(0x0063));
|
||
EXPECT(!"abc"_short_string.ends_with(0x0062));
|
||
EXPECT(!"abc"_short_string.ends_with(0x0061));
|
||
|
||
auto emoji = MUST("😀🙃"_string);
|
||
EXPECT(emoji.ends_with_bytes("\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\x99\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\x9F\x99\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\xF0\x9F\x99\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\x80\xF0\x9F\x99\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\x98\x80\xF0\x9F\x99\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\x9F\x98\x80\xF0\x9F\x99\x83"sv));
|
||
EXPECT(emoji.ends_with_bytes("\xF0\x9F\x98\x80\xF0\x9F\x99\x83"sv));
|
||
EXPECT(!emoji.ends_with_bytes("a"sv));
|
||
EXPECT(!emoji.ends_with_bytes("😀"sv));
|
||
|
||
EXPECT(emoji.ends_with(0x1F643));
|
||
EXPECT(!emoji.ends_with(0x1F600));
|
||
}
|