xref: /aosp_15_r20/external/pigweed/pw_tokenizer/tokenize_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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