Ticket #20461: paralleltests.py

File paralleltests.py, 3.1 KB (added by senko, 11 years ago)

Run django tests in parallel

Line 
1#!/usr/bin/env python
2#
3# Run Django test suite in parallel
4#
5# Run it as: ./paralleltests.py --testdir=/path/to/django/tests --runners=<N> --settings=test_sqlite [test_labels ...]
6#
7# The optimal number of runners is probably the number of cores * 2
8# If 'testdir' optional argument is not given, it's assumed that it is the
9# current working directory.
10
11
12from subprocess import Popen
13import os
14import sys
15import time
16
17runtests = None
18
19MISSING_APPS = [
20 'django.contrib.formtools',
21 'django.contrib.webdesign',
22 'django.contrib.sitemaps',
23 'django.contrib.gis'
24]
25
26
27def parse_options(argv):
28 global runtests
29
30 opts = []
31 labels = []
32 n_runners = 1
33 testdir = '.'
34 for arg in argv:
35 if arg.startswith('--testdir='):
36 testdir = arg.split('=')[1]
37 elif arg.startswith('--runners='):
38 n_runners = int(arg.split('=')[1])
39 elif arg.startswith('-'):
40 opts.append(arg)
41 else:
42 labels.append(arg)
43
44 testdir = os.path.abspath(testdir)
45 if not os.path.exists(os.path.join(testdir, 'runtests.py')):
46 raise ValueError("Directory '%s' doesn't seem to be django tests directory" % testdir)
47
48 sys.path.insert(0, testdir)
49 os.chdir(testdir)
50
51 import runtests as rt
52 runtests = rt
53
54 return n_runners, labels, opts
55
56
57def discover_tests():
58 test_labels = set([d for d in os.listdir(os.getcwd()) if
59 os.path.isdir(d) and d != '__pycache__' and
60 d not in runtests.SUBDIRS_TO_SKIP])
61 return list(test_labels | set(MISSING_APPS))
62
63
64def split_labels(n_runners, labels):
65 for t in runtests.ALWAYS_INSTALLED_APPS:
66 labels.append(t)
67
68 groups = []
69 for i in range(n_runners):
70 groups.append([])
71 for i, l in enumerate(labels):
72 groups[i % n_runners].append(l)
73
74 for g in groups:
75 if 'model_inheritance_same_model_name' in g:
76 g.remove('model_inheritance_same_model_name')
77 if 'model_inheritance' in g:
78 g.append('model_inheritance_same_model_name')
79
80 return groups
81
82
83def run_tests(groups, extra_opts):
84 runners = []
85 for g in groups:
86 if len(g) == 0:
87 continue
88 cmd = ['./runtests.py'] + extra_opts + g
89 runners.append(Popen(cmd))
90 time.sleep(0.1)
91 return runners
92
93
94def wait_for_tests(runners):
95 success = True
96
97 while len(runners) > 0:
98 time.sleep(0.1)
99 for r in runners:
100 exitcode = r.poll()
101 if exitcode is not None:
102 runners.remove(r)
103 success = success and (exitcode == 0)
104 break
105 return success
106
107
108def terminate_tests(runners):
109 for r in runners:
110 r.kill()
111
112
113def run(n_runners, labels, extra_opts):
114 if not labels:
115 labels = discover_tests()
116 groups = split_labels(n_runners, labels)
117
118 runners = run_tests(groups, extra_opts)
119 try:
120 success = wait_for_tests(runners)
121 except KeyboardInterrupt:
122 terminate_tests(runners)
123 success = False
124 return success
125
126
127if __name__ == '__main__':
128 n_runners, labels, opts = parse_options(sys.argv[1:])
129 success = run(n_runners, labels, opts)
130 sys.exit(0 if success else -1)
Back to Top