blob: de1877848b3e295a26ab6257b45dfb6e5f561b3c [file] [log] [blame]
// Copyright 2018 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 <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_log.h"
#include "shill/mock_manager.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"
#include "shill/test_event_dispatcher.h"
using base::Bind;
using base::Unretained;
using testing::_;
using testing::HasSubstr;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::SetArgPointee;
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() = default;
MOCK_METHOD0(OnFailureCallback, void());
MOCK_METHOD0(OnGatewayChangeCallback, void());
const LinkMonitor::FailureCallback failure_callback() const {
return failure_callback_;
}
const LinkMonitor::GatewayChangeCallback gateway_change_callback() const {
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()
: manager_(&control_, &dispatcher_, &metrics_),
device_info_(&manager_),
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()) {}
~LinkMonitorTest() override = default;
void SetUp() override {
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(SetArgPointee<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(SetArgPointee<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:
EventDispatcherForTest dispatcher_;
StrictMock<MockMetrics> metrics_;
MockControl control_;
MockManager manager_;
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