普通文本  |  710行  |  27.26 KB

//
// Copyright (C) 2013 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 "shill/traffic_monitor.h"

#include <string>
#include <vector>

#include <base/bind.h>
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
#include <netinet/in.h>

#include "shill/mock_connection_info_reader.h"
#include "shill/mock_device.h"
#include "shill/mock_event_dispatcher.h"
#include "shill/mock_ipconfig.h"
#include "shill/mock_socket_info_reader.h"
#include "shill/nice_mock_control.h"

using base::Bind;
using base::StringPrintf;
using base::Unretained;
using std::string;
using std::vector;
using testing::_;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::Test;

namespace shill {

class TrafficMonitorTest : public Test {
 public:
  static const char kLocalIpAddr[];
  static const uint16_t kLocalPort1;
  static const uint16_t kLocalPort2;
  static const uint16_t kLocalPort3;
  static const uint16_t kLocalPort4;
  static const uint16_t kLocalPort5;
  static const char kRemoteIpAddr[];
  static const uint16_t kRemotePort;
  static const uint64_t kTxQueueLength1;
  static const uint64_t kTxQueueLength2;
  static const uint64_t kTxQueueLength3;
  static const uint64_t kTxQueueLength4;

  TrafficMonitorTest()
      : device_(new MockDevice(&control_,
                               &dispatcher_,
                               nullptr,
                               nullptr,
                               "netdev0",
                               "00:11:22:33:44:55",
                               1)),
        ipconfig_(new MockIPConfig(&control_, "netdev0")),
        mock_socket_info_reader_(new MockSocketInfoReader),
        mock_connection_info_reader_(new MockConnectionInfoReader),
        monitor_(device_, &dispatcher_),
        local_addr_(IPAddress::kFamilyIPv4),
        remote_addr_(IPAddress::kFamilyIPv4) {
    local_addr_.SetAddressFromString(kLocalIpAddr);
    remote_addr_.SetAddressFromString(kRemoteIpAddr);
  }

  MOCK_METHOD1(OnNoOutgoingPackets, void(int));

 protected:
  virtual void SetUp() {
    monitor_.socket_info_reader_.reset(
        mock_socket_info_reader_);  // Passes ownership
    monitor_.connection_info_reader_.reset(
        mock_connection_info_reader_);  // Passes ownership

    device_->set_ipconfig(ipconfig_);
    ipconfig_properties_.address = kLocalIpAddr;
    EXPECT_CALL(*ipconfig_.get(), properties())
        .WillRepeatedly(ReturnRef(ipconfig_properties_));
  }

  void VerifyStopped() {
    EXPECT_TRUE(monitor_.sample_traffic_callback_.IsCancelled());
    EXPECT_EQ(0, monitor_.accummulated_congested_tx_queues_samples_);
  }

  void VerifyStarted() {
    EXPECT_FALSE(monitor_.sample_traffic_callback_.IsCancelled());
  }

  void SetupMockSocketInfos(const vector<SocketInfo>& socket_infos) {
    mock_socket_infos_ = socket_infos;
    EXPECT_CALL(*mock_socket_info_reader_, LoadTcpSocketInfo(_))
        .WillRepeatedly(
            Invoke(this, &TrafficMonitorTest::MockLoadTcpSocketInfo));
  }

  void SetupMockConnectionInfos(
      const vector<ConnectionInfo>& connection_infos) {
    mock_connection_infos_ = connection_infos;
    EXPECT_CALL(*mock_connection_info_reader_, LoadConnectionInfo(_))
        .WillRepeatedly(
            Invoke(this, &TrafficMonitorTest::MockLoadConnectionInfo));
  }

  bool MockLoadTcpSocketInfo(vector<SocketInfo>* info_list) {
    *info_list = mock_socket_infos_;
    return true;
  }

  bool MockLoadConnectionInfo(vector<ConnectionInfo>* info_list) {
    *info_list = mock_connection_infos_;
    return true;
  }

  string FormatIPPort(const IPAddress& ip, const uint16_t port) {
    return StringPrintf("%s:%d", ip.ToString().c_str(), port);
  }

  NiceMockControl control_;
  NiceMock<MockEventDispatcher> dispatcher_;
  scoped_refptr<MockDevice> device_;
  scoped_refptr<MockIPConfig> ipconfig_;
  IPConfig::Properties ipconfig_properties_;
  MockSocketInfoReader* mock_socket_info_reader_;
  MockConnectionInfoReader* mock_connection_info_reader_;
  TrafficMonitor monitor_;
  vector<SocketInfo> mock_socket_infos_;
  vector<ConnectionInfo> mock_connection_infos_;
  IPAddress local_addr_;
  IPAddress remote_addr_;
};

// static
const char TrafficMonitorTest::kLocalIpAddr[] = "127.0.0.1";
const uint16_t TrafficMonitorTest::kLocalPort1 = 1234;
const uint16_t TrafficMonitorTest::kLocalPort2 = 2345;
const uint16_t TrafficMonitorTest::kLocalPort3 = 3456;
const uint16_t TrafficMonitorTest::kLocalPort4 = 4567;
const uint16_t TrafficMonitorTest::kLocalPort5 = 4567;
const char TrafficMonitorTest::kRemoteIpAddr[] = "192.168.1.1";
const uint16_t TrafficMonitorTest::kRemotePort = 5678;
const uint64_t TrafficMonitorTest::kTxQueueLength1 = 111;
const uint64_t TrafficMonitorTest::kTxQueueLength2 = 222;
const uint64_t TrafficMonitorTest::kTxQueueLength3 = 333;
const uint64_t TrafficMonitorTest::kTxQueueLength4 = 444;

TEST_F(TrafficMonitorTest, StartAndStop) {
  // Stop without start
  monitor_.Stop();
  VerifyStopped();

  // Normal start
  monitor_.Start();
  VerifyStarted();

  // Stop after start
  monitor_.Stop();
  VerifyStopped();

  // Stop again without start
  monitor_.Stop();
  VerifyStopped();
}

TEST_F(TrafficMonitorTest, BuildIPPortToTxQueueLengthValid) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  TrafficMonitor::IPPortToTxQueueLengthMap tx_queue_lengths;
  monitor_.BuildIPPortToTxQueueLength(socket_infos, &tx_queue_lengths);
  EXPECT_EQ(1, tx_queue_lengths.size());
  string ip_port = FormatIPPort(local_addr_, TrafficMonitorTest::kLocalPort1);
  EXPECT_EQ(TrafficMonitorTest::kTxQueueLength1, tx_queue_lengths[ip_port]);
}

TEST_F(TrafficMonitorTest, BuildIPPortToTxQueueLengthInvalidDevice) {
  vector<SocketInfo> socket_infos;
  IPAddress foreign_ip_addr(IPAddress::kFamilyIPv4);
  foreign_ip_addr.SetAddressFromString("192.167.1.1");
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 foreign_ip_addr,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  TrafficMonitor::IPPortToTxQueueLengthMap tx_queue_lengths;
  monitor_.BuildIPPortToTxQueueLength(socket_infos, &tx_queue_lengths);
  EXPECT_EQ(0, tx_queue_lengths.size());
}

TEST_F(TrafficMonitorTest, BuildIPPortToTxQueueLengthZero) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 0,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  TrafficMonitor::IPPortToTxQueueLengthMap tx_queue_lengths;
  monitor_.BuildIPPortToTxQueueLength(socket_infos, &tx_queue_lengths);
  EXPECT_EQ(0, tx_queue_lengths.size());
}

TEST_F(TrafficMonitorTest, BuildIPPortToTxQueueLengthInvalidConnectionState) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateSynSent,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  TrafficMonitor::IPPortToTxQueueLengthMap tx_queue_lengths;
  monitor_.BuildIPPortToTxQueueLength(socket_infos, &tx_queue_lengths);
  EXPECT_EQ(0, tx_queue_lengths.size());
}

TEST_F(TrafficMonitorTest, BuildIPPortToTxQueueLengthInvalidTimerState) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateNoTimerPending));
  TrafficMonitor::IPPortToTxQueueLengthMap tx_queue_lengths;
  monitor_.BuildIPPortToTxQueueLength(socket_infos, &tx_queue_lengths);
  EXPECT_EQ(0, tx_queue_lengths.size());
}

TEST_F(TrafficMonitorTest, BuildIPPortToTxQueueLengthMultipleEntries) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateSynSent,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateNoTimerPending));
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort2,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength2,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort3,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength3,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort4,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength4,
                 0,
                 SocketInfo::kTimerStateNoTimerPending));
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort5,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 0,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  TrafficMonitor::IPPortToTxQueueLengthMap tx_queue_lengths;
  monitor_.BuildIPPortToTxQueueLength(socket_infos, &tx_queue_lengths);
  EXPECT_EQ(2, tx_queue_lengths.size());
  string ip_port = FormatIPPort(local_addr_, TrafficMonitorTest::kLocalPort2);
  EXPECT_EQ(kTxQueueLength2, tx_queue_lengths[ip_port]);
  ip_port = FormatIPPort(local_addr_, TrafficMonitorTest::kLocalPort3);
  EXPECT_EQ(kTxQueueLength3, tx_queue_lengths[ip_port]);
}

TEST_F(TrafficMonitorTest, SampleTrafficStuckTxQueueSameQueueLength) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
  Mock::VerifyAndClearExpectations(this);

  // Mimic same queue length by using same mock socket info.
  EXPECT_CALL(*this, OnNoOutgoingPackets(
      TrafficMonitor::kNetworkProblemCongestedTxQueue));
  monitor_.SampleTraffic();
  Mock::VerifyAndClearExpectations(this);

  // Perform another sampling pass and make sure the callback is only
  // triggered once.
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
}

TEST_F(TrafficMonitorTest, SampleTrafficStuckTxQueueIncreasingQueueLength) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
  Mock::VerifyAndClearExpectations(this);

  socket_infos.clear();
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1 + 1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  EXPECT_CALL(*this, OnNoOutgoingPackets(
      TrafficMonitor::kNetworkProblemCongestedTxQueue));
  monitor_.SampleTraffic();
}

TEST_F(TrafficMonitorTest, SampleTrafficStuckTxQueueVariousQueueLengths) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength2,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
  Mock::VerifyAndClearExpectations(this);

  socket_infos.clear();
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
  Mock::VerifyAndClearExpectations(this);

  socket_infos.clear();
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength2,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  EXPECT_CALL(*this, OnNoOutgoingPackets(
      TrafficMonitor::kNetworkProblemCongestedTxQueue));
  monitor_.SampleTraffic();
}

TEST_F(TrafficMonitorTest, SampleTrafficUnstuckTxQueueZeroQueueLength) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();

  socket_infos.clear();
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 0,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.SampleTraffic();
  EXPECT_EQ(0, monitor_.accummulated_congested_tx_queues_samples_);
}

TEST_F(TrafficMonitorTest, SampleTrafficUnstuckTxQueueNoConnection) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();

  socket_infos.clear();
  SetupMockSocketInfos(socket_infos);
  monitor_.SampleTraffic();
  EXPECT_EQ(0, monitor_.accummulated_congested_tx_queues_samples_);
}

TEST_F(TrafficMonitorTest, SampleTrafficUnstuckTxQueueStateChanged) {
  vector<SocketInfo> socket_infos;
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateEstablished,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 TrafficMonitorTest::kTxQueueLength1,
                 0,
                 SocketInfo::kTimerStateRetransmitTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();

  socket_infos.clear();
  socket_infos.push_back(
      SocketInfo(SocketInfo::kConnectionStateClose,
                 local_addr_,
                 TrafficMonitorTest::kLocalPort1,
                 remote_addr_,
                 TrafficMonitorTest::kRemotePort,
                 0,
                 0,
                 SocketInfo::kTimerStateNoTimerPending));
  SetupMockSocketInfos(socket_infos);
  monitor_.SampleTraffic();
  EXPECT_EQ(0, monitor_.accummulated_congested_tx_queues_samples_);
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsTimedOut) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   true, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  // Make sure the no routing event is not fired before the threshold is
  // exceeded.
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 1; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
  Mock::VerifyAndClearExpectations(this);

  // This call should cause the threshold to exceed.
  EXPECT_CALL(*this, OnNoOutgoingPackets(
      TrafficMonitor::kNetworkProblemDNSFailure)).Times(1);
  monitor_.SampleTraffic();
  Mock::VerifyAndClearExpectations(this);

  // Make sure the event is only fired once.
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsOutstanding) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds + 1,
                   true, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsSuccessful) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   false, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 1; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsFailureThenSuccess) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   true, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 1; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
  Mock::VerifyAndClearExpectations(this);

  connection_infos.clear();
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   false, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  monitor_.SampleTraffic();
  EXPECT_EQ(0, monitor_.accummulated_dns_failures_samples_);
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsTimedOutInvalidProtocol) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_TCP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   true, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsTimedOutInvalidSourceIp) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   true, remote_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsTimedOutOutsideTimeWindow) {
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds -
                   TrafficMonitor::kSamplingIntervalMilliseconds / 1000,
                   true, remote_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitor::kDnsPort,
                   remote_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
}

TEST_F(TrafficMonitorTest, SampleTrafficNonDnsTimedOut) {
  const uint16_t kNonDnsPort = 54;
  vector<ConnectionInfo> connection_infos;
  connection_infos.push_back(
    ConnectionInfo(IPPROTO_UDP,
                   TrafficMonitor::kDnsTimedOutThresholdSeconds - 1,
                   true, local_addr_, TrafficMonitorTest::kLocalPort1,
                   remote_addr_, kNonDnsPort,
                   remote_addr_, kNonDnsPort,
                   local_addr_, TrafficMonitorTest::kLocalPort1));
  SetupMockConnectionInfos(connection_infos);
  monitor_.set_network_problem_detected_callback(
      Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
  for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
       ++count) {
    monitor_.SampleTraffic();
  }
}

TEST_F(TrafficMonitorTest, SampleTrafficDnsStatsReset) {
  vector<ConnectionInfo> connection_infos;
  SetupMockConnectionInfos(connection_infos);
  monitor_.accummulated_dns_failures_samples_ = 1;
  monitor_.SampleTraffic();
  EXPECT_EQ(0, monitor_.accummulated_dns_failures_samples_);
}

}  // namespace shill