blob: 4abe7a7d6b16c620235f4550296d77440dfca1b2 [file] [log] [blame]
Josh Gao191c1542015-12-09 11:26:11 -08001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2015 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18from __future__ import print_function
19
20import contextlib
21import hashlib
22import os
23import posixpath
24import random
25import re
26import shlex
27import shutil
28import signal
29import socket
30import string
31import subprocess
32import sys
33import tempfile
Josh Gaoc970aef2018-03-19 15:35:11 -070034import threading
Josh Gaofe50bb72016-06-22 18:27:22 -070035import time
Josh Gao191c1542015-12-09 11:26:11 -080036import unittest
37
Josh Gao191c1542015-12-09 11:26:11 -080038import adb
39
Josh Gao191c1542015-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 Gao191c1542015-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 Purselleaae97e2016-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 Gao191c1542015-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 Purselleaae97e2016-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 Gao191c1542015-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 Gao255c5c82016-03-03 14:49:02 -0800195 forward_spec = 'tcp:' + str(forward_port)
196 reverse_spec = 'tcp:' + str(reverse_port)
Josh Gao191c1542015-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 Pursellcf467412016-04-26 13:25:57 -0700258 if not self.device.has_shell_protocol():
Josh Gao191c1542015-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 Gaoa019f782017-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 Gao191c1542015-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 Hughescabfa112016-10-19 14:47:11 -0700324 PTY allocation behavior should match ssh.
Josh Gao191c1542015-12-09 11:26:11 -0800325 """
Josh Gao191c1542015-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 Hughescabfa112016-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 Gao191c1542015-12-09 11:26:11 -0800360
Elliott Hughescabfa112016-10-19 14:47:11 -0700361 # -t: PTY if stdin is a terminal.
362 self.assertEqual((True, False), check_pty(['-t']))
Josh Gao191c1542015-12-09 11:26:11 -0800363
364 # -t -t: always allocate PTY.
365 self.assertEqual((True, True), check_pty(['-t', '-t']))
366
Elliott Hughescabfa112016-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 Gao191c1542015-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 Pursellcf467412016-04-26 13:25:57 -0700391 if not self.device.has_shell_protocol():
Josh Gao191c1542015-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 Pursellcf467412016-04-26 13:25:57 -0700421 if not self.device.has_shell_protocol():
Josh Gao191c1542015-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 Gaoe76b9f32016-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 Gao191c1542015-12-09 11:26:11 -0800443
444 def test_non_interactive_stdin(self):
445 """Tests that non-interactive shells send stdin."""
David Pursellcf467412016-04-26 13:25:57 -0700446 if not self.device.has_shell_protocol():
Josh Gao191c1542015-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 Gaofe50bb72016-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 Gao470622f2016-10-21 13:17:32 -0700475 read
Josh Gaofe50bb72016-06-22 18:27:22 -0700476 """.format(path=log_path)
477
478 script = ";".join([x.strip() for x in script.strip().splitlines()])
479
Josh Gao470622f2016-10-21 13:17:32 -0700480 process = self.device.shell_popen([script], kill_atexit=False,
481 stdin=subprocess.PIPE,
482 stdout=subprocess.PIPE)
Josh Gaofe50bb72016-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 Gao470622f2016-10-21 13:17:32 -0700490 time.sleep(1)
Josh Gaofe50bb72016-06-22 18:27:22 -0700491
492 stdout, _ = self.device.shell(["cat", log_path])
493 self.assertEqual(stdout.strip(), "SIGHUP")
494
Josh Gaoc970aef2018-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 Gao191c1542015-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 Gao191c1542015-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.
765 os.mkdir(os.path.join(host_dir, 'empty'))
766
767 self.device.push(host_dir, self.DEVICE_TEMP_DIR)
768
769 test_empty_cmd = ['[', '-d',
770 os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
771 rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
772 self.assertEqual(rc, 0)
773 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
774 finally:
775 if host_dir is not None:
776 shutil.rmtree(host_dir)
777
Josh Gao94dc19f2016-09-14 16:13:50 -0700778 @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
779 def test_push_symlink(self):
780 """Push a symlink.
781
782 Bug: http://b/31491920
783 """
784 try:
785 host_dir = tempfile.mkdtemp()
786
787 # Make sure the temp directory isn't setuid, or else adb will
788 # complain.
789 os.chmod(host_dir, 0o700)
790
791 with open(os.path.join(host_dir, 'foo'), 'w') as f:
792 f.write('foo')
793
794 symlink_path = os.path.join(host_dir, 'symlink')
795 os.symlink('foo', symlink_path)
796
797 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
798 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
799 self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
800 rc, out, _ = self.device.shell_nocheck(
801 ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
802 self.assertEqual(0, rc)
803 self.assertEqual(out.strip(), 'foo')
804 finally:
805 if host_dir is not None:
806 shutil.rmtree(host_dir)
807
Josh Gao191c1542015-12-09 11:26:11 -0800808 def test_multiple_push(self):
809 """Push multiple files to the device in one adb push command.
810
811 Bug: http://b/25324823
812 """
813
814 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
815 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
816
817 try:
818 host_dir = tempfile.mkdtemp()
819
820 # Create some random files and a subdirectory containing more files.
821 temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
822
Josh Gao255c5c82016-03-03 14:49:02 -0800823 subdir = os.path.join(host_dir, 'subdir')
Josh Gao191c1542015-12-09 11:26:11 -0800824 os.mkdir(subdir)
825 subdir_temp_files = make_random_host_files(in_dir=subdir,
826 num_files=4)
827
828 paths = map(lambda temp_file: temp_file.full_path, temp_files)
829 paths.append(subdir)
830 self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
831
832 for temp_file in temp_files:
833 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
834 temp_file.base_name)
835 self._verify_remote(temp_file.checksum, remote_path)
836
837 for subdir_temp_file in subdir_temp_files:
838 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
839 # BROKEN: http://b/25394682
Josh Gao255c5c82016-03-03 14:49:02 -0800840 # 'subdir';
Josh Gao191c1542015-12-09 11:26:11 -0800841 temp_file.base_name)
842 self._verify_remote(temp_file.checksum, remote_path)
843
844
845 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
846 finally:
847 if host_dir is not None:
848 shutil.rmtree(host_dir)
849
Josh Gaoafcdcd72016-02-19 15:55:55 -0800850 @requires_non_root
851 def test_push_error_reporting(self):
852 """Make sure that errors that occur while pushing a file get reported
853
854 Bug: http://b/26816782
855 """
856 with tempfile.NamedTemporaryFile() as tmp_file:
857 tmp_file.write('\0' * 1024 * 1024)
858 tmp_file.flush()
859 try:
860 self.device.push(local=tmp_file.name, remote='/system/')
Josh Gao255c5c82016-03-03 14:49:02 -0800861 self.fail('push should not have succeeded')
Josh Gaoafcdcd72016-02-19 15:55:55 -0800862 except subprocess.CalledProcessError as e:
863 output = e.output
864
Josh Gao79ce3fe2016-11-18 15:31:11 -0800865 self.assertTrue('Permission denied' in output or
866 'Read-only file system' in output)
Josh Gao191c1542015-12-09 11:26:11 -0800867
Josh Gao4c0078d2018-06-28 18:43:19 -0700868 @requires_non_root
869 def test_push_directory_creation(self):
870 """Regression test for directory creation.
871
872 Bug: http://b/110953234
873 """
874 with tempfile.NamedTemporaryFile() as tmp_file:
875 tmp_file.write('\0' * 1024 * 1024)
876 tmp_file.flush()
877 remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
878 self.device.shell(['rm', '-rf', remote_path])
879
880 remote_path += '/filename'
881 self.device.push(local=tmp_file.name, remote=remote_path)
882
Josh Gao191c1542015-12-09 11:26:11 -0800883 def _test_pull(self, remote_file, checksum):
884 tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
885 tmp_write.close()
886 self.device.pull(remote=remote_file, local=tmp_write.name)
887 with open(tmp_write.name, 'rb') as tmp_read:
888 host_contents = tmp_read.read()
889 host_md5 = compute_md5(host_contents)
890 self.assertEqual(checksum, host_md5)
891 os.remove(tmp_write.name)
892
893 @requires_non_root
894 def test_pull_error_reporting(self):
895 self.device.shell(['touch', self.DEVICE_TEMP_FILE])
896 self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
897
898 try:
899 output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
900 except subprocess.CalledProcessError as e:
901 output = e.output
902
903 self.assertIn('Permission denied', output)
904
905 self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
906
907 def test_pull(self):
908 """Pull a randomly generated file from specified device."""
909 kbytes = 512
910 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
911 cmd = ['dd', 'if=/dev/urandom',
912 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
913 'count={}'.format(kbytes)]
914 self.device.shell(cmd)
915 dev_md5, _ = self.device.shell(
916 [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
917 self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
918 self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
919
920 def test_pull_dir(self):
921 """Pull a randomly generated directory of files from the device."""
922 try:
923 host_dir = tempfile.mkdtemp()
924
925 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
926 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
927
928 # Populate device directory with random files.
929 temp_files = make_random_device_files(
930 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
931
932 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
933
934 for temp_file in temp_files:
Josh Gaoce8f2cd2015-12-09 14:20:23 -0800935 host_path = os.path.join(
936 host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
937 temp_file.base_name)
938 self._verify_local(temp_file.checksum, host_path)
Josh Gao191c1542015-12-09 11:26:11 -0800939
940 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
941 finally:
942 if host_dir is not None:
943 shutil.rmtree(host_dir)
944
Josh Gao1e611a32016-02-26 13:26:55 -0800945 def test_pull_dir_symlink(self):
946 """Pull a directory into a symlink to a directory.
947
948 Bug: http://b/27362811
949 """
Josh Gao255c5c82016-03-03 14:49:02 -0800950 if os.name != 'posix':
Josh Gao1e611a32016-02-26 13:26:55 -0800951 raise unittest.SkipTest('requires POSIX')
952
953 try:
954 host_dir = tempfile.mkdtemp()
Josh Gao255c5c82016-03-03 14:49:02 -0800955 real_dir = os.path.join(host_dir, 'dir')
956 symlink = os.path.join(host_dir, 'symlink')
Josh Gao1e611a32016-02-26 13:26:55 -0800957 os.mkdir(real_dir)
958 os.symlink(real_dir, symlink)
959
960 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
961 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
962
963 # Populate device directory with random files.
964 temp_files = make_random_device_files(
965 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
966
967 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
968
969 for temp_file in temp_files:
970 host_path = os.path.join(
971 real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
972 temp_file.base_name)
973 self._verify_local(temp_file.checksum, host_path)
974
975 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
976 finally:
977 if host_dir is not None:
978 shutil.rmtree(host_dir)
979
980 def test_pull_dir_symlink_collision(self):
981 """Pull a directory into a colliding symlink to directory."""
Josh Gao255c5c82016-03-03 14:49:02 -0800982 if os.name != 'posix':
Josh Gao1e611a32016-02-26 13:26:55 -0800983 raise unittest.SkipTest('requires POSIX')
984
985 try:
986 host_dir = tempfile.mkdtemp()
Josh Gao255c5c82016-03-03 14:49:02 -0800987 real_dir = os.path.join(host_dir, 'real')
Josh Gao1e611a32016-02-26 13:26:55 -0800988 tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
989 symlink = os.path.join(host_dir, tmp_dirname)
990 os.mkdir(real_dir)
991 os.symlink(real_dir, symlink)
992
993 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
994 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
995
996 # Populate device directory with random files.
997 temp_files = make_random_device_files(
998 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
999
1000 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
1001
1002 for temp_file in temp_files:
1003 host_path = os.path.join(real_dir, temp_file.base_name)
1004 self._verify_local(temp_file.checksum, host_path)
1005
1006 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1007 finally:
1008 if host_dir is not None:
1009 shutil.rmtree(host_dir)
1010
Josh Gao89ec3a82016-03-02 16:00:02 -08001011 def test_pull_dir_nonexistent(self):
1012 """Pull a directory of files from the device to a nonexistent path."""
1013 try:
1014 host_dir = tempfile.mkdtemp()
1015 dest_dir = os.path.join(host_dir, 'dest')
1016
1017 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1018 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
1019
1020 # Populate device directory with random files.
1021 temp_files = make_random_device_files(
1022 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1023
1024 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
1025
1026 for temp_file in temp_files:
1027 host_path = os.path.join(dest_dir, temp_file.base_name)
1028 self._verify_local(temp_file.checksum, host_path)
1029
1030 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1031 finally:
1032 if host_dir is not None:
1033 shutil.rmtree(host_dir)
1034
Josh Gaof2642242015-12-09 14:03:30 -08001035 def test_pull_symlink_dir(self):
1036 """Pull a symlink to a directory of symlinks to files."""
1037 try:
1038 host_dir = tempfile.mkdtemp()
1039
1040 remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
1041 remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
1042 remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
1043
1044 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1045 self.device.shell(['mkdir', '-p', remote_dir, remote_links])
1046 self.device.shell(['ln', '-s', remote_links, remote_symlink])
1047
1048 # Populate device directory with random files.
1049 temp_files = make_random_device_files(
1050 self.device, in_dir=remote_dir, num_files=32)
1051
1052 for temp_file in temp_files:
1053 self.device.shell(
1054 ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
1055 posixpath.join(remote_links, temp_file.base_name)])
1056
1057 self.device.pull(remote=remote_symlink, local=host_dir)
1058
1059 for temp_file in temp_files:
1060 host_path = os.path.join(
1061 host_dir, 'symlink', temp_file.base_name)
1062 self._verify_local(temp_file.checksum, host_path)
1063
1064 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1065 finally:
1066 if host_dir is not None:
1067 shutil.rmtree(host_dir)
1068
Josh Gao191c1542015-12-09 11:26:11 -08001069 def test_pull_empty(self):
1070 """Pull a directory containing an empty directory from the device."""
1071 try:
1072 host_dir = tempfile.mkdtemp()
1073
1074 remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
1075 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1076 self.device.shell(['mkdir', '-p', remote_empty_path])
1077
1078 self.device.pull(remote=remote_empty_path, local=host_dir)
1079 self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
1080 finally:
1081 if host_dir is not None:
1082 shutil.rmtree(host_dir)
1083
1084 def test_multiple_pull(self):
1085 """Pull a randomly generated directory of files from the device."""
1086
1087 try:
1088 host_dir = tempfile.mkdtemp()
1089
Josh Gao255c5c82016-03-03 14:49:02 -08001090 subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
Josh Gao191c1542015-12-09 11:26:11 -08001091 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1092 self.device.shell(['mkdir', '-p', subdir])
1093
1094 # Create some random files and a subdirectory containing more files.
1095 temp_files = make_random_device_files(
1096 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
1097
1098 subdir_temp_files = make_random_device_files(
1099 self.device, in_dir=subdir, num_files=4, prefix='subdir_')
1100
1101 paths = map(lambda temp_file: temp_file.full_path, temp_files)
1102 paths.append(subdir)
1103 self.device._simple_call(['pull'] + paths + [host_dir])
1104
1105 for temp_file in temp_files:
1106 local_path = os.path.join(host_dir, temp_file.base_name)
1107 self._verify_local(temp_file.checksum, local_path)
1108
1109 for subdir_temp_file in subdir_temp_files:
1110 local_path = os.path.join(host_dir,
Josh Gao255c5c82016-03-03 14:49:02 -08001111 'subdir',
Josh Gao191c1542015-12-09 11:26:11 -08001112 subdir_temp_file.base_name)
1113 self._verify_local(subdir_temp_file.checksum, local_path)
1114
1115 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1116 finally:
1117 if host_dir is not None:
1118 shutil.rmtree(host_dir)
1119
Dan Albert06b0d6b2017-05-18 22:56:48 -07001120 def verify_sync(self, device, temp_files, device_dir):
1121 """Verifies that a list of temp files was synced to the device."""
1122 # Confirm that every file on the device mirrors that on the host.
1123 for temp_file in temp_files:
1124 device_full_path = posixpath.join(
1125 device_dir, temp_file.base_name)
1126 dev_md5, _ = device.shell(
1127 [get_md5_prog(self.device), device_full_path])[0].split()
1128 self.assertEqual(temp_file.checksum, dev_md5)
1129
Josh Gao191c1542015-12-09 11:26:11 -08001130 def test_sync(self):
Dan Albert06b0d6b2017-05-18 22:56:48 -07001131 """Sync a host directory to the data partition."""
Josh Gao191c1542015-12-09 11:26:11 -08001132
1133 try:
1134 base_dir = tempfile.mkdtemp()
1135
1136 # Create mirror device directory hierarchy within base_dir.
1137 full_dir_path = base_dir + self.DEVICE_TEMP_DIR
1138 os.makedirs(full_dir_path)
1139
1140 # Create 32 random files within the host mirror.
Dan Albert06b0d6b2017-05-18 22:56:48 -07001141 temp_files = make_random_host_files(
1142 in_dir=full_dir_path, num_files=32)
Josh Gao191c1542015-12-09 11:26:11 -08001143
Dan Albert06b0d6b2017-05-18 22:56:48 -07001144 # Clean up any stale files on the device.
Dan Albertdef4aae2017-05-18 13:52:45 -07001145 device = adb.get_device() # pylint: disable=no-member
Josh Gao191c1542015-12-09 11:26:11 -08001146 device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1147
Dan Albertdef4aae2017-05-18 13:52:45 -07001148 old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
1149 os.environ['ANDROID_PRODUCT_OUT'] = base_dir
Josh Gao191c1542015-12-09 11:26:11 -08001150 device.sync('data')
Dan Albertdef4aae2017-05-18 13:52:45 -07001151 if old_product_out is None:
1152 del os.environ['ANDROID_PRODUCT_OUT']
1153 else:
1154 os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
Josh Gao191c1542015-12-09 11:26:11 -08001155
Dan Albert06b0d6b2017-05-18 22:56:48 -07001156 self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
Josh Gao191c1542015-12-09 11:26:11 -08001157
Dan Albert06b0d6b2017-05-18 22:56:48 -07001158 #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
Josh Gao191c1542015-12-09 11:26:11 -08001159 finally:
1160 if base_dir is not None:
1161 shutil.rmtree(base_dir)
1162
Dan Albert06b0d6b2017-05-18 22:56:48 -07001163 def test_push_sync(self):
1164 """Sync a host directory to a specific path."""
1165
1166 try:
1167 temp_dir = tempfile.mkdtemp()
1168 temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
1169
1170 device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
1171
1172 # Clean up any stale files on the device.
1173 device = adb.get_device() # pylint: disable=no-member
1174 device.shell(['rm', '-rf', device_dir])
1175
1176 device.push(temp_dir, device_dir, sync=True)
1177
1178 self.verify_sync(device, temp_files, device_dir)
1179
1180 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1181 finally:
1182 if temp_dir is not None:
1183 shutil.rmtree(temp_dir)
1184
Josh Gao191c1542015-12-09 11:26:11 -08001185 def test_unicode_paths(self):
1186 """Ensure that we can support non-ASCII paths, even on Windows."""
1187 name = u'로보카 폴리'
1188
1189 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1190 remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
1191
1192 ## push.
1193 tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
1194 tf.close()
1195 self.device.push(tf.name, remote_path)
1196 os.remove(tf.name)
1197 self.assertFalse(os.path.exists(tf.name))
1198
1199 # Verify that the device ended up with the expected UTF-8 path
1200 output = self.device.shell(
1201 ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
Josh Gao2c621ae2018-03-19 16:09:05 -07001202 self.assertEqual(remote_path, output)
Josh Gao191c1542015-12-09 11:26:11 -08001203
1204 # pull.
1205 self.device.pull(remote_path, tf.name)
1206 self.assertTrue(os.path.exists(tf.name))
1207 os.remove(tf.name)
1208 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1209
1210
Yabin Cuib5e11412017-03-10 16:01:01 -08001211class DeviceOfflineTest(DeviceTest):
1212 def _get_device_state(self, serialno):
1213 output = subprocess.check_output(self.device.adb_cmd + ['devices'])
1214 for line in output.split('\n'):
1215 m = re.match('(\S+)\s+(\S+)', line)
1216 if m and m.group(1) == serialno:
1217 return m.group(2)
1218 return None
1219
Josh Gao33d14b82017-09-13 14:51:23 -07001220 def disabled_test_killed_when_pushing_a_large_file(self):
Yabin Cuib5e11412017-03-10 16:01:01 -08001221 """
1222 While running adb push with a large file, kill adb server.
1223 Occasionally the device becomes offline. Because the device is still
1224 reading data without realizing that the adb server has been restarted.
1225 Test if we can bring the device online automatically now.
1226 http://b/32952319
1227 """
1228 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1229 # 1. Push a large file
1230 file_path = 'tmp_large_file'
1231 try:
1232 fh = open(file_path, 'w')
1233 fh.write('\0' * (100 * 1024 * 1024))
1234 fh.close()
1235 subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
1236 time.sleep(0.1)
1237 # 2. Kill the adb server
1238 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1239 subproc.terminate()
1240 finally:
1241 try:
1242 os.unlink(file_path)
1243 except:
1244 pass
1245 # 3. See if the device still exist.
1246 # Sleep to wait for the adb server exit.
1247 time.sleep(0.5)
1248 # 4. The device should be online
1249 self.assertEqual(self._get_device_state(serialno), 'device')
1250
Josh Gao33d14b82017-09-13 14:51:23 -07001251 def disabled_test_killed_when_pulling_a_large_file(self):
Yabin Cuib5e11412017-03-10 16:01:01 -08001252 """
1253 While running adb pull with a large file, kill adb server.
1254 Occasionally the device can't be connected. Because the device is trying to
1255 send a message larger than what is expected by the adb server.
1256 Test if we can bring the device online automatically now.
1257 """
1258 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1259 file_path = 'tmp_large_file'
1260 try:
1261 # 1. Create a large file on device.
1262 self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
1263 'bs=1000000', 'count=100'])
1264 # 2. Pull the large file on host.
1265 subproc = subprocess.Popen(self.device.adb_cmd +
1266 ['pull','/data/local/tmp/tmp_large_file', file_path])
1267 time.sleep(0.1)
1268 # 3. Kill the adb server
1269 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1270 subproc.terminate()
1271 finally:
1272 try:
1273 os.unlink(file_path)
1274 except:
1275 pass
1276 # 4. See if the device still exist.
1277 # Sleep to wait for the adb server exit.
1278 time.sleep(0.5)
1279 self.assertEqual(self._get_device_state(serialno), 'device')
1280
1281
Josh Gaoef3d3432017-05-02 15:01:09 -07001282 def test_packet_size_regression(self):
1283 """Test for http://b/37783561
1284
1285 Receiving packets of a length divisible by 512 but not 1024 resulted in
1286 the adb client waiting indefinitely for more input.
1287 """
1288 # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
1289 # Probe some surrounding values as well, for the hell of it.
Josh Gao2ea46522018-04-10 14:35:06 -07001290 for base in [512] + range(1024, 1024 * 16, 1024):
1291 for offset in [-6, -5, -4]:
1292 length = base + offset
1293 cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
1294 'echo', 'foo']
1295 rc, stdout, _ = self.device.shell_nocheck(cmd)
Josh Gaoef3d3432017-05-02 15:01:09 -07001296
Josh Gao2ea46522018-04-10 14:35:06 -07001297 self.assertEqual(0, rc)
Josh Gaoef3d3432017-05-02 15:01:09 -07001298
Josh Gao2ea46522018-04-10 14:35:06 -07001299 # Output should be '\0' * length, followed by "foo\n"
1300 self.assertEqual(length, len(stdout) - 4)
1301 self.assertEqual(stdout, "\0" * length + "foo\n")
Josh Gaoef3d3432017-05-02 15:01:09 -07001302
1303
Josh Gao191c1542015-12-09 11:26:11 -08001304def main():
1305 random.seed(0)
1306 if len(adb.get_devices()) > 0:
1307 suite = unittest.TestLoader().loadTestsFromName(__name__)
1308 unittest.TextTestRunner(verbosity=3).run(suite)
1309 else:
1310 print('Test suite must be run with attached devices')
1311
1312
1313if __name__ == '__main__':
1314 main()