##// END OF EJS Templates
run-tests: split tests/blacklist in tests/blacklists/*...
Nicolas Dumazet -
r10300:c437745f default
parent child Browse files
Show More
@@ -0,0 +1,14
1 Put here definitions of blacklists for run-tests.py
2
3 Create a file per blacklist. Each file should list the names of tests that you
4 want to be skipped.
5 File names are meant to be used as targets for run-tests.py --blacklist
6 option.
7 Lines starting with # are ignored. White spaces are stripped.
8
9 e.g. if you create a blacklist/example file containing:
10 test-hgrc
11 # some comment
12 test-help
13 then calling "run-tests.py --blacklist blacklists/example" will exclude
14 test-hgrc and test-help from the list of tests to run.
@@ -1,37 +1,21
1 # ConfigParser format
2 # Definitions of blacklists for run-tests.py
3 #
4 # Identify in config sections a list of tests you want to be skipped.
5 # Section names are meant to be used as targets for run-tests.py --blacklist
6 # option.
7 # "test-" prefixes should be omitted from test names. Values are not used.
8 #
9 # e.g. if your file looks like:
10 ## [example]
11 ## hgrc =
12 ## help = "this string is not used"
13 # then calling "run-tests.py --blacklist example" will exclude test-hgrc and
14 # test-help from the list of tests to run.
15
16 [inotify-failures]
17 # When --inotify is activated, help output and config changes:
1 # When --inotify is activated, help output and config changes:
18 debugcomplete =
2 test-debugcomplete
19 empty =
3 test-empty
20 fncache =
4 test-fncache
21 globalopts =
5 test-globalopts
22 help =
6 test-help
23 hgrc =
7 test-hgrc
24 inherit-mode =
8 test-inherit-mode
25 qrecord =
9 test-qrecord
26 strict =
10 test-strict
27
11
28 # --inotify activates de facto the inotify extension. It does not play well
12 # --inotify activates de facto the inotify extension. It does not play well
29 # with inotify-specific tests, which activate/desactivate inotify at will:
13 # with inotify-specific tests, which activate/desactivate inotify at will:
30 inotify =
14 test-inotify
31 inotify-debuginotify =
15 test-inotify-debuginotify
32 inotify-dirty-dirstate =
16 test-inotify-dirty-dirstate
33 inotify-issue1208 =
17 test-inotify-issue1208
34 inotify-issue1371 =
18 test-inotify-issue1371
35 inotify-issue1542 =
19 test-inotify-issue1542
36 inotify-issue1556 =
20 test-inotify-issue1556
37 inotify-lookup =
21 test-inotify-lookup
@@ -1,922 +1,930
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # run-tests.py - Run a set of tests on Mercurial
3 # run-tests.py - Run a set of tests on Mercurial
4 #
4 #
5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 # Modifying this script is tricky because it has many modes:
10 # Modifying this script is tricky because it has many modes:
11 # - serial (default) vs parallel (-jN, N > 1)
11 # - serial (default) vs parallel (-jN, N > 1)
12 # - no coverage (default) vs coverage (-c, -C, -s)
12 # - no coverage (default) vs coverage (-c, -C, -s)
13 # - temp install (default) vs specific hg script (--with-hg, --local)
13 # - temp install (default) vs specific hg script (--with-hg, --local)
14 # - tests are a mix of shell scripts and Python scripts
14 # - tests are a mix of shell scripts and Python scripts
15 #
15 #
16 # If you change this script, it is recommended that you ensure you
16 # If you change this script, it is recommended that you ensure you
17 # haven't broken it by running it in various modes with a representative
17 # haven't broken it by running it in various modes with a representative
18 # sample of test scripts. For example:
18 # sample of test scripts. For example:
19 #
19 #
20 # 1) serial, no coverage, temp install:
20 # 1) serial, no coverage, temp install:
21 # ./run-tests.py test-s*
21 # ./run-tests.py test-s*
22 # 2) serial, no coverage, local hg:
22 # 2) serial, no coverage, local hg:
23 # ./run-tests.py --local test-s*
23 # ./run-tests.py --local test-s*
24 # 3) serial, coverage, temp install:
24 # 3) serial, coverage, temp install:
25 # ./run-tests.py -c test-s*
25 # ./run-tests.py -c test-s*
26 # 4) serial, coverage, local hg:
26 # 4) serial, coverage, local hg:
27 # ./run-tests.py -c --local test-s* # unsupported
27 # ./run-tests.py -c --local test-s* # unsupported
28 # 5) parallel, no coverage, temp install:
28 # 5) parallel, no coverage, temp install:
29 # ./run-tests.py -j2 test-s*
29 # ./run-tests.py -j2 test-s*
30 # 6) parallel, no coverage, local hg:
30 # 6) parallel, no coverage, local hg:
31 # ./run-tests.py -j2 --local test-s*
31 # ./run-tests.py -j2 --local test-s*
32 # 7) parallel, coverage, temp install:
32 # 7) parallel, coverage, temp install:
33 # ./run-tests.py -j2 -c test-s* # currently broken
33 # ./run-tests.py -j2 -c test-s* # currently broken
34 # 8) parallel, coverage, local install:
34 # 8) parallel, coverage, local install:
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
36 # 9) parallel, custom tmp dir:
36 # 9) parallel, custom tmp dir:
37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
38 #
38 #
39 # (You could use any subset of the tests: test-s* happens to match
39 # (You could use any subset of the tests: test-s* happens to match
40 # enough that it's worth doing parallel runs, few enough that it
40 # enough that it's worth doing parallel runs, few enough that it
41 # completes fairly quickly, includes both shell and Python scripts, and
41 # completes fairly quickly, includes both shell and Python scripts, and
42 # includes some scripts that run daemon processes.)
42 # includes some scripts that run daemon processes.)
43
43
44 from ConfigParser import ConfigParser
45 import difflib
44 import difflib
46 import errno
45 import errno
47 import optparse
46 import optparse
48 import os
47 import os
49 import subprocess
48 import subprocess
50 import shutil
49 import shutil
51 import signal
50 import signal
52 import sys
51 import sys
53 import tempfile
52 import tempfile
54 import time
53 import time
55
54
56 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
57 def Popen4(cmd, bufsize=-1):
56 def Popen4(cmd, bufsize=-1):
58 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
59 close_fds=closefds,
58 close_fds=closefds,
60 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stderr=subprocess.STDOUT)
60 stderr=subprocess.STDOUT)
62 p.fromchild = p.stdout
61 p.fromchild = p.stdout
63 p.tochild = p.stdin
62 p.tochild = p.stdin
64 p.childerr = p.stderr
63 p.childerr = p.stderr
65 return p
64 return p
66
65
67 # reserved exit code to skip test (used by hghave)
66 # reserved exit code to skip test (used by hghave)
68 SKIPPED_STATUS = 80
67 SKIPPED_STATUS = 80
69 SKIPPED_PREFIX = 'skipped: '
68 SKIPPED_PREFIX = 'skipped: '
70 FAILED_PREFIX = 'hghave check failed: '
69 FAILED_PREFIX = 'hghave check failed: '
71 PYTHON = sys.executable
70 PYTHON = sys.executable
72
71
73 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
72 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
74
73
75 defaults = {
74 defaults = {
76 'jobs': ('HGTEST_JOBS', 1),
75 'jobs': ('HGTEST_JOBS', 1),
77 'timeout': ('HGTEST_TIMEOUT', 180),
76 'timeout': ('HGTEST_TIMEOUT', 180),
78 'port': ('HGTEST_PORT', 20059),
77 'port': ('HGTEST_PORT', 20059),
79 }
78 }
80
79
81 def parseargs():
80 def parseargs():
82 parser = optparse.OptionParser("%prog [options] [tests]")
81 parser = optparse.OptionParser("%prog [options] [tests]")
83 parser.add_option("-C", "--annotate", action="store_true",
82 parser.add_option("-C", "--annotate", action="store_true",
84 help="output files annotated with coverage")
83 help="output files annotated with coverage")
85 parser.add_option("--child", type="int",
84 parser.add_option("--child", type="int",
86 help="run as child process, summary to given fd")
85 help="run as child process, summary to given fd")
87 parser.add_option("-c", "--cover", action="store_true",
86 parser.add_option("-c", "--cover", action="store_true",
88 help="print a test coverage report")
87 help="print a test coverage report")
89 parser.add_option("-f", "--first", action="store_true",
88 parser.add_option("-f", "--first", action="store_true",
90 help="exit on the first test failure")
89 help="exit on the first test failure")
91 parser.add_option("-i", "--interactive", action="store_true",
90 parser.add_option("-i", "--interactive", action="store_true",
92 help="prompt to accept changed output")
91 help="prompt to accept changed output")
93 parser.add_option("-j", "--jobs", type="int",
92 parser.add_option("-j", "--jobs", type="int",
94 help="number of jobs to run in parallel"
93 help="number of jobs to run in parallel"
95 " (default: $%s or %d)" % defaults['jobs'])
94 " (default: $%s or %d)" % defaults['jobs'])
96 parser.add_option("-k", "--keywords",
95 parser.add_option("-k", "--keywords",
97 help="run tests matching keywords")
96 help="run tests matching keywords")
98 parser.add_option("--keep-tmpdir", action="store_true",
97 parser.add_option("--keep-tmpdir", action="store_true",
99 help="keep temporary directory after running tests")
98 help="keep temporary directory after running tests")
100 parser.add_option("--tmpdir", type="string",
99 parser.add_option("--tmpdir", type="string",
101 help="run tests in the given temporary directory"
100 help="run tests in the given temporary directory"
102 " (implies --keep-tmpdir)")
101 " (implies --keep-tmpdir)")
103 parser.add_option("-d", "--debug", action="store_true",
102 parser.add_option("-d", "--debug", action="store_true",
104 help="debug mode: write output of test scripts to console"
103 help="debug mode: write output of test scripts to console"
105 " rather than capturing and diff'ing it (disables timeout)")
104 " rather than capturing and diff'ing it (disables timeout)")
106 parser.add_option("-R", "--restart", action="store_true",
105 parser.add_option("-R", "--restart", action="store_true",
107 help="restart at last error")
106 help="restart at last error")
108 parser.add_option("-p", "--port", type="int",
107 parser.add_option("-p", "--port", type="int",
109 help="port on which servers should listen"
108 help="port on which servers should listen"
110 " (default: $%s or %d)" % defaults['port'])
109 " (default: $%s or %d)" % defaults['port'])
111 parser.add_option("-r", "--retest", action="store_true",
110 parser.add_option("-r", "--retest", action="store_true",
112 help="retest failed tests")
111 help="retest failed tests")
113 parser.add_option("-s", "--cover_stdlib", action="store_true",
112 parser.add_option("-s", "--cover_stdlib", action="store_true",
114 help="print a test coverage report inc. standard libraries")
113 help="print a test coverage report inc. standard libraries")
115 parser.add_option("-S", "--noskips", action="store_true",
114 parser.add_option("-S", "--noskips", action="store_true",
116 help="don't report skip tests verbosely")
115 help="don't report skip tests verbosely")
117 parser.add_option("-t", "--timeout", type="int",
116 parser.add_option("-t", "--timeout", type="int",
118 help="kill errant tests after TIMEOUT seconds"
117 help="kill errant tests after TIMEOUT seconds"
119 " (default: $%s or %d)" % defaults['timeout'])
118 " (default: $%s or %d)" % defaults['timeout'])
120 parser.add_option("-v", "--verbose", action="store_true",
119 parser.add_option("-v", "--verbose", action="store_true",
121 help="output verbose messages")
120 help="output verbose messages")
122 parser.add_option("-n", "--nodiff", action="store_true",
121 parser.add_option("-n", "--nodiff", action="store_true",
123 help="skip showing test changes")
122 help="skip showing test changes")
124 parser.add_option("--with-hg", type="string",
123 parser.add_option("--with-hg", type="string",
125 metavar="HG",
124 metavar="HG",
126 help="test using specified hg script rather than a "
125 help="test using specified hg script rather than a "
127 "temporary installation")
126 "temporary installation")
128 parser.add_option("--local", action="store_true",
127 parser.add_option("--local", action="store_true",
129 help="shortcut for --with-hg=<testdir>/../hg")
128 help="shortcut for --with-hg=<testdir>/../hg")
130 parser.add_option("--pure", action="store_true",
129 parser.add_option("--pure", action="store_true",
131 help="use pure Python code instead of C extensions")
130 help="use pure Python code instead of C extensions")
132 parser.add_option("-3", "--py3k-warnings", action="store_true",
131 parser.add_option("-3", "--py3k-warnings", action="store_true",
133 help="enable Py3k warnings on Python 2.6+")
132 help="enable Py3k warnings on Python 2.6+")
134 parser.add_option("--inotify", action="store_true",
133 parser.add_option("--inotify", action="store_true",
135 help="enable inotify extension when running tests")
134 help="enable inotify extension when running tests")
136 parser.add_option("--blacklist", action="append",
135 parser.add_option("--blacklist", action="append",
137 help="skip tests listed in the specified section of "
136 help="skip tests listed in the specified blacklist file")
138 "the blacklist file")
139
137
140 for option, default in defaults.items():
138 for option, default in defaults.items():
141 defaults[option] = int(os.environ.get(*default))
139 defaults[option] = int(os.environ.get(*default))
142 parser.set_defaults(**defaults)
140 parser.set_defaults(**defaults)
143 (options, args) = parser.parse_args()
141 (options, args) = parser.parse_args()
144
142
145 if options.with_hg:
143 if options.with_hg:
146 if not (os.path.isfile(options.with_hg) and
144 if not (os.path.isfile(options.with_hg) and
147 os.access(options.with_hg, os.X_OK)):
145 os.access(options.with_hg, os.X_OK)):
148 parser.error('--with-hg must specify an executable hg script')
146 parser.error('--with-hg must specify an executable hg script')
149 if not os.path.basename(options.with_hg) == 'hg':
147 if not os.path.basename(options.with_hg) == 'hg':
150 sys.stderr.write('warning: --with-hg should specify an hg script')
148 sys.stderr.write('warning: --with-hg should specify an hg script')
151 if options.local:
149 if options.local:
152 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
150 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
153 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
151 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
154 if not os.access(hgbin, os.X_OK):
152 if not os.access(hgbin, os.X_OK):
155 parser.error('--local specified, but %r not found or not executable'
153 parser.error('--local specified, but %r not found or not executable'
156 % hgbin)
154 % hgbin)
157 options.with_hg = hgbin
155 options.with_hg = hgbin
158
156
159 options.anycoverage = (options.cover or
157 options.anycoverage = (options.cover or
160 options.cover_stdlib or
158 options.cover_stdlib or
161 options.annotate)
159 options.annotate)
162
160
163 if options.anycoverage and options.with_hg:
161 if options.anycoverage and options.with_hg:
164 # I'm not sure if this is a fundamental limitation or just a
162 # I'm not sure if this is a fundamental limitation or just a
165 # bug. But I don't want to waste people's time and energy doing
163 # bug. But I don't want to waste people's time and energy doing
166 # test runs that don't give the results they want.
164 # test runs that don't give the results they want.
167 parser.error("sorry, coverage options do not work when --with-hg "
165 parser.error("sorry, coverage options do not work when --with-hg "
168 "or --local specified")
166 "or --local specified")
169
167
170 global vlog
168 global vlog
171 if options.verbose:
169 if options.verbose:
172 if options.jobs > 1 or options.child is not None:
170 if options.jobs > 1 or options.child is not None:
173 pid = "[%d]" % os.getpid()
171 pid = "[%d]" % os.getpid()
174 else:
172 else:
175 pid = None
173 pid = None
176 def vlog(*msg):
174 def vlog(*msg):
177 if pid:
175 if pid:
178 print pid,
176 print pid,
179 for m in msg:
177 for m in msg:
180 print m,
178 print m,
181 print
179 print
182 sys.stdout.flush()
180 sys.stdout.flush()
183 else:
181 else:
184 vlog = lambda *msg: None
182 vlog = lambda *msg: None
185
183
186 if options.tmpdir:
184 if options.tmpdir:
187 options.tmpdir = os.path.expanduser(options.tmpdir)
185 options.tmpdir = os.path.expanduser(options.tmpdir)
188
186
189 if options.jobs < 1:
187 if options.jobs < 1:
190 parser.error('--jobs must be positive')
188 parser.error('--jobs must be positive')
191 if options.interactive and options.jobs > 1:
189 if options.interactive and options.jobs > 1:
192 print '(--interactive overrides --jobs)'
190 print '(--interactive overrides --jobs)'
193 options.jobs = 1
191 options.jobs = 1
194 if options.interactive and options.debug:
192 if options.interactive and options.debug:
195 parser.error("-i/--interactive and -d/--debug are incompatible")
193 parser.error("-i/--interactive and -d/--debug are incompatible")
196 if options.debug:
194 if options.debug:
197 if options.timeout != defaults['timeout']:
195 if options.timeout != defaults['timeout']:
198 sys.stderr.write(
196 sys.stderr.write(
199 'warning: --timeout option ignored with --debug\n')
197 'warning: --timeout option ignored with --debug\n')
200 options.timeout = 0
198 options.timeout = 0
201 if options.py3k_warnings:
199 if options.py3k_warnings:
202 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
200 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
203 parser.error('--py3k-warnings can only be used on Python 2.6+')
201 parser.error('--py3k-warnings can only be used on Python 2.6+')
204 if options.blacklist:
202 if options.blacklist:
205 configparser = ConfigParser()
206 configparser.read("blacklist")
207 blacklist = dict()
203 blacklist = dict()
208 for section in options.blacklist:
204 for filename in options.blacklist:
209 for (item, value) in configparser.items(section):
205 try:
210 blacklist["test-" + item] = section
206 path = os.path.expanduser(os.path.expandvars(filename))
207 f = open(path, "r")
208 except IOError, err:
209 if err.errno != errno.ENOENT:
210 raise
211 print "warning: no such blacklist file: %s" % filename
212 continue
213
214 for line in f.readlines():
215 line = line.strip()
216 if line and not line.startswith('#'):
217 blacklist[line] = filename
218
211 options.blacklist = blacklist
219 options.blacklist = blacklist
212
220
213 return (options, args)
221 return (options, args)
214
222
215 def rename(src, dst):
223 def rename(src, dst):
216 """Like os.rename(), trade atomicity and opened files friendliness
224 """Like os.rename(), trade atomicity and opened files friendliness
217 for existing destination support.
225 for existing destination support.
218 """
226 """
219 shutil.copy(src, dst)
227 shutil.copy(src, dst)
220 os.remove(src)
228 os.remove(src)
221
229
222 def splitnewlines(text):
230 def splitnewlines(text):
223 '''like str.splitlines, but only split on newlines.
231 '''like str.splitlines, but only split on newlines.
224 keep line endings.'''
232 keep line endings.'''
225 i = 0
233 i = 0
226 lines = []
234 lines = []
227 while True:
235 while True:
228 n = text.find('\n', i)
236 n = text.find('\n', i)
229 if n == -1:
237 if n == -1:
230 last = text[i:]
238 last = text[i:]
231 if last:
239 if last:
232 lines.append(last)
240 lines.append(last)
233 return lines
241 return lines
234 lines.append(text[i:n + 1])
242 lines.append(text[i:n + 1])
235 i = n + 1
243 i = n + 1
236
244
237 def parsehghaveoutput(lines):
245 def parsehghaveoutput(lines):
238 '''Parse hghave log lines.
246 '''Parse hghave log lines.
239 Return tuple of lists (missing, failed):
247 Return tuple of lists (missing, failed):
240 * the missing/unknown features
248 * the missing/unknown features
241 * the features for which existence check failed'''
249 * the features for which existence check failed'''
242 missing = []
250 missing = []
243 failed = []
251 failed = []
244 for line in lines:
252 for line in lines:
245 if line.startswith(SKIPPED_PREFIX):
253 if line.startswith(SKIPPED_PREFIX):
246 line = line.splitlines()[0]
254 line = line.splitlines()[0]
247 missing.append(line[len(SKIPPED_PREFIX):])
255 missing.append(line[len(SKIPPED_PREFIX):])
248 elif line.startswith(FAILED_PREFIX):
256 elif line.startswith(FAILED_PREFIX):
249 line = line.splitlines()[0]
257 line = line.splitlines()[0]
250 failed.append(line[len(FAILED_PREFIX):])
258 failed.append(line[len(FAILED_PREFIX):])
251
259
252 return missing, failed
260 return missing, failed
253
261
254 def showdiff(expected, output, ref, err):
262 def showdiff(expected, output, ref, err):
255 for line in difflib.unified_diff(expected, output, ref, err):
263 for line in difflib.unified_diff(expected, output, ref, err):
256 sys.stdout.write(line)
264 sys.stdout.write(line)
257
265
258 def findprogram(program):
266 def findprogram(program):
259 """Search PATH for a executable program"""
267 """Search PATH for a executable program"""
260 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
268 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
261 name = os.path.join(p, program)
269 name = os.path.join(p, program)
262 if os.access(name, os.X_OK):
270 if os.access(name, os.X_OK):
263 return name
271 return name
264 return None
272 return None
265
273
266 def checktools():
274 def checktools():
267 # Before we go any further, check for pre-requisite tools
275 # Before we go any further, check for pre-requisite tools
268 # stuff from coreutils (cat, rm, etc) are not tested
276 # stuff from coreutils (cat, rm, etc) are not tested
269 for p in requiredtools:
277 for p in requiredtools:
270 if os.name == 'nt':
278 if os.name == 'nt':
271 p += '.exe'
279 p += '.exe'
272 found = findprogram(p)
280 found = findprogram(p)
273 if found:
281 if found:
274 vlog("# Found prerequisite", p, "at", found)
282 vlog("# Found prerequisite", p, "at", found)
275 else:
283 else:
276 print "WARNING: Did not find prerequisite tool: "+p
284 print "WARNING: Did not find prerequisite tool: "+p
277
285
278 def cleanup(options):
286 def cleanup(options):
279 if not options.keep_tmpdir:
287 if not options.keep_tmpdir:
280 vlog("# Cleaning up HGTMP", HGTMP)
288 vlog("# Cleaning up HGTMP", HGTMP)
281 shutil.rmtree(HGTMP, True)
289 shutil.rmtree(HGTMP, True)
282
290
283 def usecorrectpython():
291 def usecorrectpython():
284 # some tests run python interpreter. they must use same
292 # some tests run python interpreter. they must use same
285 # interpreter we use or bad things will happen.
293 # interpreter we use or bad things will happen.
286 exedir, exename = os.path.split(sys.executable)
294 exedir, exename = os.path.split(sys.executable)
287 if exename == 'python':
295 if exename == 'python':
288 path = findprogram('python')
296 path = findprogram('python')
289 if os.path.dirname(path) == exedir:
297 if os.path.dirname(path) == exedir:
290 return
298 return
291 vlog('# Making python executable in test path use correct Python')
299 vlog('# Making python executable in test path use correct Python')
292 mypython = os.path.join(BINDIR, 'python')
300 mypython = os.path.join(BINDIR, 'python')
293 try:
301 try:
294 os.symlink(sys.executable, mypython)
302 os.symlink(sys.executable, mypython)
295 except AttributeError:
303 except AttributeError:
296 # windows fallback
304 # windows fallback
297 shutil.copyfile(sys.executable, mypython)
305 shutil.copyfile(sys.executable, mypython)
298 shutil.copymode(sys.executable, mypython)
306 shutil.copymode(sys.executable, mypython)
299
307
300 def installhg(options):
308 def installhg(options):
301 vlog("# Performing temporary installation of HG")
309 vlog("# Performing temporary installation of HG")
302 installerrs = os.path.join("tests", "install.err")
310 installerrs = os.path.join("tests", "install.err")
303 pure = options.pure and "--pure" or ""
311 pure = options.pure and "--pure" or ""
304
312
305 # Run installer in hg root
313 # Run installer in hg root
306 script = os.path.realpath(sys.argv[0])
314 script = os.path.realpath(sys.argv[0])
307 hgroot = os.path.dirname(os.path.dirname(script))
315 hgroot = os.path.dirname(os.path.dirname(script))
308 os.chdir(hgroot)
316 os.chdir(hgroot)
309 nohome = '--home=""'
317 nohome = '--home=""'
310 if os.name == 'nt':
318 if os.name == 'nt':
311 # The --home="" trick works only on OS where os.sep == '/'
319 # The --home="" trick works only on OS where os.sep == '/'
312 # because of a distutils convert_path() fast-path. Avoid it at
320 # because of a distutils convert_path() fast-path. Avoid it at
313 # least on Windows for now, deal with .pydistutils.cfg bugs
321 # least on Windows for now, deal with .pydistutils.cfg bugs
314 # when they happen.
322 # when they happen.
315 nohome = ''
323 nohome = ''
316 cmd = ('%s setup.py %s clean --all'
324 cmd = ('%s setup.py %s clean --all'
317 ' install --force --prefix="%s" --install-lib="%s"'
325 ' install --force --prefix="%s" --install-lib="%s"'
318 ' --install-scripts="%s" %s >%s 2>&1'
326 ' --install-scripts="%s" %s >%s 2>&1'
319 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, nohome,
327 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, nohome,
320 installerrs))
328 installerrs))
321 vlog("# Running", cmd)
329 vlog("# Running", cmd)
322 if os.system(cmd) == 0:
330 if os.system(cmd) == 0:
323 if not options.verbose:
331 if not options.verbose:
324 os.remove(installerrs)
332 os.remove(installerrs)
325 else:
333 else:
326 f = open(installerrs)
334 f = open(installerrs)
327 for line in f:
335 for line in f:
328 print line,
336 print line,
329 f.close()
337 f.close()
330 sys.exit(1)
338 sys.exit(1)
331 os.chdir(TESTDIR)
339 os.chdir(TESTDIR)
332
340
333 usecorrectpython()
341 usecorrectpython()
334
342
335 vlog("# Installing dummy diffstat")
343 vlog("# Installing dummy diffstat")
336 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
344 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
337 f.write('#!' + sys.executable + '\n'
345 f.write('#!' + sys.executable + '\n'
338 'import sys\n'
346 'import sys\n'
339 'files = 0\n'
347 'files = 0\n'
340 'for line in sys.stdin:\n'
348 'for line in sys.stdin:\n'
341 ' if line.startswith("diff "):\n'
349 ' if line.startswith("diff "):\n'
342 ' files += 1\n'
350 ' files += 1\n'
343 'sys.stdout.write("files patched: %d\\n" % files)\n')
351 'sys.stdout.write("files patched: %d\\n" % files)\n')
344 f.close()
352 f.close()
345 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
353 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
346
354
347 if options.py3k_warnings and not options.anycoverage:
355 if options.py3k_warnings and not options.anycoverage:
348 vlog("# Updating hg command to enable Py3k Warnings switch")
356 vlog("# Updating hg command to enable Py3k Warnings switch")
349 f = open(os.path.join(BINDIR, 'hg'), 'r')
357 f = open(os.path.join(BINDIR, 'hg'), 'r')
350 lines = [line.rstrip() for line in f]
358 lines = [line.rstrip() for line in f]
351 lines[0] += ' -3'
359 lines[0] += ' -3'
352 f.close()
360 f.close()
353 f = open(os.path.join(BINDIR, 'hg'), 'w')
361 f = open(os.path.join(BINDIR, 'hg'), 'w')
354 for line in lines:
362 for line in lines:
355 f.write(line + '\n')
363 f.write(line + '\n')
356 f.close()
364 f.close()
357
365
358 if options.anycoverage:
366 if options.anycoverage:
359 vlog("# Installing coverage wrapper")
367 vlog("# Installing coverage wrapper")
360 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
368 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
361 if os.path.exists(COVERAGE_FILE):
369 if os.path.exists(COVERAGE_FILE):
362 os.unlink(COVERAGE_FILE)
370 os.unlink(COVERAGE_FILE)
363 # Create a wrapper script to invoke hg via coverage.py
371 # Create a wrapper script to invoke hg via coverage.py
364 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
372 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
365 f = open(os.path.join(BINDIR, 'hg'), 'w')
373 f = open(os.path.join(BINDIR, 'hg'), 'w')
366 f.write('#!' + sys.executable + '\n')
374 f.write('#!' + sys.executable + '\n')
367 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
375 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
368 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
376 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
369 (os.path.join(TESTDIR, 'coverage.py'),
377 (os.path.join(TESTDIR, 'coverage.py'),
370 os.path.join(BINDIR, '_hg.py')))
378 os.path.join(BINDIR, '_hg.py')))
371 f.close()
379 f.close()
372 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
380 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
373
381
374 def outputcoverage(options):
382 def outputcoverage(options):
375
383
376 vlog('# Producing coverage report')
384 vlog('# Producing coverage report')
377 os.chdir(PYTHONDIR)
385 os.chdir(PYTHONDIR)
378
386
379 def covrun(*args):
387 def covrun(*args):
380 start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
388 start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
381 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
389 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
382 vlog('# Running: %s' % cmd)
390 vlog('# Running: %s' % cmd)
383 os.system(cmd)
391 os.system(cmd)
384
392
385 omit = [BINDIR, TESTDIR, PYTHONDIR]
393 omit = [BINDIR, TESTDIR, PYTHONDIR]
386 if not options.cover_stdlib:
394 if not options.cover_stdlib:
387 # Exclude as system paths (ignoring empty strings seen on win)
395 # Exclude as system paths (ignoring empty strings seen on win)
388 omit += [x for x in sys.path if x != '']
396 omit += [x for x in sys.path if x != '']
389 omit = ','.join(omit)
397 omit = ','.join(omit)
390
398
391 covrun('-c') # combine from parallel processes
399 covrun('-c') # combine from parallel processes
392 for fn in os.listdir(TESTDIR):
400 for fn in os.listdir(TESTDIR):
393 if fn.startswith('.coverage.'):
401 if fn.startswith('.coverage.'):
394 os.unlink(os.path.join(TESTDIR, fn))
402 os.unlink(os.path.join(TESTDIR, fn))
395
403
396 covrun('-i', '-r', '"--omit=%s"' % omit) # report
404 covrun('-i', '-r', '"--omit=%s"' % omit) # report
397 if options.annotate:
405 if options.annotate:
398 adir = os.path.join(TESTDIR, 'annotated')
406 adir = os.path.join(TESTDIR, 'annotated')
399 if not os.path.isdir(adir):
407 if not os.path.isdir(adir):
400 os.mkdir(adir)
408 os.mkdir(adir)
401 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
409 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
402
410
403 class Timeout(Exception):
411 class Timeout(Exception):
404 pass
412 pass
405
413
406 def alarmed(signum, frame):
414 def alarmed(signum, frame):
407 raise Timeout
415 raise Timeout
408
416
409 def run(cmd, options):
417 def run(cmd, options):
410 """Run command in a sub-process, capturing the output (stdout and stderr).
418 """Run command in a sub-process, capturing the output (stdout and stderr).
411 Return a tuple (exitcode, output). output is None in debug mode."""
419 Return a tuple (exitcode, output). output is None in debug mode."""
412 # TODO: Use subprocess.Popen if we're running on Python 2.4
420 # TODO: Use subprocess.Popen if we're running on Python 2.4
413 if options.debug:
421 if options.debug:
414 proc = subprocess.Popen(cmd, shell=True)
422 proc = subprocess.Popen(cmd, shell=True)
415 ret = proc.wait()
423 ret = proc.wait()
416 return (ret, None)
424 return (ret, None)
417
425
418 if os.name == 'nt' or sys.platform.startswith('java'):
426 if os.name == 'nt' or sys.platform.startswith('java'):
419 tochild, fromchild = os.popen4(cmd)
427 tochild, fromchild = os.popen4(cmd)
420 tochild.close()
428 tochild.close()
421 output = fromchild.read()
429 output = fromchild.read()
422 ret = fromchild.close()
430 ret = fromchild.close()
423 if ret == None:
431 if ret == None:
424 ret = 0
432 ret = 0
425 else:
433 else:
426 proc = Popen4(cmd)
434 proc = Popen4(cmd)
427 try:
435 try:
428 output = ''
436 output = ''
429 proc.tochild.close()
437 proc.tochild.close()
430 output = proc.fromchild.read()
438 output = proc.fromchild.read()
431 ret = proc.wait()
439 ret = proc.wait()
432 if os.WIFEXITED(ret):
440 if os.WIFEXITED(ret):
433 ret = os.WEXITSTATUS(ret)
441 ret = os.WEXITSTATUS(ret)
434 except Timeout:
442 except Timeout:
435 vlog('# Process %d timed out - killing it' % proc.pid)
443 vlog('# Process %d timed out - killing it' % proc.pid)
436 os.kill(proc.pid, signal.SIGTERM)
444 os.kill(proc.pid, signal.SIGTERM)
437 ret = proc.wait()
445 ret = proc.wait()
438 if ret == 0:
446 if ret == 0:
439 ret = signal.SIGTERM << 8
447 ret = signal.SIGTERM << 8
440 output += ("\n### Abort: timeout after %d seconds.\n"
448 output += ("\n### Abort: timeout after %d seconds.\n"
441 % options.timeout)
449 % options.timeout)
442 return ret, splitnewlines(output)
450 return ret, splitnewlines(output)
443
451
444 def runone(options, test, skips, fails):
452 def runone(options, test, skips, fails):
445 '''tristate output:
453 '''tristate output:
446 None -> skipped
454 None -> skipped
447 True -> passed
455 True -> passed
448 False -> failed'''
456 False -> failed'''
449
457
450 def skip(msg):
458 def skip(msg):
451 if not options.verbose:
459 if not options.verbose:
452 skips.append((test, msg))
460 skips.append((test, msg))
453 else:
461 else:
454 print "\nSkipping %s: %s" % (testpath, msg)
462 print "\nSkipping %s: %s" % (testpath, msg)
455 return None
463 return None
456
464
457 def fail(msg):
465 def fail(msg):
458 fails.append((test, msg))
466 fails.append((test, msg))
459 if not options.nodiff:
467 if not options.nodiff:
460 print "\nERROR: %s %s" % (testpath, msg)
468 print "\nERROR: %s %s" % (testpath, msg)
461 return None
469 return None
462
470
463 vlog("# Test", test)
471 vlog("# Test", test)
464
472
465 # create a fresh hgrc
473 # create a fresh hgrc
466 hgrc = open(HGRCPATH, 'w+')
474 hgrc = open(HGRCPATH, 'w+')
467 hgrc.write('[ui]\n')
475 hgrc.write('[ui]\n')
468 hgrc.write('slash = True\n')
476 hgrc.write('slash = True\n')
469 hgrc.write('[defaults]\n')
477 hgrc.write('[defaults]\n')
470 hgrc.write('backout = -d "0 0"\n')
478 hgrc.write('backout = -d "0 0"\n')
471 hgrc.write('commit = -d "0 0"\n')
479 hgrc.write('commit = -d "0 0"\n')
472 hgrc.write('tag = -d "0 0"\n')
480 hgrc.write('tag = -d "0 0"\n')
473 if options.inotify:
481 if options.inotify:
474 hgrc.write('[extensions]\n')
482 hgrc.write('[extensions]\n')
475 hgrc.write('inotify=\n')
483 hgrc.write('inotify=\n')
476 hgrc.write('[inotify]\n')
484 hgrc.write('[inotify]\n')
477 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
485 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
478 hgrc.write('appendpid=True\n')
486 hgrc.write('appendpid=True\n')
479 hgrc.close()
487 hgrc.close()
480
488
481 err = os.path.join(TESTDIR, test+".err")
489 err = os.path.join(TESTDIR, test+".err")
482 ref = os.path.join(TESTDIR, test+".out")
490 ref = os.path.join(TESTDIR, test+".out")
483 testpath = os.path.join(TESTDIR, test)
491 testpath = os.path.join(TESTDIR, test)
484
492
485 if os.path.exists(err):
493 if os.path.exists(err):
486 os.remove(err) # Remove any previous output files
494 os.remove(err) # Remove any previous output files
487
495
488 # Make a tmp subdirectory to work in
496 # Make a tmp subdirectory to work in
489 tmpd = os.path.join(HGTMP, test)
497 tmpd = os.path.join(HGTMP, test)
490 os.mkdir(tmpd)
498 os.mkdir(tmpd)
491 os.chdir(tmpd)
499 os.chdir(tmpd)
492
500
493 try:
501 try:
494 tf = open(testpath)
502 tf = open(testpath)
495 firstline = tf.readline().rstrip()
503 firstline = tf.readline().rstrip()
496 tf.close()
504 tf.close()
497 except:
505 except:
498 firstline = ''
506 firstline = ''
499 lctest = test.lower()
507 lctest = test.lower()
500
508
501 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
509 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
502 py3kswitch = options.py3k_warnings and ' -3' or ''
510 py3kswitch = options.py3k_warnings and ' -3' or ''
503 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, testpath)
511 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, testpath)
504 elif lctest.endswith('.bat'):
512 elif lctest.endswith('.bat'):
505 # do not run batch scripts on non-windows
513 # do not run batch scripts on non-windows
506 if os.name != 'nt':
514 if os.name != 'nt':
507 return skip("batch script")
515 return skip("batch script")
508 # To reliably get the error code from batch files on WinXP,
516 # To reliably get the error code from batch files on WinXP,
509 # the "cmd /c call" prefix is needed. Grrr
517 # the "cmd /c call" prefix is needed. Grrr
510 cmd = 'cmd /c call "%s"' % testpath
518 cmd = 'cmd /c call "%s"' % testpath
511 else:
519 else:
512 # do not run shell scripts on windows
520 # do not run shell scripts on windows
513 if os.name == 'nt':
521 if os.name == 'nt':
514 return skip("shell script")
522 return skip("shell script")
515 # do not try to run non-executable programs
523 # do not try to run non-executable programs
516 if not os.path.exists(testpath):
524 if not os.path.exists(testpath):
517 return fail("does not exist")
525 return fail("does not exist")
518 elif not os.access(testpath, os.X_OK):
526 elif not os.access(testpath, os.X_OK):
519 return skip("not executable")
527 return skip("not executable")
520 cmd = '"%s"' % testpath
528 cmd = '"%s"' % testpath
521
529
522 if options.timeout > 0:
530 if options.timeout > 0:
523 signal.alarm(options.timeout)
531 signal.alarm(options.timeout)
524
532
525 vlog("# Running", cmd)
533 vlog("# Running", cmd)
526 ret, out = run(cmd, options)
534 ret, out = run(cmd, options)
527 vlog("# Ret was:", ret)
535 vlog("# Ret was:", ret)
528
536
529 if options.timeout > 0:
537 if options.timeout > 0:
530 signal.alarm(0)
538 signal.alarm(0)
531
539
532 mark = '.'
540 mark = '.'
533
541
534 skipped = (ret == SKIPPED_STATUS)
542 skipped = (ret == SKIPPED_STATUS)
535 # If we're not in --debug mode and reference output file exists,
543 # If we're not in --debug mode and reference output file exists,
536 # check test output against it.
544 # check test output against it.
537 if options.debug:
545 if options.debug:
538 refout = None # to match out == None
546 refout = None # to match out == None
539 elif os.path.exists(ref):
547 elif os.path.exists(ref):
540 f = open(ref, "r")
548 f = open(ref, "r")
541 refout = splitnewlines(f.read())
549 refout = splitnewlines(f.read())
542 f.close()
550 f.close()
543 else:
551 else:
544 refout = []
552 refout = []
545
553
546 if skipped:
554 if skipped:
547 mark = 's'
555 mark = 's'
548 if out is None: # debug mode: nothing to parse
556 if out is None: # debug mode: nothing to parse
549 missing = ['unknown']
557 missing = ['unknown']
550 failed = None
558 failed = None
551 else:
559 else:
552 missing, failed = parsehghaveoutput(out)
560 missing, failed = parsehghaveoutput(out)
553 if not missing:
561 if not missing:
554 missing = ['irrelevant']
562 missing = ['irrelevant']
555 if failed:
563 if failed:
556 fail("hghave failed checking for %s" % failed[-1])
564 fail("hghave failed checking for %s" % failed[-1])
557 skipped = False
565 skipped = False
558 else:
566 else:
559 skip(missing[-1])
567 skip(missing[-1])
560 elif out != refout:
568 elif out != refout:
561 mark = '!'
569 mark = '!'
562 if ret:
570 if ret:
563 fail("output changed and returned error code %d" % ret)
571 fail("output changed and returned error code %d" % ret)
564 else:
572 else:
565 fail("output changed")
573 fail("output changed")
566 if not options.nodiff:
574 if not options.nodiff:
567 showdiff(refout, out, ref, err)
575 showdiff(refout, out, ref, err)
568 ret = 1
576 ret = 1
569 elif ret:
577 elif ret:
570 mark = '!'
578 mark = '!'
571 fail("returned error code %d" % ret)
579 fail("returned error code %d" % ret)
572
580
573 if not options.verbose:
581 if not options.verbose:
574 sys.stdout.write(mark)
582 sys.stdout.write(mark)
575 sys.stdout.flush()
583 sys.stdout.flush()
576
584
577 if ret != 0 and not skipped and not options.debug:
585 if ret != 0 and not skipped and not options.debug:
578 # Save errors to a file for diagnosis
586 # Save errors to a file for diagnosis
579 f = open(err, "wb")
587 f = open(err, "wb")
580 for line in out:
588 for line in out:
581 f.write(line)
589 f.write(line)
582 f.close()
590 f.close()
583
591
584 # Kill off any leftover daemon processes
592 # Kill off any leftover daemon processes
585 try:
593 try:
586 fp = open(DAEMON_PIDS)
594 fp = open(DAEMON_PIDS)
587 for line in fp:
595 for line in fp:
588 try:
596 try:
589 pid = int(line)
597 pid = int(line)
590 except ValueError:
598 except ValueError:
591 continue
599 continue
592 try:
600 try:
593 os.kill(pid, 0)
601 os.kill(pid, 0)
594 vlog('# Killing daemon process %d' % pid)
602 vlog('# Killing daemon process %d' % pid)
595 os.kill(pid, signal.SIGTERM)
603 os.kill(pid, signal.SIGTERM)
596 time.sleep(0.25)
604 time.sleep(0.25)
597 os.kill(pid, 0)
605 os.kill(pid, 0)
598 vlog('# Daemon process %d is stuck - really killing it' % pid)
606 vlog('# Daemon process %d is stuck - really killing it' % pid)
599 os.kill(pid, signal.SIGKILL)
607 os.kill(pid, signal.SIGKILL)
600 except OSError, err:
608 except OSError, err:
601 if err.errno != errno.ESRCH:
609 if err.errno != errno.ESRCH:
602 raise
610 raise
603 fp.close()
611 fp.close()
604 os.unlink(DAEMON_PIDS)
612 os.unlink(DAEMON_PIDS)
605 except IOError:
613 except IOError:
606 pass
614 pass
607
615
608 os.chdir(TESTDIR)
616 os.chdir(TESTDIR)
609 if not options.keep_tmpdir:
617 if not options.keep_tmpdir:
610 shutil.rmtree(tmpd, True)
618 shutil.rmtree(tmpd, True)
611 if skipped:
619 if skipped:
612 return None
620 return None
613 return ret == 0
621 return ret == 0
614
622
615 _hgpath = None
623 _hgpath = None
616
624
617 def _gethgpath():
625 def _gethgpath():
618 """Return the path to the mercurial package that is actually found by
626 """Return the path to the mercurial package that is actually found by
619 the current Python interpreter."""
627 the current Python interpreter."""
620 global _hgpath
628 global _hgpath
621 if _hgpath is not None:
629 if _hgpath is not None:
622 return _hgpath
630 return _hgpath
623
631
624 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
632 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
625 pipe = os.popen(cmd % PYTHON)
633 pipe = os.popen(cmd % PYTHON)
626 try:
634 try:
627 _hgpath = pipe.read().strip()
635 _hgpath = pipe.read().strip()
628 finally:
636 finally:
629 pipe.close()
637 pipe.close()
630 return _hgpath
638 return _hgpath
631
639
632 def _checkhglib(verb):
640 def _checkhglib(verb):
633 """Ensure that the 'mercurial' package imported by python is
641 """Ensure that the 'mercurial' package imported by python is
634 the one we expect it to be. If not, print a warning to stderr."""
642 the one we expect it to be. If not, print a warning to stderr."""
635 expecthg = os.path.join(PYTHONDIR, 'mercurial')
643 expecthg = os.path.join(PYTHONDIR, 'mercurial')
636 actualhg = _gethgpath()
644 actualhg = _gethgpath()
637 if actualhg != expecthg:
645 if actualhg != expecthg:
638 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
646 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
639 ' (expected %s)\n'
647 ' (expected %s)\n'
640 % (verb, actualhg, expecthg))
648 % (verb, actualhg, expecthg))
641
649
642 def runchildren(options, tests):
650 def runchildren(options, tests):
643 if INST:
651 if INST:
644 installhg(options)
652 installhg(options)
645 _checkhglib("Testing")
653 _checkhglib("Testing")
646
654
647 optcopy = dict(options.__dict__)
655 optcopy = dict(options.__dict__)
648 optcopy['jobs'] = 1
656 optcopy['jobs'] = 1
649 if optcopy['with_hg'] is None:
657 if optcopy['with_hg'] is None:
650 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
658 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
651 opts = []
659 opts = []
652 for opt, value in optcopy.iteritems():
660 for opt, value in optcopy.iteritems():
653 name = '--' + opt.replace('_', '-')
661 name = '--' + opt.replace('_', '-')
654 if value is True:
662 if value is True:
655 opts.append(name)
663 opts.append(name)
656 elif value is not None:
664 elif value is not None:
657 opts.append(name + '=' + str(value))
665 opts.append(name + '=' + str(value))
658
666
659 tests.reverse()
667 tests.reverse()
660 jobs = [[] for j in xrange(options.jobs)]
668 jobs = [[] for j in xrange(options.jobs)]
661 while tests:
669 while tests:
662 for job in jobs:
670 for job in jobs:
663 if not tests:
671 if not tests:
664 break
672 break
665 job.append(tests.pop())
673 job.append(tests.pop())
666 fps = {}
674 fps = {}
667 for j, job in enumerate(jobs):
675 for j, job in enumerate(jobs):
668 if not job:
676 if not job:
669 continue
677 continue
670 rfd, wfd = os.pipe()
678 rfd, wfd = os.pipe()
671 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
679 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
672 childtmp = os.path.join(HGTMP, 'child%d' % j)
680 childtmp = os.path.join(HGTMP, 'child%d' % j)
673 childopts += ['--tmpdir', childtmp]
681 childopts += ['--tmpdir', childtmp]
674 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
682 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
675 vlog(' '.join(cmdline))
683 vlog(' '.join(cmdline))
676 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
684 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
677 os.close(wfd)
685 os.close(wfd)
678 failures = 0
686 failures = 0
679 tested, skipped, failed = 0, 0, 0
687 tested, skipped, failed = 0, 0, 0
680 skips = []
688 skips = []
681 fails = []
689 fails = []
682 while fps:
690 while fps:
683 pid, status = os.wait()
691 pid, status = os.wait()
684 fp = fps.pop(pid)
692 fp = fps.pop(pid)
685 l = fp.read().splitlines()
693 l = fp.read().splitlines()
686 test, skip, fail = map(int, l[:3])
694 test, skip, fail = map(int, l[:3])
687 split = -fail or len(l)
695 split = -fail or len(l)
688 for s in l[3:split]:
696 for s in l[3:split]:
689 skips.append(s.split(" ", 1))
697 skips.append(s.split(" ", 1))
690 for s in l[split:]:
698 for s in l[split:]:
691 fails.append(s.split(" ", 1))
699 fails.append(s.split(" ", 1))
692 tested += test
700 tested += test
693 skipped += skip
701 skipped += skip
694 failed += fail
702 failed += fail
695 vlog('pid %d exited, status %d' % (pid, status))
703 vlog('pid %d exited, status %d' % (pid, status))
696 failures |= status
704 failures |= status
697 print
705 print
698 if not options.noskips:
706 if not options.noskips:
699 for s in skips:
707 for s in skips:
700 print "Skipped %s: %s" % (s[0], s[1])
708 print "Skipped %s: %s" % (s[0], s[1])
701 for s in fails:
709 for s in fails:
702 print "Failed %s: %s" % (s[0], s[1])
710 print "Failed %s: %s" % (s[0], s[1])
703
711
704 _checkhglib("Tested")
712 _checkhglib("Tested")
705 print "# Ran %d tests, %d skipped, %d failed." % (
713 print "# Ran %d tests, %d skipped, %d failed." % (
706 tested, skipped, failed)
714 tested, skipped, failed)
707 sys.exit(failures != 0)
715 sys.exit(failures != 0)
708
716
709 def runtests(options, tests):
717 def runtests(options, tests):
710 global DAEMON_PIDS, HGRCPATH
718 global DAEMON_PIDS, HGRCPATH
711 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
719 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
712 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
720 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
713
721
714 try:
722 try:
715 if INST:
723 if INST:
716 installhg(options)
724 installhg(options)
717 _checkhglib("Testing")
725 _checkhglib("Testing")
718
726
719 if options.timeout > 0:
727 if options.timeout > 0:
720 try:
728 try:
721 signal.signal(signal.SIGALRM, alarmed)
729 signal.signal(signal.SIGALRM, alarmed)
722 vlog('# Running each test with %d second timeout' %
730 vlog('# Running each test with %d second timeout' %
723 options.timeout)
731 options.timeout)
724 except AttributeError:
732 except AttributeError:
725 print 'WARNING: cannot run tests with timeouts'
733 print 'WARNING: cannot run tests with timeouts'
726 options.timeout = 0
734 options.timeout = 0
727
735
728 tested = 0
736 tested = 0
729 failed = 0
737 failed = 0
730 skipped = 0
738 skipped = 0
731
739
732 if options.restart:
740 if options.restart:
733 orig = list(tests)
741 orig = list(tests)
734 while tests:
742 while tests:
735 if os.path.exists(tests[0] + ".err"):
743 if os.path.exists(tests[0] + ".err"):
736 break
744 break
737 tests.pop(0)
745 tests.pop(0)
738 if not tests:
746 if not tests:
739 print "running all tests"
747 print "running all tests"
740 tests = orig
748 tests = orig
741
749
742 skips = []
750 skips = []
743 fails = []
751 fails = []
744
752
745 for test in tests:
753 for test in tests:
746 if options.blacklist:
754 if options.blacklist:
747 section = options.blacklist.get(test)
755 filename = options.blacklist.get(test)
748 if section is not None:
756 if filename is not None:
749 skips.append((test, "blacklisted (%s section)" % section))
757 skips.append((test, "blacklisted (%s)" % filename))
750 skipped += 1
758 skipped += 1
751 continue
759 continue
752
760
753 if options.retest and not os.path.exists(test + ".err"):
761 if options.retest and not os.path.exists(test + ".err"):
754 skipped += 1
762 skipped += 1
755 continue
763 continue
756
764
757 if options.keywords:
765 if options.keywords:
758 t = open(test).read().lower() + test.lower()
766 t = open(test).read().lower() + test.lower()
759 for k in options.keywords.lower().split():
767 for k in options.keywords.lower().split():
760 if k in t:
768 if k in t:
761 break
769 break
762 else:
770 else:
763 skipped +=1
771 skipped +=1
764 continue
772 continue
765
773
766 ret = runone(options, test, skips, fails)
774 ret = runone(options, test, skips, fails)
767 if ret is None:
775 if ret is None:
768 skipped += 1
776 skipped += 1
769 elif not ret:
777 elif not ret:
770 if options.interactive:
778 if options.interactive:
771 print "Accept this change? [n] ",
779 print "Accept this change? [n] ",
772 answer = sys.stdin.readline().strip()
780 answer = sys.stdin.readline().strip()
773 if answer.lower() in "y yes".split():
781 if answer.lower() in "y yes".split():
774 rename(test + ".err", test + ".out")
782 rename(test + ".err", test + ".out")
775 tested += 1
783 tested += 1
776 fails.pop()
784 fails.pop()
777 continue
785 continue
778 failed += 1
786 failed += 1
779 if options.first:
787 if options.first:
780 break
788 break
781 tested += 1
789 tested += 1
782
790
783 if options.child:
791 if options.child:
784 fp = os.fdopen(options.child, 'w')
792 fp = os.fdopen(options.child, 'w')
785 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
793 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
786 for s in skips:
794 for s in skips:
787 fp.write("%s %s\n" % s)
795 fp.write("%s %s\n" % s)
788 for s in fails:
796 for s in fails:
789 fp.write("%s %s\n" % s)
797 fp.write("%s %s\n" % s)
790 fp.close()
798 fp.close()
791 else:
799 else:
792 print
800 print
793 for s in skips:
801 for s in skips:
794 print "Skipped %s: %s" % s
802 print "Skipped %s: %s" % s
795 for s in fails:
803 for s in fails:
796 print "Failed %s: %s" % s
804 print "Failed %s: %s" % s
797 _checkhglib("Tested")
805 _checkhglib("Tested")
798 print "# Ran %d tests, %d skipped, %d failed." % (
806 print "# Ran %d tests, %d skipped, %d failed." % (
799 tested, skipped, failed)
807 tested, skipped, failed)
800
808
801 if options.anycoverage:
809 if options.anycoverage:
802 outputcoverage(options)
810 outputcoverage(options)
803 except KeyboardInterrupt:
811 except KeyboardInterrupt:
804 failed = True
812 failed = True
805 print "\ninterrupted!"
813 print "\ninterrupted!"
806
814
807 if failed:
815 if failed:
808 sys.exit(1)
816 sys.exit(1)
809
817
810 def main():
818 def main():
811 (options, args) = parseargs()
819 (options, args) = parseargs()
812 if not options.child:
820 if not options.child:
813 os.umask(022)
821 os.umask(022)
814
822
815 checktools()
823 checktools()
816
824
817 # Reset some environment variables to well-known values so that
825 # Reset some environment variables to well-known values so that
818 # the tests produce repeatable output.
826 # the tests produce repeatable output.
819 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
827 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
820 os.environ['TZ'] = 'GMT'
828 os.environ['TZ'] = 'GMT'
821 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
829 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
822 os.environ['CDPATH'] = ''
830 os.environ['CDPATH'] = ''
823 os.environ['COLUMNS'] = '80'
831 os.environ['COLUMNS'] = '80'
824 os.environ['http_proxy'] = ''
832 os.environ['http_proxy'] = ''
825
833
826 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
834 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
827 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
835 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
828 if options.tmpdir:
836 if options.tmpdir:
829 options.keep_tmpdir = True
837 options.keep_tmpdir = True
830 tmpdir = options.tmpdir
838 tmpdir = options.tmpdir
831 if os.path.exists(tmpdir):
839 if os.path.exists(tmpdir):
832 # Meaning of tmpdir has changed since 1.3: we used to create
840 # Meaning of tmpdir has changed since 1.3: we used to create
833 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
841 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
834 # tmpdir already exists.
842 # tmpdir already exists.
835 sys.exit("error: temp dir %r already exists" % tmpdir)
843 sys.exit("error: temp dir %r already exists" % tmpdir)
836
844
837 # Automatically removing tmpdir sounds convenient, but could
845 # Automatically removing tmpdir sounds convenient, but could
838 # really annoy anyone in the habit of using "--tmpdir=/tmp"
846 # really annoy anyone in the habit of using "--tmpdir=/tmp"
839 # or "--tmpdir=$HOME".
847 # or "--tmpdir=$HOME".
840 #vlog("# Removing temp dir", tmpdir)
848 #vlog("# Removing temp dir", tmpdir)
841 #shutil.rmtree(tmpdir)
849 #shutil.rmtree(tmpdir)
842 os.makedirs(tmpdir)
850 os.makedirs(tmpdir)
843 else:
851 else:
844 tmpdir = tempfile.mkdtemp('', 'hgtests.')
852 tmpdir = tempfile.mkdtemp('', 'hgtests.')
845 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
853 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
846 DAEMON_PIDS = None
854 DAEMON_PIDS = None
847 HGRCPATH = None
855 HGRCPATH = None
848
856
849 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
857 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
850 os.environ["HGMERGE"] = "internal:merge"
858 os.environ["HGMERGE"] = "internal:merge"
851 os.environ["HGUSER"] = "test"
859 os.environ["HGUSER"] = "test"
852 os.environ["HGENCODING"] = "ascii"
860 os.environ["HGENCODING"] = "ascii"
853 os.environ["HGENCODINGMODE"] = "strict"
861 os.environ["HGENCODINGMODE"] = "strict"
854 os.environ["HGPORT"] = str(options.port)
862 os.environ["HGPORT"] = str(options.port)
855 os.environ["HGPORT1"] = str(options.port + 1)
863 os.environ["HGPORT1"] = str(options.port + 1)
856 os.environ["HGPORT2"] = str(options.port + 2)
864 os.environ["HGPORT2"] = str(options.port + 2)
857
865
858 if options.with_hg:
866 if options.with_hg:
859 INST = None
867 INST = None
860 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
868 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
861
869
862 # This looks redundant with how Python initializes sys.path from
870 # This looks redundant with how Python initializes sys.path from
863 # the location of the script being executed. Needed because the
871 # the location of the script being executed. Needed because the
864 # "hg" specified by --with-hg is not the only Python script
872 # "hg" specified by --with-hg is not the only Python script
865 # executed in the test suite that needs to import 'mercurial'
873 # executed in the test suite that needs to import 'mercurial'
866 # ... which means it's not really redundant at all.
874 # ... which means it's not really redundant at all.
867 PYTHONDIR = BINDIR
875 PYTHONDIR = BINDIR
868 else:
876 else:
869 INST = os.path.join(HGTMP, "install")
877 INST = os.path.join(HGTMP, "install")
870 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
878 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
871 PYTHONDIR = os.path.join(INST, "lib", "python")
879 PYTHONDIR = os.path.join(INST, "lib", "python")
872
880
873 os.environ["BINDIR"] = BINDIR
881 os.environ["BINDIR"] = BINDIR
874 os.environ["PYTHON"] = PYTHON
882 os.environ["PYTHON"] = PYTHON
875
883
876 if not options.child:
884 if not options.child:
877 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
885 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
878 os.environ["PATH"] = os.pathsep.join(path)
886 os.environ["PATH"] = os.pathsep.join(path)
879
887
880 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
888 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
881 # can run .../tests/run-tests.py test-foo where test-foo
889 # can run .../tests/run-tests.py test-foo where test-foo
882 # adds an extension to HGRC
890 # adds an extension to HGRC
883 pypath = [PYTHONDIR, TESTDIR]
891 pypath = [PYTHONDIR, TESTDIR]
884 # We have to augment PYTHONPATH, rather than simply replacing
892 # We have to augment PYTHONPATH, rather than simply replacing
885 # it, in case external libraries are only available via current
893 # it, in case external libraries are only available via current
886 # PYTHONPATH. (In particular, the Subversion bindings on OS X
894 # PYTHONPATH. (In particular, the Subversion bindings on OS X
887 # are in /opt/subversion.)
895 # are in /opt/subversion.)
888 oldpypath = os.environ.get('PYTHONPATH')
896 oldpypath = os.environ.get('PYTHONPATH')
889 if oldpypath:
897 if oldpypath:
890 pypath.append(oldpypath)
898 pypath.append(oldpypath)
891 os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
899 os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
892
900
893 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
901 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
894
902
895 if len(args) == 0:
903 if len(args) == 0:
896 args = os.listdir(".")
904 args = os.listdir(".")
897 args.sort()
905 args.sort()
898
906
899 tests = []
907 tests = []
900 for test in args:
908 for test in args:
901 if (test.startswith("test-") and '~' not in test and
909 if (test.startswith("test-") and '~' not in test and
902 ('.' not in test or test.endswith('.py') or
910 ('.' not in test or test.endswith('.py') or
903 test.endswith('.bat'))):
911 test.endswith('.bat'))):
904 tests.append(test)
912 tests.append(test)
905 if not tests:
913 if not tests:
906 print "# Ran 0 tests, 0 skipped, 0 failed."
914 print "# Ran 0 tests, 0 skipped, 0 failed."
907 return
915 return
908
916
909 vlog("# Using TESTDIR", TESTDIR)
917 vlog("# Using TESTDIR", TESTDIR)
910 vlog("# Using HGTMP", HGTMP)
918 vlog("# Using HGTMP", HGTMP)
911 vlog("# Using PATH", os.environ["PATH"])
919 vlog("# Using PATH", os.environ["PATH"])
912 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
920 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
913
921
914 try:
922 try:
915 if len(tests) > 1 and options.jobs > 1:
923 if len(tests) > 1 and options.jobs > 1:
916 runchildren(options, tests)
924 runchildren(options, tests)
917 else:
925 else:
918 runtests(options, tests)
926 runtests(options, tests)
919 finally:
927 finally:
920 cleanup(options)
928 cleanup(options)
921
929
922 main()
930 main()
General Comments 0
You need to be logged in to leave comments. Login now