Files
filament/libs/utils/test/test_CString.cpp
2025-09-26 08:07:55 -07:00

619 lines
17 KiB
C++

/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <utils/CString.h>
#include <algorithm>
#include <climits>
#include <utility>
#include <type_traits>
using namespace utils;
TEST(CString, EmptyString) {
CString const emptyString("");
EXPECT_STREQ("", emptyString.c_str_safe());
EXPECT_EQ(0, emptyString.length());
EXPECT_TRUE(emptyString.empty());
}
TEST(CString, Constructors) {
// CString()
{
CString str;
EXPECT_EQ(nullptr, str.c_str());
EXPECT_STREQ("", str.c_str_safe());
EXPECT_EQ(0, str.length());
EXPECT_TRUE(str.empty());
}
// CString(const char* cstr, size_t length)
{
CString str("foobar", 3);
EXPECT_STREQ("foo", str.c_str());
EXPECT_EQ(3, str.length());
}
// CString(size_t length)
{
CString str(5);
EXPECT_EQ(5, str.length());
// The memory is zero-initialized
EXPECT_EQ(0, str.c_str()[0]);
EXPECT_EQ(0, str.c_str()[4]);
}
// CString(const char* cstr)
{
const char* hello_cstr = "hello";
CString str(hello_cstr);
EXPECT_STREQ("hello", str.c_str());
EXPECT_EQ(5, str.length());
}
// CString(StringLiteral<N>)
{
CString str("literal"); // this uses the template constructor
EXPECT_STREQ("literal", str.c_str());
EXPECT_EQ(7, str.length());
}
// Copy constructor
{
CString s1("copy me");
CString s2(s1);
EXPECT_STREQ("copy me", s2.c_str());
EXPECT_NE(s1.c_str(), s2.c_str()); // should be a deep copy
}
// Move constructor
{
CString s1("move me");
const char* original_cstr = s1.c_str();
CString s2(std::move(s1));
EXPECT_STREQ("move me", s2.c_str());
EXPECT_EQ(original_cstr, s2.c_str()); // pointer should be moved
EXPECT_EQ(nullptr, s1.c_str()); // original should be empty
}
}
TEST(CString, LiteralConstructor) {
// This test verifies that CString can be constructed from a string literal,
// and that this uses the desired template constructor, not the explicit
// (and costly) `const char*` constructor.
// Test copy-initialization. CString should be convertible from a string literal.
// This works because "literal" has type `const char[N]` which matches
// `StringLiteral<N>` and calls the non-explicit template constructor.
static_assert(std::is_convertible_v<decltype("literal"), CString>,
"CString should be convertible from a string literal.");
CString s_copy = "literal";
EXPECT_STREQ("literal", s_copy.c_str());
EXPECT_EQ(7, s_copy.length());
// Test direct-initialization. This should also use the same efficient constructor.
static_assert(std::is_constructible_v<CString, decltype("literal")>,
"CString should be constructible from a string literal.");
CString s_direct("literal");
EXPECT_STREQ("literal", s_direct.c_str());
EXPECT_EQ(7, s_direct.length());
// Verify that direct-initialization uses the sized constructor by using an embedded null.
// If the strlen() constructor were used, the length would be 3.
// The size of "abc\0def" is 8 (including the terminating null). The constructor
// calculates the length as N-1 = 7.
CString const s_direct_null("abc\0def");
EXPECT_EQ(7, s_direct_null.length());
// CString should NOT be convertible from a `const char*`.
// This is because the `const char*` constructor is explicit.
static_assert(!std::is_convertible_v<const char*, CString>,
"CString should not be convertible from a const char*.");
// This demonstrates the non-convertibility. The following line would fail to compile:
// const char* cstr = "hello";
// CString s2 = cstr;
// Explicit construction from a const char* should still work (via direct-initialization).
const char* cstr = "explicit";
CString s2(cstr);
EXPECT_STREQ("explicit", s2.c_str());
EXPECT_EQ(8, s2.length());
}
TEST(CString, Assignment) {
// Copy assignment
{
CString s1("copy");
CString s2;
s2 = s1;
EXPECT_STREQ("copy", s2.c_str());
EXPECT_NE(s1.c_str(), s2.c_str());
}
// Move assignment
{
CString s1("move");
const char* original_cstr = s1.c_str();
CString s2;
s2 = std::move(s1);
EXPECT_STREQ("move", s2.c_str());
EXPECT_EQ(original_cstr, s2.c_str());
EXPECT_EQ(nullptr, s1.c_str());
}
// self-assignment
{
CString s1("self");
const char* original_cstr = s1.c_str();
s1 = s1;
EXPECT_STREQ("self", s1.c_str());
EXPECT_EQ(original_cstr, s1.c_str());
}
}
TEST(CString, Swap) {
CString s1("first");
CString s2("second");
const char* p1 = s1.c_str();
const char* p2 = s2.c_str();
size_t l1 = s1.length();
size_t l2 = s2.length();
s1.swap(s2);
EXPECT_STREQ("second", s1.c_str());
EXPECT_STREQ("first", s2.c_str());
EXPECT_EQ(p2, s1.c_str());
EXPECT_EQ(p1, s2.c_str());
EXPECT_EQ(l2, s1.length());
EXPECT_EQ(l1, s2.length());
}
TEST(CString, Concatenation) {
// operator+=
{
CString s("hello");
s += CString(" world");
EXPECT_STREQ("hello world", s.c_str());
}
{
CString s("hello");
s += " world";
EXPECT_STREQ("hello world", s.c_str());
}
{
CString s("hello");
const char* world = " world";
s += world;
EXPECT_STREQ("hello world", s.c_str());
}
{
CString s("hello");
s += "";
EXPECT_STREQ("hello", s.c_str());
}
{
CString s;
s += "world";
EXPECT_STREQ("world", s.c_str());
}
// operator+
{
CString s1("hello");
CString s2(" world");
CString s3 = s1 + s2;
EXPECT_STREQ("hello world", s3.c_str());
}
{
CString s1("hello");
CString s2 = s1 + " world";
EXPECT_STREQ("hello world", s2.c_str());
}
{
CString s1(" world");
CString s2 = "hello" + s1;
EXPECT_STREQ("hello world", s2.c_str());
}
{
CString s1("hello");
const char* world = " world";
CString s2 = s1 + world;
EXPECT_STREQ("hello world", s2.c_str());
}
{
CString s1(" world");
const char* hello = "hello";
CString s2 = hello + s1;
EXPECT_STREQ("hello world", s2.c_str());
}
{
CString s = CString("a") + CString("b") + "c" + "d";
EXPECT_STREQ("abcd", s.c_str());
}
}
TEST(CString, Comparison) {
CString s1("abc");
CString s2("abc");
CString s3("def");
CString s4("ab");
EXPECT_TRUE(s1 == s2);
EXPECT_FALSE(s1 == s3);
EXPECT_TRUE(s1 != s3);
EXPECT_FALSE(s1 != s2);
EXPECT_TRUE(s1 < s3);
EXPECT_FALSE(s3 < s1);
EXPECT_TRUE(s3 > s1);
EXPECT_FALSE(s1 > s3);
EXPECT_TRUE(s1 <= s2);
EXPECT_TRUE(s1 <= s3);
EXPECT_FALSE(s3 <= s1);
EXPECT_TRUE(s2 >= s1);
EXPECT_TRUE(s3 >= s1);
EXPECT_FALSE(s1 >= s3);
EXPECT_TRUE(s4 < s1);
EXPECT_TRUE(s1 > s4);
}
TEST(CString, ElementAccess) {
CString str("01234");
const CString cstr("const");
EXPECT_EQ('0', str.front());
EXPECT_EQ('4', str.back());
EXPECT_EQ('c', cstr.front());
EXPECT_EQ('t', cstr.back());
EXPECT_EQ('2', str[2]);
EXPECT_EQ('n', cstr[2]);
str[0] = 'A';
EXPECT_STREQ("A1234", str.c_str());
EXPECT_EQ('3', str.at(3));
EXPECT_EQ('t', cstr.at(4));
// iterators
std::string s(str.begin(), str.end());
EXPECT_EQ("A1234", s);
EXPECT_TRUE(std::equal(cstr.begin(), cstr.end(), "const"));
}
TEST(CString, Append) {
// Append CString
{
CString str("foo");
str.append(CString("bar"));
EXPECT_STREQ("foobar", str.c_str());
}
// Append string literal
{
CString str("foo");
str.append("bar");
EXPECT_STREQ("foobar", str.c_str());
}
// Append const char*
{
CString str("foo");
const char* bar = "bar";
str.append(bar);
EXPECT_STREQ("foobar", str.c_str());
}
// Append nullptr
{
CString str("foo");
const char* bar = nullptr;
str.append(bar);
EXPECT_STREQ("foo", str.c_str());
}
// Append to empty
{
CString str;
str.append("foo");
EXPECT_STREQ("foo", str.c_str());
}
{
CString str;
str.append(CString("foo"));
EXPECT_STREQ("foo", str.c_str());
}
{
CString str;
const char* foo = "foo";
str.append(foo);
EXPECT_STREQ("foo", str.c_str());
}
// Append empty
{
CString str("foo");
str.append("");
EXPECT_STREQ("foo", str.c_str());
}
{
CString str("foo");
str.append(CString(""));
EXPECT_STREQ("foo", str.c_str());
}
{
CString str("foo");
const char* empty = "";
str.append(empty);
EXPECT_STREQ("foo", str.c_str());
}
// Chaining
{
CString str;
str.append("foo").append("bar").append(CString("baz"));
EXPECT_STREQ("foobarbaz", str.c_str());
}
// r-value appends
{
CString str = CString("foo").append("bar");
EXPECT_STREQ("foobar", str.c_str());
}
}
TEST(CString, Insert) {
// with CString
{
CString str("foobaz");
str.insert(3, CString("bar"));
EXPECT_STREQ("foobarbaz", str.c_str());
}
// with string literal
{
CString str("world");
str.insert(0, "hello ");
EXPECT_STREQ("hello world", str.c_str());
}
// with const char*
{
CString str("foo");
const char* bar = "bar";
str.insert(3, bar);
EXPECT_STREQ("foobar", str.c_str());
}
// with nullptr
{
CString str("foo");
const char* bar = nullptr;
str.insert(1, bar);
EXPECT_STREQ("foo", str.c_str());
}
// r-value insert
{
const char* bar = "bar";
CString str = CString("foo").insert(3, bar);
EXPECT_STREQ("foobar", str.c_str());
}
}
TEST(CString, Replace) {
// with CString
{
CString str("foo bar baz");
str.replace(4, 3, CString("dpa"));
EXPECT_STREQ("foo dpa baz", str.c_str());
}
// with string literal
{
CString str("foo bar baz");
str.replace(4, 3, "dpa");
EXPECT_STREQ("foo dpa baz", str.c_str());
}
// with const char*
{
CString str("foo bar baz");
const char* dpa = "dpa";
str.replace(4, 3, dpa);
EXPECT_STREQ("foo dpa baz", str.c_str());
}
// with nullptr
{
CString str("foo bar baz");
const char* dpa = nullptr;
str.replace(4, 3, dpa);
EXPECT_STREQ("foo baz", str.c_str());
}
// with empty string
{
CString str("foo bar baz");
str.replace(4, 3, "");
EXPECT_STREQ("foo baz", str.c_str());
}
// replace that grows the string
{
CString str("foo bar baz");
str.replace(4, 3, "abcdef");
EXPECT_STREQ("foo abcdef baz", str.c_str());
}
// replace that shrinks the string
{
CString str("foo bar baz");
str.replace(4, 3, "a");
EXPECT_STREQ("foo a baz", str.c_str());
}
// replace at the beginning
{
CString str("foo bar baz");
str.replace(0, 3, "lkj");
EXPECT_STREQ("lkj bar baz", str.c_str());
}
// replace at the end
{
CString str("foo bar baz");
str.replace(8, 3, "lkj");
EXPECT_STREQ("foo bar lkj", str.c_str());
}
// replace all
{
CString str("foo bar baz");
str.replace(0, 11, "lkj");
EXPECT_STREQ("lkj", str.c_str());
}
// r-value replace
{
const char* dpa = "dpa";
CString str = CString("foo bar baz").replace(4, 3, dpa);
EXPECT_STREQ("foo dpa baz", str.c_str());
}
}
TEST(CString, ReplaceInPlace) {
// Shrinking replacement
{
CString str("0123456789");
const char* const original_cstr = str.c_str();
str.replace(3, 3, "ab");
EXPECT_STREQ("012ab6789", str.c_str());
EXPECT_EQ(9, str.length());
EXPECT_EQ(original_cstr, str.c_str());
}
{
CString str("0123456789");
const char* const original_cstr = str.c_str();
str.replace(0, 3, "ab");
EXPECT_STREQ("ab3456789", str.c_str());
EXPECT_EQ(9, str.length());
EXPECT_EQ(original_cstr, str.c_str());
}
{
CString str("0123456789");
const char* const original_cstr = str.c_str();
str.replace(7, 3, "ab");
EXPECT_STREQ("0123456ab", str.c_str());
EXPECT_EQ(9, str.length());
EXPECT_EQ(original_cstr, str.c_str());
}
{
CString str("0123456789");
const char* const original_cstr = str.c_str();
str.replace(0, 10, "ab");
EXPECT_STREQ("ab", str.c_str());
EXPECT_EQ(2, str.length());
EXPECT_EQ(original_cstr, str.c_str());
}
// Same size replacement
{
CString str("0123456789");
const char* const original_cstr = str.c_str();
str.replace(0, 10, "abcdefghij");
EXPECT_STREQ("abcdefghij", str.c_str());
EXPECT_EQ(10, str.length());
EXPECT_EQ(original_cstr, str.c_str());
}
// Shrink to empty
{
CString str("0123456789");
const char* const original_cstr = str.c_str();
str.replace(3, 3, "");
EXPECT_STREQ("0126789", str.c_str());
EXPECT_EQ(7, str.length());
EXPECT_EQ(original_cstr, str.c_str());
}
}
TEST(CString, ReplaceZeroLength) {
{
std::string str("foobar");
str.replace(6, 0, "abc");
EXPECT_STREQ("foobarabc", str.c_str());
}
{
CString str;
str.replace(0, 0, CString("far"));
EXPECT_STREQ("far", str.c_str());
}
}
TEST(CString, ReplacePastEndOfString) {
{
CString str("foo bar baz");
str.replace(0, 100, CString("bat"));
EXPECT_STREQ("bat", str.c_str());
}
{
CString str("foo bar baz");
str.replace(8, 100, CString("bat"));
EXPECT_STREQ("foo bar bat", str.c_str());
}
}
TEST(CString, ToString) {
EXPECT_STREQ("0", to_string(0).c_str());
EXPECT_STREQ("123", to_string(123).c_str());
EXPECT_STREQ("-456", to_string(-456).c_str());
EXPECT_STREQ("2147483647", to_string(INT_MAX).c_str());
EXPECT_STREQ("-2147483648", to_string(INT_MIN).c_str());
EXPECT_STREQ("0", to_string(0u).c_str());
EXPECT_STREQ("4294967295", to_string(UINT_MAX).c_str());
EXPECT_STREQ("0", to_string((short)0).c_str());
EXPECT_STREQ("32767", to_string(SHRT_MAX).c_str());
EXPECT_STREQ("-32768", to_string(SHRT_MIN).c_str());
EXPECT_STREQ("0", to_string((unsigned short)0).c_str());
EXPECT_STREQ("65535", to_string(USHRT_MAX).c_str());
#if LONG_MAX == 2147483647
EXPECT_STREQ("2147483647", to_string(LONG_MAX).c_str());
EXPECT_STREQ("-2147483648", to_string(LONG_MIN).c_str());
EXPECT_STREQ("4294967295", to_string(ULONG_MAX).c_str());
#else
EXPECT_STREQ("9223372036854775807", to_string(LONG_MAX).c_str());
EXPECT_STREQ("-9223372036854775808", to_string(LONG_MIN).c_str());
EXPECT_STREQ("18446744073709551615", to_string(ULONG_MAX).c_str());
#endif
EXPECT_STREQ("9223372036854775807", to_string(LLONG_MAX).c_str());
EXPECT_STREQ("-9223372036854775808", to_string(LLONG_MIN).c_str());
EXPECT_STREQ("18446744073709551615", to_string(ULLONG_MAX).c_str());
EXPECT_STREQ("0.000000", to_string(0.0f).c_str());
EXPECT_STREQ("1.500000", to_string(1.5f).c_str());
EXPECT_STREQ("-3.140000", to_string(-3.14f).c_str());
}
TEST(FixedSizeString, EmptyString) {
{
FixedSizeString<32> str;
EXPECT_STREQ("", str.c_str());
}
{
FixedSizeString<32> str("");
EXPECT_STREQ("", str.c_str());
}
}
TEST(FixedSizeString, Constructors) {
{
FixedSizeString<32> str("short string");
EXPECT_STREQ("short string", str.c_str());
}
{
FixedSizeString<16> str("a long string abcdefghijklmnopqrst");
EXPECT_STREQ("a long string a", str.c_str());
}
}