| # 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. |
| |
| import dbus |
| import logging |
| import os |
| import time |
| |
| from autotest_lib.client.bin import test |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.cros.cellular import mm1_constants |
| from autotest_lib.client.cros.cellular import test_environment |
| from autotest_lib.client.cros.networking import pm_proxy |
| |
| I_ACTIVATION_TEST = 'Interface.LTEActivationTest' |
| TEST_MODEMS_MODULE_PATH = os.path.join(os.path.dirname(__file__), 'files', |
| 'modems.py') |
| |
| LONG_TIMEOUT = 20 |
| SHORT_TIMEOUT = 10 |
| |
| class ActivationTest(object): |
| """ |
| Super class that implements setup code that is common to the individual |
| tests. |
| |
| """ |
| def __init__(self, test): |
| self.test = test |
| |
| |
| def Cleanup(self): |
| """ |
| Makes the modem look like it has been activated to satisfy the test |
| end condition. |
| |
| """ |
| # Set the MDN to a non-zero value, so that shill removes the ICCID from |
| # activating_iccid_store.profile. This way, individual test runs won't |
| # interfere with each other. |
| modem = self.test.pseudomm.wait_for_modem(timeout_seconds=LONG_TIMEOUT) |
| modem.iface_properties.Set(mm1_constants.I_MODEM, |
| 'OwnNumbers', |
| ['1111111111']) |
| # Put the modem in the unknown subscription state so that the mdn value is |
| # used to remove the iccid entry |
| self.test.pseudomm.iface_testing.SetSubscriptionState( |
| mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN, |
| mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN) |
| time.sleep(5) |
| self.test.CheckServiceActivationState('activated') |
| |
| |
| def Run(self): |
| """ |
| Configures the pseudomodem to run with the test modem, runs the test |
| and cleans up. |
| |
| """ |
| self.RunTest() |
| self.Cleanup() |
| |
| |
| def TestModemClass(self): |
| """ Returns the name of the custom modem to use for this test. """ |
| raise NotImplementedError() |
| |
| |
| def RunTest(self): |
| """ |
| Runs the body of the test. Should be implemented by the subclass. |
| |
| """ |
| raise NotImplementedError() |
| |
| |
| class ActivationResetTest(ActivationTest): |
| """ |
| This test verifies that the modem resets after online payment. |
| |
| """ |
| def TestModemClass(self): |
| return 'TestModem' |
| |
| |
| def RunTest(self): |
| # Service should appear as 'not-activated'. |
| self.test.CheckServiceActivationState('not-activated') |
| self.test.CheckResetCalled(False) |
| |
| # Call 'CompleteActivation' on the device. The service will become |
| # 'activating' and the modem should reset immediately. |
| # Not checking for the intermediate 'activating' state because it makes |
| # the test too fragile |
| service = self.test.FindCellularService() |
| service.CompleteCellularActivation() |
| time.sleep(SHORT_TIMEOUT) |
| self.test.CheckResetCalled(True) |
| |
| |
| class ActivationCompleteTest(ActivationTest): |
| """ |
| This test verifies that the service eventually becomes 'activated' in the |
| case of a post-payment registration and the modem finally registers |
| to a network after a reset. |
| |
| """ |
| def TestModemClass(self): |
| return 'ResetRequiredForActivationModem' |
| |
| |
| def RunTest(self): |
| # Service should appear as 'not-activated'. |
| self.test.CheckServiceActivationState('not-activated') |
| self.test.CheckResetCalled(False) |
| |
| # Call 'CompleteActivation' on the device. The service will become |
| # 'activating' and the modem should reset immediately. |
| # Not checking for the intermediate 'activating' state because it makes |
| # the test too fragile |
| service = self.test.FindCellularService() |
| service.CompleteCellularActivation() |
| time.sleep(SHORT_TIMEOUT) |
| self.test.CheckResetCalled(True) |
| |
| # The service should register and be marked as 'activated'. |
| self.test.CheckServiceActivationState('activated') |
| |
| |
| class ActivationDueToMdnTest(ActivationTest): |
| """ |
| This test verifies that a valid MDN should cause the service to get marked |
| as 'activated' when the modem is in unknown subscription state. |
| |
| """ |
| def TestModemClass(self): |
| return 'TestModem' |
| |
| |
| def RunTest(self): |
| # Service should appear as 'not-activated'. |
| self.test.CheckServiceActivationState('not-activated') |
| |
| # Update the MDN. The service should get marked as activated. |
| modem = self.test.pseudomm.get_modem() |
| modem.iface_properties.Set(mm1_constants.I_MODEM, |
| 'OwnNumbers', |
| ['1111111111']) |
| # Put the modem in the unknown subscription state so that the mdn value is |
| # used to determine the service activation status. |
| self.test.pseudomm.iface_testing.SetSubscriptionState( |
| mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN, |
| mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN) |
| time.sleep(SHORT_TIMEOUT) |
| self.test.CheckServiceActivationState('activated') |
| |
| |
| class cellular_ActivateLTE(test.test): |
| """ |
| After an online payment to activate a network, shill keeps track of service |
| activation by monitoring changes to network registration and MDN updates |
| combined with a modem reset. The test checks that the |
| Cellular.ActivationState property of the service has the correct value |
| associated with it by simulating possible scenarios using the pseudo modem |
| manager. |
| |
| """ |
| version = 1 |
| |
| def GetModemState(self): |
| """Returns the current ModemManager modem state.""" |
| modem = self.pseudomm.get_modem() |
| props = modem.properties(mm1_constants.I_MODEM) |
| return props['State'] |
| |
| |
| def SetResetCalled(self, value): |
| """ |
| Sets the value of the "ResetCalled" property of the current |
| modem. |
| |
| @param value: Value to set in the property. |
| |
| """ |
| modem = self.pseudomm.get_modem() |
| if modem is None: |
| return |
| modem.iface_properties.Set( |
| I_ACTIVATION_TEST, |
| 'ResetCalled', |
| dbus.types.Boolean(value)) |
| |
| |
| def GetResetCalled(self, modem): |
| """ |
| Returns the current value of the "ResetCalled" property of the current |
| modem. |
| |
| @param modem: Modem proxy to send the query to. |
| |
| """ |
| return modem.properties(I_ACTIVATION_TEST)['ResetCalled'] |
| |
| |
| def _CheckResetCalledHelper(self, expected_value): |
| modem = self.pseudomm.get_modem() |
| if modem is None: |
| return False |
| try: |
| return self.GetResetCalled(modem) == expected_value |
| except dbus.exceptions.DBusException as e: |
| name = e.get_dbus_name() |
| if (name == mm1_constants.DBUS_UNKNOWN_METHOD or |
| name == mm1_constants.DBUS_UNKNOWN_OBJECT): |
| return False |
| raise e |
| |
| |
| def CheckResetCalled(self, expected_value): |
| """ |
| Checks that the ResetCalled property on the modem matches the expect |
| value. |
| |
| @param expected_value: The expected value of ResetCalled. |
| |
| """ |
| utils.poll_for_condition( |
| lambda: self._CheckResetCalledHelper(expected_value), |
| exception=error.TestFail("\"ResetCalled\" did not match: " + |
| str(expected_value)), |
| timeout=LONG_TIMEOUT) |
| |
| |
| def EnsureModemStateReached(self, expected_state, timeout): |
| """ |
| Asserts that the underlying modem state becomes |expected_state| within |
| |timeout|. |
| |
| @param expected_state: The expected modem state. |
| @param timeout: Timeout in which the condition should be met. |
| |
| """ |
| utils.poll_for_condition( |
| lambda: self.GetModemState() == expected_state, |
| exception=error.TestFail( |
| 'Modem failed to reach state ' + |
| mm1_constants.ModemStateToString(expected_state)), |
| timeout=timeout) |
| |
| |
| def CheckServiceActivationState(self, expected_state): |
| """ |
| Asserts that the service activation state matches |expected_state| |
| within SHORT_TIMEOUT. |
| |
| @param expected_state: The expected service activation state. |
| |
| """ |
| logging.info('Checking for service activation state: %s', |
| expected_state) |
| service = self.FindCellularService() |
| success, state, duration = self.test_env.shill.wait_for_property_in( |
| service, |
| 'Cellular.ActivationState', |
| [expected_state], |
| SHORT_TIMEOUT) |
| if not success and state != expected_state: |
| raise error.TestError( |
| 'Service activation state should be \'%s\', but it is \'%s\'.' |
| % (expected_state, state)) |
| |
| |
| def FindCellularService(self, check_not_none=True): |
| """ |
| Returns the current cellular service. |
| |
| @param check_not_none: If True, an error will be raised if no service |
| was found. |
| |
| """ |
| if check_not_none: |
| utils.poll_for_condition( |
| lambda: (self.test_env.shill.find_cellular_service_object() |
| is not None), |
| exception=error.TestError( |
| 'Could not find cellular service within timeout.'), |
| timeout=LONG_TIMEOUT); |
| |
| service = self.test_env.shill.find_cellular_service_object() |
| |
| # Check once more, to make sure it's valid. |
| if check_not_none and not service: |
| raise error.TestError('Could not find cellular service.') |
| return service |
| |
| |
| def FindCellularDevice(self): |
| """Returns the current cellular device.""" |
| device = self.test_env.shill.find_cellular_device_object() |
| if not device: |
| raise error.TestError('Could not find cellular device.') |
| return device |
| |
| |
| def ResetCellularDevice(self): |
| """ |
| Resets all modems, guaranteeing that the operation succeeds and doesn't |
| fail due to race conditions in pseudomodem start-up and test execution. |
| |
| """ |
| self.EnsureModemStateReached( |
| mm1_constants.MM_MODEM_STATE_ENABLED, SHORT_TIMEOUT) |
| self.test_env.shill.reset_modem(self.FindCellularDevice()) |
| self.EnsureModemStateReached( |
| mm1_constants.MM_MODEM_STATE_ENABLED, SHORT_TIMEOUT) |
| |
| |
| def run_once(self): |
| tests = [ |
| ActivationResetTest(self), |
| ActivationCompleteTest(self), |
| ActivationDueToMdnTest(self), |
| ] |
| |
| for test in tests: |
| self.test_env = test_environment.CellularPseudoMMTestEnvironment( |
| pseudomm_args = ({'family' : '3GPP', |
| 'test-module' : TEST_MODEMS_MODULE_PATH, |
| 'test-modem-class' : test.TestModemClass(), |
| 'test-sim-class' : 'TestSIM'},)) |
| with self.test_env: |
| self.pseudomm = pm_proxy.PseudoMMProxy.get_proxy() |
| # Set the reset flag to False explicitly before each test |
| # sequence starts to ignore the reset as a part of the test init |
| self.SetResetCalled(False) |
| test.Run() |