1 // Copyright 2019 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
15 #include "pw_string/string_builder.h"
22 #include <string_view>
24 #include "gtest/gtest.h"
25 #include "pw_string/format.h"
27 namespace this_pw_test {
33 static constexpr const char* kToString = "This is a CustomType";
35 CustomType() = default;
37 // Non-copyable to verify StringBuffer's << operator doesn't copy it.
38 CustomType(const CustomType&) = delete;
39 CustomType& operator=(const CustomType&) = delete;
42 } // namespace this_pw_test
47 StatusWithSize ToString<this_pw_test::CustomType>(
48 const this_pw_test::CustomType&, std::span<char> buffer) {
49 return string::Format(buffer, this_pw_test::CustomType::kToString);
57 using this_pw_test::CustomType;
59 TEST(StringBuilder, EmptyBuffer_SizeAndMaxSizeAreCorrect) {
60 StringBuilder sb(std::span<char>{});
62 EXPECT_TRUE(sb.empty());
63 EXPECT_EQ(0u, sb.size());
64 EXPECT_EQ(0u, sb.max_size());
67 using namespace std::literals::string_view_literals;
69 constexpr std::string_view kNoTouch = "DO NOT TOUCH\0VALUE SHOULD NOT CHANGE"sv;
71 TEST(StringBuilder, EmptyBuffer_StreamOutput_WritesNothing) {
72 char buffer[kNoTouch.size()];
73 std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
75 StringBuilder sb(std::span(buffer, 0));
77 sb << CustomType() << " is " << 12345;
78 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
79 EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
82 TEST(StringBuilder, EmptyBuffer_Append_WritesNothing) {
83 char buffer[kNoTouch.size()];
84 std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
86 StringBuilder sb(std::span(buffer, 0));
88 EXPECT_FALSE(sb.append("Hello").ok());
89 EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
92 TEST(StringBuilder, EmptyBuffer_Resize_WritesNothing) {
93 char buffer[kNoTouch.size()];
94 std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
96 StringBuilder sb(std::span(buffer, 0));
100 EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
103 TEST(StringBuilder, EmptyBuffer_AppendEmpty_ResourceExhausted) {
104 StringBuilder sb(std::span<char>{});
105 EXPECT_EQ(OkStatus(), sb.last_status());
106 EXPECT_EQ(OkStatus(), sb.status());
110 EXPECT_EQ(Status::ResourceExhausted(), sb.last_status());
111 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
114 TEST(StringBuilder, Status_StartsOk) {
116 EXPECT_EQ(OkStatus(), sb.status());
117 EXPECT_EQ(OkStatus(), sb.last_status());
120 TEST(StringBuilder, Status_StatusAndLastStatusUpdate) {
122 sb << "Well, if only there were enough room in here for this string";
123 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
124 EXPECT_EQ(Status::ResourceExhausted(), sb.last_status());
127 EXPECT_EQ(Status::OutOfRange(), sb.status());
128 EXPECT_EQ(Status::OutOfRange(), sb.last_status());
131 EXPECT_EQ(Status::OutOfRange(), sb.status());
132 EXPECT_EQ(OkStatus(), sb.last_status());
135 TEST(StringBuilder, Status_ClearStatus_SetsStatuesToOk) {
136 StringBuffer<2> sb = MakeString<2>("Won't fit!!!!!");
137 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
138 EXPECT_EQ(Status::ResourceExhausted(), sb.last_status());
141 EXPECT_EQ(OkStatus(), sb.status());
142 EXPECT_EQ(OkStatus(), sb.last_status());
145 TEST(StringBuilder, StreamOutput_OutputSelf) {
146 auto sb = MakeString<32>("echo!");
149 EXPECT_STREQ("echo!echo!", sb.data());
150 EXPECT_EQ(10u, sb.size());
153 TEST(StringBuilder, PushBack) {
156 EXPECT_EQ(OkStatus(), sb.last_status());
157 EXPECT_EQ(1u, sb.size());
158 EXPECT_STREQ("?", sb.data());
161 TEST(StringBuilder, PushBack_Full) {
164 EXPECT_EQ(Status::ResourceExhausted(), sb.last_status());
165 EXPECT_EQ(0u, sb.size());
168 TEST(StringBuilder, PopBack) {
169 auto sb = MakeString<12>("Welcome!");
171 EXPECT_EQ(OkStatus(), sb.last_status());
172 EXPECT_EQ(7u, sb.size());
173 EXPECT_STREQ("Welcome", sb.data());
176 TEST(StringBuilder, PopBack_Empty) {
179 EXPECT_EQ(Status::OutOfRange(), sb.last_status());
180 EXPECT_EQ(0u, sb.size());
183 TEST(StringBuilder, Append_NonTerminatedString) {
184 static char bad_string[256];
185 std::memset(bad_string, '?', sizeof(bad_string));
188 EXPECT_EQ(Status::ResourceExhausted(), sb.append(bad_string).last_status());
189 EXPECT_STREQ("?????", sb.data());
192 TEST(StringBuilder, Append_Chars) {
195 EXPECT_TRUE(sb.append(7, '?').ok());
196 EXPECT_STREQ("???????", sb.data());
199 TEST(StringBuilder, Append_Chars_Full) {
202 EXPECT_EQ(Status::ResourceExhausted(), sb.append(8, '?').last_status());
203 EXPECT_STREQ("???????", sb.data());
206 TEST(StringBuilder, Append_PartialCString) {
208 EXPECT_TRUE(sb.append("123456", 4).ok());
209 EXPECT_EQ(4u, sb.size());
210 EXPECT_STREQ("1234", sb.data());
213 TEST(StringBuilder, Append_CString) {
214 auto sb = MakeString("hello");
215 EXPECT_TRUE(sb.append(" goodbye").ok());
216 EXPECT_STREQ("hello goodbye", sb.data());
217 EXPECT_EQ(13u, sb.size());
220 TEST(StringBuilder, Append_CString_Full) {
221 auto sb = MakeString<6>("hello");
222 EXPECT_EQ(Status::ResourceExhausted(), sb.append("890123", 1).last_status());
223 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
224 EXPECT_EQ(sb.max_size(), sb.size());
225 EXPECT_STREQ("hello", sb.data());
228 TEST(StringBuilder, Append_StringView) {
229 auto sb = MakeString<32>("hello");
230 EXPECT_TRUE(sb.append("???"sv).ok());
231 EXPECT_EQ("hello???"sv, sb);
234 TEST(StringBuilder, Append_StringView_Substring) {
235 auto sb = MakeString<32>("I like ");
236 EXPECT_TRUE(sb.append("your shoes!!!"sv, 5, 5).ok());
237 EXPECT_EQ("I like shoes"sv, sb);
240 TEST(StringBuilder, Append_StringView_RemainingSubstring) {
241 auto sb = MakeString<32>("I like ");
242 EXPECT_TRUE(sb.append("your shoes!!!"sv, 5).ok());
243 EXPECT_EQ("I like shoes!!!"sv, sb);
246 TEST(StringBuilder, Resize_Smaller) {
247 auto sb = MakeString<12>("Four");
249 EXPECT_TRUE(sb.ok());
250 EXPECT_EQ(2u, sb.size());
251 EXPECT_STREQ("Fo", sb.data());
254 TEST(StringBuilder, Resize_Clear) {
255 auto sb = MakeString<12>("Four");
257 EXPECT_TRUE(sb.ok());
258 EXPECT_EQ(0u, sb.size());
259 EXPECT_STREQ("", sb.data());
262 TEST(StringBuilder, Resize_Larger_Fails) {
263 auto sb = MakeString<12>("Four");
264 EXPECT_EQ(4u, sb.size());
266 EXPECT_EQ(sb.status(), Status::OutOfRange());
267 EXPECT_EQ(4u, sb.size());
270 TEST(StringBuilder, Resize_LargerThanCapacity_Fails) {
271 auto sb = MakeString<12>("Four");
273 EXPECT_EQ(sb.status(), Status::OutOfRange());
274 EXPECT_EQ(4u, sb.size());
275 EXPECT_STREQ("Four", sb.data());
278 TEST(StringBuilder, Format_Normal) {
279 std::byte buffer[64];
280 StringBuilder sb(buffer);
281 EXPECT_TRUE(sb.Format("0x%x", 0xabc).ok());
282 EXPECT_STREQ("0xabc", sb.data());
286 EXPECT_TRUE(sb.Format("GHI").ok());
287 EXPECT_STREQ("0xabcdefGHI", sb.data());
290 TEST(StringBuilder, Format_ExhaustBuffer) {
292 EXPECT_EQ(Status::ResourceExhausted(), sb.Format("012345").status());
294 EXPECT_STREQ("01234", sb.data());
295 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
298 TEST(StringBuilder, StreamOutput_MultipleTypes) {
299 constexpr const char* kExpected = "This is -1true example\n of this";
300 constexpr const char* kExample = "example";
303 sb << "This is " << -1 << true << ' ' << kExample << '\n' << " of this";
305 EXPECT_STREQ(kExpected, sb.data());
306 EXPECT_EQ(std::strlen(kExpected), sb.size());
309 TEST(StringBuilder, StreamOutput_FullBufferIgnoresExtraStrings) {
311 EXPECT_EQ(5u, sb.max_size()); // max_size() excludes the null terminator
314 EXPECT_TRUE(sb.ok());
315 EXPECT_STREQ("0", sb.data());
317 sb << true << "Now it's way " << static_cast<unsigned char>(2) << " long";
318 EXPECT_FALSE(sb.ok());
319 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
320 EXPECT_STREQ("0true", sb.data());
323 TEST(StringBuilder, StreamOutput_ExhaustBuffer_InOneString) {
325 EXPECT_EQ(8u, sb.max_size());
327 sb << "0123456789"; // write 10 chars
328 EXPECT_FALSE(sb.ok());
329 EXPECT_STREQ("01234567", sb.data()); // only can fit 8
330 EXPECT_EQ(8u, sb.size());
335 EXPECT_STREQ("01234567", sb.data());
338 TEST(StringBuilder, StreamOutput_ExhaustBuffer_InTwoStrings) {
341 sb << "01"; // fill 3/4 of buffer
342 EXPECT_EQ(2u, sb.size());
344 EXPECT_STREQ("012", sb.data());
345 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
346 EXPECT_EQ(3u, sb.size());
349 TEST(StringBuilder, StreamOutput_NonTerminatedString) {
350 static char bad_string[256];
351 std::memset(bad_string, '?', sizeof(bad_string));
354 sb << "hey" << bad_string;
356 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
357 EXPECT_STREQ("hey??", sb.data());
360 TEST(StringBuilder, SteamOutput_StringView) {
361 StringBuffer<6> buffer;
362 constexpr std::string_view hello("hello");
365 EXPECT_EQ(OkStatus(), buffer.status());
366 EXPECT_STREQ("hello", buffer.data());
369 TEST(StringBuilder, StreamOutput_EmptyStringView) {
370 StringBuffer<4> buffer;
371 buffer << "hi" << std::string_view() << "!";
372 EXPECT_TRUE(buffer.ok());
373 EXPECT_STREQ("hi!", buffer.data());
376 TEST(StringBuffer, Assign) {
377 StringBuffer<10> one;
378 StringBuffer<10> two;
381 ASSERT_STREQ("What", one.data());
383 EXPECT_STREQ("What", two.data());
384 EXPECT_NE(one.data(), two.data());
388 EXPECT_STREQ("What the", one.data());
389 EXPECT_STREQ("What heck", two.data());
392 ASSERT_STREQ("What heck", two.data());
393 ASSERT_EQ(Status::ResourceExhausted(), two.status());
394 ASSERT_EQ(Status::ResourceExhausted(), two.last_status());
397 EXPECT_STREQ("What heck", one.data());
398 EXPECT_EQ(Status::ResourceExhausted(), one.status());
399 EXPECT_EQ(Status::ResourceExhausted(), one.last_status());
401 StringBuffer<12> three;
403 EXPECT_STREQ(three.data(), two.data());
404 EXPECT_EQ(three.size(), two.size());
407 TEST(StringBuffer, CopyConstructFromSameSize) {
408 StringBuffer<10> one;
411 ASSERT_STREQ("What", one.data());
412 StringBuffer<10> two(one);
413 EXPECT_STREQ("What", two.data());
414 EXPECT_NE(one.data(), two.data());
418 EXPECT_STREQ("What the", one.data());
419 EXPECT_STREQ("What heck", two.data());
423 ASSERT_STREQ("What heck", two.data());
424 ASSERT_EQ(Status::ResourceExhausted(), two.status());
425 ASSERT_EQ(OkStatus(), two.last_status());
428 TEST(StringBuffer, CopyConstructFromSmaller) {
429 StringBuffer<10> one = MakeString<10>("You are the chosen one.");
430 StringBuffer<12> two(one);
432 EXPECT_STREQ("You are t", two.data());
433 EXPECT_EQ(Status::ResourceExhausted(), two.status());
436 TEST(StringBuilder, Object) {
440 EXPECT_STREQ(CustomType::kToString, sb.data());
441 EXPECT_EQ(std::strlen(CustomType::kToString), sb.size());
444 TEST(MakeString, Object) {
446 const auto sb = MakeString<64>(custom);
448 EXPECT_STREQ(CustomType::kToString, sb.data());
449 EXPECT_EQ(std::strlen(CustomType::kToString), sb.size());
452 TEST(MakeString, IntegerTypes) {
453 EXPECT_STREQ("0123-4567",
459 static_cast<unsigned short>(5),
460 static_cast<short>(6),
461 static_cast<unsigned char>(7))
465 TEST(MakeString, Char) {
466 EXPECT_STREQ("a b c", MakeString('a', ' ', 'b', ' ', 'c').data());
469 TEST(MakeString, Float) { EXPECT_STREQ("-inf", MakeString(-INFINITY).data()); }
471 TEST(MakeString, Pointer_Null) {
472 EXPECT_STREQ("(null)", MakeString(nullptr).data());
473 EXPECT_STREQ("(null)", MakeString(static_cast<void*>(nullptr)).data());
476 TEST(MakeString, Pointer_NonNull) {
477 EXPECT_STREQ("1", MakeString(reinterpret_cast<void*>(0x1)).data());
478 EXPECT_STREQ("123", MakeString(reinterpret_cast<int*>(0x123)).data());
481 TEST(MakeString, Pointer_CustomType) {
482 char expected[32] = {};
485 std::snprintf(expected,
488 reinterpret_cast<uintptr_t>(&custom));
490 EXPECT_STREQ(expected, MakeString(&custom).data());
493 TEST(MakeString, Bool) {
494 EXPECT_STREQ("true", MakeString(true).data());
495 EXPECT_STREQ("false", MakeString(false).data());
498 TEST(MakeString, MutableString) {
499 char chars[] = {'C', 'o', 'o', 'l', '\0'};
500 EXPECT_STREQ("Cool?", MakeString(chars, "?").data());
503 TEST(MakeString, Empty_IsEmpty) { EXPECT_TRUE(MakeString().empty()); }
505 constexpr char kLongestString[] = "18446744073709551615"; // largest uint64_t
507 TEST(MakeString, DefaultSizeString_FitsWholeString) {
510 MakeString(184, "467", u'\x04', "40", '7', '3', '7', "0", "", 955ul, 1615)
514 TEST(MakeString, LargerThanDefaultSize_Truncates) {
515 auto sb = MakeString("1844674407", 3709551615, 123456);
517 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
518 EXPECT_STREQ(kLongestString, sb.data());
521 TEST(MakeString, StringLiteral_ResizesToFitWholeLiteral) {
522 EXPECT_STREQ("", MakeString().data());
524 auto normal = MakeString("");
525 static_assert(normal.max_size() == decltype(MakeString(1))::max_size());
527 auto resized = MakeString("This string is reeeeeeeeeaaaaallly long!!!!!");
528 static_assert(resized.max_size() > decltype(MakeString(1))::max_size());
529 static_assert(resized.max_size() ==
530 sizeof("This string is reeeeeeeeeaaaaallly long!!!!!") - 1);
533 TEST(MakeString, StringLiteral_UsesLongerFixedSize) {
534 auto fixed_size = MakeString<64>("");
535 static_assert(fixed_size.max_size() == 63u);
536 EXPECT_EQ(fixed_size.max_size(), 63u);
537 EXPECT_STREQ("", fixed_size.data());
540 TEST(MakeString, StringLiteral_TruncatesShorterFixedSize) {
541 EXPECT_STREQ("Goo", MakeString<4>("Google").data());
542 EXPECT_STREQ("Google", MakeString<7>("Google").data());
543 EXPECT_EQ(MakeString().max_size(), MakeString("Google").max_size());
544 EXPECT_STREQ("Google", MakeString("Google").data());
547 TEST(MakeString, DefaultSize_FitsMaxAndMinInts) {
548 EXPECT_STREQ("-9223372036854775808",
549 MakeString(std::numeric_limits<int64_t>::min()).data());
550 EXPECT_STREQ("18446744073709551615",
551 MakeString(std::numeric_limits<uint64_t>::max()).data());
554 TEST(MakeString, OutputToTemporaryStringBuffer) {
555 EXPECT_STREQ("hello", (MakeString<6>("hello ") << "world").data());
556 EXPECT_STREQ("hello world", (MakeString("hello ") << "world").data());
559 // Test MakeString's default size calculations.
560 template <typename... Args>
561 constexpr size_t DefaultStringBufferSize(Args&&...) {
562 return string_internal::DefaultStringBufferSize<Args...>();
565 // Default sizes are rounded up to 24 bytes.
566 static_assert(DefaultStringBufferSize("") == 24);
567 static_assert(DefaultStringBufferSize("123") == 24);
568 static_assert(DefaultStringBufferSize("123", "456", "78901234567890") == 24);
569 static_assert(DefaultStringBufferSize("1234567890", "1234567890", "123") == 24);
570 static_assert(DefaultStringBufferSize(1234, 5678, 9012) == 24);
572 // The buffer is sized to fix strings needing more than 24 bytes.
573 static_assert(DefaultStringBufferSize("1234567890", "1234567890", "1234") ==
575 static_assert(DefaultStringBufferSize("1234567890", "1234567890", "12345") ==
577 static_assert(DefaultStringBufferSize("1234567890", "1234567890", "12345678") ==
580 // Four bytes are allocated for each non-string argument.
581 static_assert(DefaultStringBufferSize(1234, 5678, 9012, 3456, 7890, 1234) ==
583 static_assert(DefaultStringBufferSize('a', nullptr, 'b', 4, 5, 6, 7, 8) == 33);