blob: f9d859d79ac065a6ad00a51807961ac5b111ad9e [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/pppoe/pppoe_service.h"
#include <map>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <base/memory/ref_counted.h>
#include "shill/dhcp/mock_dhcp_config.h"
#include "shill/dhcp/mock_dhcp_provider.h"
#include "shill/ethernet/mock_ethernet.h"
#include "shill/mock_control.h"
#include "shill/mock_device_info.h"
#include "shill/mock_external_task.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_process_manager.h"
#include "shill/ppp_device.h"
#include "shill/ppp_device_factory.h"
#include "shill/service.h"
#include "shill/test_event_dispatcher.h"
#include "shill/testing.h"
using testing::_;
using testing::Invoke;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::StrEq;
using testing::WithoutArgs;
namespace shill {
class PPPoEServiceTest : public testing::Test {
public:
PPPoEServiceTest()
: manager_(&control_interface_, &dispatcher_, &metrics_),
ethernet_(new NiceMock<MockEthernet>(
&manager_, "ethernet", "aabbccddeeff", 0)),
device_info_(&manager_) {
Service::SetNextSerialNumberForTesting(0);
service_ =
new PPPoEService(&manager_, ethernet_->weak_ptr_factory_.GetWeakPtr());
manager_.set_mock_device_info(&device_info_);
service_->process_manager_ = &process_manager_;
}
PPPoEServiceTest(const PPPoEServiceTest&) = delete;
PPPoEServiceTest& operator=(const PPPoEServiceTest&) = delete;
~PPPoEServiceTest() override {
Error error;
service_->Disconnect(&error, __func__);
dispatcher_.DispatchPendingEvents();
}
protected:
void FakeConnectionSuccess() {
EXPECT_CALL(*ethernet_, link_up()).WillRepeatedly(Return(true));
EXPECT_CALL(process_manager_, StartProcess(_, _, _, _, _, _))
.WillOnce(Return(0));
Error error;
service_->Connect(&error, "in test");
EXPECT_TRUE(error.IsSuccess());
service_->SetState(Service::kStateConnected);
}
bool IsAuthenticating() { return service_->authenticating_; }
void OnPPPDied(pid_t pid, int exit) { service_->OnPPPDied(pid, exit); }
int max_failure() { return service_->max_failure_; }
const PPPDeviceRefPtr& device() { return service_->ppp_device_; }
EventDispatcherForTest dispatcher_;
NiceMock<MockMetrics> metrics_;
NiceMock<MockControl> control_interface_;
NiceMock<MockProcessManager> process_manager_;
NiceMock<MockManager> manager_;
scoped_refptr<NiceMock<MockEthernet>> ethernet_;
NiceMock<MockDeviceInfo> device_info_;
scoped_refptr<PPPoEService> service_;
};
MATCHER_P(LinkNamed, name, "") {
return arg->link_name() == name;
}
TEST_F(PPPoEServiceTest, LogName) {
EXPECT_EQ("ppoe_0", service_->log_name());
}
TEST_F(PPPoEServiceTest, AuthenticationFailure) {
EXPECT_CALL(*ethernet_, link_up()).WillRepeatedly(Return(true));
FakeConnectionSuccess();
std::map<std::string, std::string> empty_dict;
service_->Notify(kPPPReasonAuthenticating, empty_dict);
auto previous_state = service_->state();
service_->Notify(kPPPReasonDisconnect, empty_dict);
// First max_failure - 1 failures should not do anything; pppd will retry
// the connection.
for (int i = 1; i < max_failure(); ++i) {
EXPECT_EQ(service_->state(), previous_state);
service_->Notify(kPPPReasonDisconnect, empty_dict);
}
// Last auth failure should lead to the connection failing after pppd
// terminates itself.
OnPPPDied(0, 0);
EXPECT_NE(service_->state(), previous_state);
EXPECT_EQ(service_->state(), Service::kStateFailure);
EXPECT_EQ(service_->failure(), Service::kFailurePPPAuth);
}
TEST_F(PPPoEServiceTest, DisconnectBeforeConnect) {
EXPECT_CALL(*ethernet_, link_up()).WillRepeatedly(Return(true));
FakeConnectionSuccess();
std::map<std::string, std::string> empty_dict;
service_->Notify(kPPPReasonAuthenticating, empty_dict);
service_->Notify(kPPPReasonAuthenticated, empty_dict);
constexpr int kExitCode = 10;
service_->Notify(kPPPReasonDisconnect, empty_dict);
OnPPPDied(0, kExitCode);
EXPECT_EQ(service_->state(), Service::kStateFailure);
EXPECT_EQ(service_->failure(), PPPDevice::ExitStatusToFailure(kExitCode));
}
TEST_F(PPPoEServiceTest, ConnectFailsWhenEthernetLinkDown) {
EXPECT_CALL(*ethernet_, link_up()).WillRepeatedly(Return(false));
Error error;
service_->Connect(&error, "in test");
EXPECT_FALSE(error.IsSuccess());
}
TEST_F(PPPoEServiceTest, OnPPPConnected) {
static const char kLinkName[] = "ppp0";
std::map<std::string, std::string> params = {{kPPPInterfaceName, kLinkName}};
EXPECT_CALL(device_info_, GetIndex(StrEq(kLinkName))).WillOnce(Return(0));
EXPECT_CALL(device_info_, RegisterDevice(_));
#ifndef DISABLE_DHCPV6
// Lead to the creation of a mock rather than real DHCPConfig to avoid trying
// to create a dhcpcd process.
NiceMock<MockDHCPProvider> dhcp_provider;
EXPECT_CALL(dhcp_provider, CreateIPv6Config(_, _))
.WillOnce(
Return(new NiceMock<MockDHCPConfig>(&control_interface_, kLinkName)));
EXPECT_CALL(manager_, IsDHCPv6EnabledForDevice(StrEq(kLinkName)))
.WillOnce(WithoutArgs(Invoke([this, &dhcp_provider]() {
this->device()->set_dhcp_provider(&dhcp_provider);
return true;
})));
#endif // DISABLE_DHCPV6
EXPECT_CALL(manager_, OnInnerDevicesChanged());
service_->OnPPPConnected(params);
Mock::VerifyAndClearExpectations(&manager_);
// Note that crbug.com/1030324 precludes the ability of VirtualDevices to be
// enabled(). enabled_pending() suffices here.
EXPECT_TRUE(device()->enabled_pending());
EXPECT_EQ(device()->selected_service(), service_);
ASSERT_NE(device()->ipconfig(), nullptr);
EXPECT_FALSE(device()->ipconfig()->properties().blackhole_ipv6);
#ifndef DISABLE_DHCPV6
EXPECT_NE(device()->dhcpv6_config(), nullptr);
#endif // DISABLE_DHCPV6
}
TEST_F(PPPoEServiceTest, Connect) {
static const char kLinkName[] = "ppp0";
EXPECT_CALL(*ethernet_, link_up()).WillRepeatedly(Return(true));
EXPECT_CALL(device_info_, GetIndex(StrEq(kLinkName))).WillOnce(Return(0));
EXPECT_CALL(device_info_, RegisterDevice(LinkNamed(kLinkName)));
FakeConnectionSuccess();
std::map<std::string, std::string> empty_dict;
service_->Notify(kPPPReasonAuthenticating, empty_dict);
service_->Notify(kPPPReasonAuthenticated, empty_dict);
std::map<std::string, std::string> connect_dict = {
{kPPPInterfaceName, kLinkName},
};
service_->Notify(kPPPReasonConnect, connect_dict);
EXPECT_EQ(service_->state(), Service::kStateOnline);
}
TEST_F(PPPoEServiceTest, Disconnect) {
FakeConnectionSuccess();
auto weak_ptr = service_->weak_ptr_factory_.GetWeakPtr();
MockExternalTask* pppd =
new MockExternalTask(&control_interface_, &process_manager_, weak_ptr,
base::Bind(&PPPoEService::OnPPPDied, weak_ptr));
service_->pppd_.reset(pppd);
PPPDevice* ppp_device =
PPPDeviceFactory::GetInstance()->CreatePPPDevice(&manager_, "ppp0", 0);
service_->ppp_device_ = ppp_device;
ppp_device->SelectService(service_);
EXPECT_CALL(*pppd, OnDelete());
Error error;
service_->Disconnect(&error, "in test");
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(ppp_device->selected_service(), nullptr);
}
TEST_F(PPPoEServiceTest, DisconnectDuringAssociation) {
FakeConnectionSuccess();
Error error;
service_->Disconnect(&error, "in test");
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(service_->state(), Service::kStateIdle);
}
} // namespace shill