I forgot to remove a line of code that I was using to test the -f option with multipr...
[shedskin:mainline.git] / tests / run.py
1 #!/usr/bin/env python
2 import traceback
3 import sys
4 import os
5 import time
6 import subprocess
7 import glob
8 import os.path
9 import functools
10 import itertools
11 from difflib import unified_diff
12 from multiprocessing import Pool
13 from multiprocessing.pool import IMapIterator
14
15
16 #Fix for multiprocessing. Without this, Ctrl+C will not kill the process immediately
17 set_timeout_decorator = lambda func: lambda self: func(self, timeout=1e100)
18 IMapIterator.next = set_timeout_decorator(IMapIterator.next)
19
20
21 if os.path.exists('../shedskin/__init__.py'):
22     SS = '../shedskin/__init__.py'
23 else:
24     SS = '../../shedskin/__init__.py'
25
26
27 def usage():
28     print "'-l': give individual test numbers"
29     print "'-r': reverse test order"
30     print "'-f': break after first failure"
31     print "'-e': run extension module tests"
32     print "'-n': normal tests as extension modules"
33     print "'-x': run error/warning message tests"
34     print "'-p': run the tests in parallel"
35     sys.exit()
36
37
38 def parse_options():
39     args, options = [], set()
40     for arg in sys.argv[1:]:
41         if arg.startswith('-'):
42             options.update(arg[1:])
43         else:
44             args.append(int(arg))
45     return args, options
46
47
48 def test_numbers(args, options):
49     if 'l' in options:
50         tests = args
51
52     elif len(args) == 1:
53         tests = [args[0]]
54     elif len(args) == 2:
55         if args[0] > args[1]:
56             args[0], args[1] = args[1], args[0]
57             options.add('r')
58         tests = range(args[0], args[1])
59     else:
60         tests = sorted([int(os.path.splitext(f)[0]) for f in glob.glob('*.py') if f != 'run.py'])
61
62     if 'r' in options:
63         tests.reverse()
64
65     return tests
66
67
68 def tests(args, options):
69     parallel = 'p' in options
70     msvc = 'v' in options
71     partial_run_test = functools.partial(run_test, msvc=msvc, options=options)
72     tests = [test for test in test_numbers(args, options)
73              if os.path.exists('%d.py' % test)]
74     failures = []
75     imap = Pool().imap if parallel else itertools.imap
76
77     for result in imap(partial_run_test, tests):
78         if result is not None:
79             failures.append(result)
80             if 'f' in options:
81                 break
82
83     return failures
84
85
86 def main():
87     args, options = parse_options()
88     if 'h' in options:
89         usage()
90
91     if 'e' in options:
92         failures = extmod_tests(args, options)
93     elif 'x'in options:
94         failures = error_tests(args, options)
95     else:
96         failures = tests(args, options)
97
98     if not failures:
99         print '*** no failures, yay!'
100     else:
101         print '*** tests failed:', len(failures)
102         print failures
103
104
105 def run_test(test, msvc, options):
106     parallel = 'p' in options
107     show_output = not parallel
108
109     print '*** test:', test
110     with open(os.devnull, "w") as fnull:
111         if show_output:
112             fnull = None
113         execute = functools.partial(subprocess.call, stdout=fnull, stderr=fnull, shell=True)
114         t0 = time.time()
115         try:
116             if msvc:
117                 assert execute('python %s -v %d' % (SS, test)) == 0
118             elif 'n' in options:
119                 assert execute('python %s -e -m Makefile.%d %d' % (SS, test, test)) == 0
120             else:
121                 assert execute('python %s -m Makefile.%d %d' % (SS, test, test)) == 0
122             if msvc:
123                 assert execute('nmake /C /S clean') == 0
124                 assert execute('nmake /C /S') == 0
125                 command = '.\\%d' % test
126             else:
127                 assert execute('make clean -f Makefile.%d' % test) == 0
128                 assert execute('make -f Makefile.%d' % test) == 0
129                 if sys.platform == 'win32':
130                     command = '%d' % test
131                 else:
132                     command = './%d' % test
133             if 'n' in options:
134                 if test not in [136, 154, 163, 191, 196, 197, 198]:  # sys.exit
135                     assert execute('python -c "__import__(str(%d))"' % test) == 0
136             else:
137                 check_output(command, 'python %d.py' % test)
138             print '*** success: %d (%.2f)' % (test, time.time() - t0)
139         except AssertionError:
140             print '*** failure:', test
141             traceback.print_exc()
142             return test
143
144
145 def extmod_tests(args, options):
146     failures = []
147     tests = sorted([int(t[1:]) for t in glob.glob('e*') if t[1:].isdigit()])
148     for test in tests:
149         print '*** test:', test
150         os.chdir('e%d' % test)
151         try:
152             extmod = file('main.py').next()[1:].strip()
153             assert os.system('python ../%s -e %s' % (SS, extmod)) == 0
154             assert os.system('make') == 0
155             native_output = get_output('python main.py')
156             if sys.platform == 'win32':
157                 ext = '.pyd'
158             else:
159                 ext = '.so'
160             assert os.system('rm %s' % (extmod + ext)) == 0
161             cpython_output = get_output('python main.py')
162             if native_output != cpython_output:
163                 print 'diff:'
164                 print generate_diff(native_output, cpython_output)
165                 raise AssertionError
166             print '*** success:', test
167         except AssertionError:
168             print '*** failure:', test
169             failures.append(test)
170         os.chdir('..')
171     return failures
172
173
174 def error_tests(args, options):
175     failures = []
176     os.chdir('errs')
177     tests = sorted([int(t[:-3]) for t in glob.glob('*.py') if t[:-3].isdigit()])
178     for test in tests:
179         print '*** test:', test
180         try:
181             checks = []
182             for line in file('%d.py' % test):
183                 if line.startswith('#*'):
184                     checks.append(line[1:].strip())
185             output = get_output('python ../%s %d 2>&1' % (SS, test))
186             assert not [l for l in output if 'Traceback' in l]
187             for check in checks:
188                 assert [l for l in output if l.startswith(check)]
189                 print check
190             print '*** success:', test
191         except AssertionError:
192             print '*** failure:', test
193             failures.append(test)
194     os.chdir('..')
195     return failures
196
197
198 def get_output(command):
199     com = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE).stdout
200     output = com.readlines()
201     com.close()
202     return output
203
204
205 def check_output(command, command2):
206     native_output = get_output(command)
207     cpython_output = get_output(command2)
208     if native_output != cpython_output:
209         print 'diff:'
210         print generate_diff(native_output, cpython_output)
211         raise AssertionError
212
213
214 def generate_diff(native_output, cpython_output):
215     return ''.join(unified_diff(native_output, cpython_output, lineterm="\n"))
216
217 if __name__ == '__main__':
218     main()