2016-10-13 02:03:30 -04:00
|
|
|
import os
|
2016-10-13 02:41:53 -04:00
|
|
|
import sys
|
2016-10-13 02:45:37 -04:00
|
|
|
import shutil
|
2016-10-16 22:20:36 +03:00
|
|
|
import subprocess as sp
|
2016-10-13 02:03:30 -04:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2016-10-13 02:41:53 -04:00
|
|
|
import xonsh
|
|
|
|
from xonsh.platform import ON_WINDOWS
|
|
|
|
|
2017-02-12 12:52:13 -05:00
|
|
|
from tools import (skip_if_on_windows, skip_if_on_darwin, skip_if_on_travis,
|
|
|
|
ON_WINDOWS, ON_DARWIN, ON_TRAVIS)
|
2016-10-16 20:30:24 +03:00
|
|
|
|
2016-10-16 21:08:37 +03:00
|
|
|
|
2016-10-13 02:41:53 -04:00
|
|
|
XONSH_PREFIX = xonsh.__file__
|
|
|
|
if 'site-packages' in XONSH_PREFIX:
|
|
|
|
# must be installed version of xonsh
|
|
|
|
num_up = 5
|
|
|
|
else:
|
|
|
|
# must be in source dir
|
|
|
|
num_up = 2
|
|
|
|
for i in range(num_up):
|
|
|
|
XONSH_PREFIX = os.path.dirname(XONSH_PREFIX)
|
|
|
|
PATH = os.path.join(os.path.dirname(__file__), 'bin') + os.pathsep + \
|
|
|
|
os.path.join(XONSH_PREFIX, 'bin') + os.pathsep + \
|
|
|
|
os.path.join(XONSH_PREFIX, 'Scripts') + os.pathsep + \
|
|
|
|
os.path.join(XONSH_PREFIX, 'scripts') + os.pathsep + \
|
|
|
|
os.path.dirname(sys.executable) + os.pathsep + \
|
|
|
|
os.environ['PATH']
|
|
|
|
|
2016-10-16 21:08:37 +03:00
|
|
|
|
2017-02-12 11:42:44 -05:00
|
|
|
skip_if_no_xonsh = pytest.mark.skipif(shutil.which('xonsh', path=PATH) is None,
|
|
|
|
reason='xonsh not on path')
|
|
|
|
|
|
|
|
|
2016-10-16 22:20:36 +03:00
|
|
|
def run_xonsh(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.STDOUT):
|
2016-10-16 21:08:37 +03:00
|
|
|
env = dict(os.environ)
|
|
|
|
env['PATH'] = PATH
|
|
|
|
env['XONSH_DEBUG'] = '1'
|
|
|
|
env['XONSH_SHOW_TRACEBACK'] = '1'
|
|
|
|
env['RAISE_SUBPROC_ERROR'] = '1'
|
2016-10-16 21:43:38 +03:00
|
|
|
env['PROMPT'] = ''
|
2016-10-16 21:08:37 +03:00
|
|
|
xonsh = 'xonsh.bat' if ON_WINDOWS else 'xon.sh'
|
|
|
|
xonsh = shutil.which(xonsh, path=PATH)
|
2016-10-16 22:20:36 +03:00
|
|
|
proc = sp.Popen([xonsh, '--no-rc'],
|
|
|
|
env=env,
|
|
|
|
stdin=stdin,
|
|
|
|
stdout=stdout,
|
|
|
|
stderr=stderr,
|
|
|
|
universal_newlines=True,
|
|
|
|
)
|
2016-10-16 21:08:37 +03:00
|
|
|
try:
|
|
|
|
out, err = proc.communicate(input=cmd, timeout=10)
|
2016-10-16 22:20:36 +03:00
|
|
|
except sp.TimeoutExpired:
|
2016-10-16 21:08:37 +03:00
|
|
|
proc.kill()
|
|
|
|
raise
|
|
|
|
return out, err, proc.returncode
|
|
|
|
|
2017-02-12 13:11:29 -05:00
|
|
|
|
|
|
|
def check_run_xonsh(cmd, fmt, exp):
|
|
|
|
"""The ``fmt`` parameter is a function
|
|
|
|
that formats the output of cmd, can be None.
|
|
|
|
"""
|
|
|
|
out, err, rtn = run_xonsh(cmd, stderr=sp.DEVNULL)
|
|
|
|
if callable(fmt):
|
|
|
|
out = fmt(out)
|
|
|
|
if callable(exp):
|
|
|
|
exp = exp()
|
|
|
|
assert out == exp
|
|
|
|
assert rtn == 0
|
|
|
|
|
|
|
|
|
2016-10-13 02:03:30 -04:00
|
|
|
#
|
|
|
|
# The following list contains a (stdin, stdout, returncode) tuples
|
|
|
|
#
|
|
|
|
|
|
|
|
ALL_PLATFORMS = [
|
|
|
|
# test calling a function alias
|
|
|
|
("""
|
|
|
|
def _f():
|
|
|
|
print('hello')
|
|
|
|
|
|
|
|
aliases['f'] = _f
|
|
|
|
f
|
|
|
|
""", "hello\n", 0),
|
2017-12-07 23:24:06 -05:00
|
|
|
# test redirecting a function alias to a file
|
2016-10-13 02:03:30 -04:00
|
|
|
("""
|
|
|
|
def _f():
|
|
|
|
print('Wow Mom!')
|
|
|
|
|
|
|
|
aliases['f'] = _f
|
|
|
|
f > tttt
|
|
|
|
|
|
|
|
with open('tttt') as tttt:
|
|
|
|
s = tttt.read().strip()
|
|
|
|
print('REDIRECTED OUTPUT: ' + s)
|
|
|
|
""", "REDIRECTED OUTPUT: Wow Mom!\n", 0),
|
2017-12-07 23:24:06 -05:00
|
|
|
# test redirecting a function alias from stderr -> stdout
|
|
|
|
("""
|
|
|
|
def _f(args, stdin, stdout, stderr):
|
|
|
|
print('The Truth is Out There', file=stderr)
|
|
|
|
|
|
|
|
aliases['f'] = _f
|
|
|
|
f e>o
|
|
|
|
""", "The Truth is Out There\n", 0),
|
2016-10-13 03:20:42 -04:00
|
|
|
# test system exit in function alias
|
|
|
|
("""
|
|
|
|
import sys
|
|
|
|
def _f():
|
|
|
|
sys.exit(42)
|
|
|
|
|
|
|
|
aliases['f'] = _f
|
|
|
|
print(![f].returncode)
|
|
|
|
""", "42\n", 0),
|
2016-10-24 22:41:01 -04:00
|
|
|
# test uncaptured streaming alias,
|
|
|
|
# order actually printed in is non-deterministic
|
2016-10-13 22:53:06 -04:00
|
|
|
("""
|
|
|
|
def _test_stream(args, stdin, stdout, stderr):
|
2016-10-24 22:41:01 -04:00
|
|
|
print('hallo on stream', file=stderr)
|
|
|
|
print('hallo on stream', file=stdout)
|
2016-10-13 22:53:06 -04:00
|
|
|
return 1
|
|
|
|
|
|
|
|
aliases['test-stream'] = _test_stream
|
|
|
|
x = ![test-stream]
|
|
|
|
print(x.returncode)
|
2016-10-24 22:41:01 -04:00
|
|
|
""", "hallo on stream\nhallo on stream\n1\n", 0),
|
2016-10-13 22:53:06 -04:00
|
|
|
# test captured streaming alias
|
|
|
|
("""
|
|
|
|
def _test_stream(args, stdin, stdout, stderr):
|
|
|
|
print('hallo on err', file=stderr)
|
|
|
|
print('hallo on out', file=stdout)
|
|
|
|
return 1
|
|
|
|
|
|
|
|
aliases['test-stream'] = _test_stream
|
|
|
|
x = !(test-stream)
|
|
|
|
print(x.returncode)
|
|
|
|
""", "hallo on err\n1\n", 0),
|
2016-10-16 12:07:39 -04:00
|
|
|
# test piping aliases
|
|
|
|
("""
|
|
|
|
def dummy(args, inn, out, err):
|
|
|
|
out.write('hey!')
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def dummy2(args, inn, out, err):
|
|
|
|
s = inn.read()
|
|
|
|
out.write(s.upper())
|
|
|
|
return 0
|
|
|
|
|
|
|
|
aliases['d'] = dummy
|
|
|
|
aliases['d2'] = dummy2
|
|
|
|
d | d2
|
|
|
|
""", "HEY!", 0),
|
2016-10-18 21:14:08 -04:00
|
|
|
# test output larger than most pipe buffers
|
|
|
|
("""
|
|
|
|
def _g(args, stdin=None):
|
|
|
|
for i in range(1000):
|
|
|
|
print('x' * 100)
|
|
|
|
|
|
|
|
aliases['g'] = _g
|
|
|
|
g
|
|
|
|
""", (("x"*100) + '\n') * 1000, 0),
|
2016-10-25 20:55:42 -04:00
|
|
|
# test piping 'real' command
|
|
|
|
("""
|
|
|
|
with open('tttt', 'w') as fp:
|
|
|
|
fp.write("Wow mom!\\n")
|
|
|
|
|
2016-10-26 00:40:47 -04:00
|
|
|
![cat tttt | wc]
|
2016-10-26 00:51:56 -04:00
|
|
|
""", ' 1 2 10\n' if ON_WINDOWS else " 1 2 9 <stdin>\n", 0),
|
2016-10-26 20:51:28 -04:00
|
|
|
# test double piping 'real' command
|
|
|
|
("""
|
|
|
|
with open('tttt', 'w') as fp:
|
|
|
|
fp.write("Wow mom!\\n")
|
|
|
|
|
|
|
|
![cat tttt | wc | wc]
|
2016-10-26 20:55:40 -04:00
|
|
|
""", ' 1 3 24\n' if ON_WINDOWS else " 1 4 16 <stdin>\n", 0),
|
2017-02-11 14:54:36 -05:00
|
|
|
# test unthreadable alias (which should trigger a ProcPoxy call)
|
|
|
|
("""
|
2017-02-12 11:01:24 +08:00
|
|
|
from xonsh.tools import unthreadable
|
2017-02-11 14:54:36 -05:00
|
|
|
|
|
|
|
@unthreadable
|
|
|
|
def _f():
|
|
|
|
return 'hello\\n'
|
|
|
|
|
|
|
|
aliases['f'] = _f
|
|
|
|
f
|
|
|
|
""", "hello\n", 0),
|
2017-02-26 21:54:39 -05:00
|
|
|
# test ambiguous globs
|
|
|
|
("""
|
|
|
|
import os
|
|
|
|
|
|
|
|
def _echo(args):
|
|
|
|
print(' '.join(args))
|
|
|
|
aliases['echo'] = _echo
|
|
|
|
|
|
|
|
files = ['Actually_test.tst', 'Actually.tst', 'Complete_test.tst', 'Complete.tst']
|
|
|
|
|
|
|
|
# touch the file
|
|
|
|
for f in files:
|
|
|
|
with open(f, 'w'):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# echo the files
|
|
|
|
echo *.tst and echo *_test.tst
|
|
|
|
echo *_test.tst
|
|
|
|
echo *_test.tst and echo *.tst
|
|
|
|
|
|
|
|
# remove the files
|
|
|
|
for f in files:
|
|
|
|
os.remove(f)
|
2017-02-27 14:11:39 -05:00
|
|
|
""",
|
|
|
|
'Actually.tst Actually_test.tst Complete.tst Complete_test.tst\n'
|
|
|
|
'Actually_test.tst Complete_test.tst\n'
|
|
|
|
'Actually_test.tst Complete_test.tst\n'
|
|
|
|
'Actually_test.tst Complete_test.tst\n'
|
|
|
|
'Actually.tst Actually_test.tst Complete.tst Complete_test.tst\n',
|
|
|
|
0),
|
2017-03-02 22:49:52 -05:00
|
|
|
#
|
|
|
|
# test ambiguous line continuations
|
|
|
|
#
|
|
|
|
("""
|
|
|
|
def _echo(args):
|
|
|
|
print(' '.join(args))
|
|
|
|
aliases['echo'] = _echo
|
|
|
|
|
|
|
|
echo --option1 \
|
|
|
|
--option2
|
|
|
|
""", '--option1 --option2\n', 0),
|
2017-04-21 17:46:04 -04:00
|
|
|
#
|
|
|
|
# test @$() with aliases
|
|
|
|
#
|
|
|
|
("""
|
|
|
|
aliases['ls'] = 'spam spam sausage spam'
|
|
|
|
|
|
|
|
echo @$(which ls)
|
|
|
|
""", 'spam spam sausage spam\n', 0),
|
2016-10-13 02:03:30 -04:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('case', ALL_PLATFORMS)
|
|
|
|
def test_script(case):
|
|
|
|
script, exp_out, exp_rtn = case
|
2016-10-16 21:08:37 +03:00
|
|
|
out, err, rtn = run_xonsh(script)
|
2017-02-27 14:11:39 -05:00
|
|
|
assert exp_out == out
|
2016-10-16 21:08:37 +03:00
|
|
|
assert exp_rtn == rtn
|
2016-10-13 02:03:30 -04:00
|
|
|
|
2016-10-16 20:30:24 +03:00
|
|
|
|
2017-01-11 23:29:28 -05:00
|
|
|
ALL_PLATFORMS_STDERR = [
|
|
|
|
# test redirecting a function alias
|
|
|
|
("""
|
2017-12-07 23:24:06 -05:00
|
|
|
def _f(args, stdin, stdout):
|
2017-01-11 23:29:28 -05:00
|
|
|
print('Wow Mom!', file=stdout)
|
|
|
|
|
|
|
|
aliases['f'] = _f
|
|
|
|
f o>e
|
|
|
|
""", "Wow Mom!\n", 0),
|
|
|
|
]
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('case', ALL_PLATFORMS_STDERR)
|
2017-07-15 17:51:21 -05:00
|
|
|
def test_script_stderr(case):
|
2017-01-11 23:29:28 -05:00
|
|
|
script, exp_err, exp_rtn = case
|
|
|
|
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
|
|
|
|
assert exp_err == err
|
|
|
|
assert exp_rtn == rtn
|
|
|
|
|
2016-10-16 20:30:24 +03:00
|
|
|
@skip_if_on_windows
|
|
|
|
@pytest.mark.parametrize('cmd, fmt, exp', [
|
2016-10-26 00:40:47 -04:00
|
|
|
('pwd', None, lambda: os.getcwd() + '\n'),
|
2016-10-16 20:30:24 +03:00
|
|
|
('echo WORKING', None, 'WORKING\n'),
|
|
|
|
('ls -f', lambda out: out.splitlines().sort(), os.listdir().sort()),
|
|
|
|
])
|
2017-02-11 19:22:30 -05:00
|
|
|
def test_single_command_no_windows(cmd, fmt, exp):
|
2017-02-12 13:11:29 -05:00
|
|
|
check_run_xonsh(cmd, fmt, exp)
|
2017-02-11 19:22:30 -05:00
|
|
|
|
|
|
|
|
2017-07-15 17:51:21 -05:00
|
|
|
def test_eof_syntax_error():
|
|
|
|
"""Ensures syntax errors for EOF appear on last line."""
|
|
|
|
script = 'x = 1\na = (1, 0\n'
|
|
|
|
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
|
2017-07-15 17:53:47 -05:00
|
|
|
assert ':0:0: EOF in multi-line statement' not in err
|
|
|
|
assert ':2:0: EOF in multi-line statement' in err
|
2017-07-15 17:51:21 -05:00
|
|
|
|
|
|
|
|
2017-02-12 17:09:51 -05:00
|
|
|
_bad_case = pytest.mark.skipif(ON_DARWIN or ON_WINDOWS or ON_TRAVIS,
|
|
|
|
reason="bad platforms")
|
2017-02-12 13:11:29 -05:00
|
|
|
|
|
|
|
@_bad_case
|
|
|
|
def test_printfile():
|
2017-02-12 17:09:51 -05:00
|
|
|
check_run_xonsh('printfile.xsh', None, 'printfile.xsh\n')
|
2017-02-12 13:11:29 -05:00
|
|
|
|
|
|
|
|
|
|
|
@_bad_case
|
|
|
|
def test_printname():
|
2017-02-12 17:09:51 -05:00
|
|
|
check_run_xonsh('printfile.xsh', None, 'printfile.xsh\n')
|
2017-02-12 13:11:29 -05:00
|
|
|
|
|
|
|
|
|
|
|
@_bad_case
|
|
|
|
def test_sourcefile():
|
2017-02-12 17:18:57 -05:00
|
|
|
check_run_xonsh('printfile.xsh', None, 'printfile.xsh\n')
|
2017-02-12 13:11:29 -05:00
|
|
|
|
2016-10-16 20:30:24 +03:00
|
|
|
|
2017-02-25 21:45:32 -05:00
|
|
|
@_bad_case
|
|
|
|
@pytest.mark.parametrize('cmd, fmt, exp', [
|
|
|
|
# test subshell wrapping
|
|
|
|
("""
|
|
|
|
with open('tttt', 'w') as fp:
|
|
|
|
fp.write("Wow mom!\\n")
|
|
|
|
|
|
|
|
(wc) < tttt
|
|
|
|
""", None, " 1 2 9 <stdin>\n"),
|
|
|
|
# test subshell statement wrapping
|
|
|
|
("""
|
|
|
|
with open('tttt', 'w') as fp:
|
|
|
|
fp.write("Wow mom!\\n")
|
|
|
|
|
|
|
|
(wc;) < tttt
|
|
|
|
""", None, " 1 2 9 <stdin>\n"),
|
|
|
|
])
|
|
|
|
def test_subshells(cmd, fmt, exp):
|
|
|
|
check_run_xonsh(cmd, fmt, exp)
|
|
|
|
|
2016-10-16 20:30:24 +03:00
|
|
|
|
|
|
|
@skip_if_on_windows
|
2016-10-16 21:08:37 +03:00
|
|
|
@pytest.mark.parametrize('cmd, exp', [
|
2016-10-26 00:40:47 -04:00
|
|
|
('pwd', lambda: os.getcwd() + '\n'),
|
2016-10-16 20:30:24 +03:00
|
|
|
])
|
2016-10-16 21:08:37 +03:00
|
|
|
def test_redirect_out_to_file(cmd, exp, tmpdir):
|
2016-10-16 20:30:24 +03:00
|
|
|
outfile = tmpdir.mkdir('xonsh_test_dir').join('xonsh_test_file')
|
2016-11-14 21:42:43 -05:00
|
|
|
command = '{} > {}\n'.format(cmd, outfile)
|
2016-10-16 21:43:38 +03:00
|
|
|
out, _, _ = run_xonsh(command)
|
2016-10-16 20:30:24 +03:00
|
|
|
content = outfile.read()
|
2016-10-26 00:40:47 -04:00
|
|
|
if callable(exp):
|
|
|
|
exp = exp()
|
2016-10-16 20:30:24 +03:00
|
|
|
assert content == exp
|