1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
19 #include "gtest/gtest.h"
20 #include "pw_tokenizer/tokenize_to_global_handler.h"
21 #include "pw_tokenizer/tokenize_to_global_handler_with_payload.h"
22 #include "pw_tokenizer_private/tokenize_test.h"
24 namespace pw::tokenizer {
27 // Constructs an array with the hashed string followed by the provided bytes.
28 template <uint8_t... kData, size_t kSize>
29 constexpr auto ExpectedData(const char (&format)[kSize]) {
30 const uint32_t value = Hash(format);
31 return std::array<uint8_t, sizeof(uint32_t) + sizeof...(kData)>{
32 static_cast<uint8_t>(value & 0xff),
33 static_cast<uint8_t>(value >> 8 & 0xff),
34 static_cast<uint8_t>(value >> 16 & 0xff),
35 static_cast<uint8_t>(value >> 24 & 0xff),
39 // Test fixture for both global handler functions. Both need a global message
40 // buffer. To keep the message buffers separate, template this on the derived
42 template <typename Impl>
43 class GlobalMessage : public ::testing::Test {
45 static void SetMessage(const uint8_t* message, size_t size) {
46 ASSERT_LE(size, sizeof(message_));
47 std::memcpy(message_, message, size);
48 message_size_bytes_ = size;
53 std::memset(message_, 0, sizeof(message_));
54 message_size_bytes_ = 0;
57 static uint8_t message_[256];
58 static size_t message_size_bytes_;
61 template <typename Impl>
62 uint8_t GlobalMessage<Impl>::message_[256] = {};
63 template <typename Impl>
64 size_t GlobalMessage<Impl>::message_size_bytes_ = 0;
66 class TokenizeToGlobalHandler : public GlobalMessage<TokenizeToGlobalHandler> {
69 TEST_F(TokenizeToGlobalHandler, Variety) {
70 PW_TOKENIZE_TO_GLOBAL_HANDLER("%x%lld%1.2f%s", 0, 0ll, -0.0, "");
72 ExpectedData<0, 0, 0x00, 0x00, 0x00, 0x80, 0>("%x%lld%1.2f%s");
73 ASSERT_EQ(expected.size(), message_size_bytes_);
74 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
77 TEST_F(TokenizeToGlobalHandler, Strings) {
78 PW_TOKENIZE_TO_GLOBAL_HANDLER("The answer is: %s", "5432!");
79 constexpr std::array<uint8_t, 10> expected =
80 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
81 ASSERT_EQ(expected.size(), message_size_bytes_);
82 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
85 TEST_F(TokenizeToGlobalHandler, Domain_Strings) {
86 PW_TOKENIZE_TO_GLOBAL_HANDLER_DOMAIN(
87 "TEST_DOMAIN", "The answer is: %s", "5432!");
88 constexpr std::array<uint8_t, 10> expected =
89 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
90 ASSERT_EQ(expected.size(), message_size_bytes_);
91 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
94 TEST_F(TokenizeToGlobalHandler, C_SequentialZigZag) {
95 pw_tokenizer_ToGlobalHandlerTest_SequentialZigZag();
97 constexpr std::array<uint8_t, 18> expected =
98 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
99 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
100 ASSERT_EQ(expected.size(), message_size_bytes_);
101 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
104 extern "C" void pw_tokenizer_HandleEncodedMessage(
105 const uint8_t* encoded_message, size_t size_bytes) {
106 TokenizeToGlobalHandler::SetMessage(encoded_message, size_bytes);
109 class TokenizeToGlobalHandlerWithPayload
110 : public GlobalMessage<TokenizeToGlobalHandlerWithPayload> {
112 static void SetPayload(pw_tokenizer_Payload payload) {
113 payload_ = static_cast<intptr_t>(payload);
117 TokenizeToGlobalHandlerWithPayload() { payload_ = {}; }
119 static intptr_t payload_;
122 intptr_t TokenizeToGlobalHandlerWithPayload::payload_;
124 TEST_F(TokenizeToGlobalHandlerWithPayload, Variety) {
125 ASSERT_NE(payload_, 123);
127 const auto expected =
128 ExpectedData<0, 0, 0x00, 0x00, 0x00, 0x80, 0>("%x%lld%1.2f%s");
130 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
131 static_cast<pw_tokenizer_Payload>(123),
137 ASSERT_EQ(expected.size(), message_size_bytes_);
138 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
139 EXPECT_EQ(payload_, 123);
141 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
142 static_cast<pw_tokenizer_Payload>(-543),
148 ASSERT_EQ(expected.size(), message_size_bytes_);
149 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
150 EXPECT_EQ(payload_, -543);
153 constexpr std::array<uint8_t, 10> kExpected =
154 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
156 TEST_F(TokenizeToGlobalHandlerWithPayload, Strings_ZeroPayload) {
157 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD({}, "The answer is: %s", "5432!");
159 ASSERT_EQ(kExpected.size(), message_size_bytes_);
160 EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
161 EXPECT_EQ(payload_, 0);
164 TEST_F(TokenizeToGlobalHandlerWithPayload, Strings_NonZeroPayload) {
165 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
166 static_cast<pw_tokenizer_Payload>(5432), "The answer is: %s", "5432!");
168 ASSERT_EQ(kExpected.size(), message_size_bytes_);
169 EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
170 EXPECT_EQ(payload_, 5432);
173 TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Strings) {
174 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_DOMAIN(
176 static_cast<pw_tokenizer_Payload>(5432),
179 ASSERT_EQ(kExpected.size(), message_size_bytes_);
180 EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
181 EXPECT_EQ(payload_, 5432);
189 TEST_F(TokenizeToGlobalHandlerWithPayload, PointerToStack) {
192 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
193 reinterpret_cast<pw_tokenizer_Payload>(&foo), "Boring!");
195 constexpr auto expected = ExpectedData("Boring!");
196 static_assert(expected.size() == 4);
197 ASSERT_EQ(expected.size(), message_size_bytes_);
198 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
200 Foo* payload_foo = reinterpret_cast<Foo*>(payload_);
201 ASSERT_EQ(&foo, payload_foo);
202 EXPECT_EQ(payload_foo->a, 254u);
203 EXPECT_TRUE(payload_foo->b);
206 TEST_F(TokenizeToGlobalHandlerWithPayload, C_SequentialZigZag) {
207 pw_tokenizer_ToGlobalHandlerWithPayloadTest_SequentialZigZag();
209 constexpr std::array<uint8_t, 18> expected =
210 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
211 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
212 ASSERT_EQ(expected.size(), message_size_bytes_);
213 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
214 EXPECT_EQ(payload_, 600613);
217 extern "C" void pw_tokenizer_HandleEncodedMessageWithPayload(
218 pw_tokenizer_Payload payload,
219 const uint8_t* encoded_message,
221 TokenizeToGlobalHandlerWithPayload::SetMessage(encoded_message, size_bytes);
222 TokenizeToGlobalHandlerWithPayload::SetPayload(payload);
225 // Hijack an internal macro to capture the tokenizer domain.
226 #undef _PW_TOKENIZER_RECORD_ORIGINAL_STRING
227 #define _PW_TOKENIZER_RECORD_ORIGINAL_STRING(token, domain, string) \
228 tokenizer_domain = domain; \
229 string_literal = string
231 TEST_F(TokenizeToGlobalHandler, Domain_Default) {
232 const char* tokenizer_domain = nullptr;
233 const char* string_literal = nullptr;
235 PW_TOKENIZE_TO_GLOBAL_HANDLER("404");
237 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
238 EXPECT_STREQ(string_literal, "404");
241 TEST_F(TokenizeToGlobalHandler, Domain_Specified) {
242 const char* tokenizer_domain = nullptr;
243 const char* string_literal = nullptr;
245 PW_TOKENIZE_TO_GLOBAL_HANDLER_DOMAIN("www.google.com", "404");
247 EXPECT_STREQ(tokenizer_domain, "www.google.com");
248 EXPECT_STREQ(string_literal, "404");
251 TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Default) {
252 const char* tokenizer_domain = nullptr;
253 const char* string_literal = nullptr;
255 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
256 static_cast<pw_tokenizer_Payload>(123), "Wow%s", "???");
258 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
259 EXPECT_STREQ(string_literal, "Wow%s");
262 TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Specified) {
263 const char* tokenizer_domain = nullptr;
264 const char* string_literal = nullptr;
266 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_DOMAIN(
267 "THEDOMAIN", static_cast<pw_tokenizer_Payload>(123), "1234567890");
269 EXPECT_STREQ(tokenizer_domain, "THEDOMAIN");
270 EXPECT_STREQ(string_literal, "1234567890");
274 } // namespace pw::tokenizer