1 // Copyright 2019 The Pigweed Authors
2 //
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
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
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
13 // the License.
14
15 #include "pw_string/string_builder.h"
16
17 #include <cinttypes>
18 #include <cmath>
19 #include <cstdint>
20 #include <cstring>
21 #include <span>
22 #include <string_view>
23
24 #include "gtest/gtest.h"
25 #include "pw_string/format.h"
26
27 namespace this_pw_test {
28
29 struct CustomType {
30 uint32_t a;
31 uint32_t b;
32
33 static constexpr const char* kToString = "This is a CustomType";
34
35 CustomType() = default;
36
37 // Non-copyable to verify StringBuffer's << operator doesn't copy it.
38 CustomType(const CustomType&) = delete;
39 CustomType& operator=(const CustomType&) = delete;
40 };
41
42 } // namespace this_pw_test
43
44 namespace pw {
45
46 template <>
ToString(const this_pw_test::CustomType &,std::span<char> buffer)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);
50 }
51
52 } // namespace pw
53
54 namespace pw {
55 namespace {
56
57 using this_pw_test::CustomType;
58
TEST(StringBuilder,EmptyBuffer_SizeAndMaxSizeAreCorrect)59 TEST(StringBuilder, EmptyBuffer_SizeAndMaxSizeAreCorrect) {
60 StringBuilder sb(std::span<char>{});
61
62 EXPECT_TRUE(sb.empty());
63 EXPECT_EQ(0u, sb.size());
64 EXPECT_EQ(0u, sb.max_size());
65 }
66
67 using namespace std::literals::string_view_literals;
68
69 constexpr std::string_view kNoTouch = "DO NOT TOUCH\0VALUE SHOULD NOT CHANGE"sv;
70
TEST(StringBuilder,EmptyBuffer_StreamOutput_WritesNothing)71 TEST(StringBuilder, EmptyBuffer_StreamOutput_WritesNothing) {
72 char buffer[kNoTouch.size()];
73 std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
74
75 StringBuilder sb(std::span(buffer, 0));
76
77 sb << CustomType() << " is " << 12345;
78 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
79 EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
80 }
81
TEST(StringBuilder,EmptyBuffer_Append_WritesNothing)82 TEST(StringBuilder, EmptyBuffer_Append_WritesNothing) {
83 char buffer[kNoTouch.size()];
84 std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
85
86 StringBuilder sb(std::span(buffer, 0));
87
88 EXPECT_FALSE(sb.append("Hello").ok());
89 EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
90 }
91
TEST(StringBuilder,EmptyBuffer_Resize_WritesNothing)92 TEST(StringBuilder, EmptyBuffer_Resize_WritesNothing) {
93 char buffer[kNoTouch.size()];
94 std::memcpy(buffer, kNoTouch.data(), sizeof(buffer));
95
96 StringBuilder sb(std::span(buffer, 0));
97
98 sb.resize(0);
99 EXPECT_TRUE(sb.ok());
100 EXPECT_EQ(kNoTouch, std::string_view(buffer, sizeof(buffer)));
101 }
102
TEST(StringBuilder,EmptyBuffer_AppendEmpty_ResourceExhausted)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());
107
108 sb << "";
109
110 EXPECT_EQ(Status::ResourceExhausted(), sb.last_status());
111 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
112 }
113
TEST(StringBuilder,Status_StartsOk)114 TEST(StringBuilder, Status_StartsOk) {
115 StringBuffer<16> sb;
116 EXPECT_EQ(OkStatus(), sb.status());
117 EXPECT_EQ(OkStatus(), sb.last_status());
118 }
119
TEST(StringBuilder,Status_StatusAndLastStatusUpdate)120 TEST(StringBuilder, Status_StatusAndLastStatusUpdate) {
121 StringBuffer<16> sb;
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());
125
126 sb.resize(1029);
127 EXPECT_EQ(Status::OutOfRange(), sb.status());
128 EXPECT_EQ(Status::OutOfRange(), sb.last_status());
129
130 sb << "";
131 EXPECT_EQ(Status::OutOfRange(), sb.status());
132 EXPECT_EQ(OkStatus(), sb.last_status());
133 }
134
TEST(StringBuilder,Status_ClearStatus_SetsStatuesToOk)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());
139
140 sb.clear_status();
141 EXPECT_EQ(OkStatus(), sb.status());
142 EXPECT_EQ(OkStatus(), sb.last_status());
143 }
144
TEST(StringBuilder,StreamOutput_OutputSelf)145 TEST(StringBuilder, StreamOutput_OutputSelf) {
146 auto sb = MakeString<32>("echo!");
147 sb << sb;
148
149 EXPECT_STREQ("echo!echo!", sb.data());
150 EXPECT_EQ(10u, sb.size());
151 }
152
TEST(StringBuilder,PushBack)153 TEST(StringBuilder, PushBack) {
154 StringBuffer<12> sb;
155 sb.push_back('?');
156 EXPECT_EQ(OkStatus(), sb.last_status());
157 EXPECT_EQ(1u, sb.size());
158 EXPECT_STREQ("?", sb.data());
159 }
160
TEST(StringBuilder,PushBack_Full)161 TEST(StringBuilder, PushBack_Full) {
162 StringBuffer<1> sb;
163 sb.push_back('!');
164 EXPECT_EQ(Status::ResourceExhausted(), sb.last_status());
165 EXPECT_EQ(0u, sb.size());
166 }
167
TEST(StringBuilder,PopBack)168 TEST(StringBuilder, PopBack) {
169 auto sb = MakeString<12>("Welcome!");
170 sb.pop_back();
171 EXPECT_EQ(OkStatus(), sb.last_status());
172 EXPECT_EQ(7u, sb.size());
173 EXPECT_STREQ("Welcome", sb.data());
174 }
175
TEST(StringBuilder,PopBack_Empty)176 TEST(StringBuilder, PopBack_Empty) {
177 StringBuffer<12> sb;
178 sb.pop_back();
179 EXPECT_EQ(Status::OutOfRange(), sb.last_status());
180 EXPECT_EQ(0u, sb.size());
181 }
182
TEST(StringBuilder,Append_NonTerminatedString)183 TEST(StringBuilder, Append_NonTerminatedString) {
184 static char bad_string[256];
185 std::memset(bad_string, '?', sizeof(bad_string));
186
187 StringBuffer<6> sb;
188 EXPECT_EQ(Status::ResourceExhausted(), sb.append(bad_string).last_status());
189 EXPECT_STREQ("?????", sb.data());
190 }
191
TEST(StringBuilder,Append_Chars)192 TEST(StringBuilder, Append_Chars) {
193 StringBuffer<8> sb;
194
195 EXPECT_TRUE(sb.append(7, '?').ok());
196 EXPECT_STREQ("???????", sb.data());
197 }
198
TEST(StringBuilder,Append_Chars_Full)199 TEST(StringBuilder, Append_Chars_Full) {
200 StringBuffer<8> sb;
201
202 EXPECT_EQ(Status::ResourceExhausted(), sb.append(8, '?').last_status());
203 EXPECT_STREQ("???????", sb.data());
204 }
205
TEST(StringBuilder,Append_PartialCString)206 TEST(StringBuilder, Append_PartialCString) {
207 StringBuffer<12> sb;
208 EXPECT_TRUE(sb.append("123456", 4).ok());
209 EXPECT_EQ(4u, sb.size());
210 EXPECT_STREQ("1234", sb.data());
211 }
212
TEST(StringBuilder,Append_CString)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());
218 }
219
TEST(StringBuilder,Append_CString_Full)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());
226 }
227
TEST(StringBuilder,Append_StringView)228 TEST(StringBuilder, Append_StringView) {
229 auto sb = MakeString<32>("hello");
230 EXPECT_TRUE(sb.append("???"sv).ok());
231 EXPECT_EQ("hello???"sv, sb);
232 }
233
TEST(StringBuilder,Append_StringView_Substring)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);
238 }
239
TEST(StringBuilder,Append_StringView_RemainingSubstring)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);
244 }
245
TEST(StringBuilder,Resize_Smaller)246 TEST(StringBuilder, Resize_Smaller) {
247 auto sb = MakeString<12>("Four");
248 sb.resize(2);
249 EXPECT_TRUE(sb.ok());
250 EXPECT_EQ(2u, sb.size());
251 EXPECT_STREQ("Fo", sb.data());
252 }
253
TEST(StringBuilder,Resize_Clear)254 TEST(StringBuilder, Resize_Clear) {
255 auto sb = MakeString<12>("Four");
256 sb.resize(0);
257 EXPECT_TRUE(sb.ok());
258 EXPECT_EQ(0u, sb.size());
259 EXPECT_STREQ("", sb.data());
260 }
261
TEST(StringBuilder,Resize_Larger_Fails)262 TEST(StringBuilder, Resize_Larger_Fails) {
263 auto sb = MakeString<12>("Four");
264 EXPECT_EQ(4u, sb.size());
265 sb.resize(10);
266 EXPECT_EQ(sb.status(), Status::OutOfRange());
267 EXPECT_EQ(4u, sb.size());
268 }
269
TEST(StringBuilder,Resize_LargerThanCapacity_Fails)270 TEST(StringBuilder, Resize_LargerThanCapacity_Fails) {
271 auto sb = MakeString<12>("Four");
272 sb.resize(1234);
273 EXPECT_EQ(sb.status(), Status::OutOfRange());
274 EXPECT_EQ(4u, sb.size());
275 EXPECT_STREQ("Four", sb.data());
276 }
277
TEST(StringBuilder,Format_Normal)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());
283
284 sb << "def";
285
286 EXPECT_TRUE(sb.Format("GHI").ok());
287 EXPECT_STREQ("0xabcdefGHI", sb.data());
288 }
289
TEST(StringBuilder,Format_ExhaustBuffer)290 TEST(StringBuilder, Format_ExhaustBuffer) {
291 StringBuffer<6> sb;
292 EXPECT_EQ(Status::ResourceExhausted(), sb.Format("012345").status());
293
294 EXPECT_STREQ("01234", sb.data());
295 EXPECT_EQ(Status::ResourceExhausted(), sb.status());
296 }
297
TEST(StringBuilder,StreamOutput_MultipleTypes)298 TEST(StringBuilder, StreamOutput_MultipleTypes) {
299 constexpr const char* kExpected = "This is -1true example\n of this";
300 constexpr const char* kExample = "example";
301
302 StringBuffer<64> sb;
303 sb << "This is " << -1 << true << ' ' << kExample << '\n' << " of this";
304
305 EXPECT_STREQ(kExpected, sb.data());
306 EXPECT_EQ(std::strlen(kExpected), sb.size());
307 }
308
TEST(StringBuilder,StreamOutput_FullBufferIgnoresExtraStrings)309 TEST(StringBuilder, StreamOutput_FullBufferIgnoresExtraStrings) {
310 StringBuffer<6> sb;
311 EXPECT_EQ(5u, sb.max_size()); // max_size() excludes the null terminator
312
313 sb << 1 - 1;
314 EXPECT_TRUE(sb.ok());
315 EXPECT_STREQ("0", sb.data());
316
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());
321 }
322
TEST(StringBuilder,StreamOutput_ExhaustBuffer_InOneString)323 TEST(StringBuilder, StreamOutput_ExhaustBuffer_InOneString) {
324 StringBuffer<9> sb;
325 EXPECT_EQ(8u, sb.max_size());
326
327 sb << "0123456789"; // write 10 chars
328