1 // Copyright 2020 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_tokenizer/tokenize.h"
16
17 #include <cinttypes>
18 #include <cstdint>
19 #include <cstring>
20 #include <iterator>
21 #include <limits>
22
23 #include "pw_tokenizer/hash.h"
24 #include "pw_tokenizer_private/tokenize_test.h"
25 #include "pw_unit_test/framework.h"
26 #include "pw_varint/varint.h"
27
28 namespace pw::tokenizer {
29 namespace {
30
31 // Constructs an array with the hashed string followed by the provided bytes.
32 template <uint8_t... kData, size_t kSize>
ExpectedData(const char (& format)[kSize],uint32_t token_mask=std::numeric_limits<uint32_t>::max ())33 constexpr auto ExpectedData(
34 const char (&format)[kSize],
35 uint32_t token_mask = std::numeric_limits<uint32_t>::max()) {
36 const uint32_t value = Hash(format) & token_mask;
37 return std::array<uint8_t, sizeof(uint32_t) + sizeof...(kData)>{
38 static_cast<uint8_t>(value & 0xff),
39 static_cast<uint8_t>(value >> 8 & 0xff),
40 static_cast<uint8_t>(value >> 16 & 0xff),
41 static_cast<uint8_t>(value >> 24 & 0xff),
42 kData...};
43 }
44
TEST(TokenizeString,EmptyString_IsZero)45 TEST(TokenizeString, EmptyString_IsZero) {
46 constexpr pw_tokenizer_Token token = PW_TOKENIZE_STRING("");
47 EXPECT_EQ(0u, token);
48 }
49
TEST(TokenizeString,String_MatchesHash)50 TEST(TokenizeString, String_MatchesHash) {
51 constexpr uint32_t token = PW_TOKENIZE_STRING("[:-)");
52 EXPECT_EQ(Hash("[:-)"), token);
53 }
54
TEST(TokenizeString,String_MatchesHashExpr)55 TEST(TokenizeString, String_MatchesHashExpr) {
56 EXPECT_EQ(Hash("[:-)"), PW_TOKENIZE_STRING_EXPR("[:-)"));
57 }
58
TEST(TokenizeString,ExpressionWithStringVariable)59 TEST(TokenizeString, ExpressionWithStringVariable) {
60 constexpr char kTestString[] = "test";
61 EXPECT_EQ(Hash(kTestString), PW_TOKENIZE_STRING_EXPR(kTestString));
62 EXPECT_EQ(Hash(kTestString),
63 PW_TOKENIZE_STRING_DOMAIN_EXPR("TEST_DOMAIN", kTestString));
64 EXPECT_EQ(
65 Hash(kTestString) & 0xAAAAAAAA,
66 PW_TOKENIZE_STRING_MASK_EXPR("TEST_DOMAIN", 0xAAAAAAAA, kTestString));
67 }
68
69 constexpr uint32_t kGlobalToken = PW_TOKENIZE_STRING(">:-[]");
70
TEST(TokenizeString,GlobalVariable_MatchesHash)71 TEST(TokenizeString, GlobalVariable_MatchesHash) {
72 EXPECT_EQ(Hash(">:-[]"), kGlobalToken);
73 }
74
75 struct TokenizedWithinClass {
76 static constexpr uint32_t kThisToken = PW_TOKENIZE_STRING("???");
77 };
78
79 static_assert(Hash("???") == TokenizedWithinClass::kThisToken);
80
TEST(TokenizeString,ClassMember_MatchesHash)81 TEST(TokenizeString, ClassMember_MatchesHash) {
82 EXPECT_EQ(Hash("???"), TokenizedWithinClass().kThisToken);
83 }
84
TEST(TokenizeString,WithinNonCapturingLambda)85 TEST(TokenizeString, WithinNonCapturingLambda) {
86 uint32_t non_capturing_lambda = [] {
87 return PW_TOKENIZE_STRING("Lambda!");
88 }();
89
90 EXPECT_EQ(Hash("Lambda!"), non_capturing_lambda);
91 }
92
TEST(TokenizeString,WithinCapturingLambda)93 TEST(TokenizeString, WithinCapturingLambda) {
94 bool executed_lambda = false;
95 uint32_t capturing_lambda = [&executed_lambda] {
96 if (executed_lambda) {
97 return PW_TOKENIZE_STRING("Never should be returned!");
98 }
99 executed_lambda = true;
100 return PW_TOKENIZE_STRING("Capturing lambda!");
101 }();
102
103 ASSERT_TRUE(executed_lambda);
104 EXPECT_EQ(Hash("Capturing lambda!"), capturing_lambda);
105 }
106
TEST(TokenizeString,Mask)107 TEST(TokenizeString, Mask) {
108 [[maybe_unused]] constexpr uint32_t token = PW_TOKENIZE_STRING("(O_o)");
109 [[maybe_unused]] constexpr uint32_t masked_1 =
110 PW_TOKENIZE_STRING_MASK("domain", 0xAAAAAAAA, "(O_o)");
111 [[maybe_unused]] constexpr uint32_t masked_2 =
112 PW_TOKENIZE_STRING_MASK("domain", 0x55555555, "(O_o)");
113 [[maybe_unused]] constexpr uint32_t masked_3 =
114 PW_TOKENIZE_STRING_MASK("domain", 0xFFFF0000, "(O_o)");
115
116 static_assert(token != masked_1 && token != masked_2 && token != masked_3);
117 static_assert(masked_1 != masked_2 && masked_2 != masked_3);
118 static_assert((token & 0xAAAAAAAA) == masked_1);
119 static_assert((token & 0x55555555) == masked_2);
120 static_assert((token & 0xFFFF0000) == masked_3);
121 }
122
TEST(TokenizeString,MaskExpr)123 TEST(TokenizeString, MaskExpr) {
124 uint32_t token = PW_TOKENIZE_STRING("(O_o)");
125 uint32_t masked_1 =
126 PW_TOKENIZE_STRING_MASK_EXPR("domain", 0xAAAAAAAA, "(O_o)");
127 uint32_t masked_2 =
128 PW_TOKENIZE_STRING_MASK_EXPR("domain", 0x55555555, "(O_o)");
129 uint32_t masked_3 =
130 PW_TOKENIZE_STRING_MASK_EXPR("domain", 0xFFFF0000, "(O_o)");
131
132 EXPECT_TRUE(token != masked_1 && token != masked_2 && token != masked_3);
133 EXPECT_TRUE(masked_1 != masked_2 && masked_2 != masked_3);
134 EXPECT_TRUE((token & 0xAAAAAAAA) == masked_1);
135 EXPECT_TRUE((token & 0x55555555) == masked_2);
136 EXPECT_TRUE((token & 0xFFFF0000) == masked_3);
137 }
138
139 // Use a function with a shorter name to test tokenizing __func__ and
140 // __PRETTY_FUNCTION__.
141 //
142 // WARNING: This function might cause errors for compilers other than GCC and
143 // clang. It relies on two GCC/clang extensions:
144 //
145 // 1 - The __PRETTY_FUNCTION__ C++ function name variable.
146 // 2 - __func__ as a static constexpr array instead of static const. See
147 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66639 for background.
148 //
TestName()149 void TestName() {
150 constexpr uint32_t function_hash = PW_TOKENIZE_STRING(__func__);
151 EXPECT_EQ(pw::tokenizer::Hash(__func__), function_hash);
152
153 // Check the non-standard __PRETTY_FUNCTION__ name.
154 constexpr uint32_t pretty_function = PW_TOKENIZE_STRING(__PRETTY_FUNCTION__);
155 EXPECT_EQ(pw::tokenizer::Hash(__PRETTY_FUNCTION__), pretty_function);
156 }
157
TEST(TokenizeString,FunctionName)158 TEST(TokenizeString, FunctionName) { TestName(); }
159
TEST(TokenizeString,Array)160 TEST(TokenizeString, Array) {
161 constexpr char array[] = "won-won-won-wonderful";
162
163 const uint32_t array_hash = PW_TOKENIZE_STRING(array);
164 EXPECT_EQ(Hash(array), array_hash);
165 }
166
TEST(TokenizeString,NullInString)167 TEST(TokenizeString, NullInString) {
168 // Use PW_TOKENIZER_STRING_TOKEN to avoid emitting strings with NUL into the
169 // ELF file. The CSV database format does not support NUL.
170 constexpr char nulls[32] = {};
171 static_assert(Hash(nulls) == PW_TOKENIZER_STRING_TOKEN(nulls));
172 static_assert(PW_TOKENIZER_STRING_TOKEN(nulls) != 0u);
173
174 static_assert(PW_TOKENIZER_STRING_TOKEN("\0") == Hash("\0"));
175 static_assert(PW_TOKENIZER_STRING_TOKEN("\0") != Hash(""));
176
177 static_assert(PW_TOKENIZER_STRING_TOKEN("abc\0def") == Hash("abc\0def"));
178
179 static_assert(Hash("abc\0def") != Hash("abc\0def\0"));
180 }
181
182 // Verify that we can tokenize multiple strings from one source line.
183 #define THREE_FOR_ONE(first, second, third) \
184 [[maybe_unused]] constexpr uint32_t token_1 = \
185 PW_TOKENIZE_STRING_DOMAIN("TEST_DOMAIN", first); \
186 [[maybe_unused]] constexpr uint32_t token_2 = \
187 PW_TOKENIZE_STRING_DOMAIN("TEST_DOMAIN", second); \
188 [[maybe_unused]] constexpr uint32_t token_3 = \
189 PW_TOKENIZE_STRING_DOMAIN("TEST_DOMAIN", third);
190
TEST(TokenizeString,MultipleTokenizationsInOneMacroExpansion)191 TEST(TokenizeString, MultipleTokenizationsInOneMacroExpansion) {
192 // This verifies that we can safely tokenize multiple times in a single macro
193 // expansion. This can be useful when for example a name and description are
194 // both tokenized after being passed into a macro.
195 //
196 // This test only verifies that this compiles correctly; it does not test
197 // that the tokenizations make it to the final token database.
198 THREE_FOR_ONE("hello", "yes", "something");
199 }
200
201 // Verify that we can tokenize multiple strings from one source line.
202 #define THREE_FOR_ONE_EXPR(first, second, third) \
203 [[maybe_unused]] uint32_t token_1 = \
204 PW_TOKENIZE_STRING_DOMAIN_EXPR("TEST_DOMAIN", first); \
205 [[maybe_unused]] uint32_t token_2 = \
206 PW_TOKENIZE_STRING_DOMAIN_EXPR("TEST_DOMAIN", second); \
207 [[maybe_unused]] uint32_t token_3 = \
208 PW_TOKENIZE_STRING_DOMAIN_EXPR("TEST_DOMAIN", third);
209
TEST(TokenizeString,MultipleTokenizationsInOneMacroExpansionExpr)210 TEST(TokenizeString, MultipleTokenizationsInOneMacroExpansionExpr) {
211 // This verifies that we can safely tokenize multiple times in a single macro
212 // expansion. This can be useful when for example a name and description are
213 // both tokenized after being passed into a macro.
214 //
215 // This test only verifies that this compiles correctly; it does not test
216 // that the tokenizations make it to the final token database.
217 THREE_FOR_ONE_EXPR("hello", "yes", "something");
218 }
219
220 class TokenizeToBuffer : public ::testing::Test {
221 public:
TokenizeToBuffer()222 TokenizeToBuffer() : buffer_{} {}
223
224 protected:
225 uint8_t buffer_[64];
226 };
227
TEST_F(TokenizeToBuffer,Integer64)228 TEST_F(TokenizeToBuffer, Integer64) {
229 size_t message_size = 14;
230 PW_TOKENIZE_TO_BUFFER(
231 buffer_,
232 &message_size,
233 "%" PRIu64,
234 static_cast<uint64_t>(0x55555555'55555555ull)); // 0xAAAAAAAA'AAAAAAAA
235
236 // Pattern becomes 10101010'11010101'10101010 ...
237 constexpr std::array<uint8_t, 14> expected =
238 ExpectedData<0xAA, 0xD5, 0xAA, 0xD5, 0xAA, 0xD5, 0xAA, 0xD5, 0xAA, 0x01>(
239 "%" PRIu64);
240 ASSERT_EQ(expected.size(), message_size);
241 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
242 }
243
TEST_F(TokenizeToBuffer,Integer64Overflow)244 TEST_F(TokenizeToBuffer, Integer64Overflow) {
245 size_t message_size;
246
247 for (size_t size = 4; size < 20; ++size) {
248 message_size = size;
249
250 PW_TOKENIZE_TO_BUFFER(
251 buffer_,
252 &message_size,
253 "%" PRIx64,
254 static_cast<uint64_t>(std::numeric_limits<int64_t>::min()));
255
256 if (size < 14) {
257 constexpr std::array<uint8_t, 4> empty = ExpectedData("%" PRIx64);
258 ASSERT_EQ(sizeof(uint32_t), message_size);
259 EXPECT_EQ(std::memcmp(empty.data(), &buffer_, empty.size()), 0);
260
261 // Make sure nothing was written past the end of the buffer.
262 EXPECT_TRUE(std::all_of(&buffer_[size], std::end(buffer_), [](uint8_t v) {
263 return v == '\0';
264 }));
265 } else {
266 constexpr std::array<uint8_t, 14> expected =
267 ExpectedData<0xff,
268 0xff,
269 0xff,
270 0xff,
271 0xff,
272 0xff,
273 0xff,
274 0xff,
275 0xff,
276 0x01>("%" PRIx64);
277 ASSERT_EQ(expected.size(), message_size);
278 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
279 }
280 }
281 }
282
TEST_F(TokenizeToBuffer,IntegerNegative)283 TEST_F(TokenizeToBuffer, IntegerNegative) {
284 size_t message_size = 9;
285 PW_TOKENIZE_TO_BUFFER(
286 buffer_, &message_size, "%" PRId32, std::numeric_limits<int32_t>::min());
287
288 // 0x8000'0000 -zig-zag-> 0xff'ff'ff'ff'0f
289 constexpr std::array<uint8_t, 9> expected =
290 ExpectedData<0xff, 0xff, 0xff, 0xff, 0x0f>("%" PRId32);
291 ASSERT_EQ(expected.size(), message_size);
292 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
293 }
294
TEST_F(TokenizeToBuffer,IntegerMin)295 TEST_F(TokenizeToBuffer, IntegerMin) {
296 size_t message_size = 9;
297 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "%d", -1);
298
299 constexpr std::array<uint8_t, 5> expected = ExpectedData<0x01>("%d");
300 ASSERT_EQ(expected.size(), message_size);
301 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
302 }
303
TEST_F(TokenizeToBuffer,IntegerDoesntFit)304 TEST_F(TokenizeToBuffer, IntegerDoesntFit) {
305 size_t message_size = 8;
306 PW_TOKENIZE_TO_BUFFER(
307 buffer_, &message_size, "%" PRId32, std::numeric_limits<int32_t>::min());
308
309 constexpr std::array<uint8_t, 4> expected = ExpectedData<>("%" PRId32);
310 ASSERT_EQ(expected.size(), message_size);
311 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
312 }
313
TEST_F(TokenizeToBuffer,String)314 TEST_F(TokenizeToBuffer, String) {
315 size_t message_size = sizeof(buffer_);
316
317 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
318 constexpr std::array<uint8_t, 10> expected =
319 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
320
321 ASSERT_EQ(expected.size(), message_size);
322 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
323 }
324
TEST_F(TokenizeToBuffer,String_BufferTooSmall_TruncatesAndSetsTopStatusBit)325 TEST_F(TokenizeToBuffer, String_BufferTooSmall_TruncatesAndSetsTopStatusBit) {
326 size_t message_size = 8;
327 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
328
329 constexpr std::array<uint8_t, 8> truncated_1 =
330 ExpectedData<0x83, '5', '4', '3'>("The answer is: %s");
331
332 ASSERT_EQ(truncated_1.size(), message_size);
333 EXPECT_EQ(std::memcmp(truncated_1.data(), buffer_, truncated_1.size()), 0);
334 }
335
TEST_F(TokenizeToBuffer,String_TwoBytesLeft_TruncatesToOneCharacter)336 TEST_F(TokenizeToBuffer, String_TwoBytesLeft_TruncatesToOneCharacter) {
337 size_t message_size = 6;
338 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
339
340 constexpr std::array<uint8_t, 6> truncated_2 =
341 ExpectedData<0x81, '5'>("The answer is: %s");
342
343 ASSERT_EQ(truncated_2.size(), message_size);
344 EXPECT_EQ(std::memcmp(truncated_2.data(), buffer_, truncated_2.size()), 0);
345 }
346
TEST_F(TokenizeToBuffer,String_OneByteLeft_OnlyWritesTruncatedStatusByte)347 TEST_F(TokenizeToBuffer, String_OneByteLeft_OnlyWritesTruncatedStatusByte) {
348 size_t message_size = 5;
349 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
350
351 std::array<uint8_t, 5> result = ExpectedData<0x80>("The answer is: %s");
352 ASSERT_EQ(result.size(), message_size);
353 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
354 }
355
TEST_F(TokenizeToBuffer,EmptyString_OneByteLeft_EncodesCorrectly)356 TEST_F(TokenizeToBuffer, EmptyString_OneByteLeft_EncodesCorrectly) {
357 size_t message_size = 5;
358 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "");
359
360 std::array<uint8_t, 5> result = ExpectedData<0>("The answer is: %s");
361 ASSERT_EQ(result.size(), message_size);
362 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
363 }
364
TEST_F(TokenizeToBuffer,String_ZeroBytesLeft_WritesNothing)365 TEST_F(TokenizeToBuffer, String_ZeroBytesLeft_WritesNothing) {
366 size_t message_size = 4;
367 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
368
369 constexpr std::array<uint8_t, 4> empty = ExpectedData<>("The answer is: %s");
370 ASSERT_EQ(empty.size(), message_size);
371 EXPECT_EQ(std::memcmp(empty.data(), buffer_, empty.size()), 0);
372 }
373
TEST_F(TokenizeToBuffer,Array)374 TEST_F(TokenizeToBuffer, Array) {
375 static constexpr char array[] = "1234";
376 size_t message_size = 4;
377 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, array);
378
379 constexpr std::array<uint8_t, 4> result = ExpectedData<>("1234");
380 ASSERT_EQ(result.size(), message_size);
381 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
382 }
383
TEST_F(TokenizeToBuffer,NullptrString_EncodesNull)384 TEST_F(TokenizeToBuffer, NullptrString_EncodesNull) {
385 char* string = nullptr;
386 size_t message_size = 9;
387 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", string);
388
389 std::array<uint8_t, 9> result =
390 ExpectedData<4, 'N', 'U', 'L', 'L'>("The answer is: %s");
391 ASSERT_EQ(result.size(), message_size);
392 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
393 }
394
TEST_F(TokenizeToBuffer,NullptrString_BufferTooSmall_EncodesTruncatedNull)395 TEST_F(TokenizeToBuffer, NullptrString_BufferTooSmall_EncodesTruncatedNull) {
396 char* string = nullptr;
397 size_t message_size = 6;
398 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", string);
399
400 std::array<uint8_t, 6> result = ExpectedData<0x81, 'N'>("The answer is: %s");
401 ASSERT_EQ(result.size(), message_size);
402 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
403 }
404
TEST_F(TokenizeToBuffer,Domain_String)405 TEST_F(TokenizeToBuffer, Domain_String) {
406 size_t message_size = sizeof(buffer_);
407
408 PW_TOKENIZE_TO_BUFFER_DOMAIN(
409 "TEST_DOMAIN", buffer_, &message_size, "The answer was: %s", "5432!");
410 constexpr std::array<uint8_t, 10> expected =
411 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer was: %s");
412
413 ASSERT_EQ(expected.size(), message_size);
414 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
415 }
416
TEST_F(TokenizeToBuffer,Mask)417 TEST_F(TokenizeToBuffer, Mask) {
418 size_t message_size = sizeof(buffer_);
419
420 PW_TOKENIZE_TO_BUFFER_MASK("TEST_DOMAIN",
421 0x0000FFFF,
422 buffer_,
423 &message_size,
424 "The answer was: %s",
425 "5432!");
426 constexpr std::array<uint8_t, 10> expected =
427 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer was: %s",
428 0x0000FFFF);
429
430 ASSERT_EQ(expected.size(), message_size);
431 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
432 }
433
TEST_F(TokenizeToBuffer,TruncateArgs)434 TEST_F(TokenizeToBuffer, TruncateArgs) {
435 // Args that can't fit are dropped completely
436 size_t message_size = 6;
437 PW_TOKENIZE_TO_BUFFER(buffer_,
438 &message_size,
439 "%u %d",
440 static_cast<uint8_t>(0b0010'1010u),
441 0xffffff);
442
443 constexpr std::array<uint8_t, 5> expected =
444 ExpectedData<0b0101'0100u>("%u %d");
445 ASSERT_EQ(expected.size(), message_size);
446 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
447 }
448
TEST_F(TokenizeToBuffer,NoRoomForToken)449 TEST_F(TokenizeToBuffer, NoRoomForToken) {
450 // Nothing is written if there isn't room for the token.
451 std::memset(buffer_, '$', sizeof(buffer_));
452 auto is_untouched = [](uint8_t v) { return v == '$'; };
453
454 size_t message_size = 3;
455 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer: \"%s\"", "5432!");
456 EXPECT_EQ(0u, message_size);
457 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
458
459 message_size = 2;
460 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "Jello, world!");
461 EXPECT_EQ(0u, message_size);
462 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
463
464 message_size = 1;
465 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "Jello!");
466 EXPECT_EQ(0u, message_size);
467 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
468
469 message_size = 0;
470 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "Jello?");
471 EXPECT_EQ(0u, message_size);
472 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
473 }
474
TEST_F(TokenizeToBuffer,CharArray)475 TEST_F(TokenizeToBuffer, CharArray) {
476 size_t message_size = sizeof(buffer_);
477 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, __func__);
478 constexpr auto expected = ExpectedData(__func__);
479 ASSERT_EQ(expected.size(), message_size);
480 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
481 }
482
TEST_F(TokenizeToBuffer,C_StringShortFloat)483 TEST_F(TokenizeToBuffer, C_StringShortFloat) {
484 size_t size = sizeof(buffer_);
485 pw_tokenizer_ToBufferTest_StringShortFloat(buffer_, &size);
486 constexpr std::array<uint8_t, 11> expected = // clang-format off
487 ExpectedData<1, '1', // string '1'
488 3, // -2 (zig-zag encoded)
489 0x00, 0x00, 0x40, 0x40 // 3.0 in floating point
490 >(TEST_FORMAT_STRING_SHORT_FLOAT);
491 ASSERT_EQ(expected.size(), size); // clang-format on
492 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
493 }
494
TEST_F(TokenizeToBuffer,C_SequentialZigZag)495 TEST_F(TokenizeToBuffer, C_SequentialZigZag) {
496 size_t size = sizeof(buffer_);
497 pw_tokenizer_ToBufferTest_SequentialZigZag(buffer_, &size);
498 constexpr std::array<uint8_t, 18> expected =
499 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
500 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
501
502 ASSERT_EQ(expected.size(), size);
503 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
504 }
505
TEST_F(TokenizeToBuffer,C_Overflow)506 TEST_F(TokenizeToBuffer, C_Overflow) {
507 std::memset(buffer_, '$', sizeof(buffer_));
508
509 {
510 size_t size = 7;
511 pw_tokenizer_ToBufferTest_Requires8(buffer_, &size);
512 constexpr std::array<uint8_t, 7> expected =
513 ExpectedData<2, 'h', 'i'>(TEST_FORMAT_REQUIRES_8);
514 ASSERT_EQ(expected.size(), size);
515 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
516 EXPECT_EQ(buffer_[7], '$');
517 }
518
519 {
520 size_t size = 8;
521 pw_tokenizer_ToBufferTest_Requires8(buffer_, &size);
522 constexpr std::array<uint8_t, 8> expected =
523 ExpectedData<2, 'h', 'i', 13>(TEST_FORMAT_REQUIRES_8);
524 ASSERT_EQ(expected.size(), size);
525 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
526 EXPECT_EQ(buffer_[8], '$');
527 }
528 }
529
530 #define MACRO_THAT_CALLS_ANOTHER_MACRO(action) ANOTHER_MACRO(action)
531
532 #define ANOTHER_MACRO(action) action
533
TEST_F(TokenizeToBuffer,AsArgumentToAnotherMacro)534 TEST_F(TokenizeToBuffer, AsArgumentToAnotherMacro) {
535 size_t message_size = sizeof(buffer_);
536 MACRO_THAT_CALLS_ANOTHER_MACRO(
537 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, __func__));
538 constexpr auto expected = ExpectedData(__func__);
539 ASSERT_EQ(expected.size(), message_size);
540 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
541 }
542
543 #undef MACRO_THAT_CALLS_ANOTHER_MACRO
544 #undef ANOTHER_MACRO
545
546 // Hijack an internal macro to capture the tokenizer domain.
547 #undef PW_TOKENIZER_DEFINE_TOKEN
548 #define PW_TOKENIZER_DEFINE_TOKEN(token, domain, string) \
549 tokenizer_domain = domain; \
550 string_literal = string
551
TEST_F(TokenizeToBuffer,Domain_Default)552 TEST_F(TokenizeToBuffer, Domain_Default) {
553 const char* tokenizer_domain = nullptr;
554 const char* string_literal = nullptr;
555
556 size_t message_size = sizeof(buffer_);
557
558 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
559
560 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
561 EXPECT_STREQ(string_literal, "The answer is: %s");
562 }
563
TEST_F(TokenizeToBuffer,Domain_Specified)564 TEST_F(TokenizeToBuffer, Domain_Specified) {
565 const char* tokenizer_domain = nullptr;
566 const char* string_literal = nullptr;
567
568 size_t message_size = sizeof(buffer_);
569
570 PW_TOKENIZE_TO_BUFFER_DOMAIN(
571 "._.", buffer_, &message_size, "The answer is: %s", "5432!");
572
573 EXPECT_STREQ(tokenizer_domain, "._.");
574 EXPECT_STREQ(string_literal, "The answer is: %s");
575 }
576
577 #undef PW_TOKENIZER_DEFINE_TOKEN
578 #define PW_TOKENIZER_DEFINE_TOKEN(token, domain, string) \
579 static_assert(false, \
580 "The internal PW_TOKENIZER_DEFINE_TOKEN was " \
581 "repurposed earlier in this test! The macro or any macro " \
582 "that calls it cannot be used here!")
583
584 } // namespace
585 } // namespace pw::tokenizer
586