Skip to content

Commit

Permalink
[stateful/job_control] Make case #0 work on bash and dash too
Browse files Browse the repository at this point in the history
Doesn't work on mksh somehow.

Also start using an ioctl() to send a signal to a terminal.  For Ctrl-Z
this somehow doesn't work with OSH, which I think is a bug.  It works
for Ctrl-C though.
  • Loading branch information
Andy C committed Feb 4, 2022
1 parent b1e1823 commit 7379ac8
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 33 deletions.
9 changes: 8 additions & 1 deletion spec/stateful/harness.py
Expand Up @@ -19,12 +19,17 @@
log = spec_lib.log


def expect_prompt(sh):
sh.expect(r'.*\$')


def get_pid_by_name(name):
"""Return the pid of the process matching `name`."""
# XXX: make sure this is restricted to subprocesses under us.
# This could be problematic on the continuous build if many tests are running
# in parallel.
output = pexpect.run('pgrep --exact --newest %s' % name)
#log('pgrep output %r' % output)
return int(output.split()[-1])


Expand Down Expand Up @@ -95,6 +100,8 @@ def RunCases(cases, case_predicate, shell_pairs, results):
sh = pexpect.spawn(
shell_path, sh_argv, env=env, encoding='utf-8', timeout=1.0)

sh.shell_label = shell_label # for tests to use

# Generally don't want local echo, it gets confusing fast.
sh.setecho(False)

Expand All @@ -103,7 +110,7 @@ def RunCases(cases, case_predicate, shell_pairs, results):
func(sh)
except Exception as e:
import traceback
print(e)
traceback.print_exc(file=sys.stderr)
result_row.append(Result.FAIL)
ok = False

Expand Down
146 changes: 114 additions & 32 deletions spec/stateful/job_control.py
@@ -1,68 +1,150 @@
#!/usr/bin/env python3
"""
interactive.py
spec/stateful/job_control.py
"""
from __future__ import print_function

import fcntl
import pty
import signal
import sys
import time

import harness
from harness import register, stop_process__hack, send_signal
from harness import register, stop_process__hack, send_signal, expect_prompt
from test.spec_lib import log


@register(skip_shells=['bash'])
# Hint from Stevens book
#
# http://lkml.iu.edu/hypermail/linux/kernel/1006.2/02460.html
# "TIOCSIG Generate a signal to processes in the
# current process group of the pty."

# Generated from C header file
TIOCSIG = 0x40045436

@register()
def t6(sh):
'fg twice should not result in fatal error (issue 1004)'
sh.expect(r'.*\$ ')
sh.sendline("cat")
stop_process__hack("cat")
sh.expect("\r\n\\[PID \\d+\\] Stopped")
sh.expect(r".*\$")
sh.sendline("fg")
sh.expect(r"Continue PID \d+")

#sh.sendcontrol("c")
sh.sendintr() # SIGINT
expect_prompt(sh)
sh.sendline('cat')

sh.expect(r".*\$")
sh.sendline("fg")
sh.expect("No job to put in the foreground")
time.sleep(0.1)

if 0:
os.system('ls -l /proc/%s/fd' % os.getpid())

if 0:
# TODO: Make this work for OSH!
fcntl.ioctl(sh.child_fd, TIOCSIG, signal.SIGTSTP)
else:
stop_process__hack('cat')

sh.expect('.*Stopped.*')

#sh.expect("\r\n\\[PID \\d+\\] Stopped")

sh.sendline('') # needed for dash

expect_prompt(sh)

sh.sendline('fg')

if sh.shell_label == 'osh':
sh.expect(r'Continue PID \d+')
else:
sh.expect('cat')

if 1:
# Ctrl-C to terminal
fcntl.ioctl(sh.child_fd, TIOCSIG, signal.SIGINT)
else:
sh.sendintr() # SIGINT

expect_prompt(sh)
sh.sendline('fg')

if sh.shell_label == 'osh':
sh.expect("No job to put in the foreground")
elif sh.shell_label == 'dash':
sh.expect('.*fg: No current job')
elif sh.shell_label == 'bash':
sh.expect('.*fg: current: no such job.*')
else:
raise AssertionError()


@register(skip_shells=['bash'])
def t7(sh):
'Test resuming a killed process'
sh.expect(r'.*\$ ')
sh.sendline("cat")
stop_process__hack("cat")
expect_prompt(sh)
sh.sendline('cat')
stop_process__hack('cat')

sh.expect("\r\n\\[PID \\d+\\] Stopped")
sh.expect(r".*\$")
sh.sendline("fg")
sh.expect(r"Continue PID \d+")
expect_prompt(sh)

sh.sendline('fg')
sh.expect(r'Continue PID \d+')

send_signal("cat", signal.SIGINT)
sh.expect(r".*\$")
sh.sendline("fg")
sh.expect("No job to put in the foreground")
expect_prompt(sh)

sh.sendline('fg')
sh.expect('No job to put in the foreground')


@register(skip_shells=['bash'])
def t8(sh):
'Call fg after process exits (issue 721)'

sh.expect(r".*\$")
sh.sendline("cat")
expect_prompt(sh)
sh.sendline('cat')

#osh.sendcontrol("c")
sh.sendintr() # SIGINT
expect_prompt(sh)

sh.expect(r".*\$")
sh.sendline("fg")
sh.expect("No job to put in the foreground")
sh.expect(r".*\$")
sh.sendline("fg")
sh.sendline('fg')
sh.expect("No job to put in the foreground")
sh.expect(r".*\$")
expect_prompt(sh)

sh.sendline('fg')
sh.expect('No job to put in the foreground')
expect_prompt(sh)


@register()
def bug_1005(sh):
'sleep 10 then Ctrl-Z then wait should not hang'

expect_prompt(sh)

return

sh.sendline('sleep 10')

# This is NOT right. The Ctrl-Z goes to the TERMINAL, and the TERMINAL sends
# it to both the shell process and the sleep process. The shell process
# should ignore it.

# Section 19.6 of APUE: SIGTSTP can't be delivered to a process!!
# Section 19.7: Signal generation with ioctl TIOCSIG!

#sh.kill(signal.SIGTSTP)

# This distribute Ctrl-Z to the whole process group? Why doesn't it work?
sh.sendcontrol('z')
sh.expect(r'.*Stopped.*')

#sh.expect(r'\^Z')

return
sh.sendline('wait')
sh.sendline('echo status=$?')
sh.expect(r"status=0")


if __name__ == '__main__':
Expand Down

0 comments on commit 7379ac8

Please sign in to comment.