summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormukesh agrawal <quiche@google.com>2016-10-27 14:19:12 -0700
committermukesh agrawal <quiche@google.com>2016-11-15 11:12:35 -0800
commit3b1d20a5720a5fe6b0a1eccd577b0d2447139efc (patch)
tree077ca0d3290eeaf4e71cdbd66d6cbaa1be788565
parent82816d2fd2804c7ccbdf88e0ecadf35b19cbc564 (diff)
downloadwifilogd-3b1d20a5720a5fe6b0a1eccd577b0d2447139efc.tar.gz
add MainLoop
Add the class that will read messages from our control socket, and pass those messages to the CommandProcessor. Add unit tests for the same. Bug: 32451897 Test: ./runtests.sh (on angler) Change-Id: Icd9a0e9180a91f97d99d112024664e34fdc2a466
-rw-r--r--Android.mk2
-rw-r--r--main_loop.cpp71
-rw-r--r--main_loop.h55
-rw-r--r--tests/main_loop_unittest.cpp135
4 files changed, 263 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
index a25d048..b06568d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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