/*
* Copyright (C) 2016, The Android Open Source Project
*
* 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
*
* http://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 <array>
#include <tuple>
#include <vector>
#include "gtest/gtest.h"
#include "wifilogd/message_buffer.h"
namespace android {
namespace wifilogd {
namespace {
constexpr size_t kBufferSizeBytes = 1024;
constexpr size_t kHeaderSizeBytes = MessageBuffer::GetHeaderSize();
constexpr std::array<uint8_t, 1> kSmallestMessage{};
constexpr std::array<uint8_t, kBufferSizeBytes - kHeaderSizeBytes>
kLargestMessage{};
class MessageBufferTest : public ::testing::Test {
public:
MessageBufferTest() : buffer_{kBufferSizeBytes} {}
protected:
size_t FillBufferWithMultipleMessages() {
constexpr std::array<uint8_t, kHeaderSizeBytes> message{};
static_assert(kBufferSizeBytes % (kHeaderSizeBytes + message.size()) == 0,
"messages will not fill buffer to capacity");
size_t n_written;
for (n_written = 0;
n_written < kBufferSizeBytes / (kHeaderSizeBytes + message.size());
++n_written) {
EXPECT_TRUE(buffer_.Append(message.data(), message.size()));
}
EXPECT_EQ(0U, buffer_.GetFreeSize());
return n_written;
}
std::vector<uint8_t> GetNextMessageAsByteVector() {
const uint8_t* start;
size_t len;
std::tie(start, len) = buffer_.ConsumeNextMessage();
return {start, start + len};
}
MessageBuffer buffer_;
};
} // namespace
TEST_F(MessageBufferTest, AppendMinimalOnEmptyBufferSucceeds) {
EXPECT_TRUE(buffer_.Append(kSmallestMessage.data(), kSmallestMessage.size()));
}
TEST_F(MessageBufferTest, AppendMaximalOnEmptyBufferSucceeds) {
EXPECT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
}
TEST_F(MessageBufferTest, AppendMaximalAfterFillAndClearSucceeds) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
ASSERT_FALSE(buffer_.CanFitNow(1));
buffer_.Clear();
EXPECT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
}
TEST_F(MessageBufferTest, AppendUnalignedMessagesDoesNotCrash) {
// Odd-length messages should trigger alignment problems, if any such
// problems exist. We'll need more than one, though, since the first header
// might be aligned by default.
constexpr std::array<uint8_t, 1> message{};
while (buffer_.CanFitNow(message.size())) {
ASSERT_TRUE(buffer_.Append(message.data(), message.size()));
}
}
TEST_F(MessageBufferTest, AppendLargerThanBufferFails) {
constexpr std::array<uint8_t, kBufferSizeBytes + 1> oversized_message{};
EXPECT_FALSE(
buffer_.Append(oversized_message.data(), oversized_message.size()));
}
TEST_F(MessageBufferTest, AppendLargerThanFreeSpaceFails) {
constexpr size_t expected_free = kBufferSizeBytes - kHeaderSizeBytes;
ASSERT_FALSE(buffer_.CanFitNow(expected_free + 1));
constexpr std::array<uint8_t, expected_free + 1> oversized_message{};
EXPECT_FALSE(
buffer_.Append(oversized_message.data(), oversized_message.size()));
}
TEST_F(MessageBufferTest, AppendMultipleMessagesToFillBufferDoesNotCrash) {
FillBufferWithMultipleMessages();
}
TEST_F(MessageBufferTest, CanFitNowIsCorrectOnFreshBuffer) {
EXPECT_TRUE(buffer_.CanFitNow(kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitNow(kLargestMessage.size() + 1));
}
TEST_F(MessageBufferTest, CanFitNowIsCorrectAfterSmallWrite) {
ASSERT_TRUE(buffer_.Append(kSmallestMessage.data(), kSmallestMessage.size()));
constexpr size_t expected_free =
kBufferSizeBytes - (kSmallestMessage.size() + kHeaderSizeBytes) -
kHeaderSizeBytes;
EXPECT_TRUE(buffer_.CanFitNow(expected_free));
EXPECT_FALSE(buffer_.CanFitNow(expected_free + 1));
}
TEST_F(MessageBufferTest, CanFitNowIsCorrectOnFullBuffer) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitNow(1));
}
TEST_F(MessageBufferTest, CanFitNowIsCorrectOnRewoundBuffer) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.Rewind();
EXPECT_FALSE(buffer_.CanFitNow(1));
}
TEST_F(MessageBufferTest, CanFitNowIsCorrectAfterClear) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
ASSERT_FALSE(buffer_.CanFitNow(1));
buffer_.Clear();
EXPECT_TRUE(buffer_.CanFitNow(kLargestMessage.size()));
}
TEST_F(MessageBufferTest, CanFitEverIsCorrectOnFreshBuffer) {
EXPECT_TRUE(buffer_.CanFitEver(kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitEver(kLargestMessage.size() + 1));
}
TEST_F(MessageBufferTest, CanFitEverIsCorrectAfterSmallWrite) {
ASSERT_TRUE(buffer_.Append(kSmallestMessage.data(), kSmallestMessage.size()));
EXPECT_TRUE(buffer_.CanFitEver(kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitEver(kLargestMessage.size() + 1));
}
TEST_F(MessageBufferTest, CanFitEverIsCorrectOnFullBuffer) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
EXPECT_TRUE(buffer_.CanFitEver(kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitEver(kLargestMessage.size() + 1));
}
TEST_F(MessageBufferTest, CanFitEverIsCorrectAfterRewind) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.Rewind();
EXPECT_TRUE(buffer_.CanFitEver(kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitEver(kLargestMessage.size() + 1));
}
TEST_F(MessageBufferTest, CanFitEverIsCorrectAfterClear) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.Clear();
EXPECT_TRUE(buffer_.CanFitEver(kLargestMessage.size()));
EXPECT_FALSE(buffer_.CanFitEver(kLargestMessage.size() + 1));
}
TEST_F(MessageBufferTest, ConsumeNextMessageReturnsNullOnFreshBuffer) {
const std::tuple<const uint8_t*, size_t> expected{nullptr, 0};
EXPECT_EQ(expected, buffer_.ConsumeNextMessage());
}
TEST_F(MessageBufferTest, ConsumeNextMessageReturnsNullAfterFillAndClear) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.Clear();
const std::tuple<const uint8_t*, size_t> expected{nullptr, 0};
EXPECT_EQ(expected, buffer_.ConsumeNextMessage());
}
TEST_F(MessageBufferTest, ConsumeNextMessageCanReadMinimalMessage) {
ASSERT_TRUE(buffer_.Append(kSmallestMessage.data(), kSmallestMessage.size()));
const auto& ptr_and_size = buffer_.ConsumeNextMessage();
EXPECT_NE(nullptr, std::get<0>(ptr_and_size));
EXPECT_EQ(kSmallestMessage.size(), std::get<1>(ptr_and_size));
}
TEST_F(MessageBufferTest, ConsumeNextMessageCanReadMaximalMessage) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
const auto& ptr_and_size = buffer_.ConsumeNextMessage();
EXPECT_NE(nullptr, std::get<0>(ptr_and_size));
EXPECT_EQ(kLargestMessage.size(), std::get<1>(ptr_and_size));
}
TEST_F(MessageBufferTest,
ConsumeNextMessageReturnsNullAfterMinimalMessageIsConsumed) {
ASSERT_TRUE(buffer_.Append(kSmallestMessage.data(), kSmallestMessage.size()));
buffer_.ConsumeNextMessage();
constexpr std::tuple<const uint8_t*, size_t> expected{nullptr, 0};
EXPECT_EQ(expected, buffer_.ConsumeNextMessage());
}
TEST_F(MessageBufferTest,
ConsumeNextMessageReturnsNullAfterMaximalMessageIsConsumed) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.ConsumeNextMessage();
constexpr std::tuple<const uint8_t*, size_t> expected{nullptr, 0};
EXPECT_EQ(expected, buffer_.ConsumeNextMessage());
}
TEST_F(MessageBufferTest,
ConsumeNextMessageCanRetreiveAllMessagesFromFullBuffer) {
const size_t n_written = FillBufferWithMultipleMessages();
size_t n_read = 0;
while (std::get<0>(buffer_.ConsumeNextMessage())) {
++n_read;
}
EXPECT_EQ(n_written, n_read);
constexpr std::tuple<const uint8_t*, size_t> expected{nullptr, 0};
EXPECT_EQ(expected, buffer_.ConsumeNextMessage());
}
TEST_F(MessageBufferTest,
ConsumeNextMessageCanRetreiveAllMessagesFromRewoundBuffer) {
const size_t n_written = FillBufferWithMultipleMessages();
while (std::get<0>(buffer_.ConsumeNextMessage())) {
// Silently consume message
}
buffer_.Rewind();
size_t n_read = 0;
while (std::get<0>(buffer_.ConsumeNextMessage())) {
++n_read;
}
EXPECT_EQ(n_written, n_read);
}
TEST_F(MessageBufferTest,
ConsumeNextMessageCanRetreiveMultipleUnaliagnedMessages) {
// As in AppendUnalignedMessagesDoesNotCrash, odd-length messages should
// trigger alignment problems, if any such problems exist.
const std::array<uint8_t, 1> message{};
size_t n_written = 0;
while (buffer_.CanFitNow(message.size())) {
ASSERT_TRUE(buffer_.Append(message.data(), message.size()));
++n_written;
}
size_t n_read = 0;
while (std::get<0>(buffer_.ConsumeNextMessage())) {
++n_read;
}
EXPECT_EQ(n_written, n_read);
}
TEST_F(MessageBufferTest, ConsumeNextMessageReturnsOurMessages) {
const std::vector<uint8_t> message1{{'h', 'e', 'l', 'l', 'o'}};
const std::vector<uint8_t> message2{{'w', 'o', 'r', 'l', 'd'}};
ASSERT_TRUE(
buffer_.Append(message1.data(), static_cast<uint16_t>(message1.size())));
ASSERT_TRUE(
buffer_.Append(message2.data(), static_cast<uint16_t>(message2.size())));
EXPECT_EQ(message1, GetNextMessageAsByteVector());
EXPECT_EQ(message2, GetNextMessageAsByteVector());
}
TEST_F(MessageBufferTest, GetFreeSizeIsCorrectOnFreshBuffer) {
EXPECT_EQ(kBufferSizeBytes, buffer_.GetFreeSize());
}
TEST_F(MessageBufferTest, GetFreeSizeIsCorrectAfterSmallWrite) {
ASSERT_TRUE(buffer_.Append(kSmallestMessage.data(), kSmallestMessage.size()));
EXPECT_EQ(kBufferSizeBytes - kHeaderSizeBytes - kSmallestMessage.size(),
buffer_.GetFreeSize());
}
TEST_F(MessageBufferTest, GetFreeSizeIsCorrectOnFullBuffer) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
EXPECT_EQ(0U, buffer_.GetFreeSize());
}
TEST_F(MessageBufferTest, GetFreeSizeIsCorrectAfterRewindOfFullBuffer) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.Rewind();
EXPECT_EQ(0U, buffer_.GetFreeSize());
}
TEST_F(MessageBufferTest, GetFreeSizeIsCorrectAfterClear) {
ASSERT_TRUE(buffer_.Append(kLargestMessage.data(), kLargestMessage.size()));
buffer_.Clear();
EXPECT_EQ(kBufferSizeBytes, buffer_.GetFreeSize());
}
TEST_F(MessageBufferTest, RewindDoesNotAffectWritePointer) {
const std::vector<uint8_t> message1{{'h', 'e', 'l', 'l', 'o'}};
ASSERT_TRUE(
buffer_.Append(message1.data(), static_cast<uint16_t>(message1.size())));
buffer_.Rewind();
const std::vector<uint8_t> message2{{'w', 'o', 'r', 'l', 'd'}};
ASSERT_TRUE(
buffer_.Append(message2.data(), static_cast<uint16_t>(message2.size())));
EXPECT_EQ(message1, GetNextMessageAsByteVector());
EXPECT_EQ(message2, GetNextMessageAsByteVector());
}
// Per
// github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests,
// death tests should be specially named.
using MessageBufferDeathTest = MessageBufferTest;
TEST_F(MessageBufferDeathTest, AppendZeroBytesCausesDeath) {
constexpr std::array<uint8_t, 1> message{};
EXPECT_DEATH(buffer_.Append(message.data(), 0), "Check failed");
}
TEST_F(MessageBufferDeathTest, ConstructionOfUselesslySmallBufferCausesDeath) {
EXPECT_DEATH(MessageBuffer{kHeaderSizeBytes}, "Check failed");
}
} // namespace wifilogd
} // namespace android