// Copyright (c) 2012 The Chromium 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 "chrome/browser/chromeos/mobile/mobile_activator.h" #include "base/message_loop/message_loop.h" #include "base/values.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/network/network_handler.h" #include "chromeos/network/network_state.h" #include "content/public/browser/browser_thread.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" using std::string; using content::BrowserThread; using testing::_; using testing::Eq; using testing::Invoke; using testing::Return; namespace { const char kTestServicePath[] = "/a/service/path"; const size_t kNumOTASPStates = 3; chromeos::MobileActivator::PlanActivationState kOTASPStates[kNumOTASPStates] = { chromeos::MobileActivator::PLAN_ACTIVATION_TRYING_OTASP, chromeos::MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION, chromeos::MobileActivator::PLAN_ACTIVATION_OTASP, }; } // namespace namespace chromeos { class TestMobileActivator : public MobileActivator { public: explicit TestMobileActivator(NetworkState* cellular_network) : cellular_network_(cellular_network) { // Provide reasonable defaults for basic things we're usually not testing. ON_CALL(*this, DCheckOnThread(_)) .WillByDefault(Return()); ON_CALL(*this, GetNetworkState(_)) .WillByDefault(Return(cellular_network_)); } virtual ~TestMobileActivator() {} MOCK_METHOD3(RequestCellularActivation, void(const NetworkState*, const base::Closure&, const network_handler::ErrorCallback&)); MOCK_METHOD3(ChangeState, void(const NetworkState*, MobileActivator::PlanActivationState, std::string)); MOCK_METHOD1(GetNetworkState, const NetworkState*(const std::string&)); MOCK_METHOD1(EvaluateCellularNetwork, void(const NetworkState*)); MOCK_METHOD0(SignalCellularPlanPayment, void(void)); MOCK_METHOD0(StartOTASPTimer, void(void)); MOCK_CONST_METHOD0(HasRecentCellularPlanPayment, bool(void)); void InvokeChangeState(const NetworkState* network, MobileActivator::PlanActivationState new_state, std::string error_description) { MobileActivator::ChangeState(network, new_state, error_description); } private: MOCK_CONST_METHOD1(DCheckOnThread, void(const BrowserThread::ID id)); NetworkState* cellular_network_; DISALLOW_COPY_AND_ASSIGN(TestMobileActivator); }; class MobileActivatorTest : public testing::Test { public: MobileActivatorTest() : cellular_network_(string(kTestServicePath)), mobile_activator_(&cellular_network_) { } virtual ~MobileActivatorTest() {} protected: virtual void SetUp() { DBusThreadManager::InitializeWithStub(); NetworkHandler::Initialize(); } virtual void TearDown() { NetworkHandler::Shutdown(); DBusThreadManager::Shutdown(); } void set_activator_state(const MobileActivator::PlanActivationState state) { mobile_activator_.state_ = state; } void set_network_activation_state(const std::string& activation_state) { cellular_network_.activation_state_ = activation_state; } void set_connection_state(const std::string& state) { cellular_network_.connection_state_ = state; } base::MessageLoop message_loop_; NetworkState cellular_network_; TestMobileActivator mobile_activator_; private: DISALLOW_COPY_AND_ASSIGN(MobileActivatorTest); }; TEST_F(MobileActivatorTest, BasicFlowForNewDevices) { // In a new device, we aren't connected to Verizon, we start at START // because we haven't paid Verizon (ever), and the modem isn't even partially // activated. std::string error_description; set_activator_state(MobileActivator::PLAN_ACTIVATION_START); set_connection_state(shill::kStateIdle); set_network_activation_state(shill::kActivationStateNotActivated); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION, mobile_activator_.PickNextState(&cellular_network_, &error_description)); // Now behave as if ChangeState() has initiated an activation. set_activator_state(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION); set_network_activation_state(shill::kActivationStateActivating); // We'll sit in this state while we wait for the OTASP to finish. EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION, mobile_activator_.PickNextState(&cellular_network_, &error_description)); set_network_activation_state(shill::kActivationStatePartiallyActivated); // We'll sit in this state until we go online as well. EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION, mobile_activator_.PickNextState(&cellular_network_, &error_description)); set_connection_state(shill::kStatePortal); // After we go online, we go back to START, which acts as a jumping off // point for the two types of initial OTASP. EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_START, mobile_activator_.PickNextState(&cellular_network_, &error_description)); set_activator_state(MobileActivator::PLAN_ACTIVATION_START); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP, mobile_activator_.PickNextState(&cellular_network_, &error_description)); // Very similar things happen while we're trying OTASP. set_activator_state(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP); set_network_activation_state(shill::kActivationStateActivating); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP, mobile_activator_.PickNextState(&cellular_network_, &error_description)); set_network_activation_state(shill::kActivationStatePartiallyActivated); set_connection_state(shill::kStatePortal); // And when we come back online again and aren't activating, load the portal. EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING, mobile_activator_.PickNextState(&cellular_network_, &error_description)); // The JS drives us through the payment portal. set_activator_state(MobileActivator::PLAN_ACTIVATION_SHOWING_PAYMENT); // The JS also calls us to signal that the portal is done. This triggers us // to start our final OTASP via the aptly named StartOTASP(). EXPECT_CALL(mobile_activator_, SignalCellularPlanPayment()); EXPECT_CALL(mobile_activator_, ChangeState(Eq(&cellular_network_), Eq(MobileActivator::PLAN_ACTIVATION_START_OTASP), _)); EXPECT_CALL(mobile_activator_, EvaluateCellularNetwork(Eq(&cellular_network_))); mobile_activator_.HandleSetTransactionStatus(true); // Evaluate state will defer to PickNextState to select what to do now that // we're in START_ACTIVATION. PickNextState should decide to start a final // OTASP. set_activator_state(MobileActivator::PLAN_ACTIVATION_START_OTASP); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_OTASP, mobile_activator_.PickNextState(&cellular_network_, &error_description)); // Similarly to TRYING_OTASP and INITIATING_OTASP above... set_activator_state(MobileActivator::PLAN_ACTIVATION_OTASP); set_network_activation_state(shill::kActivationStateActivating); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_OTASP, mobile_activator_.PickNextState(&cellular_network_, &error_description)); set_network_activation_state(shill::kActivationStateActivated); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_DONE, mobile_activator_.PickNextState(&cellular_network_, &error_description)); } // A fake for MobileActivator::RequestCellularActivation that always succeeds. void FakeRequestCellularActivationSuccess( const NetworkState* network, const base::Closure& success_callback, const network_handler::ErrorCallback& error_callback) { success_callback.Run(); } // A fake for MobileActivator::RequestCellularActivation that always fails. void FakeRequestCellularActivationFailure( const NetworkState* network, const base::Closure& success_callback, const network_handler::ErrorCallback& error_callback) { scoped_ptr value; error_callback.Run("", value.Pass()); } TEST_F(MobileActivatorTest, OTASPScheduling) { const std::string error; for (size_t i = 0; i < kNumOTASPStates; ++i) { // When activation works, we start a timer to watch for success. EXPECT_CALL(mobile_activator_, RequestCellularActivation(_, _, _)) .Times(1) .WillOnce(Invoke(FakeRequestCellularActivationSuccess)); EXPECT_CALL(mobile_activator_, StartOTASPTimer()) .Times(1); set_activator_state(MobileActivator::PLAN_ACTIVATION_START); mobile_activator_.InvokeChangeState(&cellular_network_, kOTASPStates[i], error); // When activation fails, it's an error, unless we're trying for the final // OTASP, in which case we try again via DELAY_OTASP. EXPECT_CALL(mobile_activator_, RequestCellularActivation(_, _, _)) .Times(1) .WillOnce(Invoke(FakeRequestCellularActivationFailure)); if (kOTASPStates[i] == MobileActivator::PLAN_ACTIVATION_OTASP) { EXPECT_CALL(mobile_activator_, ChangeState( Eq(&cellular_network_), Eq(MobileActivator::PLAN_ACTIVATION_DELAY_OTASP), _)); } else { EXPECT_CALL(mobile_activator_, ChangeState( Eq(&cellular_network_), Eq(MobileActivator::PLAN_ACTIVATION_ERROR), _)); } set_activator_state(MobileActivator::PLAN_ACTIVATION_START); mobile_activator_.InvokeChangeState(&cellular_network_, kOTASPStates[i], error); } } TEST_F(MobileActivatorTest, ReconnectOnDisconnectFromPaymentPortal) { // Most states either don't care if we're offline or expect to be offline at // some point. For instance the OTASP states expect to go offline during // activation and eventually come back. There are a few transitions states // like START_OTASP and DELAY_OTASP which don't really depend on the state of // the modem (offline or online) to work correctly. A few places however, // like when we're displaying the portal care quite a bit about going // offline. Lets test for those cases. std::string error_description; set_connection_state(shill::kStateFailure); set_network_activation_state(shill::kActivationStatePartiallyActivated); set_activator_state(MobileActivator::PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_RECONNECTING, mobile_activator_.PickNextState(&cellular_network_, &error_description)); set_activator_state(MobileActivator::PLAN_ACTIVATION_SHOWING_PAYMENT); EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_RECONNECTING, mobile_activator_.PickNextState(&cellular_network_, &error_description)); } TEST_F(MobileActivatorTest, StartAtStart) { EXPECT_CALL(mobile_activator_, HasRecentCellularPlanPayment()) .WillOnce(Return(false)); EXPECT_CALL(mobile_activator_, EvaluateCellularNetwork(Eq(&cellular_network_))); mobile_activator_.StartActivation(); EXPECT_EQ(mobile_activator_.state(), MobileActivator::PLAN_ACTIVATION_START); } } // namespace chromeos