• 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# A mockd piece of adb logcat output.
34MOCK_ADB_LOGCAT = (
35    "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
43def get_mock_ads(num, logger=None):
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",
55                            logger=logger,
56                            serial=i,
57                            h_port=None)
58        ads.append(ad)
59    return ads
60
61def get_mock_logger():
62    return mock.MagicMock(name="Logger", log_path=MOCK_LOG_PATH)
63
64def mock_get_all_instances(logger=None):
65    return get_mock_ads(5, logger=logger)
66
67def mock_list_adb_devices():
68    return [ad.serial for ad in get_mock_ads(5)]
69
70class MockAdbProxy():
71    """Mock class that swaps out calls to adb with mock calls."""
72
73    def __init__(self, serial):
74        self.serial = serial
75
76    def shell(self, params):
77        if params == "id -u":
78            return b"root"
79        if (params == "getprop | grep ro.build.product" or
80            params == "getprop | grep ro.product.name"):
81            return b"[ro.build.product]: [FakeModel]"
82        elif params == "getprop sys.boot_completed":
83            return b"1"
84        elif params == "bugreportz":
85            return b'OK:/path/bugreport.zip\n'
86
87    def bugreport(self, params):
88        expected = os.path.join(logging.log_path,
89                                "AndroidDevice%s" % self.serial, "BugReports",
90                                "test_something,sometime,%s" % (self.serial))
91        assert expected in params, "Expected '%s', got '%s'." % (expected,
92                                                                 params)
93
94    def __getattr__(self, name):
95        """All calls to the none-existent functions in adb proxy would
96        simply return the adb command string.
97        """
98        def adb_call(*args):
99            clean_name = name.replace('_', '-')
100            arg_str = ' '.join(str(elem) for elem in args)
101            return arg_str
102        return adb_call
103
104class ActsAndroidDeviceTest(unittest.TestCase):
105    """This test class has unit tests for the implementation of everything
106    under acts.controllers.android_device.
107    """
108
109    def setUp(self):
110        """Creates a temp dir to be used by tests in this test class.
111        """
112        self.tmp_dir = tempfile.mkdtemp()
113
114    def tearDown(self):
115        """Removes the temp dir.
116        """
117        shutil.rmtree(self.tmp_dir)
118
119    # Tests for android_device module functions.
120    # These tests use mock AndroidDevice instances.
121
122    @mock.patch.object(android_device, "get_all_instances",
123                       new=mock_get_all_instances)
124    @mock.patch.object(android_device, "list_adb_devices",
125                       new=mock_list_adb_devices)
126    def test_create_with_pickup_all(self):
127        pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
128        actual_ads = android_device.create(pick_all_token, logging)
129        for actual, expected in zip(actual_ads, get_mock_ads(5)):
130            self.assertEqual(actual.serial, expected.serial)
131
132    def test_create_with_empty_config(self):
133        expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
134        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
135                                     expected_msg):
136            android_device.create([], logging)
137
138    def test_create_with_not_list_config(self):
139        expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
140        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
141                                     expected_msg):
142            android_device.create("HAHA", logging)
143
144    def test_get_device_success_with_serial(self):
145        ads = get_mock_ads(5)
146        expected_serial = 0
147        ad = android_device.get_device(ads, serial=expected_serial)
148        self.assertEqual(ad.serial, expected_serial)
149
150    def test_get_device_success_with_serial_and_extra_field(self):
151        ads = get_mock_ads(5)
152        expected_serial = 1
153        expected_h_port = 5555
154        ads[1].h_port = expected_h_port
155        ad = android_device.get_device(ads,
156                                       serial=expected_serial,
157                                       h_port=expected_h_port)
158        self.assertEqual(ad.serial, expected_serial)
159        self.assertEqual(ad.h_port, expected_h_port)
160
161    def test_get_device_no_match(self):
162        ads = get_mock_ads(5)
163        expected_msg = ("Could not find a target device that matches condition"
164                        ": {'serial': 5}.")
165        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
166                                     expected_msg):
167            ad = android_device.get_device(ads, serial=len(ads))
168
169    def test_get_device_too_many_matches(self):
170        ads = get_mock_ads(5)
171        target_serial = ads[1].serial = ads[0].serial
172        expected_msg = "More than one device matched: \[0, 0\]"
173        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
174                                     expected_msg):
175            ad = android_device.get_device(ads, serial=target_serial)
176
177    def test_start_services_on_ads(self):
178        """Makes sure when an AndroidDevice fails to start some services, all
179        AndroidDevice objects get cleaned up.
180        """
181        msg = "Some error happened."
182        ads = get_mock_ads(3)
183        ads[0].start_services = mock.MagicMock()
184        ads[0].clean_up = mock.MagicMock()
185        ads[1].start_services = mock.MagicMock()
186        ads[1].clean_up = mock.MagicMock()
187        ads[2].start_services = mock.MagicMock(
188            side_effect=android_device.AndroidDeviceError(msg))
189        ads[2].clean_up = mock.MagicMock()
190        with self.assertRaisesRegexp(android_device.AndroidDeviceError, msg):
191            android_device._start_services_on_ads(ads)
192        ads[0].clean_up.assert_called_once_with()
193        ads[1].clean_up.assert_called_once_with()
194        ads[2].clean_up.assert_called_once_with()
195
196    # Tests for android_device.AndroidDevice class.
197    # These tests mock out any interaction with the OS and real android device
198    # in AndroidDeivce.
199
200    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
201    def test_AndroidDevice_instantiation(self, MockAdbProxy):
202        """Verifies the AndroidDevice object's basic attributes are correctly
203        set after instantiation.
204        """
205        mock_serial = 1
206        ml = get_mock_logger()
207        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
208        self.assertEqual(ad.serial, 1)
209        self.assertEqual(ad.model, "fakemodel")
210        self.assertIsNone(ad.adb_logcat_process)
211        self.assertIsNone(ad.adb_logcat_file_path)
212        expected_lp = os.path.join(ml.log_path,
213                                   "AndroidDevice%s" % mock_serial)
214        self.assertEqual(ad.log_path, expected_lp)
215
216    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
217    @mock.patch('acts.utils.create_dir')
218    @mock.patch('acts.utils.exe_cmd')
219    def test_AndroidDevice_take_bug_report(self,
220                                           exe_mock,
221                                           create_dir_mock,
222                                           MockAdbProxy):
223        """Verifies AndroidDevice.take_bug_report calls the correct adb command
224        and writes the bugreport file to the correct path.
225        """
226        mock_serial = 1
227        ml = get_mock_logger()
228        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
229        ad.take_bug_report("test_something", "sometime")
230        expected_path = os.path.join(MOCK_LOG_PATH,
231                                     "AndroidDevice%s" % ad.serial,
232                                     "BugReports")
233        create_dir_mock.assert_called_with(expected_path)
234
235    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
236    @mock.patch('acts.utils.create_dir')
237    @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
238    @mock.patch('acts.utils.stop_standing_subprocess')
239    def test_AndroidDevice_take_logcat(self,
240                                       stop_proc_mock,
241                                       start_proc_mock,
242                                       creat_dir_mock,
243                                       MockAdbProxy):
244        """Verifies the steps of collecting adb logcat on an AndroidDevice
245        object, including various function calls and the expected behaviors of
246        the calls.
247        """
248        mock_serial = 1
249        ml = get_mock_logger()
250        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
251        expected_msg = ("Android device .* does not have an ongoing adb logcat"
252                        " collection.")
253        # Expect error if stop is called before start.
254        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
255                                     expected_msg):
256            ad.stop_adb_logcat()
257        ad.start_adb_logcat()
258        # Verify start did the correct operations.
259        self.assertTrue(ad.adb_logcat_process)
260        expected_log_path = os.path.join(
261                                    MOCK_LOG_PATH,
262                                    "AndroidDevice%s" % ad.serial,
263                                    "adblog,fakemodel,%s.txt" % ad.serial)
264        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
265        adb_cmd = 'adb -s %s logcat -v threadtime  >> %s'
266        start_proc_mock.assert_called_with(adb_cmd % (ad.serial,
267                                                      expected_log_path))
268        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
269        expected_msg = ("Android device .* already has an adb logcat thread "
270                        "going on. Cannot start another one.")
271        # Expect error if start is called back to back.
272        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
273                                     expected_msg):
274            ad.start_adb_logcat()
275        # Verify stop did the correct operations.
276        ad.stop_adb_logcat()
277        stop_proc_mock.assert_called_with("process")
278        self.assertIsNone(ad.adb_logcat_process)
279        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
280
281    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
282    @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
283    @mock.patch('acts.utils.stop_standing_subprocess')
284    @mock.patch('acts.logger.get_log_line_timestamp',
285                return_value=MOCK_ADB_LOGCAT_END_TIME)
286    def test_AndroidDevice_cat_adb_log(self,
287                                       mock_timestamp_getter,
288                                       stop_proc_mock,
289                                       start_proc_mock,
290                                       MockAdbProxy):
291        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
292        file, locates the correct adb log lines within the given time range,
293        and writes the lines to the correct output file.
294        """
295        mock_serial = 1
296        ml = get_mock_logger()
297        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
298        # Expect error if attempted to cat adb log before starting adb logcat.
299        expected_msg = ("Attempting to cat adb log when none has been "
300                        "collected on Android device .*")
301        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
302                                     expected_msg):
303            ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
304        ad.start_adb_logcat()
305        # Direct the log path of the ad to a temp dir to avoid racing.
306        ad.log_path = os.path.join(self.tmp_dir, ad.log_path)
307        mock_adb_log_path = os.path.join(ad.log_path, "adblog,%s,%s.txt" %
308                                         (ad.model, ad.serial))
309        with open(mock_adb_log_path, 'w') as f:
310            f.write(MOCK_ADB_LOGCAT)
311        ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
312        cat_file_path = os.path.join(ad.log_path,
313                                     "AdbLogExcerpts",
314                                     ("some_test,02-29 14:02:20.123,%s,%s.txt"
315                                     ) % (ad.model, ad.serial))
316        with open(cat_file_path, 'r') as f:
317            actual_cat = f.read()
318        self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT))
319        # Stops adb logcat.
320        ad.stop_adb_logcat()
321
322if __name__ == "__main__":
323   unittest.main()
324