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