diff options
| author | Jeff Quast <contact@jeffquast.com> | 2014-11-23 04:23:31 -0800 |
|---|---|---|
| committer | Jeff Quast <contact@jeffquast.com> | 2014-11-23 04:23:31 -0800 |
| commit | 01ea0ff440facd47894c076747488c28291a47cc (patch) | |
| tree | 9a6a1f02e34f364ecfd9fed378a41ac6df8eb4eb /tests | |
| parent | a88e60f8403b1d25db1cca857009f3612a8f1e19 (diff) | |
| parent | 00c8aaed9605c446844bbe379582753492a3627b (diff) | |
| download | pexpect-01ea0ff440facd47894c076747488c28291a47cc.tar.gz | |
Merge remote-tracking branch 'origin/master' into issue-104-cannot-exec-setuids
Conflicts:
doc/history.rst
Diffstat (limited to 'tests')
| -rwxr-xr-x | tests/test_ansi.py | 59 | ||||
| -rw-r--r-- | tests/test_async.py | 51 | ||||
| -rwxr-xr-x | tests/test_constructor.py | 10 | ||||
| -rwxr-xr-x | tests/test_expect.py | 37 | ||||
| -rw-r--r-- | tests/test_repr.py | 26 | ||||
| -rwxr-xr-x | tests/test_run.py | 38 | ||||
| -rwxr-xr-x | tests/test_screen.py | 124 |
7 files changed, 318 insertions, 27 deletions
diff --git a/tests/test_ansi.py b/tests/test_ansi.py index 516509c..a9d445e 100755 --- a/tests/test_ansi.py +++ b/tests/test_ansi.py @@ -21,6 +21,9 @@ PEXPECT LICENSE from pexpect import ANSI import unittest from . import PexpectTestCase +import sys + +PY3 = (sys.version_info[0] >= 3) write_target = 'I\'ve got a ferret sticking up my nose. \n' +\ '(He\'s got a ferret sticking up his nose.) \n' +\ @@ -162,6 +165,62 @@ class ansiTestCase (PexpectTestCase.PexpectTestCase): assert str(s) == ('test ') assert s.state.memory == [s] + def test_utf8_bytes(self): + """Test that when bytes are passed in containing UTF-8 encoded + characters, where the encoding of each character consists of + multiple bytes, the characters are correctly decoded. + Incremental decoding is also tested.""" + s = ANSI.ANSI(2, 10, encoding='utf-8') + # This is the UTF-8 encoding of the UCS character "HOURGLASS" + # followed by the UTF-8 encoding of the UCS character + # "KEYBOARD". These characters can't be encoded in cp437 or + # latin-1. The "KEYBOARD" character is split into two + # separate writes. + s.write(b'\xe2\x8c\x9b') + s.write(b'\xe2\x8c') + s.write(b'\xa8') + if PY3: + assert str(s) == u'\u231b\u2328 \n ' + else: + assert unicode(s) == u'\u231b\u2328 \n ' + assert str(s) == b'\xe2\x8c\x9b\xe2\x8c\xa8 \n ' + assert s.dump() == u'\u231b\u2328 ' + assert s.pretty() == u'+----------+\n|\u231b\u2328 |\n| |\n+----------+\n' + assert s.get_abs(1, 1) == u'\u231b' + assert s.get_region(1, 1, 1, 5) == [u'\u231b\u2328 '] + + def test_unicode(self): + """Test passing in of a unicode string.""" + s = ANSI.ANSI(2, 10, encoding="utf-8") + s.write(u'\u231b\u2328') + if PY3: + assert str(s) == u'\u231b\u2328 \n ' + else: + assert unicode(s) == u'\u231b\u2328 \n ' + assert str(s) == b'\xe2\x8c\x9b\xe2\x8c\xa8 \n ' + assert s.dump() == u'\u231b\u2328 ' + assert s.pretty() == u'+----------+\n|\u231b\u2328 |\n| |\n+----------+\n' + assert s.get_abs(1, 1) == u'\u231b' + assert s.get_region(1, 1, 1, 5) == [u'\u231b\u2328 '] + + def test_decode_error(self): + """Test that default handling of decode errors replaces the + invalid characters.""" + s = ANSI.ANSI(2, 10, encoding="ascii") + s.write(b'\xff') # a non-ASCII character + # In unicode, the non-ASCII character is replaced with + # REPLACEMENT CHARACTER. + if PY3: + assert str(s) == u'\ufffd \n ' + else: + assert unicode(s) == u'\ufffd \n ' + assert str(s) == b'? \n ' + assert s.dump() == u'\ufffd ' + assert s.pretty() == u'+----------+\n|\ufffd |\n| |\n+----------+\n' + assert s.get_abs(1, 1) == u'\ufffd' + assert s.get_region(1, 1, 1, 5) == [u'\ufffd '] + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_async.py b/tests/test_async.py new file mode 100644 index 0000000..ce75572 --- /dev/null +++ b/tests/test_async.py @@ -0,0 +1,51 @@ +try: + import asyncio +except ImportError: + asyncio = None + +import sys +import unittest + +import pexpect +from .PexpectTestCase import PexpectTestCase + +def run(coro): + return asyncio.get_event_loop().run_until_complete(coro) + +@unittest.skipIf(asyncio is None, "Requires asyncio") +class AsyncTests(PexpectTestCase): + def test_simple_expect(self): + p = pexpect.spawn('cat') + p.sendline('Hello asyncio') + coro = p.expect(['Hello', pexpect.EOF] , async=True) + assert run(coro) == 0 + print('Done') + + def test_timeout(self): + p = pexpect.spawn('cat') + coro = p.expect('foo', timeout=1, async=True) + with self.assertRaises(pexpect.TIMEOUT): + run(coro) + + p = pexpect.spawn('cat') + coro = p.expect(['foo', pexpect.TIMEOUT], timeout=1, async=True) + assert run(coro) == 1 + + def test_eof(self): + p = pexpect.spawn('cat') + p.sendline('Hi') + coro = p.expect(pexpect.EOF, async=True) + p.sendeof() + assert run(coro) == 0 + + p = pexpect.spawn('cat') + p.sendeof() + coro = p.expect('Blah', async=True) + with self.assertRaises(pexpect.EOF): + run(coro) + + def test_expect_exact(self): + p = pexpect.spawn('%s list100.py' % sys.executable) + assert run(p.expect_exact(b'5', async=True)) == 0 + assert run(p.expect_exact(['wpeok', b'11'], async=True)) == 1 + assert run(p.expect_exact([b'foo', pexpect.EOF], async=True)) == 1 diff --git a/tests/test_constructor.py b/tests/test_constructor.py index 60525a0..98c473a 100755 --- a/tests/test_constructor.py +++ b/tests/test_constructor.py @@ -28,11 +28,11 @@ class TestCaseConstructor(PexpectTestCase.PexpectTestCase): the same results for different styles of invoking __init__(). This assumes that the root directory / is static during the test. ''' - p1 = pexpect.spawn('/bin/ls -l /bin') - p2 = pexpect.spawn('/bin/ls' ,['-l', '/bin']) - p1.expect (pexpect.EOF) - p2.expect (pexpect.EOF) - assert (p1.before == p2.before) + p1 = pexpect.spawn('uname -m -n -p -r -s -v') + p2 = pexpect.spawn('uname', ['-m', '-n', '-p', '-r', '-s', '-v']) + p1.expect(pexpect.EOF) + p2.expect(pexpect.EOF) + assert p1.before == p2.before def test_named_parameters (self): '''This tests that named parameters work. diff --git a/tests/test_expect.py b/tests/test_expect.py index 8ccb9c5..3f4c9d8 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -18,11 +18,13 @@ PEXPECT LICENSE OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ''' +import multiprocessing import unittest import subprocess import time import signal import sys +import os import pexpect from . import PexpectTestCase @@ -542,7 +544,40 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): signal.alarm(1) p1.expect('END') + def test_stdin_closed(self): + ''' + Ensure pexpect continues to operate even when stdin is closed + ''' + class Closed_stdin_proc(multiprocessing.Process): + def run(self): + sys.__stdin__.close() + cat = pexpect.spawn('cat') + cat.sendeof() + cat.expect(pexpect.EOF) + + proc = Closed_stdin_proc() + proc.start() + proc.join() + assert proc.exitcode == 0 + + def test_stdin_stdout_closed(self): + ''' + Ensure pexpect continues to operate even when stdin and stdout is closed + ''' + class Closed_stdin_stdout_proc(multiprocessing.Process): + def run(self): + sys.__stdin__.close() + sys.__stdout__.close() + cat = pexpect.spawn('cat') + cat.sendeof() + cat.expect(pexpect.EOF) + + proc = Closed_stdin_stdout_proc() + proc.start() + proc.join() + assert proc.exitcode == 0 + if __name__ == '__main__': unittest.main() -suite = unittest.makeSuite(ExpectTestCase,'test') +suite = unittest.makeSuite(ExpectTestCase, 'test') diff --git a/tests/test_repr.py b/tests/test_repr.py new file mode 100644 index 0000000..ce618d4 --- /dev/null +++ b/tests/test_repr.py @@ -0,0 +1,26 @@ +""" Test __str__ methods. """ +import pexpect + +from . import PexpectTestCase + + +class TestCaseMisc(PexpectTestCase.PexpectTestCase): + + def test_str_spawnu(self): + """ Exercise spawnu.__str__() """ + # given, + p = pexpect.spawnu('cat') + # exercise, + value = str(p) + # verify + assert isinstance(value, str) + + def test_str_spawn(self): + """ Exercise spawn.__str__() """ + # given, + p = pexpect.spawn('cat') + # exercise, + value = str(p) + # verify + assert isinstance(value, str) + diff --git a/tests/test_run.py b/tests/test_run.py index 814b70a..c018b4d 100755 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -22,14 +22,11 @@ PEXPECT LICENSE import pexpect import unittest import subprocess +import tempfile import sys +import os from . import PexpectTestCase -# TODO Many of these test cases blindly assume that sequential -# TODO listing of the /bin directory will yield the same results. -# TODO This may not always be true, but seems adequate for testing for now. -# TODO I should fix this at some point. - unicode_type = str if pexpect.PY3 else unicode def timeout_callback (d): @@ -44,14 +41,24 @@ class RunFuncTestCase(PexpectTestCase.PexpectTestCase): empty = b'' prep_subprocess_out = staticmethod(lambda x: x) + def setUp(self): + fd, self.rcfile = tempfile.mkstemp() + os.write(fd, b'PS1=GO: \n') + os.close(fd) + super(RunFuncTestCase, self).setUp() + + def tearDown(self): + os.unlink(self.rcfile) + super(RunFuncTestCase, self).tearDown() + def test_run_exit (self): (data, exitstatus) = self.runfunc('python exit1.py', withexitstatus=1) assert exitstatus == 1, "Exit status of 'python exit1.py' should be 1." def test_run (self): - the_old_way = subprocess.Popen(args=['ls', '-l', '/bin'], + the_old_way = subprocess.Popen(args=['uname', '-m', '-n'], stdout=subprocess.PIPE).communicate()[0].rstrip() - (the_new_way, exitstatus) = self.runfunc('ls -l /bin', withexitstatus=1) + (the_new_way, exitstatus) = self.runfunc('uname -m -n', withexitstatus=1) the_new_way = the_new_way.replace(self.cr, self.empty).rstrip() self.assertEqual(self.prep_subprocess_out(the_old_way), the_new_way) self.assertEqual(exitstatus, 0) @@ -64,6 +71,23 @@ class RunFuncTestCase(PexpectTestCase.PexpectTestCase): withexitstatus=1) assert exitstatus != 0 + def test_run_tuple_list (self): + events = [ + # second match on 'abc', echo 'def' + ('abc\r\n.*GO:', 'echo "def"\n'), + # final match on 'def': exit + ('def\r\n.*GO:', 'exit\n'), + # first match on 'GO:' prompt, echo 'abc' + ('GO:', 'echo "abc"\n') + ] + + (data, exitstatus) = pexpect.run( + 'bash --rcfile {0}'.format(self.rcfile), + withexitstatus=True, + events=events, + timeout=10) + assert exitstatus == 0 + class RunUnicodeFuncTestCase(RunFuncTestCase): runfunc = staticmethod(pexpect.runu) cr = b'\r'.decode('ascii') diff --git a/tests/test_screen.py b/tests/test_screen.py index 3f0736b..2429e57 100755 --- a/tests/test_screen.py +++ b/tests/test_screen.py @@ -19,10 +19,14 @@ PEXPECT LICENSE ''' +import sys + from pexpect import screen import unittest from . import PexpectTestCase +PY3 = (sys.version_info[0] >= 3) + fill1_target='XXXXXXXXXX\n' + \ 'XOOOOOOOOX\n' + \ 'XO::::::OX\n' + \ @@ -76,6 +80,17 @@ insert_target = 'ZXZZZZZZXZ\n' +\ 'ZZ/2.4.6ZZ' get_region_target = ['......', '.\\/...', './\\...', '......'] +unicode_box_unicode_result = u'\u2554\u2557\n\u255A\u255D' +unicode_box_pretty_result = u'''\ ++--+ +|\u2554\u2557| +|\u255A\u255D| ++--+ +''' +unicode_box_ascii_bytes_result = b'??\n??' +unicode_box_cp437_bytes_result = b'\xc9\xbb\n\xc8\xbc' +unicode_box_utf8_bytes_result = b'\xe2\x95\x94\xe2\x95\x97\n\xe2\x95\x9a\xe2\x95\x9d' + class screenTestCase (PexpectTestCase.PexpectTestCase): def make_screen_with_put (self): s = screen.screen(10,10) @@ -168,20 +183,101 @@ class screenTestCase (PexpectTestCase.PexpectTestCase): s.insert_abs (10,9,'Z') s.insert_abs (10,9,'Z') assert str(s) == insert_target - # def test_write (self): - # s = screen.screen (6,65) - # s.fill('.') - # s.cursor_home() - # for c in write_text: - # s.write (c) - # print str(s) - # assert str(s) == write_target - # def test_tetris (self): - # s = screen.screen (24,80) - # tetris_text = open ('tetris.data').read() - # for c in tetris_text: - # s.write (c) - # assert str(s) == tetris_target + + def make_screen_with_box_unicode(self, *args, **kwargs): + '''Creates a screen containing a box drawn using double-line + line drawing characters. The characters are fed in as + unicode. ''' + s = screen.screen (2,2,*args,**kwargs) + s.put_abs (1,1,u'\u2554') + s.put_abs (1,2,u'\u2557') + s.put_abs (2,1,u'\u255A') + s.put_abs (2,2,u'\u255D') + return s + + def make_screen_with_box_cp437(self, *args, **kwargs): + '''Creates a screen containing a box drawn using double-line + line drawing characters. The characters are fed in as + CP437. ''' + s = screen.screen (2,2,*args,**kwargs) + s.put_abs (1,1,b'\xc9') + s.put_abs (1,2,b'\xbb') + s.put_abs (2,1,b'\xc8') + s.put_abs (2,2,b'\xbc') + return s + + def make_screen_with_box_utf8(self, *args, **kwargs): + '''Creates a screen containing a box drawn using double-line + line drawing characters. The characters are fed in as + UTF-8. ''' + s = screen.screen (2,2,*args,**kwargs) + s.put_abs (1,1,b'\xe2\x95\x94') + s.put_abs (1,2,b'\xe2\x95\x97') + s.put_abs (2,1,b'\xe2\x95\x9a') + s.put_abs (2,2,b'\xe2\x95\x9d') + return s + + def test_unicode_ascii (self): + # With the default encoding set to ASCII, we should still be + # able to feed in unicode strings and get them back out: + s = self.make_screen_with_box_unicode('ascii') + if PY3: + assert str(s) == unicode_box_unicode_result + else: + assert unicode(s) == unicode_box_unicode_result + # And we should still get something for Python 2 str(), though + # it might not be very useful + str(s) + + assert s.pretty() == unicode_box_pretty_result + + def test_decoding_errors(self): + # With strict error handling, it should reject bytes it can't decode + with self.assertRaises(UnicodeDecodeError): + self.make_screen_with_box_cp437('ascii', 'strict') + + # replace should turn them into unicode replacement characters, U+FFFD + s = self.make_screen_with_box_cp437('ascii', 'replace') + expected = u'\ufffd\ufffd\n\ufffd\ufffd' + if PY3: + assert str(s) == expected + else: + assert unicode(s) == expected + + def test_unicode_cp437 (self): + # Verify decoding from and re-encoding to CP437. + s = self.make_screen_with_box_cp437('cp437','strict') + if PY3: + assert str(s) == unicode_box_unicode_result + else: + assert unicode(s) == unicode_box_unicode_result + assert str(s) == unicode_box_cp437_bytes_result + assert s.pretty() == unicode_box_pretty_result + + def test_unicode_utf8 (self): + # Verify decoding from and re-encoding to UTF-8. + s = self.make_screen_with_box_utf8('utf-8','strict') + if PY3: + assert str(s) == unicode_box_unicode_result + else: + assert unicode(s) == unicode_box_unicode_result + assert str(s) == unicode_box_utf8_bytes_result + assert s.pretty() == unicode_box_pretty_result + + def test_no_bytes(self): + s = screen.screen(2, 2, encoding=None) + s.put_abs(1, 1, u'A') + s.put_abs(2, 2, u'D') + + with self.assertRaises(TypeError): + s.put_abs(1, 2, b'B') + + if PY3: + assert str(s) == u'A \n D' + else: + assert unicode(s) == u'A \n D' + # This will still work if it's limited to ascii + assert str(s) == b'A \n D' if __name__ == '__main__': unittest.main() |
