OILS / test / spec_lib.py View on Github | oilshell.org

232 lines, 168 significant
1"""
2spec_lib.py
3
4Shared between sh_spec.py (Python 2) and spec/stateful/harness.py (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(r'_bin/\w+-\w+(-sh)?/osh') # e.g. $PWD/_bin/cxx-dbg/osh
24YSH_CPP_RE = re.compile(r'_bin/\w+-\w+(-sh)?/ysh') # e.g. $PWD/_bin/cxx-dbg/ysh
25OIL_CPP_RE = re.compile(r'_bin/\w+-\w+(-sh)?/oil')
26
27# e.g. bash-4.4 bash 5.2.21
28BASH_RE = re.compile(r'(bash-\d)[\d.]+$')
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 m = BASH_RE.match(path)
39 if m:
40 label = m.group(1) # bash-4 or to fit
41 else:
42 first, _ = os.path.splitext(path)
43 label = os.path.basename(first)
44
45 if label == 'osh':
46 # change the second 'osh' to 'osh_ALT' so it's distinct
47 if saw_osh:
48 if OSH_CPP_RE.search(path):
49 label = 'osh-cpp'
50 else:
51 label = 'osh_ALT'
52 saw_osh = True
53
54 elif label == 'ysh':
55 if saw_ysh:
56 if YSH_CPP_RE.search(path):
57 label = 'ysh-cpp'
58 else:
59 label = 'ysh_ALT'
60
61 saw_ysh = True
62
63 elif label == 'oil': # TODO: remove this
64 if saw_oil:
65 if OIL_CPP_RE.search(path):
66 label = 'oil-cpp'
67 else:
68 label = 'oil_ALT'
69
70 saw_oil = True
71
72 shell_pairs.append((label, path))
73 return shell_pairs
74
75
76RANGE_RE = re.compile('(\d+) \s* - \s* (\d+)', re.VERBOSE)
77
78
79def ParseRange(range_str):
80 try:
81 d = int(range_str)
82 return d, d # singleton range
83 except ValueError:
84 m = RANGE_RE.match(range_str)
85 if not m:
86 raise RuntimeError('Invalid range %r' % range_str)
87 b, e = m.groups()
88 return int(b), int(e)
89
90
91class RangePredicate(object):
92 """Zero-based indexing, inclusive ranges."""
93
94 def __init__(self, begin, end):
95 self.begin = begin
96 self.end = end
97
98 def __call__(self, i, case):
99 return self.begin <= i <= self.end
100
101
102class RegexPredicate(object):
103 """Filter by name."""
104
105 def __init__(self, desc_re):
106 self.desc_re = desc_re
107
108 def __call__(self, i, case):
109 return bool(self.desc_re.search(case['desc']))
110
111
112
113def DefineCommon(p):
114 """Flags shared between sh_spec.py and stateful/harness.py."""
115 p.add_option(
116 '-v', '--verbose', dest='verbose', action='store_true', default=False,
117 help='Show details about test failures')
118 p.add_option(
119 '-r', '--range', dest='range', default=None,
120 help='Execute only a given test range, e.g. 5-10, 5-, -10, or 5')
121 p.add_option(
122 '--regex', dest='regex', default=None,
123 help='Execute only tests whose description matches a given regex '
124 '(case-insensitive)')
125 p.add_option(
126 '--list', dest='do_list', action='store_true', default=None,
127 help='Just list tests')
128 p.add_option(
129 '--oils-failures-allowed', dest='oils_failures_allowed', type='int',
130 default=0, help="Allow this number of Oils failures")
131
132 # Select what shells to run
133 p.add_option(
134 '--oils-bin-dir', dest='oils_bin_dir', default=None,
135 help="Directory that osh and ysh live in")
136 p.add_option(
137 '--oils-cpp-bin-dir', dest='oils_cpp_bin_dir', default=None,
138 help="Directory that native C++ osh and ysh live in")
139 p.add_option(
140 '--ovm-bin-dir', dest='ovm_bin_dir', default=None,
141 help="Directory of the legacy OVM/CPython build")
142 p.add_option(
143 '--compare-shells', dest='compare_shells', action='store_true',
144 help="Compare against shells specified at the top of each file")
145
146
147def DefineStateful(p):
148 p.add_option(
149 '--num-retries', dest='num_retries',
150 type='int', default=4,
151 help='Number of retries (for spec/stateful only)')
152 p.add_option(
153 '--pexpect-timeout', dest='pexpect_timeout',
154 type='float', default=1.0,
155 help='In seconds')
156 p.add_option(
157 '--results-file', dest='results_file', default=None,
158 help='Write table of results to this file. Default is stdout.')
159
160
161def DefineShSpec(p):
162 p.add_option(
163 '-d', '--details', dest='details', action='store_true', default=False,
164 help='Show details even for successful cases (requires -v)')
165 p.add_option(
166 '-t', '--trace', dest='trace', action='store_true', default=False,
167 help='trace execution of shells to diagnose hangs')
168
169 # Execution modes
170 p.add_option(
171 '-p', '--print', dest='do_print', action='store_true', default=None,
172 help="Print test code, but don't run it")
173 p.add_option(
174 '--print-spec-suite', dest='print_spec_suite', action='store_true', default=None,
175 help="Print suite this file belongs to")
176 p.add_option(
177 '--print-table', dest='print_table', action='store_true', default=None,
178 help="Print table of test files")
179 p.add_option(
180 '--print-tagged', dest='print_tagged',
181 help="Print spec files tagged with a certain string")
182
183 # Output control
184 p.add_option(
185 '--format', dest='format', choices=['ansi', 'html'],
186 default='ansi', help="Output format (default 'ansi')")
187 p.add_option(
188 '--stats-file', dest='stats_file', default=None,
189 help="File to write stats to")
190 p.add_option(
191 '--tsv-output', dest='tsv_output', default=None,
192 help="Write a TSV log to this file. Subsumes --stats-file.")
193 p.add_option(
194 '--stats-template', dest='stats_template', default='',
195 help="Python format string for stats")
196
197 p.add_option(
198 '--path-env', dest='path_env', default='',
199 help="The full PATH, for finding binaries used in tests.")
200 p.add_option(
201 '--tmp-env', dest='tmp_env', default='',
202 help="A temporary directory that the tests can use.")
203
204 # Notes:
205 # - utf-8 is the Ubuntu default
206 # - this flag has limited usefulness. It may be better to simply export LANG=
207 # in this test case itself.
208 p.add_option(
209 '--lang-env', dest='lang_env', default='en_US.UTF-8',
210 help="The LANG= setting, which affects various libc functions.")
211 p.add_option(
212 '--env-pair', dest='env_pair', default=[], action='append',
213 help='A key=value pair to add to the environment')
214
215 p.add_option(
216 '--timeout', dest='timeout', default='',
217 help="Prefix shell invocation with 'timeout N'")
218 p.add_option(
219 '--timeout-bin', dest='timeout_bin', default=None,
220 help="Use the smoosh timeout binary at this location.")
221
222 p.add_option(
223 '--posix', dest='posix', default=False, action='store_true',
224 help='Pass -o posix to the shell (when applicable)')
225
226 p.add_option(
227 '--sh-env-var-name', dest='sh_env_var_name', default='SH',
228 help="Set this environment variable to the path of the shell")
229
230 p.add_option(
231 '--pyann-out-dir', dest='pyann_out_dir', default=None,
232 help='Run OSH with PYANN_OUT=$dir/$case_num.json')