//
// Copyright (C) 2015 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/passive_link_monitor.h"
#include <net/if_arp.h>
#include <string>
#include <gtest/gtest.h>
#include "shill/arp_client_test_helper.h"
#include "shill/arp_packet.h"
#include "shill/logging.h"
#include "shill/mock_arp_client.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/net/byte_string.h"
#include "shill/net/ip_address.h"
using base::Bind;
using base::Unretained;
using std::string;
using testing::_;
using testing::AnyNumber;
using testing::HasSubstr;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::StrictMock;
using testing::Test;
namespace shill {
namespace {
const char kInterfaceName[] = "test-interface";
const char kLocalIPAddress[] = "10.0.1.1";
const uint8_t kLocalMACAddress[] = { 0, 1, 2, 3, 4, 5 };
const char kRemoteIPAddress[] = "10.0.1.2";
const uint8_t kRemoteMACAddress[] = { 6, 7, 8, 9, 10, 11 };
} // namespace
class ResultCallbackObserver {
public:
ResultCallbackObserver()
: result_callback_(
Bind(&ResultCallbackObserver::OnResultCallback,
Unretained(this))) {}
virtual ~ResultCallbackObserver() {}
MOCK_METHOD1(OnResultCallback, void(bool status));
const PassiveLinkMonitor::ResultCallback result_callback() {
return result_callback_;
}
private:
PassiveLinkMonitor::ResultCallback result_callback_;
DISALLOW_COPY_AND_ASSIGN(ResultCallbackObserver);
};
class PassiveLinkMonitorTest : public Test {
public:
PassiveLinkMonitorTest()
: device_info_(&control_, nullptr, nullptr, nullptr),
connection_(new StrictMock<MockConnection>(&device_info_)),
client_(new MockArpClient()),
client_test_helper_(client_),
link_monitor_(connection_, &dispatcher_, observer_.result_callback()),
interface_name_(kInterfaceName) {}
virtual ~PassiveLinkMonitorTest() {}
virtual void SetUp() {
ScopeLogger::GetInstance()->EnableScopesByName("link");
ScopeLogger::GetInstance()->set_verbose_level(4);
link_monitor_.arp_client_.reset(client_);
EXPECT_CALL(*connection_, interface_name())
.WillRepeatedly(ReturnRef(interface_name_));
}
virtual void TearDown() {
ScopeLogger::GetInstance()->EnableScopesByName("-link");
ScopeLogger::GetInstance()->set_verbose_level(0);
}
void ReceiveArpPacket(uint16_t operation) {
client_test_helper_.GeneratePacket(
operation,
IPAddress(kLocalIPAddress),
ByteString(kLocalMACAddress, arraysize(kLocalMACAddress)),
IPAddress(kRemoteIPAddress),
ByteString(kRemoteMACAddress, arraysize(kRemoteMACAddress)));
link_monitor_.ReceiveRequest(0);
}
void MonitorCompleted(bool status) {
link_monitor_.MonitorCompleted(status);
}
void InvokeCycleTimeoutHandler() {
link_monitor_.CycleTimeoutHandler();
}
void SetCurrentCycleStats(int num_requests_received, int num_cycles_passed) {
link_monitor_.num_requests_received_ = num_requests_received;
link_monitor_.num_cycles_passed_ = num_cycles_passed;
}
void VerifyCurrentCycleStats(int num_requests_received,
int num_cycles_passed) {
EXPECT_EQ(num_requests_received, link_monitor_.num_requests_received_);
EXPECT_EQ(num_cycles_passed, link_monitor_.num_cycles_passed_);
}
protected:
MockEventDispatcher dispatcher_;
MockControl control_;
NiceMock<MockDeviceInfo> device_info_;
ResultCallbackObserver observer_;
scoped_refptr<MockConnection> connection_;
MockArpClient* client_;
ArpClientTestHelper client_test_helper_;
PassiveLinkMonitor link_monitor_;
const string interface_name_;
};
TEST_F(PassiveLinkMonitorTest, StartFailedArpClient) {
EXPECT_CALL(*client_, StartRequestListener()).WillOnce(Return(false));
EXPECT_FALSE(link_monitor_.Start(PassiveLinkMonitor::kDefaultMonitorCycles));
}
TEST_F(PassiveLinkMonitorTest, StartSuccess) {
EXPECT_CALL(*client_, StartRequestListener()).WillOnce(Return(true));
EXPECT_CALL(dispatcher_, CreateReadyHandler(_, IOHandler::kModeInput, _))
.Times(1);
EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(1);
EXPECT_TRUE(link_monitor_.Start(PassiveLinkMonitor::kDefaultMonitorCycles));
}
TEST_F(PassiveLinkMonitorTest, Stop) {
EXPECT_CALL(*client_, Stop()).Times(1);
link_monitor_.Stop();
Mock::VerifyAndClearExpectations(client_);
}
TEST_F(PassiveLinkMonitorTest, MonitorCompleted) {
// Monitor failed.
EXPECT_CALL(*client_, Stop()).Times(1);
EXPECT_CALL(observer_, OnResultCallback(false)).Times(1);
MonitorCompleted(false);
Mock::VerifyAndClearExpectations(client_);
Mock::VerifyAndClearExpectations(&observer_);
// Monitor succeed.
EXPECT_CALL(*client_, Stop()).Times(1);
EXPECT_CALL(observer_, OnResultCallback(true)).Times(1);
MonitorCompleted(true);
Mock::VerifyAndClearExpectations(client_);
Mock::VerifyAndClearExpectations(&observer_);
}
TEST_F(PassiveLinkMonitorTest, ReceiveArpReply) {
// Setup initial stats.
const int kRequestReceived = 0;
const int kCurrentCycle = 0;
SetCurrentCycleStats(kRequestReceived, kCurrentCycle);
ScopedMockLog log;
EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
EXPECT_CALL(log, Log(_, _, HasSubstr("This is not a request packet")))
.Times(1);
ReceiveArpPacket(ARPOP_REPLY);
// Verify no change in receive count.
VerifyCurrentCycleStats(kRequestReceived, kCurrentCycle);
Mock::VerifyAndClearExpectations(&log);
}
TEST_F(PassiveLinkMonitorTest, ReceiveArpRequest) {
// Setup initial stats.
const int kRequestReceived = 0;
const int kCurrentCycle = 0;
SetCurrentCycleStats(kRequestReceived, kCurrentCycle);
EXPECT_CALL(*client_, Stop()).Times(0);
ReceiveArpPacket(ARPOP_REQUEST);
ReceiveArpPacket(ARPOP_REQUEST);
VerifyCurrentCycleStats(kRequestReceived + 2, kCurrentCycle);
Mock::VerifyAndClearExpectations(client_);
}
TEST_F(PassiveLinkMonitorTest, ReceiveAllRequestsForCycle) {
// 4 ARP requests received in the current cycle so far.
const int kRequestReceived = 4;
const int kCurrentCycle = 0;
SetCurrentCycleStats(kRequestReceived, kCurrentCycle);
// Received all required requests for a cycle, stop the ARP client.
EXPECT_CALL(*client_, Stop()).Times(1);
ReceiveArpPacket(ARPOP_REQUEST);
Mock::VerifyAndClearExpectations(client_);
}
TEST_F(PassiveLinkMonitorTest, CycleFailed) {
// 3 ARP requests received in the current cycle so far.
const int kRequestReceived = 3;
const int kCurrentCycle = 0;
SetCurrentCycleStats(kRequestReceived, kCurrentCycle);
// Monitor failed for the current cycle, post a task to perform cleanup and
// invoke result callback.
EXPECT_CALL(*client_, StartRequestListener()).Times(0);
EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(dispatcher_, PostTask(_)).Times(1);
InvokeCycleTimeoutHandler();
}
TEST_F(PassiveLinkMonitorTest, CycleSucceed) {
// 5 ARP requests received in the current cycle so far.
const int kRequestReceived = 5;
const int kCurrentCycle = 0;
SetCurrentCycleStats(kRequestReceived, kCurrentCycle);
// Monitor succeed for the current cycle, post a task to trigger a new cycle.
EXPECT_CALL(*client_, StartRequestListener()).WillOnce(Return(true));
EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(1);
EXPECT_CALL(dispatcher_, PostTask(_)).Times(0);
InvokeCycleTimeoutHandler();
// ARP request received count should be resetted.
VerifyCurrentCycleStats(0, kCurrentCycle + 1);
}
TEST_F(PassiveLinkMonitorTest, AllCyclesCompleted) {
// 5 ARP requests received in the current cycle so far.
const int kRequestReceived = 5;
const int kCurrentCycle = PassiveLinkMonitor::kDefaultMonitorCycles - 1;
SetCurrentCycleStats(kRequestReceived, kCurrentCycle);
// Monitor completed all the cycles, post a task to perform cleanup and
// invoke result callback.
EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(dispatcher_, PostTask(_)).Times(1);
InvokeCycleTimeoutHandler();
VerifyCurrentCycleStats(0, PassiveLinkMonitor::kDefaultMonitorCycles);
}
} // namespace shill