Skip to content

Commit 96030d5

Browse files
Andy CKingMob
authored andcommitted
[spec/stateful] Implemented initial test param-passing for stateful tests needing term dimensions
1 parent 7fcb9dc commit 96030d5

File tree

3 files changed

+116
-85
lines changed

3 files changed

+116
-85
lines changed

spec/stateful/bind.py

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import pyte
1616

1717
import harness
18-
from harness import expect_prompt, register
18+
from harness import expect_prompt, register, TerminalDimensionEnvVars
1919

2020
from test.spec_lib import log
2121

@@ -181,8 +181,8 @@ def bind_x_readline_point(sh):
181181
sh.expect("READLINE_POINT is unset")
182182

183183

184-
@register(not_impl_shells=['dash', 'mksh'])
185-
def bind_x_set_readline_point_to_insert(sh):
184+
@register(not_impl_shells=['dash', 'mksh'], needs_dimensions=True)
185+
def bind_x_set_readline_point_to_insert(sh, test_params):
186186
"test bind -x for correctly using $READLINE_POINT to overwrite the cmd"
187187

188188
failing = "failing"
@@ -193,47 +193,48 @@ def bind_x_set_readline_point_to_insert(sh):
193193
bind_cmd = r"""bind -x '"\C-y": READLINE_POINT=%d' """ % new_rl_point
194194

195195
try:
196-
num_lines = int(os.environ['LINES'])
197-
num_columns = int(os.environ['COLUMNS'])
196+
num_lines = test_params['num_lines']
197+
num_columns = test_params['num_columns']
198198
except KeyError:
199-
raise RuntimeError("LINES and COLUMNS must be set")
199+
raise RuntimeError("num_lines and num_columns must be passed in")
200200

201-
screen = pyte.Screen(num_columns, num_lines)
202-
stream = pyte.Stream(screen)
201+
with TerminalDimensionEnvVars(num_lines, num_columns):
202+
screen = pyte.Screen(num_columns, num_lines)
203+
stream = pyte.Stream(screen)
203204

204-
# Need to echo, because we don't want just output
205-
sh.setecho(True)
205+
# Need to echo, because we don't want just output
206+
sh.setecho(True)
206207

207-
def _emulate_ansi_terminal(raw_output):
208-
stream.feed(raw_output)
208+
def _emulate_ansi_terminal(raw_output):
209+
stream.feed(raw_output)
209210

210-
lines = screen.display
211-
screen.reset()
211+
lines = screen.display
212+
screen.reset()
212213

213-
return '\n'.join(lines)
214+
return '\n'.join(lines)
214215

215-
# sh.sendline('stty -icanon')
216-
# time.sleep(0.1)
216+
# sh.sendline('stty -icanon')
217+
# time.sleep(0.1)
217218

218-
sh.sendline(bind_cmd)
219-
time.sleep(0.1)
219+
sh.sendline(bind_cmd)
220+
time.sleep(0.1)
220221

221-
sh.send(echo_cmd)
222-
time.sleep(0.1)
222+
sh.send(echo_cmd)
223+
time.sleep(0.1)
223224

224-
sh.sendcontrol('y')
225-
time.sleep(0.2)
225+
sh.sendcontrol('y')
226+
time.sleep(0.2)
226227

227-
sh.send("no longer ")
228-
time.sleep(0.1)
228+
sh.send("no longer ")
229+
time.sleep(0.1)
229230

230-
sh.expect(pexpect.TIMEOUT, timeout=2)
231+
sh.expect(pexpect.TIMEOUT, timeout=2)
231232

232-
screen_contents = _emulate_ansi_terminal(sh.before)
233-
if expected_cmd not in screen_contents:
234-
raise Exception(
235-
f"Expected command '{expected_cmd}' not found in screen contents:\n{screen_contents}"
236-
)
233+
screen_contents = _emulate_ansi_terminal(sh.before)
234+
if expected_cmd not in screen_contents:
235+
raise Exception(
236+
f"Expected command '{expected_cmd}' not found in screen contents:\n{screen_contents}"
237+
)
237238

238239

239240
@register(not_impl_shells=['dash', 'mksh'])

spec/stateful/harness.py

Lines changed: 83 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import optparse
1212
import os
1313
import pexpect
14+
import re
1415
import signal
1516
import sys
1617

@@ -49,12 +50,13 @@ def stop_process__hack(name, sig_num=signal.SIGSTOP):
4950
CASES = []
5051

5152

52-
def register(skip_shells=None, not_impl_shells=None):
53+
def register(skip_shells=None, not_impl_shells=None, needs_dimensions=False):
5354
skip_shells = skip_shells or []
5455
not_impl_shells = not_impl_shells or []
5556

5657
def decorator(func):
57-
CASES.append((func.__doc__, func, skip_shells, not_impl_shells))
58+
CASES.append((func.__doc__, func, skip_shells, not_impl_shells,
59+
needs_dimensions))
5860
return func
5961

6062
return decorator
@@ -67,6 +69,40 @@ class Result(object):
6769
FAIL = 4
6870

6971

72+
class TerminalDimensionEnvVars:
73+
"""
74+
Context manager for setting and unsetting LINES and COLUMNS environment variables.
75+
"""
76+
77+
def __init__(self, lines: int, columns: int):
78+
self.lines = lines
79+
self.columns = columns
80+
self.original_lines = None
81+
self.original_columns = None
82+
83+
def __enter__(self):
84+
# Save original values
85+
self.original_lines = os.environ.get('LINES')
86+
self.original_columns = os.environ.get('COLUMNS')
87+
88+
# Set new values
89+
os.environ['LINES'] = str(self.lines)
90+
os.environ['COLUMNS'] = str(self.columns)
91+
return self
92+
93+
def __exit__(self, exc_type, exc_val, exc_tb):
94+
# Restore original values
95+
if self.original_lines is None:
96+
del os.environ['LINES']
97+
else:
98+
os.environ['LINES'] = self.original_lines
99+
100+
if self.original_columns is None:
101+
del os.environ['COLUMNS']
102+
else:
103+
os.environ['COLUMNS'] = self.original_columns
104+
105+
70106
class TestRunner(object):
71107

72108
def __init__(self, num_retries, pexpect_timeout, verbose, num_lines,
@@ -77,7 +113,7 @@ def __init__(self, num_retries, pexpect_timeout, verbose, num_lines,
77113
self.num_lines = num_lines
78114
self.num_columns = num_columns
79115

80-
def RunOnce(self, shell_path, shell_label, func):
116+
def RunOnce(self, shell_path, shell_label, func, test_params={}):
81117
sh_argv = []
82118
if shell_label in ('bash', 'osh'):
83119
sh_argv.extend(['--rcfile', '/dev/null'])
@@ -87,57 +123,44 @@ def RunOnce(self, shell_path, shell_label, func):
87123
sh_argv.append('--norc')
88124
#print(sh_argv)
89125

90-
# Set LINES and COLUMNS in case a program needs them (like pyte tests)
91-
# Setting the dimensions kw param to spawn() is not sufficient
92-
original_lines = os.environ.get('LINES')
93-
original_columns = os.environ.get('COLUMNS')
94-
os.environ['LINES'] = str(self.num_lines)
95-
os.environ['COLUMNS'] = str(self.num_columns)
126+
# Python 3: encoding required
127+
sh = pexpect.spawn(
128+
shell_path,
129+
sh_argv,
130+
encoding="utf-8",
131+
dimensions=(self.num_lines, self.num_columns),
132+
# Generally don't want local echo of input, it gets confusing fast.
133+
echo=False,
134+
timeout=self.pexpect_timeout,
135+
)
96136

97-
try:
98-
# Python 3: encoding required
99-
sh = pexpect.spawn(
100-
shell_path,
101-
sh_argv,
102-
encoding="utf-8",
103-
dimensions=(self.num_lines, self.num_columns),
104-
# Generally don't want local echo of input, it gets confusing fast.
105-
echo=False,
106-
timeout=self.pexpect_timeout,
107-
)
108-
109-
sh.shell_label = shell_label # for tests to use
110-
111-
if self.verbose:
112-
sh.logfile = sys.stdout
113-
114-
ok = True
115-
try:
116-
func(sh)
117-
except Exception as e:
118-
import traceback
119-
traceback.print_exc(file=sys.stderr)
120-
return Result.FAIL
121-
ok = False
137+
sh.shell_label = shell_label # for tests to use
122138

123-
finally:
124-
sh.close()
139+
if self.verbose:
140+
sh.logfile = sys.stdout
125141

126-
if ok:
127-
return Result.OK
142+
ok = True
143+
try:
144+
# Support tests that need extra params (like dimensions), without
145+
# impacting existing tests
146+
if len(test_params) > 0:
147+
func(sh, test_params)
148+
else:
149+
func(sh)
150+
except Exception:
151+
import traceback
152+
traceback.print_exc(file=sys.stderr)
153+
return Result.FAIL
154+
ok = False
128155

129156
finally:
130-
if original_lines is None:
131-
del os.environ['LINES']
132-
else:
133-
os.environ['LINES'] = original_lines
134-
if original_columns is None:
135-
del os.environ['COLUMNS']
136-
else:
137-
os.environ['COLUMNS'] = original_columns
157+
sh.close()
138158

139-
def RunCase(self, shell_path, shell_label, func):
140-
result = self.RunOnce(shell_path, shell_label, func)
159+
if ok:
160+
return Result.OK
161+
162+
def RunCase(self, shell_path, shell_label, func, test_params={}):
163+
result = self.RunOnce(shell_path, shell_label, func, test_params)
141164

142165
if result == Result.OK:
143166
return result, -1 # short circuit for speed
@@ -148,7 +171,8 @@ def RunCase(self, shell_path, shell_label, func):
148171
log('\tFAILED first time: Retrying 4 times')
149172
for i in range(self.num_retries):
150173
log('\tRetry %d of %d', i + 1, self.num_retries)
151-
result = self.RunOnce(shell_path, shell_label, func)
174+
result = self.RunOnce(shell_path, shell_label, func,
175+
test_params)
152176
if result == Result.OK:
153177
num_success += 1
154178
else:
@@ -164,13 +188,18 @@ def RunCase(self, shell_path, shell_label, func):
164188

165189
def RunCases(self, cases, case_predicate, shell_pairs, result_table,
166190
flaky):
167-
for case_num, (desc, func, skip_shells,
168-
not_impl_shells) in enumerate(cases):
191+
for case_num, (desc, func, skip_shells, not_impl_shells,
192+
needs_dimensions) in enumerate(cases):
169193
if not case_predicate(case_num, desc):
170194
continue
171195

172196
result_row = [case_num]
173197

198+
test_params = {}
199+
if needs_dimensions:
200+
test_params['num_lines'] = self.num_lines
201+
test_params['num_columns'] = self.num_columns
202+
174203
for shell_label, shell_path in shell_pairs:
175204
skip_str = ''
176205
if shell_label in skip_shells:
@@ -195,7 +224,8 @@ def RunCases(self, cases, case_predicate, shell_pairs, result_table,
195224
flaky[case_num, shell_label] = -1
196225
continue
197226

198-
result, retries = self.RunCase(shell_path, shell_label, func)
227+
result, retries = self.RunCase(shell_path, shell_label, func,
228+
test_params)
199229
flaky[case_num, shell_label] = retries
200230

201231
result_row.append(result)
@@ -360,7 +390,7 @@ def main(argv):
360390

361391
# List test cases and return
362392
if opts.do_list:
363-
for i, (desc, _, _, _) in enumerate(CASES):
393+
for i, (desc, *_) in enumerate(CASES):
364394
print('%d\t%s' % (i, desc))
365395
return
366396

test/stateful.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ job-control() {
5555
}
5656

5757
bind() {
58-
spec/stateful/bind.py $FIRST --oils-failures-allowed 4 "$@"
58+
spec/stateful/bind.py $FIRST --oils-failures-allowed 4 --num-lines 24 --num-columns 80 "$@"
5959
}
6060

6161
# Run on just 2 shells

0 commit comments

Comments
 (0)