//
// 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/cellular/cellular_service.h"
#if defined(__ANDROID__)
#include <dbus/service_constants.h>
#else
#include <chromeos/dbus/service_constants.h>
#endif // __ANDROID__
#include <gtest/gtest.h>
#include <mm/mm-modem.h>
#include "shill/cellular/cellular_capability.h"
#include "shill/cellular/cellular_capability_cdma.h"
#include "shill/cellular/mock_cellular.h"
#include "shill/cellular/mock_modem_info.h"
#include "shill/cellular/mock_out_of_credits_detector.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_profile.h"
#include "shill/mock_store.h"
#include "shill/nice_mock_control.h"
#include "shill/service_property_change_test.h"
using std::string;
using testing::_;
using testing::InSequence;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::SetArgumentPointee;
namespace shill {
class CellularServiceTest : public testing::Test {
public:
CellularServiceTest()
: modem_info_(nullptr, &dispatcher_, nullptr, nullptr),
device_(new MockCellular(&modem_info_,
"usb0",
kAddress,
3,
Cellular::kTypeCDMA,
"",
"")),
service_(new CellularService(&modem_info_, device_)),
adaptor_(nullptr) {}
virtual ~CellularServiceTest() {
adaptor_ = nullptr;
}
virtual void SetUp() {
adaptor_ =
static_cast<ServiceMockAdaptor*>(service_->adaptor());
out_of_credits_detector_ =
new MockOutOfCreditsDetector(nullptr, nullptr, nullptr, service_.get());
// Passes ownership.
service_->set_out_of_credits_detector(out_of_credits_detector_);
}
CellularCapabilityCDMA* GetCapabilityCDMA() {
return static_cast<CellularCapabilityCDMA*>(device_->capability_.get());
}
protected:
static const char kAddress[];
string GetFriendlyName() const { return service_->friendly_name(); }
EventDispatcher dispatcher_;
MockModemInfo modem_info_;
scoped_refptr<MockCellular> device_;
CellularServiceRefPtr service_;
ServiceMockAdaptor* adaptor_; // Owned by |service_|.
MockOutOfCreditsDetector* out_of_credits_detector_; // Owned by |service_|.
};
const char CellularServiceTest::kAddress[] = "000102030405";
TEST_F(CellularServiceTest, Constructor) {
EXPECT_TRUE(service_->connectable());
}
TEST_F(CellularServiceTest, SetActivationState) {
{
InSequence call_sequence;
EXPECT_CALL(*adaptor_, EmitStringChanged(
kActivationStateProperty,
kActivationStateNotActivated));
EXPECT_CALL(*adaptor_, EmitBoolChanged(
kConnectableProperty, false));
EXPECT_CALL(*adaptor_, EmitStringChanged(
kActivationStateProperty,
kActivationStateActivating));
EXPECT_CALL(*adaptor_, EmitBoolChanged(
kConnectableProperty, true));
EXPECT_CALL(*adaptor_, EmitStringChanged(
kActivationStateProperty,
kActivationStatePartiallyActivated));
EXPECT_CALL(*adaptor_, EmitStringChanged(
kActivationStateProperty,
kActivationStateActivated));
EXPECT_CALL(*adaptor_, EmitStringChanged(
kActivationStateProperty,
kActivationStateNotActivated));
EXPECT_CALL(*adaptor_, EmitBoolChanged(
kConnectableProperty, false));
}
EXPECT_CALL(*modem_info_.mock_manager(), HasService(_))
.WillRepeatedly(Return(false));
EXPECT_TRUE(service_->activation_state().empty());
EXPECT_TRUE(service_->connectable());
service_->SetActivationState(kActivationStateNotActivated);
EXPECT_EQ(kActivationStateNotActivated, service_->activation_state());
EXPECT_FALSE(service_->connectable());
service_->SetActivationState(kActivationStateActivating);
EXPECT_EQ(kActivationStateActivating, service_->activation_state());
EXPECT_TRUE(service_->connectable());
service_->SetActivationState(kActivationStatePartiallyActivated);
EXPECT_EQ(kActivationStatePartiallyActivated, service_->activation_state());
EXPECT_TRUE(service_->connectable());
service_->SetActivationState(kActivationStateActivated);
EXPECT_EQ(kActivationStateActivated, service_->activation_state());
EXPECT_TRUE(service_->connectable());
service_->SetActivationState(kActivationStateNotActivated);
EXPECT_EQ(kActivationStateNotActivated, service_->activation_state());
EXPECT_FALSE(service_->connectable());
}
TEST_F(CellularServiceTest, SetNetworkTechnology) {
EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty,
kNetworkTechnologyUmts));
EXPECT_TRUE(service_->network_technology().empty());
service_->SetNetworkTechnology(kNetworkTechnologyUmts);
EXPECT_EQ(kNetworkTechnologyUmts, service_->network_technology());
service_->SetNetworkTechnology(kNetworkTechnologyUmts);
}
TEST_F(CellularServiceTest, SetRoamingState) {
EXPECT_CALL(*adaptor_, EmitStringChanged(kRoamingStateProperty,
kRoamingStateHome));
EXPECT_TRUE(service_->roaming_state().empty());
service_->SetRoamingState(kRoamingStateHome);
EXPECT_EQ(kRoamingStateHome, service_->roaming_state());
service_->SetRoamingState(kRoamingStateHome);
}
TEST_F(CellularServiceTest, SetStorageIdentifier) {
EXPECT_EQ(string(kTypeCellular) + "_" +
kAddress + "_" + GetFriendlyName(),
service_->GetStorageIdentifier());
service_->SetStorageIdentifier("a b c");
EXPECT_EQ("a_b_c", service_->GetStorageIdentifier());
}
TEST_F(CellularServiceTest, SetServingOperator) {
static const char kCode[] = "123456";
static const char kName[] = "Some Cellular Operator";
Stringmap test_operator;
service_->set_serving_operator(test_operator);
test_operator[kOperatorCodeKey] = kCode;
test_operator[kOperatorNameKey] = kName;
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kServingOperatorProperty, _));
service_->set_serving_operator(test_operator);
const Stringmap& serving_operator = service_->serving_operator();
ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorCodeKey));
ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorNameKey));
EXPECT_EQ(kCode, serving_operator.find(kOperatorCodeKey)->second);
EXPECT_EQ(kName, serving_operator.find(kOperatorNameKey)->second);
Mock::VerifyAndClearExpectations(adaptor_);
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kServingOperatorProperty, _)).Times(0);
service_->set_serving_operator(serving_operator);
}
TEST_F(CellularServiceTest, SetOLP) {
const char kMethod[] = "GET";
const char kURL[] = "payment.url";
const char kPostData[] = "post_man";
Stringmap olp;
service_->SetOLP("", "", "");
olp = service_->olp(); // Copy to simplify assertions below.
EXPECT_EQ("", olp[kPaymentPortalURL]);
EXPECT_EQ("", olp[kPaymentPortalMethod]);
EXPECT_EQ("", olp[kPaymentPortalPostData]);
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kPaymentPortalProperty, _));
service_->SetOLP(kURL, kMethod, kPostData);
olp = service_->olp(); // Copy to simplify assertions below.
EXPECT_EQ(kURL, olp[kPaymentPortalURL]);
EXPECT_EQ(kMethod, olp[kPaymentPortalMethod]);
EXPECT_EQ(kPostData, olp[kPaymentPortalPostData]);
}
TEST_F(CellularServiceTest, SetUsageURL) {
static const char kUsageURL[] = "usage.url";
EXPECT_CALL(*adaptor_, EmitStringChanged(kUsageURLProperty,
kUsageURL));
EXPECT_TRUE(service_->usage_url().empty());
service_->SetUsageURL(kUsageURL);
EXPECT_EQ(kUsageURL, service_->usage_url());
service_->SetUsageURL(kUsageURL);
}
TEST_F(CellularServiceTest, SetApn) {
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
ProfileRefPtr profile(new NiceMock<MockProfile>(
modem_info_.control_interface(), modem_info_.metrics(),
modem_info_.manager()));
service_->set_profile(profile);
Error error;
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
{
InSequence seq;
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularLastGoodApnProperty,
_));
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularApnProperty, _));
}
service_->SetApn(testapn, &error);
EXPECT_TRUE(error.IsSuccess());
Stringmap resultapn = service_->GetApn(&error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(2, resultapn.size());
Stringmap::const_iterator it = resultapn.find(kApnProperty);
EXPECT_TRUE(it != resultapn.end() && it->second == kApn);
it = resultapn.find(kApnUsernameProperty);
EXPECT_TRUE(it != resultapn.end() && it->second == kUsername);
EXPECT_NE(nullptr, service_->GetUserSpecifiedApn());
}
TEST_F(CellularServiceTest, ClearApn) {
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
ProfileRefPtr profile(new NiceMock<MockProfile>(
modem_info_.control_interface(), modem_info_.metrics(),
modem_info_.manager()));
service_->set_profile(profile);
Error error;
// Set up an APN to make sure that it later gets cleared.
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
{
InSequence seq;
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularLastGoodApnProperty,
_));
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularApnProperty, _));
}
service_->SetApn(testapn, &error);
Stringmap resultapn = service_->GetApn(&error);
ASSERT_TRUE(error.IsSuccess());
ASSERT_EQ(2, service_->GetApn(&error).size());
Stringmap emptyapn;
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularLastGoodApnProperty,
_)).Times(0);
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularApnProperty, _)).Times(1);
service_->SetApn(emptyapn, &error);
EXPECT_TRUE(error.IsSuccess());
resultapn = service_->GetApn(&error);
EXPECT_TRUE(resultapn.empty());
EXPECT_EQ(nullptr, service_->GetUserSpecifiedApn());;
}
TEST_F(CellularServiceTest, LastGoodApn) {
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
ProfileRefPtr profile(new NiceMock<MockProfile>(
modem_info_.control_interface(), modem_info_.metrics(),
modem_info_.manager()));
service_->set_profile(profile);
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularLastGoodApnProperty, _));
service_->SetLastGoodApn(testapn);
Stringmap* resultapn = service_->GetLastGoodApn();
EXPECT_NE(nullptr, resultapn);
EXPECT_EQ(2, resultapn->size());
Stringmap::const_iterator it = resultapn->find(kApnProperty);
EXPECT_TRUE(it != resultapn->end() && it->second == kApn);
it = resultapn->find(kApnUsernameProperty);
EXPECT_TRUE(it != resultapn->end() && it->second == kUsername);
// Now set the user-specified APN, and check that LastGoodApn got
// cleared.
Stringmap userapn;
userapn[kApnProperty] = kApn;
userapn[kApnUsernameProperty] = kUsername;
{
InSequence seq;
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularLastGoodApnProperty,
_));
EXPECT_CALL(*adaptor_,
EmitStringmapChanged(kCellularApnProperty, _));
}
Error error;
service_->SetApn(userapn, &error);
EXPECT_EQ(nullptr, service_->GetLastGoodApn());;
}
TEST_F(CellularServiceTest, IsAutoConnectable) {
const char* reason = nullptr;
ON_CALL(*out_of_credits_detector_, IsDetecting())
.WillByDefault(Return(false));
// Auto-connect should be suppressed if the device is not running.
device_->running_ = false;
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnDeviceDisabled, reason);
device_->running_ = true;
// If we're waiting on a disconnect before an activation, don't auto-connect.
GetCapabilityCDMA()->activation_starting_ = true;
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
// If we're waiting on an activation, also don't auto-connect.
GetCapabilityCDMA()->activation_starting_ = false;
GetCapabilityCDMA()->activation_state_ =
MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
GetCapabilityCDMA()->activation_state_ =
MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
// Auto-connect should be suppressed if the we're undergoing an
// out-of-credits detection.
EXPECT_CALL(*out_of_credits_detector_, IsDetecting())
.WillOnce(Return(true));
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnOutOfCreditsDetectionInProgress,
reason);
Mock::VerifyAndClearExpectations(out_of_credits_detector_);
// Auto-connect should be suppressed if we're out of credits.
EXPECT_CALL(*out_of_credits_detector_, IsDetecting())
.WillOnce(Return(false));
EXPECT_CALL(*out_of_credits_detector_, out_of_credits())
.WillOnce(Return(true));
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnOutOfCredits, reason);
Mock::VerifyAndClearExpectations(out_of_credits_detector_);
EXPECT_CALL(*out_of_credits_detector_, out_of_credits())
.WillRepeatedly(Return(false));
// But other activation states are fine.
GetCapabilityCDMA()->activation_state_ =
MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
GetCapabilityCDMA()->activation_state_ =
MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
GetCapabilityCDMA()->activation_state_ =
MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED;
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
// A PPP authentication failure means the Service is not auto-connectable.
service_->SetFailure(Service::kFailurePPPAuth);
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
// Reset failure state, to make the Service auto-connectable again.
service_->SetState(Service::kStateIdle);
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
// The following test cases are copied from ServiceTest.IsAutoConnectable
service_->SetConnectable(true);
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
// We should not auto-connect to a Service that a user has
// deliberately disconnected.
Error error;
service_->UserInitiatedDisconnect(&error);
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason);
// But if the Service is reloaded, it is eligible for auto-connect
// again.
NiceMock<MockStore> storage;
EXPECT_CALL(storage, ContainsGroup(service_->GetStorageIdentifier()))
.WillOnce(Return(true));
EXPECT_TRUE(service_->Load(&storage));
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
// A non-user initiated Disconnect doesn't change anything.
service_->Disconnect(&error, "in test");
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
// A resume also re-enables auto-connect.
service_->UserInitiatedDisconnect(&error);
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
service_->OnAfterResume();
EXPECT_TRUE(service_->IsAutoConnectable(&reason));
service_->SetState(Service::kStateConnected);
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnConnected, reason);
service_->SetState(Service::kStateAssociating);
EXPECT_FALSE(service_->IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnConnecting, reason);
}
TEST_F(CellularServiceTest, LoadResetsPPPAuthFailure) {
NiceMock<MockStore> storage;
EXPECT_CALL(storage, ContainsGroup(_)).WillRepeatedly(Return(true));
EXPECT_CALL(storage, GetString(_, _, _)).WillRepeatedly(Return(true));
const string kDefaultUser;
const string kDefaultPass;
const string kNewUser("new-username");
const string kNewPass("new-password");
for (const auto change_username : { false, true }) {
for (const auto change_password : { false, true }) {
service_->ppp_username_ = kDefaultUser;
service_->ppp_password_ = kDefaultPass;
service_->SetFailure(Service::kFailurePPPAuth);
EXPECT_TRUE(service_->IsFailed());
EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
if (change_username) {
EXPECT_CALL(storage,
GetString(_, CellularService::kStoragePPPUsername, _))
.WillOnce(DoAll(SetArgumentPointee<2>(kNewUser), Return(true)))
.RetiresOnSaturation();
}
if (change_password) {
EXPECT_CALL(storage,
GetString(_, CellularService::kStoragePPPPassword, _))
.WillOnce(DoAll(SetArgumentPointee<2>(kNewPass), Return(true)))
.RetiresOnSaturation();
}
EXPECT_TRUE(service_->Load(&storage));
if (change_username || change_password) {
EXPECT_NE(Service::kFailurePPPAuth, service_->failure());
} else {
EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
}
}
}
}
// Some of these tests duplicate signals tested above. However, it's
// convenient to have all the property change notifications documented
// (and tested) in one place.
TEST_F(CellularServiceTest, PropertyChanges) {
TestCommonPropertyChanges(service_, adaptor_);
TestAutoConnectPropertyChange(service_, adaptor_);
EXPECT_CALL(*adaptor_,
EmitStringChanged(kActivationTypeProperty, _));
service_->SetActivationType(CellularService::kActivationTypeOTA);
Mock::VerifyAndClearExpectations(adaptor_);
EXPECT_NE(kActivationStateNotActivated, service_->activation_state());
EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty, _));
service_->SetActivationState(kActivationStateNotActivated);
Mock::VerifyAndClearExpectations(adaptor_);
string network_technology = service_->network_technology();
EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty, _));
service_->SetNetworkTechnology(network_technology + "and some new stuff");
Mock::VerifyAndClearExpectations(adaptor_);
bool out_of_credits = true;
EXPECT_CALL(*adaptor_,
EmitBoolChanged(kOutOfCreditsProperty, out_of_credits));
service_->SignalOutOfCreditsChanged(out_of_credits);
Mock::VerifyAndClearExpectations(adaptor_);
string roaming_state = service_->roaming_state();
EXPECT_CALL(*adaptor_, EmitStringChanged(kRoamingStateProperty, _));
service_->SetRoamingState(roaming_state + "and some new stuff");
Mock::VerifyAndClearExpectations(adaptor_);
}
// Custom property setters should return false, and make no changes, if
// the new value is the same as the old value.
TEST_F(CellularServiceTest, CustomSetterNoopChange) {
// Test that we didn't break any setters provided by the base class.
TestCustomSetterNoopChange(service_, modem_info_.mock_manager());
// Test the new setter we added.
// First set up our environment...
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
Error error;
Stringmap testapn;
ProfileRefPtr profile(new NiceMock<MockProfile>(nullptr, nullptr, nullptr));
service_->set_profile(profile);
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
// ... then set to a known value ...
EXPECT_TRUE(service_->SetApn(testapn, &error));
EXPECT_TRUE(error.IsSuccess());
// ... then set to same value.
EXPECT_FALSE(service_->SetApn(testapn, &error));
EXPECT_TRUE(error.IsSuccess());
}
} // namespace shill