You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
10 KiB
300 lines
10 KiB
// Copyright 2020 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
#include <cinttypes>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "pw_tokenizer/tokenize_to_global_handler.h"
|
|
#include "pw_tokenizer/tokenize_to_global_handler_with_payload.h"
|
|
#include "pw_tokenizer_private/tokenize_test.h"
|
|
|
|
namespace pw::tokenizer {
|
|
namespace {
|
|
|
|
// Constructs an array with the hashed string followed by the provided bytes.
|
|
template <uint8_t... data, size_t kSize>
|
|
constexpr auto ExpectedData(
|
|
const char (&format)[kSize],
|
|
uint32_t token_mask = std::numeric_limits<uint32_t>::max()) {
|
|
const uint32_t value = Hash(format) & token_mask;
|
|
return std::array<uint8_t, sizeof(uint32_t) + sizeof...(data)>{
|
|
static_cast<uint8_t>(value & 0xff),
|
|
static_cast<uint8_t>(value >> 8 & 0xff),
|
|
static_cast<uint8_t>(value >> 16 & 0xff),
|
|
static_cast<uint8_t>(value >> 24 & 0xff),
|
|
data...};
|
|
}
|
|
|
|
// Test fixture for both global handler functions. Both need a global message
|
|
// buffer. To keep the message buffers separate, template this on the derived
|
|
// class type.
|
|
template <typename Impl>
|
|
class GlobalMessage : public ::testing::Test {
|
|
public:
|
|
static void SetMessage(const uint8_t* message, size_t size) {
|
|
ASSERT_LE(size, sizeof(message_));
|
|
std::memcpy(message_, message, size);
|
|
message_size_bytes_ = size;
|
|
}
|
|
|
|
protected:
|
|
GlobalMessage() {
|
|
std::memset(message_, 0, sizeof(message_));
|
|
message_size_bytes_ = 0;
|
|
}
|
|
|
|
static uint8_t message_[256];
|
|
static size_t message_size_bytes_;
|
|
};
|
|
|
|
template <typename Impl>
|
|
uint8_t GlobalMessage<Impl>::message_[256] = {};
|
|
template <typename Impl>
|
|
size_t GlobalMessage<Impl>::message_size_bytes_ = 0;
|
|
|
|
class TokenizeToGlobalHandler : public GlobalMessage<TokenizeToGlobalHandler> {
|
|
};
|
|
|
|
TEST_F(TokenizeToGlobalHandler, Variety) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER("%x%lld%1.2f%s", 0, 0ll, -0.0, "");
|
|
const auto expected =
|
|
ExpectedData<0, 0, 0x00, 0x00, 0x00, 0x80, 0>("%x%lld%1.2f%s");
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandler, Strings) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER("The answer is: %s", "5432!");
|
|
constexpr std::array<uint8_t, 10> expected =
|
|
ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandler, Domain_Strings) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_DOMAIN(
|
|
"TEST_DOMAIN", "The answer is: %s", "5432!");
|
|
constexpr std::array<uint8_t, 10> expected =
|
|
ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandler, Mask) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_MASK(
|
|
"TEST_DOMAIN", 0x00FFF000, "The answer is: %s", "5432!");
|
|
constexpr std::array<uint8_t, 10> expected =
|
|
ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s", 0x00FFF000);
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandler, C_SequentialZigZag) {
|
|
pw_tokenizer_ToGlobalHandlerTest_SequentialZigZag();
|
|
|
|
constexpr std::array<uint8_t, 18> expected =
|
|
ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
|
|
TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
}
|
|
|
|
extern "C" void pw_tokenizer_HandleEncodedMessage(
|
|
const uint8_t* encoded_message, size_t size_bytes) {
|
|
TokenizeToGlobalHandler::SetMessage(encoded_message, size_bytes);
|
|
}
|
|
|
|
class TokenizeToGlobalHandlerWithPayload
|
|
: public GlobalMessage<TokenizeToGlobalHandlerWithPayload> {
|
|
public:
|
|
static void SetPayload(pw_tokenizer_Payload payload) {
|
|
payload_ = static_cast<intptr_t>(payload);
|
|
}
|
|
|
|
protected:
|
|
TokenizeToGlobalHandlerWithPayload() { payload_ = {}; }
|
|
|
|
static intptr_t payload_;
|
|
};
|
|
|
|
intptr_t TokenizeToGlobalHandlerWithPayload::payload_;
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Variety) {
|
|
ASSERT_NE(payload_, 123);
|
|
|
|
const auto expected =
|
|
ExpectedData<0, 0, 0x00, 0x00, 0x00, 0x80, 0>("%x%lld%1.2f%s");
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
|
|
static_cast<pw_tokenizer_Payload>(123),
|
|
"%x%lld%1.2f%s",
|
|
0,
|
|
0ll,
|
|
-0.0,
|
|
"");
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
EXPECT_EQ(payload_, 123);
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
|
|
static_cast<pw_tokenizer_Payload>(-543),
|
|
"%x%lld%1.2f%s",
|
|
0,
|
|
0ll,
|
|
-0.0,
|
|
"");
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
EXPECT_EQ(payload_, -543);
|
|
}
|
|
|
|
constexpr std::array<uint8_t, 10> kExpected =
|
|
ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s");
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Strings_ZeroPayload) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD({}, "The answer is: %s", "5432!");
|
|
|
|
ASSERT_EQ(kExpected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
|
|
EXPECT_EQ(payload_, 0);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Strings_NonZeroPayload) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
|
|
static_cast<pw_tokenizer_Payload>(5432), "The answer is: %s", "5432!");
|
|
|
|
ASSERT_EQ(kExpected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
|
|
EXPECT_EQ(payload_, 5432);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Strings) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_DOMAIN(
|
|
"TEST_DOMAIN",
|
|
static_cast<pw_tokenizer_Payload>(5432),
|
|
"The answer is: %s",
|
|
"5432!");
|
|
ASSERT_EQ(kExpected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(kExpected.data(), message_, kExpected.size()), 0);
|
|
EXPECT_EQ(payload_, 5432);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Mask) {
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_MASK(
|
|
"TEST_DOMAIN",
|
|
0x12345678,
|
|
static_cast<pw_tokenizer_Payload>(5432),
|
|
"The answer is: %s",
|
|
"5432!");
|
|
constexpr std::array<uint8_t, 10> expected =
|
|
ExpectedData<5, '5', '4', '3', '2', '!'>("The answer is: %s", 0x12345678);
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
EXPECT_EQ(payload_, 5432);
|
|
}
|
|
|
|
struct Foo {
|
|
unsigned char a;
|
|
bool b;
|
|
};
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, PointerToStack) {
|
|
Foo foo{254u, true};
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
|
|
reinterpret_cast<pw_tokenizer_Payload>(&foo), "Boring!");
|
|
|
|
constexpr auto expected = ExpectedData("Boring!");
|
|
static_assert(expected.size() == 4);
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
|
|
Foo* payload_foo = reinterpret_cast<Foo*>(payload_);
|
|
ASSERT_EQ(&foo, payload_foo);
|
|
EXPECT_EQ(payload_foo->a, 254u);
|
|
EXPECT_TRUE(payload_foo->b);
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, C_SequentialZigZag) {
|
|
pw_tokenizer_ToGlobalHandlerWithPayloadTest_SequentialZigZag();
|
|
|
|
constexpr std::array<uint8_t, 18> expected =
|
|
ExpectedData<0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13>(
|
|
TEST_FORMAT_SEQUENTIAL_ZIG_ZAG);
|
|
ASSERT_EQ(expected.size(), message_size_bytes_);
|
|
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
|
|
EXPECT_EQ(payload_, 600613);
|
|
}
|
|
|
|
extern "C" void pw_tokenizer_HandleEncodedMessageWithPayload(
|
|
pw_tokenizer_Payload payload,
|
|
const uint8_t* encoded_message,
|
|
size_t size_bytes) {
|
|
TokenizeToGlobalHandlerWithPayload::SetMessage(encoded_message, size_bytes);
|
|
TokenizeToGlobalHandlerWithPayload::SetPayload(payload);
|
|
}
|
|
|
|
// Hijack an internal macro to capture the tokenizer domain.
|
|
#undef _PW_TOKENIZER_RECORD_ORIGINAL_STRING
|
|
#define _PW_TOKENIZER_RECORD_ORIGINAL_STRING(token, domain, string) \
|
|
tokenizer_domain = domain; \
|
|
string_literal = string
|
|
|
|
TEST_F(TokenizeToGlobalHandler, Domain_Default) {
|
|
const char* tokenizer_domain = nullptr;
|
|
const char* string_literal = nullptr;
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER("404");
|
|
|
|
EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
|
|
EXPECT_STREQ(string_literal, "404");
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandler, Domain_Specified) {
|
|
const char* tokenizer_domain = nullptr;
|
|
const char* string_literal = nullptr;
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_DOMAIN("www.google.com", "404");
|
|
|
|
EXPECT_STREQ(tokenizer_domain, "www.google.com");
|
|
EXPECT_STREQ(string_literal, "404");
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Default) {
|
|
const char* tokenizer_domain = nullptr;
|
|
const char* string_literal = nullptr;
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD(
|
|
static_cast<pw_tokenizer_Payload>(123), "Wow%s", "???");
|
|
|
|
EXPECT_STREQ(tokenizer_domain, PW_TOKENIZER_DEFAULT_DOMAIN);
|
|
EXPECT_STREQ(string_literal, "Wow%s");
|
|
}
|
|
|
|
TEST_F(TokenizeToGlobalHandlerWithPayload, Domain_Specified) {
|
|
const char* tokenizer_domain = nullptr;
|
|
const char* string_literal = nullptr;
|
|
|
|
PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_DOMAIN(
|
|
"THEDOMAIN", static_cast<pw_tokenizer_Payload>(123), "1234567890");
|
|
|
|
EXPECT_STREQ(tokenizer_domain, "THEDOMAIN");
|
|
EXPECT_STREQ(string_literal, "1234567890");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace pw::tokenizer
|