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