You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
9.7 KiB
283 lines
9.7 KiB
# Copyright (c) 2012 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 gobject
|
|
import logging
|
|
import time
|
|
|
|
from autotest_lib.client.bin import test
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.cros.cellular import modem_utils
|
|
from autotest_lib.client.cros.mainloop import ExceptionForward
|
|
from autotest_lib.client.cros.mainloop import GenericTesterMainLoop
|
|
from autotest_lib.client.cros.networking import shill_proxy
|
|
|
|
DEFAULT_TEST_TIMEOUT_S = 600
|
|
|
|
|
|
class DisableTester(GenericTesterMainLoop):
|
|
"""Base class containing main test logic."""
|
|
def __init__(self, *args, **kwargs):
|
|
super(DisableTester, self).__init__(*args, **kwargs)
|
|
|
|
@ExceptionForward
|
|
def perform_one_test(self):
|
|
"""Called by GenericMainTesterMainLoop to execute the test."""
|
|
self._configure()
|
|
disable_delay_ms = (
|
|
self.test_kwargs.get('delay_before_disable_ms', 0) +
|
|
self.test.iteration *
|
|
self.test_kwargs.get('disable_delay_per_iteration_ms', 0))
|
|
gobject.timeout_add(disable_delay_ms, self._start_disable)
|
|
self._start_test()
|
|
|
|
@ExceptionForward
|
|
def _connect_success_handler(self, *ignored_args):
|
|
logging.info('connect succeeded')
|
|
self.requirement_completed('connect')
|
|
|
|
@ExceptionForward
|
|
def _connect_error_handler(self, e):
|
|
# We disabled while connecting; error is OK
|
|
logging.info('connect errored: %s', e)
|
|
self.requirement_completed('connect')
|
|
|
|
@ExceptionForward
|
|
def _start_disable(self):
|
|
logging.info('disabling')
|
|
self.disable_start = time.time()
|
|
self._enable(False)
|
|
|
|
@ExceptionForward
|
|
def _disable_success_handler(self):
|
|
disable_elapsed = time.time() - self.disable_start
|
|
self.requirement_completed('disable')
|
|
|
|
@ExceptionForward
|
|
def _get_status_success_handler(self, status):
|
|
logging.info('Got status')
|
|
self.requirement_completed('get_status', warn_if_already_completed=False)
|
|
if self.status_delay_ms:
|
|
gobject.timeout_add(self.status_delay_ms, self._start_get_status)
|
|
|
|
def after_main_loop(self):
|
|
"""Called by GenericTesterMainLoop after the main loop has exited."""
|
|
enabled = self._enabled()
|
|
logging.info('Modem enabled: %s', enabled)
|
|
# Will return happily if no Gobi present
|
|
modem_utils.ClearGobiModemFaultInjection()
|
|
|
|
|
|
class ShillDisableTester(DisableTester):
|
|
"""Tests that disable-while-connecting works at the shill level.
|
|
Expected control flow:
|
|
|
|
* self._configure() called; registers self._disable_property_changed
|
|
to be called when device is en/disabled
|
|
|
|
* Parent class sets a timer that calls self._enable(False) when it expires.
|
|
|
|
* _start_test calls _start_connect() which sends a connect request to
|
|
the device.
|
|
|
|
* we wait for the modem to power off, at which point
|
|
_disable_property_changed (registered above) will get called
|
|
|
|
* _disable_property_changed() completes the 'disable' requirement,
|
|
and we're done.
|
|
|
|
"""
|
|
def __init__(self, *args, **kwargs):
|
|
super(ShillDisableTester, self).__init__(*args, **kwargs)
|
|
|
|
def _disable_property_changed(self, property, value, *args, **kwargs):
|
|
self._disable_success_handler()
|
|
|
|
def _start_test(self):
|
|
# We would love to add requirements based on connect, but in many
|
|
# scenarios, there is no observable response to a cancelled
|
|
# connect: We issue a connect, it returns instantly to let us know
|
|
# that the connect has started, but then the disable takes effect
|
|
# and the connect fails. We don't get a state change because no
|
|
# state change has happened: the modem never got to a different
|
|
# state before we cancelled
|
|
self.remaining_requirements = set(['disable'])
|
|
self._start_connect()
|
|
|
|
def _configure(self):
|
|
self.cellular_device = \
|
|
self.test.test_env.shill.find_cellular_device_object()
|
|
if self.cellular_device is None:
|
|
raise error.TestError("Could not find cellular device")
|
|
|
|
self.cellular_service = \
|
|
self.test.test_env.shill.find_cellular_service_object()
|
|
|
|
self.test.test_env.bus.add_signal_receiver(
|
|
self.dispatch_property_changed,
|
|
signal_name='PropertyChanged',
|
|
dbus_interface=self.cellular_device.dbus_interface,
|
|
path=self.cellular_device.object_path)
|
|
|
|
@ExceptionForward
|
|
def _expect_einprogress_handler(self, e):
|
|
pass
|
|
|
|
def _enable(self, value):
|
|
self.property_changed_actions['Powered'] = self._disable_property_changed
|
|
|
|
if value:
|
|
self.cellular_device.Enable(
|
|
reply_handler=self.ignore_handler,
|
|
error_handler=self._expect_einprogress_handler)
|
|
else:
|
|
self.cellular_device.Disable(
|
|
reply_handler=self.ignore_handler,
|
|
error_handler=self._expect_einprogress_handler)
|
|
|
|
@ExceptionForward
|
|
def _start_connect(self):
|
|
logging.info('connecting')
|
|
|
|
def _log_connect_event(property, value, *ignored_args):
|
|
logging.info('%s property changed: %s', property, value)
|
|
|
|
self.property_changed_actions['Connected'] = _log_connect_event
|
|
|
|
# Contrary to documentation, Connect just returns when it has
|
|
# fired off the lower-level dbus messages. So a success means
|
|
# nothing to us. But a failure means it didn't even try.
|
|
self.cellular_service.Connect(
|
|
reply_handler=self.ignore_handler,
|
|
error_handler=self.build_error_handler('Connect'))
|
|
|
|
def _enabled(self):
|
|
return self.test.test_env.shill.get_dbus_property(
|
|
self.cellular_device,
|
|
shill_proxy.ShillProxy.DEVICE_PROPERTY_POWERED)
|
|
|
|
|
|
class ModemDisableTester(DisableTester):
|
|
"""Tests that disable-while-connecting works at the modem-manager level.
|
|
|
|
Expected control flow:
|
|
|
|
* _configure() is called.
|
|
|
|
* Parent class sets a timer that calls self._enable(False) when it
|
|
expires.
|
|
|
|
* _start_test calls _start_connect() which sends a connect request to
|
|
the device, also sets a timer that calls GetStatus on the modem.
|
|
|
|
* wait for all three (connect, disable, get_status) to complete.
|
|
|
|
"""
|
|
def __init__(self, *args, **kwargs):
|
|
super(ModemDisableTester, self).__init__(*args, **kwargs)
|
|
|
|
def _is_gobi(self):
|
|
return 'Gobi' in self.test.test_env.modem.path
|
|
|
|
def _start_test(self):
|
|
self.remaining_requirements = set(['connect', 'disable'])
|
|
|
|
# Only cromo/gobi-cromo-plugin maintain the invariant that GetStatus
|
|
# will always succeed, so we only check it if the modem is a Gobi.
|
|
if self._is_gobi():
|
|
self.remaining_requirements.add('get_status')
|
|
self.status_delay_ms = self.test_kwargs.get('status_delay_ms', 200)
|
|
gobject.timeout_add(self.status_delay_ms, self._start_get_status)
|
|
|
|
self._start_connect()
|
|
|
|
def _configure(self):
|
|
self.simple_modem = self.test.test_env.modem.SimpleModem()
|
|
|
|
logging.info('Modem path: %s', self.test.test_env.modem.path)
|
|
|
|
if self._is_gobi():
|
|
self._configure_gobi()
|
|
else:
|
|
self._configure_non_gobi()
|
|
|
|
service = self.test.test_env.shill.wait_for_cellular_service_object()
|
|
if not service:
|
|
raise error.TestError('Modem failed to register with the network after '
|
|
're-enabling.')
|
|
|
|
def _configure_gobi(self):
|
|
gobi_modem = self.test.test_env.modem.GobiModem()
|
|
|
|
if 'async_connect_sleep_ms' in self.test_kwargs:
|
|
sleep_ms = self.test_kwargs.get('async_connect_sleep_ms', 0)
|
|
logging.info('Sleeping %d ms before connect', sleep_ms)
|
|
gobi_modem.InjectFault('AsyncConnectSleepMs', sleep_ms)
|
|
|
|
if 'connect_fails_with_error_sending_qmi_request' in self.test_kwargs:
|
|
logging.info('Injecting QMI failure')
|
|
gobi_modem.InjectFault('ConnectFailsWithErrorSendingQmiRequest', 1)
|
|
|
|
def _configure_non_gobi(self):
|
|
# Check to make sure no Gobi-specific arguments were specified.
|
|
if 'async_connect_sleep_ms' in self.test_kwargs:
|
|
raise error.TestError('async_connect_sleep_ms on non-Gobi modem')
|
|
if 'connect_fails_with_error_sending_qmi_request' in self.test_kwargs:
|
|
raise error.TestError(
|
|
'connect_fails_with_error_sending_qmi_request on non-Gobi modem')
|
|
|
|
@ExceptionForward
|
|
def _start_connect(self):
|
|
logging.info('connecting')
|
|
|
|
retval = self.simple_modem.Connect(
|
|
{},
|
|
reply_handler=self._connect_success_handler,
|
|
error_handler=self._connect_error_handler)
|
|
logging.info('connect call made. retval = %s', retval)
|
|
|
|
|
|
@ExceptionForward
|
|
def _start_get_status(self):
|
|
# Keep on calling get_status to make sure it works at all times
|
|
self.simple_modem.GetStatus(
|
|
reply_handler=self._get_status_success_handler,
|
|
error_handler=self.build_error_handler('GetStatus'))
|
|
|
|
def _enabled(self):
|
|
return self.test.test_env.modem.GetModemProperties().get('Enabled', -1)
|
|
|
|
def _enable(self, value):
|
|
self.test.test_env.modem.Enable(
|
|
value,
|
|
reply_handler=self._disable_success_handler,
|
|
error_handler=self.build_error_handler('Enable'))
|
|
|
|
|
|
class cellular_DisableWhileConnecting(test.test):
|
|
"""Check that the modem can handle a disconnect while connecting."""
|
|
version = 1
|
|
|
|
def run_once(self, test_env, **kwargs):
|
|
self.test_env = test_env
|
|
timeout_s = kwargs.get('timeout_s', DEFAULT_TEST_TIMEOUT_S)
|
|
gobject_main_loop = gobject.MainLoop()
|
|
|
|
with test_env:
|
|
logging.info('Shill-level test')
|
|
shill_level_test = ShillDisableTester(self,
|
|
gobject_main_loop,
|
|
timeout_s=timeout_s)
|
|
shill_level_test.run(**kwargs)
|
|
|
|
with test_env:
|
|
try:
|
|
logging.info('Modem-level test')
|
|
modem_level_test = ModemDisableTester(self,
|
|
gobject_main_loop,
|
|
timeout_s=timeout_s)
|
|
modem_level_test.run(**kwargs)
|
|
finally:
|
|
modem_utils.ClearGobiModemFaultInjection()
|