test failure on FreeBSD
emaste opened this issue · 5 comments
Running the tests on FreeBSD 11 I see:
% py.test
============================= test session starts ==============================
platform freebsd11 -- Python 2.7.9 -- py-1.4.26 -- pytest-2.6.4
collected 5 items
tests/test_invalid_binary.py .
tests/test_preexec_fn.py ..
tests/test_spawn.py FF
=================================== FAILURES ===================================
__________________________ PtyTestCase.test_spawn_sh ___________________________
self = <tests.test_spawn.PtyTestCase testMethod=test_spawn_sh>
def test_spawn_sh(self):
env = os.environ.copy()
env['FOO'] = 'rebar'
p = PtyProcess.spawn(['sh'], env=env)
p.read()
p.write(b'echo $FOO\n')
time.sleep(0.1)
response = p.read()
> assert b'rebar' in response
E AssertionError: assert 'rebar' in 'echo $FOO\r\n'
tests/test_spawn.py:15: AssertionError
______________________ PtyTestCase.test_spawn_unicode_sh _______________________
self = <tests.test_spawn.PtyTestCase testMethod=test_spawn_unicode_sh>
def test_spawn_unicode_sh(self):
env = os.environ.copy()
env['FOO'] = 'rebar'
p = PtyProcessUnicode.spawn(['sh'], env=env)
p.read()
p.write(u'echo $FOO\n')
time.sleep(0.1)
response = p.read()
> assert u'rebar' in response
\n' AssertionError: assert 'rebar' in 'echo $FOO
tests/test_spawn.py:31: AssertionError
====================== 2 failed, 3 passed in 0.80 seconds ======================
The expected output is produced, it is just not returned by the second p.read()
in the test. Inserting a dummy p.read()
before response = p.read()
gets the tests passing.
I was not running tests on FreeBSD (10-Release) for ptyprocess (even though we do for pexpect). Addressing this now and we'll see how the tests do -- thanks for noticing!
Strange enough, output is seen three times with /bin/sh through ptyprocess.
We would expect it once for input (echo=True by default), and a second time for output. I'm not sure yet what is happening.
/bin/sh
In [8]: p = ptyprocess.PtyProcess.spawn(['sh'])
In [9]: p.write('echo HELLO; exit 0\n')
Out[9]: 19L
In [10]: p.read()
Out[10]: '(ptyprocess)[\\u@freebsd /home/freebsd/Code/ptyprocess]$ echo HELLO; exit 0\r\n'
In [11]: p.read()
Out[11]: 'echo HELLO; exit 0\r\n'
In [12]: p.read()
Out[12]: 'HELLO\r\n'
In [13]: p.read()
---------------------------------------------------------------------------
EOFError Traceback (most recent call last)
<ipython-input-13-3cef27ad5fc2> in <module>()
----> 1 p.read()
/usr/home/freebsd/Code/ptyprocess/ptyprocess/ptyprocess.pyc in read(self, size)
520 # BSD-style EOF (also appears to work on recent Solaris (OpenIndiana))
521 self.flag_eof = True
--> 522 raise EOFError('End Of File (EOF). Empty string style platform.')
523
524 return s
EOFError: End Of File (EOF). Empty string style platform.
bash
In [2]: p = ptyprocess.PtyProcess.spawn(['bash'])
In [3]: p.write('echo HELLO; exit 0\n')
Out[3]: 19L
In [4]: p.read()
Out[4]: '(ptyprocess)[freebsd@freebsd ~/Code/ptyprocess]$ echo HELLO; exit 0\r\n'
In [5]: p.read()
Out[5]: 'HELLO\r\nexit\r\n'
In [6]: p.read()
---------------------------------------------------------------------------
EOFError Traceback (most recent call last)
<ipython-input-6-3cef27ad5fc2> in <module>()
----> 1 p.read()
/usr/home/freebsd/Code/ptyprocess/ptyprocess/ptyprocess.pyc in read(self, size)
520 # BSD-style EOF (also appears to work on recent Solaris (OpenIndiana))
521 self.flag_eof = True
--> 522 raise EOFError('End Of File (EOF). Empty string style platform.')
523
524 return s
EOFError: End Of File (EOF). Empty string style platform.
This is very strange. It is some form of a race condition that is particular with the AT&T shell supplied by FreeBSD. I'm going to just write some kind of workaround for the test case: I don't believe it exposes any actual bug with ptyprocess.
Triggered on 2nd call:
$ while [ 0 ]; do python /tmp/test_freebsd.py | grep '^-stty' && break || echo -n '.'; done
.-stty -a; TEST=1; echo $TEST; echo $FOO; exit 0
Triggered on 81st call:
$ while [ 0 ]; do python /tmp/test_freebsd.py | grep '^-stty' && break || echo -n '.'; done
.................................................................................-stty -a; TEST=1; echo $TEST; echo $FOO; exit 0
test_freebsd.py is:
from __future__ import print_function
import difflib
from ptyprocess.ptyprocess import PtyProcess
cmd = 'stty -a; TEST=1; echo $TEST; echo $FOO; exit 0\n'
env = {'PS1': '\$ ', 'FOO': 'bar'}
def get_out(proc):
out = ''
while True:
try:
out += proc.read()
except EOFError:
return out
bash = PtyProcess.spawn(['bash'], env=env, cwd='/')
bash.write(cmd)
out_bash = get_out(bash)
attsh = PtyProcess.spawn(['sh'], env=env, cwd='/')
attsh.write(cmd)
out_attsh = get_out(attsh)
map(print, difflib.unified_diff(
out_attsh.splitlines(),
out_bash.splitlines(),
fromfile='AT&T compatible shell',
tofile='bourne-again shell'))
There is something I am not able to understand about /bin/sh on freebsd: This is actually some kind of "second output of input echo" that occurs only with the AT&T-compatible /bin/sh on FreeBSD, and intermittently (a modified version of the test runs forever on OSX and Linux).