blob: ff87f514ec27c88e9eb39b3c95dac3e5fa53526e [file] [log] [blame]
Wyatt Hepler80c6ee52020-01-03 09:54:58 -08001// 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>
Wyatt Hepler4b62b892021-03-04 10:03:43 -080021#include <limits>
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080022
23#include "gtest/gtest.h"
Wyatt Heplereb020a12020-10-28 14:01:51 -070024#include "pw_tokenizer/hash.h"
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080025#include "pw_tokenizer_private/tokenize_test.h"
26#include "pw_varint/varint.h"
27
28namespace pw::tokenizer {
29namespace {
30
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080031// Constructs an array with the hashed string followed by the provided bytes.
Ewout van Bekkum5ea33402021-03-31 11:00:02 -070032template <uint8_t... kData, size_t kSize>
Wyatt Hepler4b62b892021-03-04 10:03:43 -080033constexpr auto ExpectedData(
Ewout van Bekkum5ea33402021-03-31 11:00:02 -070034 const char (&format)[kSize],
Wyatt Hepler4b62b892021-03-04 10:03:43 -080035 uint32_t token_mask = std::numeric_limits<uint32_t>::max()) {
36 const uint32_t value = Hash(format) & token_mask;
Ewout van Bekkum5ea33402021-03-31 11:00:02 -070037 return std::array<uint8_t, sizeof(uint32_t) + sizeof...(kData)>{
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080038 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),
Ewout van Bekkum5ea33402021-03-31 11:00:02 -070042 kData...};
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080043}
44
Wyatt Hepler7e587232020-08-28 07:51:29 -070045TEST(TokenizeString, EmptyString_IsZero) {
Wyatt Hepler7a5e4d62020-08-31 08:39:16 -070046 constexpr pw_tokenizer_Token token = PW_TOKENIZE_STRING("");
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080047 EXPECT_EQ(0u, token);
48}
49
Wyatt Hepler7e587232020-08-28 07:51:29 -070050TEST(TokenizeString, String_MatchesHash) {
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080051 constexpr uint32_t token = PW_TOKENIZE_STRING("[:-)");
Wyatt Heplereb020a12020-10-28 14:01:51 -070052 EXPECT_EQ(Hash("[:-)"), token);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080053}
54
55constexpr uint32_t kGlobalToken = PW_TOKENIZE_STRING(">:-[]");
56
Wyatt Hepler7e587232020-08-28 07:51:29 -070057TEST(TokenizeString, GlobalVariable_MatchesHash) {
Wyatt Heplereb020a12020-10-28 14:01:51 -070058 EXPECT_EQ(Hash(">:-[]"), kGlobalToken);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -080059}
60
Wyatt Hepler7e587232020-08-28 07:51:29 -070061struct TokenizedWithinClass {
62 static constexpr uint32_t kThisToken = PW_TOKENIZE_STRING("???");
63};
64
Wyatt Heplereb020a12020-10-28 14:01:51 -070065static_assert(Hash("???") == TokenizedWithinClass::kThisToken);
Wyatt Hepler7e587232020-08-28 07:51:29 -070066
67TEST(TokenizeString, ClassMember_MatchesHash) {
Wyatt Heplereb020a12020-10-28 14:01:51 -070068 EXPECT_EQ(Hash("???"), TokenizedWithinClass().kThisToken);
Wyatt Hepler7e587232020-08-28 07:51:29 -070069}
70
Wyatt Hepler4b62b892021-03-04 10:03:43 -080071TEST(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
Wyatt Hepler7e587232020-08-28 07:51:29 -070087// 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//
97void TestName() {
98 constexpr uint32_t function_hash = PW_TOKENIZE_STRING(__func__);
Wyatt Heplereb020a12020-10-28 14:01:51 -070099 EXPECT_EQ(pw::tokenizer::Hash(__func__), function_hash);
Wyatt Hepler7e587232020-08-28 07:51:29 -0700100
101 // Check the non-standard __PRETTY_FUNCTION__ name.
102 constexpr uint32_t pretty_function = PW_TOKENIZE_STRING(__PRETTY_FUNCTION__);
Wyatt Heplereb020a12020-10-28 14:01:51 -0700103 EXPECT_EQ(pw::tokenizer::Hash(__PRETTY_FUNCTION__), pretty_function);
Wyatt Hepler7e587232020-08-28 07:51:29 -0700104}
105
106TEST(TokenizeString, FunctionName) { TestName(); }
107
108TEST(TokenizeString, Array) {
109 constexpr char array[] = "won-won-won-wonderful";
110
111 const uint32_t array_hash = PW_TOKENIZE_STRING(array);
Wyatt Heplereb020a12020-10-28 14:01:51 -0700112 EXPECT_EQ(Hash(array), array_hash);
113}
114
115TEST(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"));
Wyatt Hepler7e587232020-08-28 07:51:29 -0700128}
129
Keir Mierle42f41f72020-08-05 00:15:56 -0700130// Verify that we can tokenize multiple strings from one source line.
Wyatt Heplereb020a12020-10-28 14:01:51 -0700131#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);
Keir Mierle42f41f72020-08-05 00:15:56 -0700138
Wyatt Hepler7e587232020-08-28 07:51:29 -0700139TEST(TokenizeString, MultipleTokenizationsInOneMacroExpansion) {
Keir Mierle42f41f72020-08-05 00:15:56 -0700140 // 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
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800149class TokenizeToBuffer : public ::testing::Test {
150 public:
151 TokenizeToBuffer() : buffer_{} {}
152
153 protected:
154 uint8_t buffer_[64];
155};
156
157TEST_F(TokenizeToBuffer, Integer64) {
158 size_t message_size = 14;
159 PW_TOKENIZE_TO_BUFFER(
160 buffer_,
161 &message_size,
162 "%" PRIu64,
163 static_cast<uint64_t>(0x55555555'55555555ull)); // 0xAAAAAAAA'AAAAAAAA
164
165 // Pattern becomes 10101010'11010101'10101010 ...
166 constexpr std::array<uint8_t, 14> expected =
167 ExpectedData<0xAA, 0xD5, 0xAA, 0xD5, 0xAA, 0xD5, 0xAA, 0xD5, 0xAA, 0x01>(
168 "%" PRIu64);
169 ASSERT_EQ(expected.size(), message_size);
170 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
171}
172
173TEST_F(TokenizeToBuffer, Integer64Overflow) {
174 size_t message_size;
175
176 for (size_t size = 4; size < 20; ++size) {
177 message_size = size;
178
179 PW_TOKENIZE_TO_BUFFER(
180 buffer_,
181 &message_size,
182 "%" PRIx64,
183 static_cast<uint64_t>(std::numeric_limits<int64_t>::min()));
184
185 if (size < 14) {
186 constexpr std::array<uint8_t, 4> empty = ExpectedData("%" PRIx64);
187 ASSERT_EQ(sizeof(uint32_t), message_size);
188 EXPECT_EQ(std::memcmp(empty.data(), &buffer_, empty.size()), 0);
189
190 // Make sure nothing was written past the end of the buffer.
191 EXPECT_TRUE(std::all_of(&buffer_[size], std::end(buffer_), [](uint8_t v) {
192 return v == '\0';
193 }));
194 } else {
195 constexpr std::array<uint8_t, 14> expected =
196 ExpectedData<0xff,
197 0xff,
198 0xff,
199 0xff,
200 0xff,
201 0xff,
202 0xff,
203 0xff,
204 0xff,
205 0x01>("%" PRIx64);
206 ASSERT_EQ(expected.size(), message_size);
207 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
208 }
209 }
210}
211
212TEST_F(TokenizeToBuffer, IntegerNegative) {
213 size_t message_size = 9;
214 PW_TOKENIZE_TO_BUFFER(
215 buffer_, &message_size, "%" PRId32, std::numeric_limits<int32_t>::min());
216
217 // 0x8000'0000 -zig-zag-> 0xff'ff'ff'ff'0f
218 constexpr std::array<uint8_t, 9> expected =
219 ExpectedData<0xff, 0xff, 0xff, 0xff, 0x0f>("%" PRId32);
220 ASSERT_EQ(expected.size(), message_size);
221 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
222}
223
224TEST_F(TokenizeToBuffer, IntegerMin) {
225 size_t message_size = 9;
226 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "%d", -1);
227
228 constexpr std::array<uint8_t, 5> expected = ExpectedData<0x01>("%d");
229 ASSERT_EQ(expected.size(), message_size);
230 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
231}
232
233TEST_F(TokenizeToBuffer, IntegerDoesntFit) {
234 size_t message_size = 8;
235 PW_TOKENIZE_TO_BUFFER(
236 buffer_, &message_size, "%" PRId32, std::numeric_limits<int32_t>::min());
237
238 constexpr std::array<uint8_t, 4> expected = ExpectedData<>("%" PRId32);
239 ASSERT_EQ(expected.size(), message_size);
240 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
241}
242
243TEST_F(TokenizeToBuffer, String) {
244 size_t message_size = sizeof(buffer_);
245
246 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
247 constexpr std::array<uint8_t, 10> expected =
248 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
249
250 ASSERT_EQ(expected.size(), message_size);
251 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
252}
253
254TEST_F(TokenizeToBuffer, String_BufferTooSmall_TruncatesAndSetsTopStatusBit) {
255 size_t message_size = 8;
256 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
257
258 constexpr std::array<uint8_t, 8> truncated_1 =
259 ExpectedData<0x83, '5', '4', '3'>("The answer is: %s");
260
261 ASSERT_EQ(truncated_1.size(), message_size);
262 EXPECT_EQ(std::memcmp(truncated_1.data(), buffer_, truncated_1.size()), 0);
263}
264
265TEST_F(TokenizeToBuffer, String_TwoBytesLeft_TruncatesToOneCharacter) {
266 size_t message_size = 6;
267 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
268
269 constexpr std::array<uint8_t, 6> truncated_2 =
270 ExpectedData<0x81, '5'>("The answer is: %s");
271
272 ASSERT_EQ(truncated_2.size(), message_size);
273 EXPECT_EQ(std::memcmp(truncated_2.data(), buffer_, truncated_2.size()), 0);
274}
275
276TEST_F(TokenizeToBuffer, String_OneByteLeft_OnlyWritesTruncatedStatusByte) {
277 size_t message_size = 5;
278 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
279
280 std::array<uint8_t, 5> result = ExpectedData<0x80>("The answer is: %s");
281 ASSERT_EQ(result.size(), message_size);
282 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
283}
284
285TEST_F(TokenizeToBuffer, EmptyString_OneByteLeft_EncodesCorrectly) {
286 size_t message_size = 5;
287 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "");
288
289 std::array<uint8_t, 5> result = ExpectedData<0>("The answer is: %s");
290 ASSERT_EQ(result.size(), message_size);
291 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
292}
293
294TEST_F(TokenizeToBuffer, String_ZeroBytesLeft_WritesNothing) {
295 size_t message_size = 4;
296 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
297
298 constexpr std::array<uint8_t, 4> empty = ExpectedData<>("The answer is: %s");
299 ASSERT_EQ(empty.size(), message_size);
300 EXPECT_EQ(std::memcmp(empty.data(), buffer_, empty.size()), 0);
301}
302
Wyatt Hepler7e587232020-08-28 07:51:29 -0700303TEST_F(TokenizeToBuffer, Array) {
304 static constexpr char array[] = "1234";
305 size_t message_size = 4;
306 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, array);
307
308 constexpr std::array<uint8_t, 4> result = ExpectedData<>("1234");
309 ASSERT_EQ(result.size(), message_size);
310 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
311}
312
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800313TEST_F(TokenizeToBuffer, NullptrString_EncodesNull) {
314 char* string = nullptr;
315 size_t message_size = 9;
316 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", string);
317
318 std::array<uint8_t, 9> result =
319 ExpectedData<4, 'N', 'U', 'L', 'L'>("The answer is: %s");
320 ASSERT_EQ(result.size(), message_size);
321 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
322}
323
324TEST_F(TokenizeToBuffer, NullptrString_BufferTooSmall_EncodesTruncatedNull) {
325 char* string = nullptr;
326 size_t message_size = 6;
327 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", string);
328
329 std::array<uint8_t, 6> result = ExpectedData<0x81, 'N'>("The answer is: %s");
330 ASSERT_EQ(result.size(), message_size);
331 EXPECT_EQ(std::memcmp(result.data(), buffer_, result.size()), 0);
332}
333
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700334TEST_F(TokenizeToBuffer, Domain_String) {
335 size_t message_size = sizeof(buffer_);
336
337 PW_TOKENIZE_TO_BUFFER_DOMAIN(
338 "TEST_DOMAIN", buffer_, &message_size, "The answer was: %s", "5432!");
339 constexpr std::array<uint8_t, 10> expected =
340 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer was: %s");
341
342 ASSERT_EQ(expected.size(), message_size);
343 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
344}
345
Wyatt Hepler4b62b892021-03-04 10:03:43 -0800346TEST_F(TokenizeToBuffer, Mask) {
347 size_t message_size = sizeof(buffer_);
348
349 PW_TOKENIZE_TO_BUFFER_MASK("TEST_DOMAIN",
350 0x0000FFFF,
351 buffer_,
352 &message_size,
353 "The answer was: %s",
354 "5432!");
355 constexpr std::array<uint8_t, 10> expected =
356 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer was: %s",
357 0x0000FFFF);
358
359 ASSERT_EQ(expected.size(), message_size);
360 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
361}
362
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800363TEST_F(TokenizeToBuffer, TruncateArgs) {
364 // Args that can't fit are dropped completely
365 size_t message_size = 6;
366 PW_TOKENIZE_TO_BUFFER(buffer_,
367 &message_size,
368 "%u %d",
369 static_cast<uint8_t>(0b0010'1010u),
370 0xffffff);
371
372 constexpr std::array<uint8_t, 5> expected =
373 ExpectedData<0b0101'0100u>("%u %d");
374 ASSERT_EQ(expected.size(), message_size);
375 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
376}
377
378TEST_F(TokenizeToBuffer, NoRoomForToken) {
379 // Nothing is written if there isn't room for the token.
380 std::memset(buffer_, '$', sizeof(buffer_));
381 auto is_untouched = [](uint8_t v) { return v == '$'; };
382
383 size_t message_size = 3;
384 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer: \"%s\"", "5432!");
385 EXPECT_EQ(0u, message_size);
386 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
387
388 message_size = 2;
389 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "Jello, world!");
390 EXPECT_EQ(0u, message_size);
391 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
392
393 message_size = 1;
394 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "Jello!");
395 EXPECT_EQ(0u, message_size);
396 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
397
398 message_size = 0;
399 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "Jello?");
400 EXPECT_EQ(0u, message_size);
401 EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
402}
403
Wyatt Heplerbcf07352021-04-05 14:44:30 -0700404TEST_F(TokenizeToBuffer, CharArray) {
405 size_t message_size = sizeof(buffer_);
406 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, __func__);
407 constexpr auto expected = ExpectedData(__func__);
408 ASSERT_EQ(expected.size(), message_size);
409 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
410}
411
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800412TEST_F(TokenizeToBuffer, C_StringShortFloat) {
413 size_t size = sizeof(buffer_);
Wyatt Hepler7a5e4d62020-08-31 08:39:16 -0700414 pw_tokenizer_ToBufferTest_StringShortFloat(buffer_, &size);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800415 constexpr std::array<uint8_t, 11> expected = // clang-format off
416 ExpectedData<1, '1', // string '1'
417 3, // -2 (zig-zag encoded)
418 0x00, 0x00, 0x40, 0x40 // 3.0 in floating point
419 >(TEST_FORMAT_STRING_SHORT_FLOAT);
420 ASSERT_EQ(expected.size(), size); // clang-format on
421 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
422}
423
424TEST_F(TokenizeToBuffer, C_SequentialZigZag) {
425 size_t size = sizeof(buffer_);
Wyatt Hepler7a5e4d62020-08-31 08:39:16 -0700426 pw_tokenizer_ToBufferTest_SequentialZigZag(buffer_, &size);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800427 constexpr std::array<uint8_t, 18> expected =
428 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
429 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
430
431 ASSERT_EQ(expected.size(), size);
432 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
433}
434
435TEST_F(TokenizeToBuffer, C_Overflow) {
436 std::memset(buffer_, '$', sizeof(buffer_));
437
438 {
439 size_t size = 7;
Wyatt Hepler7a5e4d62020-08-31 08:39:16 -0700440 pw_tokenizer_ToBufferTest_Requires8(buffer_, &size);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800441 constexpr std::array<uint8_t, 7> expected =
442 ExpectedData<2, 'h', 'i'>(TEST_FORMAT_REQUIRES_8);
443 ASSERT_EQ(expected.size(), size);
444 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
445 EXPECT_EQ(buffer_[7], '$');
446 }
447
448 {
449 size_t size = 8;
Wyatt Hepler7a5e4d62020-08-31 08:39:16 -0700450 pw_tokenizer_ToBufferTest_Requires8(buffer_, &size);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800451 constexpr std::array<uint8_t, 8> expected =
452 ExpectedData<2, 'h', 'i', 13>(TEST_FORMAT_REQUIRES_8);
453 ASSERT_EQ(expected.size(), size);
454 EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
455 EXPECT_EQ(buffer_[8], '$');
456 }
457}
458
459// Test fixture for callback and global handler. Both of these need a global
460// message buffer. To keep the message buffers separate, template this on the
461// derived class type.
462template <typename Impl>
463class GlobalMessage : public ::testing::Test {
464 public:
465 static void SetMessage(const uint8_t* message, size_t size) {
466 ASSERT_LE(size, sizeof(message_));
467 std::memcpy(message_, message, size);
468 message_size_bytes_ = size;
469 }
470
471 protected:
472 GlobalMessage() {
473 std::memset(message_, 0, sizeof(message_));
474 message_size_bytes_ = 0;
475 }
476
477 static uint8_t message_[256];
478 static size_t message_size_bytes_;
479};
480
481template <typename Impl>
482uint8_t GlobalMessage<Impl>::message_[256] = {};
483template <typename Impl>
484size_t GlobalMessage<Impl>::message_size_bytes_ = 0;
485
486class TokenizeToCallback : public GlobalMessage<TokenizeToCallback> {};
487
488TEST_F(TokenizeToCallback, Variety) {
489 PW_TOKENIZE_TO_CALLBACK(
490 SetMessage, "%s there are %x (%.2f) of them%c", "Now", 2u, 2.0f, '.');
491 const auto expected = // clang-format off
492 ExpectedData<3, 'N', 'o', 'w', // string "Now"
493 0x04, // unsigned 2 (zig-zag encoded)
494 0x00, 0x00, 0x00, 0x40, // float 2.0
495 0x5C // char '.' (0x2E, zig-zag encoded)
496 >("%s there are %x (%.2f) of them%c");
497 // clang-format on
498 ASSERT_EQ(expected.size(), message_size_bytes_);
499 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
500}
501
502TEST_F(TokenizeToCallback, Strings) {
503 PW_TOKENIZE_TO_CALLBACK(SetMessage, "The answer is: %s", "5432!");
504 constexpr std::array<uint8_t, 10> expected =
505 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
506 ASSERT_EQ(expected.size(), message_size_bytes_);
507 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
508}
509
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700510TEST_F(TokenizeToCallback, Domain_Strings) {
511 PW_TOKENIZE_TO_CALLBACK_DOMAIN(
512 "TEST_DOMAIN", SetMessage, "The answer is: %s", "5432!");
513 constexpr std::array<uint8_t, 10> expected =
514 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
515 ASSERT_EQ(expected.size(), message_size_bytes_);
516 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
517}
518
Wyatt Hepler4b62b892021-03-04 10:03:43 -0800519TEST_F(TokenizeToCallback, Mask) {
520 PW_TOKENIZE_TO_CALLBACK_MASK(
521 "TEST_DOMAIN", 0x00000FFF, SetMessage, "The answer is: %s", "5432!");
522 constexpr std::array<uint8_t, 10> expected =
523 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s", 0x00000FFF);
524 ASSERT_EQ(expected.size(), message_size_bytes_);
525 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
526}
527
Wyatt Heplerbcf07352021-04-05 14:44:30 -0700528TEST_F(TokenizeToCallback, CharArray) {
529 PW_TOKENIZE_TO_CALLBACK(SetMessage, __func__);
530 constexpr auto expected = ExpectedData(__func__);
531 ASSERT_EQ(expected.size(), message_size_bytes_);
532 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
533}
534
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800535TEST_F(TokenizeToCallback, C_SequentialZigZag) {
Wyatt Hepler7a5e4d62020-08-31 08:39:16 -0700536 pw_tokenizer_ToCallbackTest_SequentialZigZag(SetMessage);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800537
538 constexpr std::array<uint8_t, 18> expected =
539 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
540 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
541 ASSERT_EQ(expected.size(), message_size_bytes_);
542 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
543}
544
Wyatt Heplereb020a12020-10-28 14:01:51 -0700545// Hijack an internal macro to capture the tokenizer domain.
546#undef _PW_TOKENIZER_RECORD_ORIGINAL_STRING
547#define _PW_TOKENIZER_RECORD_ORIGINAL_STRING(token, domain, string) \
548 tokenizer_domain = domain; \
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700549 string_literal = string
550
551TEST_F(TokenizeToBuffer, Domain_Default) {
552 const char* tokenizer_domain = nullptr;
553 const char* string_literal = nullptr;
554
555 size_t message_size = sizeof(buffer_);
556
557 PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, "The answer is: %s", "5432!");
558
559 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
560 EXPECT_STREQ(string_literal, "The answer is: %s");
561}
562
563TEST_F(TokenizeToBuffer, Domain_Specified) {
564 const char* tokenizer_domain = nullptr;
565 const char* string_literal = nullptr;
566
567 size_t message_size = sizeof(buffer_);
568
569 PW_TOKENIZE_TO_BUFFER_DOMAIN(
570 "._.", buffer_, &message_size, "The answer is: %s", "5432!");
571
572 EXPECT_STREQ(tokenizer_domain, "._.");
573 EXPECT_STREQ(string_literal, "The answer is: %s");
574}
575
576TEST_F(TokenizeToCallback, Domain_Default) {
577 const char* tokenizer_domain = nullptr;
578 const char* string_literal = nullptr;
579
580 PW_TOKENIZE_TO_CALLBACK(SetMessage, "The answer is: %s", "5432!");
581
582 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
583 EXPECT_STREQ(string_literal, "The answer is: %s");
584}
585
586TEST_F(TokenizeToCallback, Domain_Specified) {
587 const char* tokenizer_domain = nullptr;
588 const char* string_literal = nullptr;
589
590 PW_TOKENIZE_TO_CALLBACK_DOMAIN(
591 "ThisIsTheDomain", SetMessage, "The answer is: %s", "5432!");
592
593 EXPECT_STREQ(tokenizer_domain, "ThisIsTheDomain");
594 EXPECT_STREQ(string_literal, "The answer is: %s");
595}
596
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800597} // namespace
598} // namespace pw::tokenizer