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