blob: 552e0c328d99c8e9f3bfe7e1111a365f140be3eb [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 Gaofd6ffb82018-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 # Note: If you run this test when adb connect'd to a physical device over
192 # TCP, it will fail in adb reverse due to https://code.google.com/p/android/issues/detail?id=189821
193 def test_forward_reverse_echo(self):
194 """Send data through adb forward and read it back via adb reverse"""
195 forward_port = 12345
196 reverse_port = forward_port + 1
Josh Gao255c5c82016-03-03 14:49:02 -0800197 forward_spec = 'tcp:' + str(forward_port)
198 reverse_spec = 'tcp:' + str(reverse_port)
Josh Gao191c1542015-12-09 11:26:11 -0800199 forward_setup = False
200 reverse_setup = False
201
202 try:
203 # listen on localhost:forward_port, connect to remote:forward_port
204 self.device.forward(forward_spec, forward_spec)
205 forward_setup = True
206 # listen on remote:forward_port, connect to localhost:reverse_port
207 self.device.reverse(forward_spec, reverse_spec)
208 reverse_setup = True
209
210 listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
211 with contextlib.closing(listener):
212 # Use SO_REUSEADDR so that subsequent runs of the test can grab
213 # the port even if it is in TIME_WAIT.
214 listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
215
216 # Listen on localhost:reverse_port before connecting to
217 # localhost:forward_port because that will cause adb to connect
218 # back to localhost:reverse_port.
219 listener.bind(('127.0.0.1', reverse_port))
220 listener.listen(4)
221
222 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
223 with contextlib.closing(client):
224 # Connect to the listener.
225 client.connect(('127.0.0.1', forward_port))
226
227 # Accept the client connection.
228 accepted_connection, addr = listener.accept()
229 with contextlib.closing(accepted_connection) as server:
230 data = 'hello'
231
232 # Send data into the port setup by adb forward.
233 client.sendall(data)
234 # Explicitly close() so that server gets EOF.
235 client.close()
236
237 # Verify that the data came back via adb reverse.
238 self.assertEqual(data, server.makefile().read())
239 finally:
240 if reverse_setup:
241 self.device.reverse_remove(forward_spec)
242 if forward_setup:
243 self.device.forward_remove(forward_spec)
244
245
246class ShellTest(DeviceTest):
247 def _interactive_shell(self, shell_args, input):
248 """Runs an interactive adb shell.
249
250 Args:
251 shell_args: List of string arguments to `adb shell`.
252 input: String input to send to the interactive shell.
253
254 Returns:
255 The remote exit code.
256
257 Raises:
258 unittest.SkipTest: The device doesn't support exit codes.
259 """
David Pursellcf467412016-04-26 13:25:57 -0700260 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800261 raise unittest.SkipTest('exit codes are unavailable on this device')
262
263 proc = subprocess.Popen(
264 self.device.adb_cmd + ['shell'] + shell_args,
265 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
266 stderr=subprocess.PIPE)
267 # Closing host-side stdin doesn't trigger a PTY shell to exit so we need
268 # to explicitly add an exit command to close the session from the device
269 # side, plus the necessary newline to complete the interactive command.
270 proc.communicate(input + '; exit\n')
271 return proc.returncode
272
273 def test_cat(self):
274 """Check that we can at least cat a file."""
275 out = self.device.shell(['cat', '/proc/uptime'])[0].strip()
276 elements = out.split()
277 self.assertEqual(len(elements), 2)
278
279 uptime, idle = elements
280 self.assertGreater(float(uptime), 0.0)
281 self.assertGreater(float(idle), 0.0)
282
283 def test_throws_on_failure(self):
284 self.assertRaises(adb.ShellError, self.device.shell, ['false'])
285
286 def test_output_not_stripped(self):
287 out = self.device.shell(['echo', 'foo'])[0]
288 self.assertEqual(out, 'foo' + self.device.linesep)
289
Josh Gaoa019f782017-06-16 15:34:34 -0700290 def test_shell_command_length(self):
291 # Devices that have shell_v2 should be able to handle long commands.
292 if self.device.has_shell_protocol():
293 rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
294 self.assertEqual(rc, 0)
295 self.assertTrue(out == ('x' * 16384 + '\n'))
296
Josh Gao191c1542015-12-09 11:26:11 -0800297 def test_shell_nocheck_failure(self):
298 rc, out, _ = self.device.shell_nocheck(['false'])
299 self.assertNotEqual(rc, 0)
300 self.assertEqual(out, '')
301
302 def test_shell_nocheck_output_not_stripped(self):
303 rc, out, _ = self.device.shell_nocheck(['echo', 'foo'])
304 self.assertEqual(rc, 0)
305 self.assertEqual(out, 'foo' + self.device.linesep)
306
307 def test_can_distinguish_tricky_results(self):
308 # If result checking on ADB shell is naively implemented as
309 # `adb shell <cmd>; echo $?`, we would be unable to distinguish the
310 # output from the result for a cmd of `echo -n 1`.
311 rc, out, _ = self.device.shell_nocheck(['echo', '-n', '1'])
312 self.assertEqual(rc, 0)
313 self.assertEqual(out, '1')
314
315 def test_line_endings(self):
316 """Ensure that line ending translation is not happening in the pty.
317
318 Bug: http://b/19735063
319 """
320 output = self.device.shell(['uname'])[0]
321 self.assertEqual(output, 'Linux' + self.device.linesep)
322
323 def test_pty_logic(self):
324 """Tests that a PTY is allocated when it should be.
325
Elliott Hughescabfa112016-10-19 14:47:11 -0700326 PTY allocation behavior should match ssh.
Josh Gao191c1542015-12-09 11:26:11 -0800327 """
Josh Gao191c1542015-12-09 11:26:11 -0800328 def check_pty(args):
329 """Checks adb shell PTY allocation.
330
331 Tests |args| for terminal and non-terminal stdin.
332
333 Args:
334 args: -Tt args in a list (e.g. ['-t', '-t']).
335
336 Returns:
337 A tuple (<terminal>, <non-terminal>). True indicates
338 the corresponding shell allocated a remote PTY.
339 """
340 test_cmd = self.device.adb_cmd + ['shell'] + args + ['[ -t 0 ]']
341
342 terminal = subprocess.Popen(
343 test_cmd, stdin=None,
344 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
345 terminal.communicate()
346
347 non_terminal = subprocess.Popen(
348 test_cmd, stdin=subprocess.PIPE,
349 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
350 non_terminal.communicate()
351
352 return (terminal.returncode == 0, non_terminal.returncode == 0)
353
354 # -T: never allocate PTY.
355 self.assertEqual((False, False), check_pty(['-T']))
356
Elliott Hughescabfa112016-10-19 14:47:11 -0700357 # These tests require a new device.
358 if self.device.has_shell_protocol() and os.isatty(sys.stdin.fileno()):
359 # No args: PTY only if stdin is a terminal and shell is interactive,
360 # which is difficult to reliably test from a script.
361 self.assertEqual((False, False), check_pty([]))
Josh Gao191c1542015-12-09 11:26:11 -0800362
Elliott Hughescabfa112016-10-19 14:47:11 -0700363 # -t: PTY if stdin is a terminal.
364 self.assertEqual((True, False), check_pty(['-t']))
Josh Gao191c1542015-12-09 11:26:11 -0800365
366 # -t -t: always allocate PTY.
367 self.assertEqual((True, True), check_pty(['-t', '-t']))
368
Elliott Hughescabfa112016-10-19 14:47:11 -0700369 # -tt: always allocate PTY, POSIX style (http://b/32216152).
370 self.assertEqual((True, True), check_pty(['-tt']))
371
372 # -ttt: ssh has weird even/odd behavior with multiple -t flags, but
373 # we follow the man page instead.
374 self.assertEqual((True, True), check_pty(['-ttt']))
375
376 # -ttx: -x and -tt aren't incompatible (though -Tx would be an error).
377 self.assertEqual((True, True), check_pty(['-ttx']))
378
379 # -Ttt: -tt cancels out -T.
380 self.assertEqual((True, True), check_pty(['-Ttt']))
381
382 # -ttT: -T cancels out -tt.
383 self.assertEqual((False, False), check_pty(['-ttT']))
384
Josh Gao191c1542015-12-09 11:26:11 -0800385 def test_shell_protocol(self):
386 """Tests the shell protocol on the device.
387
388 If the device supports shell protocol, this gives us the ability
389 to separate stdout/stderr and return the exit code directly.
390
391 Bug: http://b/19734861
392 """
David Pursellcf467412016-04-26 13:25:57 -0700393 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800394 raise unittest.SkipTest('shell protocol unsupported on this device')
395
396 # Shell protocol should be used by default.
397 result = self.device.shell_nocheck(
398 shlex.split('echo foo; echo bar >&2; exit 17'))
399 self.assertEqual(17, result[0])
400 self.assertEqual('foo' + self.device.linesep, result[1])
401 self.assertEqual('bar' + self.device.linesep, result[2])
402
403 self.assertEqual(17, self._interactive_shell([], 'exit 17'))
404
405 # -x flag should disable shell protocol.
406 result = self.device.shell_nocheck(
407 shlex.split('-x echo foo; echo bar >&2; exit 17'))
408 self.assertEqual(0, result[0])
409 self.assertEqual('foo{0}bar{0}'.format(self.device.linesep), result[1])
410 self.assertEqual('', result[2])
411
412 self.assertEqual(0, self._interactive_shell(['-x'], 'exit 17'))
413
414 def test_non_interactive_sigint(self):
415 """Tests that SIGINT in a non-interactive shell kills the process.
416
417 This requires the shell protocol in order to detect the broken
418 pipe; raw data transfer mode will only see the break once the
419 subprocess tries to read or write.
420
421 Bug: http://b/23825725
422 """
David Pursellcf467412016-04-26 13:25:57 -0700423 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800424 raise unittest.SkipTest('shell protocol unsupported on this device')
425
426 # Start a long-running process.
427 sleep_proc = subprocess.Popen(
428 self.device.adb_cmd + shlex.split('shell echo $$; sleep 60'),
429 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
430 stderr=subprocess.STDOUT)
431 remote_pid = sleep_proc.stdout.readline().strip()
432 self.assertIsNone(sleep_proc.returncode, 'subprocess terminated early')
433 proc_query = shlex.split('ps {0} | grep {0}'.format(remote_pid))
434
435 # Verify that the process is running, send signal, verify it stopped.
436 self.device.shell(proc_query)
437 os.kill(sleep_proc.pid, signal.SIGINT)
438 sleep_proc.communicate()
Josh Gaoe76b9f32016-10-21 12:40:42 -0700439
440 # It can take some time for the process to receive the signal and die.
441 end_time = time.time() + 3
442 while self.device.shell_nocheck(proc_query)[0] != 1:
443 self.assertFalse(time.time() > end_time,
444 'subprocess failed to terminate in time')
Josh Gao191c1542015-12-09 11:26:11 -0800445
446 def test_non_interactive_stdin(self):
447 """Tests that non-interactive shells send stdin."""
David Pursellcf467412016-04-26 13:25:57 -0700448 if not self.device.has_shell_protocol():
Josh Gao191c1542015-12-09 11:26:11 -0800449 raise unittest.SkipTest('non-interactive stdin unsupported '
450 'on this device')
451
452 # Test both small and large inputs.
453 small_input = 'foo'
454 large_input = '\n'.join(c * 100 for c in (string.ascii_letters +
455 string.digits))
456
457 for input in (small_input, large_input):
458 proc = subprocess.Popen(self.device.adb_cmd + ['shell', 'cat'],
459 stdin=subprocess.PIPE,
460 stdout=subprocess.PIPE,
461 stderr=subprocess.PIPE)
462 stdout, stderr = proc.communicate(input)
463 self.assertEqual(input.splitlines(), stdout.splitlines())
464 self.assertEqual('', stderr)
465
Josh Gaofe50bb72016-06-22 18:27:22 -0700466 def test_sighup(self):
467 """Ensure that SIGHUP gets sent upon non-interactive ctrl-c"""
468 log_path = "/data/local/tmp/adb_signal_test.log"
469
470 # Clear the output file.
471 self.device.shell_nocheck(["echo", ">", log_path])
472
473 script = """
474 trap "echo SIGINT > {path}; exit 0" SIGINT
475 trap "echo SIGHUP > {path}; exit 0" SIGHUP
476 echo Waiting
Josh Gao470622f2016-10-21 13:17:32 -0700477 read
Josh Gaofe50bb72016-06-22 18:27:22 -0700478 """.format(path=log_path)
479
480 script = ";".join([x.strip() for x in script.strip().splitlines()])
481
Josh Gao470622f2016-10-21 13:17:32 -0700482 process = self.device.shell_popen([script], kill_atexit=False,
483 stdin=subprocess.PIPE,
484 stdout=subprocess.PIPE)
Josh Gaofe50bb72016-06-22 18:27:22 -0700485
486 self.assertEqual("Waiting\n", process.stdout.readline())
487 process.send_signal(signal.SIGINT)
488 process.wait()
489
490 # Waiting for the local adb to finish is insufficient, since it hangs
491 # up immediately.
Josh Gao470622f2016-10-21 13:17:32 -0700492 time.sleep(1)
Josh Gaofe50bb72016-06-22 18:27:22 -0700493
494 stdout, _ = self.device.shell(["cat", log_path])
495 self.assertEqual(stdout.strip(), "SIGHUP")
496
Josh Gaofd6ffb82018-03-19 15:35:11 -0700497 def test_exit_stress(self):
498 """Hammer `adb shell exit 42` with multiple threads."""
499 thread_count = 48
500 result = dict()
501 def hammer(thread_idx, thread_count, result):
502 success = True
503 for i in range(thread_idx, 240, thread_count):
504 ret = subprocess.call(['adb', 'shell', 'exit {}'.format(i)])
505 if ret != i % 256:
506 success = False
507 break
508 result[thread_idx] = success
509
510 threads = []
511 for i in range(thread_count):
512 thread = threading.Thread(target=hammer, args=(i, thread_count, result))
513 thread.start()
514 threads.append(thread)
515 for thread in threads:
516 thread.join()
517 for i, success in result.iteritems():
518 self.assertTrue(success)
519
Josh Gao191c1542015-12-09 11:26:11 -0800520
521class ArgumentEscapingTest(DeviceTest):
522 def test_shell_escaping(self):
523 """Make sure that argument escaping is somewhat sane."""
524
525 # http://b/19734868
526 # Note that this actually matches ssh(1)'s behavior --- it's
527 # converted to `sh -c echo hello; echo world` which sh interprets
528 # as `sh -c echo` (with an argument to that shell of "hello"),
529 # and then `echo world` back in the first shell.
530 result = self.device.shell(
531 shlex.split("sh -c 'echo hello; echo world'"))[0]
532 result = result.splitlines()
533 self.assertEqual(['', 'world'], result)
534 # If you really wanted "hello" and "world", here's what you'd do:
535 result = self.device.shell(
536 shlex.split(r'echo hello\;echo world'))[0].splitlines()
537 self.assertEqual(['hello', 'world'], result)
538
539 # http://b/15479704
540 result = self.device.shell(shlex.split("'true && echo t'"))[0].strip()
541 self.assertEqual('t', result)
542 result = self.device.shell(
543 shlex.split("sh -c 'true && echo t'"))[0].strip()
544 self.assertEqual('t', result)
545
546 # http://b/20564385
547 result = self.device.shell(shlex.split('FOO=a BAR=b echo t'))[0].strip()
548 self.assertEqual('t', result)
549 result = self.device.shell(
550 shlex.split(r'echo -n 123\;uname'))[0].strip()
551 self.assertEqual('123Linux', result)
552
553 def test_install_argument_escaping(self):
554 """Make sure that install argument escaping works."""
555 # http://b/20323053, http://b/3090932.
556 for file_suffix in ('-text;ls;1.apk', "-Live Hold'em.apk"):
557 tf = tempfile.NamedTemporaryFile('wb', suffix=file_suffix,
558 delete=False)
559 tf.close()
560
561 # Installing bogus .apks fails if the device supports exit codes.
562 try:
563 output = self.device.install(tf.name)
564 except subprocess.CalledProcessError as e:
565 output = e.output
566
567 self.assertIn(file_suffix, output)
568 os.remove(tf.name)
569
570
571class RootUnrootTest(DeviceTest):
572 def _test_root(self):
573 message = self.device.root()
574 if 'adbd cannot run as root in production builds' in message:
575 return
576 self.device.wait()
577 self.assertEqual('root', self.device.shell(['id', '-un'])[0].strip())
578
579 def _test_unroot(self):
580 self.device.unroot()
581 self.device.wait()
582 self.assertEqual('shell', self.device.shell(['id', '-un'])[0].strip())
583
584 def test_root_unroot(self):
585 """Make sure that adb root and adb unroot work, using id(1)."""
586 if self.device.get_prop('ro.debuggable') != '1':
587 raise unittest.SkipTest('requires rootable build')
588
589 original_user = self.device.shell(['id', '-un'])[0].strip()
590 try:
591 if original_user == 'root':
592 self._test_unroot()
593 self._test_root()
594 elif original_user == 'shell':
595 self._test_root()
596 self._test_unroot()
597 finally:
598 if original_user == 'root':
599 self.device.root()
600 else:
601 self.device.unroot()
602 self.device.wait()
603
604
605class TcpIpTest(DeviceTest):
606 def test_tcpip_failure_raises(self):
607 """adb tcpip requires a port.
608
609 Bug: http://b/22636927
610 """
611 self.assertRaises(
612 subprocess.CalledProcessError, self.device.tcpip, '')
613 self.assertRaises(
614 subprocess.CalledProcessError, self.device.tcpip, 'foo')
615
616
617class SystemPropertiesTest(DeviceTest):
618 def test_get_prop(self):
619 self.assertEqual(self.device.get_prop('init.svc.adbd'), 'running')
620
621 @requires_root
622 def test_set_prop(self):
623 prop_name = 'foo.bar'
624 self.device.shell(['setprop', prop_name, '""'])
625
626 self.device.set_prop(prop_name, 'qux')
627 self.assertEqual(
628 self.device.shell(['getprop', prop_name])[0].strip(), 'qux')
629
630
631def compute_md5(string):
632 hsh = hashlib.md5()
633 hsh.update(string)
634 return hsh.hexdigest()
635
636
637def get_md5_prog(device):
638 """Older platforms (pre-L) had the name md5 rather than md5sum."""
639 try:
640 device.shell(['md5sum', '/proc/uptime'])
641 return 'md5sum'
642 except adb.ShellError:
643 return 'md5'
644
645
646class HostFile(object):
647 def __init__(self, handle, checksum):
648 self.handle = handle
649 self.checksum = checksum
650 self.full_path = handle.name
651 self.base_name = os.path.basename(self.full_path)
652
653
654class DeviceFile(object):
655 def __init__(self, checksum, full_path):
656 self.checksum = checksum
657 self.full_path = full_path
658 self.base_name = posixpath.basename(self.full_path)
659
660
661def make_random_host_files(in_dir, num_files):
662 min_size = 1 * (1 << 10)
663 max_size = 16 * (1 << 10)
664
665 files = []
666 for _ in xrange(num_files):
667 file_handle = tempfile.NamedTemporaryFile(dir=in_dir, delete=False)
668
669 size = random.randrange(min_size, max_size, 1024)
670 rand_str = os.urandom(size)
671 file_handle.write(rand_str)
672 file_handle.flush()
673 file_handle.close()
674
675 md5 = compute_md5(rand_str)
676 files.append(HostFile(file_handle, md5))
677 return files
678
679
680def make_random_device_files(device, in_dir, num_files, prefix='device_tmpfile'):
681 min_size = 1 * (1 << 10)
682 max_size = 16 * (1 << 10)
683
684 files = []
685 for file_num in xrange(num_files):
686 size = random.randrange(min_size, max_size, 1024)
687
688 base_name = prefix + str(file_num)
689 full_path = posixpath.join(in_dir, base_name)
690
691 device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
692 'bs={}'.format(size), 'count=1'])
693 dev_md5, _ = device.shell([get_md5_prog(device), full_path])[0].split()
694
695 files.append(DeviceFile(dev_md5, full_path))
696 return files
697
698
699class FileOperationsTest(DeviceTest):
700 SCRATCH_DIR = '/data/local/tmp'
701 DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
702 DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
703
704 def _verify_remote(self, checksum, remote_path):
705 dev_md5, _ = self.device.shell([get_md5_prog(self.device),
706 remote_path])[0].split()
707 self.assertEqual(checksum, dev_md5)
708
709 def _verify_local(self, checksum, local_path):
710 with open(local_path, 'rb') as host_file:
711 host_md5 = compute_md5(host_file.read())
712 self.assertEqual(host_md5, checksum)
713
714 def test_push(self):
715 """Push a randomly generated file to specified device."""
716 kbytes = 512
717 tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
718 rand_str = os.urandom(1024 * kbytes)
719 tmp.write(rand_str)
720 tmp.close()
721
722 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
723 self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
724
725 self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
726 self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
727
728 os.remove(tmp.name)
729
730 def test_push_dir(self):
731 """Push a randomly generated directory of files to the device."""
732 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
733 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
734
735 try:
736 host_dir = tempfile.mkdtemp()
737
738 # Make sure the temp directory isn't setuid, or else adb will complain.
739 os.chmod(host_dir, 0o700)
740
741 # Create 32 random files.
742 temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
743 self.device.push(host_dir, self.DEVICE_TEMP_DIR)
744
745 for temp_file in temp_files:
746 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
747 os.path.basename(host_dir),
748 temp_file.base_name)
749 self._verify_remote(temp_file.checksum, remote_path)
750 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
751 finally:
752 if host_dir is not None:
753 shutil.rmtree(host_dir)
754
755 @unittest.expectedFailure # b/25566053
756 def test_push_empty(self):
757 """Push a directory containing an empty directory to the device."""
758 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
759 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
760
761 try:
762 host_dir = tempfile.mkdtemp()
763
764 # Make sure the temp directory isn't setuid, or else adb will complain.
765 os.chmod(host_dir, 0o700)
766
767 # Create an empty directory.
768 os.mkdir(os.path.join(host_dir, 'empty'))
769
770 self.device.push(host_dir, self.DEVICE_TEMP_DIR)
771
772 test_empty_cmd = ['[', '-d',
773 os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
774 rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
775 self.assertEqual(rc, 0)
776 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
777 finally:
778 if host_dir is not None:
779 shutil.rmtree(host_dir)
780
Josh Gao94dc19f2016-09-14 16:13:50 -0700781 @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
782 def test_push_symlink(self):
783 """Push a symlink.
784
785 Bug: http://b/31491920
786 """
787 try:
788 host_dir = tempfile.mkdtemp()
789
790 # Make sure the temp directory isn't setuid, or else adb will
791 # complain.
792 os.chmod(host_dir, 0o700)
793
794 with open(os.path.join(host_dir, 'foo'), 'w') as f:
795 f.write('foo')
796
797 symlink_path = os.path.join(host_dir, 'symlink')
798 os.symlink('foo', symlink_path)
799
800 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
801 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
802 self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
803 rc, out, _ = self.device.shell_nocheck(
804 ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
805 self.assertEqual(0, rc)
806 self.assertEqual(out.strip(), 'foo')
807 finally:
808 if host_dir is not None:
809 shutil.rmtree(host_dir)
810
Josh Gao191c1542015-12-09 11:26:11 -0800811 def test_multiple_push(self):
812 """Push multiple files to the device in one adb push command.
813
814 Bug: http://b/25324823
815 """
816
817 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
818 self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
819
820 try:
821 host_dir = tempfile.mkdtemp()
822
823 # Create some random files and a subdirectory containing more files.
824 temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
825
Josh Gao255c5c82016-03-03 14:49:02 -0800826 subdir = os.path.join(host_dir, 'subdir')
Josh Gao191c1542015-12-09 11:26:11 -0800827 os.mkdir(subdir)
828 subdir_temp_files = make_random_host_files(in_dir=subdir,
829 num_files=4)
830
831 paths = map(lambda temp_file: temp_file.full_path, temp_files)
832 paths.append(subdir)
833 self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
834
835 for temp_file in temp_files:
836 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
837 temp_file.base_name)
838 self._verify_remote(temp_file.checksum, remote_path)
839
840 for subdir_temp_file in subdir_temp_files:
841 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
842 # BROKEN: http://b/25394682
Josh Gao255c5c82016-03-03 14:49:02 -0800843 # 'subdir';
Josh Gao191c1542015-12-09 11:26:11 -0800844 temp_file.base_name)
845 self._verify_remote(temp_file.checksum, remote_path)
846
847
848 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
849 finally:
850 if host_dir is not None:
851 shutil.rmtree(host_dir)
852
Josh Gaoafcdcd72016-02-19 15:55:55 -0800853 @requires_non_root
854 def test_push_error_reporting(self):
855 """Make sure that errors that occur while pushing a file get reported
856
857 Bug: http://b/26816782
858 """
859 with tempfile.NamedTemporaryFile() as tmp_file:
860 tmp_file.write('\0' * 1024 * 1024)
861 tmp_file.flush()
862 try:
863 self.device.push(local=tmp_file.name, remote='/system/')
Josh Gao255c5c82016-03-03 14:49:02 -0800864 self.fail('push should not have succeeded')
Josh Gaoafcdcd72016-02-19 15:55:55 -0800865 except subprocess.CalledProcessError as e:
866 output = e.output
867
Josh Gao79ce3fe2016-11-18 15:31:11 -0800868 self.assertTrue('Permission denied' in output or
869 'Read-only file system' in output)
Josh Gao191c1542015-12-09 11:26:11 -0800870
Josh Gaocfe40112018-06-28 18:43:19 -0700871 @requires_non_root
872 def test_push_directory_creation(self):
873 """Regression test for directory creation.
874
875 Bug: http://b/110953234
876 """
877 with tempfile.NamedTemporaryFile() as tmp_file:
878 tmp_file.write('\0' * 1024 * 1024)
879 tmp_file.flush()
880 remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
881 self.device.shell(['rm', '-rf', remote_path])
882
883 remote_path += '/filename'
884 self.device.push(local=tmp_file.name, remote=remote_path)
885
Josh Gao191c1542015-12-09 11:26:11 -0800886 def _test_pull(self, remote_file, checksum):
887 tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
888 tmp_write.close()
889 self.device.pull(remote=remote_file, local=tmp_write.name)
890 with open(tmp_write.name, 'rb') as tmp_read:
891 host_contents = tmp_read.read()
892 host_md5 = compute_md5(host_contents)
893 self.assertEqual(checksum, host_md5)
894 os.remove(tmp_write.name)
895
896 @requires_non_root
897 def test_pull_error_reporting(self):
898 self.device.shell(['touch', self.DEVICE_TEMP_FILE])
899 self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
900
901 try:
902 output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
903 except subprocess.CalledProcessError as e:
904 output = e.output
905
906 self.assertIn('Permission denied', output)
907
908 self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
909
910 def test_pull(self):
911 """Pull a randomly generated file from specified device."""
912 kbytes = 512
913 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
914 cmd = ['dd', 'if=/dev/urandom',
915 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
916 'count={}'.format(kbytes)]
917 self.device.shell(cmd)
918 dev_md5, _ = self.device.shell(
919 [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
920 self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
921 self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
922
923 def test_pull_dir(self):
924 """Pull a randomly generated directory of files from the device."""
925 try:
926 host_dir = tempfile.mkdtemp()
927
928 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
929 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
930
931 # Populate device directory with random files.
932 temp_files = make_random_device_files(
933 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
934
935 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
936
937 for temp_file in temp_files:
Josh Gaoce8f2cd2015-12-09 14:20:23 -0800938 host_path = os.path.join(
939 host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
940 temp_file.base_name)
941 self._verify_local(temp_file.checksum, host_path)
Josh Gao191c1542015-12-09 11:26:11 -0800942
943 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
944 finally:
945 if host_dir is not None:
946 shutil.rmtree(host_dir)
947
Josh Gao1e611a32016-02-26 13:26:55 -0800948 def test_pull_dir_symlink(self):
949 """Pull a directory into a symlink to a directory.
950
951 Bug: http://b/27362811
952 """
Josh Gao255c5c82016-03-03 14:49:02 -0800953 if os.name != 'posix':
Josh Gao1e611a32016-02-26 13:26:55 -0800954 raise unittest.SkipTest('requires POSIX')
955
956 try:
957 host_dir = tempfile.mkdtemp()
Josh Gao255c5c82016-03-03 14:49:02 -0800958 real_dir = os.path.join(host_dir, 'dir')
959 symlink = os.path.join(host_dir, 'symlink')
Josh Gao1e611a32016-02-26 13:26:55 -0800960 os.mkdir(real_dir)
961 os.symlink(real_dir, symlink)
962
963 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
964 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
965
966 # Populate device directory with random files.
967 temp_files = make_random_device_files(
968 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
969
970 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
971
972 for temp_file in temp_files:
973 host_path = os.path.join(
974 real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
975 temp_file.base_name)
976 self._verify_local(temp_file.checksum, host_path)
977
978 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
979 finally:
980 if host_dir is not None:
981 shutil.rmtree(host_dir)
982
983 def test_pull_dir_symlink_collision(self):
984 """Pull a directory into a colliding symlink to directory."""
Josh Gao255c5c82016-03-03 14:49:02 -0800985 if os.name != 'posix':
Josh Gao1e611a32016-02-26 13:26:55 -0800986 raise unittest.SkipTest('requires POSIX')
987
988 try:
989 host_dir = tempfile.mkdtemp()
Josh Gao255c5c82016-03-03 14:49:02 -0800990 real_dir = os.path.join(host_dir, 'real')
Josh Gao1e611a32016-02-26 13:26:55 -0800991 tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
992 symlink = os.path.join(host_dir, tmp_dirname)
993 os.mkdir(real_dir)
994 os.symlink(real_dir, symlink)
995
996 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
997 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
998
999 # Populate device directory with random files.
1000 temp_files = make_random_device_files(
1001 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1002
1003 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
1004
1005 for temp_file in temp_files:
1006 host_path = os.path.join(real_dir, temp_file.base_name)
1007 self._verify_local(temp_file.checksum, host_path)
1008
1009 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1010 finally:
1011 if host_dir is not None:
1012 shutil.rmtree(host_dir)
1013
Josh Gao89ec3a82016-03-02 16:00:02 -08001014 def test_pull_dir_nonexistent(self):
1015 """Pull a directory of files from the device to a nonexistent path."""
1016 try:
1017 host_dir = tempfile.mkdtemp()
1018 dest_dir = os.path.join(host_dir, 'dest')
1019
1020 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1021 self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
1022
1023 # Populate device directory with random files.
1024 temp_files = make_random_device_files(
1025 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
1026
1027 self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
1028
1029 for temp_file in temp_files:
1030 host_path = os.path.join(dest_dir, temp_file.base_name)
1031 self._verify_local(temp_file.checksum, host_path)
1032
1033 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1034 finally:
1035 if host_dir is not None:
1036 shutil.rmtree(host_dir)
1037
Josh Gaof2642242015-12-09 14:03:30 -08001038 def test_pull_symlink_dir(self):
1039 """Pull a symlink to a directory of symlinks to files."""
1040 try:
1041 host_dir = tempfile.mkdtemp()
1042
1043 remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
1044 remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
1045 remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
1046
1047 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1048 self.device.shell(['mkdir', '-p', remote_dir, remote_links])
1049 self.device.shell(['ln', '-s', remote_links, remote_symlink])
1050
1051 # Populate device directory with random files.
1052 temp_files = make_random_device_files(
1053 self.device, in_dir=remote_dir, num_files=32)
1054
1055 for temp_file in temp_files:
1056 self.device.shell(
1057 ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
1058 posixpath.join(remote_links, temp_file.base_name)])
1059
1060 self.device.pull(remote=remote_symlink, local=host_dir)
1061
1062 for temp_file in temp_files:
1063 host_path = os.path.join(
1064 host_dir, 'symlink', temp_file.base_name)
1065 self._verify_local(temp_file.checksum, host_path)
1066
1067 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1068 finally:
1069 if host_dir is not None:
1070 shutil.rmtree(host_dir)
1071
Josh Gao191c1542015-12-09 11:26:11 -08001072 def test_pull_empty(self):
1073 """Pull a directory containing an empty directory from the device."""
1074 try:
1075 host_dir = tempfile.mkdtemp()
1076
1077 remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
1078 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1079 self.device.shell(['mkdir', '-p', remote_empty_path])
1080
1081 self.device.pull(remote=remote_empty_path, local=host_dir)
1082 self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
1083 finally:
1084 if host_dir is not None:
1085 shutil.rmtree(host_dir)
1086
1087 def test_multiple_pull(self):
1088 """Pull a randomly generated directory of files from the device."""
1089
1090 try:
1091 host_dir = tempfile.mkdtemp()
1092
Josh Gao255c5c82016-03-03 14:49:02 -08001093 subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
Josh Gao191c1542015-12-09 11:26:11 -08001094 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1095 self.device.shell(['mkdir', '-p', subdir])
1096
1097 # Create some random files and a subdirectory containing more files.
1098 temp_files = make_random_device_files(
1099 self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
1100
1101 subdir_temp_files = make_random_device_files(
1102 self.device, in_dir=subdir, num_files=4, prefix='subdir_')
1103
1104 paths = map(lambda temp_file: temp_file.full_path, temp_files)
1105 paths.append(subdir)
1106 self.device._simple_call(['pull'] + paths + [host_dir])
1107
1108 for temp_file in temp_files:
1109 local_path = os.path.join(host_dir, temp_file.base_name)
1110 self._verify_local(temp_file.checksum, local_path)
1111
1112 for subdir_temp_file in subdir_temp_files:
1113 local_path = os.path.join(host_dir,
Josh Gao255c5c82016-03-03 14:49:02 -08001114 'subdir',
Josh Gao191c1542015-12-09 11:26:11 -08001115 subdir_temp_file.base_name)
1116 self._verify_local(subdir_temp_file.checksum, local_path)
1117
1118 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1119 finally:
1120 if host_dir is not None:
1121 shutil.rmtree(host_dir)
1122
Dan Albert06b0d6b2017-05-18 22:56:48 -07001123 def verify_sync(self, device, temp_files, device_dir):
1124 """Verifies that a list of temp files was synced to the device."""
1125 # Confirm that every file on the device mirrors that on the host.
1126 for temp_file in temp_files:
1127 device_full_path = posixpath.join(
1128 device_dir, temp_file.base_name)
1129 dev_md5, _ = device.shell(
1130 [get_md5_prog(self.device), device_full_path])[0].split()
1131 self.assertEqual(temp_file.checksum, dev_md5)
1132
Josh Gao191c1542015-12-09 11:26:11 -08001133 def test_sync(self):
Dan Albert06b0d6b2017-05-18 22:56:48 -07001134 """Sync a host directory to the data partition."""
Josh Gao191c1542015-12-09 11:26:11 -08001135
1136 try:
1137 base_dir = tempfile.mkdtemp()
1138
1139 # Create mirror device directory hierarchy within base_dir.
1140 full_dir_path = base_dir + self.DEVICE_TEMP_DIR
1141 os.makedirs(full_dir_path)
1142
1143 # Create 32 random files within the host mirror.
Dan Albert06b0d6b2017-05-18 22:56:48 -07001144 temp_files = make_random_host_files(
1145 in_dir=full_dir_path, num_files=32)
Josh Gao191c1542015-12-09 11:26:11 -08001146
Dan Albert06b0d6b2017-05-18 22:56:48 -07001147 # Clean up any stale files on the device.
Dan Albertdef4aae2017-05-18 13:52:45 -07001148 device = adb.get_device() # pylint: disable=no-member
Josh Gao191c1542015-12-09 11:26:11 -08001149 device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1150
Dan Albertdef4aae2017-05-18 13:52:45 -07001151 old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
1152 os.environ['ANDROID_PRODUCT_OUT'] = base_dir
Josh Gao191c1542015-12-09 11:26:11 -08001153 device.sync('data')
Dan Albertdef4aae2017-05-18 13:52:45 -07001154 if old_product_out is None:
1155 del os.environ['ANDROID_PRODUCT_OUT']
1156 else:
1157 os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
Josh Gao191c1542015-12-09 11:26:11 -08001158
Dan Albert06b0d6b2017-05-18 22:56:48 -07001159 self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
Josh Gao191c1542015-12-09 11:26:11 -08001160
Dan Albert06b0d6b2017-05-18 22:56:48 -07001161 #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
Josh Gao191c1542015-12-09 11:26:11 -08001162 finally:
1163 if base_dir is not None:
1164 shutil.rmtree(base_dir)
1165
Dan Albert06b0d6b2017-05-18 22:56:48 -07001166 def test_push_sync(self):
1167 """Sync a host directory to a specific path."""
1168
1169 try:
1170 temp_dir = tempfile.mkdtemp()
1171 temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
1172
1173 device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
1174
1175 # Clean up any stale files on the device.
1176 device = adb.get_device() # pylint: disable=no-member
1177 device.shell(['rm', '-rf', device_dir])
1178
1179 device.push(temp_dir, device_dir, sync=True)
1180
1181 self.verify_sync(device, temp_files, device_dir)
1182
1183 self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
1184 finally:
1185 if temp_dir is not None:
1186 shutil.rmtree(temp_dir)
1187
Josh Gao191c1542015-12-09 11:26:11 -08001188 def test_unicode_paths(self):
1189 """Ensure that we can support non-ASCII paths, even on Windows."""
1190 name = u'로보카 폴리'
1191
1192 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1193 remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
1194
1195 ## push.
1196 tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
1197 tf.close()
1198 self.device.push(tf.name, remote_path)
1199 os.remove(tf.name)
1200 self.assertFalse(os.path.exists(tf.name))
1201
1202 # Verify that the device ended up with the expected UTF-8 path
1203 output = self.device.shell(
1204 ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
1205 self.assertEqual(remote_path.encode('utf-8'), output)
1206
1207 # pull.
1208 self.device.pull(remote_path, tf.name)
1209 self.assertTrue(os.path.exists(tf.name))
1210 os.remove(tf.name)
1211 self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
1212
1213
Yabin Cuib5e11412017-03-10 16:01:01 -08001214class DeviceOfflineTest(DeviceTest):
1215 def _get_device_state(self, serialno):
1216 output = subprocess.check_output(self.device.adb_cmd + ['devices'])
1217 for line in output.split('\n'):
1218 m = re.match('(\S+)\s+(\S+)', line)
1219 if m and m.group(1) == serialno:
1220 return m.group(2)
1221 return None
1222
Josh Gao33d14b82017-09-13 14:51:23 -07001223 def disabled_test_killed_when_pushing_a_large_file(self):
Yabin Cuib5e11412017-03-10 16:01:01 -08001224 """
1225 While running adb push with a large file, kill adb server.
1226 Occasionally the device becomes offline. Because the device is still
1227 reading data without realizing that the adb server has been restarted.
1228 Test if we can bring the device online automatically now.
1229 http://b/32952319
1230 """
1231 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1232 # 1. Push a large file
1233 file_path = 'tmp_large_file'
1234 try:
1235 fh = open(file_path, 'w')
1236 fh.write('\0' * (100 * 1024 * 1024))
1237 fh.close()
1238 subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
1239 time.sleep(0.1)
1240 # 2. Kill the adb server
1241 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1242 subproc.terminate()
1243 finally:
1244 try:
1245 os.unlink(file_path)
1246 except:
1247 pass
1248 # 3. See if the device still exist.
1249 # Sleep to wait for the adb server exit.
1250 time.sleep(0.5)
1251 # 4. The device should be online
1252 self.assertEqual(self._get_device_state(serialno), 'device')
1253
Josh Gao33d14b82017-09-13 14:51:23 -07001254 def disabled_test_killed_when_pulling_a_large_file(self):
Yabin Cuib5e11412017-03-10 16:01:01 -08001255 """
1256 While running adb pull with a large file, kill adb server.
1257 Occasionally the device can't be connected. Because the device is trying to
1258 send a message larger than what is expected by the adb server.
1259 Test if we can bring the device online automatically now.
1260 """
1261 serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
1262 file_path = 'tmp_large_file'
1263 try:
1264 # 1. Create a large file on device.
1265 self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
1266 'bs=1000000', 'count=100'])
1267 # 2. Pull the large file on host.
1268 subproc = subprocess.Popen(self.device.adb_cmd +
1269 ['pull','/data/local/tmp/tmp_large_file', file_path])
1270 time.sleep(0.1)
1271 # 3. Kill the adb server
1272 subprocess.check_call(self.device.adb_cmd + ['kill-server'])
1273 subproc.terminate()
1274 finally:
1275 try:
1276 os.unlink(file_path)
1277 except:
1278 pass
1279 # 4. See if the device still exist.
1280 # Sleep to wait for the adb server exit.
1281 time.sleep(0.5)
1282 self.assertEqual(self._get_device_state(serialno), 'device')
1283
1284
Josh Gaoef3d3432017-05-02 15:01:09 -07001285 def test_packet_size_regression(self):
1286 """Test for http://b/37783561
1287
1288 Receiving packets of a length divisible by 512 but not 1024 resulted in
1289 the adb client waiting indefinitely for more input.
1290 """
1291 # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
1292 # Probe some surrounding values as well, for the hell of it.
Josh Gao2e05dfa2018-04-10 14:35:06 -07001293 for base in [512] + range(1024, 1024 * 16, 1024):
1294 for offset in [-6, -5, -4]:
1295 length = base + offset
1296 cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
1297 'echo', 'foo']
1298 rc, stdout, _ = self.device.shell_nocheck(cmd)
Josh Gaoef3d3432017-05-02 15:01:09 -07001299
Josh Gao2e05dfa2018-04-10 14:35:06 -07001300 self.assertEqual(0, rc)
Josh Gaoef3d3432017-05-02 15:01:09 -07001301
Josh Gao2e05dfa2018-04-10 14:35:06 -07001302 # Output should be '\0' * length, followed by "foo\n"
1303 self.assertEqual(length, len(stdout) - 4)
1304 self.assertEqual(stdout, "\0" * length + "foo\n")
Josh Gaoef3d3432017-05-02 15:01:09 -07001305
1306
Josh Gao191c1542015-12-09 11:26:11 -08001307def main():
1308 random.seed(0)
1309 if len(adb.get_devices()) > 0:
1310 suite = unittest.TestLoader().loadTestsFromName(__name__)
1311 unittest.TextTestRunner(verbosity=3).run(suite)
1312 else:
1313 print('Test suite must be run with attached devices')
1314
1315
1316if __name__ == '__main__':
1317 main()