[FIX] use flag_eof, and make more robust terminating process

This commit is contained in:
Benedek Racz 2020-04-05 09:10:15 +02:00
parent 3ba138a6fc
commit 215f688562
4 changed files with 65 additions and 25 deletions

View File

@ -148,6 +148,10 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
wexpect.EOF]) wexpect.EOF])
self.assertEqual(index, 3, (index, p.before, p.after)) self.assertEqual(index, 3, (index, p.before, p.after))
# Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def test_expect_index (self): def test_expect_index (self):
'''This tests that mixed list of regex strings, TIMEOUT, and EOF all '''This tests that mixed list of regex strings, TIMEOUT, and EOF all
return the correct index when matched. return the correct index when matched.
@ -161,6 +165,9 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
p = wexpect.spawn('cat', timeout=5, echo=False) p = wexpect.spawn('cat', timeout=5, echo=False)
p.expect = p.expect_exact p.expect = p.expect_exact
self._expect_index(p) self._expect_index(p)
# Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def _expect_index (self, p): def _expect_index (self, p):
p.sendline ('1234') p.sendline ('1234')
@ -218,6 +225,10 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
the_old_way = the_old_way.replace('\r\n', '\n' the_old_way = the_old_way.replace('\r\n', '\n'
).replace('\r', '\n').replace('\n\n', '\n').rstrip() ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
self.assertEqual(the_old_way, the_new_way) self.assertEqual(the_old_way, the_new_way)
# Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
p = wexpect.spawn('echo hello.?world') p = wexpect.spawn('echo hello.?world')
i = p.expect_exact('.?') i = p.expect_exact('.?')
self.assertEqual(p.before, 'hello') self.assertEqual(p.before, 'hello')
@ -236,6 +247,10 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
).replace('\r', '\n').replace('\n\n', '\n').rstrip() ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
self.assertEqual(the_old_way, the_new_way) self.assertEqual(the_old_way, the_new_way)
# Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def test_expect_timeout (self): def test_expect_timeout (self):
p = wexpect.spawn('cat', timeout=5) p = wexpect.spawn('cat', timeout=5)
p.expect(wexpect.TIMEOUT) # This tells it to wait for timeout. p.expect(wexpect.TIMEOUT) # This tells it to wait for timeout.
@ -250,6 +265,9 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
else: else:
self.fail ('Expected an EOF exception.') self.fail ('Expected an EOF exception.')
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def test_buffer_interface(self): def test_buffer_interface(self):
p = wexpect.spawn('cat', timeout=5) p = wexpect.spawn('cat', timeout=5)
p.sendline ('Hello') p.sendline ('Hello')
@ -257,6 +275,8 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
assert len(p.buffer) assert len(p.buffer)
p.buffer = 'Testing' p.buffer = 'Testing'
p.sendeof () p.sendeof ()
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def _before_after(self, p): def _before_after(self, p):
p.timeout = 5 p.timeout = 5
@ -275,6 +295,9 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
assert p.before.startswith(', 51, 52'), p.before[:20] assert p.before.startswith(', 51, 52'), p.before[:20]
assert p.before.endswith(', 99]\r\n'), p.before[-20:] assert p.before.endswith(', 99]\r\n'), p.before[-20:]
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def test_before_after(self): def test_before_after(self):
'''This tests expect() for some simple before/after things. '''This tests expect() for some simple before/after things.
''' '''
@ -313,6 +336,8 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
p.sendline('list(range(4*5))') p.sendline('list(range(4*5))')
self.assertEqual(p.expect(['12,', '2,']), 1) self.assertEqual(p.expect(['12,', '2,']), 1)
p.sendline('exit()')
def test_ordering(self): def test_ordering(self):
'''This tests expect() for which pattern is returned '''This tests expect() for which pattern is returned
when many may eventually match. I (Grahn) am a bit when many may eventually match. I (Grahn) am a bit
@ -333,6 +358,10 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase):
p.expect = p.expect_exact p.expect = p.expect_exact
self._ordering(p) self._ordering(p)
# Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
if wexpect.spawn_class_name == 'SpawnSocket':
p.wait()
def _greed(self, expect): def _greed(self, expect):
# End at the same point: the one with the earliest start should win # End at the same point: the one with the earliest start should win
self.assertEqual(expect(['3, 4', '2, 3, 4']), 1) self.assertEqual(expect(['3, 4', '2, 3, 4']), 1)

View File

@ -63,6 +63,7 @@ def main():
buffer_size_x=args.buffer_size_x, buffer_size_y=args.buffer_size_y, buffer_size_x=args.buffer_size_x, buffer_size_y=args.buffer_size_y,
local_echo=str2bool(args.local_echo), interact=str2bool(args.interact)) local_echo=str2bool(args.local_echo), interact=str2bool(args.interact))
logger.info(f'Exiting with status: {cons.child_exitstatus}')
sys.exit(cons.child_exitstatus) sys.exit(cons.child_exitstatus)
except Exception as e: # pragma: no cover except Exception as e: # pragma: no cover

View File

@ -469,7 +469,9 @@ class ConsoleReaderSocket(ConsoleReaderBase):
def close_connection(self): def close_connection(self):
if self.connection: if self.connection:
self.connection.shutdown(socket.SHUT_RDWR)
self.connection.close() self.connection.close()
self.connection = None
def send_to_host(self, msg): def send_to_host(self, msg):
# convert to bytes # convert to bytes

View File

@ -441,6 +441,9 @@ class SpawnBase:
# Child process has not been started... Not alive # Child process has not been started... Not alive
return False return False
if self.exitstatus is not None:
return False
try: try:
self.exitstatus = self.child_process.wait(timeout=0) self.exitstatus = self.child_process.wait(timeout=0)
logger.info(f'exitstatus: {self.exitstatus}') logger.info(f'exitstatus: {self.exitstatus}')
@ -450,11 +453,17 @@ class SpawnBase:
def kill(self, sig=signal.SIGTERM): def kill(self, sig=signal.SIGTERM):
"""Sig == sigint for ctrl-c otherwise the child is terminated.""" """Sig == sigint for ctrl-c otherwise the child is terminated."""
try: try:
self.child_process.send_signal(sig) logger.info(f'Sending kill signal: {sig}')
except psutil.NoSuchProcess as e: self.send(SIGNAL_CHARS[sig])
logger.info('Child has already died. %s', e) self.terminated = True
except EOF as e:
logger.info(e)
def wait(self, child=True, console=False): def wait(self, child=True, console=False):
if self.exitstatus is not None:
return self.exitstatus
if child: if child:
self.exitstatus = self.child_process.wait() self.exitstatus = self.child_process.wait()
logger.info(f'exitstatus: {self.exitstatus}') logger.info(f'exitstatus: {self.exitstatus}')
@ -589,6 +598,10 @@ class SpawnBase:
def send(self, s, delaybeforesend=None): def send(self, s, delaybeforesend=None):
"""Virtual definition """Virtual definition
""" """
if self.flag_eof:
logger.info('EOF: End of file has been already detected.')
raise EOF('End of file has been already detected.')
if delaybeforesend is None: if delaybeforesend is None:
delaybeforesend = self.delaybeforesend delaybeforesend = self.delaybeforesend
@ -837,8 +850,8 @@ class SpawnBase:
else: else:
self.match = None self.match = None
self.match_index = None self.match_index = None
logger.info(f'EOF: {e}\n{self}') logger.info('Raise EOF again')
raise EOF(f'{e}\n{self}') raise
except TIMEOUT as e: except TIMEOUT as e:
self.buffer = incoming self.buffer = incoming
self.before = incoming self.before = incoming
@ -968,26 +981,19 @@ class SpawnPipe(SpawnBase):
except pywintypes.error as e: except pywintypes.error as e:
if e.args[0] == winerror.ERROR_BROKEN_PIPE: # 109 if e.args[0] == winerror.ERROR_BROKEN_PIPE: # 109
logger.info("EOF: broken pipe, bye bye") logger.info("EOF: broken pipe, bye bye")
self.flag_eof = True
raise EOF("broken pipe, bye bye") raise EOF("broken pipe, bye bye")
elif e.args[0] == winerror.ERROR_NO_DATA: elif e.args[0] == winerror.ERROR_NO_DATA:
'''232 (0xE8) '''232 (0xE8)
The pipe is being closed. The pipe is being closed.
''' '''
logger.info("The pipe is being closed.") logger.info("The pipe is being closed.")
self.flag_eof = True
raise EOF("The pipe is being closed.") raise EOF("The pipe is being closed.")
else: else:
raise raise
return len(s) return len(s)
def kill(self, sig=signal.SIGTERM):
"""Sig == sigint for ctrl-c otherwise the child is terminated."""
try:
logger.info(f'Sending kill signal: {sig}')
self.send(SIGNAL_CHARS[sig])
self.terminated = True
except EOF as e:
logger.info(e)
class SpawnSocket(SpawnBase): class SpawnSocket(SpawnBase):
@ -1015,8 +1021,16 @@ class SpawnSocket(SpawnBase):
the log. """ the log. """
if isinstance(s, str): if isinstance(s, str):
s = str.encode(s) s = str.encode(s)
try:
if s:
logger.debug(f"Writing: {s}")
self.sock.sendall(s) self.sock.sendall(s)
logger.spam(f"WriteFile finished.")
return len(s) return len(s)
except ConnectionResetError as e:
logger.info("ConnectionResetError")
self.flag_eof = True
raise EOF("ConnectionResetError")
def connect_to_child(self): def connect_to_child(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -1024,7 +1038,9 @@ class SpawnSocket(SpawnBase):
self.sock.settimeout(.2) self.sock.settimeout(.2)
def disconnect_from_child(self): def disconnect_from_child(self):
logger.info('disconnect_from_child')
if self.sock: if self.sock:
self.sock.shutdown(socket.SHUT_RDWR)
self.sock.close() self.sock.close()
self.sock = None self.sock = None
@ -1065,14 +1081,6 @@ class SpawnSocket(SpawnBase):
return s.decode() return s.decode()
def kill(self, sig=signal.SIGTERM):
"""Sig == sigint for ctrl-c otherwise the child is terminated."""
try:
logger.info(f'Sending kill signal: {sig}')
self.send(SIGNAL_CHARS[sig])
except EOF as e:
logger.info(e)
class searcher_re (object): class searcher_re (object):
"""This is regular expression string search helper for the """This is regular expression string search helper for the