blob: c8a4d9bf9aa4b794931c7f8269733bd92c344da3 [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
Josh Gao49e3c632015-12-09 11:26:11 -0800753 def test_push_empty(self):
754 """Push a directory containing an empty directory to the device."""
755 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
756 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
757
758 try:
759 host_dir = tempfile.mkdtemp()
760
761 # Make sure the temp directory isn't setuid, or else adb will complain.
762 os.chmod(host_dir, 0o700)
763
764 # Create an empty directory.
Josh Gaoc117edb2018-08-08 14:25:41 -0700765 empty_dir_path = os.path.join(host_dir, 'empty')
766 os.mkdir(empty_dir_path);
Josh Gao49e3c632015-12-09 11:26:11 -0800767
Josh Gaoc117edb2018-08-08 14:25:41 -0700768 self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
Josh Gao49e3c632015-12-09 11:26:11 -0800769
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
Josh Gaof9671172018-06-28 18:43:19 -0700869 @requires_non_root
870 def test_push_directory_creation(self):
871 """Regression test for directory creation.
872
873 Bug: http://b/110953234
874 """
875 with tempfile.NamedTemporaryFile() as tmp_file:
876 tmp_file.write('\0' * 1024 * 1024)
877 tmp_file.flush()
878 remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
879 self.device.shell(['rm', '-rf', remote_path])
880
881 remote_path += '/filename'
882 self.device.push(local=tmp_file.name, remote=remote_path)
883
Josh Gao49e3c632015-12-09 11:26:11 -0800884 def _test_pull(self, remote_file, checksum):
885 tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
886 tmp_write.close()
887 self.device.pull(remote=remote_file, local=tmp_write.name)
888 with open(tmp_write.name, 'rb') as tmp_read:
889 host_contents = tmp_read.read()
890 host_md5 = compute_md5(host_contents)
891 self.assertEqual(checksum, host_md5)
892 os.remove(tmp_write.name)
893
894 @requires_non_root
895 def test_pull_error_reporting(self):
896 self.device.shell(['touch', self.DEVICE_TEMP_FILE])
897 self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
898
899 try:
900 output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
901 except subprocess.CalledProcessError as e:
902 output = e.output
903
904 self.assertIn('Permission denied', output)
905
906 self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
907
908 def test_pull(self):
909 """Pull a randomly generated file from specified device."""
910 kbytes = 512
911 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
912 cmd = ['dd', 'if=/dev/urandom',
913 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
914 'count={}'.format(kbytes)]
915 self.device.shell(cmd)
916 dev_md5, _ = self.device.shell(
917 [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
918 self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
919 self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
920
921 def test_pull_dir(self):
922 """Pull a randomly generated directory of files from the device."""
923 try:
924 host_dir = tempfile.mkdtemp()
925
926 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
927 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
928
929 # Populate device directory with random files.
930 temp_files = make_random_device_files(
931 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
932
933 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
934
935 for temp_file in temp_files:
Josh Gao38752792015-12-09 14:20:23 -0800936 host_path = os.path.join(
937 host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
938 temp_file.base_name)
939 self._verify_local(temp_file.checksum, host_path)
Josh Gao49e3c632015-12-09 11:26:11 -0800940
941 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
942 finally:
943 if host_dir is not None:
944 shutil.rmtree(host_dir)
945
Josh Gao49726bc2016-02-26 13:26:55 -0800946 def test_pull_dir_symlink(self):
947 """Pull a directory into a symlink to a directory.
948
949 Bug: http://b/27362811
950 """
Josh Gao18f74202016-03-03 14:49:02 -0800951 if os.name != 'posix':
Josh Gao49726bc2016-02-26 13:26:55 -0800952 raise unittest.SkipTest('requires POSIX')
953
954 try:
955 host_dir = tempfile.mkdtemp()
Josh Gao18f74202016-03-03 14:49:02 -0800956 real_dir = os.path.join(host_dir, 'dir')
957 symlink = os.path.join(host_dir, 'symlink')
Josh Gao49726bc2016-02-26 13:26:55 -0800958 os.mkdir(real_dir)
959 os.symlink(real_dir, symlink)
960
961 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
962 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
963
964 # Populate device directory with random files.
965 temp_files = make_random_device_files(
966 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
967
968 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
969
970 for temp_file in temp_files:
971 host_path = os.path.join(
972 real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
973 temp_file.base_name)
974 self._verify_local(temp_file.checksum, host_path)
975
976 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
977 finally:
978 if host_dir is not None:
979 shutil.rmtree(host_dir)
980
981 def test_pull_dir_symlink_collision(self):
982 """Pull a directory into a colliding symlink to directory."""
Josh Gao18f74202016-03-03 14:49:02 -0800983 if os.name != 'posix':
Josh Gao49726bc2016-02-26 13:26:55 -0800984 raise unittest.SkipTest('requires POSIX')
985
986 try:
987 host_dir = tempfile.mkdtemp()
Josh Gao18f74202016-03-03 14:49:02 -0800988 real_dir = os.path.join(host_dir, 'real')
Josh Gao49726bc2016-02-26 13:26:55 -0800989 tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
990 symlink = os.path.join(host_dir, tmp_dirname)
991 os.mkdir(real_dir)
992 os.symlink(real_dir, symlink)
993
994 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
995 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
996
997 # Populate device directory with random files.
998 temp_files = make_random_device_files(
999 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1000
1001 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
1002
1003 for temp_file in temp_files:
1004 host_path = os.path.join(real_dir, temp_file.base_name)
1005 self._verify_local(temp_file.checksum, host_path)
1006
1007 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1008 finally:
1009 if host_dir is not None:
1010 shutil.rmtree(host_dir)
1011
Josh Gaoa842b382016-03-02 16:00:02 -08001012 def test_pull_dir_nonexistent(self):
1013 """Pull a directory of files from the device to a nonexistent path."""
1014 try:
1015 host_dir = tempfile.mkdtemp()
1016 dest_dir = os.path.join(host_dir, 'dest')
1017
1018 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1019 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
1020
1021 # Populate device directory with random files.
1022 temp_files = make_random_device_files(
1023 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1024
1025 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
1026
1027 for temp_file in temp_files:
1028 host_path = os.path.join(dest_dir, temp_file.base_name)
1029 self._verify_local(temp_file.checksum, host_path)
1030
1031 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1032 finally:
1033 if host_dir is not None:
1034 shutil.rmtree(host_dir)
1035
Josh Gaod9a2fd62015-12-09 14:03:30 -08001036 def test_pull_symlink_dir(self):
1037 """Pull a symlink to a directory of symlinks to files."""
1038 try:
1039 host_dir = tempfile.mkdtemp()
1040
1041 remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
1042 remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
1043 remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
1044
1045 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1046 self.device.shell(['mkdir', '-p', remote_dir, remote_links])
1047 self.device.shell(['ln', '-s', remote_links, remote_symlink])
1048
1049 # Populate device directory with random files.
1050 temp_files = make_random_device_files(
1051 self.device, in_dir=remote_dir, num_files=32)
1052
1053 for temp_file in temp_files:
1054 self.device.shell(
1055 ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
1056 posixpath.join(remote_links, temp_file.base_name)])
1057
1058 self.device.pull(remote=remote_symlink, local=host_dir)
1059
1060 for temp_file in temp_files:
1061 host_path = os.path.join(
1062 host_dir, 'symlink', temp_file.base_name)
1063 self._verify_local(temp_file.checksum, host_path)
1064
1065 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1066 finally:
1067 if host_dir is not None:
1068 shutil.rmtree(host_dir)
1069
Josh Gao49e3c632015-12-09 11:26:11 -08001070 def test_pull_empty(self):
1071 """Pull a directory containing an empty directory from the device."""
1072 try:
1073 host_dir = tempfile.mkdtemp()
1074
1075 remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
1076 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1077 self.device.shell(['mkdir', '-p', remote_empty_path])
1078
1079 self.device.pull(remote=remote_empty_path, local=host_dir)
1080 self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
1081 finally:
1082 if host_dir is not None:
1083 shutil.rmtree(host_dir)
1084
1085 def test_multiple_pull(self):
1086 """Pull a randomly generated directory of files from the device."""
1087
1088 try:
1089 host_dir = tempfile.mkdtemp()
1090
Josh Gao18f74202016-03-03 14:49:02 -08001091 subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
Josh Gao49e3c632015-12-09 11:26:11 -08001092 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1093 self.device.shell(['mkdir', '-p', subdir])
1094
1095 # Create some random files and a subdirectory containing more files.
1096 temp_files = make_random_device_files(
1097 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
1098
1099 subdir_temp_files = make_random_device_files(
1100 self.device, in_dir=subdir, num_files=4, prefix='subdir_')
1101
1102 paths = map(lambda temp_file: temp_file.full_path, temp_files)
1103 paths.append(subdir)
1104 self.device._simple_call(['pull'] + paths + [host_dir])
1105
1106 for temp_file in temp_files:
1107 local_path = os.path.join(host_dir, temp_file.base_name)
1108 self._verify_local(temp_file.checksum, local_path)
1109
1110 for subdir_temp_file in subdir_temp_files:
1111 local_path = os.path.join(host_dir,
Josh Gao18f74202016-03-03 14:49:02 -08001112 'subdir',
Josh Gao49e3c632015-12-09 11:26:11 -08001113 subdir_temp_file.base_name)
1114 self._verify_local(subdir_temp_file.checksum, local_path)
1115
1116 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1117 finally:
1118 if host_dir is not None:
1119 shutil.rmtree(host_dir)
1120
Dan Albert8449e062017-05-18 22:56:48 -07001121 def verify_sync(self, device, temp_files, device_dir):
1122 """Verifies that a list of temp files was synced to the device."""
1123 # Confirm that every file on the device mirrors that on the host.
1124 for temp_file in temp_files:
1125 device_full_path = posixpath.join(
1126 device_dir, temp_file.base_name)
1127 dev_md5, _ = device.shell(
1128 [get_md5_prog(self.device), device_full_path])[0].split()
1129 self.assertEqual(temp_file.checksum, dev_md5)
1130
Josh Gao49e3c632015-12-09 11:26:11 -08001131 def test_sync(self):
Dan Albert8449e062017-05-18 22:56:48 -07001132 """Sync a host directory to the data partition."""
Josh Gao49e3c632015-12-09 11:26:11 -08001133
1134 try:
1135 base_dir = tempfile.mkdtemp()
1136
1137 # Create mirror device directory hierarchy within base_dir.
1138 full_dir_path = base_dir + self.DEVICE_TEMP_DIR
1139 os.makedirs(full_dir_path)
1140
1141 # Create 32 random files within the host mirror.
Dan Albert8449e062017-05-18 22:56:48 -07001142 temp_files = make_random_host_files(
1143 in_dir=full_dir_path, num_files=32)
Josh Gao49e3c632015-12-09 11:26:11 -08001144
Dan Albert8449e062017-05-18 22:56:48 -07001145 # Clean up any stale files on the device.
Dan Albertb29a57b2017-05-18 13:52:45 -07001146 device = adb.get_device() # pylint: disable=no-member
Josh Gao49e3c632015-12-09 11:26:11 -08001147 device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1148
Dan Albertb29a57b2017-05-18 13:52:45 -07001149 old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
1150 os.environ['ANDROID_PRODUCT_OUT'] = base_dir
Josh Gao49e3c632015-12-09 11:26:11 -08001151 device.sync('data')
Dan Albertb29a57b2017-05-18 13:52:45 -07001152 if old_product_out is None:
1153 del os.environ['ANDROID_PRODUCT_OUT']
1154 else:
1155 os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
Josh Gao49e3c632015-12-09 11:26:11 -08001156
Dan Albert8449e062017-05-18 22:56:48 -07001157 self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
Josh Gao49e3c632015-12-09 11:26:11 -08001158
Dan Albert8449e062017-05-18 22:56:48 -07001159 #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
Josh Gao49e3c632015-12-09 11:26:11 -08001160 finally:
1161 if base_dir is not None:
1162 shutil.rmtree(base_dir)
1163
Dan Albert8449e062017-05-18 22:56:48 -07001164 def test_push_sync(self):
1165 """Sync a host directory to a specific path."""
1166
1167 try:
1168 temp_dir = tempfile.mkdtemp()
1169 temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
1170
1171 device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
1172
1173 # Clean up any stale files on the device.
1174 device = adb.get_device() # pylint: disable=no-member
1175 device.shell(['rm', '-rf', device_dir])
1176
1177 device.push(temp_dir, device_dir, sync=True)
1178
1179 self.verify_sync(device, temp_files, device_dir)
1180
1181 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1182 finally:
1183 if temp_dir is not None:
1184 shutil.rmtree(temp_dir)
1185
Josh Gao49e3c632015-12-09 11:26:11 -08001186 def test_unicode_paths(self):
1187 """Ensure that we can support non-ASCII paths, even on Windows."""
1188 name = u'로보카 폴리'
1189
1190 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1191 remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
1192
1193 ## push.
1194 tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
1195 tf.close()
1196 self.device.push(tf.name, remote_path)
1197 os.remove(tf.name)
1198 self.assertFalse(os.path.exists(tf.name))
1199
1200 # Verify that the device ended up with the expected UTF-8 path
1201 output = self.device.shell(
1202 ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
Josh Gao4f8504e2018-03-19 16:09:05 -07001203 self.assertEqual(remote_path, output)
Josh Gao49e3c632015-12-09 11:26:11 -08001204
1205 # pull.
1206 self.device.pull(remote_path, tf.name)
1207 self.assertTrue(os.path.exists(tf.name))
1208 os.remove(tf.name)
1209 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1210
1211
Yabin Cui3cf1b362017-03-10 16:01:01 -08001212class DeviceOfflineTest(DeviceTest):
1213 def _get_device_state(self, serialno):
1214 output = subprocess.check_output(self.device.adb_cmd + ['devices'])
1215 for line in output.split('\n'):
1216 m = re.match('(\S+)\s+(\S+)', line)
1217 if m and m.group(1) == serialno:
1218 return m.group(2)
1219 return None
1220
Josh Gao6e0ed552017-09-13 14:51:23 -07001221 def disabled_test_killed_when_pushing_a_large_file(self):
Yabin Cui3cf1b362017-03-10 16:01:01 -08001222 """
1223 While running adb push with a large file, kill adb server.
1224 Occasionally the device becomes offline. Because the device is still
1225 reading data without realizing that the adb server has been restarted.
1226 Test if we can bring the device online automatically now.
1227 http://b/32952319
1228 """
1229 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1230 # 1. Push a large file
1231 file_path = 'tmp_large_file'
1232 try:
1233 fh = open(file_path, 'w')
1234 fh.write('\0' * (100 * 1024 * 1024))
1235 fh.close()
1236 subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
1237 time.sleep(0.1)
1238 # 2. Kill the adb server
1239 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1240 subproc.terminate()
1241 finally:
1242 try:
1243 os.unlink(file_path)
1244 except:
1245 pass
1246 # 3. See if the device still exist.
1247 # Sleep to wait for the adb server exit.
1248 time.sleep(0.5)
1249 # 4. The device should be online
1250 self.assertEqual(self._get_device_state(serialno), 'device')
1251
Josh Gao6e0ed552017-09-13 14:51:23 -07001252 def disabled_test_killed_when_pulling_a_large_file(self):
Yabin Cui3cf1b362017-03-10 16:01:01 -08001253 """
1254 While running adb pull with a large file, kill adb server.
1255 Occasionally the device can't be connected. Because the device is trying to
1256 send a message larger than what is expected by the adb server.
1257 Test if we can bring the device online automatically now.
1258 """
1259 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1260 file_path = 'tmp_large_file'
1261 try:
1262 # 1. Create a large file on device.
1263 self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
1264 'bs=1000000', 'count=100'])
1265 # 2. Pull the large file on host.
1266 subproc = subprocess.Popen(self.device.adb_cmd +
1267 ['pull','/data/local/tmp/tmp_large_file', file_path])
1268 time.sleep(0.1)
1269 # 3. Kill the adb server
1270 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1271 subproc.terminate()
1272 finally:
1273 try:
1274 os.unlink(file_path)
1275 except:
1276 pass
1277 # 4. See if the device still exist.
1278 # Sleep to wait for the adb server exit.
1279 time.sleep(0.5)
1280 self.assertEqual(self._get_device_state(serialno), 'device')
1281
1282
Josh Gao3734cf02017-05-02 15:01:09 -07001283 def test_packet_size_regression(self):
1284 """Test for http://b/37783561
1285
1286 Receiving packets of a length divisible by 512 but not 1024 resulted in
1287 the adb client waiting indefinitely for more input.
1288 """
1289 # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
1290 # Probe some surrounding values as well, for the hell of it.
Josh Gaoc7f2d192018-04-10 14:35:06 -07001291 for base in [512] + range(1024, 1024 * 16, 1024):
1292 for offset in [-6, -5, -4]:
1293 length = base + offset
1294 cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
1295 'echo', 'foo']
1296 rc, stdout, _ = self.device.shell_nocheck(cmd)
Josh Gao3734cf02017-05-02 15:01:09 -07001297
Josh Gaoc7f2d192018-04-10 14:35:06 -07001298 self.assertEqual(0, rc)
Josh Gao3734cf02017-05-02 15:01:09 -07001299
Josh Gaoc7f2d192018-04-10 14:35:06 -07001300 # Output should be '\0' * length, followed by "foo\n"
1301 self.assertEqual(length, len(stdout) - 4)
1302 self.assertEqual(stdout, "\0" * length + "foo\n")
Josh Gao3734cf02017-05-02 15:01:09 -07001303
1304
Josh Gao49e3c632015-12-09 11:26:11 -08001305def main():
1306 random.seed(0)
1307 if len(adb.get_devices()) > 0:
1308 suite = unittest.TestLoader().loadTestsFromName(__name__)
1309 unittest.TextTestRunner(verbosity=3).run(suite)
1310 else:
1311 print('Test suite must be run with attached devices')
1312
1313
1314if __name__ == '__main__':
1315 main()