adb: add tests for -Tt shell arguments.
Adds python tests to check that -T (disable PTY) and -t (force PTY) arguments work as expected for `adb shell`. Bug: http://b/23825231 Change-Id: I5343fae35b2be8459a9b95125f66def46c26adf4
This commit is contained in:
@@ -156,7 +156,7 @@ class AndroidDevice(object):
|
|||||||
# adb on Windows returns \r\n even if adbd returns \n.
|
# adb on Windows returns \r\n even if adbd returns \n.
|
||||||
_RETURN_CODE_SEARCH_LENGTH = len('{0}255\r\n'.format(_RETURN_CODE_DELIMITER))
|
_RETURN_CODE_SEARCH_LENGTH = len('{0}255\r\n'.format(_RETURN_CODE_DELIMITER))
|
||||||
|
|
||||||
# Shell protocol feature string.
|
# Feature name strings.
|
||||||
SHELL_PROTOCOL_FEATURE = 'shell_2'
|
SHELL_PROTOCOL_FEATURE = 'shell_2'
|
||||||
|
|
||||||
def __init__(self, serial, product=None):
|
def __init__(self, serial, product=None):
|
||||||
|
|||||||
@@ -112,6 +112,33 @@ class DeviceTest(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class ShellTest(DeviceTest):
|
class ShellTest(DeviceTest):
|
||||||
|
def _interactive_shell(self, shell_args, input):
|
||||||
|
"""Runs an interactive adb shell.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shell_args: List of string arguments to `adb shell`.
|
||||||
|
input: String input to send to the interactive shell.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The remote exit code.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
unittest.SkipTest: The device doesn't support exit codes.
|
||||||
|
"""
|
||||||
|
if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
|
||||||
|
raise unittest.SkipTest('exit codes are unavailable on this device')
|
||||||
|
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
self.device.adb_cmd + ['shell'] + shell_args,
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
# Closing host-side stdin doesn't currently trigger the interactive
|
||||||
|
# shell to exit so we need to explicitly add an exit command to
|
||||||
|
# close the session from the device side, and append linesep to complete
|
||||||
|
# the interactive command.
|
||||||
|
proc.communicate('{}; exit{}'.format(input, self.device.linesep))
|
||||||
|
return proc.returncode
|
||||||
|
|
||||||
def test_cat(self):
|
def test_cat(self):
|
||||||
"""Check that we can at least cat a file."""
|
"""Check that we can at least cat a file."""
|
||||||
out = self.device.shell(['cat', '/proc/uptime'])[0].strip()
|
out = self.device.shell(['cat', '/proc/uptime'])[0].strip()
|
||||||
@@ -155,30 +182,27 @@ class ShellTest(DeviceTest):
|
|||||||
output = self.device.shell(['uname'])[0]
|
output = self.device.shell(['uname'])[0]
|
||||||
self.assertEqual(output, 'Linux' + self.device.linesep)
|
self.assertEqual(output, 'Linux' + self.device.linesep)
|
||||||
|
|
||||||
def test_pty_logic(self):
|
def test_default_pty_logic(self):
|
||||||
"""Verify PTY logic for shells.
|
"""Verify default PTY logic for shells.
|
||||||
|
|
||||||
Interactive shells should use a PTY, non-interactive should not.
|
Interactive shells should use a PTY, non-interactive should not.
|
||||||
|
|
||||||
Bug: http://b/21215503
|
Bug: http://b/21215503
|
||||||
"""
|
"""
|
||||||
proc = subprocess.Popen(
|
|
||||||
self.device.adb_cmd + ['shell'], stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
# [ -t 0 ] is used (rather than `tty`) to provide portability. This
|
# [ -t 0 ] is used (rather than `tty`) to provide portability. This
|
||||||
# gives an exit code of 0 iff stdin is connected to a terminal.
|
# gives an exit code of 0 iff stdin is connected to a terminal.
|
||||||
#
|
self.assertEqual(0, self._interactive_shell([], '[ -t 0 ]'))
|
||||||
# Closing host-side stdin doesn't currently trigger the interactive
|
self.assertEqual(1, self.device.shell_nocheck(['[ -t 0 ]'])[0])
|
||||||
# shell to exit so we need to explicitly add an exit command to
|
|
||||||
# close the session from the device side, and append \n to complete
|
|
||||||
# the interactive command.
|
|
||||||
result = proc.communicate('[ -t 0 ]; echo x$?; exit 0\n')[0]
|
|
||||||
partition = result.rpartition('x')
|
|
||||||
self.assertEqual(partition[1], 'x')
|
|
||||||
self.assertEqual(int(partition[2]), 0)
|
|
||||||
|
|
||||||
exit_code = self.device.shell_nocheck(['[ -t 0 ]'])[0]
|
def test_pty_arguments(self):
|
||||||
self.assertEqual(exit_code, 1)
|
"""Tests the -T and -t arguments to manually control PTY."""
|
||||||
|
if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
|
||||||
|
raise unittest.SkipTest('PTY arguments unsupported on this device')
|
||||||
|
|
||||||
|
self.assertEqual(0, self._interactive_shell(['-t'], '[ -t 0 ]'))
|
||||||
|
self.assertEqual(1, self._interactive_shell(['-T'], '[ -t 0 ]'))
|
||||||
|
self.assertEqual(0, self.device.shell_nocheck(['-t', '[ -t 0 ]'])[0])
|
||||||
|
self.assertEqual(1, self.device.shell_nocheck(['-T', '[ -t 0 ]'])[0])
|
||||||
|
|
||||||
def test_shell_protocol(self):
|
def test_shell_protocol(self):
|
||||||
"""Tests the shell protocol on the device.
|
"""Tests the shell protocol on the device.
|
||||||
@@ -190,9 +214,9 @@ class ShellTest(DeviceTest):
|
|||||||
"""
|
"""
|
||||||
if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
|
if self.device.SHELL_PROTOCOL_FEATURE not in self.device.features:
|
||||||
raise unittest.SkipTest('shell protocol unsupported on this device')
|
raise unittest.SkipTest('shell protocol unsupported on this device')
|
||||||
|
|
||||||
result = self.device.shell_nocheck(
|
result = self.device.shell_nocheck(
|
||||||
shlex.split('echo foo; echo bar >&2; exit 17'))
|
shlex.split('echo foo; echo bar >&2; exit 17'))
|
||||||
|
|
||||||
self.assertEqual(17, result[0])
|
self.assertEqual(17, result[0])
|
||||||
self.assertEqual('foo' + self.device.linesep, result[1])
|
self.assertEqual('foo' + self.device.linesep, result[1])
|
||||||
self.assertEqual('bar' + self.device.linesep, result[2])
|
self.assertEqual('bar' + self.device.linesep, result[2])
|
||||||
|
|||||||
Reference in New Issue
Block a user