OILS / test / spec_lib.py View on Github | oils.pub

308 lines, 241 significant
1"""spec_lib.py.
2
3Shared between sh_spec.py (Python 2) and spec/stateful/harness.py
4(Python 3)!
5"""
6from __future__ import print_function
7
8import os
9import re
10import sys
11
12
13def log(msg, *args):
14 # type: (str, *Any) -> None
15 if args:
16 msg = msg % args
17 print(msg, file=sys.stderr)
18
19
20# Note that devtools/release.sh spec-all runs with bin/osh and $DIR/_bin/osh,
21# which should NOT match
22
23OSH_CPP_RE = re.compile(
24 r'_bin/\w+-\w+(-sh)?/osh') # e.g. $PWD/_bin/cxx-dbg/osh
25YSH_CPP_RE = re.compile(
26 r'_bin/\w+-\w+(-sh)?/ysh') # e.g. $PWD/_bin/cxx-dbg/ysh
27OIL_CPP_RE = re.compile(r'_bin/\w+-\w+(-sh)?/oil')
28
29
30def MakeShellPairs(shells):
31 shell_pairs = []
32
33 saw_osh = False
34 saw_ysh = False
35 saw_oil = False
36
37 for path in shells:
38 if path[-1].isdigit(): # bash-4.4, zsh-5.9
39 label = os.path.basename(path)[:7]
40 else:
41 first, _ = os.path.splitext(path)
42 label = os.path.basename(first)
43
44 # Hack for 'toysh': it's the only shell we call as 'sh'
45 if label == 'sh':
46 label = 'toysh'
47
48 if label == 'osh':
49 # change the second 'osh' to 'osh_ALT' so it's distinct
50 if saw_osh:
51 if OSH_CPP_RE.search(path):
52 label = 'osh-cpp'
53 else:
54 label = 'osh_ALT'
55 saw_osh = True
56
57 elif label == 'ysh':
58 if saw_ysh:
59 if YSH_CPP_RE.search(path):
60 label = 'ysh-cpp'
61 else:
62 label = 'ysh_ALT'
63
64 saw_ysh = True
65
66 elif label == 'oil': # TODO: remove this
67 if saw_oil:
68 if OIL_CPP_RE.search(path):
69 label = 'oil-cpp'
70 else:
71 label = 'oil_ALT'
72
73 saw_oil = True
74
75 shell_pairs.append((label, path))
76 return shell_pairs
77
78
79RANGE_RE = re.compile('(\d+) \s* - \s* (\d+)', re.VERBOSE)
80
81
82def ParseRange(range_str):
83 try:
84 d = int(range_str)
85 return d, d # singleton range
86 except ValueError:
87 m = RANGE_RE.match(range_str)
88 if not m:
89 raise RuntimeError('Invalid range %r' % range_str)
90 b, e = m.groups()
91 return int(b), int(e)
92
93
94class RangePredicate(object):
95 """Zero-based indexing, inclusive ranges."""
96
97 def __init__(self, begin, end):
98 self.begin = begin
99 self.end = end
100
101 def __call__(self, i, case):
102 return self.begin <= i <= self.end
103
104
105class RegexPredicate(object):
106 """Filter by name."""
107
108 def __init__(self, desc_re):
109 self.desc_re = desc_re
110
111 def __call__(self, i, case):
112 return bool(self.desc_re.search(case['desc']))
113
114
115def DefineCommon(p):
116 """Flags shared between sh_spec.py and stateful/harness.py."""
117 p.add_option('-v',
118 '--verbose',
119 dest='verbose',
120 action='store_true',
121 default=False,
122 help='Show details about test failures')
123 p.add_option(
124 '-r',
125 '--range',
126 dest='range',
127 default=None,
128 help='Execute only a given test range, e.g. 5-10, 5-, -10, or 5')
129 p.add_option(
130 '--regex',
131 dest='regex',
132 default=None,
133 help='Execute only tests whose description matches a given regex '
134 '(case-insensitive)')
135 p.add_option('--list',
136 dest='do_list',
137 action='store_true',
138 default=None,
139 help='Just list tests')
140 p.add_option('--oils-failures-allowed',
141 dest='oils_failures_allowed',
142 type='int',
143 default=0,
144 help="Allow this number of Oils failures")
145
146 # Select what shells to run
147 p.add_option('--oils-bin-dir',
148 dest='oils_bin_dir',
149 default=None,
150 help="Directory that osh and ysh live in")
151 p.add_option('--oils-cpp-bin-dir',
152 dest='oils_cpp_bin_dir',
153 default=None,
154 help="Directory that native C++ osh and ysh live in")
155 p.add_option('--ovm-bin-dir',
156 dest='ovm_bin_dir',
157 default=None,
158 help="Directory of the legacy OVM/CPython build")
159 p.add_option(
160 '--compare-shells',
161 dest='compare_shells',
162 action='store_true',
163 help="Compare against shells specified at the top of each file")
164
165
166def DefineStateful(p):
167 p.add_option('--num-retries',
168 dest='num_retries',
169 type='int',
170 default=4,
171 help='Number of retries (for spec/stateful only)')
172 p.add_option('--pexpect-timeout',
173 dest='pexpect_timeout',
174 type='float',
175 default=1.0,
176 help='In seconds')
177 p.add_option(
178 '--results-file',
179 dest='results_file',
180 default=None,
181 help='Write table of results to this file. Default is stdout.')
182 # 24x80 (lines X columns) is the pexpect/ptyprocess default
183 p.add_option('--num-lines',
184 dest='num_lines',
185 type='int',
186 default=24,
187 help='Number of lines to emulate in terminal')
188 p.add_option('--num-columns',
189 dest='num_columns',
190 type='int',
191 default=80,
192 help='Number of columns to emulate in terminal')
193
194
195def DefineShSpec(p):
196 p.add_option('-d',
197 '--details',
198 dest='details',
199 action='store_true',
200 default=False,
201 help='Show details even for successful cases (requires -v)')
202 p.add_option('-t',
203 '--trace',
204 dest='trace',
205 action='store_true',
206 default=False,
207 help='trace execution of shells to diagnose hangs')
208
209 # Execution modes
210 p.add_option('-p',
211 '--print',
212 dest='do_print',
213 action='store_true',
214 default=None,
215 help="Print test code, but don't run it")
216 p.add_option('--print-spec-suite',
217 dest='print_spec_suite',
218 action='store_true',
219 default=None,
220 help="Print suite this file belongs to")
221 p.add_option('--print-table',
222 dest='print_table',
223 action='store_true',
224 default=None,
225 help="Print table of test files")
226 p.add_option('--print-tagged',
227 dest='print_tagged',
228 help="Print spec files tagged with a certain string")
229
230 # Output control
231 p.add_option('--format',
232 dest='format',
233 choices=['ansi', 'html'],
234 default='ansi',
235 help="Output format (default 'ansi')")
236 p.add_option('--stats-file',
237 dest='stats_file',
238 default=None,
239 help="File to write stats to")
240 p.add_option('--tsv-output',
241 dest='tsv_output',
242 default=None,
243 help="Write a TSV log to this file. Subsumes --stats-file.")
244 p.add_option('--stats-template',
245 dest='stats_template',
246 default='',
247 help="Python format string for stats")
248
249 p.add_option('--path-env',
250 dest='path_env',
251 default='',
252 help="The full PATH, for finding binaries used in tests.")
253 p.add_option('--tmp-env',
254 dest='tmp_env',
255 default='',
256 help="A temporary directory that the tests can use.")
257
258 # Notes:
259 # - utf-8 is the Ubuntu default
260 # - this flag has limited usefulness. It may be better to simply export LANG=
261 # in this test case itself.
262 if 0:
263 p.add_option(
264 '--lang-env',
265 dest='lang_env',
266 default='en_US.UTF-8',
267 help="The LANG= setting, which affects various libc functions.")
268 p.add_option('--env-pair',
269 dest='env_pair',
270 default=[],
271 action='append',
272 help='A key=value pair to add to the environment')
273
274 p.add_option('--timeout',
275 dest='timeout',
276 default='',
277 help="Prefix shell invocation with 'timeout N'")
278 p.add_option('--timeout-bin',
279 dest='timeout_bin',
280 default=None,
281 help="Use the smoosh timeout binary at this location.")
282
283 p.add_option('--posix',
284 dest='posix',
285 default=False,
286 action='store_true',
287 help='Pass -o posix to the shell (when applicable)')
288
289 p.add_option('--sh-env-var-name',
290 dest='sh_env_var_name',
291 default='SH',
292 help="Set this environment variable to the path of the shell")
293
294 p.add_option('--pyann-out-dir',
295 dest='pyann_out_dir',
296 default=None,
297 help='Run OSH with PYANN_OUT=$dir/$case_num.json')
298
299 p.add_option('--test-case-json',
300 dest='test_case_json',
301 default=False,
302 action='store_true',
303 help='Export Test Files as JSON')
304
305 p.add_option('--yahtzee-out-file',
306 dest='yahtzee_out_file',
307 default='',
308 help='Report on disagreement')