• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
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 base_test
25from acts.controllers import android_device
26
27# Mock log path for a test run.
28MOCK_LOG_PATH = "/tmp/logs/MockTest/xx-xx-xx_xx-xx-xx/"
29# The expected result of the cat adb operation.
30MOCK_ADB_LOGCAT_CAT_RESULT = [
31    "02-29 14:02:21.456  4454  Something\n",
32    "02-29 14:02:21.789  4454  Something again\n"
33]
34# A mockd piece of adb logcat output.
35MOCK_ADB_LOGCAT = ("02-29 14:02:19.123  4454  Nothing\n"
36                   "%s"
37                   "02-29 14:02:22.123  4454  Something again and again\n"
38                   ) % ''.join(MOCK_ADB_LOGCAT_CAT_RESULT)
39# Mock start and end time of the adb cat.
40MOCK_ADB_LOGCAT_BEGIN_TIME = "02-29 14:02:20.123"
41MOCK_ADB_LOGCAT_END_TIME = "02-29 14:02:22.000"
42
43
44def get_mock_ads(num):
45    """Generates a list of mock AndroidDevice objects.
46
47    The serial number of each device will be integer 0 through num - 1.
48
49    Args:
50        num: An integer that is the number of mock AndroidDevice objects to
51            create.
52    """
53    ads = []
54    for i in range(num):
55        ad = mock.MagicMock(name="AndroidDevice", serial=i, h_port=None)
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():
69    """Mock class that swaps out calls to adb with mock calls."""
70
71    def __init__(self, serial, fail_br=False, fail_br_before_N=False):
72        self.serial = serial
73        self.fail_br = fail_br
74        self.fail_br_before_N = fail_br_before_N
75
76    def shell(self, params, ignore_status=False, timeout=60):
77        if params == "id -u":
78            return "root"
79        elif params == "bugreportz":
80            if self.fail_br:
81                return "OMG I died!\n"
82            return "OK:/path/bugreport.zip\n"
83        elif params == "bugreportz -v":
84            if self.fail_br_before_N:
85                return "/system/bin/sh: bugreportz: not found"
86            return "1.1"
87
88    def getprop(self, params):
89        if params == "ro.build.id":
90            return "AB42"
91        elif params == "ro.build.type":
92            return "userdebug"
93        elif params == "ro.build.product" or params == "ro.product.name":
94            return "FakeModel"
95        elif params == "sys.boot_completed":
96            return "1"
97
98    def devices(self):
99        return "\t".join([str(self.serial), "device"])
100
101    def bugreport(self, params, timeout=android_device.BUG_REPORT_TIMEOUT):
102        expected = os.path.join(
103            logging.log_path, "AndroidDevice%s" % self.serial,
104            "test_something", "AndroidDevice%s_sometime" % self.serial)
105        assert expected in params, "Expected '%s', got '%s'." % (expected,
106                                                                 params)
107
108    def __getattr__(self, name):
109        """All calls to the none-existent functions in adb proxy would
110        simply return the adb command string.
111        """
112
113        def adb_call(*args, **kwargs):
114            arg_str = ' '.join(str(elem) for elem in args)
115            return arg_str
116
117        return adb_call
118
119
120class MockFastbootProxy():
121    """Mock class that swaps out calls to adb with mock calls."""
122
123    def __init__(self, serial):
124        self.serial = serial
125
126    def devices(self):
127        return "xxxx\tdevice\nyyyy\tdevice"
128
129    def __getattr__(self, name):
130        def fastboot_call(*args):
131            arg_str = ' '.join(str(elem) for elem in args)
132            return arg_str
133
134        return fastboot_call
135
136
137class ActsAndroidDeviceTest(unittest.TestCase):
138    """This test class has unit tests for the implementation of everything
139    under acts.controllers.android_device.
140    """
141
142    def setUp(self):
143        # Set log_path to logging since acts logger setup is not called.
144        if not hasattr(logging, "log_path"):
145            setattr(logging, "log_path", "/tmp/logs")
146        # Creates a temp dir to be used by tests in this test class.
147        self.tmp_dir = tempfile.mkdtemp()
148
149    def tearDown(self):
150        """Removes the temp dir.
151        """
152        shutil.rmtree(self.tmp_dir)
153
154    # Tests for android_device module functions.
155    # These tests use mock AndroidDevice instances.
156
157    @mock.patch.object(
158        android_device, "get_all_instances", new=mock_get_all_instances)
159    @mock.patch.object(
160        android_device, "list_adb_devices", new=mock_list_adb_devices)
161    def test_create_with_pickup_all(self):
162        pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
163        actual_ads = android_device.create(pick_all_token)
164        for actual, expected in zip(actual_ads, get_mock_ads(5)):
165            self.assertEqual(actual.serial, expected.serial)
166
167    def test_create_with_empty_config(self):
168        expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
169        with self.assertRaisesRegex(android_device.AndroidDeviceError,
170                                    expected_msg):
171            android_device.create([])
172
173    def test_create_with_not_list_config(self):
174        expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
175        with self.assertRaisesRegex(android_device.AndroidDeviceError,
176                                    expected_msg):
177            android_device.create("HAHA")
178
179    def test_get_device_success_with_serial(self):
180        ads = get_mock_ads(5)
181        expected_serial = 0
182        ad = android_device.get_device(ads, serial=expected_serial)
183        self.assertEqual(ad.serial, expected_serial)
184
185    def test_get_device_success_with_serial_and_extra_field(self):
186        ads = get_mock_ads(5)
187        expected_serial = 1
188        expected_h_port = 5555
189        ads[1].h_port = expected_h_port
190        ad = android_device.get_device(
191            ads, serial=expected_serial, h_port=expected_h_port)
192        self.assertEqual(ad.serial, expected_serial)
193        self.assertEqual(ad.h_port, expected_h_port)
194
195    def test_get_device_no_match(self):
196        ads = get_mock_ads(5)
197        expected_msg = ("Could not find a target device that matches condition"
198                        ": {'serial': 5}.")
199        with self.assertRaisesRegex(android_device.AndroidDeviceError,
200                                    expected_msg):
201            ad = android_device.get_device(ads, serial=len(ads))
202
203    def test_get_device_too_many_matches(self):
204        ads = get_mock_ads(5)
205        target_serial = ads[1].serial = ads[0].serial
206        expected_msg = "More than one device matched: \[0, 0\]"
207        with self.assertRaisesRegex(android_device.AndroidDeviceError,
208                                    expected_msg):
209            ad = android_device.get_device(ads, serial=target_serial)
210
211    def test_start_services_on_ads(self):
212        """Makes sure when an AndroidDevice fails to start some services, all
213        AndroidDevice objects get cleaned up.
214        """
215        msg = "Some error happened."
216        ads = get_mock_ads(3)
217        ads[0].start_services = mock.MagicMock()
218        ads[0].clean_up = mock.MagicMock()
219        ads[1].start_services = mock.MagicMock()
220        ads[1].clean_up = mock.MagicMock()
221        ads[2].start_services = mock.MagicMock(
222            side_effect=android_device.AndroidDeviceError(msg))
223        ads[2].clean_up = mock.MagicMock()
224        with self.assertRaisesRegex(android_device.AndroidDeviceError, msg):
225            android_device._start_services_on_ads(ads)
226        ads[0].clean_up.assert_called_once_with()
227        ads[1].clean_up.assert_called_once_with()
228        ads[2].clean_up.assert_called_once_with()
229
230    # Tests for android_device.AndroidDevice class.
231    # These tests mock out any interaction with the OS and real android device
232    # in AndroidDeivce.
233
234    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
235    @mock.patch(
236        'acts.controllers.fastboot.FastbootProxy',
237        return_value=MockFastbootProxy(1))
238    def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
239        """Verifies the AndroidDevice object's basic attributes are correctly
240        set after instantiation.
241        """
242        mock_serial = 1
243        ad = android_device.AndroidDevice(serial=mock_serial)
244        self.assertEqual(ad.serial, 1)
245        self.assertEqual(ad.model, "fakemodel")
246        self.assertIsNone(ad.adb_logcat_process)
247        self.assertIsNone(ad.adb_logcat_file_path)
248        expected_lp = os.path.join(logging.log_path,
249                                   "AndroidDevice%s" % mock_serial)
250        self.assertEqual(ad.log_path, expected_lp)
251
252    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
253    @mock.patch(
254        'acts.controllers.fastboot.FastbootProxy',
255        return_value=MockFastbootProxy(1))
256    def test_AndroidDevice_build_info(self, MockFastboot, MockAdbProxy):
257        """Verifies the AndroidDevice object's basic attributes are correctly
258        set after instantiation.
259        """
260        ad = android_device.AndroidDevice(serial=1)
261        build_info = ad.build_info
262        self.assertEqual(build_info["build_id"], "AB42")
263        self.assertEqual(build_info["build_type"], "userdebug")
264
265    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
266    @mock.patch(
267        'acts.controllers.fastboot.FastbootProxy',
268        return_value=MockFastbootProxy(1))
269    @mock.patch('acts.utils.create_dir')
270    @mock.patch('acts.utils.exe_cmd')
271    def test_AndroidDevice_take_bug_report(self, exe_mock, create_dir_mock,
272                                           FastbootProxy, MockAdbProxy):
273        """Verifies AndroidDevice.take_bug_report calls the correct adb command
274        and writes the bugreport file to the correct path.
275        """
276        mock_serial = 1
277        ad = android_device.AndroidDevice(serial=mock_serial)
278        ad.take_bug_report("test_something", "sometime")
279        expected_path = os.path.join(
280            logging.log_path, "AndroidDevice%s" % ad.serial, "test_something")
281        create_dir_mock.assert_called_with(expected_path)
282
283    @mock.patch(
284        'acts.controllers.adb.AdbProxy',
285        return_value=MockAdbProxy(1, fail_br=True))
286    @mock.patch(
287        'acts.controllers.fastboot.FastbootProxy',
288        return_value=MockFastbootProxy(1))
289    @mock.patch('acts.utils.create_dir')
290    @mock.patch('acts.utils.exe_cmd')
291    def test_AndroidDevice_take_bug_report_fail(
292            self, exe_mock, create_dir_mock, FastbootProxy, MockAdbProxy):
293        """Verifies AndroidDevice.take_bug_report writes out the correct message
294        when taking bugreport fails.
295        """
296        mock_serial = 1
297        ad = android_device.AndroidDevice(serial=mock_serial)
298        expected_msg = "Failed to take bugreport on 1: OMG I died!"
299        with self.assertRaisesRegex(android_device.AndroidDeviceError,
300                                    expected_msg):
301            ad.take_bug_report("test_something", "sometime")
302
303    @mock.patch(
304        'acts.controllers.adb.AdbProxy',
305        return_value=MockAdbProxy(1, fail_br_before_N=True))
306    @mock.patch(
307        'acts.controllers.fastboot.FastbootProxy',
308        return_value=MockFastbootProxy(1))
309    @mock.patch('acts.utils.create_dir')
310    @mock.patch('acts.utils.exe_cmd')
311    def test_AndroidDevice_take_bug_report_fallback(
312            self, exe_mock, create_dir_mock, FastbootProxy, MockAdbProxy):
313        """Verifies AndroidDevice.take_bug_report falls back to traditional
314        bugreport on builds that do not have bugreportz.
315        """
316        mock_serial = 1
317        ad = android_device.AndroidDevice(serial=mock_serial)
318        ad.take_bug_report("test_something", "sometime")
319        expected_path = os.path.join(
320            logging.log_path, "AndroidDevice%s" % ad.serial, "test_something")
321        create_dir_mock.assert_called_with(expected_path)
322
323    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
324    @mock.patch(
325        'acts.controllers.fastboot.FastbootProxy',
326        return_value=MockFastbootProxy(1))
327    @mock.patch('acts.utils.create_dir')
328    @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
329    @mock.patch('acts.utils.stop_standing_subprocess')
330    def test_AndroidDevice_take_logcat(self, stop_proc_mock, start_proc_mock,
331                                       creat_dir_mock, FastbootProxy,
332                                       MockAdbProxy):
333        """Verifies the steps of collecting adb logcat on an AndroidDevice
334        object, including various function calls and the expected behaviors of
335        the calls.
336        """
337        mock_serial = 1
338        ad = android_device.AndroidDevice(serial=mock_serial)
339        expected_msg = ("Android device .* does not have an ongoing adb logcat"
340                        " collection.")
341        # Expect error if stop is called before start.
342        with self.assertRaisesRegex(android_device.AndroidDeviceError,
343                                    expected_msg):
344            ad.stop_adb_logcat()
345        ad.start_adb_logcat()
346        # Verify start did the correct operations.
347        self.assertTrue(ad.adb_logcat_process)
348        expected_log_path = os.path.join(logging.log_path,
349                                         "AndroidDevice%s" % ad.serial,
350                                         "adblog,fakemodel,%s.txt" % ad.serial)
351        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
352        adb_cmd = 'adb -s %s logcat -v threadtime -b all >> %s'
353        start_proc_mock.assert_called_with(adb_cmd % (ad.serial,
354                                                      expected_log_path))
355        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
356        expected_msg = ("Android device .* already has an adb logcat thread "
357                        "going on. Cannot start another one.")
358        # Expect error if start is called back to back.
359        with self.assertRaisesRegex(android_device.AndroidDeviceError,
360                                    expected_msg):
361            ad.start_adb_logcat()
362        # Verify stop did the correct operations.
363        ad.stop_adb_logcat()
364        stop_proc_mock.assert_called_with("process")
365        self.assertIsNone(ad.adb_logcat_process)
366        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
367
368    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
369    @mock.patch(
370        'acts.controllers.fastboot.FastbootProxy',
371        return_value=MockFastbootProxy(1))
372    @mock.patch('acts.utils.create_dir')
373    @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
374    @mock.patch('acts.utils.stop_standing_subprocess')
375    def test_AndroidDevice_take_logcat_with_user_param(
376            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
377            FastbootProxy, MockAdbProxy):
378        """Verifies the steps of collecting adb logcat on an AndroidDevice
379        object, including various function calls and the expected behaviors of
380        the calls.
381        """
382        mock_serial = 1
383        ad = android_device.AndroidDevice(serial=mock_serial)
384        ad.adb_logcat_param = "-b radio"
385        expected_msg = ("Android device .* does not have an ongoing adb logcat"
386                        " collection.")
387        # Expect error if stop is called before start.
388        with self.assertRaisesRegex(android_device.AndroidDeviceError,
389                                    expected_msg):
390            ad.stop_adb_logcat()
391        ad.start_adb_logcat()
392        # Verify start did the correct operations.
393        self.assertTrue(ad.adb_logcat_process)
394        expected_log_path = os.path.join(logging.log_path,
395                                         "AndroidDevice%s" % ad.serial,
396                                         "adblog,fakemodel,%s.txt" % ad.serial)
397        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
398        adb_cmd = 'adb -s %s logcat -v threadtime -b radio >> %s'
399        start_proc_mock.assert_called_with(adb_cmd % (ad.serial,
400                                                      expected_log_path))
401        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
402
403    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
404    @mock.patch(
405        'acts.controllers.fastboot.FastbootProxy',
406        return_value=MockFastbootProxy(1))
407    @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
408    @mock.patch('acts.utils.stop_standing_subprocess')
409    @mock.patch(
410        'acts.logger.get_log_line_timestamp',
411        return_value=MOCK_ADB_LOGCAT_END_TIME)
412    def test_AndroidDevice_cat_adb_log(self, mock_timestamp_getter,
413                                       stop_proc_mock, start_proc_mock,
414                                       FastbootProxy, MockAdbProxy):
415        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
416        file, locates the correct adb log lines within the given time range,
417        and writes the lines to the correct output file.
418        """
419        mock_serial = 1
420        ad = android_device.AndroidDevice(serial=mock_serial)
421        # Expect error if attempted to cat adb log before starting adb logcat.
422        expected_msg = ("Attempting to cat adb log when none has been "
423                        "collected on Android device .*")
424        with self.assertRaisesRegex(android_device.AndroidDeviceError,
425                                    expected_msg):
426            ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
427        ad.start_adb_logcat()
428        # Direct the log path of the ad to a temp dir to avoid racing.
429        ad.log_path = os.path.join(self.tmp_dir, ad.log_path)
430        mock_adb_log_path = os.path.join(ad.log_path, "adblog,%s,%s.txt" %
431                                         (ad.model, ad.serial))
432        with open(mock_adb_log_path, 'w') as f:
433            f.write(MOCK_ADB_LOGCAT)
434        ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
435        cat_file_path = os.path.join(
436            ad.log_path, "AdbLogExcerpts",
437            ("some_test,02-29 14:02:20.123,%s,%s.txt") % (ad.model, ad.serial))
438        with open(cat_file_path, 'r') as f:
439            actual_cat = f.read()
440        self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT))
441        # Stops adb logcat.
442        ad.stop_adb_logcat()
443
444
445if __name__ == "__main__":
446    unittest.main()
447