shill: Add SocketInfoReader class to read TCP socket information.
BUG=chromium:225907
TEST=Build and run unit tests.
Change-Id: I9b4f7258a0eca67d3e6260078ca010df148859e9
Reviewed-on: https://gerrit.chromium.org/gerrit/47300
Reviewed-by: Thieu Le <thieule@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
Commit-Queue: Ben Chan <benchan@chromium.org>
diff --git a/Makefile b/Makefile
index 22b3dcb..388c982 100644
--- a/Makefile
+++ b/Makefile
@@ -284,6 +284,7 @@
shill_test_config.o \
shill_time.o \
socket_info.o \
+ socket_info_reader.o \
sockets.o \
static_ip_parameters.o \
supplicant_bss_proxy.o \
@@ -402,6 +403,7 @@
mock_routing_table.o \
mock_rtnl_handler.o \
mock_service.o \
+ mock_socket_info_reader.o \
mock_sockets.o \
mock_store.o \
mock_supplicant_bss_proxy.o \
@@ -445,6 +447,7 @@
shims/certificates_unittest.o \
shims/netfilter_queue_processor_unittest.o \
socket_info_unittest.o \
+ socket_info_reader_unittest.o \
static_ip_parameters_unittest.o \
technology_unittest.o \
traffic_monitor_unittest.o \
diff --git a/mock_socket_info_reader.cc b/mock_socket_info_reader.cc
new file mode 100644
index 0000000..23c3bfd
--- /dev/null
+++ b/mock_socket_info_reader.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/mock_socket_info_reader.h"
+
+namespace shill {
+
+MockSocketInfoReader::MockSocketInfoReader() {}
+
+MockSocketInfoReader::~MockSocketInfoReader() {}
+
+} // namespace shill
diff --git a/mock_socket_info_reader.h b/mock_socket_info_reader.h
new file mode 100644
index 0000000..1ced570
--- /dev/null
+++ b/mock_socket_info_reader.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MOCK_SOCKET_INFO_READER_H_
+#define SHILL_MOCK_SOCKET_INFO_READER_H_
+
+#include <vector>
+
+#include <base/basictypes.h>
+#include <gmock/gmock.h>
+
+#include "shill/socket_info_reader.h"
+
+namespace shill {
+
+class SocketInfo;
+
+class MockSocketInfoReader : public SocketInfoReader {
+ public:
+ MockSocketInfoReader();
+ virtual ~MockSocketInfoReader();
+
+ MOCK_METHOD1(LoadTcpSocketInfo, bool(std::vector<SocketInfo> *info_list));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSocketInfoReader);
+};
+
+} // namespace shill
+
+#endif // SHILL_MOCK_SOCKET_INFO_READER_H_
diff --git a/socket_info_reader.cc b/socket_info_reader.cc
new file mode 100644
index 0000000..26f7db7
--- /dev/null
+++ b/socket_info_reader.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/socket_info_reader.h"
+
+#include <algorithm>
+#include <limits>
+
+#include <base/file_path.h>
+#include <base/string_number_conversions.h>
+#include <base/string_split.h>
+
+#include "shill/file_reader.h"
+#include "shill/logging.h"
+
+using base::FilePath;
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace {
+
+const char kTcpSocketInfoFilePath[] = "/proc/net/tcp";
+
+} // namespace
+
+SocketInfoReader::SocketInfoReader() {}
+
+SocketInfoReader::~SocketInfoReader() {}
+
+bool SocketInfoReader::LoadTcpSocketInfo(vector<SocketInfo> *info_list) {
+ return LoadSocketInfo(FilePath(kTcpSocketInfoFilePath), info_list);
+}
+
+bool SocketInfoReader::LoadSocketInfo(const FilePath &info_file_path,
+ vector<SocketInfo> *info_list) {
+ FileReader file_reader;
+ if (!file_reader.Open(info_file_path)) {
+ SLOG(Link, 2) << __func__ << ": Failed to open '"
+ << info_file_path.value() << "'.";
+ return false;
+ }
+
+ info_list->clear();
+ string line;
+ while (file_reader.ReadLine(&line)) {
+ SocketInfo socket_info;
+ if (ParseSocketInfo(line, &socket_info))
+ info_list->push_back(socket_info);
+ }
+ return true;
+}
+
+bool SocketInfoReader::ParseSocketInfo(const string &input,
+ SocketInfo *socket_info) {
+ vector<string> tokens;
+ base::SplitStringAlongWhitespace(input, &tokens);
+ if (tokens.size() < 10) {
+ return false;
+ }
+
+ IPAddress ip_address(IPAddress::kFamilyUnknown);
+ uint16 port = 0;
+
+ if (!ParseIPAddressAndPort(tokens[1], &ip_address, &port)) {
+ return false;
+ }
+ socket_info->set_local_ip_address(ip_address);
+ socket_info->set_local_port(port);
+
+ if (!ParseIPAddressAndPort(tokens[2], &ip_address, &port)) {
+ return false;
+ }
+ socket_info->set_remote_ip_address(ip_address);
+ socket_info->set_remote_port(port);
+
+ SocketInfo::ConnectionState connection_state =
+ SocketInfo::kConnectionStateUnknown;
+ if (!ParseConnectionState(tokens[3], &connection_state)) {
+ return false;
+ }
+ socket_info->set_connection_state(connection_state);
+
+ uint64 transmit_queue_value = 0, receive_queue_value = 0;
+ if (!ParseTransimitAndReceiveQueueValues(
+ tokens[4], &transmit_queue_value, &receive_queue_value)) {
+ return false;
+ }
+ socket_info->set_transmit_queue_value(transmit_queue_value);
+ socket_info->set_receive_queue_value(receive_queue_value);
+
+ SocketInfo::TimerState timer_state = SocketInfo::kTimerStateUnknown;
+ if (!ParseTimerState(tokens[5], &timer_state)) {
+ return false;
+ }
+ socket_info->set_timer_state(timer_state);
+
+ return true;
+}
+
+bool SocketInfoReader::ParseIPAddressAndPort(
+ const string &input, IPAddress *ip_address, uint16 *port) {
+ vector<string> tokens;
+
+ base::SplitString(input, ':', &tokens);
+ if (tokens.size() != 2 ||
+ !ParseIPAddress(tokens[0], ip_address) ||
+ !ParsePort(tokens[1], port)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool SocketInfoReader::ParseIPAddress(const string &input,
+ IPAddress *ip_address) {
+ vector<uint8> bytes;
+ if (!base::HexStringToBytes(input, &bytes))
+ return false;
+
+ IPAddress::Family family;
+ if (bytes.size() == 4)
+ family = IPAddress::kFamilyIPv4;
+ else if (bytes.size() == 6)
+ family = IPAddress::kFamilyIPv6;
+ else
+ return false;
+
+ // TODO(benchan): This doesn't work with IPv6 addresses. Fix it
+ // by introducing a proper method in ByteString to handle byte order
+ // conversion.
+ std::reverse(bytes.begin(), bytes.end());
+
+ *ip_address = IPAddress(family, ByteString(&bytes[0], bytes.size()));
+ return true;
+}
+
+bool SocketInfoReader::ParsePort(const string &input, uint16 *port) {
+ int result = 0;
+
+ if (input.size() != 4 || !base::HexStringToInt(input, &result) ||
+ result < 0 || result > std::numeric_limits<uint16>::max()) {
+ return false;
+ }
+
+ *port = result;
+ return true;
+}
+
+bool SocketInfoReader::ParseTransimitAndReceiveQueueValues(
+ const string &input,
+ uint64 *transmit_queue_value, uint64 *receive_queue_value) {
+ vector<string> tokens;
+ int64 signed_transmit_queue_value = 0, signed_receive_queue_value = 0;
+
+ base::SplitString(input, ':', &tokens);
+ if (tokens.size() != 2 ||
+ !base::HexStringToInt64(tokens[0], &signed_transmit_queue_value) ||
+ !base::HexStringToInt64(tokens[1], &signed_receive_queue_value)) {
+ return false;
+ }
+
+ *transmit_queue_value = static_cast<uint64>(signed_transmit_queue_value);
+ *receive_queue_value = static_cast<uint64>(signed_receive_queue_value);
+ return true;
+}
+
+bool SocketInfoReader::ParseConnectionState(
+ const string &input, SocketInfo::ConnectionState *connection_state) {
+ int result = 0;
+
+ if (input.size() != 2 || !base::HexStringToInt(input, &result)) {
+ return false;
+ }
+
+ if (result > 0 && result < SocketInfo::kConnectionStateMax) {
+ *connection_state = static_cast<SocketInfo::ConnectionState>(result);
+ } else {
+ *connection_state = SocketInfo::kConnectionStateUnknown;
+ }
+ return true;
+}
+
+bool SocketInfoReader::ParseTimerState(
+ const string &input, SocketInfo::TimerState *timer_state) {
+ vector<string> tokens;
+ int result = 0;
+
+ base::SplitString(input, ':', &tokens);
+ if (tokens.size() != 2 || tokens[0].size() != 2 ||
+ !base::HexStringToInt(tokens[0], &result)) {
+ return false;
+ }
+
+ if (result < SocketInfo::kTimerStateMax) {
+ *timer_state = static_cast<SocketInfo::TimerState>(result);
+ } else {
+ *timer_state = SocketInfo::kTimerStateUnknown;
+ }
+ return true;
+}
+
+} // namespace shill
diff --git a/socket_info_reader.h b/socket_info_reader.h
new file mode 100644
index 0000000..af32525
--- /dev/null
+++ b/socket_info_reader.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_SOCKET_INFO_READER_H_
+#define SHILL_SOCKET_INFO_READER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+#include <gtest/gtest_prod.h>
+
+#include "shill/socket_info.h"
+
+namespace base {
+
+class FilePath;
+
+} // namespace base
+
+namespace shill {
+
+class SocketInfoReader {
+ public:
+ SocketInfoReader();
+ virtual ~SocketInfoReader();
+
+ virtual bool LoadTcpSocketInfo(std::vector<SocketInfo> *info_list);
+
+ private:
+ FRIEND_TEST(SocketInfoReaderTest, LoadSocketInfo);
+ FRIEND_TEST(SocketInfoReaderTest, ParseConnectionState);
+ FRIEND_TEST(SocketInfoReaderTest, ParseIPAddress);
+ FRIEND_TEST(SocketInfoReaderTest, ParseIPAddressAndPort);
+ FRIEND_TEST(SocketInfoReaderTest, ParsePort);
+ FRIEND_TEST(SocketInfoReaderTest, ParseSocketInfo);
+ FRIEND_TEST(SocketInfoReaderTest, ParseTimerState);
+ FRIEND_TEST(SocketInfoReaderTest, ParseTransimitAndReceiveQueueValues);
+
+ bool LoadSocketInfo(const base::FilePath &info_file_path,
+ std::vector<SocketInfo> *info_list);
+ bool ParseSocketInfo(const std::string &input, SocketInfo *socket_info);
+ bool ParseIPAddressAndPort(
+ const std::string &input, IPAddress *ip_address, uint16 *port);
+ bool ParseIPAddress(const std::string &input, IPAddress *ip_address);
+ bool ParsePort(const std::string &input, uint16 *port);
+ bool ParseTransimitAndReceiveQueueValues(
+ const std::string &input,
+ uint64 *transmit_queue_value, uint64 *receive_queue_value);
+ bool ParseConnectionState(const std::string &input,
+ SocketInfo::ConnectionState *connection_state);
+ bool ParseTimerState(const std::string &input,
+ SocketInfo::TimerState *timer_state);
+
+ DISALLOW_COPY_AND_ASSIGN(SocketInfoReader);
+};
+
+} // namespace shill
+
+#endif // SHILL_SOCKET_INFO_READER_H_
diff --git a/socket_info_reader_unittest.cc b/socket_info_reader_unittest.cc
new file mode 100644
index 0000000..1fda67b
--- /dev/null
+++ b/socket_info_reader_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/socket_info_reader.h"
+
+#include <base/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/stringprintf.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using base::ScopedTempDir;
+using std::string;
+using std::vector;
+
+// TODO(benchan): Test IPv6 addresses.
+
+namespace shill {
+
+namespace {
+
+const char kIPAddress_0_0_0_0[] = "0.0.0.0";
+const char kIPAddress_127_0_0_1[] = "127.0.0.1";
+const char kIPAddress_192_168_1_10[] = "192.168.1.10";
+const char kIPAddress_255_255_255_255[] = "255.255.255.255";
+const char *kSocketInfoLines[] = {
+ " sl local_address rem_address st tx_queue rx_queue tr tm->when "
+ "retrnsmt uid timeout inode ",
+ " 0: 0100007F:0019 00000000:0000 0A 0000000A:00000005 00:00000000 "
+ "00000000 0 0 36948 1 0000000000000000 100 0 0 10 -1 ",
+ " 1: 0A01A8C0:0050 0100007F:03FC 01 00000000:00000000 00:00000000 "
+ "00000000 65534 0 2787034 1 0000000000000000 100 0 0 10 -1 ",
+};
+
+} // namespace
+
+class SocketInfoReaderTest : public testing::Test {
+ protected:
+ IPAddress StringToIPv4Address(const string &address_string) {
+ IPAddress ip_address(IPAddress::kFamilyIPv4);
+ EXPECT_TRUE(ip_address.SetAddressFromString(address_string));
+ return ip_address;
+ }
+
+ void CreateSocketInfoFile(const char **lines, size_t num_lines,
+ const FilePath &dir_path, FilePath *file_path) {
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(dir_path, file_path));
+ for (size_t i = 0; i < num_lines; ++i) {
+ string line = lines[i];
+ line += '\n';
+ ASSERT_EQ(line.size(),
+ file_util::AppendToFile(*file_path, line.data(), line.size()));
+ }
+ }
+
+ void ExpectSocketInfoEqual(const SocketInfo &info1, const SocketInfo &info2) {
+ EXPECT_EQ(info1.connection_state(), info2.connection_state());
+ EXPECT_TRUE(info1.local_ip_address().Equals(info2.local_ip_address()));
+ EXPECT_EQ(info1.local_port(), info2.local_port());
+ EXPECT_TRUE(info1.remote_ip_address().Equals(info2.remote_ip_address()));
+ EXPECT_EQ(info1.remote_port(), info2.remote_port());
+ EXPECT_EQ(info1.transmit_queue_value(), info2.transmit_queue_value());
+ EXPECT_EQ(info1.receive_queue_value(), info2.receive_queue_value());
+ EXPECT_EQ(info1.timer_state(), info2.timer_state());
+ }
+
+ SocketInfoReader reader_;
+};
+
+TEST_F(SocketInfoReaderTest, LoadSocketInfo) {
+ FilePath file_path("/non-existent-file");
+ vector<SocketInfo> info_list;
+
+ EXPECT_FALSE(reader_.LoadSocketInfo(file_path, &info_list));
+ EXPECT_TRUE(info_list.empty());
+
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ CreateSocketInfoFile(kSocketInfoLines, 1, temp_dir.path(), &file_path);
+ EXPECT_TRUE(reader_.LoadSocketInfo(file_path, &info_list));
+ EXPECT_TRUE(info_list.empty());
+
+ CreateSocketInfoFile(kSocketInfoLines, arraysize(kSocketInfoLines),
+ temp_dir.path(), &file_path);
+ EXPECT_TRUE(reader_.LoadSocketInfo(file_path, &info_list));
+ EXPECT_EQ(arraysize(kSocketInfoLines) - 1, info_list.size());
+ ExpectSocketInfoEqual(SocketInfo(SocketInfo::kConnectionStateListen,
+ StringToIPv4Address(kIPAddress_127_0_0_1),
+ 25,
+ StringToIPv4Address(kIPAddress_0_0_0_0),
+ 0,
+ 10,
+ 5,
+ SocketInfo::kTimerStateNoTimerPending),
+ info_list[0]);
+ ExpectSocketInfoEqual(SocketInfo(SocketInfo::kConnectionStateEstablished,
+ StringToIPv4Address(kIPAddress_192_168_1_10),
+ 80,
+ StringToIPv4Address(kIPAddress_127_0_0_1),
+ 1020,
+ 0,
+ 0,
+ SocketInfo::kTimerStateNoTimerPending),
+ info_list[1]);
+}
+
+TEST_F(SocketInfoReaderTest, ParseSocketInfo) {
+ SocketInfo info;
+
+ EXPECT_FALSE(reader_.ParseSocketInfo("", &info));
+ EXPECT_FALSE(reader_.ParseSocketInfo(kSocketInfoLines[0], &info));
+
+ EXPECT_TRUE(reader_.ParseSocketInfo(kSocketInfoLines[1], &info));
+ ExpectSocketInfoEqual(SocketInfo(SocketInfo::kConnectionStateListen,
+ StringToIPv4Address(kIPAddress_127_0_0_1),
+ 25,
+ StringToIPv4Address(kIPAddress_0_0_0_0),
+ 0,
+ 10,
+ 5,
+ SocketInfo::kTimerStateNoTimerPending),
+ info);
+}
+
+TEST_F(SocketInfoReaderTest, ParseIPAddressAndPort) {
+ IPAddress ip_address(IPAddress::kFamilyUnknown);
+ uint16 port = 0;
+
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort("", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort("00000000", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort("00000000:", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(":0000", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort("0000000Y:0000",
+ &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort("00000000:000Y", &ip_address,
+ &port));
+
+ EXPECT_TRUE(reader_.ParseIPAddressAndPort("0a01A8c0:0050",
+ &ip_address, &port));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_192_168_1_10)));
+ EXPECT_EQ(80, port);
+}
+
+TEST_F(SocketInfoReaderTest, ParseIPAddress) {
+ IPAddress ip_address(IPAddress::kFamilyUnknown);
+
+ EXPECT_FALSE(reader_.ParseIPAddress("", &ip_address));
+ EXPECT_FALSE(reader_.ParseIPAddress("0", &ip_address));
+ EXPECT_FALSE(reader_.ParseIPAddress("00", &ip_address));
+ EXPECT_FALSE(reader_.ParseIPAddress("0000000Y", &ip_address));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("00000000", &ip_address));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_0_0_0_0)));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("0100007F", &ip_address));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_127_0_0_1)));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("0a01A8c0", &ip_address));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_192_168_1_10)));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("ffffffff", &ip_address));
+ EXPECT_TRUE(ip_address.Equals(
+ StringToIPv4Address(kIPAddress_255_255_255_255)));
+}
+
+TEST_F(SocketInfoReaderTest, ParsePort) {
+ uint16 port = 0;
+
+ EXPECT_FALSE(reader_.ParsePort("", &port));
+ EXPECT_FALSE(reader_.ParsePort("0", &port));
+ EXPECT_FALSE(reader_.ParsePort("00", &port));
+ EXPECT_FALSE(reader_.ParsePort("000", &port));
+ EXPECT_FALSE(reader_.ParsePort("000Y", &port));
+
+ EXPECT_TRUE(reader_.ParsePort("0000", &port));
+ EXPECT_EQ(0, port);
+
+ EXPECT_TRUE(reader_.ParsePort("0050", &port));
+ EXPECT_EQ(80, port);
+
+ EXPECT_TRUE(reader_.ParsePort("abCD", &port));
+ EXPECT_EQ(43981, port);
+
+ EXPECT_TRUE(reader_.ParsePort("ffff", &port));
+ EXPECT_EQ(65535, port);
+}
+
+TEST_F(SocketInfoReaderTest, ParseTransimitAndReceiveQueueValues) {
+ uint64 transmit_queue_value = 0, receive_queue_value = 0;
+
+ EXPECT_FALSE(reader_.ParseTransimitAndReceiveQueueValues(
+ "", &transmit_queue_value, &receive_queue_value));
+ EXPECT_FALSE(reader_.ParseTransimitAndReceiveQueueValues(
+ "00000000", &transmit_queue_value, &receive_queue_value));
+ EXPECT_FALSE(reader_.ParseTransimitAndReceiveQueueValues(
+ "00000000:", &transmit_queue_value, &receive_queue_value));
+ EXPECT_FALSE(reader_.ParseTransimitAndReceiveQueueValues(
+ ":00000000", &transmit_queue_value, &receive_queue_value));
+ EXPECT_FALSE(reader_.ParseTransimitAndReceiveQueueValues(
+ "0000000Y:00000000", &transmit_queue_value, &receive_queue_value));
+ EXPECT_FALSE(reader_.ParseTransimitAndReceiveQueueValues(
+ "00000000:0000000Y", &transmit_queue_value, &receive_queue_value));
+
+ EXPECT_TRUE(reader_.ParseTransimitAndReceiveQueueValues(
+ "00000001:FFFFFFFF", &transmit_queue_value, &receive_queue_value));
+ EXPECT_EQ(1, transmit_queue_value);
+ EXPECT_EQ(0xffffffff, receive_queue_value);
+}
+
+TEST_F(SocketInfoReaderTest, ParseConnectionState) {
+ SocketInfo::ConnectionState connection_state =
+ SocketInfo::kConnectionStateUnknown;
+
+ EXPECT_FALSE(reader_.ParseConnectionState("", &connection_state));
+ EXPECT_FALSE(reader_.ParseConnectionState("0", &connection_state));
+ EXPECT_FALSE(reader_.ParseConnectionState("X", &connection_state));
+
+ EXPECT_TRUE(reader_.ParseConnectionState("00", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateUnknown, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("01", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateEstablished, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("02", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateSynSent, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("03", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateSynRecv, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("04", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateFinWait1, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("05", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateFinWait2, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("06", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateTimeWait, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("07", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateClose, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("08", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateCloseWait, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("09", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateLastAck, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("0A", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateListen, connection_state);
+ EXPECT_TRUE(reader_.ParseConnectionState("0B", &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateClosing, connection_state);
+
+ for (int i = SocketInfo::kConnectionStateMax; i < 256; ++i) {
+ EXPECT_TRUE(reader_.ParseConnectionState(
+ base::StringPrintf("%02X", i), &connection_state));
+ EXPECT_EQ(SocketInfo::kConnectionStateUnknown, connection_state);
+ }
+}
+
+TEST_F(SocketInfoReaderTest, ParseTimerState) {
+ SocketInfo::TimerState timer_state = SocketInfo::kTimerStateUnknown;
+
+ EXPECT_FALSE(reader_.ParseTimerState("", &timer_state));
+ EXPECT_FALSE(reader_.ParseTimerState("0", &timer_state));
+ EXPECT_FALSE(reader_.ParseTimerState("X", &timer_state));
+ EXPECT_FALSE(reader_.ParseTimerState("00", &timer_state));
+
+ EXPECT_TRUE(reader_.ParseTimerState("00:00000000", &timer_state));
+ EXPECT_EQ(SocketInfo::kTimerStateNoTimerPending, timer_state);
+ EXPECT_TRUE(reader_.ParseTimerState("01:00000000", &timer_state));
+ EXPECT_EQ(SocketInfo::kTimerStateRetransmitTimerPending, timer_state);
+ EXPECT_TRUE(reader_.ParseTimerState("02:00000000", &timer_state));
+ EXPECT_EQ(SocketInfo::kTimerStateAnotherTimerPending, timer_state);
+ EXPECT_TRUE(reader_.ParseTimerState("03:00000000", &timer_state));
+ EXPECT_EQ(SocketInfo::kTimerStateInTimeWaitState, timer_state);
+ EXPECT_TRUE(reader_.ParseTimerState("04:00000000", &timer_state));
+ EXPECT_EQ(SocketInfo::kTimerStateZeroWindowProbeTimerPending, timer_state);
+
+ for (int i = SocketInfo::kTimerStateMax; i < 256; ++i) {
+ EXPECT_TRUE(reader_.ParseTimerState(
+ base::StringPrintf("%02X:00000000", i), &timer_state));
+ EXPECT_EQ(SocketInfo::kTimerStateUnknown, timer_state);
+ }
+}
+
+} // namespace shill