| // 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<base::DictionaryValue> 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 |