• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import dbus
6import logging
7import os
8import time
9
10from autotest_lib.client.bin import test
11from autotest_lib.client.bin import utils
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.cros.cellular import mm1_constants
14from autotest_lib.client.cros.cellular import test_environment
15from autotest_lib.client.cros.networking import pm_proxy
16
17I_ACTIVATION_TEST = 'Interface.LTEActivationTest'
18TEST_MODEMS_MODULE_PATH = os.path.join(os.path.dirname(__file__), 'files',
19                                       'modems.py')
20
21LONG_TIMEOUT = 20
22SHORT_TIMEOUT = 10
23
24class ActivationTest(object):
25    """
26    Super class that implements setup code that is common to the individual
27    tests.
28
29    """
30    def __init__(self, test):
31        self.test = test
32
33
34    def Cleanup(self):
35        """
36        Makes the modem look like it has been activated to satisfy the test
37        end condition.
38
39        """
40        # Set the MDN to a non-zero value, so that shill removes the ICCID from
41        # activating_iccid_store.profile. This way, individual test runs won't
42        # interfere with each other.
43        modem = self.test.pseudomm.wait_for_modem(timeout_seconds=LONG_TIMEOUT)
44        modem.iface_properties.Set(mm1_constants.I_MODEM,
45                                   'OwnNumbers',
46                                   ['1111111111'])
47        # Put the modem in the unknown subscription state so that the mdn value is
48        # used to remove the iccid entry
49        self.test.pseudomm.iface_testing.SetSubscriptionState(
50                mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN,
51                mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN)
52        time.sleep(5)
53        self.test.CheckServiceActivationState('activated')
54
55
56    def Run(self):
57        """
58        Configures the pseudomodem to run with the test modem, runs the test
59        and cleans up.
60
61        """
62        self.RunTest()
63        self.Cleanup()
64
65
66    def TestModemClass(self):
67        """ Returns the name of the custom modem to use for this test. """
68        raise NotImplementedError()
69
70
71    def RunTest(self):
72        """
73        Runs the body of the test. Should be implemented by the subclass.
74
75        """
76        raise NotImplementedError()
77
78
79class ActivationResetTest(ActivationTest):
80    """
81    This test verifies that the modem resets after online payment.
82
83    """
84    def TestModemClass(self):
85        return 'TestModem'
86
87
88    def RunTest(self):
89        # Service should appear as 'not-activated'.
90        self.test.CheckServiceActivationState('not-activated')
91        self.test.CheckResetCalled(False)
92
93        # Call 'CompleteActivation' on the device. The service will become
94        # 'activating' and the modem should reset immediately.
95        # Not checking for the intermediate 'activating' state because it makes
96        # the test too fragile
97        service = self.test.FindCellularService()
98        service.CompleteCellularActivation()
99        time.sleep(SHORT_TIMEOUT)
100        self.test.CheckResetCalled(True)
101
102
103class ActivationCompleteTest(ActivationTest):
104    """
105    This test verifies that the service eventually becomes 'activated' in the
106    case of a post-payment registration and the modem finally registers
107    to a network after a reset.
108
109    """
110    def TestModemClass(self):
111        return 'ResetRequiredForActivationModem'
112
113
114    def RunTest(self):
115        # Service should appear as 'not-activated'.
116        self.test.CheckServiceActivationState('not-activated')
117        self.test.CheckResetCalled(False)
118
119        # Call 'CompleteActivation' on the device. The service will become
120        # 'activating' and the modem should reset immediately.
121        # Not checking for the intermediate 'activating' state because it makes
122        # the test too fragile
123        service = self.test.FindCellularService()
124        service.CompleteCellularActivation()
125        time.sleep(SHORT_TIMEOUT)
126        self.test.CheckResetCalled(True)
127
128        # The service should register and be marked as 'activated'.
129        self.test.CheckServiceActivationState('activated')
130
131
132class ActivationDueToMdnTest(ActivationTest):
133    """
134    This test verifies that a valid MDN should cause the service to get marked
135    as 'activated' when the modem is in unknown subscription state.
136
137    """
138    def TestModemClass(self):
139        return 'TestModem'
140
141
142    def RunTest(self):
143        # Service should appear as 'not-activated'.
144        self.test.CheckServiceActivationState('not-activated')
145
146        # Update the MDN. The service should get marked as activated.
147        modem = self.test.pseudomm.get_modem()
148        modem.iface_properties.Set(mm1_constants.I_MODEM,
149                                   'OwnNumbers',
150                                   ['1111111111'])
151        # Put the modem in the unknown subscription state so that the mdn value is
152        # used to determine the service activation status.
153        self.test.pseudomm.iface_testing.SetSubscriptionState(
154                mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN,
155                mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN)
156        time.sleep(SHORT_TIMEOUT)
157        self.test.CheckServiceActivationState('activated')
158
159
160class network_LTEActivate(test.test):
161    """
162    After an online payment to activate a network, shill keeps track of service
163    activation by monitoring changes to network registration and MDN updates
164    combined with a modem reset. The test checks that the
165    Cellular.ActivationState property of the service has the correct value
166    associated with it by simulating possible scenarios using the pseudo modem
167    manager.
168
169    """
170    version = 1
171
172    def GetModemState(self):
173        """Returns the current ModemManager modem state."""
174        modem = self.pseudomm.get_modem()
175        props = modem.properties(mm1_constants.I_MODEM)
176        return props['State']
177
178
179    def SetResetCalled(self, value):
180        """
181        Sets the value of the "ResetCalled" property of the current
182        modem.
183
184        @param value: Value to set in the property.
185
186        """
187        modem = self.pseudomm.get_modem()
188        if modem is None:
189            return
190        modem.iface_properties.Set(
191                I_ACTIVATION_TEST,
192                'ResetCalled',
193                dbus.types.Boolean(value))
194
195
196    def GetResetCalled(self, modem):
197        """
198        Returns the current value of the "ResetCalled" property of the current
199        modem.
200
201        @param modem: Modem proxy to send the query to.
202
203        """
204        return modem.properties(I_ACTIVATION_TEST)['ResetCalled']
205
206
207    def _CheckResetCalledHelper(self, expected_value):
208        modem = self.pseudomm.get_modem()
209        if modem is None:
210            return False
211        try:
212            return self.GetResetCalled(modem) == expected_value
213        except dbus.exceptions.DBusException as e:
214            name = e.get_dbus_name()
215            if (name == mm1_constants.DBUS_UNKNOWN_METHOD or
216                name == mm1_constants.DBUS_UNKNOWN_OBJECT):
217                return False
218            raise e
219
220
221    def CheckResetCalled(self, expected_value):
222        """
223        Checks that the ResetCalled property on the modem matches the expect
224        value.
225
226        @param expected_value: The expected value of ResetCalled.
227
228        """
229        utils.poll_for_condition(
230            lambda: self._CheckResetCalledHelper(expected_value),
231            exception=error.TestFail("\"ResetCalled\" did not match: " +
232                                     str(expected_value)),
233            timeout=LONG_TIMEOUT)
234
235
236    def EnsureModemStateReached(self, expected_state, timeout):
237        """
238        Asserts that the underlying modem state becomes |expected_state| within
239        |timeout|.
240
241        @param expected_state: The expected modem state.
242        @param timeout: Timeout in which the condition should be met.
243
244        """
245        utils.poll_for_condition(
246                lambda: self.GetModemState() == expected_state,
247                exception=error.TestFail(
248                        'Modem failed to reach state ' +
249                        mm1_constants.ModemStateToString(expected_state)),
250                timeout=timeout)
251
252
253    def CheckServiceActivationState(self, expected_state):
254        """
255        Asserts that the service activation state matches |expected_state|
256        within SHORT_TIMEOUT.
257
258        @param expected_state: The expected service activation state.
259
260        """
261        logging.info('Checking for service activation state: %s',
262                     expected_state)
263        service = self.FindCellularService()
264        success, state, duration = self.test_env.shill.wait_for_property_in(
265            service,
266            'Cellular.ActivationState',
267            [expected_state],
268            SHORT_TIMEOUT)
269        if not success and state != expected_state:
270            raise error.TestError(
271                'Service activation state should be \'%s\', but it is \'%s\'.'
272                % (expected_state, state))
273
274
275    def FindCellularService(self, check_not_none=True):
276        """
277        Returns the current cellular service.
278
279        @param check_not_none: If True, an error will be raised if no service
280                was found.
281
282        """
283        if check_not_none:
284            utils.poll_for_condition(
285                    lambda: (self.test_env.shill.find_cellular_service_object()
286                             is not None),
287                    exception=error.TestError(
288                            'Could not find cellular service within timeout.'),
289                    timeout=LONG_TIMEOUT);
290
291        service = self.test_env.shill.find_cellular_service_object()
292
293        # Check once more, to make sure it's valid.
294        if check_not_none and not service:
295            raise error.TestError('Could not find cellular service.')
296        return service
297
298
299    def FindCellularDevice(self):
300        """Returns the current cellular device."""
301        device = self.test_env.shill.find_cellular_device_object()
302        if not device:
303            raise error.TestError('Could not find cellular device.')
304        return device
305
306
307    def ResetCellularDevice(self):
308        """
309        Resets all modems, guaranteeing that the operation succeeds and doesn't
310        fail due to race conditions in pseudomodem start-up and test execution.
311
312        """
313        self.EnsureModemStateReached(
314                mm1_constants.MM_MODEM_STATE_ENABLED, SHORT_TIMEOUT)
315        self.test_env.shill.reset_modem(self.FindCellularDevice())
316        self.EnsureModemStateReached(
317                mm1_constants.MM_MODEM_STATE_ENABLED, SHORT_TIMEOUT)
318
319
320    def run_once(self):
321        tests = [
322            ActivationResetTest(self),
323            ActivationCompleteTest(self),
324            ActivationDueToMdnTest(self),
325        ]
326
327        for test in tests:
328            self.test_env = test_environment.CellularPseudoMMTestEnvironment(
329                    pseudomm_args = ({'family' : '3GPP',
330                                      'test-module' : TEST_MODEMS_MODULE_PATH,
331                                      'test-modem-class' : test.TestModemClass(),
332                                      'test-sim-class' : 'TestSIM'},))
333            with self.test_env:
334                self.pseudomm = pm_proxy.PseudoMMProxy.get_proxy()
335                # Set the reset flag to False explicitly before each test
336                # sequence starts to ignore the reset as a part of the test init
337                self.SetResetCalled(False)
338                test.Run()
339