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 "gtest/gtest.h"
24 #include "pw_status/status.h"
25 #include "pw_string/type_to_string.h"
26
27 namespace pw {
28
29 struct CustomType {
30 unsigned a;
31 unsigned b;
32
33 static constexpr const char* kToString = "This is a CustomType";
34
35 CustomType() = default;
36
37 // Non-copyable to verify that ToString doesn't copy it.
38 CustomType(const CustomType&) = delete;
39 CustomType& operator=(const CustomType&) = delete;
40 };
41
ToString(const CustomType &,std::span<char> buffer)42 StatusWithSize ToString(const CustomType&, std::span<char> buffer) {
43 int result =
44 std::snprintf(buffer.data(), buffer.size(), CustomType::kToString);
45 if (result < 0) {
46 return StatusWithSize::Unknown();
47 }
48 if (static_cast<size_t>(result) < buffer.size()) {
49 return StatusWithSize(result);
50 }
51 return StatusWithSize::ResourceExhausted(buffer.empty() ? 0u
52 : buffer.size() - 1);
53 }
54
55 namespace {
56
57 char buffer[128] = {};
58 char expected[128] = {};
59
TEST(ToString,Bool)60 TEST(ToString, Bool) {
61 const volatile bool b = true;
62 EXPECT_EQ(4u, ToString(b, buffer).size());
63 EXPECT_STREQ("true", buffer);
64 EXPECT_EQ(5u, ToString(false, buffer).size());
65 EXPECT_STREQ("false", buffer);
66 }
67
TEST(ToString,Char)68 TEST(ToString, Char) {
69 EXPECT_EQ(1u, ToString('%', buffer).size());
70 EXPECT_STREQ("%", buffer);
71 }
72
73 template <typename T>
74 constexpr T kInteger = 127;
75
TEST(ToString,Integer_AllTypesAreSupported)76 TEST(ToString, Integer_AllTypesAreSupported) {
77 EXPECT_EQ(3u, ToString(kInteger<unsigned char>, buffer).size());
78 EXPECT_STREQ("127", buffer);
79 EXPECT_EQ(3u, ToString(kInteger<signed char>, buffer).size());
80 EXPECT_STREQ("127", buffer);
81 EXPECT_EQ(3u, ToString(kInteger<unsigned short>, buffer).size());
82 EXPECT_STREQ("127", buffer);
83 EXPECT_EQ(3u, ToString(kInteger<signed short>, buffer).size());
84 EXPECT_STREQ("127", buffer);
85 EXPECT_EQ(3u, ToString(kInteger<unsigned int>, buffer).size());
86 EXPECT_STREQ("127", buffer);
87 EXPECT_EQ(3u, ToString(kInteger<signed int>, buffer).size());
88 EXPECT_STREQ("127", buffer);
89 EXPECT_EQ(3u, ToString(kInteger<unsigned long>, buffer).size());
90 EXPECT_STREQ("127", buffer);
91 EXPECT_EQ(3u, ToString(kInteger<signed long>, buffer).size());
92 EXPECT_STREQ("127", buffer);
93 EXPECT_EQ(3u, ToString(kInteger<unsigned long long>, buffer).size());
94 EXPECT_STREQ("127", buffer);
95 EXPECT_EQ(3u, ToString(kInteger<signed long long>, buffer).size());
96 EXPECT_STREQ("127", buffer);
97 }
98
TEST(ToString,ScopedEnum)99 TEST(ToString, ScopedEnum) {
100 enum class MyEnum : short { kLuckyNumber = 8 };
101
102 auto result = ToString(MyEnum::kLuckyNumber, buffer);
103 EXPECT_EQ(1u, result.size());
104 EXPECT_EQ(OkStatus(), result.status());
105 EXPECT_STREQ("8", buffer);
106 }
107
TEST(ToString,Integer_EmptyBuffer_WritesNothing)108 TEST(ToString, Integer_EmptyBuffer_WritesNothing) {
109 auto result = ToString(-1234, std::span(buffer, 0));
110 EXPECT_EQ(0u, result.size());
111 EXPECT_EQ(Status::ResourceExhausted(), result.status());
112 }
113
TEST(ToString,Integer_BufferTooSmall_WritesNullTerminator)114 TEST(ToString, Integer_BufferTooSmall_WritesNullTerminator) {
115 auto result = ToString(-1234, std::span(buffer, 5));
116 EXPECT_EQ(0u, result.size());
117 EXPECT_FALSE(result.ok());
118 EXPECT_STREQ("", buffer);
119 }
120
TEST(ToString,Float)121 TEST(ToString, Float) {
122 EXPECT_EQ(1u, ToString(0.0f, buffer).size());
123 EXPECT_STREQ("0", buffer);
124 EXPECT_EQ(3u, ToString(INFINITY, buffer).size());
125 EXPECT_STREQ("inf", buffer);
126 EXPECT_EQ(4u, ToString(-NAN, buffer).size());
127 EXPECT_STREQ("-NaN", buffer);
128 }
129
TEST(ToString,Pointer_NonNull_WritesValue)130 TEST(ToString, Pointer_NonNull_WritesValue) {
131 CustomType custom;
132 const size_t length = std::snprintf(expected,
133 sizeof(expected),
134 "%" PRIxPTR,
135 reinterpret_cast<intptr_t>(&custom));
136
137 EXPECT_EQ(length, ToString(&custom, buffer).size());
138 EXPECT_STREQ(expected, buffer);
139 EXPECT_EQ(length, ToString(static_cast<void*>(&custom), buffer).size());
140 EXPECT_STREQ(expected, buffer);
141 EXPECT_EQ(1u, ToString(reinterpret_cast<int*>(4), buffer).size());
142 EXPECT_STREQ("4", buffer);
143 }
144
TEST(ToString,Pointer_Nullptr_WritesNull)145 TEST(ToString, Pointer_Nullptr_WritesNull) {
146 EXPECT_EQ(string::kNullPointerString.size(),
147 ToString(nullptr, buffer).size());
148 EXPECT_EQ(string::kNullPointerString, buffer);
149 }
150
TEST(ToString,Pointer_NullValuedPointer_WritesNull)151 TEST(ToString, Pointer_NullValuedPointer_WritesNull) {
152 EXPECT_EQ(string::kNullPointerString.size(),
153 ToString(static_cast<const CustomType*>(nullptr), buffer).size());
154 EXPECT_EQ(string::kNullPointerString, buffer);
155 }
156
TEST(ToString,Pointer_NullValuedCString_WritesNull)157 TEST(ToString, Pointer_NullValuedCString_WritesNull) {
158 EXPECT_EQ(string::kNullPointerString.size(),
159 ToString(static_cast<char*>(nullptr), buffer).size());
160 EXPECT_EQ(string::kNullPointerString, buffer);
161
162 EXPECT_EQ(string::kNullPointerString.size(),
163 ToString(static_cast<const char*>(nullptr), buffer).size());
164 EXPECT_EQ(string::kNullPointerString, buffer);
165 }
166
TEST(ToString,String_Literal)167 TEST(ToString, String_Literal) {
168 EXPECT_EQ(0u, ToString("", buffer).size());
169 EXPECT_STREQ("", buffer);
170 EXPECT_EQ(5u, ToString("hello", buffer).size());
171 EXPECT_STREQ("hello", buffer);
172 }
173
TEST(ToString,String_Pointer)174 TEST(ToString, String_Pointer) {
175 EXPECT_EQ(0u, ToString(static_cast<const char*>(""), buffer).size());
176 EXPECT_STREQ("", buffer);
177 EXPECT_EQ(5u, ToString(static_cast<const char*>("hello"), buffer).size());
178 EXPECT_STREQ("hello", buffer);
179 }
180
TEST(ToString,String_MutableBuffer)181 TEST(ToString, String_MutableBuffer) {
182 char chars[] = {'C', 'o', 'o', 'l', '\0'};
183 EXPECT_EQ(sizeof(chars) - 1, ToString(chars, buffer).size());
184 EXPECT_STREQ("Cool", buffer);
185 }
186
TEST(ToString,String_MutablePointer)187 TEST(ToString, String_MutablePointer) {
188 char chars[] = {'b', 'o', 'o', 'l', '\0'};
189 EXPECT_EQ(sizeof(chars) - 1,
190 ToString(static_cast<char*>(chars), buffer).size());
191 EXPECT_STREQ("bool", buffer);
192 }
193
TEST(ToString,Object)194 TEST(ToString, Object) {
195 CustomType custom;
196 EXPECT_EQ(std::strlen(CustomType::kToString),
197 ToString(custom, buffer).size());
198 EXPECT_STREQ(CustomType::kToString, buffer);
199 }
200
201 enum Foo : uint8_t {
202 BAR = 32,
203 BAZ = 100,
204 };
205
TEST(ToString,Enum)206 TEST(ToString, Enum) {
207 EXPECT_EQ(2u, ToString(Foo::BAR, buffer).size());
208 EXPECT_STREQ("32", buffer);
209
210 EXPECT_EQ(3u, ToString(Foo::BAZ, buffer).size());
211 EXPECT_STREQ("100", buffer);
212 }
213
TEST(ToString,Status)214 TEST(ToString, Status) {
215 EXPECT_EQ(2u, ToString(Status(), buffer).size());
216 EXPECT_STREQ(Status().str(), buffer);
217 }
218
TEST(ToString,StatusCode)219 TEST(ToString, StatusCode) {
220 EXPECT_EQ(sizeof("UNAVAILABLE") - 1,
221 ToString(Status::Unavailable(), buffer).size());
222 EXPECT_STREQ("UNAVAILABLE", buffer);
223 }
224
TEST(ToString,StdArrayAsBuffer)225 TEST(ToString, StdArrayAsBuffer) {
226 std::array<char, 128> test_buffer;
227 EXPECT_EQ(5u, ToString(false, test_buffer).size());
228 EXPECT_STREQ("false", test_buffer.data());
229 EXPECT_EQ(2u, ToString("Hi", test_buffer).size());
230 EXPECT_STREQ("Hi", test_buffer.data());
231 EXPECT_EQ(string::kNullPointerString.size(),
232 ToString(static_cast<void*>(nullptr), test_buffer).size());
233 EXPECT_EQ(string::kNullPointerString, test_buffer.data());
234 }
235
TEST(ToString,StringView)236 TEST(ToString, StringView) {
237 std::string_view view = "cool";
238 EXPECT_EQ(4u, ToString(view, buffer).size());
239 EXPECT_STREQ("cool", buffer);
240 }
241
TEST(ToString,StringView_TooSmall_Truncates)242 TEST(ToString, StringView_TooSmall_Truncates) {
243 std::string_view view = "kale!";
244 EXPECT_EQ(3u, ToString(view, std::span(buffer, 4)).size());
245 EXPECT_STREQ("kal", buffer);
246 }
247
TEST(ToString,StringView_EmptyBuffer_WritesNothing)248 TEST(ToString, StringView_EmptyBuffer_WritesNothing) {
249 constexpr char kOriginal[] = {'@', '#', '$', '%'};
250 char test_buffer[sizeof(kOriginal)];
251 std::memcpy(test_buffer, kOriginal, sizeof(kOriginal));
252
253 EXPECT_EQ(
254 0u,
255 ToString(std::string_view("Hello!"), std::span(test_buffer, 0)).size());
256 ASSERT_EQ(0, std::memcmp(kOriginal, test_buffer, sizeof(kOriginal)));
257 }
258
TEST(ToString,StdString)259 TEST(ToString, StdString) {
260 EXPECT_EQ(5u, ToString(std::string("Whoa!"), buffer).size());
261 EXPECT_STREQ("Whoa!", buffer);
262
263 EXPECT_EQ(0u, ToString(std::string(), buffer).size());
264 EXPECT_STREQ("", buffer);
265 }
266
267 } // namespace
268 } // namespace pw
269