diff options
author | mukesh agrawal <quiche@google.com> | 2016-11-15 19:45:09 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-11-15 19:45:09 +0000 |
commit | 1549f4b8e68d8456d1fb4d84ba47bc47cda15597 (patch) | |
tree | 077ca0d3290eeaf4e71cdbd66d6cbaa1be788565 | |
parent | 848b5c896c848ecae25508900f24df34e177797b (diff) | |
parent | e15b826ff52bca5fc2b6d54202c0024877c31878 (diff) | |
download | wifilogd-1549f4b8e68d8456d1fb4d84ba47bc47cda15597.tar.gz |
add MainLoop am: 3b1d20a572 am: 519e17b2a8 am: 468798aa84
am: e15b826ff5
Change-Id: I822f46dc0a92e8bf1b29ba78ca0a09784937f76c
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | main_loop.cpp | 71 | ||||
-rw-r--r-- | main_loop.h | 55 | ||||
-rw-r--r-- | tests/main_loop_unittest.cpp | 135 |
4 files changed, 263 insertions, 0 deletions
@@ -33,6 +33,7 @@ LOCAL_CPPFLAGS := $(wifilogd_cpp_flags) LOCAL_C_INCLUDES := $(wifilogd_includes) LOCAL_SRC_FILES := \ command_processor.cpp \ + main_loop.cpp \ memory_reader.cpp \ message_buffer.cpp \ os.cpp \ @@ -55,6 +56,7 @@ LOCAL_SRC_FILES := \ tests/command_processor_unittest.cpp \ tests/local_utils_unittest.cpp \ tests/main.cpp \ + tests/main_loop_unittest.cpp \ tests/memory_reader_unittest.cpp \ tests/message_buffer_unittest.cpp \ tests/mock_command_processor.cpp \ diff --git a/main_loop.cpp b/main_loop.cpp new file mode 100644 index 0000000..62bed31 --- /dev/null +++ b/main_loop.cpp @@ -0,0 +1,71 @@ +/* + * 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 <cstdint> +#include <cstring> +#include <memory> +#include <utility> + +#include "android-base/logging.h" + +#include "wifilogd/main_loop.h" +#include "wifilogd/protocol.h" + +namespace android { +namespace wifilogd { + +namespace { +constexpr auto kMainBufferSizeBytes = 128 * 1024; +} + +MainLoop::MainLoop(const std::string& socket_name) + : MainLoop(socket_name, std::make_unique<Os>(), + std::make_unique<CommandProcessor>(kMainBufferSizeBytes)) {} + +MainLoop::MainLoop(const std::string& socket_name, std::unique_ptr<Os> os, + std::unique_ptr<CommandProcessor> command_processor) + : os_(std::move(os)), command_processor_(std::move(command_processor)) { + Os::Errno err; + std::tie(sock_fd_, err) = os_->GetControlSocket(socket_name); + if (err) { + LOG(FATAL) << "Failed to get control socket: " << std::strerror(errno); + } +} + +void MainLoop::RunOnce() { + std::array<uint8_t, protocol::kMaxMessageSize> input_buf; + size_t datagram_len; + Os::Errno err; + std::tie(datagram_len, err) = + os_->ReceiveDatagram(sock_fd_, input_buf.data(), input_buf.size()); + if (err) { + // TODO(b/32098735): Increment stats counter. + // TODO(b/32481888): Improve error handling. + return; + } + + if (datagram_len > protocol::kMaxMessageSize) { + // TODO(b/32098735): Increment stats counter. + datagram_len = protocol::kMaxMessageSize; + } + + command_processor_->ProcessCommand(input_buf.data(), datagram_len, + Os::kInvalidFd); +} + +} // namespace wifilogd +} // namespace android diff --git a/main_loop.h b/main_loop.h new file mode 100644 index 0000000..c5a4b48 --- /dev/null +++ b/main_loop.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef MAIN_LOOP_H_ +#define MAIN_LOOP_H_ + +#include <memory> +#include <string> + +#include "android-base/macros.h" + +#include "wifilogd/command_processor.h" +#include "wifilogd/os.h" + +namespace android { +namespace wifilogd { + +// The main event loop for wifilogd. +class MainLoop { + public: + explicit MainLoop(const std::string& socket_name); + MainLoop(const std::string& socket_name, std::unique_ptr<Os> os, + std::unique_ptr<CommandProcessor> command_processor); + + // Runs one iteration of the loop. + void RunOnce(); + + private: + std::unique_ptr<Os> os_; + std::unique_ptr<CommandProcessor> command_processor_; + // We use an int, rather than a unique_fd, because the file + // descriptor's lifetime is managed by init. (init creates + // the socket before forking our process.) + int sock_fd_; + + DISALLOW_COPY_AND_ASSIGN(MainLoop); +}; + +} // namespace wifilogd +} // namespace android + +#endif // MAIN_LOOP_H_ diff --git a/tests/main_loop_unittest.cpp b/tests/main_loop_unittest.cpp new file mode 100644 index 0000000..db04027 --- /dev/null +++ b/tests/main_loop_unittest.cpp @@ -0,0 +1,135 @@ +/* + * 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 <cerrno> +#include <memory> +#include <tuple> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "wifilogd/tests/mock_command_processor.h" +#include "wifilogd/tests/mock_os.h" + +#include "wifilogd/main_loop.h" +#include "wifilogd/protocol.h" + +namespace android { +namespace wifilogd { +namespace { + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Ge; +using ::testing::Return; +using ::testing::StrictMock; + +constexpr int kControlSocketFd = 100; +constexpr char kFakeSocketName[] = "fake-socket"; + +class MainLoopTest : public ::testing::Test { + public: + MainLoopTest() + : os_(new StrictMock<MockOs>()), + command_processor_(new StrictMock<MockCommandProcessor>()) { + EXPECT_CALL(*os_, GetControlSocket(kFakeSocketName)) + .WillOnce(Return(std::tuple<size_t, Os::Errno>{kControlSocketFd, 0})); + main_loop_ = std::make_unique<MainLoop>( + kFakeSocketName, std::unique_ptr<Os>{os_}, + std::unique_ptr<CommandProcessor>{command_processor_}); + } + + protected: + std::unique_ptr<MainLoop> main_loop_; + // We use raw pointers to access the mocks, since ownership passes + // to |main_loop_|. + StrictMock<MockOs>* os_; + StrictMock<MockCommandProcessor>* command_processor_; +}; + +} // namespace + +TEST_F(MainLoopTest, RunOnceReadsFromCorrectSocket) { + EXPECT_CALL(*os_, ReceiveDatagram(kControlSocketFd, _, _)); + EXPECT_CALL(*command_processor_, ProcessCommand(_, _, _)).Times(AnyNumber()); + main_loop_->RunOnce(); +} + +TEST_F(MainLoopTest, RunOnceReadsWithSufficientlyLargeBuffer) { + EXPECT_CALL(*os_, ReceiveDatagram(_, _, Ge(protocol::kMaxMessageSize))); + EXPECT_CALL(*command_processor_, ProcessCommand(_, _, _)).Times(AnyNumber()); + main_loop_->RunOnce(); +} + +TEST_F(MainLoopTest, RunOncePassesSmallestValidMessageToCommandProcessor) { + EXPECT_CALL(*os_, ReceiveDatagram(_, _, _)) + .WillOnce( + Return(std::tuple<size_t, Os::Errno>{sizeof(protocol::Command), 0})); + EXPECT_CALL(*command_processor_, + ProcessCommand(_, sizeof(protocol::Command), _)); + main_loop_->RunOnce(); +} + +TEST_F(MainLoopTest, RunOncePassesLargestValidMessageToCommandProcessor) { + EXPECT_CALL(*os_, ReceiveDatagram(_, _, _)) + .WillOnce( + Return(std::tuple<size_t, Os::Errno>{protocol::kMaxMessageSize, 0})); + EXPECT_CALL(*command_processor_, + ProcessCommand(_, protocol::kMaxMessageSize, _)); + main_loop_->RunOnce(); +} + +TEST_F(MainLoopTest, RunOncePassesRuntMessageToCommandProcessor) { + EXPECT_CALL(*os_, ReceiveDatagram(_, _, _)) + .WillOnce(Return(std::tuple<size_t, Os::Errno>{0, 0})); + EXPECT_CALL(*command_processor_, ProcessCommand(_, 0, _)); + main_loop_->RunOnce(); +} + +TEST_F(MainLoopTest, RunOnceLimitsMaxSizeReportedToCommandProcessor) { + EXPECT_CALL(*os_, ReceiveDatagram(_, _, _)) + .WillOnce(Return( + std::tuple<size_t, Os::Errno>{protocol::kMaxMessageSize + 1, 0})); + EXPECT_CALL(*command_processor_, + ProcessCommand(_, protocol::kMaxMessageSize, _)); + main_loop_->RunOnce(); +} + +TEST_F(MainLoopTest, RunOnceDoesNotPassDataToCommandProcessorOnError) { + EXPECT_CALL(*os_, ReceiveDatagram(_, _, protocol::kMaxMessageSize)) + .WillOnce(Return(std::tuple<size_t, Os::Errno>{0, EINTR})); + EXPECT_CALL(*command_processor_, ProcessCommand(_, _, _)).Times(0); + main_loop_->RunOnce(); +} + +// Per +// github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests, +// death tests should be specially named. +using MainLoopDeathTest = MainLoopTest; + +TEST_F(MainLoopDeathTest, CtorFailureToFetchControlSocketCausesDeath) { + auto os = std::make_unique<StrictMock<MockOs>>(); + auto command_processor = std::make_unique<StrictMock<MockCommandProcessor>>(); + ON_CALL(*os, GetControlSocket(kFakeSocketName)) + .WillByDefault(Return(std::tuple<size_t, Os::Errno>{-1, ERANGE})); + EXPECT_DEATH( + MainLoop(kFakeSocketName, std::move(os), std::move(command_processor)), + "Failed to get control socket"); +} + +} // namespace wifilogd +} // namespace android |