Skip to content

Commit 22a8dbd

Browse files
committed
NF - tool for bisecting nose tests failures
Can also look for specific output in the error log, such as segmentation faults.
1 parent f6d1472 commit 22a8dbd

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

tools/bisect_nose.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env python
2+
""" Utility for git-bisecting nose failures
3+
"""
4+
DESCRIP = 'Check nose output for given text, set sys exit for git bisect'
5+
EPILOG = \
6+
"""
7+
Imagine you've just detected a nose test failure. The failure is in a
8+
particular test or test module - here 'test_analyze.py'. The failure *is* in
9+
git branch ``main-master`` but it *is not* in tag ``v1.6.1``. Then you can
10+
bisect with something like::
11+
12+
git co main-master
13+
git bisect start HEAD v1.6.1 --
14+
git bisect run /path/to/bisect_nose.py nibabel/tests/test_analyze.py:TestAnalyzeImage.test_str
15+
16+
You might well want to test that::
17+
18+
nosetests nibabel/tests/test_analyze.py:TestAnalyzeImage.test_str
19+
20+
works as you expect first.
21+
22+
Let's say instead that you prefer to recognize the failure with an output
23+
string. Maybe this is because there are lots of errors but you are only
24+
interested in one of them, or because you are looking for a Segmentation fault
25+
instead of a test failure. Then::
26+
27+
git co main-master
28+
git bisect start HEAD v1.6.1 --
29+
git bisect run /path/to/bisect_nose.py --error-txt='HeaderDataError: data dtype "int64" not recognized' nibabel/tests/test_analyze.py
30+
31+
where ``error-txt`` is in fact a regular expression.
32+
33+
You will need 'argparse' installed somewhere. This is in the system libraries
34+
for python 2.7 and python 3.2 onwards.
35+
36+
We run the tests in a temporary directory, so the code you are testing must be
37+
on the python path.
38+
"""
39+
import os
40+
import sys
41+
import shutil
42+
import tempfile
43+
import re
44+
from functools import partial
45+
from subprocess import check_call, Popen, PIPE, CalledProcessError
46+
47+
from argparse import ArgumentParser, RawDescriptionHelpFormatter
48+
49+
caller = partial(check_call, shell=True)
50+
popener = partial(Popen, stdout=PIPE, stderr=PIPE, shell=True)
51+
52+
# git bisect exit codes
53+
UNTESTABLE = 125
54+
GOOD = 0
55+
BAD = 1
56+
57+
def call_or_untestable(cmd):
58+
try:
59+
caller(cmd)
60+
except CalledProcessError:
61+
sys.exit(UNTESTABLE)
62+
63+
64+
def main():
65+
parser = ArgumentParser(description=DESCRIP,
66+
epilog=EPILOG,
67+
formatter_class=RawDescriptionHelpFormatter)
68+
parser.add_argument('test_path', type=str,
69+
help='Path to test')
70+
parser.add_argument('--error-txt', type=str,
71+
help='regular expression for error of interest')
72+
parser.add_argument('--clean', action='store_true',
73+
help='Clean git tree before running tests')
74+
parser.add_argument('--build', action='store_true',
75+
help='Build git tree before running tests')
76+
# parse the command line
77+
args = parser.parse_args()
78+
path = os.path.abspath(args.test_path)
79+
if args.clean:
80+
print "Cleaning"
81+
call_or_untestable('git clean -fxd')
82+
if args.build:
83+
print "Building"
84+
call_or_untestable('python setup.py build_ext -i')
85+
cwd = os.getcwd()
86+
tmpdir = tempfile.mkdtemp()
87+
try:
88+
os.chdir(tmpdir)
89+
print "Testing"
90+
proc = popener('nosetests ' + path)
91+
stdout, stderr = proc.communicate()
92+
finally:
93+
os.chdir(cwd)
94+
shutil.rmtree(tmpdir)
95+
if args.error_txt:
96+
regex = re.compile(args.error_txt)
97+
if regex.search(stderr):
98+
sys.exit(BAD)
99+
sys.exit(GOOD)
100+
sys.exit(proc.returncode)
101+
102+
103+
if __name__ == '__main__':
104+
main()

0 commit comments

Comments
 (0)