• 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 logging
18import mock
19import os
20import shutil
21import tempfile
22import unittest
23
24from acts import logger
25from acts.controllers import android_device
26from acts.controllers.android_lib import errors
27
28# Mock log path for a test run.
29MOCK_LOG_PATH = "/tmp/logs/MockTest/xx-xx-xx_xx-xx-xx/"
30
31# Mock start and end time of the adb cat.
32MOCK_ADB_EPOCH_BEGIN_TIME = 191000123
33MOCK_ADB_LOGCAT_BEGIN_TIME = logger.normalize_log_line_timestamp(
34    logger.epoch_to_log_line_timestamp(MOCK_ADB_EPOCH_BEGIN_TIME))
35MOCK_ADB_LOGCAT_END_TIME = "1970-01-02 21:22:02.000"
36
37MOCK_SERIAL = 1
38MOCK_RELEASE_BUILD_ID = "ABC1.123456.007"
39MOCK_DEV_BUILD_ID = "ABC-MR1"
40MOCK_NYC_BUILD_ID = "N4F27P"
41
42
43def get_mock_ads(num):
44    """Generates a list of mock AndroidDevice objects.
45
46    The serial number of each device will be integer 0 through num - 1.
47
48    Args:
49        num: An integer that is the number of mock AndroidDevice objects to
50            create.
51    """
52    ads = []
53    for i in range(num):
54        ad = mock.MagicMock(name="AndroidDevice", serial=i, h_port=None)
55        ad.ensure_screen_on = mock.MagicMock(return_value=True)
56        ads.append(ad)
57    return ads
58
59
60def mock_get_all_instances():
61    return get_mock_ads(5)
62
63
64def mock_list_adb_devices():
65    return [ad.serial for ad in get_mock_ads(5)]
66
67
68class MockAdbProxy(object):
69    """Mock class that swaps out calls to adb with mock calls."""
70
71    def __init__(self,
72                 serial,
73                 fail_br=False,
74                 fail_br_before_N=False,
75                 build_id=MOCK_RELEASE_BUILD_ID,
76                 return_value=None):
77        self.serial = serial
78        self.fail_br = fail_br
79        self.fail_br_before_N = fail_br_before_N
80        self.return_value = return_value
81        self.return_multiple = False
82        self.build_id = build_id
83
84    def shell(self, params, ignore_status=False, timeout=60):
85        if params == "id -u":
86            return "root"
87        elif params == "bugreportz":
88            if self.fail_br:
89                return "OMG I died!\n"
90            return "OK:/path/bugreport.zip\n"
91        elif params == "bugreportz -v":
92            if self.fail_br_before_N:
93                return "/system/bin/sh: bugreportz: not found"
94            return "1.1"
95        else:
96            if self.return_multiple:
97                return self.return_value.pop(0)
98            else:
99                return self.return_value
100
101    def getprop(self, params):
102        if params == "ro.build.id":
103            return self.build_id
104        elif params == "ro.build.version.incremental":
105            return "123456789"
106        elif params == "ro.build.type":
107            return "userdebug"
108        elif params == "ro.build.product" or params == "ro.product.name":
109            return "FakeModel"
110        elif params == "sys.boot_completed":
111            return "1"
112
113    def devices(self):
114        return "\t".join([str(self.serial), "device"])
115
116    def bugreport(self, params, timeout=android_device.BUG_REPORT_TIMEOUT):
117        expected = os.path.join(
118            logging.log_path, "AndroidDevice%s" % self.serial,
119            "AndroidDevice%s_%s.txt" %
120            (self.serial,
121             logger.normalize_log_line_timestamp(MOCK_ADB_LOGCAT_BEGIN_TIME)))
122        assert expected in params, "Expected '%s', got '%s'." % (expected,
123                                                                 params)
124
125    def __getattr__(self, name):
126        """All calls to the none-existent functions in adb proxy would
127        simply return the adb command string.
128        """
129
130        def adb_call(*args, **kwargs):
131            arg_str = ' '.join(str(elem) for elem in args)
132            return arg_str
133
134        return adb_call
135
136
137class MockFastbootProxy():
138    """Mock class that swaps out calls to adb with mock calls."""
139
140    def __init__(self, serial):
141        self.serial = serial
142
143    def devices(self):
144        return "xxxx\tdevice\nyyyy\tdevice"
145
146    def __getattr__(self, name):
147        def fastboot_call(*args):
148            arg_str = ' '.join(str(elem) for elem in args)
149            return arg_str
150
151        return fastboot_call
152
153
154class ActsAndroidDeviceTest(unittest.TestCase):
155    """This test class has unit tests for the implementation of everything
156    under acts.controllers.android_device.
157    """
158
159    def setUp(self):
160        # Set log_path to logging since acts logger setup is not called.
161        if not hasattr(logging, "log_path"):
162            setattr(logging, "log_path", "/tmp/logs")
163        # Creates a temp dir to be used by tests in this test class.
164        self.tmp_dir = tempfile.mkdtemp()
165
166    def tearDown(self):
167        """Removes the temp dir.
168        """
169        shutil.rmtree(self.tmp_dir)
170
171    # Tests for android_device module functions.
172    # These tests use mock AndroidDevice instances.
173
174    @mock.patch.object(
175        android_device, "get_all_instances", new=mock_get_all_instances)
176    @mock.patch.object(
177        android_device, "list_adb_devices", new=mock_list_adb_devices)
178    def test_create_with_pickup_all(self):
179        pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
180        actual_ads = android_device.create(pick_all_token)
181        for actual, expected in zip(actual_ads, get_mock_ads(5)):
182            self.assertEqual(actual.serial, expected.serial)
183
184    def test_create_with_empty_config(self):
185        expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
186        with self.assertRaisesRegex(errors.AndroidDeviceConfigError,
187                                    expected_msg):
188            android_device.create([])
189
190    def test_create_with_not_list_config(self):
191        expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
192        with self.assertRaisesRegex(errors.AndroidDeviceConfigError,
193                                    expected_msg):
194            android_device.create("HAHA")
195
196    def test_get_device_success_with_serial(self):
197        ads = get_mock_ads(5)
198        expected_serial = 0
199        ad = android_device.get_device(ads, serial=expected_serial)
200        self.assertEqual(ad.serial, expected_serial)
201
202    def test_get_device_success_with_serial_and_extra_field(self):
203        ads = get_mock_ads(5)
204        expected_serial = 1
205        expected_h_port = 5555
206        ads[1].h_port = expected_h_port
207        ad = android_device.get_device(
208            ads, serial=expected_serial, h_port=expected_h_port)
209        self.assertEqual(ad.serial, expected_serial)
210        self.assertEqual(ad.h_port, expected_h_port)
211
212    def test_get_device_no_match(self):
213        ads = get_mock_ads(5)
214        expected_msg = ("Could not find a target device that matches condition"
215                        ": {'serial': 5}.")
216        with self.assertRaisesRegex(ValueError, expected_msg):
217            ad = android_device.get_device(ads, serial=len(ads))
218
219    def test_get_device_too_many_matches(self):
220        ads = get_mock_ads(5)
221        target_serial = ads[1].serial = ads[0].serial
222        expected_msg = "More than one device matched: \[0, 0\]"
223        with self.assertRaisesRegex(ValueError, expected_msg):
224            ad = android_device.get_device(ads, serial=target_serial)
225
226    def test_start_services_on_ads(self):
227        """Makes sure when an AndroidDevice fails to start some services, all
228        AndroidDevice objects get cleaned up.
229        """
230        msg = "Some error happened."
231        ads = get_mock_ads(3)
232        ads[0].start_services = mock.MagicMock()
233        ads[0].clean_up = mock.MagicMock()
234        ads[1].start_services = mock.MagicMock()
235        ads[1].clean_up = mock.MagicMock()
236        ads[2].start_services = mock.MagicMock(
237            side_effect=errors.AndroidDeviceError(msg))
238        ads[2].clean_up = mock.MagicMock()
239        with self.assertRaisesRegex(errors.AndroidDeviceError, msg):
240            android_device._start_services_on_ads(ads)
241        ads[0].clean_up.assert_called_once_with()
242        ads[1].clean_up.assert_called_once_with()
243        ads[2].clean_up.assert_called_once_with()
244
245    # Tests for android_device.AndroidDevice class.
246    # These tests mock out any interaction with the OS and real android device
247    # in AndroidDeivce.
248
249    @mock.patch(
250        'acts.controllers.adb.AdbProxy',
251        return_value=MockAdbProxy(MOCK_SERIAL))
252    @mock.patch(
253        'acts.controllers.fastboot.FastbootProxy',
254        return_value=MockFastbootProxy(MOCK_SERIAL))
255    def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
256        """Verifies the AndroidDevice object's basic attributes are correctly
257        set after instantiation.
258        """
259        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
260        self.assertEqual(ad.serial, 1)
261        self.assertEqual(ad.model, "fakemodel")
262        self.assertIsNone(ad.adb_logcat_process)
263        expected_lp = os.path.join(logging.log_path,
264                                   "AndroidDevice%s" % MOCK_SERIAL)
265        self.assertEqual(ad.log_path, expected_lp)
266
267    @mock.patch(
268        'acts.controllers.adb.AdbProxy',
269        return_value=MockAdbProxy(MOCK_SERIAL))
270    @mock.patch(
271        'acts.controllers.fastboot.FastbootProxy',
272        return_value=MockFastbootProxy(MOCK_SERIAL))
273    def test_AndroidDevice_build_info_release(self, MockFastboot,
274                                              MockAdbProxy):
275        """Verifies the AndroidDevice object's basic attributes are correctly
276        set after instantiation.
277        """
278        ad = android_device.AndroidDevice(serial=1)
279        build_info = ad.build_info
280        self.assertEqual(build_info["build_id"], "ABC1.123456.007")
281        self.assertEqual(build_info["build_type"], "userdebug")
282
283    @mock.patch(
284        'acts.controllers.adb.AdbProxy',
285        return_value=MockAdbProxy(MOCK_SERIAL, build_id=MOCK_DEV_BUILD_ID))
286    @mock.patch(
287        'acts.controllers.fastboot.FastbootProxy',
288        return_value=MockFastbootProxy(MOCK_SERIAL))
289    def test_AndroidDevice_build_info_dev(self, MockFastboot, MockAdbProxy):
290        """Verifies the AndroidDevice object's basic attributes are correctly
291        set after instantiation.
292        """
293        ad = android_device.AndroidDevice(serial=1)
294        build_info = ad.build_info
295        self.assertEqual(build_info["build_id"], "123456789")
296        self.assertEqual(build_info["build_type"], "userdebug")
297
298    @mock.patch(
299        'acts.controllers.adb.AdbProxy',
300        return_value=MockAdbProxy(MOCK_SERIAL, build_id=MOCK_NYC_BUILD_ID))
301    @mock.patch(
302        'acts.controllers.fastboot.FastbootProxy',
303        return_value=MockFastbootProxy(MOCK_SERIAL))
304    def test_AndroidDevice_build_info_nyc(self, MockFastboot, MockAdbProxy):
305        """Verifies the AndroidDevice object's build id is set correctly for
306        NYC releases.
307        """
308        ad = android_device.AndroidDevice(serial=1)
309        build_info = ad.build_info
310        self.assertEqual(build_info["build_id"], MOCK_NYC_BUILD_ID)
311
312    @mock.patch(
313        'acts.controllers.adb.AdbProxy',
314        return_value=MockAdbProxy(MOCK_SERIAL))
315    @mock.patch(
316        'acts.controllers.fastboot.FastbootProxy',
317        return_value=MockFastbootProxy(MOCK_SERIAL))
318    @mock.patch('acts.utils.create_dir')
319    @mock.patch('acts.utils.exe_cmd')
320    @mock.patch('acts.controllers.android_device.AndroidDevice.device_log_path',
321                new_callable=mock.PropertyMock)
322    def test_AndroidDevice_take_bug_report(self, mock_log_path, exe_mock,
323                                           create_dir_mock, FastbootProxy,
324                                           MockAdbProxy):
325        """Verifies AndroidDevice.take_bug_report calls the correct adb command
326        and writes the bugreport file to the correct path.
327        """
328        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
329        mock_log_path.return_value = os.path.join(
330            logging.log_path, "AndroidDevice%s" % ad.serial)
331        ad.take_bug_report("test_something", 234325.32)
332        create_dir_mock.assert_called_with(mock_log_path())
333
334    @mock.patch(
335        'acts.controllers.adb.AdbProxy',
336        return_value=MockAdbProxy(MOCK_SERIAL, fail_br=True))
337    @mock.patch(
338        'acts.controllers.fastboot.FastbootProxy',
339        return_value=MockFastbootProxy(MOCK_SERIAL))
340    @mock.patch('acts.utils.create_dir')
341    @mock.patch('acts.utils.exe_cmd')
342    @mock.patch('acts.controllers.android_device.AndroidDevice.device_log_path',
343                new_callable=mock.PropertyMock)
344    def test_AndroidDevice_take_bug_report_fail(
345            self, mock_log_path, exe_mock, create_dir_mock, FastbootProxy,
346            MockAdbProxy):
347        """Verifies AndroidDevice.take_bug_report writes out the correct message
348        when taking bugreport fails.
349        """
350        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
351        mock_log_path.return_value = os.path.join(
352            logging.log_path, "AndroidDevice%s" % ad.serial)
353        expected_msg = "Failed to take bugreport on 1: OMG I died!"
354        with self.assertRaisesRegex(errors.AndroidDeviceError,
355                                    expected_msg):
356            ad.take_bug_report("test_something", 4346343.23)
357
358    @mock.patch(
359        'acts.controllers.adb.AdbProxy',
360        return_value=MockAdbProxy(MOCK_SERIAL, fail_br_before_N=True))
361    @mock.patch(
362        'acts.controllers.fastboot.FastbootProxy',
363        return_value=MockFastbootProxy(MOCK_SERIAL))
364    @mock.patch('acts.utils.create_dir')
365    @mock.patch('acts.utils.exe_cmd')
366    @mock.patch('acts.controllers.android_device.AndroidDevice.device_log_path',
367                new_callable=mock.PropertyMock)
368    def test_AndroidDevice_take_bug_report_fallback(
369            self, mock_log_path, exe_mock, create_dir_mock, FastbootProxy,
370            MockAdbProxy):
371        """Verifies AndroidDevice.take_bug_report falls back to traditional
372        bugreport on builds that do not have bugreportz.
373        """
374        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
375        mock_log_path.return_value = os.path.join(
376            logging.log_path, "AndroidDevice%s" % ad.serial)
377        ad.take_bug_report("test_something", MOCK_ADB_EPOCH_BEGIN_TIME)
378        create_dir_mock.assert_called_with(mock_log_path())
379
380    @mock.patch(
381        'acts.controllers.adb.AdbProxy',
382        return_value=MockAdbProxy(MOCK_SERIAL))
383    @mock.patch(
384        'acts.controllers.fastboot.FastbootProxy',
385        return_value=MockFastbootProxy(MOCK_SERIAL))
386    @mock.patch('acts.libs.proc.process.Process')
387    def test_AndroidDevice_start_adb_logcat(self, proc_mock, FastbootProxy,
388                                            MockAdbProxy):
389        """Verifies the AndroidDevice method start_adb_logcat. Checks that the
390        underlying logcat process is started properly and correct warning msgs
391        are generated.
392        """
393        with mock.patch(
394                ('acts.controllers.android_lib.logcat.'
395                 'create_logcat_keepalive_process'),
396                return_value=proc_mock) as create_proc_mock:
397            ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
398            ad.start_adb_logcat()
399            # Verify start did the correct operations.
400            self.assertTrue(ad.adb_logcat_process)
401            log_dir = "AndroidDevice%s" % ad.serial
402            create_proc_mock.assert_called_with(
403                ad.serial, log_dir, '-b all')
404            proc_mock.start.assert_called_with()
405            # Expect warning msg if start is called back to back.
406            expected_msg = "Android device .* already has a running adb logcat"
407            proc_mock.is_running.return_value = True
408            with self.assertLogs(level='WARNING') as log:
409                ad.start_adb_logcat()
410                self.assertRegex(log.output[0], expected_msg)
411
412    @mock.patch(
413        'acts.controllers.adb.AdbProxy',
414        return_value=MockAdbProxy(MOCK_SERIAL))
415    @mock.patch(
416        'acts.controllers.fastboot.FastbootProxy',
417        return_value=MockFastbootProxy(MOCK_SERIAL))
418    @mock.patch('acts.controllers.android_lib.logcat.'
419                'create_logcat_keepalive_process')
420    def test_AndroidDevice_start_adb_logcat_with_user_param(
421            self, create_proc_mock, FastbootProxy, MockAdbProxy):
422        """Verifies that start_adb_logcat generates the correct adb logcat
423        command if adb_logcat_param is specified.
424        """
425        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
426        ad.adb_logcat_param = "-b radio"
427        ad.start_adb_logcat()
428        # Verify that create_logcat_keepalive_process is called with the
429        # correct command.
430        log_dir = "AndroidDevice%s" % ad.serial
431        create_proc_mock.assert_called_with(
432            ad.serial, log_dir, '-b radio')
433
434    @mock.patch(
435        'acts.controllers.adb.AdbProxy',
436        return_value=MockAdbProxy(MOCK_SERIAL))
437    @mock.patch(
438        'acts.controllers.fastboot.FastbootProxy',
439        return_value=MockFastbootProxy(MOCK_SERIAL))
440    @mock.patch('acts.libs.proc.process.Process')
441    def test_AndroidDevice_stop_adb_logcat(self, proc_mock, FastbootProxy,
442                                       MockAdbProxy):
443        """Verifies the AndroidDevice method stop_adb_logcat. Checks that the
444        underlying logcat process is stopped properly and correct warning msgs
445        are generated.
446        """
447        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
448        ad.adb_logcat_process = proc_mock
449        # Expect warning msg if stop is called before start.
450        expected_msg = (
451            "Android device .* does not have an ongoing adb logcat")
452        proc_mock.is_running.return_value = False
453        with self.assertLogs(level='WARNING') as log:
454            ad.stop_adb_logcat()
455            self.assertRegex(log.output[0], expected_msg)
456
457        # Verify the underlying process is stopped.
458        proc_mock.is_running.return_value = True
459        ad.stop_adb_logcat()
460        proc_mock.stop.assert_called_with()
461
462    @mock.patch(
463        'acts.controllers.adb.AdbProxy',
464        return_value=MockAdbProxy(MOCK_SERIAL))
465    def test_get_apk_process_id_process_cannot_find(self, adb_proxy):
466        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
467        ad.adb.return_value = "does_not_contain_value"
468        self.assertEqual(None, ad.get_package_pid("some_package"))
469
470    @mock.patch(
471        'acts.controllers.adb.AdbProxy',
472        return_value=MockAdbProxy(MOCK_SERIAL))
473    def test_get_apk_process_id_process_exists_second_try(self, adb_proxy):
474        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
475        ad.adb.return_multiple = True
476        ad.adb.return_value = ["", "system 1 2 3 4  S com.some_package"]
477        self.assertEqual(1, ad.get_package_pid("some_package"))
478
479    @mock.patch(
480        'acts.controllers.adb.AdbProxy',
481        return_value=MockAdbProxy(MOCK_SERIAL))
482    def test_get_apk_process_id_bad_return(self, adb_proxy):
483        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
484        ad.adb.return_value = "bad_return_index_error"
485        self.assertEqual(None, ad.get_package_pid("some_package"))
486
487    @mock.patch(
488        'acts.controllers.adb.AdbProxy',
489        return_value=MockAdbProxy(MOCK_SERIAL))
490    def test_get_apk_process_id_bad_return(self, adb_proxy):
491        ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
492        ad.adb.return_value = "bad return value error"
493        self.assertEqual(None, ad.get_package_pid("some_package"))
494
495
496if __name__ == "__main__":
497    unittest.main()
498