//
// Copyright (C) 2012 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/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