blob: 9a71cbc4e51f3e9d6a95b17ca0aae6195c7619c9 [file] [log] [blame]
/*
* Copyright (C) 2020 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>
#include "chre/platform/atomic.h"
#include "chre/platform/condition_variable.h"
#include "chre/platform/mutex.h"
#include "chre/platform/shared/bt_snoop_log.h"
#include "chre/platform/shared/log_buffer.h"
using testing::ContainerEq;
namespace chre {
// TODO(b/146164384): Test that the onLogsReady callback is called
// asynchronously
class TestLogBufferCallback : public LogBufferCallbackInterface {
public:
void onLogsReady() {
// Do nothing
}
};
static constexpr size_t kDefaultBufferSize = 1024;
// Helpers
void copyStringWithOffset(char *destination, const char *source,
size_t sourceOffset) {
size_t strlength = strlen(source + sourceOffset);
// +1 to copy nullbyte on the end
memcpy(destination, source + sourceOffset, strlength + 1);
}
TEST(LogBuffer, HandleOneLogAndCopy) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 20;
char outBuffer[kOutBufferSize];
const char *testLogStr = "test";
char testedBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr);
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_EQ(bytesCopied, strlen(testLogStr) + LogBuffer::kLogDataOffset + 1);
copyStringWithOffset(testedBuffer, outBuffer, LogBuffer::kLogDataOffset);
EXPECT_TRUE(strcmp(testedBuffer, testLogStr) == 0);
}
TEST(LogBuffer, HandleTwoLogsAndCopy) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 30;
char outBuffer[kOutBufferSize];
const char *testLogStr = "test";
const char *testLogStr2 = "test2";
char testedBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr2);
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_EQ(bytesCopied, strlen(testLogStr) + strlen(testLogStr2) +
2 * LogBuffer::kLogDataOffset + 2);
copyStringWithOffset(testedBuffer, outBuffer, LogBuffer::kLogDataOffset);
EXPECT_TRUE(strcmp(testedBuffer, testLogStr) == 0);
copyStringWithOffset(testedBuffer, outBuffer,
2 * LogBuffer::kLogDataOffset + strlen(testLogStr) + 1);
EXPECT_TRUE(strcmp(testedBuffer, testLogStr2) == 0);
}
TEST(LogBuffer, FailOnMoreCopyThanHandle) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 20;
char outBuffer[kOutBufferSize];
const char *testLogStr = "test";
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr);
size_t numLogsDropped;
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_EQ(bytesCopied, 0);
}
TEST(LogBuffer, FailOnHandleLargerLogThanBufferSize) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 20;
char outBuffer[kOutBufferSize];
// Note the size of this log is too big to fit in the buffer that we are
// using for the LogBuffer object
std::string testLogStrStr(kDefaultBufferSize + 1, 'a');
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStrStr.c_str());
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
// Should not be able to read this log out because there should be no log in
// the first place
EXPECT_EQ(bytesCopied, 0);
}
TEST(LogBuffer, StringLogOverwritten) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 200;
char outBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
constexpr size_t kLogPayloadSize = 100;
constexpr size_t kBufferUsePerLog = LogBuffer::kLogDataOffset +
LogBuffer::kStringLogOverhead +
kLogPayloadSize;
constexpr int kNumInsertions = 10;
constexpr int kNumLogDropsExpected =
kNumInsertions - kDefaultBufferSize / kBufferUsePerLog;
static_assert(kNumLogDropsExpected > 0);
// This for loop adds 1060 (kNumInsertions * kBufferUsePerLog) bytes of data
// through the buffer which is > than 1024.
for (size_t i = 0; i < kNumInsertions; i++) {
std::string testLogStrStr(kLogPayloadSize, 'a' + i);
const char *testLogStr = testLogStrStr.c_str();
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr);
}
EXPECT_EQ(logBuffer.getBufferSize(),
(kNumInsertions - kNumLogDropsExpected) * kBufferUsePerLog);
EXPECT_EQ(logBuffer.getNumLogsDropped(), kNumLogDropsExpected);
for (size_t i = logBuffer.getNumLogsDropped(); i < kNumInsertions; i++) {
// Should have read out the i-th from front test log string which a string
// log 'a' + i.
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_TRUE(strcmp(outBuffer + LogBuffer::kLogDataOffset,
std::string(kLogPayloadSize, 'a' + i).c_str()) == 0);
EXPECT_EQ(bytesCopied, kBufferUsePerLog);
}
}
TEST(LogBuffer, TokenizedLogOverwritten) {
char buffer[kDefaultBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
constexpr size_t kLogPayloadSize = 100;
constexpr size_t kBufferUsePerLog = LogBuffer::kLogDataOffset +
LogBuffer::kTokenizedLogOffset +
kLogPayloadSize;
constexpr int kNumInsertions = 10;
constexpr int kNumLogDropsExpected =
kNumInsertions - kDefaultBufferSize / kBufferUsePerLog;
static_assert(kNumLogDropsExpected > 0);
// This for loop adds 1060 (kNumInsertions * kBufferUsePerLog) bytes of data
// through the buffer which is > than 1024.
for (size_t i = 0; i < kNumInsertions; i++) {
std::vector<uint8_t> testData(kLogPayloadSize, i);
logBuffer.handleEncodedLog(LogBufferLogLevel::INFO, 0, testData.data(),
testData.size());
}
EXPECT_EQ(logBuffer.getBufferSize(),
(kNumInsertions - kNumLogDropsExpected) * kBufferUsePerLog);
EXPECT_EQ(logBuffer.getNumLogsDropped(), kNumLogDropsExpected);
for (size_t i = logBuffer.getNumLogsDropped(); i < kNumInsertions; i++) {
// Should have read out the i-th from front test log data which is an
// integer i.
std::vector<uint8_t> outBuffer(kBufferUsePerLog, 0x77);
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer.data(), outBuffer.size(), &numLogsDropped);
// Validate that the log size in Tokenized log header matches the expected
// log size.
EXPECT_EQ(outBuffer[LogBuffer::kLogDataOffset], kLogPayloadSize);
outBuffer.erase(outBuffer.begin(), outBuffer.begin() +
LogBuffer::kLogDataOffset +
LogBuffer::kTokenizedLogOffset);
EXPECT_THAT(outBuffer,
ContainerEq(std::vector<uint8_t>(kLogPayloadSize, i)));
EXPECT_EQ(bytesCopied, kBufferUsePerLog);
}
}
TEST(LogBuffer, BtSnoopLogOverwritten) {
char buffer[kDefaultBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
constexpr size_t kLogPayloadSize = 100;
constexpr size_t kBufferUsePerLog = LogBuffer::kLogDataOffset +
LogBuffer::kBtSnoopLogOffset +
kLogPayloadSize;
constexpr int kNumInsertions = 10;
constexpr int kNumLogDropsExpected =
kNumInsertions - kDefaultBufferSize / kBufferUsePerLog;
static_assert(kNumLogDropsExpected > 0);
// This for loop adds 1070 (kNumInsertions * kBufferUsePerLog) bytes of data
// through the buffer which is > than 1024.
for (size_t i = 0; i < kNumInsertions; i++) {
std::vector<uint8_t> testData(kLogPayloadSize, i);
logBuffer.handleBtLog(BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER, 0,
testData.data(), testData.size());
}
EXPECT_EQ(logBuffer.getBufferSize(),
(kNumInsertions - kNumLogDropsExpected) * kBufferUsePerLog);
EXPECT_EQ(logBuffer.getNumLogsDropped(), kNumLogDropsExpected);
for (size_t i = logBuffer.getNumLogsDropped(); i < kNumInsertions; i++) {
// Should have read out the i-th from front test log data which is an
// integer i.
std::vector<uint8_t> outBuffer(kBufferUsePerLog, 0x77);
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer.data(), outBuffer.size(), &numLogsDropped);
// Validate that the Bt Snoop log header matches the expected log direction
// and size.
constexpr int kBtSnoopLogHeaderSizeOffset = 1;
EXPECT_EQ(
static_cast<BtSnoopDirection>(outBuffer[LogBuffer::kLogDataOffset]),
BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER);
EXPECT_EQ(
outBuffer[LogBuffer::kLogDataOffset + kBtSnoopLogHeaderSizeOffset],
kLogPayloadSize);
outBuffer.erase(outBuffer.begin(), outBuffer.begin() +
LogBuffer::kLogDataOffset +
LogBuffer::kBtSnoopLogOffset);
EXPECT_THAT(outBuffer,
ContainerEq(std::vector<uint8_t>(kLogPayloadSize, i)));
EXPECT_EQ(bytesCopied, kBufferUsePerLog);
}
}
TEST(LogBuffer, CopyIntoEmptyBuffer) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 0;
char outBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, "test");
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_EQ(bytesCopied, 0);
}
TEST(LogBuffer, NoCopyInfoBufferAfterHandleEmptyLog) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 200;
char outBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, "");
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_EQ(bytesCopied, 0);
}
TEST(LogBuffer, HandleLogOfNullBytes) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 200;
char outBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, "\0\0\0");
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
EXPECT_EQ(bytesCopied, 0);
}
TEST(LogBuffer, TruncateLongLog) {
char buffer[kDefaultBufferSize];
constexpr size_t kOutBufferSize = 500;
char outBuffer[kOutBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
std::string testStr(LogBuffer::kLogMaxSize + 1, 'a');
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testStr.c_str());
size_t numLogsDropped;
size_t bytesCopied =
logBuffer.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
// Should truncate the logs down to the kLogMaxSize + kLogDataOffset by the
// time it is copied out.
EXPECT_EQ(bytesCopied, LogBuffer::kLogMaxSize + LogBuffer::kLogDataOffset);
}
TEST(LogBuffer, WouldCauseOverflowTest) {
char buffer[kDefaultBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
// With an empty buffer inserting an empty string (only null terminator)
// should not overflow ASSERT because if this fails the next ASSERT statement
// is undefined most likely.
ASSERT_FALSE(logBuffer.logWouldCauseOverflow(1));
// This for loop adds 1000 bytes of data (kLogPayloadSize + kStringLogOverhead
// + kLogDataOffset). There is 24 bytes of space left in the buffer after this
// loop.
constexpr size_t kLogPayloadSize = 94;
for (size_t i = 0; i < 10; i++) {
std::string testLogStrStr(kLogPayloadSize, 'a');
const char *testLogStr = testLogStrStr.c_str();
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr);
}
// This adds 18 (kLogPayloadSize + kStringLogOverhead + kLogDataOffset) bytes
// of data. After this log entry there is room enough for a log of character
// size 1.
constexpr size_t kLastLogPayloadSize = 12;
std::string testLogStrStr(kLastLogPayloadSize, 'a');
const char *testLogStr = testLogStrStr.c_str();
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr);
// There should be just enough space for this log.
ASSERT_FALSE(logBuffer.logWouldCauseOverflow(1));
// Inserting any more than a one char log should cause overflow.
ASSERT_TRUE(logBuffer.logWouldCauseOverflow(2));
}
TEST(LogBuffer, TransferTest) {
char buffer[kDefaultBufferSize];
const size_t kOutBufferSize = 10;
char outBuffer[kOutBufferSize];
size_t numLogsDropped;
TestLogBufferCallback callback;
LogBuffer logBufferFrom(&callback, buffer, kDefaultBufferSize);
LogBuffer logBufferTo(&callback, buffer, kDefaultBufferSize);
const char *str1 = "str1";
const char *str2 = "str2";
const char *str3 = "str3";
logBufferFrom.handleLog(LogBufferLogLevel::INFO, 0, str1);
logBufferFrom.handleLog(LogBufferLogLevel::INFO, 0, str2);
logBufferFrom.handleLog(LogBufferLogLevel::INFO, 0, str3);
logBufferFrom.transferTo(logBufferTo);
// The logs should have the text of each of the logs pushed onto the From
// buffer in FIFO ordering.
logBufferTo.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
ASSERT_TRUE(strcmp(outBuffer + LogBuffer::kLogDataOffset, str1) == 0);
logBufferTo.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
ASSERT_TRUE(strcmp(outBuffer + LogBuffer::kLogDataOffset, str2) == 0);
logBufferTo.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
ASSERT_TRUE(strcmp(outBuffer + LogBuffer::kLogDataOffset, str3) == 0);
size_t bytesCopied =
logBufferTo.copyLogs(outBuffer, kOutBufferSize, &numLogsDropped);
// There should have been no logs left in the To buffer for that last copyLogs
ASSERT_EQ(bytesCopied, 0);
}
TEST(LogBuffer, GetLogDataLengthTest) {
char buffer[kDefaultBufferSize];
TestLogBufferCallback callback;
LogBuffer logBuffer(&callback, buffer, kDefaultBufferSize);
constexpr size_t kLogPayloadSize = 10;
constexpr size_t kBufferUseStringLog = LogBuffer::kLogDataOffset +
LogBuffer::kStringLogOverhead +
kLogPayloadSize;
constexpr size_t kBufferUseTokenizedLog = LogBuffer::kLogDataOffset +
LogBuffer::kTokenizedLogOffset +
kLogPayloadSize;
uint8_t mCurrentLogStartingIndex = 0;
std::string testLogStr(kLogPayloadSize, 'a');
logBuffer.handleLog(LogBufferLogLevel::INFO, 0, testLogStr.c_str());
EXPECT_EQ(logBuffer.getLogDataLength(
mCurrentLogStartingIndex + LogBuffer::kLogDataOffset,
LogType::STRING),
LogBuffer::kStringLogOverhead + kLogPayloadSize);
mCurrentLogStartingIndex += kBufferUseStringLog;
std::vector<uint8_t> testLogTokenized(kLogPayloadSize, 0x77);
logBuffer.handleEncodedLog(LogBufferLogLevel::INFO, 0,
testLogTokenized.data(), testLogTokenized.size());
EXPECT_EQ(logBuffer.getLogDataLength(
mCurrentLogStartingIndex + LogBuffer::kLogDataOffset,
LogType::TOKENIZED),
LogBuffer::kTokenizedLogOffset + kLogPayloadSize);
mCurrentLogStartingIndex += kBufferUseTokenizedLog;
std::vector<uint8_t> testLogBtSnoop(kLogPayloadSize, 0x77);
logBuffer.handleBtLog(BtSnoopDirection::INCOMING_FROM_BT_CONTROLLER, 0,
testLogBtSnoop.data(), testLogBtSnoop.size());
EXPECT_EQ(logBuffer.getLogDataLength(
mCurrentLogStartingIndex + LogBuffer::kLogDataOffset,
LogType::BLUETOOTH),
LogBuffer::kBtSnoopLogOffset + kLogPayloadSize);
}
// TODO(srok): Add multithreaded tests
} // namespace chre