• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2016 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import time
18import unittest
19
20import mock
21
22from acts import utils
23from acts import signals
24from acts.controllers.adb import AdbError
25
26PROVISIONED_STATE_GOOD = 1
27
28
29class ByPassSetupWizardTests(unittest.TestCase):
30    """This test class for unit testing acts.utils.bypass_setup_wizard."""
31
32    def test_start_standing_subproc(self):
33        with self.assertRaisesRegex(utils.ActsUtilsError,
34                                    'Process .* has terminated'):
35            utils.start_standing_subprocess('sleep 0', check_health_delay=0.1)
36
37    def test_stop_standing_subproc(self):
38        p = utils.start_standing_subprocess('sleep 0')
39        time.sleep(0.1)
40        with self.assertRaisesRegex(utils.ActsUtilsError,
41                                    'Process .* has terminated'):
42            utils.stop_standing_subprocess(p)
43
44    @mock.patch('time.sleep')
45    def test_bypass_setup_wizard_no_complications(self, _):
46        ad = mock.Mock()
47        ad.adb.shell.side_effect = [
48            # Return value for SetupWizardExitActivity
49            BypassSetupWizardReturn.NO_COMPLICATIONS,
50            # Return value for device_provisioned
51            PROVISIONED_STATE_GOOD,
52        ]
53        ad.adb.return_state = BypassSetupWizardReturn.NO_COMPLICATIONS
54        self.assertTrue(utils.bypass_setup_wizard(ad))
55        self.assertFalse(
56            ad.adb.root_adb.called,
57            'The root command should not be called if there are no '
58            'complications.')
59
60    @mock.patch('time.sleep')
61    def test_bypass_setup_wizard_unrecognized_error(self, _):
62        ad = mock.Mock()
63        ad.adb.shell.side_effect = [
64            # Return value for SetupWizardExitActivity
65            BypassSetupWizardReturn.UNRECOGNIZED_ERR,
66            # Return value for device_provisioned
67            PROVISIONED_STATE_GOOD,
68        ]
69        with self.assertRaises(AdbError):
70            utils.bypass_setup_wizard(ad)
71        self.assertFalse(
72            ad.adb.root_adb.called,
73            'The root command should not be called if we do not have a '
74            'codepath for recovering from the failure.')
75
76    @mock.patch('time.sleep')
77    def test_bypass_setup_wizard_need_root_access(self, _):
78        ad = mock.Mock()
79        ad.adb.shell.side_effect = [
80            # Return value for SetupWizardExitActivity
81            BypassSetupWizardReturn.ROOT_ADB_NO_COMP,
82            # Return value for rooting the device
83            BypassSetupWizardReturn.NO_COMPLICATIONS,
84            # Return value for device_provisioned
85            PROVISIONED_STATE_GOOD
86        ]
87
88        utils.bypass_setup_wizard(ad)
89
90        self.assertTrue(
91            ad.adb.root_adb_called,
92            'The command required root access, but the device was never '
93            'rooted.')
94
95    @mock.patch('time.sleep')
96    def test_bypass_setup_wizard_need_root_already_skipped(self, _):
97        ad = mock.Mock()
98        ad.adb.shell.side_effect = [
99            # Return value for SetupWizardExitActivity
100            BypassSetupWizardReturn.ROOT_ADB_SKIPPED,
101            # Return value for SetupWizardExitActivity after root
102            BypassSetupWizardReturn.ALREADY_BYPASSED,
103            # Return value for device_provisioned
104            PROVISIONED_STATE_GOOD
105        ]
106        self.assertTrue(utils.bypass_setup_wizard(ad))
107        self.assertTrue(ad.adb.root_adb_called)
108
109    @mock.patch('time.sleep')
110    def test_bypass_setup_wizard_root_access_still_fails(self, _):
111        ad = mock.Mock()
112        ad.adb.shell.side_effect = [
113            # Return value for SetupWizardExitActivity
114            BypassSetupWizardReturn.ROOT_ADB_FAILS,
115            # Return value for SetupWizardExitActivity after root
116            BypassSetupWizardReturn.UNRECOGNIZED_ERR,
117            # Return value for device_provisioned
118            PROVISIONED_STATE_GOOD
119        ]
120
121        with self.assertRaises(AdbError):
122            utils.bypass_setup_wizard(ad)
123        self.assertTrue(ad.adb.root_adb_called)
124
125
126class BypassSetupWizardReturn:
127    # No complications. Bypass works the first time without issues.
128    NO_COMPLICATIONS = ('Starting: Intent { cmp=com.google.android.setupwizard/'
129                        '.SetupWizardExitActivity }')
130
131    # Fail with doesn't need to be skipped/was skipped already.
132    ALREADY_BYPASSED = AdbError('', 'ADB_CMD_OUTPUT:0', 'Error type 3\n'
133                                                        'Error: Activity class',
134                                1)
135    # Fail with different error.
136    UNRECOGNIZED_ERR = AdbError('', 'ADB_CMD_OUTPUT:0', 'Error type 4\n'
137                                                        'Error: Activity class',
138                                0)
139    # Fail, get root access, then no complications arise.
140    ROOT_ADB_NO_COMP = AdbError('', 'ADB_CMD_OUTPUT:255',
141                                'Security exception: Permission Denial: '
142                                'starting Intent { flg=0x10000000 '
143                                'cmp=com.google.android.setupwizard/'
144                                '.SetupWizardExitActivity } from null '
145                                '(pid=5045, uid=2000) not exported from uid '
146                                '10000', 0)
147    # Even with root access, the bypass setup wizard doesn't need to be skipped.
148    ROOT_ADB_SKIPPED = AdbError('', 'ADB_CMD_OUTPUT:255',
149                                'Security exception: Permission Denial: '
150                                'starting Intent { flg=0x10000000 '
151                                'cmp=com.google.android.setupwizard/'
152                                '.SetupWizardExitActivity } from null '
153                                '(pid=5045, uid=2000) not exported from '
154                                'uid 10000', 0)
155    # Even with root access, the bypass setup wizard fails
156    ROOT_ADB_FAILS = AdbError(
157        '', 'ADB_CMD_OUTPUT:255',
158        'Security exception: Permission Denial: starting Intent { '
159        'flg=0x10000000 cmp=com.google.android.setupwizard/'
160        '.SetupWizardExitActivity } from null (pid=5045, uid=2000) not '
161        'exported from uid 10000', 0)
162
163
164class ConcurrentActionsTest(unittest.TestCase):
165    """Tests acts.utils.run_concurrent_actions and related functions."""
166
167    @staticmethod
168    def function_returns_passed_in_arg(arg):
169        return arg
170
171    @staticmethod
172    def function_raises_passed_in_exception_type(exception_type):
173        raise exception_type
174
175    def test_run_concurrent_actions_no_raise_returns_proper_return_values(self):
176        """Tests run_concurrent_actions_no_raise returns in the correct order.
177
178        Each function passed into run_concurrent_actions_no_raise returns the
179        values returned from each individual callable in the order passed in.
180        """
181        ret_values = utils.run_concurrent_actions_no_raise(
182            lambda: self.function_returns_passed_in_arg('ARG1'),
183            lambda: self.function_returns_passed_in_arg('ARG2'),
184            lambda: self.function_returns_passed_in_arg('ARG3')
185        )
186
187        self.assertEqual(len(ret_values), 3)
188        self.assertEqual(ret_values[0], 'ARG1')
189        self.assertEqual(ret_values[1], 'ARG2')
190        self.assertEqual(ret_values[2], 'ARG3')
191
192    def test_run_concurrent_actions_no_raise_returns_raised_exceptions(self):
193        """Tests run_concurrent_actions_no_raise returns raised exceptions.
194
195        Instead of allowing raised exceptions to be raised in the main thread,
196        this function should capture the exception and return them in the slot
197        the return value should have been returned in.
198        """
199        ret_values = utils.run_concurrent_actions_no_raise(
200            lambda: self.function_raises_passed_in_exception_type(IndexError),
201            lambda: self.function_raises_passed_in_exception_type(KeyError)
202        )
203
204        self.assertEqual(len(ret_values), 2)
205        self.assertEqual(ret_values[0].__class__, IndexError)
206        self.assertEqual(ret_values[1].__class__, KeyError)
207
208    def test_run_concurrent_actions_returns_proper_return_values(self):
209        """Tests run_concurrent_actions returns in the correct order.
210
211        Each function passed into run_concurrent_actions returns the values
212        returned from each individual callable in the order passed in.
213        """
214
215        ret_values = utils.run_concurrent_actions(
216            lambda: self.function_returns_passed_in_arg('ARG1'),
217            lambda: self.function_returns_passed_in_arg('ARG2'),
218            lambda: self.function_returns_passed_in_arg('ARG3')
219        )
220
221        self.assertEqual(len(ret_values), 3)
222        self.assertEqual(ret_values[0], 'ARG1')
223        self.assertEqual(ret_values[1], 'ARG2')
224        self.assertEqual(ret_values[2], 'ARG3')
225
226    def test_run_concurrent_actions_raises_exceptions(self):
227        """Tests run_concurrent_actions raises exceptions from given actions."""
228        with self.assertRaises(KeyError):
229            utils.run_concurrent_actions(
230                lambda: self.function_returns_passed_in_arg('ARG1'),
231                lambda: self.function_raises_passed_in_exception_type(KeyError)
232            )
233
234    def test_test_concurrent_actions_raises_non_test_failure(self):
235        """Tests test_concurrent_actions raises the given exception."""
236        with self.assertRaises(KeyError):
237            utils.test_concurrent_actions(
238                lambda: self.function_raises_passed_in_exception_type(KeyError),
239                failure_exceptions=signals.TestFailure
240            )
241
242    def test_test_concurrent_actions_raises_test_failure(self):
243        """Tests test_concurrent_actions raises the given exception."""
244        with self.assertRaises(signals.TestFailure):
245            utils.test_concurrent_actions(
246                lambda: self.function_raises_passed_in_exception_type(KeyError),
247                failure_exceptions=KeyError
248            )
249
250
251if __name__ == '__main__':
252    unittest.main()
253