blob: ddceda9d9eaf40fea5e9417b83cdcb6544f981e4 [file] [log] [blame]
Josh Gao191c1542015-12-09 11:26:11 -08001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2015 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18from __future__ import print_function
19
20import contextlib
21import hashlib
22import os
23import posixpath
24import random
25import re
26import shlex
27import shutil
28import signal
29import socket
30import string
31import subprocess
32import sys
33import tempfile
Josh Gaofe50bb72016-06-22 18:27:22 -070034import time
Josh Gao191c1542015-12-09 11:26:11 -080035import unittest
36
37import mock
38
39import adb
40
41
42def requires_root(func):
43 def wrapper(self, *args):
44 if self.device.get_prop('ro.debuggable') != '1':
45 raise unittest.SkipTest('requires rootable build')
46
47 was_root = self.device.shell(['id', '-un'])[0].strip() == 'root'
48 if not was_root:
49 self.device.root()
50 self.device.wait()
51
52 try:
53 func(self, *args)
54 finally:
55 if not was_root:
56 self.device.unroot()
57 self.device.wait()
58
59 return wrapper
60
61
62def requires_non_root(func):
63 def wrapper(self, *args):
64 was_root = self.device.shell(['id', '-un'])[0].strip() == 'root'
65 if was_root:
66 self.device.unroot()
67 self.device.wait()
68
69 try:
70 func(self, *args)
71 finally:
72 if was_root:
73 self.device.root()
74 self.device.wait()
75
76 return wrapper
77
78
79class GetDeviceTest(unittest.TestCase):
80 def setUp(self):
81 self.android_serial = os.getenv('ANDROID_SERIAL')
82 if 'ANDROID_SERIAL' in os.environ:
83 del os.environ['ANDROID_SERIAL']
84
85 def tearDown(self):
86 if self.android_serial is not None:
87 os.environ['ANDROID_SERIAL'] = self.android_serial
88 else:
89 if 'ANDROID_SERIAL' in os.environ:
90 del os.environ['ANDROID_SERIAL']
91
92 @mock.patch('adb.device.get_devices')
93 def test_explicit(self, mock_get_devices):
94 mock_get_devices.return_value = ['foo', 'bar']
95 device = adb.get_device('foo')
96 self.assertEqual(device.serial, 'foo')
97
98 @mock.patch('adb.device.get_devices')
99 def test_from_env(self, mock_get_devices):
100 mock_get_devices.return_value = ['foo', 'bar']
101 os.environ['ANDROID_SERIAL'] = 'foo'
102 device = adb.get_device()
103 self.assertEqual(device.serial, 'foo')
104
105 @mock.patch('adb.device.get_devices')
106 def test_arg_beats_env(self, mock_get_devices):
107 mock_get_devices.return_value = ['foo', 'bar']
108 os.environ['ANDROID_SERIAL'] = 'bar'
109 device = adb.get_device('foo')
110 self.assertEqual(device.serial, 'foo')
111
112 @mock.patch('adb.device.get_devices')
113 def test_no_such_device(self, mock_get_devices):
114 mock_get_devices.return_value = ['foo', 'bar']
115 self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
116
117 os.environ['ANDROID_SERIAL'] = 'baz'
118 self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
119
120 @mock.patch('adb.device.get_devices')
121 def test_unique_device(self, mock_get_devices):
122 mock_get_devices.return_value = ['foo']
123 device = adb.get_device()
124 self.assertEqual(device.serial, 'foo')
125
126 @mock.patch('adb.device.get_devices')
127 def test_no_unique_device(self, mock_get_devices):
128 mock_get_devices.return_value = ['foo', 'bar']
129 self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
130
131
132class DeviceTest(unittest.TestCase):
133 def setUp(self):
134 self.device = adb.get_device()
135
136
137class ForwardReverseTest(DeviceTest):
138 def _test_no_rebind(self, description, direction_list, direction,
139 direction_no_rebind, direction_remove_all):
140 msg = direction_list()
141 self.assertEqual('', msg.strip(),
142 description + ' list must be empty to run this test.')
143
144 # Use --no-rebind with no existing binding
145 direction_no_rebind('tcp:5566', 'tcp:6655')
146 msg = direction_list()
147 self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
148
149 # Use --no-rebind with existing binding
150 with self.assertRaises(subprocess.CalledProcessError):
151 direction_no_rebind('tcp:5566', 'tcp:6677')
152 msg = direction_list()
153 self.assertFalse(re.search(r'tcp:5566.+tcp:6677', msg))
154 self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
155
156 # Use the absence of --no-rebind with existing binding
157 direction('tcp:5566', 'tcp:6677')
158 msg = direction_list()
159 self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
160 self.assertTrue(re.search(r'tcp:5566.+tcp:6677', msg))
161
162 direction_remove_all()
163 msg = direction_list()
164 self.assertEqual('', msg.strip())
165
166 def test_forward_no_rebind(self):
167 self._test_no_rebind('forward', self.device.forward_list,
168 self.device.forward, self.device.forward_no_rebind,
169 self.device.forward_remove_all)
170
171 def test_reverse_no_rebind(self):
172 self._test_no_rebind('reverse', self.device.reverse_list,
173 self.device.reverse, self.device.reverse_no_rebind,
174 self.device.reverse_remove_all)
175
176 def test_forward(self):
177 msg = self.device.forward_list()
178 self.assertEqual('', msg.strip(),
179 'Forwarding list must be empty to run this test.')
180 self.device.forward('tcp:5566', 'tcp:6655')
181 msg = self.device.forward_list()
182 self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
183 self.device.forward('tcp:7788', 'tcp:8877')
184 msg = self.device.forward_list()
185 self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
186 self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
187 self.device.forward_remove('tcp:5566')
188 msg = self.device.forward_list()
189 self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
190 self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
191 self.device.forward_remove_all()
192 msg = self.device.forward_list()
193 self.assertEqual('', msg.strip())
194
David Purselleaae97e2016-04-07 11:25:48 -0700195 def test_forward_tcp_port_0(self):
196 self.assertEqual('', self.device.forward_list().strip(),
197 'Forwarding list must be empty to run this test.')
198
199 try:
200 # If resolving TCP port 0 is supported, `adb forward` will print
201 # the actual port number.
202 port = self.device.forward('tcp:0', 'tcp:8888').strip()
203 if not port:
204 raise unittest.SkipTest('Forwarding tcp:0 is not available.')
205
206 self.assertTrue(re.search(r'tcp:{}.+tcp:8888'.format(port),
207 self.device.forward_list()))
208 finally:
209 self.device.forward_remove_all()
210
Josh Gao191c1542015-12-09 11:26:11 -0800211 def test_reverse(self):
212 msg = self.device.reverse_list()
213 self.assertEqual('', msg.strip(),
214 'Reverse forwarding list must be empty to run this test.')
215 self.device.reverse('tcp:5566', 'tcp:6655')
216 msg = self.device.reverse_list()
217 self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
218 self.device.reverse('tcp:7788', 'tcp:8877')
219 msg = self.device.reverse_list()
220 self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
221 self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
222 self.device.reverse_remove('tcp:5566')
223 msg = self.device.reverse_list()
224 self.assertFalse(re.search(r'tcp:5566.+tcp:6655', msg))
225 self.assertTrue(re.search(r'tcp:7788.+tcp:8877', msg))
226 self.device.reverse_remove_all()
227 msg = self.device.reverse_list()
228 self.assertEqual('', msg.strip())
229
David Purselleaae97e2016-04-07 11:25:48 -0700230 def test_reverse_tcp_port_0(self):
231 self.assertEqual('', self.device.reverse_list().strip(),
232 'Reverse list must be empty to run this test.')
233
234 try:
235 # If resolving TCP port 0 is supported, `adb reverse` will print
236 # the actual port number.
237 port = self.device.reverse('tcp:0', 'tcp:8888').strip()
238 if not port:
239 raise unittest.SkipTest('Reversing tcp:0 is not available.')
240
241 self.assertTrue(re.search(r'tcp:{}.+tcp:8888'.format(port),
242 self.device.reverse_list()))
243 finally:
244 self.device.reverse_remove_all()
245
Josh Gao191c1542015-12-09 11:26:11 -0800246 # Note: If you run this test when adb connect'd to a physical device over
247 # TCP, it will fail in adb reverse due to https://code.google.com/p/android/issues/detail?id=189821
248 def test_forward_reverse_echo(self):
249 """Send data through adb forward and read it back via adb reverse"""
250 forward_port = 12345
251 reverse_port = forward_port + 1
Josh Gao255c5c82016-03-03 14:49:02 -0800252 forward_spec = 'tcp:' + str(forward_port)
253 reverse_spec = 'tcp:' + str(reverse_port)
Josh Gao191c1542015-12-09 11:26:11 -0800254 forward_setup = False
255 reverse_setup = False
256
257 try:
258 # listen on localhost:forward_port, connect to remote:forward_port
259 self.device.forward(forward_spec, forward_spec)
260 forward_setup = True
261 # listen on remote:forward_port, connect to localhost:reverse_port
262 self.device.reverse(forward_spec, reverse_spec)
263 reverse_setup = True
264
265 listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
266 with contextlib.closing(listener):
267 # Use SO_REUSEADDR so that subsequent runs of the test can grab
268 # the port even if it is in TIME_WAIT.
269 listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
270
271 # Listen on localhost:reverse_port before connecting to
272 # localhost:forward_port because that will cause adb to connect
273 # back to localhost:reverse_port.
274 listener.bind(('127.0.0.1', reverse_port))
275 listener.listen(4)
276
277 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
278 with contextlib.closing(client):
279 # Connect to the listener.
280 client.connect(('127.0.0.1', forward_port))
281
282 # Accept the client connection.
283 accepted_connection, addr = listener.accept()
284 with contextlib.closing(accepted_connection) as server:
285 data = 'hello'
286
287 # Send data into the port setup by adb forward.
288 client.sendall(data)
289 # Explicitly close() so that server gets EOF.
290 client.close()
291
292 # Verify that the data came back via adb reverse.
293 self.assertEqual(data, server.makefile().read())
294 finally:
295 if reverse_setup:
296 self.device.reverse_remove(forward_spec)
297 if forward_setup:
298 self.device.forward_remove(forward_spec)
299
300
301class ShellTest(DeviceTest):
302 def _interactive_shell(self, shell_args, input):
303 """Runs an interactive adb shell.
304
305 Args:
306 shell_args: List of string arguments to `adb shell`.
307 input: String input to send to the interactive shell.
308
309 Returns:
310 The remote exit code.
311
312 Raises:
313 unittest.SkipTest: The device doesn't support exit codes.
314 """
David Pursellcf467412016-04-26 13:25:57 -0700315 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800316 raise unittest.SkipTest('exit codes are unavailable on this device')
317
318 proc = subprocess.Popen(
319 self.device.adb_cmd + ['shell'] + shell_args,
320 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
321 stderr=subprocess.PIPE)
322 # Closing host-side stdin doesn't trigger a PTY shell to exit so we need
323 # to explicitly add an exit command to close the session from the device
324 # side, plus the necessary newline to complete the interactive command.
325 proc.communicate(input + '; exit\n')
326 return proc.returncode
327
328 def test_cat(self):
329 """Check that we can at least cat a file."""
330 out = self.device.shell(['cat', '/proc/uptime'])[0].strip()
331 elements = out.split()
332 self.assertEqual(len(elements), 2)
333
334 uptime, idle = elements
335 self.assertGreater(float(uptime), 0.0)
336 self.assertGreater(float(idle), 0.0)
337
338 def test_throws_on_failure(self):
339 self.assertRaises(adb.ShellError, self.device.shell, ['false'])
340
341 def test_output_not_stripped(self):
342 out = self.device.shell(['echo', 'foo'])[0]
343 self.assertEqual(out, 'foo' + self.device.linesep)
344
Josh Gaoa019f782017-06-16 15:34:34 -0700345 def test_shell_command_length(self):
346 # Devices that have shell_v2 should be able to handle long commands.
347 if self.device.has_shell_protocol():
348 rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
349 self.assertEqual(rc, 0)
350 self.assertTrue(out == ('x' * 16384 + '\n'))
351
Josh Gao191c1542015-12-09 11:26:11 -0800352 def test_shell_nocheck_failure(self):
353 rc, out, _ = self.device.shell_nocheck(['false'])
354 self.assertNotEqual(rc, 0)
355 self.assertEqual(out, '')
356
357 def test_shell_nocheck_output_not_stripped(self):
358 rc, out, _ = self.device.shell_nocheck(['echo', 'foo'])
359 self.assertEqual(rc, 0)
360 self.assertEqual(out, 'foo' + self.device.linesep)
361
362 def test_can_distinguish_tricky_results(self):
363 # If result checking on ADB shell is naively implemented as
364 # `adb shell <cmd>; echo $?`, we would be unable to distinguish the
365 # output from the result for a cmd of `echo -n 1`.
366 rc, out, _ = self.device.shell_nocheck(['echo', '-n', '1'])
367 self.assertEqual(rc, 0)
368 self.assertEqual(out, '1')
369
370 def test_line_endings(self):
371 """Ensure that line ending translation is not happening in the pty.
372
373 Bug: http://b/19735063
374 """
375 output = self.device.shell(['uname'])[0]
376 self.assertEqual(output, 'Linux' + self.device.linesep)
377
378 def test_pty_logic(self):
379 """Tests that a PTY is allocated when it should be.
380
Elliott Hughescabfa112016-10-19 14:47:11 -0700381 PTY allocation behavior should match ssh.
Josh Gao191c1542015-12-09 11:26:11 -0800382 """
Josh Gao191c1542015-12-09 11:26:11 -0800383 def check_pty(args):
384 """Checks adb shell PTY allocation.
385
386 Tests |args| for terminal and non-terminal stdin.
387
388 Args:
389 args: -Tt args in a list (e.g. ['-t', '-t']).
390
391 Returns:
392 A tuple (<terminal>, <non-terminal>). True indicates
393 the corresponding shell allocated a remote PTY.
394 """
395 test_cmd = self.device.adb_cmd + ['shell'] + args + ['[ -t 0 ]']
396
397 terminal = subprocess.Popen(
398 test_cmd, stdin=None,
399 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
400 terminal.communicate()
401
402 non_terminal = subprocess.Popen(
403 test_cmd, stdin=subprocess.PIPE,
404 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
405 non_terminal.communicate()
406
407 return (terminal.returncode == 0, non_terminal.returncode == 0)
408
409 # -T: never allocate PTY.
410 self.assertEqual((False, False), check_pty(['-T']))
411
Elliott Hughescabfa112016-10-19 14:47:11 -0700412 # These tests require a new device.
413 if self.device.has_shell_protocol() and os.isatty(sys.stdin.fileno()):
414 # No args: PTY only if stdin is a terminal and shell is interactive,
415 # which is difficult to reliably test from a script.
416 self.assertEqual((False, False), check_pty([]))
Josh Gao191c1542015-12-09 11:26:11 -0800417
Elliott Hughescabfa112016-10-19 14:47:11 -0700418 # -t: PTY if stdin is a terminal.
419 self.assertEqual((True, False), check_pty(['-t']))
Josh Gao191c1542015-12-09 11:26:11 -0800420
421 # -t -t: always allocate PTY.
422 self.assertEqual((True, True), check_pty(['-t', '-t']))
423
Elliott Hughescabfa112016-10-19 14:47:11 -0700424 # -tt: always allocate PTY, POSIX style (http://b/32216152).
425 self.assertEqual((True, True), check_pty(['-tt']))
426
427 # -ttt: ssh has weird even/odd behavior with multiple -t flags, but
428 # we follow the man page instead.
429 self.assertEqual((True, True), check_pty(['-ttt']))
430
431 # -ttx: -x and -tt aren't incompatible (though -Tx would be an error).
432 self.assertEqual((True, True), check_pty(['-ttx']))
433
434 # -Ttt: -tt cancels out -T.
435 self.assertEqual((True, True), check_pty(['-Ttt']))
436
437 # -ttT: -T cancels out -tt.
438 self.assertEqual((False, False), check_pty(['-ttT']))
439
Josh Gao191c1542015-12-09 11:26:11 -0800440 def test_shell_protocol(self):
441 """Tests the shell protocol on the device.
442
443 If the device supports shell protocol, this gives us the ability
444 to separate stdout/stderr and return the exit code directly.
445
446 Bug: http://b/19734861
447 """
David Pursellcf467412016-04-26 13:25:57 -0700448 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800449 raise unittest.SkipTest('shell protocol unsupported on this device')
450
451 # Shell protocol should be used by default.
452 result = self.device.shell_nocheck(
453 shlex.split('echo foo; echo bar >&2; exit 17'))
454 self.assertEqual(17, result[0])
455 self.assertEqual('foo' + self.device.linesep, result[1])
456 self.assertEqual('bar' + self.device.linesep, result[2])
457
458 self.assertEqual(17, self._interactive_shell([], 'exit 17'))
459
460 # -x flag should disable shell protocol.
461 result = self.device.shell_nocheck(
462 shlex.split('-x echo foo; echo bar >&2; exit 17'))
463 self.assertEqual(0, result[0])
464 self.assertEqual('foo{0}bar{0}'.format(self.device.linesep), result[1])
465 self.assertEqual('', result[2])
466
467 self.assertEqual(0, self._interactive_shell(['-x'], 'exit 17'))
468
469 def test_non_interactive_sigint(self):
470 """Tests that SIGINT in a non-interactive shell kills the process.
471
472 This requires the shell protocol in order to detect the broken
473 pipe; raw data transfer mode will only see the break once the
474 subprocess tries to read or write.
475
476 Bug: http://b/23825725
477 """
David Pursellcf467412016-04-26 13:25:57 -0700478 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800479 raise unittest.SkipTest('shell protocol unsupported on this device')
480
481 # Start a long-running process.
482 sleep_proc = subprocess.Popen(
483 self.device.adb_cmd + shlex.split('shell echo $$; sleep 60'),
484 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
485 stderr=subprocess.STDOUT)
486 remote_pid = sleep_proc.stdout.readline().strip()
487 self.assertIsNone(sleep_proc.returncode, 'subprocess terminated early')
488 proc_query = shlex.split('ps {0} | grep {0}'.format(remote_pid))
489
490 # Verify that the process is running, send signal, verify it stopped.
491 self.device.shell(proc_query)
492 os.kill(sleep_proc.pid, signal.SIGINT)
493 sleep_proc.communicate()
Josh Gaoe76b9f32016-10-21 12:40:42 -0700494
495 # It can take some time for the process to receive the signal and die.
496 end_time = time.time() + 3
497 while self.device.shell_nocheck(proc_query)[0] != 1:
498 self.assertFalse(time.time() > end_time,
499 'subprocess failed to terminate in time')
Josh Gao191c1542015-12-09 11:26:11 -0800500
501 def test_non_interactive_stdin(self):
502 """Tests that non-interactive shells send stdin."""
David Pursellcf467412016-04-26 13:25:57 -0700503 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800504 raise unittest.SkipTest('non-interactive stdin unsupported '
505 'on this device')
506
507 # Test both small and large inputs.
508 small_input = 'foo'
509 large_input = '\n'.join(c * 100 for c in (string.ascii_letters +
510 string.digits))
511
512 for input in (small_input, large_input):
513 proc = subprocess.Popen(self.device.adb_cmd + ['shell', 'cat'],
514 stdin=subprocess.PIPE,
515 stdout=subprocess.PIPE,
516 stderr=subprocess.PIPE)
517 stdout, stderr = proc.communicate(input)
518 self.assertEqual(input.splitlines(), stdout.splitlines())
519 self.assertEqual('', stderr)
520
Josh Gaofe50bb72016-06-22 18:27:22 -0700521 def test_sighup(self):
522 """Ensure that SIGHUP gets sent upon non-interactive ctrl-c"""
523 log_path = "/data/local/tmp/adb_signal_test.log"
524
525 # Clear the output file.
526 self.device.shell_nocheck(["echo", ">", log_path])
527
528 script = """
529 trap "echo SIGINT > {path}; exit 0" SIGINT
530 trap "echo SIGHUP > {path}; exit 0" SIGHUP
531 echo Waiting
Josh Gao470622f2016-10-21 13:17:32 -0700532 read
Josh Gaofe50bb72016-06-22 18:27:22 -0700533 """.format(path=log_path)
534
535 script = ";".join([x.strip() for x in script.strip().splitlines()])
536
Josh Gao470622f2016-10-21 13:17:32 -0700537 process = self.device.shell_popen([script], kill_atexit=False,
538 stdin=subprocess.PIPE,
539 stdout=subprocess.PIPE)
Josh Gaofe50bb72016-06-22 18:27:22 -0700540
541 self.assertEqual("Waiting\n", process.stdout.readline())
542 process.send_signal(signal.SIGINT)
543 process.wait()
544
545 # Waiting for the local adb to finish is insufficient, since it hangs
546 # up immediately.
Josh Gao470622f2016-10-21 13:17:32 -0700547 time.sleep(1)
Josh Gaofe50bb72016-06-22 18:27:22 -0700548
549 stdout, _ = self.device.shell(["cat", log_path])
550 self.assertEqual(stdout.strip(), "SIGHUP")
551
Josh Gao191c1542015-12-09 11:26:11 -0800552
553class ArgumentEscapingTest(DeviceTest):
554 def test_shell_escaping(self):
555 """Make sure that argument escaping is somewhat sane."""
556
557 # http://b/19734868
558 # Note that this actually matches ssh(1)'s behavior --- it's
559 # converted to `sh -c echo hello; echo world` which sh interprets
560 # as `sh -c echo` (with an argument to that shell of "hello"),
561 # and then `echo world` back in the first shell.
562 result = self.device.shell(
563 shlex.split("sh -c 'echo hello; echo world'"))[0]
564 result = result.splitlines()
565 self.assertEqual(['', 'world'], result)
566 # If you really wanted "hello" and "world", here's what you'd do:
567 result = self.device.shell(
568 shlex.split(r'echo hello\;echo world'))[0].splitlines()
569 self.assertEqual(['hello', 'world'], result)
570
571 # http://b/15479704
572 result = self.device.shell(shlex.split("'true && echo t'"))[0].strip()
573 self.assertEqual('t', result)
574 result = self.device.shell(
575 shlex.split("sh -c 'true && echo t'"))[0].strip()
576 self.assertEqual('t', result)
577
578 # http://b/20564385
579 result = self.device.shell(shlex.split('FOO=a BAR=b echo t'))[0].strip()
580 self.assertEqual('t', result)
581 result = self.device.shell(
582 shlex.split(r'echo -n 123\;uname'))[0].strip()
583 self.assertEqual('123Linux', result)
584
585 def test_install_argument_escaping(self):
586 """Make sure that install argument escaping works."""
587 # http://b/20323053, http://b/3090932.
588 for file_suffix in ('-text;ls;1.apk', "-Live Hold'em.apk"):
589 tf = tempfile.NamedTemporaryFile('wb', suffix=file_suffix,
590 delete=False)
591 tf.close()
592
593 # Installing bogus .apks fails if the device supports exit codes.
594 try:
595 output = self.device.install(tf.name)
596 except subprocess.CalledProcessError as e:
597 output = e.output
598
599 self.assertIn(file_suffix, output)
600 os.remove(tf.name)
601
602
603class RootUnrootTest(DeviceTest):
604 def _test_root(self):
605 message = self.device.root()
606 if 'adbd cannot run as root in production builds' in message:
607 return
608 self.device.wait()
609 self.assertEqual('root', self.device.shell(['id', '-un'])[0].strip())
610
611 def _test_unroot(self):
612 self.device.unroot()
613 self.device.wait()
614 self.assertEqual('shell', self.device.shell(['id', '-un'])[0].strip())
615
616 def test_root_unroot(self):
617 """Make sure that adb root and adb unroot work, using id(1)."""
618 if self.device.get_prop('ro.debuggable') != '1':
619 raise unittest.SkipTest('requires rootable build')
620
621 original_user = self.device.shell(['id', '-un'])[0].strip()
622 try:
623 if original_user == 'root':
624 self._test_unroot()
625 self._test_root()
626 elif original_user == 'shell':
627 self._test_root()
628 self._test_unroot()
629 finally:
630 if original_user == 'root':
631 self.device.root()
632 else:
633 self.device.unroot()
634 self.device.wait()
635
636
637class TcpIpTest(DeviceTest):
638 def test_tcpip_failure_raises(self):
639 """adb tcpip requires a port.
640
641 Bug: http://b/22636927
642 """
643 self.assertRaises(
644 subprocess.CalledProcessError, self.device.tcpip, '')
645 self.assertRaises(
646 subprocess.CalledProcessError, self.device.tcpip, 'foo')
647
648
649class SystemPropertiesTest(DeviceTest):
650 def test_get_prop(self):
651 self.assertEqual(self.device.get_prop('init.svc.adbd'), 'running')
652
653 @requires_root
654 def test_set_prop(self):
655 prop_name = 'foo.bar'
656 self.device.shell(['setprop', prop_name, '""'])
657
658 self.device.set_prop(prop_name, 'qux')
659 self.assertEqual(
660 self.device.shell(['getprop', prop_name])[0].strip(), 'qux')
661
662
663def compute_md5(string):
664 hsh = hashlib.md5()
665 hsh.update(string)
666 return hsh.hexdigest()
667
668
669def get_md5_prog(device):
670 """Older platforms (pre-L) had the name md5 rather than md5sum."""
671 try:
672 device.shell(['md5sum', '/proc/uptime'])
673 return 'md5sum'
674 except adb.ShellError:
675 return 'md5'
676
677
678class HostFile(object):
679 def __init__(self, handle, checksum):
680 self.handle = handle
681 self.checksum = checksum
682 self.full_path = handle.name
683 self.base_name = os.path.basename(self.full_path)
684
685
686class DeviceFile(object):
687 def __init__(self, checksum, full_path):
688 self.checksum = checksum
689 self.full_path = full_path
690 self.base_name = posixpath.basename(self.full_path)
691
692
693def make_random_host_files(in_dir, num_files):
694 min_size = 1 * (1 << 10)
695 max_size = 16 * (1 << 10)
696
697 files = []
698 for _ in xrange(num_files):
699 file_handle = tempfile.NamedTemporaryFile(dir=in_dir, delete=False)
700
701 size = random.randrange(min_size, max_size, 1024)
702 rand_str = os.urandom(size)
703 file_handle.write(rand_str)
704 file_handle.flush()
705 file_handle.close()
706
707 md5 = compute_md5(rand_str)
708 files.append(HostFile(file_handle, md5))
709 return files
710
711
712def make_random_device_files(device, in_dir, num_files, prefix='device_tmpfile'):
713 min_size = 1 * (1 << 10)
714 max_size = 16 * (1 << 10)
715
716 files = []
717 for file_num in xrange(num_files):
718 size = random.randrange(min_size, max_size, 1024)
719
720 base_name = prefix + str(file_num)
721 full_path = posixpath.join(in_dir, base_name)
722
723 device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
724 'bs={}'.format(size), 'count=1'])
725 dev_md5, _ = device.shell([get_md5_prog(device), full_path])[0].split()
726
727 files.append(DeviceFile(dev_md5, full_path))
728 return files
729
730
731class FileOperationsTest(DeviceTest):
732 SCRATCH_DIR = '/data/local/tmp'
733 DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
734 DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
735
736 def _verify_remote(self, checksum, remote_path):
737 dev_md5, _ = self.device.shell([get_md5_prog(self.device),
738 remote_path])[0].split()
739 self.assertEqual(checksum, dev_md5)
740
741 def _verify_local(self, checksum, local_path):
742 with open(local_path, 'rb') as host_file:
743 host_md5 = compute_md5(host_file.read())
744 self.assertEqual(host_md5, checksum)
745
746 def test_push(self):
747 """Push a randomly generated file to specified device."""
748 kbytes = 512
749 tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
750 rand_str = os.urandom(1024 * kbytes)
751 tmp.write(rand_str)
752 tmp.close()
753
754 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
755 self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
756
757 self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
758 self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
759
760 os.remove(tmp.name)
761
762 def test_push_dir(self):
763 """Push a randomly generated directory of files to the device."""
764 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
765 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
766
767 try:
768 host_dir = tempfile.mkdtemp()
769
770 # Make sure the temp directory isn't setuid, or else adb will complain.
771 os.chmod(host_dir, 0o700)
772
773 # Create 32 random files.
774 temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
775 self.device.push(host_dir, self.DEVICE_TEMP_DIR)
776
777 for temp_file in temp_files:
778 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
779 os.path.basename(host_dir),
780 temp_file.base_name)
781 self._verify_remote(temp_file.checksum, remote_path)
782 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
783 finally:
784 if host_dir is not None:
785 shutil.rmtree(host_dir)
786
787 @unittest.expectedFailure # b/25566053
788 def test_push_empty(self):
789 """Push a directory containing an empty directory to the device."""
790 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
791 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
792
793 try:
794 host_dir = tempfile.mkdtemp()
795
796 # Make sure the temp directory isn't setuid, or else adb will complain.
797 os.chmod(host_dir, 0o700)
798
799 # Create an empty directory.
800 os.mkdir(os.path.join(host_dir, 'empty'))
801
802 self.device.push(host_dir, self.DEVICE_TEMP_DIR)
803
804 test_empty_cmd = ['[', '-d',
805 os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
806 rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
807 self.assertEqual(rc, 0)
808 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
809 finally:
810 if host_dir is not None:
811 shutil.rmtree(host_dir)
812
Josh Gao94dc19f2016-09-14 16:13:50 -0700813 @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
814 def test_push_symlink(self):
815 """Push a symlink.
816
817 Bug: http://b/31491920
818 """
819 try:
820 host_dir = tempfile.mkdtemp()
821
822 # Make sure the temp directory isn't setuid, or else adb will
823 # complain.
824 os.chmod(host_dir, 0o700)
825
826 with open(os.path.join(host_dir, 'foo'), 'w') as f:
827 f.write('foo')
828
829 symlink_path = os.path.join(host_dir, 'symlink')
830 os.symlink('foo', symlink_path)
831
832 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
833 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
834 self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
835 rc, out, _ = self.device.shell_nocheck(
836 ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
837 self.assertEqual(0, rc)
838 self.assertEqual(out.strip(), 'foo')
839 finally:
840 if host_dir is not None:
841 shutil.rmtree(host_dir)
842
Josh Gao191c1542015-12-09 11:26:11 -0800843 def test_multiple_push(self):
844 """Push multiple files to the device in one adb push command.
845
846 Bug: http://b/25324823
847 """
848
849 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
850 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
851
852 try:
853 host_dir = tempfile.mkdtemp()
854
855 # Create some random files and a subdirectory containing more files.
856 temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
857
Josh Gao255c5c82016-03-03 14:49:02 -0800858 subdir = os.path.join(host_dir, 'subdir')
Josh Gao191c1542015-12-09 11:26:11 -0800859 os.mkdir(subdir)
860 subdir_temp_files = make_random_host_files(in_dir=subdir,
861 num_files=4)
862
863 paths = map(lambda temp_file: temp_file.full_path, temp_files)
864 paths.append(subdir)
865 self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
866
867 for temp_file in temp_files:
868 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
869 temp_file.base_name)
870 self._verify_remote(temp_file.checksum, remote_path)
871
872 for subdir_temp_file in subdir_temp_files:
873 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
874 # BROKEN: http://b/25394682
Josh Gao255c5c82016-03-03 14:49:02 -0800875 # 'subdir';
Josh Gao191c1542015-12-09 11:26:11 -0800876 temp_file.base_name)
877 self._verify_remote(temp_file.checksum, remote_path)
878
879
880 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
881 finally:
882 if host_dir is not None:
883 shutil.rmtree(host_dir)
884
Josh Gaoafcdcd72016-02-19 15:55:55 -0800885 @requires_non_root
886 def test_push_error_reporting(self):
887 """Make sure that errors that occur while pushing a file get reported
888
889 Bug: http://b/26816782
890 """
891 with tempfile.NamedTemporaryFile() as tmp_file:
892 tmp_file.write('\0' * 1024 * 1024)
893 tmp_file.flush()
894 try:
895 self.device.push(local=tmp_file.name, remote='/system/')
Josh Gao255c5c82016-03-03 14:49:02 -0800896 self.fail('push should not have succeeded')
Josh Gaoafcdcd72016-02-19 15:55:55 -0800897 except subprocess.CalledProcessError as e:
898 output = e.output
899
Josh Gao79ce3fe2016-11-18 15:31:11 -0800900 self.assertTrue('Permission denied' in output or
901 'Read-only file system' in output)
Josh Gao191c1542015-12-09 11:26:11 -0800902
903 def _test_pull(self, remote_file, checksum):
904 tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
905 tmp_write.close()
906 self.device.pull(remote=remote_file, local=tmp_write.name)
907 with open(tmp_write.name, 'rb') as tmp_read:
908 host_contents = tmp_read.read()
909 host_md5 = compute_md5(host_contents)
910 self.assertEqual(checksum, host_md5)
911 os.remove(tmp_write.name)
912
913 @requires_non_root
914 def test_pull_error_reporting(self):
915 self.device.shell(['touch', self.DEVICE_TEMP_FILE])
916 self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
917
918 try:
919 output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
920 except subprocess.CalledProcessError as e:
921 output = e.output
922
923 self.assertIn('Permission denied', output)
924
925 self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
926
927 def test_pull(self):
928 """Pull a randomly generated file from specified device."""
929 kbytes = 512
930 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
931 cmd = ['dd', 'if=/dev/urandom',
932 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
933 'count={}'.format(kbytes)]
934 self.device.shell(cmd)
935 dev_md5, _ = self.device.shell(
936 [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
937 self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
938 self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
939
940 def test_pull_dir(self):
941 """Pull a randomly generated directory of files from the device."""
942 try:
943 host_dir = tempfile.mkdtemp()
944
945 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
946 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
947
948 # Populate device directory with random files.
949 temp_files = make_random_device_files(
950 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
951
952 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
953
954 for temp_file in temp_files:
Josh Gaoce8f2cd2015-12-09 14:20:23 -0800955 host_path = os.path.join(
956 host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
957 temp_file.base_name)
958 self._verify_local(temp_file.checksum, host_path)
Josh Gao191c1542015-12-09 11:26:11 -0800959
960 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
961 finally:
962 if host_dir is not None:
963 shutil.rmtree(host_dir)
964
Josh Gao1e611a32016-02-26 13:26:55 -0800965 def test_pull_dir_symlink(self):
966 """Pull a directory into a symlink to a directory.
967
968 Bug: http://b/27362811
969 """
Josh Gao255c5c82016-03-03 14:49:02 -0800970 if os.name != 'posix':
Josh Gao1e611a32016-02-26 13:26:55 -0800971 raise unittest.SkipTest('requires POSIX')
972
973 try:
974 host_dir = tempfile.mkdtemp()
Josh Gao255c5c82016-03-03 14:49:02 -0800975 real_dir = os.path.join(host_dir, 'dir')
976 symlink = os.path.join(host_dir, 'symlink')
Josh Gao1e611a32016-02-26 13:26:55 -0800977 os.mkdir(real_dir)
978 os.symlink(real_dir, symlink)
979
980 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
981 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
982
983 # Populate device directory with random files.
984 temp_files = make_random_device_files(
985 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
986
987 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
988
989 for temp_file in temp_files:
990 host_path = os.path.join(
991 real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
992 temp_file.base_name)
993 self._verify_local(temp_file.checksum, host_path)
994
995 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
996 finally:
997 if host_dir is not None:
998 shutil.rmtree(host_dir)
999
1000 def test_pull_dir_symlink_collision(self):
1001 """Pull a directory into a colliding symlink to directory."""
Josh Gao255c5c82016-03-03 14:49:02 -08001002 if os.name != 'posix':
Josh Gao1e611a32016-02-26 13:26:55 -08001003 raise unittest.SkipTest('requires POSIX')
1004
1005 try:
1006 host_dir = tempfile.mkdtemp()
Josh Gao255c5c82016-03-03 14:49:02 -08001007 real_dir = os.path.join(host_dir, 'real')
Josh Gao1e611a32016-02-26 13:26:55 -08001008 tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
1009 symlink = os.path.join(host_dir, tmp_dirname)
1010 os.mkdir(real_dir)
1011 os.symlink(real_dir, symlink)
1012
1013 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1014 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
1015
1016 # Populate device directory with random files.
1017 temp_files = make_random_device_files(
1018 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1019
1020 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
1021
1022 for temp_file in temp_files:
1023 host_path = os.path.join(real_dir, temp_file.base_name)
1024 self._verify_local(temp_file.checksum, host_path)
1025
1026 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1027 finally:
1028 if host_dir is not None:
1029 shutil.rmtree(host_dir)
1030
Josh Gao89ec3a82016-03-02 16:00:02 -08001031 def test_pull_dir_nonexistent(self):
1032 """Pull a directory of files from the device to a nonexistent path."""
1033 try:
1034 host_dir = tempfile.mkdtemp()
1035 dest_dir = os.path.join(host_dir, 'dest')
1036
1037 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1038 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
1039
1040 # Populate device directory with random files.
1041 temp_files = make_random_device_files(
1042 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1043
1044 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
1045
1046 for temp_file in temp_files:
1047 host_path = os.path.join(dest_dir, temp_file.base_name)
1048 self._verify_local(temp_file.checksum, host_path)
1049
1050 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1051 finally:
1052 if host_dir is not None:
1053 shutil.rmtree(host_dir)
1054
Josh Gaof2642242015-12-09 14:03:30 -08001055 def test_pull_symlink_dir(self):
1056 """Pull a symlink to a directory of symlinks to files."""
1057 try:
1058 host_dir = tempfile.mkdtemp()
1059
1060 remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
1061 remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
1062 remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
1063
1064 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1065 self.device.shell(['mkdir', '-p', remote_dir, remote_links])
1066 self.device.shell(['ln', '-s', remote_links, remote_symlink])
1067
1068 # Populate device directory with random files.
1069 temp_files = make_random_device_files(
1070 self.device, in_dir=remote_dir, num_files=32)
1071
1072 for temp_file in temp_files:
1073 self.device.shell(
1074 ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
1075 posixpath.join(remote_links, temp_file.base_name)])
1076
1077 self.device.pull(remote=remote_symlink, local=host_dir)
1078
1079 for temp_file in temp_files:
1080 host_path = os.path.join(
1081 host_dir, 'symlink', temp_file.base_name)
1082 self._verify_local(temp_file.checksum, host_path)
1083
1084 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1085 finally:
1086 if host_dir is not None:
1087 shutil.rmtree(host_dir)
1088
Josh Gao191c1542015-12-09 11:26:11 -08001089 def test_pull_empty(self):
1090 """Pull a directory containing an empty directory from the device."""
1091 try:
1092 host_dir = tempfile.mkdtemp()
1093
1094 remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
1095 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1096 self.device.shell(['mkdir', '-p', remote_empty_path])
1097
1098 self.device.pull(remote=remote_empty_path, local=host_dir)
1099 self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
1100 finally:
1101 if host_dir is not None:
1102 shutil.rmtree(host_dir)
1103
1104 def test_multiple_pull(self):
1105 """Pull a randomly generated directory of files from the device."""
1106
1107 try:
1108 host_dir = tempfile.mkdtemp()
1109
Josh Gao255c5c82016-03-03 14:49:02 -08001110 subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
Josh Gao191c1542015-12-09 11:26:11 -08001111 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1112 self.device.shell(['mkdir', '-p', subdir])
1113
1114 # Create some random files and a subdirectory containing more files.
1115 temp_files = make_random_device_files(
1116 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
1117
1118 subdir_temp_files = make_random_device_files(
1119 self.device, in_dir=subdir, num_files=4, prefix='subdir_')
1120
1121 paths = map(lambda temp_file: temp_file.full_path, temp_files)
1122 paths.append(subdir)
1123 self.device._simple_call(['pull'] + paths + [host_dir])
1124
1125 for temp_file in temp_files:
1126 local_path = os.path.join(host_dir, temp_file.base_name)
1127 self._verify_local(temp_file.checksum, local_path)
1128
1129 for subdir_temp_file in subdir_temp_files:
1130 local_path = os.path.join(host_dir,
Josh Gao255c5c82016-03-03 14:49:02 -08001131 'subdir',
Josh Gao191c1542015-12-09 11:26:11 -08001132 subdir_temp_file.base_name)
1133 self._verify_local(subdir_temp_file.checksum, local_path)
1134
1135 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1136 finally:
1137 if host_dir is not None:
1138 shutil.rmtree(host_dir)
1139
Dan Albert06b0d6b2017-05-18 22:56:48 -07001140 def verify_sync(self, device, temp_files, device_dir):
1141 """Verifies that a list of temp files was synced to the device."""
1142 # Confirm that every file on the device mirrors that on the host.
1143 for temp_file in temp_files:
1144 device_full_path = posixpath.join(
1145 device_dir, temp_file.base_name)
1146 dev_md5, _ = device.shell(
1147 [get_md5_prog(self.device), device_full_path])[0].split()
1148 self.assertEqual(temp_file.checksum, dev_md5)
1149
Josh Gao191c1542015-12-09 11:26:11 -08001150 def test_sync(self):
Dan Albert06b0d6b2017-05-18 22:56:48 -07001151 """Sync a host directory to the data partition."""
Josh Gao191c1542015-12-09 11:26:11 -08001152
1153 try:
1154 base_dir = tempfile.mkdtemp()
1155
1156 # Create mirror device directory hierarchy within base_dir.
1157 full_dir_path = base_dir + self.DEVICE_TEMP_DIR
1158 os.makedirs(full_dir_path)
1159
1160 # Create 32 random files within the host mirror.
Dan Albert06b0d6b2017-05-18 22:56:48 -07001161 temp_files = make_random_host_files(
1162 in_dir=full_dir_path, num_files=32)
Josh Gao191c1542015-12-09 11:26:11 -08001163
Dan Albert06b0d6b2017-05-18 22:56:48 -07001164 # Clean up any stale files on the device.
Dan Albertdef4aae2017-05-18 13:52:45 -07001165 device = adb.get_device() # pylint: disable=no-member
Josh Gao191c1542015-12-09 11:26:11 -08001166 device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1167
Dan Albertdef4aae2017-05-18 13:52:45 -07001168 old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
1169 os.environ['ANDROID_PRODUCT_OUT'] = base_dir
Josh Gao191c1542015-12-09 11:26:11 -08001170 device.sync('data')
Dan Albertdef4aae2017-05-18 13:52:45 -07001171 if old_product_out is None:
1172 del os.environ['ANDROID_PRODUCT_OUT']
1173 else:
1174 os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
Josh Gao191c1542015-12-09 11:26:11 -08001175
Dan Albert06b0d6b2017-05-18 22:56:48 -07001176 self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
Josh Gao191c1542015-12-09 11:26:11 -08001177
Dan Albert06b0d6b2017-05-18 22:56:48 -07001178 #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
Josh Gao191c1542015-12-09 11:26:11 -08001179 finally:
1180 if base_dir is not None:
1181 shutil.rmtree(base_dir)
1182
Dan Albert06b0d6b2017-05-18 22:56:48 -07001183 def test_push_sync(self):
1184 """Sync a host directory to a specific path."""
1185
1186 try:
1187 temp_dir = tempfile.mkdtemp()
1188 temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
1189
1190 device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
1191
1192 # Clean up any stale files on the device.
1193 device = adb.get_device() # pylint: disable=no-member
1194 device.shell(['rm', '-rf', device_dir])
1195
1196 device.push(temp_dir, device_dir, sync=True)
1197
1198 self.verify_sync(device, temp_files, device_dir)
1199
1200 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1201 finally:
1202 if temp_dir is not None:
1203 shutil.rmtree(temp_dir)
1204
Josh Gao191c1542015-12-09 11:26:11 -08001205 def test_unicode_paths(self):
1206 """Ensure that we can support non-ASCII paths, even on Windows."""
1207 name = u'로보카 폴리'
1208
1209 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1210 remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
1211
1212 ## push.
1213 tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
1214 tf.close()
1215 self.device.push(tf.name, remote_path)
1216 os.remove(tf.name)
1217 self.assertFalse(os.path.exists(tf.name))
1218
1219 # Verify that the device ended up with the expected UTF-8 path
1220 output = self.device.shell(
1221 ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
1222 self.assertEqual(remote_path.encode('utf-8'), output)
1223
1224 # pull.
1225 self.device.pull(remote_path, tf.name)
1226 self.assertTrue(os.path.exists(tf.name))
1227 os.remove(tf.name)
1228 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1229
1230
Yabin Cuib5e11412017-03-10 16:01:01 -08001231class DeviceOfflineTest(DeviceTest):
1232 def _get_device_state(self, serialno):
1233 output = subprocess.check_output(self.device.adb_cmd + ['devices'])
1234 for line in output.split('\n'):
1235 m = re.match('(\S+)\s+(\S+)', line)
1236 if m and m.group(1) == serialno:
1237 return m.group(2)
1238 return None
1239
Josh Gao33d14b82017-09-13 14:51:23 -07001240 def disabled_test_killed_when_pushing_a_large_file(self):
Yabin Cuib5e11412017-03-10 16:01:01 -08001241 """
1242 While running adb push with a large file, kill adb server.
1243 Occasionally the device becomes offline. Because the device is still
1244 reading data without realizing that the adb server has been restarted.
1245 Test if we can bring the device online automatically now.
1246 http://b/32952319
1247 """
1248 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1249 # 1. Push a large file
1250 file_path = 'tmp_large_file'
1251 try:
1252 fh = open(file_path, 'w')
1253 fh.write('\0' * (100 * 1024 * 1024))
1254 fh.close()
1255 subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
1256 time.sleep(0.1)
1257 # 2. Kill the adb server
1258 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1259 subproc.terminate()
1260 finally:
1261 try:
1262 os.unlink(file_path)
1263 except:
1264 pass
1265 # 3. See if the device still exist.
1266 # Sleep to wait for the adb server exit.
1267 time.sleep(0.5)
1268 # 4. The device should be online
1269 self.assertEqual(self._get_device_state(serialno), 'device')
1270
Josh Gao33d14b82017-09-13 14:51:23 -07001271 def disabled_test_killed_when_pulling_a_large_file(self):
Yabin Cuib5e11412017-03-10 16:01:01 -08001272 """
1273 While running adb pull with a large file, kill adb server.
1274 Occasionally the device can't be connected. Because the device is trying to
1275 send a message larger than what is expected by the adb server.
1276 Test if we can bring the device online automatically now.
1277 """
1278 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1279 file_path = 'tmp_large_file'
1280 try:
1281 # 1. Create a large file on device.
1282 self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
1283 'bs=1000000', 'count=100'])
1284 # 2. Pull the large file on host.
1285 subproc = subprocess.Popen(self.device.adb_cmd +
1286 ['pull','/data/local/tmp/tmp_large_file', file_path])
1287 time.sleep(0.1)
1288 # 3. Kill the adb server
1289 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1290 subproc.terminate()
1291 finally:
1292 try:
1293 os.unlink(file_path)
1294 except:
1295 pass
1296 # 4. See if the device still exist.
1297 # Sleep to wait for the adb server exit.
1298 time.sleep(0.5)
1299 self.assertEqual(self._get_device_state(serialno), 'device')
1300
1301
Josh Gaoef3d3432017-05-02 15:01:09 -07001302 def test_packet_size_regression(self):
1303 """Test for http://b/37783561
1304
1305 Receiving packets of a length divisible by 512 but not 1024 resulted in
1306 the adb client waiting indefinitely for more input.
1307 """
1308 # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
1309 # Probe some surrounding values as well, for the hell of it.
1310 for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
1311 cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
1312 'echo', 'foo']
1313 rc, stdout, _ = self.device.shell_nocheck(cmd)
1314
1315 self.assertEqual(0, rc)
1316
1317 # Output should be '\0' * length, followed by "foo\n"
1318 self.assertEqual(length, len(stdout) - 4)
1319 self.assertEqual(stdout, "\0" * length + "foo\n")
1320
1321
Josh Gao191c1542015-12-09 11:26:11 -08001322def main():
1323 random.seed(0)
1324 if len(adb.get_devices()) > 0:
1325 suite = unittest.TestLoader().loadTestsFromName(__name__)
1326 unittest.TextTestRunner(verbosity=3).run(suite)
1327 else:
1328 print('Test suite must be run with attached devices')
1329
1330
1331if __name__ == '__main__':
1332 main()