blob: 89885a8e6d48d14ed096cc43c073d49f589200f4 [file] [log] [blame]
Wyatt Hepler6639c452020-05-06 11:43:07 -07001// 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 <cinttypes>
16#include <cstdint>
17#include <cstring>
18
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"
23
24namespace pw::tokenizer {
25namespace {
26
27// The hash to use for this test. This makes sure the strings are shorter than
28// the configured max length to ensure this test works with any reasonable
29// configuration.
30template <size_t kSize>
31constexpr uint32_t TestHash(const char (&string)[kSize]) {
32 constexpr unsigned kTestHashLength = 48;
33 static_assert(kTestHashLength <= PW_TOKENIZER_CFG_HASH_LENGTH);
34 static_assert(kSize <= kTestHashLength + 1);
35 return PwTokenizer65599FixedLengthHash(std::string_view(string, kSize - 1),
36 kTestHashLength);
37}
38
39// Constructs an array with the hashed string followed by the provided bytes.
40template <uint8_t... kData, size_t kSize>
41constexpr auto ExpectedData(const char (&format)[kSize]) {
42 const uint32_t value = TestHash(format);
43 return std::array<uint8_t, sizeof(uint32_t) + sizeof...(kData)>{
44 static_cast<uint8_t>(value & 0xff),
45 static_cast<uint8_t>(value >> 8 & 0xff),
46 static_cast<uint8_t>(value >> 16 & 0xff),
47 static_cast<uint8_t>(value >> 24 & 0xff),
48 kData...};
49}
50
51// Test fixture for both global handler functions. Both need a global message
52// buffer. To keep the message buffers separate, template this on the derived
53// class type.
54template <typename Impl>
55class GlobalMessage : public ::testing::Test {
56 public:
57 static void SetMessage(const uint8_t* message, size_t size) {
58 ASSERT_LE(size, sizeof(message_));
59 std::memcpy(message_, message, size);
60 message_size_bytes_ = size;
61 }
62
63 protected:
64 GlobalMessage() {
65 std::memset(message_, 0, sizeof(message_));
66 message_size_bytes_ = 0;
67 }
68
69 static uint8_t message_[256];
70 static size_t message_size_bytes_;
71};
72
73template <typename Impl>
74uint8_t GlobalMessage<Impl>::message_[256] = {};
75template <typename Impl>
76size_t GlobalMessage<Impl>::message_size_bytes_ = 0;
77
78class TokenizeToGlobalHandler : public GlobalMessage<TokenizeToGlobalHandler> {
79};
80
81TEST_F(TokenizeToGlobalHandler, Variety) {
82 PW_TOKENIZE_TO_GLOBAL_HANDLER("%x%lld%1.2f%s", 0, 0ll, -0.0, "");
83 const auto expected =
84 ExpectedData<0, 0, 0x00, 0x00, 0x00, 0x80, 0>("%x%lld%1.2f%s");
85 ASSERT_EQ(expected.size(), message_size_bytes_);
86 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
87}
88
89TEST_F(TokenizeToGlobalHandler, Strings) {
90 PW_TOKENIZE_TO_GLOBAL_HANDLER("The answer is: %s", "5432!");
91 constexpr std::array<uint8_t, 10> expected =
92 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
93 ASSERT_EQ(expected.size(), message_size_bytes_);
94 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
95}
96
Wyatt Heplerd58eef92020-05-08 10:39:56 -070097TEST_F(TokenizeToGlobalHandler, Domain_Strings) {
98 PW_TOKENIZE_TO_GLOBAL_HANDLER_DOMAIN(
99 "TEST_DOMAIN", "The answer is: %s", "5432!");
100 constexpr std::array<uint8_t, 10> expected =
101 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
102 ASSERT_EQ(expected.size(), message_size_bytes_);
103 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
104}
105
Wyatt Hepler6639c452020-05-06 11:43:07 -0700106TEST_F(TokenizeToGlobalHandler, C_SequentialZigZag) {
107 pw_TokenizeToGlobalHandlerTest_SequentialZigZag();
108
109 constexpr std::array<uint8_t, 18> expected =
110 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
111 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
112 ASSERT_EQ(expected.size(), message_size_bytes_);
113 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
114}
115
116extern "C" void pw_TokenizerHandleEncodedMessage(const uint8_t* encoded_message,
117 size_t size_bytes) {
118 TokenizeToGlobalHandler::SetMessage(encoded_message, size_bytes);
119}
120
121class TokenizeToGlobalHandlerWithPayload
122 : public GlobalMessage<TokenizeToGlobalHandlerWithPayload> {
123 public:
124 static void SetPayload(pw_TokenizerPayload payload) {
125 payload_ = static_cast<intptr_t>(payload);
126 }
127
128 protected:
129 TokenizeToGlobalHandlerWithPayload() { payload_ = {}; }
130
131 static intptr_t payload_;
132};
133
134intptr_t TokenizeToGlobalHandlerWithPayload::payload_;
135
136TEST_F(TokenizeToGlobalHandlerWithPayload, Variety) {
137 ASSERT_NE(payload_, 123);
138
139 const auto expected =
140 ExpectedData<0, 0, 0x00, 0x00, 0x00, 0x80, 0>("%x%lld%1.2f%s");
141
142 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
143 static_cast<pw_TokenizerPayload>(123), "%x%lld%1.2f%s", 0, 0ll, -0.0, "");
144 ASSERT_EQ(expected.size(), message_size_bytes_);
145 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
146 EXPECT_EQ(payload_, 123);
147
148 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
149 static_cast<pw_TokenizerPayload>(-543),
150 "%x%lld%1.2f%s",
151 0,
152 0ll,
153 -0.0,
154 "");
155 ASSERT_EQ(expected.size(), message_size_bytes_);
156 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
157 EXPECT_EQ(payload_, -543);
158}
159
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700160constexpr std::array<uint8_t, 10> kExpected =
161 ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
Wyatt Hepler6639c452020-05-06 11:43:07 -0700162
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700163TEST_F(TokenizeToGlobalHandlerWithPayload, Strings_ZeroPayload) {
164 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD({}, "The answer is: %s", "5432!");
165
166 ASSERT_EQ(kExpected.size(), message_size_bytes_);
167 EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
168 EXPECT_EQ(payload_, 0);
169}
170
171TEST_F(TokenizeToGlobalHandlerWithPayload, Strings_NonZeroPayload) {
Wyatt Hepler6639c452020-05-06 11:43:07 -0700172 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
173 static_cast<pw_TokenizerPayload>(5432), "The answer is: %s", "5432!");
174
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700175 ASSERT_EQ(kExpected.size(), message_size_bytes_);
176 EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
Wyatt Hepler6639c452020-05-06 11:43:07 -0700177 EXPECT_EQ(payload_, 5432);
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700178}
Wyatt Hepler6639c452020-05-06 11:43:07 -0700179
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700180TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Strings) {
181 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_DOMAIN(
182 "TEST_DOMAIN",
183 static_cast<pw_TokenizerPayload>(5432),
184 "The answer is: %s",
185 "5432!");
186 ASSERT_EQ(kExpected.size(), message_size_bytes_);
187 EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
188 EXPECT_EQ(payload_, 5432);
Wyatt Hepler6639c452020-05-06 11:43:07 -0700189}
190
191struct Foo {
192 unsigned char a;
193 bool b;
194};
195
196TEST_F(TokenizeToGlobalHandlerWithPayload, PointerToStack) {
197 Foo foo{254u, true};
198
199 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
200 reinterpret_cast<pw_TokenizerPayload>(&foo), "Boring!");
201
202 constexpr auto expected = ExpectedData("Boring!");
203 static_assert(expected.size() == 4);
204 ASSERT_EQ(expected.size(), message_size_bytes_);
205 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
206
207 Foo* payload_foo = reinterpret_cast<Foo*>(payload_);
208 ASSERT_EQ(&foo, payload_foo);
209 EXPECT_EQ(payload_foo->a, 254u);
210 EXPECT_TRUE(payload_foo->b);
211}
212
213TEST_F(TokenizeToGlobalHandlerWithPayload, C_SequentialZigZag) {
214 pw_TokenizeToGlobalHandlerWithPayloadTest_SequentialZigZag();
215
216 constexpr std::array<uint8_t, 18> expected =
217 ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
218 TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
219 ASSERT_EQ(expected.size(), message_size_bytes_);
220 EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
221 EXPECT_EQ(payload_, 600613);
222}
223
224extern "C" void pw_TokenizerHandleEncodedMessageWithPayload(
225 pw_TokenizerPayload payload,
226 const uint8_t* encoded_message,
227 size_t size_bytes) {
228 TokenizeToGlobalHandlerWithPayload::SetMessage(encoded_message, size_bytes);
229 TokenizeToGlobalHandlerWithPayload::SetPayload(payload);
230}
231
Wyatt Heplerd58eef92020-05-08 10:39:56 -0700232// Hijack the PW_TOKENIZE_STRING_DOMAIN macro to capture the tokenizer domain.
233#undef PW_TOKENIZE_STRING_DOMAIN
234#define PW_TOKENIZE_STRING_DOMAIN(domain, string) \
235 /* assigned to a variable */ PW_TOKENIZER_STRING_TOKEN(string); \
236 tokenizer_domain = domain; \
237 string_literal = string
238
239TEST_F(TokenizeToGlobalHandler, Domain_Default) {
240 const char* tokenizer_domain = nullptr;
241 const char* string_literal = nullptr;
242
243 PW_TOKENIZE_TO_GLOBAL_HANDLER("404");
244
245 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
246 EXPECT_STREQ(string_literal, "404");
247}
248
249TEST_F(TokenizeToGlobalHandler, Domain_Specified) {
250 const char* tokenizer_domain = nullptr;
251 const char* string_literal = nullptr;
252
253 PW_TOKENIZE_TO_GLOBAL_HANDLER_DOMAIN("www.google.com", "404");
254
255 EXPECT_STREQ(tokenizer_domain, "www.google.com");
256 EXPECT_STREQ(string_literal, "404");
257}
258
259TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Default) {
260 const char* tokenizer_domain = nullptr;
261 const char* string_literal = nullptr;
262
263 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
264 static_cast<pw_TokenizerPayload>(123), "Wow%s", "???");
265
266 EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
267 EXPECT_STREQ(string_literal, "Wow%s");
268}
269
270TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Specified) {
271 const char* tokenizer_domain = nullptr;
272 const char* string_literal = nullptr;
273
274 PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_DOMAIN(
275 "THEDOMAIN", static_cast<pw_TokenizerPayload>(123), "1234567890");
276
277 EXPECT_STREQ(tokenizer_domain, "THEDOMAIN");
278 EXPECT_STREQ(string_literal, "1234567890");
279}
280
Wyatt Hepler6639c452020-05-06 11:43:07 -0700281} // namespace
282} // namespace pw::tokenizer