123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- /*
- * 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 <errno.h>
- #include <unistd.h>
- #include <algorithm>
- #include <cstring>
- #include <memory>
- #include <string>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- #include "android-base/unique_fd.h"
- #include "gmock/gmock.h"
- #include "gtest/gtest.h"
- #include "wifilogd/byte_buffer.h"
- #include "wifilogd/local_utils.h"
- #include "wifilogd/protocol.h"
- #include "wifilogd/tests/mock_os.h"
- #include "wifilogd/command_processor.h"
- namespace android {
- namespace wifilogd {
- namespace {
- using ::android::base::unique_fd;
- using ::testing::_;
- using ::testing::AnyNumber;
- using ::testing::AtLeast;
- using ::testing::EndsWith;
- using ::testing::HasSubstr;
- using ::testing::Invoke;
- using ::testing::Return;
- using ::testing::StartsWith;
- using ::testing::StrictMock;
- using local_utils::GetMaxVal;
- // The CommandBuffer is deliberately larger than the maximal permitted
- // command, so that we can test the CommandProcessor's handling of oversized
- // inputs.
- using CommandBuffer = ByteBuffer<protocol::kMaxMessageSize * 2>;
- constexpr size_t kBufferSizeBytes = protocol::kMaxMessageSize * 16;
- constexpr char kLogRecordSeparator = '\n';
- constexpr size_t kMaxAsciiMessagePayloadLen = protocol::kMaxMessageSize -
- sizeof(protocol::Command) -
- sizeof(protocol::AsciiMessage);
- class CommandProcessorTest : public ::testing::Test {
- public:
- CommandProcessorTest() {
- os_ = new StrictMock<MockOs>();
- auto& accumulator = written_to_os_;
- ON_CALL(*os_, Write(_, _, _))
- .WillByDefault(Invoke(
- [&accumulator](int /*fd*/, const void* write_buf, size_t buflen) {
- accumulator.append(static_cast<const char*>(write_buf), buflen);
- return std::tuple<size_t, Os::Errno>(buflen, 0);
- }));
- command_processor_ = std::unique_ptr<CommandProcessor>(
- new CommandProcessor(kBufferSizeBytes, std::unique_ptr<Os>(os_)));
- }
- protected:
- CommandBuffer BuildAsciiMessageCommandWithAdjustments(
- const std::string& tag, const std::string& message,
- ssize_t command_payload_len_adjustment,
- ssize_t ascii_message_tag_len_adjustment,
- ssize_t ascii_message_data_len_adjustment) {
- const size_t adjusted_tag_len =
- tag.length() + ascii_message_tag_len_adjustment;
- const size_t adjusted_data_len =
- message.length() + ascii_message_data_len_adjustment;
- const auto ascii_message_header =
- protocol::AsciiMessage()
- .set_tag_len(SAFELY_CLAMP(
- adjusted_tag_len, uint8_t, 0,
- GetMaxVal<decltype(protocol::AsciiMessage::tag_len)>()))
- .set_data_len(SAFELY_CLAMP(
- adjusted_data_len, uint16_t, 0,
- GetMaxVal<decltype(protocol::AsciiMessage::data_len)>()))
- .set_severity(protocol::MessageSeverity::kError);
- EXPECT_EQ(adjusted_tag_len, ascii_message_header.tag_len);
- EXPECT_EQ(adjusted_data_len, ascii_message_header.data_len);
- const size_t payload_len = sizeof(ascii_message_header) + tag.length() +
- message.length() +
- command_payload_len_adjustment;
- const auto command =
- protocol::Command()
- .set_opcode(protocol::Opcode::kWriteAsciiMessage)
- .set_payload_len(SAFELY_CLAMP(
- payload_len, uint16_t, 0,
- GetMaxVal<decltype(protocol::Command::payload_len)>()));
- EXPECT_EQ(payload_len, command.payload_len);
- return CommandBuffer()
- .AppendOrDie(&command, sizeof(command))
- .AppendOrDie(&ascii_message_header, sizeof(ascii_message_header))
- .AppendOrDie(tag.data(), tag.length())
- .AppendOrDie(message.data(), message.length());
- }
- CommandBuffer BuildAsciiMessageCommand(const std::string& tag,
- const std::string& message) {
- return BuildAsciiMessageCommandWithAdjustments(tag, message, 0, 0, 0);
- }
- bool SendAsciiMessageWithAdjustments(
- const std::string& tag, const std::string& message,
- ssize_t transport_len_adjustment, ssize_t command_payload_len_adjustment,
- ssize_t ascii_message_tag_len_adjustment,
- ssize_t ascii_message_data_len_adjustment) {
- const CommandBuffer& command_buffer(BuildAsciiMessageCommandWithAdjustments(
- tag, message, command_payload_len_adjustment,
- ascii_message_tag_len_adjustment, ascii_message_data_len_adjustment));
- EXPECT_CALL(*os_, GetTimestamp(CLOCK_MONOTONIC));
- EXPECT_CALL(*os_, GetTimestamp(CLOCK_BOOTTIME));
- EXPECT_CALL(*os_, GetTimestamp(CLOCK_REALTIME));
- return command_processor_->ProcessCommand(
- command_buffer.data(), command_buffer.size() + transport_len_adjustment,
- Os::kInvalidFd);
- }
- bool SendAsciiMessage(const std::string& tag, const std::string& message) {
- return SendAsciiMessageWithAdjustments(tag, message, 0, 0, 0, 0);
- }
- bool SendDumpBuffers() {
- const auto command = protocol::Command()
- .set_opcode(protocol::Opcode::kDumpBuffers)
- .set_payload_len(0);
- const auto buf = CommandBuffer().AppendOrDie(&command, sizeof(command));
- constexpr int kFakeFd = 100;
- return command_processor_->ProcessCommand(buf.data(), buf.size(), kFakeFd);
- }
- std::string written_to_os_; // Must out-live |os_|
- std::unique_ptr<CommandProcessor> command_processor_;
- // We use a raw pointer to access the mock, since ownership passes
- // to |command_processor_|.
- StrictMock<MockOs>* os_;
- };
- } // namespace
- // A valid ASCII message should, of course, be processed successfully.
- TEST_F(CommandProcessorTest, ProcessCommandOnValidAsciiMessageSucceeds) {
- EXPECT_TRUE(SendAsciiMessage("tag", "message"));
- }
- // If the buffer given to ProcessCommand() is shorter than a protocol::Command,
- // then we discard the data.
- TEST_F(CommandProcessorTest,
- ProcessCommandOnAsciiMessageShorterThanCommandFails) {
- const CommandBuffer& command_buffer(
- BuildAsciiMessageCommand("tag", "message"));
- EXPECT_FALSE(command_processor_->ProcessCommand(
- command_buffer.data(), sizeof(protocol::Command) - 1, Os::kInvalidFd));
- }
- // In all other cases, we save the data we got, and will try to salvage the
- // contents when dumping.
- TEST_F(CommandProcessorTest, ProcessCommandOnAsciiMessageWithEmtpyTagSucceeds) {
- EXPECT_TRUE(SendAsciiMessage("", "message"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandOnAsciiMessageWithEmptyMessageSucceeds) {
- EXPECT_TRUE(SendAsciiMessage("tag", ""));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandOnAsciiMessageWithEmptyTagAndMessageSucceeds) {
- EXPECT_TRUE(SendAsciiMessage("", ""));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandOnAsciiMessageWithBadCommandLengthSucceeds) {
- EXPECT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 0, 1, 0, 0));
- EXPECT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 0, -1, 0, 0));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandOnAsciiMessageWithBadTagLengthSucceeds) {
- EXPECT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 0, 0, 1, 0));
- EXPECT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 0, 0, -1, 0));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandOnAsciiMessageWithBadMessageLengthSucceeds) {
- EXPECT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 0, 0, 0, 1));
- EXPECT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 0, 0, 0, -1));
- }
- TEST_F(CommandProcessorTest, ProcessCommandOnOverlyLargeAsciiMessageSucceeds) {
- const std::string tag{"tag"};
- EXPECT_TRUE(SendAsciiMessage(
- tag, std::string(kMaxAsciiMessagePayloadLen - tag.size() + 1, '.')));
- }
- TEST_F(CommandProcessorTest, ProcessCommandInvalidOpcodeReturnsFailure) {
- using opcode_enum_t = decltype(protocol::Command::opcode);
- using opcode_integral_t = std::underlying_type<opcode_enum_t>::type;
- constexpr auto invalid_opcode = GetMaxVal<opcode_integral_t>();
- const auto command =
- protocol::Command()
- .set_opcode(local_utils::CopyFromBufferOrDie<opcode_enum_t>(
- &invalid_opcode, sizeof(invalid_opcode)))
- .set_payload_len(0);
- const auto buf = CommandBuffer().AppendOrDie(&command, sizeof(command));
- constexpr int kFakeFd = 100;
- EXPECT_FALSE(
- command_processor_->ProcessCommand(buf.data(), buf.size(), kFakeFd));
- }
- TEST_F(CommandProcessorTest, ProcessCommandSucceedsEvenAfterFillingBuffer) {
- const std::string tag{"tag"};
- const std::string message(kMaxAsciiMessagePayloadLen - tag.size(), '.');
- for (size_t cumulative_payload_bytes = 0;
- cumulative_payload_bytes <= kBufferSizeBytes;
- cumulative_payload_bytes += (tag.size() + message.size())) {
- EXPECT_TRUE(SendAsciiMessage(tag, message));
- }
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersOutputIncludesCorrectlyFormattedTimestamps) {
- const CommandBuffer& command_buf(BuildAsciiMessageCommand("tag", "message"));
- EXPECT_CALL(*os_, GetTimestamp(CLOCK_MONOTONIC))
- .WillOnce(Return(Os::Timestamp{0, 999}));
- EXPECT_CALL(*os_, GetTimestamp(CLOCK_BOOTTIME))
- .WillOnce(Return(Os::Timestamp{1, 1000}));
- EXPECT_CALL(*os_, GetTimestamp(CLOCK_REALTIME))
- .WillOnce(Return(Os::Timestamp{123456, 123456000}));
- EXPECT_TRUE(command_processor_->ProcessCommand(
- command_buf.data(), command_buf.size(), Os::kInvalidFd));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- EXPECT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, StartsWith("0.000000 1.000001 123456.123456"));
- }
- TEST_F(CommandProcessorTest, ProcessCommandDumpBuffersSucceedsOnEmptyLog) {
- EXPECT_CALL(*os_, Write(_, _, _)).Times(0);
- EXPECT_TRUE(SendDumpBuffers());
- }
- TEST_F(CommandProcessorTest, ProcessCommandDumpBuffersIncludesAllMessages) {
- constexpr int kNumMessages = 5;
- for (size_t i = 0; i < kNumMessages; ++i) {
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- }
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- EXPECT_TRUE(SendDumpBuffers());
- EXPECT_EQ(kNumMessages,
- std::count(written_to_os_.begin(), written_to_os_.end(),
- kLogRecordSeparator));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageIncludesTagAndMessage) {
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("tag message\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesEmptyTag) {
- ASSERT_TRUE(SendAsciiMessage("", "message"));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("[empty] message\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesEmptyMessage) {
- ASSERT_TRUE(SendAsciiMessage("tag", ""));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("tag [empty]\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesEmptyTagAndEmptyMessage) {
- ASSERT_TRUE(SendAsciiMessage("", ""));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("[empty] [empty]\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageSanitizesUnprintableChars) {
- ASSERT_TRUE(SendAsciiMessage("\xfftag\xff", "\xffmessage\xff"));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("?tag? ?message?\n"));
- }
- TEST_F(
- CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooShortForAsciiMessage) { // NOLINT(whitespace/line_length)
- ASSERT_TRUE(SendAsciiMessageWithAdjustments("", "", -1, 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("[truncated-header]\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooShortForTagStart) {
- constexpr char kTag[] = "tag";
- constexpr char kMessage[] = "message";
- ASSERT_TRUE(SendAsciiMessageWithAdjustments(
- kTag, kMessage, -(std::strlen(kTag) + std::strlen(kMessage)), 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("[buffer-overrun] [buffer-overrun]\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooShortForTagEnd) {
- constexpr char kTag[] = "tag";
- constexpr char kMessage[] = "message";
- ASSERT_TRUE(SendAsciiMessageWithAdjustments(
- kTag, kMessage, -(1 + std::strlen(kMessage)), 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_,
- EndsWith("ta[buffer-overrun] [buffer-overrun]\n"));
- }
- TEST_F(
- CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooShortForLogMessageStart) { // NOLINT(whitespace/line_length)
- constexpr char kTag[] = "tag";
- constexpr char kMessage[] = "message";
- ASSERT_TRUE(SendAsciiMessageWithAdjustments(kTag, kMessage,
- -std::strlen(kMessage), 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("tag [buffer-overrun]\n"));
- }
- TEST_F(
- CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooShortForLogMessageEnd) { // NOLINT(whitespace/line_length)
- ASSERT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", -1, 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("tag messag[buffer-overrun]\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooLongForTag) {
- ASSERT_TRUE(SendAsciiMessageWithAdjustments("tag", "", 100, 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("tag [empty]\n"));
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersAsciiMessageHandlesMessageTooLongForMessage) {
- ASSERT_TRUE(SendAsciiMessageWithAdjustments("tag", "message", 100, 0, 0, 0));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- EXPECT_THAT(written_to_os_, EndsWith("tag message\n"));
- }
- TEST_F(CommandProcessorTest, ProcessCommandDumpBuffersStopsAfterFirstError) {
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- EXPECT_CALL(*os_, Write(_, _, _))
- .WillOnce(Return(std::tuple<size_t, Os::Errno>{-1, EBADF}));
- ASSERT_FALSE(SendDumpBuffers());
- }
- TEST_F(CommandProcessorTest, ProcessCommandDumpBuffersContinuesPastEintr) {
- constexpr int kNumMessages = 5;
- for (size_t i = 0; i < kNumMessages; ++i) {
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- }
- std::string written_to_os;
- EXPECT_CALL(*os_, Write(_, _, _))
- .WillRepeatedly(Invoke(
- [&written_to_os](int /*fd*/, const void* write_buf, size_t buflen) {
- written_to_os.append(static_cast<const char*>(write_buf), buflen);
- return std::tuple<size_t, Os::Errno>{buflen / 2, EINTR};
- }));
- EXPECT_TRUE(SendDumpBuffers());
- EXPECT_EQ(kNumMessages, std::count(written_to_os.begin(), written_to_os.end(),
- kLogRecordSeparator));
- }
- TEST_F(CommandProcessorTest, ProcessCommandDumpBuffersIsIdempotent) {
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- ASSERT_TRUE(SendDumpBuffers());
- ASSERT_GT(written_to_os_.size(), 0U);
- written_to_os_.clear();
- ASSERT_EQ(0U, written_to_os_.size());
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- EXPECT_TRUE(SendDumpBuffers());
- EXPECT_GT(written_to_os_.size(), 0U);
- }
- TEST_F(CommandProcessorTest,
- ProcessCommandDumpBuffersIsIdempotentEvenWithWriteFailure) {
- ASSERT_TRUE(SendAsciiMessage("tag", "message"));
- EXPECT_CALL(*os_, Write(_, _, _))
- .WillOnce(Return(std::tuple<size_t, Os::Errno>{-1, EBADF}));
- ASSERT_FALSE(SendDumpBuffers());
- ASSERT_EQ(0U, written_to_os_.size());
- EXPECT_CALL(*os_, Write(_, _, _)).Times(AtLeast(1));
- EXPECT_TRUE(SendDumpBuffers());
- EXPECT_GT(written_to_os_.size(), 0U);
- }
- // Strictly speaking, this is not a unit test. But there's no easy way to get
- // unique_fd to call on an instance of our Os.
- TEST_F(CommandProcessorTest, ProcessCommandClosesFd) {
- int pipe_fds[2];
- ASSERT_EQ(0, pipe(pipe_fds));
- const unique_fd our_fd{pipe_fds[0]};
- const int their_fd = pipe_fds[1];
- const CommandBuffer& command_buffer(
- BuildAsciiMessageCommand("tag", "message"));
- EXPECT_CALL(*os_, GetTimestamp(_)).Times(AnyNumber());
- EXPECT_TRUE(command_processor_->ProcessCommand(
- command_buffer.data(), command_buffer.size(), their_fd));
- EXPECT_EQ(-1, close(their_fd));
- EXPECT_EQ(EBADF, errno);
- }
- // Strictly speaking, this is not a unit test. But there's no easy way to get
- // unique_fd to call on an instance of our Os.
- TEST_F(CommandProcessorTest, ProcessCommandClosesFdEvenOnFailure) {
- int pipe_fds[2];
- ASSERT_EQ(0, pipe(pipe_fds));
- const unique_fd our_fd{pipe_fds[0]};
- const int their_fd = pipe_fds[1];
- const CommandBuffer command_buffer;
- EXPECT_FALSE(command_processor_->ProcessCommand(
- command_buffer.data(), command_buffer.size(), their_fd));
- EXPECT_EQ(-1, close(their_fd));
- EXPECT_EQ(EBADF, errno);
- }
- } // namespace wifilogd
- } // namespace android
|