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