| // Copyright (c) 2012 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/link_monitor.h" |
| |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <gtest/gtest.h> |
| |
| #include "shill/logging.h" |
| #include "shill/mock_active_link_monitor.h" |
| #include "shill/mock_connection.h" |
| #include "shill/mock_control.h" |
| #include "shill/mock_device_info.h" |
| #include "shill/mock_event_dispatcher.h" |
| #include "shill/mock_log.h" |
| #include "shill/mock_metrics.h" |
| #include "shill/mock_passive_link_monitor.h" |
| #include "shill/net/byte_string.h" |
| #include "shill/net/mock_time.h" |
| |
| using base::Bind; |
| using base::Unretained; |
| using std::string; |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::HasSubstr; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::Return; |
| using testing::ReturnRef; |
| using testing::SetArgumentPointee; |
| using testing::StrictMock; |
| using testing::Test; |
| |
| namespace shill { |
| |
| namespace { |
| const uint8_t kGatewayMACAddress[] = { 0, 1, 2, 3, 4, 5 }; |
| } // namespace |
| |
| class LinkMonitorObserver { |
| public: |
| LinkMonitorObserver() |
| : failure_callback_( |
| Bind(&LinkMonitorObserver::OnFailureCallback, Unretained(this))), |
| gateway_change_callback_( |
| Bind(&LinkMonitorObserver::OnGatewayChangeCallback, |
| Unretained(this))) {} |
| virtual ~LinkMonitorObserver() {} |
| |
| MOCK_METHOD0(OnFailureCallback, void()); |
| MOCK_METHOD0(OnGatewayChangeCallback, void()); |
| |
| const LinkMonitor::FailureCallback failure_callback() { |
| return failure_callback_; |
| } |
| |
| const LinkMonitor::GatewayChangeCallback gateway_change_callback() { |
| return gateway_change_callback_; |
| } |
| |
| private: |
| LinkMonitor::FailureCallback failure_callback_; |
| LinkMonitor::GatewayChangeCallback gateway_change_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LinkMonitorObserver); |
| }; |
| |
| class LinkMonitorTest : public Test { |
| public: |
| LinkMonitorTest() |
| : metrics_(&dispatcher_), |
| device_info_(&control_, nullptr, nullptr, nullptr), |
| connection_(new StrictMock<MockConnection>(&device_info_)), |
| active_link_monitor_(new MockActiveLinkMonitor()), |
| passive_link_monitor_(new MockPassiveLinkMonitor()), |
| monitor_(connection_, |
| &dispatcher_, |
| &metrics_, |
| &device_info_, |
| observer_.failure_callback(), |
| observer_.gateway_change_callback()) {} |
| virtual ~LinkMonitorTest() {} |
| |
| virtual void SetUp() { |
| monitor_.active_link_monitor_.reset(active_link_monitor_); |
| monitor_.passive_link_monitor_.reset(passive_link_monitor_); |
| monitor_.time_ = &time_; |
| |
| time_val_.tv_sec = 0; |
| time_val_.tv_usec = 0; |
| EXPECT_CALL(time_, GetTimeMonotonic(_)) |
| .WillRepeatedly(DoAll(SetArgumentPointee<0>(time_val_), Return(0))); |
| EXPECT_CALL(*connection_, technology()) |
| .WillRepeatedly(Return(Technology::kEthernet)); |
| } |
| |
| void AdvanceTime(int time_ms) { |
| struct timeval adv_time = { |
| static_cast<time_t>(time_ms/1000), |
| static_cast<time_t>((time_ms % 1000) * 1000) }; |
| timeradd(&time_val_, &adv_time, &time_val_); |
| EXPECT_CALL(time_, GetTimeMonotonic(_)) |
| .WillRepeatedly(DoAll(SetArgumentPointee<0>(time_val_), Return(0))); |
| } |
| |
| void SetGatewayMacAddress(const ByteString& gateway_mac_address) { |
| monitor_.gateway_mac_address_ = gateway_mac_address; |
| } |
| |
| void VerifyGatewayMacAddress(const ByteString& gateway_mac_address) { |
| EXPECT_TRUE(monitor_.gateway_mac_address_.Equals(gateway_mac_address)); |
| } |
| |
| void TriggerActiveLinkMonitorFailure(Metrics::LinkMonitorFailure failure, |
| int broadcast_failure_count, |
| int unicast_failure_count) { |
| monitor_.OnActiveLinkMonitorFailure(failure, |
| broadcast_failure_count, |
| unicast_failure_count); |
| } |
| |
| void TriggerActiveLinkMonitorSuccess() { |
| monitor_.OnActiveLinkMonitorSuccess(); |
| } |
| |
| void TriggerPassiveLinkMonitorResultCallback(bool status) { |
| monitor_.OnPassiveLinkMonitorResultCallback(status); |
| } |
| |
| protected: |
| MockEventDispatcher dispatcher_; |
| StrictMock<MockMetrics> metrics_; |
| MockControl control_; |
| NiceMock<MockDeviceInfo> device_info_; |
| scoped_refptr<MockConnection> connection_; |
| MockTime time_; |
| struct timeval time_val_; |
| MockActiveLinkMonitor* active_link_monitor_; |
| MockPassiveLinkMonitor* passive_link_monitor_; |
| LinkMonitorObserver observer_; |
| LinkMonitor monitor_; |
| }; |
| |
| MATCHER_P(IsMacAddress, mac_address, "") { |
| return mac_address.Equals(arg); |
| } |
| |
| TEST_F(LinkMonitorTest, Start) { |
| EXPECT_CALL(*active_link_monitor_, |
| Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)) |
| .WillOnce(Return(false)); |
| EXPECT_FALSE(monitor_.Start()); |
| Mock::VerifyAndClearExpectations(active_link_monitor_); |
| |
| EXPECT_CALL(*active_link_monitor_, |
| Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(monitor_.Start()); |
| Mock::VerifyAndClearExpectations(active_link_monitor_); |
| } |
| |
| TEST_F(LinkMonitorTest, OnAfterResume) { |
| ByteString gateway_mac(kGatewayMACAddress, arraysize(kGatewayMACAddress)); |
| const bool kGatewayUnicastArpSupport = true; |
| SetGatewayMacAddress(gateway_mac); |
| // Verify gateway settings persist when link monitor is restarted, and |
| // active link monitor is started with fast test period. |
| EXPECT_CALL(*active_link_monitor_, Stop()).Times(1); |
| EXPECT_CALL(*passive_link_monitor_, Stop()).Times(1); |
| EXPECT_CALL(*active_link_monitor_, gateway_supports_unicast_arp()) |
| .WillOnce(Return(kGatewayUnicastArpSupport)); |
| EXPECT_CALL(*active_link_monitor_, |
| set_gateway_mac_address(IsMacAddress(gateway_mac))); |
| EXPECT_CALL(*active_link_monitor_, |
| set_gateway_supports_unicast_arp(kGatewayUnicastArpSupport)); |
| EXPECT_CALL(*active_link_monitor_, |
| Start(ActiveLinkMonitor::kFastTestPeriodMilliseconds)); |
| monitor_.OnAfterResume(); |
| VerifyGatewayMacAddress(gateway_mac); |
| Mock::VerifyAndClearExpectations(active_link_monitor_); |
| Mock::VerifyAndClearExpectations(passive_link_monitor_); |
| } |
| |
| TEST_F(LinkMonitorTest, OnActiveLinkMonitorFailure) { |
| // Start link monitor. |
| EXPECT_CALL(*active_link_monitor_, |
| Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(monitor_.Start()); |
| Mock::VerifyAndClearExpectations(active_link_monitor_); |
| |
| const int kBroadcastFailureCount = 5; |
| const int kUnicastFailureCount = 3; |
| const int kElapsedTimeMilliseconds = 5000; |
| |
| // Active monitor failed after 5 seconds. |
| EXPECT_CALL(observer_, OnFailureCallback()).Times(1); |
| EXPECT_CALL(metrics_, SendEnumToUMA( |
| HasSubstr("LinkMonitorFailure"), |
| Metrics::kLinkMonitorFailureThresholdReached, _)); |
| EXPECT_CALL(metrics_, SendToUMA( |
| HasSubstr("LinkMonitorSecondsToFailure"), kElapsedTimeMilliseconds / 1000, |
| _, _, _)); |
| EXPECT_CALL(metrics_, SendToUMA( |
| HasSubstr("BroadcastErrorsAtFailure"), kBroadcastFailureCount, |
| _, _, _)); |
| EXPECT_CALL(metrics_, SendToUMA( |
| HasSubstr("UnicastErrorsAtFailure"), kUnicastFailureCount, |
| _, _, _)); |
| AdvanceTime(kElapsedTimeMilliseconds); |
| TriggerActiveLinkMonitorFailure(Metrics::kLinkMonitorFailureThresholdReached, |
| kBroadcastFailureCount, |
| kUnicastFailureCount); |
| } |
| |
| TEST_F(LinkMonitorTest, OnActiveLinkMonitorSuccess) { |
| ByteString gateway_mac(kGatewayMACAddress, |
| arraysize(kGatewayMACAddress)); |
| EXPECT_CALL(*active_link_monitor_, gateway_mac_address()) |
| .WillRepeatedly(ReturnRef(gateway_mac)); |
| |
| // Active link monitor succeed for the first time, gateway MAC address will be |
| // updated. |
| EXPECT_CALL(observer_, OnGatewayChangeCallback()).Times(1); |
| EXPECT_CALL(*passive_link_monitor_, Start( |
| PassiveLinkMonitor::kDefaultMonitorCycles)).Times(1); |
| TriggerActiveLinkMonitorSuccess(); |
| VerifyGatewayMacAddress(gateway_mac); |
| Mock::VerifyAndClearExpectations(&observer_); |
| Mock::VerifyAndClearExpectations(passive_link_monitor_); |
| |
| // Active link monitor succeed again, gateway MAC address not changed. |
| EXPECT_CALL(observer_, OnGatewayChangeCallback()).Times(0); |
| EXPECT_CALL(*passive_link_monitor_, Start( |
| PassiveLinkMonitor::kDefaultMonitorCycles)).Times(1); |
| TriggerActiveLinkMonitorSuccess(); |
| VerifyGatewayMacAddress(gateway_mac); |
| Mock::VerifyAndClearExpectations(&observer_); |
| Mock::VerifyAndClearExpectations(passive_link_monitor_); |
| } |
| |
| TEST_F(LinkMonitorTest, OnPassiveLinkMonitorResultCallback) { |
| // Active link monitor should start regardless of the result of the passive |
| // link monitor. |
| |
| EXPECT_CALL(*active_link_monitor_, |
| Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)); |
| TriggerPassiveLinkMonitorResultCallback(true); |
| Mock::VerifyAndClearExpectations(active_link_monitor_); |
| |
| EXPECT_CALL(*active_link_monitor_, |
| Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)); |
| TriggerPassiveLinkMonitorResultCallback(false); |
| Mock::VerifyAndClearExpectations(active_link_monitor_); |
| } |
| |
| } // namespace shill |