1# Lint as: python2, python3
2# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from __future__ import absolute_import
7from __future__ import division
8from __future__ import print_function
9
10import dbus
11import logging
12
13from six.moves import range
14
15from autotest_lib.client.bin import test
16from autotest_lib.client.common_lib import error
17from autotest_lib.client.cros.cellular.pseudomodem import modem_3gpp
18from autotest_lib.client.cros.cellular.pseudomodem import modem_cdma
19from autotest_lib.client.cros.cellular.pseudomodem import pm_errors
20from autotest_lib.client.cros.cellular.pseudomodem import utils as pm_utils
21from autotest_lib.client.cros.networking import cellular_proxy
22from autotest_lib.client.cros.networking import shill_proxy
23
24
25def _GetModemSuperClass(family):
26    """
27    Obtains the correct Modem base class to use for the given family.
28
29    @param family: The modem family. Should be one of |3GPP|/|CDMA|.
30    @returns: The relevant Modem base class.
31    @raises error.TestError, if |family| is not one of '3GPP' or 'CDMA'.
32
33    """
34    if family == '3GPP':
35        return modem_3gpp.Modem3gpp
36    elif family == 'CDMA':
37        return modem_cdma.ModemCdma
38    else:
39        raise error.TestError('Invalid pseudomodem family: %s', family)
40
41
42def GetFailConnectModem(family):
43    """
44    Returns the correct modem subclass based on |family|.
45
46    @param family: A string containing either '3GPP' or 'CDMA'.
47
48    """
49    modem_class = _GetModemSuperClass(family)
50
51    class FailConnectModem(modem_class):
52        """Custom fake Modem that always fails to connect."""
53        @pm_utils.log_dbus_method(return_cb_arg='return_cb',
54                                  raise_cb_arg='raise_cb')
55        def Connect(self, properties, return_cb, raise_cb):
56            logging.info('Connect call will fail.')
57            raise_cb(pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED))
58
59    return FailConnectModem()
60
61
62class cellular_ConnectFailure(test.test):
63    """
64    Tests that 3G connect failures are handled by shill properly.
65
66    This test will fail if a connect failure does not immediately cause the
67    service to enter the Failed state.
68
69    """
70    version = 1
71
72    def _connect_to_3g_network(self, config_timeout):
73        """
74        Attempts to connect to a 3G network using shill.
75
76        @param config_timeout: Timeout (in seconds) before giving up on
77                               connect.
78
79        @raises: error.TestFail if connection fails.
80
81        """
82        service = self.test_env.shill.find_cellular_service_object()
83
84        try:
85            service.Connect()
86        except dbus.DBusException as e:
87            logging.info('Expected error: %s', e)
88
89        _, state, _ = self.test_env.shill.wait_for_property_in(
90                service,
91                shill_proxy.ShillProxy.SERVICE_PROPERTY_STATE,
92                ('ready', 'portal', 'online', 'failure'),
93                config_timeout)
94
95        if state != 'failure':
96            raise error.TestFail('Service state should be failure not %s' %
97                                 state)
98
99
100    def run_once(self, test_env, connect_count=4):
101        with test_env:
102            self.test_env = test_env
103            for count in range(connect_count):
104                logging.info('Connect attempt %d', count + 1)
105                self._connect_to_3g_network(config_timeout=
106                        cellular_proxy.CellularProxy.SERVICE_CONNECT_TIMEOUT)
107