Skip to content

Commit 481f653

Browse files
author
Andy C
committed
[test/interactive] Implement FAIL status
- A bunch of refactoring. - Document a test matrix. This is more work on #1077.
1 parent 2009f8f commit 481f653

File tree

1 file changed

+103
-82
lines changed

1 file changed

+103
-82
lines changed

test/interactive.py

+103-82
Original file line numberDiff line numberDiff line change
@@ -63,74 +63,6 @@ def stop_process__hack(name):
6363
send_signal(name, signal.SIGSTOP)
6464

6565

66-
class InteractiveTest(object):
67-
"""Define a test case for the interactive shell.
68-
69-
This is implemented as a context-manager wrapper around
70-
pexpect.spawn, use like this:
71-
72-
with InteractiveTest("Describe the test") as osh:
73-
osh.sendline(...)
74-
osh.expect(...)
75-
...
76-
"""
77-
78-
def __init__(self, description, program=SHELL):
79-
self.program = program
80-
self.shell = None
81-
self.description = description
82-
83-
def __enter__(self):
84-
if 0:
85-
if DEBUG:
86-
print(self.description)
87-
else:
88-
print(self.description, end='')
89-
90-
#env = dict(os.environ)
91-
#env['PS1'] = 'test$ '
92-
env = None
93-
94-
sh_argv = ['--rcfile', '/dev/null']
95-
96-
# Why the heck is --norc different from --rcfile /dev/null in bash??? This
97-
# makes it so the prompt of the parent shell doesn't leak. Very annoying.
98-
if self.program == 'bash':
99-
sh_argv.append('--norc')
100-
#print(sh_argv)
101-
102-
# Python 3: encoding required
103-
self.shell = pexpect.spawn(
104-
self.program, sh_argv, env=env, encoding='utf-8',
105-
timeout=TIMEOUT)
106-
107-
# suppress output when DEBUG is not set.
108-
if DEBUG:
109-
self.shell.logfile = sys.stdout
110-
111-
# generally don't want local echo, it gets confusing fast.
112-
self.shell.setecho(False)
113-
return self.shell
114-
115-
def __exit__(self, t, v, tb):
116-
global g_failures
117-
self.shell.close()
118-
119-
if not DEBUG:
120-
# show result of test
121-
if tb:
122-
g_failures += 1
123-
print("... Fail")
124-
else:
125-
print("... OK")
126-
# Allow other tests to keep running
127-
return True
128-
else:
129-
# Fail fast when in debug mode.
130-
pass
131-
132-
133-
13466
CASES = []
13567

13668
def register(skip_shells=None):
@@ -142,10 +74,62 @@ def decorator(func):
14274
return func
14375
return decorator
14476

77+
#
78+
# Test Cases
79+
#
80+
# TODO:
81+
# - Fold code from demo/
82+
# - bug-858-trap.sh
83+
# - sigwinch-bug.sh
84+
# - signal-during-read.sh -- actually 5 kinds of read
85+
# - Fill out this TEST MATRIX.
86+
#
87+
# A. Which shell? osh, bash, dash, etc.
88+
#
89+
# B. What mode is it in?
90+
#
91+
# 1. Interactive (stdin is a terminal)
92+
# 2. Non-interactive
93+
#
94+
# C. What is the main thread of the shell doing?
95+
#
96+
# 1. waiting for external process: sleep 1
97+
# 2. wait builtin: sleep 5 & wait
98+
# variants: wait -n: this matters when testing exit code
99+
# 3. read builtin read
100+
# variants: FIVE kinds, read -d, read -n, etc.
101+
# 4. computation, e.g. fibonacci with $(( a + b ))
102+
#
103+
# if interactive:
104+
# 5. keyboard input from terminal with select()
105+
#
106+
# Another way to categorize the main loop:
107+
# 1. running script code
108+
# 2. running trap code
109+
# 3. running TAB completion plugin code
110+
#
111+
# D. What is it interrupted by?
112+
#
113+
# 1. SIGINT
114+
# 2. SIGTSTP
115+
# 3. SIGWINCH
116+
# 4. SIGUSR1 -- doesn't this quit?
117+
#
118+
# if interactive:
119+
# 1. SIGINT Ctrl-C from terminal (relies on signal distribution to child?)
120+
# 2. SIGTSTP Ctrl-Z from terminal
121+
#
122+
# E. What is the signal state?
123+
#
124+
# 1. no trap handlers installed
125+
# 2. trap 'echo X' SIGWINCH
126+
# 3. trap 'echo X' SIGINT ?
127+
145128

146129
# TODO: Make this pass in OSH
147-
@register(skip_shells=['osh'])
148-
def a(sh):
130+
#@register(skip_shells=['osh'])
131+
@register()
132+
def t0(sh):
149133
'wait builtin then SIGWINCH (issue 1067)'
150134

151135
sh.sendline('sleep 1 &')
@@ -163,7 +147,7 @@ def a(sh):
163147

164148

165149
@register()
166-
def b(sh):
150+
def t1(sh):
167151
'Ctrl-C during external command'
168152

169153
sh.sendline('sleep 5')
@@ -178,7 +162,7 @@ def b(sh):
178162

179163

180164
@register()
181-
def c(sh):
165+
def t2(sh):
182166
'Ctrl-C during read builtin'
183167

184168
sh.sendline('read')
@@ -194,7 +178,7 @@ def c(sh):
194178

195179
# TODO: fix on OSH
196180
@register(skip_shells=['osh'])
197-
def d(sh):
181+
def t3(sh):
198182
'Ctrl-C during wait builtin'
199183

200184
sh.sendline('sleep 5 &')
@@ -211,7 +195,7 @@ def d(sh):
211195

212196

213197
@register()
214-
def e(sh):
198+
def t4(sh):
215199
'Ctrl-C during pipeline'
216200
sh.sendline('sleep 5 | cat')
217201

@@ -224,9 +208,8 @@ def e(sh):
224208
sh.expect('status=130')
225209

226210

227-
# TODO: make it work on OSH
228211
@register(skip_shells=['osh'])
229-
def f(sh):
212+
def t5(sh):
230213
'Ctrl-C during Command Sub (issue 467)'
231214
sh.sendline('`sleep 5`')
232215

@@ -241,7 +224,7 @@ def f(sh):
241224

242225

243226
@register(skip_shells=['bash'])
244-
def g(sh):
227+
def t6(sh):
245228
'fg twice should not result in fatal error (issue 1004)'
246229
sh.expect(r'.*\$ ')
247230
sh.sendline("cat")
@@ -260,7 +243,7 @@ def g(sh):
260243

261244

262245
@register(skip_shells=['bash'])
263-
def h(sh):
246+
def t7(sh):
264247
'Test resuming a killed process'
265248
sh.expect(r'.*\$ ')
266249
sh.sendline("cat")
@@ -276,7 +259,7 @@ def h(sh):
276259

277260

278261
@register(skip_shells=['bash'])
279-
def j(sh):
262+
def t8(sh):
280263
'Call fg after process exits (issue 721)'
281264

282265
sh.expect(r".*\$")
@@ -312,6 +295,7 @@ def WriteHeader(self, sh_labels):
312295
class Result(object):
313296
SKIP = 1
314297
OK = 2
298+
FAIL = 3
315299

316300

317301
def RunCases(cases, case_predicate, shell_pairs, results):
@@ -333,11 +317,40 @@ def RunCases(cases, case_predicate, shell_pairs, results):
333317
result_row.append(Result.SKIP)
334318
continue
335319

336-
with InteractiveTest(desc, program=shell_path) as sh:
320+
env = None
321+
sh_argv = ['--rcfile', '/dev/null']
322+
323+
# Why the heck is --norc different from --rcfile /dev/null in bash??? This
324+
# makes it so the prompt of the parent shell doesn't leak. Very annoying.
325+
if shell_label == 'bash':
326+
sh_argv.append('--norc')
327+
#print(sh_argv)
328+
329+
# Python 3: encoding required
330+
sh = pexpect.spawn(
331+
shell_path, sh_argv, env=env, encoding='utf-8', timeout=TIMEOUT)
332+
333+
# suppress output when DEBUG is not set.
334+
if DEBUG:
335+
sh.logfile = sys.stdout
336+
337+
# Generally don't want local echo, it gets confusing fast.
338+
sh.setecho(False)
339+
340+
ok = True
341+
try:
337342
func(sh)
343+
except Exception as e:
344+
import traceback
345+
print(e)
346+
result_row.append(Result.FAIL)
347+
ok = False
338348

339-
# TODO: Failing will raise an exception and we won't print anything?
340-
result_row.append(Result.OK)
349+
finally:
350+
sh.close()
351+
352+
if ok:
353+
result_row.append(Result.OK)
341354

342355
result_row.append(desc)
343356
results.append(result_row)
@@ -362,6 +375,8 @@ def PrintResults(shell_pairs, results):
362375
for cell in row[1:-1]:
363376
if cell == Result.SKIP:
364377
f.write('SKIP\t')
378+
elif cell == Result.FAIL:
379+
f.write('%sFAIL%s\t' % (ansi.BOLD + ansi.RED, ansi.RESET))
365380
elif cell == Result.OK:
366381
f.write('%sok%s\t' % (ansi.BOLD + ansi.GREEN, ansi.RESET))
367382
else:
@@ -376,6 +391,12 @@ def main(argv):
376391
o = spec_lib.Options()
377392
opts, argv = o.parse_args(argv)
378393

394+
# List test cases and return
395+
if opts.do_list:
396+
for i, (desc, _, _) in enumerate(CASES):
397+
print('%d\t%s' % (i, desc))
398+
return
399+
379400
shells = argv[1:]
380401
shell_pairs = spec_lib.MakeShellPairs(shells)
381402

0 commit comments

Comments
 (0)