blob: 5ec7a1d9421fd347bb0f6f693f37f58ef9722415 [file] [log] [blame]
// Copyright (c) 2013 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/active_passive_out_of_credits_detector.h"
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "shill/event_dispatcher.h"
#include "shill/mock_cellular.h"
#include "shill/mock_cellular_service.h"
#include "shill/mock_connection.h"
#include "shill/mock_connection_health_checker.h"
#include "shill/mock_device_info.h"
#include "shill/mock_manager.h"
#include "shill/mock_modem_info.h"
#include "shill/mock_proxy_factory.h"
#include "shill/mock_traffic_monitor.h"
using base::Bind;
using base::Unretained;
using std::string;
using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::ReturnPointee;
using testing::ReturnRef;
using testing::StrictMock;
namespace shill {
class ActivePassiveOutOfCreditsDetectorTest : public testing::Test {
public:
ActivePassiveOutOfCreditsDetectorTest()
: modem_info_(nullptr, &dispatcher_, &metrics_, &manager_, nullptr),
device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
modem_info_.metrics(), modem_info_.manager()),
manager_(modem_info_.control_interface(), modem_info_.dispatcher(),
modem_info_.metrics(), modem_info_.glib()),
metrics_(modem_info_.dispatcher()),
cellular_(new NiceMock<MockCellular>(&modem_info_,
"usb0",
kAddress,
3,
Cellular::kTypeCDMA,
"",
"",
"",
ProxyFactory::GetInstance())),
service_(new NiceMock<MockCellularService>(&modem_info_, cellular_)),
connection_(new NiceMock<MockConnection>(&device_info_)),
out_of_credits_detector_(
new ActivePassiveOutOfCreditsDetector(
modem_info_.dispatcher(), modem_info_.manager(),
modem_info_.metrics(), service_)) {}
virtual void SetUp() {
service_->connection_ = connection_;
cellular_->service_ = service_;
service_->SetRoamingState(kRoamingStateHome);
ON_CALL(*connection_, interface_name())
.WillByDefault(ReturnRef(interface_name_));
ON_CALL(*connection_, dns_servers())
.WillByDefault(ReturnRef(dns_servers_));
ON_CALL(manager_, GetPortalCheckURL())
.WillByDefault(ReturnRef(portal_check_url_));
ON_CALL(*service_, explicitly_disconnected()).WillByDefault(Return(false));
ON_CALL(*service_, resume_start_time())
.WillByDefault(ReturnRef(resume_start_time_));
}
virtual void TearDown() {
cellular_->service_ = nullptr; // Break circular reference.
}
void OnConnectionHealthCheckerResult(
ConnectionHealthChecker::Result result) {}
protected:
static const char kAddress[];
void SetMockServiceState(Service::ConnectState old_state,
Service::ConnectState new_state) {
out_of_credits_detector_->NotifyServiceStateChanged(old_state, new_state);
}
void SetTrafficMonitor(TrafficMonitor *traffic_monitor) {
out_of_credits_detector_->set_traffic_monitor(traffic_monitor);
}
void SetConnectionHealthChecker(ConnectionHealthChecker *health_checker) {
out_of_credits_detector_->set_connection_health_checker(health_checker);
}
EventDispatcher dispatcher_;
MockModemInfo modem_info_;
NiceMock<MockDeviceInfo> device_info_;
NiceMock<MockManager> manager_;
NiceMock<MockMetrics> metrics_;
scoped_refptr<NiceMock<MockCellular>> cellular_;
scoped_refptr<NiceMock<MockCellularService>> service_;
scoped_refptr<NiceMock<MockConnection>> connection_;
string interface_name_;
vector<string> dns_servers_;
string portal_check_url_;
base::Time resume_start_time_;
std::unique_ptr<ActivePassiveOutOfCreditsDetector> out_of_credits_detector_;
};
const char ActivePassiveOutOfCreditsDetectorTest::kAddress[] = "000102030405";
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopOutOfCreditsDetected) {
EXPECT_CALL(*service_, Connect(_, _)).Times(2);
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateFailure);
EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
dispatcher_.DispatchPendingEvents();
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
SetMockServiceState(Service::kStateConfiguring, Service::kStateIdle);
EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
dispatcher_.DispatchPendingEvents();
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopDetectionNotSkippedAfterSlowResume) {
resume_start_time_ =
base::Time::Now() -
base::TimeDelta::FromSeconds(
ActivePassiveOutOfCreditsDetector::kOutOfCreditsResumeIgnoreSeconds + 1);
EXPECT_CALL(*service_, Connect(_, _)).Times(2);
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateFailure);
EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
dispatcher_.DispatchPendingEvents();
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
SetMockServiceState(Service::kStateConfiguring, Service::kStateIdle);
EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
dispatcher_.DispatchPendingEvents();
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopDetectionSkippedAfterResume) {
resume_start_time_ = base::Time::Now();
ON_CALL(*service_, resume_start_time())
.WillByDefault(ReturnRef(resume_start_time_));
EXPECT_CALL(*service_, Connect(_, _)).Times(0);
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
// There should not be any pending connect requests but dispatch pending
// events anyway to be sure.
dispatcher_.DispatchPendingEvents();
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopDetectionSkippedAlreadyOutOfCredits) {
EXPECT_CALL(*service_, Connect(_, _)).Times(0);
out_of_credits_detector_->ReportOutOfCredits(true);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
// There should not be any pending connect requests but dispatch pending
// events anyway to be sure.
dispatcher_.DispatchPendingEvents();
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopDetectionSkippedExplicitDisconnect) {
EXPECT_CALL(*service_, Connect(_, _)).Times(0);
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
EXPECT_CALL(*service_, explicitly_disconnected()).WillOnce(Return(true));
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
// There should not be any pending connect requests but dispatch pending
// events anyway to be sure.
dispatcher_.DispatchPendingEvents();
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopDetectionConnectionNotDropped) {
EXPECT_CALL(*service_, Connect(_, _)).Times(0);
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
SetMockServiceState(Service::kStateConfiguring, Service::kStateConnected);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
// There should not be any pending connect requests but dispatch pending
// events anyway to be sure.
dispatcher_.DispatchPendingEvents();
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
ConnectDisconnectLoopDetectionIntermittentNetwork) {
EXPECT_CALL(*service_, Connect(_, _)).Times(0);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
out_of_credits_detector_->connect_start_time_ =
base::Time::Now() -
base::TimeDelta::FromSeconds(
ActivePassiveOutOfCreditsDetector::
kOutOfCreditsConnectionDropSeconds + 1);
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
// There should not be any pending connect requests but dispatch pending
// events anyway to be sure.
dispatcher_.DispatchPendingEvents();
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest, StartTrafficMonitor) {
MockTrafficMonitor *traffic_monitor = new StrictMock<MockTrafficMonitor>();
SetTrafficMonitor(traffic_monitor); // Passes ownership.
// Traffic monitor should only start when the service is connected.
EXPECT_CALL(*traffic_monitor, Start()).Times(1);
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
Mock::VerifyAndClearExpectations(traffic_monitor);
// Traffic monitor should not start for other state transitions.
EXPECT_CALL(*traffic_monitor, Start()).Times(0);
EXPECT_CALL(*traffic_monitor, Stop()).Times(AnyNumber());
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
SetMockServiceState(Service::kStateIdle, Service::kStateConfiguring);
SetMockServiceState(Service::kStateConfiguring, Service::kStateFailure);
SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
SetMockServiceState(Service::kStateConfiguring, Service::kStatePortal);
SetMockServiceState(Service::kStatePortal, Service::kStateOnline);
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest, StopTrafficMonitor) {
// Traffic monitor should stop when the service is disconnected.
MockTrafficMonitor *traffic_monitor = new StrictMock<MockTrafficMonitor>();
SetTrafficMonitor(traffic_monitor); // Passes ownership.
EXPECT_CALL(*traffic_monitor, Start());
EXPECT_CALL(*traffic_monitor, Stop());
SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
Mock::VerifyAndClearExpectations(traffic_monitor);
EXPECT_CALL(*traffic_monitor, Start());
EXPECT_CALL(*traffic_monitor, Stop());
SetMockServiceState(Service::kStateIdle, Service::kStateConnected);
SetMockServiceState(Service::kStateConnected, Service::kStateFailure);
Mock::VerifyAndClearExpectations(traffic_monitor);
// Need an additional call to Stop() because |traffic_monitor| destructor
// will call stop.
EXPECT_CALL(*traffic_monitor, Stop());
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest, OnNoNetworkRouting) {
// Make sure the connection health checker starts when there is no network
// routing.
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
MockConnectionHealthChecker *health_checker =
new MockConnectionHealthChecker(
service_->connection(),
modem_info_.dispatcher(),
manager_.health_checker_remote_ips(),
Bind(&ActivePassiveOutOfCreditsDetectorTest::
OnConnectionHealthCheckerResult,
Unretained(this)));
SetConnectionHealthChecker(health_checker); // Passes ownership.
EXPECT_CALL(*health_checker, Start());
out_of_credits_detector_->OnNoNetworkRouting(0);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
Mock::VerifyAndClearExpectations(health_checker);
// Make sure connection health checker does not start again if there is a
// health check in progress.
EXPECT_CALL(*health_checker, health_check_in_progress())
.WillOnce(Return(true));
EXPECT_CALL(*health_checker, Start()).Times(0);
out_of_credits_detector_->OnNoNetworkRouting(0);
}
TEST_F(ActivePassiveOutOfCreditsDetectorTest,
OnConnectionHealthCheckerResult) {
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
EXPECT_CALL(*service_, Disconnect(_, _)).Times(0);
out_of_credits_detector_->OnConnectionHealthCheckerResult(
ConnectionHealthChecker::kResultUnknown);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
out_of_credits_detector_->OnConnectionHealthCheckerResult(
ConnectionHealthChecker::kResultConnectionFailure);
EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
Mock::VerifyAndClearExpectations(service_);
EXPECT_CALL(*service_, Disconnect(_,
::testing::StrEq("out-of-credits"))).
Times(1);
out_of_credits_detector_->OnConnectionHealthCheckerResult(
ConnectionHealthChecker::kResultCongestedTxQueue);
EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
}
} // namespace shill