diff options
Diffstat (limited to 'pw_containers/variable_length_entry_queue_test.cc')
-rw-r--r-- | pw_containers/variable_length_entry_queue_test.cc | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/pw_containers/variable_length_entry_queue_test.cc b/pw_containers/variable_length_entry_queue_test.cc new file mode 100644 index 000000000..2e4fb90d7 --- /dev/null +++ b/pw_containers/variable_length_entry_queue_test.cc @@ -0,0 +1,271 @@ +// Copyright 2023 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 "pw_containers/variable_length_entry_queue.h" + +#include <cstring> +#include <string_view> +#include <variant> + +#include "gtest/gtest.h" +#include "pw_containers_private/variable_length_entry_queue_test_oracle.h" + +namespace { + +struct PushOverwrite { + std::string_view data; +}; +struct Push { + std::string_view data; +}; +struct Pop {}; +struct Clear {}; +struct SizeEquals { + size_t expected; +}; + +using TestStep = std::variant<PushOverwrite, Push, Pop, Clear, SizeEquals>; + +// Copies an entry, which might be wrapped, to a single std::vector. +std::vector<std::byte> ReadEntry( + const pw_VariableLengthEntryQueue_Iterator& it) { + auto entry = pw_VariableLengthEntryQueue_GetEntry(&it); + std::vector<std::byte> value(entry.size_1 + entry.size_2); + EXPECT_EQ(value.size(), + pw_VariableLengthEntryQueue_Entry_Copy( + &entry, value.data(), entry.size_1 + entry.size_2)); + return value; +} + +#define ASSERT_CONTENTS_EQ(oracle, queue) \ + auto oracle_it = oracle.begin(); \ + auto queue_it = pw_VariableLengthEntryQueue_Begin(queue); \ + const auto queue_end = pw_VariableLengthEntryQueue_End(queue); \ + uint32_t entries_compared = 0; \ + while (oracle_it != oracle.end() && \ + !pw_VariableLengthEntryQueue_Iterator_Equal(&queue_it, &queue_end)) { \ + ASSERT_EQ(*oracle_it++, ReadEntry(queue_it)); \ + pw_VariableLengthEntryQueue_Iterator_Advance(&queue_it); \ + entries_compared += 1; \ + } \ + ASSERT_EQ(entries_compared, oracle.size()) + +// Declares a test that performs a series of operations on a +// VariableLengthEntryQueue and the "oracle" class, and checks that they match +// after every step. +#define DATA_DRIVEN_TEST(program, max_entry_size) \ + TEST(VariableLengthEntryQueue, \ + DataDrivenTest_##program##_MaxSizeBytes##max_entry_size) { \ + pw::containers::VariableLengthEntryQueueTestOracle oracle(max_entry_size); \ + PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(c_queue, max_entry_size); \ + \ + for (const TestStep& step : program) { \ + /* Take the action */ \ + if (auto ow = std::get_if<PushOverwrite>(&step); ow != nullptr) { \ + pw_VariableLengthEntryQueue_PushOverwrite( \ + c_queue, ow->data.data(), static_cast<uint32_t>(ow->data.size())); \ + oracle.push_overwrite(pw::as_bytes(pw::span(ow->data))); \ + } else if (auto push = std::get_if<Push>(&step); push != nullptr) { \ + pw_VariableLengthEntryQueue_Push( \ + c_queue, \ + push->data.data(), \ + static_cast<uint32_t>(push->data.size())); \ + oracle.push(pw::as_bytes(pw::span(push->data))); \ + } else if (std::holds_alternative<Pop>(step)) { \ + pw_VariableLengthEntryQueue_Pop(c_queue); \ + oracle.pop(); \ + } else if (auto size = std::get_if<SizeEquals>(&step); \ + size != nullptr) { \ + size_t actual = pw_VariableLengthEntryQueue_Size(c_queue); \ + ASSERT_EQ(oracle.size(), actual); \ + ASSERT_EQ(size->expected, actual); \ + } else if (std::holds_alternative<Clear>(step)) { \ + pw_VariableLengthEntryQueue_Clear(c_queue); \ + oracle.clear(); \ + } else { \ + FAIL() << "Unhandled case"; \ + } \ + /* Check size and other functions */ \ + ASSERT_EQ(pw_VariableLengthEntryQueue_Size(c_queue), oracle.size()); \ + ASSERT_EQ(pw_VariableLengthEntryQueue_SizeBytes(c_queue), \ + oracle.size_bytes()); \ + ASSERT_EQ(pw_VariableLengthEntryQueue_MaxSizeBytes(c_queue), \ + oracle.max_size_bytes()); \ + ASSERT_CONTENTS_EQ(oracle, c_queue); \ + } \ + } \ + static_assert(true, "use a semicolon") + +constexpr TestStep kPop[] = { + SizeEquals{0}, + PushOverwrite{""}, + SizeEquals{1}, + Pop{}, + SizeEquals{0}, +}; + +DATA_DRIVEN_TEST(kPop, 0); // Only holds one empty entry. +DATA_DRIVEN_TEST(kPop, 1); +DATA_DRIVEN_TEST(kPop, 6); + +constexpr TestStep kOverwriteLargeEntriesWithSmall[] = { + PushOverwrite{"12345"}, + PushOverwrite{"abcde"}, + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{""}, + SizeEquals{6}, + Pop{}, + Pop{}, + Pop{}, + Pop{}, + Pop{}, + Pop{}, + SizeEquals{0}, +}; +DATA_DRIVEN_TEST(kOverwriteLargeEntriesWithSmall, 5); +DATA_DRIVEN_TEST(kOverwriteLargeEntriesWithSmall, 6); +DATA_DRIVEN_TEST(kOverwriteLargeEntriesWithSmall, 7); + +constexpr TestStep kOverwriteVaryingSizes012[] = { + PushOverwrite{""}, PushOverwrite{""}, PushOverwrite{""}, + PushOverwrite{""}, PushOverwrite{""}, PushOverwrite{"1"}, + PushOverwrite{"2"}, PushOverwrite{""}, PushOverwrite{"3"}, + PushOverwrite{"4"}, PushOverwrite{""}, PushOverwrite{"5"}, + PushOverwrite{"6"}, PushOverwrite{"ab"}, PushOverwrite{"cd"}, + PushOverwrite{""}, PushOverwrite{"ef"}, PushOverwrite{"gh"}, + PushOverwrite{"ij"}, +}; +DATA_DRIVEN_TEST(kOverwriteVaryingSizes012, 2); +DATA_DRIVEN_TEST(kOverwriteVaryingSizes012, 3); + +constexpr TestStep kOverwriteVaryingSizesUpTo4[] = { + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{"1"}, + PushOverwrite{"2"}, + PushOverwrite{"3"}, + PushOverwrite{"ab"}, + PushOverwrite{"cd"}, + PushOverwrite{"ef"}, + PushOverwrite{"123"}, + PushOverwrite{"456"}, + PushOverwrite{"789"}, + PushOverwrite{"abcd"}, + PushOverwrite{"efgh"}, + PushOverwrite{"ijkl"}, + Pop{}, + SizeEquals{0}, +}; +DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo4, 4); +DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo4, 5); +DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo4, 6); + +constexpr char kBigEntryBytes[196]{}; + +constexpr TestStep kTwoBytePrefix[] = { + PushOverwrite{std::string_view(kBigEntryBytes, 128)}, + PushOverwrite{std::string_view(kBigEntryBytes, 128)}, + PushOverwrite{std::string_view(kBigEntryBytes, 127)}, + PushOverwrite{std::string_view(kBigEntryBytes, 128)}, + PushOverwrite{std::string_view(kBigEntryBytes, 127)}, + SizeEquals{1}, + Pop{}, + SizeEquals{0}, +}; +DATA_DRIVEN_TEST(kTwoBytePrefix, 128); +DATA_DRIVEN_TEST(kTwoBytePrefix, 129); + +constexpr TestStep kClear[] = { + Push{"abcdefg"}, + PushOverwrite{""}, + PushOverwrite{""}, + PushOverwrite{"a"}, + PushOverwrite{"b"}, + Clear{}, + SizeEquals{0}, + Clear{}, +}; +DATA_DRIVEN_TEST(kClear, 7); +DATA_DRIVEN_TEST(kClear, 100); + +TEST(VariableLengthEntryQueue, DeclareMacro) { + PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(queue, 123); + + constexpr size_t kArraySizeBytes = + 123 + 1 /*prefix*/ + 1 /* end */ + 3 /* round up */ + + PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 * 4; + static_assert(sizeof(queue) == kArraySizeBytes); + EXPECT_EQ(pw_VariableLengthEntryQueue_RawStorageSizeBytes(queue), + kArraySizeBytes - 3 /* padding isn't included */); + + EXPECT_EQ(pw_VariableLengthEntryQueue_MaxSizeBytes(queue), 123u); + EXPECT_EQ(pw_VariableLengthEntryQueue_SizeBytes(queue), 0u); + EXPECT_TRUE(pw_VariableLengthEntryQueue_Empty(queue)); +} + +TEST(VariableLengthEntryQueue, InitializeExistingBuffer) { + constexpr size_t kArraySize = + 10 + PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32; + uint32_t queue[kArraySize]; + pw_VariableLengthEntryQueue_Init(queue, kArraySize); + + EXPECT_EQ(pw_VariableLengthEntryQueue_RawStorageSizeBytes(queue), + sizeof(queue)); + EXPECT_EQ(pw_VariableLengthEntryQueue_MaxSizeBytes(queue), + sizeof(uint32_t) * 10u - 1 /*prefix*/ - 1 /*end*/); + EXPECT_EQ(pw_VariableLengthEntryQueue_SizeBytes(queue), 0u); + EXPECT_EQ(pw_VariableLengthEntryQueue_Size(queue), 0u); + EXPECT_TRUE(pw_VariableLengthEntryQueue_Empty(queue)); +} + +TEST(VariableLengthEntryQueue, MaxSizeElement) { + // Test max size elements for a few sizes. Commented out statements fail an + // assert because the elements are too large. + PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(q16, 126); + PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(q17, 127); + PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(q18, 128); + PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(q19, 129); + + pw_VariableLengthEntryQueue_PushOverwrite(q16, kBigEntryBytes, 126); + pw_VariableLengthEntryQueue_PushOverwrite(q17, kBigEntryBytes, 126); + pw_VariableLengthEntryQueue_PushOverwrite(q18, kBigEntryBytes, 126); + pw_VariableLengthEntryQueue_PushOverwrite(q19, kBigEntryBytes, 126); + + // pw_VariableLengthEntryQueue_PushOverwrite(q16, kBigEntryBytes, 127); + pw_VariableLengthEntryQueue_PushOverwrite(q17, kBigEntryBytes, 127); + pw_VariableLengthEntryQueue_PushOverwrite(q18, kBigEntryBytes, 127); + pw_VariableLengthEntryQueue_PushOverwrite(q19, kBigEntryBytes, 127); + + // pw_VariableLengthEntryQueue_PushOverwrite(q16, kBigEntryBytes, 128); + // pw_VariableLengthEntryQueue_PushOverwrite(q17, kBigEntryBytes, 128); + pw_VariableLengthEntryQueue_PushOverwrite(q18, kBigEntryBytes, 128); + pw_VariableLengthEntryQueue_PushOverwrite(q19, kBigEntryBytes, 128); + + // pw_VariableLengthEntryQueue_PushOverwrite(q16, kBigEntryBytes, 129); + // pw_VariableLengthEntryQueue_PushOverwrite(q17, kBigEntryBytes, 129); + // pw_VariableLengthEntryQueue_PushOverwrite(q18, kBigEntryBytes, 129); + pw_VariableLengthEntryQueue_PushOverwrite(q19, kBigEntryBytes, 129); + + EXPECT_EQ(pw_VariableLengthEntryQueue_Size(q16), 1u); + EXPECT_EQ(pw_VariableLengthEntryQueue_Size(q17), 1u); + EXPECT_EQ(pw_VariableLengthEntryQueue_Size(q18), 1u); + EXPECT_EQ(pw_VariableLengthEntryQueue_Size(q19), 1u); +} + +} // namespace |