xref: /aosp_15_r20/external/pigweed/pw_string/to_string_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/to_string.h"
16 
17 #include <array>
18 #include <cinttypes>
19 #include <cmath>
20 #include <cstring>
21 #include <string>
22 
23 #include "pw_result/result.h"
24 #include "pw_span/span.h"
25 #include "pw_status/status.h"
26 #include "pw_string/internal/config.h"
27 #include "pw_string/type_to_string.h"
28 #include "pw_unit_test/framework.h"
29 
30 namespace pw {
31 
32 struct CustomType {
33   unsigned a;
34   unsigned b;
35 
36   static constexpr const char* kToString = "This is a CustomType";
37 
CustomTypepw::CustomType38   CustomType() : a(0), b(0) {}
39 
40   // Non-copyable to verify that ToString doesn't copy it.
41   CustomType(const CustomType&) = delete;
42   CustomType& operator=(const CustomType&) = delete;
43 };
44 
ToString(const CustomType &,span<char> buffer)45 StatusWithSize ToString(const CustomType&, span<char> buffer) {
46   int result =
47       std::snprintf(buffer.data(), buffer.size(), CustomType::kToString);
48   if (result < 0) {
49     return StatusWithSize::Unknown();
50   }
51   if (static_cast<size_t>(result) < buffer.size()) {
52     return StatusWithSize(result);
53   }
54   return StatusWithSize::ResourceExhausted(buffer.empty() ? 0u
55                                                           : buffer.size() - 1);
56 }
57 
58 namespace {
59 
60 char buffer[128] = {};
61 char expected[128] = {};
62 
TEST(ToString,Bool)63 TEST(ToString, Bool) {
64   const volatile bool b = true;
65   EXPECT_EQ(4u, ToString(b, buffer).size());
66   EXPECT_STREQ("true", buffer);
67   EXPECT_EQ(5u, ToString(false, buffer).size());
68   EXPECT_STREQ("false", buffer);
69 }
70 
TEST(ToString,Char)71 TEST(ToString, Char) {
72   EXPECT_EQ(1u, ToString('%', buffer).size());
73   EXPECT_STREQ("%", buffer);
74 }
75 
76 template <typename T>
77 constexpr T kInteger = 127;
78 
TEST(ToString,Integer_AllTypesAreSupported)79 TEST(ToString, Integer_AllTypesAreSupported) {
80   EXPECT_EQ(3u, ToString(kInteger<unsigned char>, buffer).size());
81   EXPECT_STREQ("127", buffer);
82   EXPECT_EQ(3u, ToString(kInteger<signed char>, buffer).size());
83   EXPECT_STREQ("127", buffer);
84   EXPECT_EQ(3u, ToString(kInteger<unsigned short>, buffer).size());
85   EXPECT_STREQ("127", buffer);
86   EXPECT_EQ(3u, ToString(kInteger<signed short>, buffer).size());
87   EXPECT_STREQ("127", buffer);
88   EXPECT_EQ(3u, ToString(kInteger<unsigned int>, buffer).size());
89   EXPECT_STREQ("127", buffer);
90   EXPECT_EQ(3u, ToString(kInteger<signed int>, buffer).size());
91   EXPECT_STREQ("127", buffer);
92   EXPECT_EQ(3u, ToString(kInteger<unsigned long>, buffer).size());
93   EXPECT_STREQ("127", buffer);
94   EXPECT_EQ(3u, ToString(kInteger<signed long>, buffer).size());
95   EXPECT_STREQ("127", buffer);
96   EXPECT_EQ(3u, ToString(kInteger<unsigned long long>, buffer).size());
97   EXPECT_STREQ("127", buffer);
98   EXPECT_EQ(3u, ToString(kInteger<signed long long>, buffer).size());
99   EXPECT_STREQ("127", buffer);
100 }
101 
TEST(ToString,ScopedEnum)102 TEST(ToString, ScopedEnum) {
103   enum class MyEnum : short { kLuckyNumber = 8 };
104 
105   auto result = ToString(MyEnum::kLuckyNumber, buffer);
106   EXPECT_EQ(1u, result.size());
107   EXPECT_EQ(OkStatus(), result.status());
108   EXPECT_STREQ("8", buffer);
109 }
110 
TEST(ToString,Integer_EmptyBuffer_WritesNothing)111 TEST(ToString, Integer_EmptyBuffer_WritesNothing) {
112   auto result = ToString(-1234, span(buffer, 0));
113   EXPECT_EQ(0u, result.size());
114   EXPECT_EQ(Status::ResourceExhausted(), result.status());
115 }
116 
TEST(ToString,Integer_BufferTooSmall_WritesNullTerminator)117 TEST(ToString, Integer_BufferTooSmall_WritesNullTerminator) {
118   auto result = ToString(-1234, span(buffer, 5));
119   EXPECT_EQ(0u, result.size());
120   EXPECT_FALSE(result.ok());
121   EXPECT_STREQ("", buffer);
122 }
123 
TEST(ToString,Float)124 TEST(ToString, Float) {
125   if (string::internal::config::kEnableDecimalFloatExpansion) {
126     EXPECT_EQ(5u, ToString(0.0f, buffer).size());
127     EXPECT_STREQ("0.000", buffer);
128     EXPECT_EQ(6u, ToString(33.444f, buffer).size());
129     EXPECT_STREQ("33.444", buffer);
130     EXPECT_EQ(3u, ToString(INFINITY, buffer).size());
131     EXPECT_STREQ("inf", buffer);
132     EXPECT_EQ(3u, ToString(NAN, buffer).size());
133     EXPECT_STREQ("nan", buffer);
134   } else {
135     EXPECT_EQ(1u, ToString(0.0f, buffer).size());
136     EXPECT_STREQ("0", buffer);
137     EXPECT_EQ(3u, ToString(INFINITY, buffer).size());
138     EXPECT_STREQ("inf", buffer);
139     EXPECT_EQ(4u, ToString(-NAN, buffer).size());
140     EXPECT_STREQ("-NaN", buffer);
141   }
142 }
143 
TEST(ToString,Pointer_NonNull_WritesValue)144 TEST(ToString, Pointer_NonNull_WritesValue) {
145   CustomType custom;
146   const size_t length =
147       static_cast<size_t>(std::snprintf(expected,
148                                         sizeof(expected),
149                                         "%" PRIxPTR,
150                                         reinterpret_cast<intptr_t>(&custom)));
151 
152   EXPECT_EQ(length, ToString(&custom, buffer).size());
153   EXPECT_STREQ(expected, buffer);
154   EXPECT_EQ(length, ToString(static_cast<void*>(&custom), buffer).size());
155   EXPECT_STREQ(expected, buffer);
156   EXPECT_EQ(1u, ToString(reinterpret_cast<int*>(4), buffer).size());
157   EXPECT_STREQ("4", buffer);
158 }
159 
TEST(ToString,Pointer_Nullptr_WritesNull)160 TEST(ToString, Pointer_Nullptr_WritesNull) {
161   EXPECT_EQ(string::kNullPointerString.size(),
162             ToString(nullptr, buffer).size());
163   EXPECT_EQ(string::kNullPointerString, buffer);
164 }
165 
TEST(ToString,Pointer_NullValuedPointer_WritesNull)166 TEST(ToString, Pointer_NullValuedPointer_WritesNull) {
167   EXPECT_EQ(string::kNullPointerString.size(),
168             ToString(static_cast<const CustomType*>(nullptr), buffer).size());
169   EXPECT_EQ(string::kNullPointerString, buffer);
170 }
171 
TEST(ToString,Pointer_NullValuedCString_WritesNull)172 TEST(ToString, Pointer_NullValuedCString_WritesNull) {
173   EXPECT_EQ(string::kNullPointerString.size(),
174             ToString(static_cast<char*>(nullptr), buffer).size());
175   EXPECT_EQ(string::kNullPointerString, buffer);
176 
177   EXPECT_EQ(string::kNullPointerString.size(),
178             ToString(static_cast<const char*>(nullptr), buffer).size());
179   EXPECT_EQ(string::kNullPointerString, buffer);
180 }
181 
TEST(ToString,String_Literal)182 TEST(ToString, String_Literal) {
183   EXPECT_EQ(0u, ToString("", buffer).size());
184   EXPECT_STREQ("", buffer);
185   EXPECT_EQ(5u, ToString("hello", buffer).size());
186   EXPECT_STREQ("hello", buffer);
187 }
188 
TEST(ToString,String_Pointer)189 TEST(ToString, String_Pointer) {
190   EXPECT_EQ(0u, ToString(static_cast<const char*>(""), buffer).size());
191   EXPECT_STREQ("", buffer);
192   EXPECT_EQ(5u, ToString(static_cast<const char*>("hello"), buffer).size());
193   EXPECT_STREQ("hello", buffer);
194 }
195 
TEST(ToString,String_MutableBuffer)196 TEST(ToString, String_MutableBuffer) {
197   char chars[] = {'C', 'o', 'o', 'l', '\0'};
198   EXPECT_EQ(sizeof(chars) - 1, ToString(chars, buffer).size());
199   EXPECT_STREQ("Cool", buffer);
200 }
201 
TEST(ToString,String_MutablePointer)202 TEST(ToString, String_MutablePointer) {
203   char chars[] = {'b', 'o', 'o', 'l', '\0'};
204   EXPECT_EQ(sizeof(chars) - 1,
205             ToString(static_cast<char*>(chars), buffer).size());
206   EXPECT_STREQ("bool", buffer);
207 }
208 
TEST(ToString,Object)209 TEST(ToString, Object) {
210   CustomType custom;
211   EXPECT_EQ(std::strlen(CustomType::kToString),
212             ToString(custom, buffer).size());
213   EXPECT_STREQ(CustomType::kToString, buffer);
214 }
215 
216 enum Foo : uint8_t {
217   BAR = 32,
218   BAZ = 100,
219 };
220 
TEST(ToString,Enum)221 TEST(ToString, Enum) {
222   EXPECT_EQ(2u, ToString(Foo::BAR, buffer).size());
223   EXPECT_STREQ("32", buffer);
224 
225   EXPECT_EQ(3u, ToString(Foo::BAZ, buffer).size());
226   EXPECT_STREQ("100", buffer);
227 }
228 
TEST(ToString,Status)229 TEST(ToString, Status) {
230   EXPECT_EQ(2u, ToString(Status(), buffer).size());
231   EXPECT_STREQ(Status().str(), buffer);
232 }
233 
TEST(ToString,StatusCode)234 TEST(ToString, StatusCode) {
235   EXPECT_EQ(sizeof("UNAVAILABLE") - 1,
236             ToString(Status::Unavailable(), buffer).size());
237   EXPECT_STREQ("UNAVAILABLE", buffer);
238 }
239 
TEST(ToString,StdArrayAsBuffer)240 TEST(ToString, StdArrayAsBuffer) {
241   std::array<char, 128> test_buffer;
242   EXPECT_EQ(5u, ToString(false, test_buffer).size());
243   EXPECT_STREQ("false", test_buffer.data());
244   EXPECT_EQ(2u, ToString("Hi", test_buffer).size());
245   EXPECT_STREQ("Hi", test_buffer.data());
246   EXPECT_EQ(string::kNullPointerString.size(),
247             ToString(static_cast<void*>(nullptr), test_buffer).size());
248   EXPECT_EQ(string::kNullPointerString, test_buffer.data());
249 }
250 
TEST(ToString,StringView)251 TEST(ToString, StringView) {
252   std::string_view view = "cool";
253   EXPECT_EQ(4u, ToString(view, buffer).size());
254   EXPECT_STREQ("cool", buffer);
255 }
256 
TEST(ToString,StringView_TooSmall_Truncates)257 TEST(ToString, StringView_TooSmall_Truncates) {
258   std::string_view view = "kale!";
259   EXPECT_EQ(3u, ToString(view, span(buffer, 4)).size());
260   EXPECT_STREQ("kal", buffer);
261 }
262 
TEST(ToString,StringView_EmptyBuffer_WritesNothing)263 TEST(ToString, StringView_EmptyBuffer_WritesNothing) {
264   constexpr char kOriginal[] = {'@', '#', '$', '%'};
265   char test_buffer[sizeof(kOriginal)];
266   std::memcpy(test_buffer, kOriginal, sizeof(kOriginal));
267 
268   EXPECT_EQ(0u,
269             ToString(std::string_view("Hello!"), span(test_buffer, 0)).size());
270   ASSERT_EQ(0, std::memcmp(kOriginal, test_buffer, sizeof(kOriginal)));
271 }
272 
TEST(ToString,StdString)273 TEST(ToString, StdString) {
274   EXPECT_EQ(5u, ToString(std::string("Whoa!"), buffer).size());
275   EXPECT_STREQ("Whoa!", buffer);
276 
277   EXPECT_EQ(0u, ToString(std::string(), buffer).size());
278   EXPECT_STREQ("", buffer);
279 }
280 
TEST(ToString,StdNullopt)281 TEST(ToString, StdNullopt) {
282   EXPECT_EQ(12u, ToString(std::nullopt, buffer).size());
283   EXPECT_STREQ("std::nullopt", buffer);
284 }
285 
TEST(ToString,StdOptionalWithoutValue)286 TEST(ToString, StdOptionalWithoutValue) {
287   std::optional<uint16_t> v = std::nullopt;
288   EXPECT_EQ(12u, ToString(v, buffer).size());
289   EXPECT_STREQ("std::nullopt", buffer);
290 }
291 
TEST(ToString,StdOptionalWithValue)292 TEST(ToString, StdOptionalWithValue) {
293   std::optional<uint16_t> v = 5;
294   EXPECT_EQ(1u, ToString(v, buffer).size());
295   EXPECT_STREQ("5", buffer);
296 }
297 
TEST(ToString,ResultWithNonOkStatus)298 TEST(ToString, ResultWithNonOkStatus) {
299   Result<int> v = Status::ResourceExhausted();
300   EXPECT_EQ(18u, ToString(v, buffer).size());
301   EXPECT_STREQ("RESOURCE_EXHAUSTED", buffer);
302 }
303 
TEST(ToString,ResultWithValue)304 TEST(ToString, ResultWithValue) {
305   Result<int> v = 27;
306   EXPECT_EQ(6u, ToString(v, buffer).size());
307   EXPECT_STREQ("Ok(27)", buffer);
308 }
309 
TEST(ToString,EmptyArrayUsesIterableFormat)310 TEST(ToString, EmptyArrayUsesIterableFormat) {
311   std::array<int, 0> v = {};
312   EXPECT_EQ(2u, ToString(v, buffer).size());
313   EXPECT_STREQ("[]", buffer);
314 }
315 
TEST(ToString,ArrayUsesIterableFormat)316 TEST(ToString, ArrayUsesIterableFormat) {
317   std::array<int, 3> v = {1, 2, 3};
318   EXPECT_EQ(9u, ToString(v, buffer).size());
319   EXPECT_STREQ("[1, 2, 3]", buffer);
320 }
321 
TEST(ToString,SpanUsesIterableFormat)322 TEST(ToString, SpanUsesIterableFormat) {
323   std::array<int, 3> arr = {1, 2, 3};
324   span v(arr);
325   EXPECT_EQ(9u, ToString(v, buffer).size());
326   EXPECT_STREQ("[1, 2, 3]", buffer);
327 }
328 
TEST(ToString,ArrayOfStringsUsesIterableFormat)329 TEST(ToString, ArrayOfStringsUsesIterableFormat) {
330   std::array<const char*, 3> v({"foo", "bar", "baz"});
331   EXPECT_EQ(15u, ToString(v, buffer).size());
332   EXPECT_STREQ("[foo, bar, baz]", buffer);
333 }
334 
TEST(ToString,TimeVerySmallEndsInNsOrUnrepresentable)335 TEST(ToString, TimeVerySmallEndsInNsOrUnrepresentable) {
336   size_t size =
337       ToString(std::chrono::system_clock::time_point::min(), buffer).size();
338   ASSERT_GE(size, 3u);
339   if (std::strcmp("ns", &buffer[size - 2]) != 0) {
340     EXPECT_STREQ("An unrepresentably long time ago (>292 years).", buffer);
341   }
342 }
343 
TEST(ToString,TimeVeryBigEndsInNsOrUnrepresentable)344 TEST(ToString, TimeVeryBigEndsInNsOrUnrepresentable) {
345   size_t size =
346       ToString(std::chrono::system_clock::time_point::max(), buffer).size();
347   ASSERT_GE(size, 3u);
348   if (std::strcmp("ns", &buffer[size - 2]) != 0) {
349     EXPECT_STREQ("An unrepresentably long time in the future (>292 years).",
350                  buffer);
351   }
352 }
353 
TEST(ToString,TimeJustRight)354 TEST(ToString, TimeJustRight) {
355   EXPECT_TRUE(ToString(std::chrono::system_clock::time_point(), buffer).ok());
356   EXPECT_STREQ("0ns", buffer);
357 }
358 
359 int64_t sensor_clock_tick_count;
360 
361 struct SensorClock {
362   using rep = int64_t;
363   using period = std::ratio<1, 32768>;
364   using duration = std::chrono::duration<rep, period>;
365   using time_point = std::chrono::time_point<SensorClock>;
nowpw::__anon3f2f52730111::SensorClock366   static time_point now() noexcept {
367     return time_point(duration(sensor_clock_tick_count));
368   }
369 };
370 
TEST(ToString,TimeSensorClockZero)371 TEST(ToString, TimeSensorClockZero) {
372   sensor_clock_tick_count = 0;
373   EXPECT_TRUE(ToString(SensorClock::now(), buffer).ok());
374   EXPECT_STREQ("0ns", buffer);
375 }
376 
TEST(ToString,TimeSensorClockMax)377 TEST(ToString, TimeSensorClockMax) {
378   sensor_clock_tick_count = std::numeric_limits<int64_t>::max();
379   EXPECT_TRUE(ToString(SensorClock::now(), buffer).ok());
380   EXPECT_STREQ("An unrepresentably long time in the future (>292 years).",
381                buffer);
382 }
383 
TEST(ToString,TimeSensorClockMin)384 TEST(ToString, TimeSensorClockMin) {
385   sensor_clock_tick_count = std::numeric_limits<int64_t>::min();
386   EXPECT_TRUE(ToString(SensorClock::now(), buffer).ok());
387   EXPECT_STREQ("An unrepresentably long time ago (>292 years).", buffer);
388 }
389 
TEST(ToString,DurationEndsInNs)390 TEST(ToString, DurationEndsInNs) {
391   EXPECT_TRUE(ToString(std::chrono::nanoseconds::zero(), buffer).ok());
392   EXPECT_STREQ("0ns", buffer);
393 }
394 
395 }  // namespace
396 }  // namespace pw
397