# 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()