##// END OF EJS Templates
run-tests: do not duplicate killdaemons() code
Patrick Mezard -
r17464:eddfb9a5 default
parent child Browse files
Show More
@@ -1,25 +1,40 b''
1 1 #!/usr/bin/env python
2 2
3 3 import os, time, errno, signal
4 4
5 def killdaemons(pidfile, tryhard=True, remove=False, logfn=None):
6 if not logfn:
7 logfn = lambda s: s
5 8 # Kill off any leftover daemon processes
6 9 try:
7 fp = open(os.environ['DAEMON_PIDS'])
10 fp = open(pidfile)
8 11 for line in fp:
9 12 try:
10 13 pid = int(line)
11 14 except ValueError:
12 15 continue
13 16 try:
14 17 os.kill(pid, 0)
18 logfn('# Killing daemon process %d' % pid)
15 19 os.kill(pid, signal.SIGTERM)
20 if tryhard:
16 21 for i in range(10):
17 22 time.sleep(0.05)
18 23 os.kill(pid, 0)
24 else:
25 time.sleep(0.1)
26 os.kill(pid, 0)
27 logfn('# Daemon process %d is stuck - really killing it' % pid)
19 28 os.kill(pid, signal.SIGKILL)
20 29 except OSError, err:
21 30 if err.errno != errno.ESRCH:
22 31 raise
23 32 fp.close()
33 if remove:
34 os.unlink(pidfile)
24 35 except IOError:
25 36 pass
37
38 if __name__ == '__main__':
39 killdaemons(os.environ['DAEMON_PIDS'])
40
@@ -1,1324 +1,1304 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 # Modifying this script is tricky because it has many modes:
11 11 # - serial (default) vs parallel (-jN, N > 1)
12 12 # - no coverage (default) vs coverage (-c, -C, -s)
13 13 # - temp install (default) vs specific hg script (--with-hg, --local)
14 14 # - tests are a mix of shell scripts and Python scripts
15 15 #
16 16 # If you change this script, it is recommended that you ensure you
17 17 # haven't broken it by running it in various modes with a representative
18 18 # sample of test scripts. For example:
19 19 #
20 20 # 1) serial, no coverage, temp install:
21 21 # ./run-tests.py test-s*
22 22 # 2) serial, no coverage, local hg:
23 23 # ./run-tests.py --local test-s*
24 24 # 3) serial, coverage, temp install:
25 25 # ./run-tests.py -c test-s*
26 26 # 4) serial, coverage, local hg:
27 27 # ./run-tests.py -c --local test-s* # unsupported
28 28 # 5) parallel, no coverage, temp install:
29 29 # ./run-tests.py -j2 test-s*
30 30 # 6) parallel, no coverage, local hg:
31 31 # ./run-tests.py -j2 --local test-s*
32 32 # 7) parallel, coverage, temp install:
33 33 # ./run-tests.py -j2 -c test-s* # currently broken
34 34 # 8) parallel, coverage, local install:
35 35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
36 36 # 9) parallel, custom tmp dir:
37 37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
38 38 #
39 39 # (You could use any subset of the tests: test-s* happens to match
40 40 # enough that it's worth doing parallel runs, few enough that it
41 41 # completes fairly quickly, includes both shell and Python scripts, and
42 42 # includes some scripts that run daemon processes.)
43 43
44 44 from distutils import version
45 45 import difflib
46 46 import errno
47 47 import optparse
48 48 import os
49 49 import shutil
50 50 import subprocess
51 51 import signal
52 52 import sys
53 53 import tempfile
54 54 import time
55 55 import re
56 56 import threading
57 import killdaemons as killmod
57 58
58 59 processlock = threading.Lock()
59 60
60 61 closefds = os.name == 'posix'
61 62 def Popen4(cmd, wd, timeout):
62 63 processlock.acquire()
63 64 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd,
64 65 close_fds=closefds,
65 66 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
66 67 stderr=subprocess.STDOUT)
67 68 processlock.release()
68 69
69 70 p.fromchild = p.stdout
70 71 p.tochild = p.stdin
71 72 p.childerr = p.stderr
72 73
73 74 p.timeout = False
74 75 if timeout:
75 76 def t():
76 77 start = time.time()
77 78 while time.time() - start < timeout and p.returncode is None:
78 79 time.sleep(.1)
79 80 p.timeout = True
80 81 if p.returncode is None:
81 82 terminate(p)
82 83 threading.Thread(target=t).start()
83 84
84 85 return p
85 86
86 87 # reserved exit code to skip test (used by hghave)
87 88 SKIPPED_STATUS = 80
88 89 SKIPPED_PREFIX = 'skipped: '
89 90 FAILED_PREFIX = 'hghave check failed: '
90 91 PYTHON = sys.executable.replace('\\', '/')
91 92 IMPL_PATH = 'PYTHONPATH'
92 93 if 'java' in sys.platform:
93 94 IMPL_PATH = 'JYTHONPATH'
94 95
95 96 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
96 97
97 98 defaults = {
98 99 'jobs': ('HGTEST_JOBS', 1),
99 100 'timeout': ('HGTEST_TIMEOUT', 180),
100 101 'port': ('HGTEST_PORT', 20059),
101 102 'shell': ('HGTEST_SHELL', 'sh'),
102 103 }
103 104
104 105 def parselistfiles(files, listtype, warn=True):
105 106 entries = dict()
106 107 for filename in files:
107 108 try:
108 109 path = os.path.expanduser(os.path.expandvars(filename))
109 110 f = open(path, "r")
110 111 except IOError, err:
111 112 if err.errno != errno.ENOENT:
112 113 raise
113 114 if warn:
114 115 print "warning: no such %s file: %s" % (listtype, filename)
115 116 continue
116 117
117 118 for line in f.readlines():
118 119 line = line.split('#', 1)[0].strip()
119 120 if line:
120 121 entries[line] = filename
121 122
122 123 f.close()
123 124 return entries
124 125
125 126 def parseargs():
126 127 parser = optparse.OptionParser("%prog [options] [tests]")
127 128
128 129 # keep these sorted
129 130 parser.add_option("--blacklist", action="append",
130 131 help="skip tests listed in the specified blacklist file")
131 132 parser.add_option("--whitelist", action="append",
132 133 help="always run tests listed in the specified whitelist file")
133 134 parser.add_option("-C", "--annotate", action="store_true",
134 135 help="output files annotated with coverage")
135 136 parser.add_option("--child", type="int",
136 137 help="run as child process, summary to given fd")
137 138 parser.add_option("-c", "--cover", action="store_true",
138 139 help="print a test coverage report")
139 140 parser.add_option("-d", "--debug", action="store_true",
140 141 help="debug mode: write output of test scripts to console"
141 142 " rather than capturing and diff'ing it (disables timeout)")
142 143 parser.add_option("-f", "--first", action="store_true",
143 144 help="exit on the first test failure")
144 145 parser.add_option("-H", "--htmlcov", action="store_true",
145 146 help="create an HTML report of the coverage of the files")
146 147 parser.add_option("--inotify", action="store_true",
147 148 help="enable inotify extension when running tests")
148 149 parser.add_option("-i", "--interactive", action="store_true",
149 150 help="prompt to accept changed output")
150 151 parser.add_option("-j", "--jobs", type="int",
151 152 help="number of jobs to run in parallel"
152 153 " (default: $%s or %d)" % defaults['jobs'])
153 154 parser.add_option("--keep-tmpdir", action="store_true",
154 155 help="keep temporary directory after running tests")
155 156 parser.add_option("-k", "--keywords",
156 157 help="run tests matching keywords")
157 158 parser.add_option("-l", "--local", action="store_true",
158 159 help="shortcut for --with-hg=<testdir>/../hg")
159 160 parser.add_option("-n", "--nodiff", action="store_true",
160 161 help="skip showing test changes")
161 162 parser.add_option("-p", "--port", type="int",
162 163 help="port on which servers should listen"
163 164 " (default: $%s or %d)" % defaults['port'])
164 165 parser.add_option("--pure", action="store_true",
165 166 help="use pure Python code instead of C extensions")
166 167 parser.add_option("-R", "--restart", action="store_true",
167 168 help="restart at last error")
168 169 parser.add_option("-r", "--retest", action="store_true",
169 170 help="retest failed tests")
170 171 parser.add_option("-S", "--noskips", action="store_true",
171 172 help="don't report skip tests verbosely")
172 173 parser.add_option("--shell", type="string",
173 174 help="shell to use (default: $%s or %s)" % defaults['shell'])
174 175 parser.add_option("-t", "--timeout", type="int",
175 176 help="kill errant tests after TIMEOUT seconds"
176 177 " (default: $%s or %d)" % defaults['timeout'])
177 178 parser.add_option("--tmpdir", type="string",
178 179 help="run tests in the given temporary directory"
179 180 " (implies --keep-tmpdir)")
180 181 parser.add_option("-v", "--verbose", action="store_true",
181 182 help="output verbose messages")
182 183 parser.add_option("--view", type="string",
183 184 help="external diff viewer")
184 185 parser.add_option("--with-hg", type="string",
185 186 metavar="HG",
186 187 help="test using specified hg script rather than a "
187 188 "temporary installation")
188 189 parser.add_option("-3", "--py3k-warnings", action="store_true",
189 190 help="enable Py3k warnings on Python 2.6+")
190 191 parser.add_option('--extra-config-opt', action="append",
191 192 help='set the given config opt in the test hgrc')
192 193
193 194 for option, (envvar, default) in defaults.items():
194 195 defaults[option] = type(default)(os.environ.get(envvar, default))
195 196 parser.set_defaults(**defaults)
196 197 (options, args) = parser.parse_args()
197 198
198 199 # jython is always pure
199 200 if 'java' in sys.platform or '__pypy__' in sys.modules:
200 201 options.pure = True
201 202
202 203 if options.with_hg:
203 204 options.with_hg = os.path.expanduser(options.with_hg)
204 205 if not (os.path.isfile(options.with_hg) and
205 206 os.access(options.with_hg, os.X_OK)):
206 207 parser.error('--with-hg must specify an executable hg script')
207 208 if not os.path.basename(options.with_hg) == 'hg':
208 209 sys.stderr.write('warning: --with-hg should specify an hg script\n')
209 210 if options.local:
210 211 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
211 212 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
212 213 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
213 214 parser.error('--local specified, but %r not found or not executable'
214 215 % hgbin)
215 216 options.with_hg = hgbin
216 217
217 218 options.anycoverage = options.cover or options.annotate or options.htmlcov
218 219 if options.anycoverage:
219 220 try:
220 221 import coverage
221 222 covver = version.StrictVersion(coverage.__version__).version
222 223 if covver < (3, 3):
223 224 parser.error('coverage options require coverage 3.3 or later')
224 225 except ImportError:
225 226 parser.error('coverage options now require the coverage package')
226 227
227 228 if options.anycoverage and options.local:
228 229 # this needs some path mangling somewhere, I guess
229 230 parser.error("sorry, coverage options do not work when --local "
230 231 "is specified")
231 232
232 233 global vlog
233 234 if options.verbose:
234 235 if options.jobs > 1 or options.child is not None:
235 236 pid = "[%d]" % os.getpid()
236 237 else:
237 238 pid = None
238 239 def vlog(*msg):
239 240 iolock.acquire()
240 241 if pid:
241 242 print pid,
242 243 for m in msg:
243 244 print m,
244 245 print
245 246 sys.stdout.flush()
246 247 iolock.release()
247 248 else:
248 249 vlog = lambda *msg: None
249 250
250 251 if options.tmpdir:
251 252 options.tmpdir = os.path.expanduser(options.tmpdir)
252 253
253 254 if options.jobs < 1:
254 255 parser.error('--jobs must be positive')
255 256 if options.interactive and options.jobs > 1:
256 257 print '(--interactive overrides --jobs)'
257 258 options.jobs = 1
258 259 if options.interactive and options.debug:
259 260 parser.error("-i/--interactive and -d/--debug are incompatible")
260 261 if options.debug:
261 262 if options.timeout != defaults['timeout']:
262 263 sys.stderr.write(
263 264 'warning: --timeout option ignored with --debug\n')
264 265 options.timeout = 0
265 266 if options.py3k_warnings:
266 267 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
267 268 parser.error('--py3k-warnings can only be used on Python 2.6+')
268 269 if options.blacklist:
269 270 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
270 271 if options.whitelist:
271 272 options.whitelisted = parselistfiles(options.whitelist, 'whitelist',
272 273 warn=options.child is None)
273 274 else:
274 275 options.whitelisted = {}
275 276
276 277 return (options, args)
277 278
278 279 def rename(src, dst):
279 280 """Like os.rename(), trade atomicity and opened files friendliness
280 281 for existing destination support.
281 282 """
282 283 shutil.copy(src, dst)
283 284 os.remove(src)
284 285
285 286 def splitnewlines(text):
286 287 '''like str.splitlines, but only split on newlines.
287 288 keep line endings.'''
288 289 i = 0
289 290 lines = []
290 291 while True:
291 292 n = text.find('\n', i)
292 293 if n == -1:
293 294 last = text[i:]
294 295 if last:
295 296 lines.append(last)
296 297 return lines
297 298 lines.append(text[i:n + 1])
298 299 i = n + 1
299 300
300 301 def parsehghaveoutput(lines):
301 302 '''Parse hghave log lines.
302 303 Return tuple of lists (missing, failed):
303 304 * the missing/unknown features
304 305 * the features for which existence check failed'''
305 306 missing = []
306 307 failed = []
307 308 for line in lines:
308 309 if line.startswith(SKIPPED_PREFIX):
309 310 line = line.splitlines()[0]
310 311 missing.append(line[len(SKIPPED_PREFIX):])
311 312 elif line.startswith(FAILED_PREFIX):
312 313 line = line.splitlines()[0]
313 314 failed.append(line[len(FAILED_PREFIX):])
314 315
315 316 return missing, failed
316 317
317 318 def showdiff(expected, output, ref, err):
318 319 print
319 320 for line in difflib.unified_diff(expected, output, ref, err):
320 321 sys.stdout.write(line)
321 322
322 323 def findprogram(program):
323 324 """Search PATH for a executable program"""
324 325 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
325 326 name = os.path.join(p, program)
326 327 if os.name == 'nt' or os.access(name, os.X_OK):
327 328 return name
328 329 return None
329 330
330 331 def checktools():
331 332 # Before we go any further, check for pre-requisite tools
332 333 # stuff from coreutils (cat, rm, etc) are not tested
333 334 for p in requiredtools:
334 335 if os.name == 'nt':
335 336 p += '.exe'
336 337 found = findprogram(p)
337 338 if found:
338 339 vlog("# Found prerequisite", p, "at", found)
339 340 else:
340 341 print "WARNING: Did not find prerequisite tool: "+p
341 342
342 343 def terminate(proc):
343 344 """Terminate subprocess (with fallback for Python versions < 2.6)"""
344 345 vlog('# Terminating process %d' % proc.pid)
345 346 try:
346 347 getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
347 348 except OSError:
348 349 pass
349 350
350 351 def killdaemons():
351 # Kill off any leftover daemon processes
352 try:
353 fp = open(DAEMON_PIDS)
354 for line in fp:
355 try:
356 pid = int(line)
357 except ValueError:
358 continue
359 try:
360 os.kill(pid, 0)
361 vlog('# Killing daemon process %d' % pid)
362 os.kill(pid, signal.SIGTERM)
363 time.sleep(0.1)
364 os.kill(pid, 0)
365 vlog('# Daemon process %d is stuck - really killing it' % pid)
366 os.kill(pid, signal.SIGKILL)
367 except OSError, err:
368 if err.errno != errno.ESRCH:
369 raise
370 fp.close()
371 os.unlink(DAEMON_PIDS)
372 except IOError:
373 pass
352 return killmod.killdaemons(DAEMON_PIDS, tryhard=False, remove=True,
353 logfn=vlog)
374 354
375 355 def cleanup(options):
376 356 if not options.keep_tmpdir:
377 357 vlog("# Cleaning up HGTMP", HGTMP)
378 358 shutil.rmtree(HGTMP, True)
379 359
380 360 def usecorrectpython():
381 361 # some tests run python interpreter. they must use same
382 362 # interpreter we use or bad things will happen.
383 363 exedir, exename = os.path.split(sys.executable)
384 364 if exename in ('python', 'python.exe'):
385 365 path = findprogram(exename)
386 366 if os.path.dirname(path) == exedir:
387 367 return
388 368 else:
389 369 exename = 'python'
390 370 vlog('# Making python executable in test path use correct Python')
391 371 mypython = os.path.join(BINDIR, exename)
392 372 try:
393 373 os.symlink(sys.executable, mypython)
394 374 except AttributeError:
395 375 # windows fallback
396 376 shutil.copyfile(sys.executable, mypython)
397 377 shutil.copymode(sys.executable, mypython)
398 378
399 379 def installhg(options):
400 380 vlog("# Performing temporary installation of HG")
401 381 installerrs = os.path.join("tests", "install.err")
402 382 pure = options.pure and "--pure" or ""
403 383
404 384 # Run installer in hg root
405 385 script = os.path.realpath(sys.argv[0])
406 386 hgroot = os.path.dirname(os.path.dirname(script))
407 387 os.chdir(hgroot)
408 388 nohome = '--home=""'
409 389 if os.name == 'nt':
410 390 # The --home="" trick works only on OS where os.sep == '/'
411 391 # because of a distutils convert_path() fast-path. Avoid it at
412 392 # least on Windows for now, deal with .pydistutils.cfg bugs
413 393 # when they happen.
414 394 nohome = ''
415 395 cmd = ('%s setup.py %s clean --all'
416 396 ' build --build-base="%s"'
417 397 ' install --force --prefix="%s" --install-lib="%s"'
418 398 ' --install-scripts="%s" %s >%s 2>&1'
419 399 % (sys.executable, pure, os.path.join(HGTMP, "build"),
420 400 INST, PYTHONDIR, BINDIR, nohome, installerrs))
421 401 vlog("# Running", cmd)
422 402 if os.system(cmd) == 0:
423 403 if not options.verbose:
424 404 os.remove(installerrs)
425 405 else:
426 406 f = open(installerrs)
427 407 for line in f:
428 408 print line,
429 409 f.close()
430 410 sys.exit(1)
431 411 os.chdir(TESTDIR)
432 412
433 413 usecorrectpython()
434 414
435 415 vlog("# Installing dummy diffstat")
436 416 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
437 417 f.write('#!' + sys.executable + '\n'
438 418 'import sys\n'
439 419 'files = 0\n'
440 420 'for line in sys.stdin:\n'
441 421 ' if line.startswith("diff "):\n'
442 422 ' files += 1\n'
443 423 'sys.stdout.write("files patched: %d\\n" % files)\n')
444 424 f.close()
445 425 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
446 426
447 427 if options.py3k_warnings and not options.anycoverage:
448 428 vlog("# Updating hg command to enable Py3k Warnings switch")
449 429 f = open(os.path.join(BINDIR, 'hg'), 'r')
450 430 lines = [line.rstrip() for line in f]
451 431 lines[0] += ' -3'
452 432 f.close()
453 433 f = open(os.path.join(BINDIR, 'hg'), 'w')
454 434 for line in lines:
455 435 f.write(line + '\n')
456 436 f.close()
457 437
458 438 hgbat = os.path.join(BINDIR, 'hg.bat')
459 439 if os.path.isfile(hgbat):
460 440 # hg.bat expects to be put in bin/scripts while run-tests.py
461 441 # installation layout put it in bin/ directly. Fix it
462 442 f = open(hgbat, 'rb')
463 443 data = f.read()
464 444 f.close()
465 445 if '"%~dp0..\python" "%~dp0hg" %*' in data:
466 446 data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
467 447 '"%~dp0python" "%~dp0hg" %*')
468 448 f = open(hgbat, 'wb')
469 449 f.write(data)
470 450 f.close()
471 451 else:
472 452 print 'WARNING: cannot fix hg.bat reference to python.exe'
473 453
474 454 if options.anycoverage:
475 455 custom = os.path.join(TESTDIR, 'sitecustomize.py')
476 456 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
477 457 vlog('# Installing coverage trigger to %s' % target)
478 458 shutil.copyfile(custom, target)
479 459 rc = os.path.join(TESTDIR, '.coveragerc')
480 460 vlog('# Installing coverage rc to %s' % rc)
481 461 os.environ['COVERAGE_PROCESS_START'] = rc
482 462 fn = os.path.join(INST, '..', '.coverage')
483 463 os.environ['COVERAGE_FILE'] = fn
484 464
485 465 def outputcoverage(options):
486 466
487 467 vlog('# Producing coverage report')
488 468 os.chdir(PYTHONDIR)
489 469
490 470 def covrun(*args):
491 471 cmd = 'coverage %s' % ' '.join(args)
492 472 vlog('# Running: %s' % cmd)
493 473 os.system(cmd)
494 474
495 475 if options.child:
496 476 return
497 477
498 478 covrun('-c')
499 479 omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
500 480 covrun('-i', '-r', '"--omit=%s"' % omit) # report
501 481 if options.htmlcov:
502 482 htmldir = os.path.join(TESTDIR, 'htmlcov')
503 483 covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
504 484 if options.annotate:
505 485 adir = os.path.join(TESTDIR, 'annotated')
506 486 if not os.path.isdir(adir):
507 487 os.mkdir(adir)
508 488 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
509 489
510 490 def pytest(test, wd, options, replacements):
511 491 py3kswitch = options.py3k_warnings and ' -3' or ''
512 492 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
513 493 vlog("# Running", cmd)
514 494 return run(cmd, wd, options, replacements)
515 495
516 496 def shtest(test, wd, options, replacements):
517 497 cmd = '%s "%s"' % (options.shell, test)
518 498 vlog("# Running", cmd)
519 499 return run(cmd, wd, options, replacements)
520 500
521 501 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
522 502 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
523 503 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
524 504 escapemap.update({'\\': '\\\\', '\r': r'\r'})
525 505 def escapef(m):
526 506 return escapemap[m.group(0)]
527 507 def stringescape(s):
528 508 return escapesub(escapef, s)
529 509
530 510 def rematch(el, l):
531 511 try:
532 512 # ensure that the regex matches to the end of the string
533 513 return re.match(el + r'\Z', l)
534 514 except re.error:
535 515 # el is an invalid regex
536 516 return False
537 517
538 518 def globmatch(el, l):
539 519 # The only supported special characters are * and ? plus / which also
540 520 # matches \ on windows. Escaping of these caracters is supported.
541 521 i, n = 0, len(el)
542 522 res = ''
543 523 while i < n:
544 524 c = el[i]
545 525 i += 1
546 526 if c == '\\' and el[i] in '*?\\/':
547 527 res += el[i - 1:i + 1]
548 528 i += 1
549 529 elif c == '*':
550 530 res += '.*'
551 531 elif c == '?':
552 532 res += '.'
553 533 elif c == '/' and os.name == 'nt':
554 534 res += '[/\\\\]'
555 535 else:
556 536 res += re.escape(c)
557 537 return rematch(res, l)
558 538
559 539 def linematch(el, l):
560 540 if el == l: # perfect match (fast)
561 541 return True
562 542 if (el and
563 543 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', l) or
564 544 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', l) or
565 545 el.endswith(" (esc)\n") and
566 546 (el[:-7].decode('string-escape') + '\n' == l or
567 547 el[:-7].decode('string-escape').replace('\r', '') +
568 548 '\n' == l and os.name == 'nt'))):
569 549 return True
570 550 return False
571 551
572 552 def tsttest(test, wd, options, replacements):
573 553 # We generate a shell script which outputs unique markers to line
574 554 # up script results with our source. These markers include input
575 555 # line number and the last return code
576 556 salt = "SALT" + str(time.time())
577 557 def addsalt(line, inpython):
578 558 if inpython:
579 559 script.append('%s %d 0\n' % (salt, line))
580 560 else:
581 561 script.append('echo %s %s $?\n' % (salt, line))
582 562
583 563 # After we run the shell script, we re-unify the script output
584 564 # with non-active parts of the source, with synchronization by our
585 565 # SALT line number markers. The after table contains the
586 566 # non-active components, ordered by line number
587 567 after = {}
588 568 pos = prepos = -1
589 569
590 570 # Expected shellscript output
591 571 expected = {}
592 572
593 573 # We keep track of whether or not we're in a Python block so we
594 574 # can generate the surrounding doctest magic
595 575 inpython = False
596 576
597 577 # True or False when in a true or false conditional section
598 578 skipping = None
599 579
600 580 def hghave(reqs):
601 581 # TODO: do something smarter when all other uses of hghave is gone
602 582 tdir = TESTDIR.replace('\\', '/')
603 583 proc = Popen4('%s -c "%s/hghave %s"' %
604 584 (options.shell, tdir, ' '.join(reqs)), wd, 0)
605 585 proc.communicate()
606 586 ret = proc.wait()
607 587 if wifexited(ret):
608 588 ret = os.WEXITSTATUS(ret)
609 589 return ret == 0
610 590
611 591 f = open(test)
612 592 t = f.readlines()
613 593 f.close()
614 594
615 595 script = []
616 596 if options.debug:
617 597 script.append('set -x\n')
618 598 if os.getenv('MSYSTEM'):
619 599 script.append('alias pwd="pwd -W"\n')
620 600 for n, l in enumerate(t):
621 601 if not l.endswith('\n'):
622 602 l += '\n'
623 603 if l.startswith('#if'):
624 604 if skipping is not None:
625 605 after.setdefault(pos, []).append(' !!! nested #if\n')
626 606 skipping = not hghave(l.split()[1:])
627 607 after.setdefault(pos, []).append(l)
628 608 elif l.startswith('#else'):
629 609 if skipping is None:
630 610 after.setdefault(pos, []).append(' !!! missing #if\n')
631 611 skipping = not skipping
632 612 after.setdefault(pos, []).append(l)
633 613 elif l.startswith('#endif'):
634 614 if skipping is None:
635 615 after.setdefault(pos, []).append(' !!! missing #if\n')
636 616 skipping = None
637 617 after.setdefault(pos, []).append(l)
638 618 elif skipping:
639 619 after.setdefault(pos, []).append(l)
640 620 elif l.startswith(' >>> '): # python inlines
641 621 after.setdefault(pos, []).append(l)
642 622 prepos = pos
643 623 pos = n
644 624 if not inpython:
645 625 # we've just entered a Python block, add the header
646 626 inpython = True
647 627 addsalt(prepos, False) # make sure we report the exit code
648 628 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
649 629 addsalt(n, True)
650 630 script.append(l[2:])
651 631 elif l.startswith(' ... '): # python inlines
652 632 after.setdefault(prepos, []).append(l)
653 633 script.append(l[2:])
654 634 elif l.startswith(' $ '): # commands
655 635 if inpython:
656 636 script.append("EOF\n")
657 637 inpython = False
658 638 after.setdefault(pos, []).append(l)
659 639 prepos = pos
660 640 pos = n
661 641 addsalt(n, False)
662 642 cmd = l[4:].split()
663 643 if len(cmd) == 2 and cmd[0] == 'cd':
664 644 l = ' $ cd %s || exit 1\n' % cmd[1]
665 645 script.append(l[4:])
666 646 elif l.startswith(' > '): # continuations
667 647 after.setdefault(prepos, []).append(l)
668 648 script.append(l[4:])
669 649 elif l.startswith(' '): # results
670 650 # queue up a list of expected results
671 651 expected.setdefault(pos, []).append(l[2:])
672 652 else:
673 653 if inpython:
674 654 script.append("EOF\n")
675 655 inpython = False
676 656 # non-command/result - queue up for merged output
677 657 after.setdefault(pos, []).append(l)
678 658
679 659 if inpython:
680 660 script.append("EOF\n")
681 661 if skipping is not None:
682 662 after.setdefault(pos, []).append(' !!! missing #endif\n')
683 663 addsalt(n + 1, False)
684 664
685 665 # Write out the script and execute it
686 666 fd, name = tempfile.mkstemp(suffix='hg-tst')
687 667 try:
688 668 for l in script:
689 669 os.write(fd, l)
690 670 os.close(fd)
691 671
692 672 cmd = '%s "%s"' % (options.shell, name)
693 673 vlog("# Running", cmd)
694 674 exitcode, output = run(cmd, wd, options, replacements)
695 675 # do not merge output if skipped, return hghave message instead
696 676 # similarly, with --debug, output is None
697 677 if exitcode == SKIPPED_STATUS or output is None:
698 678 return exitcode, output
699 679 finally:
700 680 os.remove(name)
701 681
702 682 # Merge the script output back into a unified test
703 683
704 684 pos = -1
705 685 postout = []
706 686 ret = 0
707 687 for n, l in enumerate(output):
708 688 lout, lcmd = l, None
709 689 if salt in l:
710 690 lout, lcmd = l.split(salt, 1)
711 691
712 692 if lout:
713 693 if lcmd:
714 694 # output block had no trailing newline, clean up
715 695 lout += ' (no-eol)\n'
716 696
717 697 # find the expected output at the current position
718 698 el = None
719 699 if pos in expected and expected[pos]:
720 700 el = expected[pos].pop(0)
721 701
722 702 if linematch(el, lout):
723 703 postout.append(" " + el)
724 704 else:
725 705 if needescape(lout):
726 706 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
727 707 postout.append(" " + lout) # let diff deal with it
728 708
729 709 if lcmd:
730 710 # add on last return code
731 711 ret = int(lcmd.split()[1])
732 712 if ret != 0:
733 713 postout.append(" [%s]\n" % ret)
734 714 if pos in after:
735 715 # merge in non-active test bits
736 716 postout += after.pop(pos)
737 717 pos = int(lcmd.split()[0])
738 718
739 719 if pos in after:
740 720 postout += after.pop(pos)
741 721
742 722 return exitcode, postout
743 723
744 724 wifexited = getattr(os, "WIFEXITED", lambda x: False)
745 725 def run(cmd, wd, options, replacements):
746 726 """Run command in a sub-process, capturing the output (stdout and stderr).
747 727 Return a tuple (exitcode, output). output is None in debug mode."""
748 728 # TODO: Use subprocess.Popen if we're running on Python 2.4
749 729 if options.debug:
750 730 proc = subprocess.Popen(cmd, shell=True, cwd=wd)
751 731 ret = proc.wait()
752 732 return (ret, None)
753 733
754 734 proc = Popen4(cmd, wd, options.timeout)
755 735 def cleanup():
756 736 terminate(proc)
757 737 ret = proc.wait()
758 738 if ret == 0:
759 739 ret = signal.SIGTERM << 8
760 740 killdaemons()
761 741 return ret
762 742
763 743 output = ''
764 744 proc.tochild.close()
765 745
766 746 try:
767 747 output = proc.fromchild.read()
768 748 except KeyboardInterrupt:
769 749 vlog('# Handling keyboard interrupt')
770 750 cleanup()
771 751 raise
772 752
773 753 ret = proc.wait()
774 754 if wifexited(ret):
775 755 ret = os.WEXITSTATUS(ret)
776 756
777 757 if proc.timeout:
778 758 ret = 'timeout'
779 759
780 760 if ret:
781 761 killdaemons()
782 762
783 763 for s, r in replacements:
784 764 output = re.sub(s, r, output)
785 765 return ret, splitnewlines(output)
786 766
787 767 def runone(options, test):
788 768 '''tristate output:
789 769 None -> skipped
790 770 True -> passed
791 771 False -> failed'''
792 772
793 773 global results, resultslock, iolock
794 774
795 775 testpath = os.path.join(TESTDIR, test)
796 776
797 777 def result(l, e):
798 778 resultslock.acquire()
799 779 results[l].append(e)
800 780 resultslock.release()
801 781
802 782 def skip(msg):
803 783 if not options.verbose:
804 784 result('s', (test, msg))
805 785 else:
806 786 iolock.acquire()
807 787 print "\nSkipping %s: %s" % (testpath, msg)
808 788 iolock.release()
809 789 return None
810 790
811 791 def fail(msg, ret):
812 792 if not options.nodiff:
813 793 iolock.acquire()
814 794 print "\nERROR: %s %s" % (testpath, msg)
815 795 iolock.release()
816 796 if (not ret and options.interactive
817 797 and os.path.exists(testpath + ".err")):
818 798 iolock.acquire()
819 799 print "Accept this change? [n] ",
820 800 answer = sys.stdin.readline().strip()
821 801 iolock.release()
822 802 if answer.lower() in "y yes".split():
823 803 if test.endswith(".t"):
824 804 rename(testpath + ".err", testpath)
825 805 else:
826 806 rename(testpath + ".err", testpath + ".out")
827 807 result('p', test)
828 808 return
829 809 result('f', (test, msg))
830 810
831 811 def success():
832 812 result('p', test)
833 813
834 814 def ignore(msg):
835 815 result('i', (test, msg))
836 816
837 817 if (os.path.basename(test).startswith("test-") and '~' not in test and
838 818 ('.' not in test or test.endswith('.py') or
839 819 test.endswith('.bat') or test.endswith('.t'))):
840 820 if not os.path.exists(test):
841 821 skip("doesn't exist")
842 822 return None
843 823 else:
844 824 vlog('# Test file', test, 'not supported, ignoring')
845 825 return None # not a supported test, don't record
846 826
847 827 if not (options.whitelisted and test in options.whitelisted):
848 828 if options.blacklist and test in options.blacklist:
849 829 skip("blacklisted")
850 830 return None
851 831
852 832 if options.retest and not os.path.exists(test + ".err"):
853 833 ignore("not retesting")
854 834 return None
855 835
856 836 if options.keywords:
857 837 fp = open(test)
858 838 t = fp.read().lower() + test.lower()
859 839 fp.close()
860 840 for k in options.keywords.lower().split():
861 841 if k in t:
862 842 break
863 843 else:
864 844 ignore("doesn't match keyword")
865 845 return None
866 846
867 847 vlog("# Test", test)
868 848
869 849 # create a fresh hgrc
870 850 hgrc = open(HGRCPATH, 'w+')
871 851 hgrc.write('[ui]\n')
872 852 hgrc.write('slash = True\n')
873 853 hgrc.write('[defaults]\n')
874 854 hgrc.write('backout = -d "0 0"\n')
875 855 hgrc.write('commit = -d "0 0"\n')
876 856 hgrc.write('tag = -d "0 0"\n')
877 857 if options.inotify:
878 858 hgrc.write('[extensions]\n')
879 859 hgrc.write('inotify=\n')
880 860 hgrc.write('[inotify]\n')
881 861 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
882 862 hgrc.write('appendpid=True\n')
883 863 if options.extra_config_opt:
884 864 for opt in options.extra_config_opt:
885 865 section, key = opt.split('.', 1)
886 866 assert '=' in key, ('extra config opt %s must '
887 867 'have an = for assignment' % opt)
888 868 hgrc.write('[%s]\n%s\n' % (section, key))
889 869 hgrc.close()
890 870
891 871 ref = os.path.join(TESTDIR, test+".out")
892 872 err = os.path.join(TESTDIR, test+".err")
893 873 if os.path.exists(err):
894 874 os.remove(err) # Remove any previous output files
895 875 try:
896 876 tf = open(testpath)
897 877 firstline = tf.readline().rstrip()
898 878 tf.close()
899 879 except IOError:
900 880 firstline = ''
901 881 lctest = test.lower()
902 882
903 883 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
904 884 runner = pytest
905 885 elif lctest.endswith('.t'):
906 886 runner = tsttest
907 887 ref = testpath
908 888 else:
909 889 # do not try to run non-executable programs
910 890 if not os.access(testpath, os.X_OK):
911 891 return skip("not executable")
912 892 runner = shtest
913 893
914 894 # Make a tmp subdirectory to work in
915 895 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
916 896 os.path.join(HGTMP, os.path.basename(test))
917 897
918 898 replacements = [
919 899 (r':%s\b' % options.port, ':$HGPORT'),
920 900 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
921 901 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
922 902 ]
923 903 if os.name == 'nt':
924 904 replacements.append((r'\r\n', '\n'))
925 905 replacements.append(
926 906 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
927 907 c in '/\\' and r'[/\\]' or
928 908 c.isdigit() and c or
929 909 '\\' + c
930 910 for c in testtmp), '$TESTTMP'))
931 911 else:
932 912 replacements.append((re.escape(testtmp), '$TESTTMP'))
933 913
934 914 os.mkdir(testtmp)
935 915 ret, out = runner(testpath, testtmp, options, replacements)
936 916 vlog("# Ret was:", ret)
937 917
938 918 mark = '.'
939 919
940 920 skipped = (ret == SKIPPED_STATUS)
941 921
942 922 # If we're not in --debug mode and reference output file exists,
943 923 # check test output against it.
944 924 if options.debug:
945 925 refout = None # to match "out is None"
946 926 elif os.path.exists(ref):
947 927 f = open(ref, "r")
948 928 refout = list(splitnewlines(f.read()))
949 929 f.close()
950 930 else:
951 931 refout = []
952 932
953 933 if (ret != 0 or out != refout) and not skipped and not options.debug:
954 934 # Save errors to a file for diagnosis
955 935 f = open(err, "wb")
956 936 for line in out:
957 937 f.write(line)
958 938 f.close()
959 939
960 940 if skipped:
961 941 mark = 's'
962 942 if out is None: # debug mode: nothing to parse
963 943 missing = ['unknown']
964 944 failed = None
965 945 else:
966 946 missing, failed = parsehghaveoutput(out)
967 947 if not missing:
968 948 missing = ['irrelevant']
969 949 if failed:
970 950 fail("hghave failed checking for %s" % failed[-1], ret)
971 951 skipped = False
972 952 else:
973 953 skip(missing[-1])
974 954 elif ret == 'timeout':
975 955 mark = 't'
976 956 fail("timed out", ret)
977 957 elif out != refout:
978 958 mark = '!'
979 959 if not options.nodiff:
980 960 iolock.acquire()
981 961 if options.view:
982 962 os.system("%s %s %s" % (options.view, ref, err))
983 963 else:
984 964 showdiff(refout, out, ref, err)
985 965 iolock.release()
986 966 if ret:
987 967 fail("output changed and returned error code %d" % ret, ret)
988 968 else:
989 969 fail("output changed", ret)
990 970 ret = 1
991 971 elif ret:
992 972 mark = '!'
993 973 fail("returned error code %d" % ret, ret)
994 974 else:
995 975 success()
996 976
997 977 if not options.verbose:
998 978 iolock.acquire()
999 979 sys.stdout.write(mark)
1000 980 sys.stdout.flush()
1001 981 iolock.release()
1002 982
1003 983 killdaemons()
1004 984
1005 985 if not options.keep_tmpdir:
1006 986 shutil.rmtree(testtmp, True)
1007 987 if skipped:
1008 988 return None
1009 989 return ret == 0
1010 990
1011 991 _hgpath = None
1012 992
1013 993 def _gethgpath():
1014 994 """Return the path to the mercurial package that is actually found by
1015 995 the current Python interpreter."""
1016 996 global _hgpath
1017 997 if _hgpath is not None:
1018 998 return _hgpath
1019 999
1020 1000 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
1021 1001 pipe = os.popen(cmd % PYTHON)
1022 1002 try:
1023 1003 _hgpath = pipe.read().strip()
1024 1004 finally:
1025 1005 pipe.close()
1026 1006 return _hgpath
1027 1007
1028 1008 def _checkhglib(verb):
1029 1009 """Ensure that the 'mercurial' package imported by python is
1030 1010 the one we expect it to be. If not, print a warning to stderr."""
1031 1011 expecthg = os.path.join(PYTHONDIR, 'mercurial')
1032 1012 actualhg = _gethgpath()
1033 1013 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
1034 1014 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
1035 1015 ' (expected %s)\n'
1036 1016 % (verb, actualhg, expecthg))
1037 1017
1038 1018 def runchildren(options, tests):
1039 1019 if INST:
1040 1020 installhg(options)
1041 1021 _checkhglib("Testing")
1042 1022
1043 1023 optcopy = dict(options.__dict__)
1044 1024 optcopy['jobs'] = 1
1045 1025
1046 1026 # Because whitelist has to override keyword matches, we have to
1047 1027 # actually load the whitelist in the children as well, so we allow
1048 1028 # the list of whitelist files to pass through and be parsed in the
1049 1029 # children, but not the dict of whitelisted tests resulting from
1050 1030 # the parse, used here to override blacklisted tests.
1051 1031 whitelist = optcopy['whitelisted'] or []
1052 1032 del optcopy['whitelisted']
1053 1033
1054 1034 blacklist = optcopy['blacklist'] or []
1055 1035 del optcopy['blacklist']
1056 1036 blacklisted = []
1057 1037
1058 1038 if optcopy['with_hg'] is None:
1059 1039 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
1060 1040 optcopy.pop('anycoverage', None)
1061 1041
1062 1042 opts = []
1063 1043 for opt, value in optcopy.iteritems():
1064 1044 name = '--' + opt.replace('_', '-')
1065 1045 if value is True:
1066 1046 opts.append(name)
1067 1047 elif isinstance(value, list):
1068 1048 for v in value:
1069 1049 opts.append(name + '=' + str(v))
1070 1050 elif value is not None:
1071 1051 opts.append(name + '=' + str(value))
1072 1052
1073 1053 tests.reverse()
1074 1054 jobs = [[] for j in xrange(options.jobs)]
1075 1055 while tests:
1076 1056 for job in jobs:
1077 1057 if not tests:
1078 1058 break
1079 1059 test = tests.pop()
1080 1060 if test not in whitelist and test in blacklist:
1081 1061 blacklisted.append(test)
1082 1062 else:
1083 1063 job.append(test)
1084 1064 fps = {}
1085 1065
1086 1066 for j, job in enumerate(jobs):
1087 1067 if not job:
1088 1068 continue
1089 1069 rfd, wfd = os.pipe()
1090 1070 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
1091 1071 childtmp = os.path.join(HGTMP, 'child%d' % j)
1092 1072 childopts += ['--tmpdir', childtmp]
1093 1073 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
1094 1074 vlog(' '.join(cmdline))
1095 1075 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
1096 1076 os.close(wfd)
1097 1077 signal.signal(signal.SIGINT, signal.SIG_IGN)
1098 1078 failures = 0
1099 1079 tested, skipped, failed = 0, 0, 0
1100 1080 skips = []
1101 1081 fails = []
1102 1082 while fps:
1103 1083 pid, status = os.wait()
1104 1084 fp = fps.pop(pid)
1105 1085 l = fp.read().splitlines()
1106 1086 try:
1107 1087 test, skip, fail = map(int, l[:3])
1108 1088 except ValueError:
1109 1089 test, skip, fail = 0, 0, 0
1110 1090 split = -fail or len(l)
1111 1091 for s in l[3:split]:
1112 1092 skips.append(s.split(" ", 1))
1113 1093 for s in l[split:]:
1114 1094 fails.append(s.split(" ", 1))
1115 1095 tested += test
1116 1096 skipped += skip
1117 1097 failed += fail
1118 1098 vlog('pid %d exited, status %d' % (pid, status))
1119 1099 failures |= status
1120 1100 print
1121 1101 skipped += len(blacklisted)
1122 1102 if not options.noskips:
1123 1103 for s in skips:
1124 1104 print "Skipped %s: %s" % (s[0], s[1])
1125 1105 for s in blacklisted:
1126 1106 print "Skipped %s: blacklisted" % s
1127 1107 for s in fails:
1128 1108 print "Failed %s: %s" % (s[0], s[1])
1129 1109
1130 1110 _checkhglib("Tested")
1131 1111 print "# Ran %d tests, %d skipped, %d failed." % (
1132 1112 tested, skipped, failed)
1133 1113
1134 1114 if options.anycoverage:
1135 1115 outputcoverage(options)
1136 1116 sys.exit(failures != 0)
1137 1117
1138 1118 results = dict(p=[], f=[], s=[], i=[])
1139 1119 resultslock = threading.Lock()
1140 1120 iolock = threading.Lock()
1141 1121
1142 1122 def runqueue(options, tests, results):
1143 1123 for test in tests:
1144 1124 ret = runone(options, test)
1145 1125 if options.first and ret is not None and not ret:
1146 1126 break
1147 1127
1148 1128 def runtests(options, tests):
1149 1129 global DAEMON_PIDS, HGRCPATH
1150 1130 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1151 1131 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1152 1132
1153 1133 try:
1154 1134 if INST:
1155 1135 installhg(options)
1156 1136 _checkhglib("Testing")
1157 1137
1158 1138 if options.restart:
1159 1139 orig = list(tests)
1160 1140 while tests:
1161 1141 if os.path.exists(tests[0] + ".err"):
1162 1142 break
1163 1143 tests.pop(0)
1164 1144 if not tests:
1165 1145 print "running all tests"
1166 1146 tests = orig
1167 1147
1168 1148 runqueue(options, tests, results)
1169 1149
1170 1150 failed = len(results['f'])
1171 1151 tested = len(results['p']) + failed
1172 1152 skipped = len(results['s'])
1173 1153 ignored = len(results['i'])
1174 1154
1175 1155 if options.child:
1176 1156 fp = os.fdopen(options.child, 'w')
1177 1157 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1178 1158 for s in results['s']:
1179 1159 fp.write("%s %s\n" % s)
1180 1160 for s in results['f']:
1181 1161 fp.write("%s %s\n" % s)
1182 1162 fp.close()
1183 1163 else:
1184 1164 print
1185 1165 for s in results['s']:
1186 1166 print "Skipped %s: %s" % s
1187 1167 for s in results['f']:
1188 1168 print "Failed %s: %s" % s
1189 1169 _checkhglib("Tested")
1190 1170 print "# Ran %d tests, %d skipped, %d failed." % (
1191 1171 tested, skipped + ignored, failed)
1192 1172
1193 1173 if options.anycoverage:
1194 1174 outputcoverage(options)
1195 1175 except KeyboardInterrupt:
1196 1176 failed = True
1197 1177 print "\ninterrupted!"
1198 1178
1199 1179 if failed:
1200 1180 sys.exit(1)
1201 1181
1202 1182 def main():
1203 1183 (options, args) = parseargs()
1204 1184 if not options.child:
1205 1185 os.umask(022)
1206 1186
1207 1187 checktools()
1208 1188
1209 1189 if len(args) == 0:
1210 1190 args = os.listdir(".")
1211 1191 args.sort()
1212 1192
1213 1193 tests = args
1214 1194
1215 1195 # Reset some environment variables to well-known values so that
1216 1196 # the tests produce repeatable output.
1217 1197 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1218 1198 os.environ['TZ'] = 'GMT'
1219 1199 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1220 1200 os.environ['CDPATH'] = ''
1221 1201 os.environ['COLUMNS'] = '80'
1222 1202 os.environ['GREP_OPTIONS'] = ''
1223 1203 os.environ['http_proxy'] = ''
1224 1204 os.environ['no_proxy'] = ''
1225 1205 os.environ['NO_PROXY'] = ''
1226 1206 os.environ['TERM'] = 'xterm'
1227 1207
1228 1208 # unset env related to hooks
1229 1209 for k in os.environ.keys():
1230 1210 if k.startswith('HG_'):
1231 1211 # can't remove on solaris
1232 1212 os.environ[k] = ''
1233 1213 del os.environ[k]
1234 1214
1235 1215 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1236 1216 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1237 1217 if options.tmpdir:
1238 1218 options.keep_tmpdir = True
1239 1219 tmpdir = options.tmpdir
1240 1220 if os.path.exists(tmpdir):
1241 1221 # Meaning of tmpdir has changed since 1.3: we used to create
1242 1222 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1243 1223 # tmpdir already exists.
1244 1224 sys.exit("error: temp dir %r already exists" % tmpdir)
1245 1225
1246 1226 # Automatically removing tmpdir sounds convenient, but could
1247 1227 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1248 1228 # or "--tmpdir=$HOME".
1249 1229 #vlog("# Removing temp dir", tmpdir)
1250 1230 #shutil.rmtree(tmpdir)
1251 1231 os.makedirs(tmpdir)
1252 1232 else:
1253 1233 d = None
1254 1234 if os.name == 'nt':
1255 1235 # without this, we get the default temp dir location, but
1256 1236 # in all lowercase, which causes troubles with paths (issue3490)
1257 1237 d = os.getenv('TMP')
1258 1238 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1259 1239 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1260 1240 DAEMON_PIDS = None
1261 1241 HGRCPATH = None
1262 1242
1263 1243 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1264 1244 os.environ["HGMERGE"] = "internal:merge"
1265 1245 os.environ["HGUSER"] = "test"
1266 1246 os.environ["HGENCODING"] = "ascii"
1267 1247 os.environ["HGENCODINGMODE"] = "strict"
1268 1248 os.environ["HGPORT"] = str(options.port)
1269 1249 os.environ["HGPORT1"] = str(options.port + 1)
1270 1250 os.environ["HGPORT2"] = str(options.port + 2)
1271 1251
1272 1252 if options.with_hg:
1273 1253 INST = None
1274 1254 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1275 1255
1276 1256 # This looks redundant with how Python initializes sys.path from
1277 1257 # the location of the script being executed. Needed because the
1278 1258 # "hg" specified by --with-hg is not the only Python script
1279 1259 # executed in the test suite that needs to import 'mercurial'
1280 1260 # ... which means it's not really redundant at all.
1281 1261 PYTHONDIR = BINDIR
1282 1262 else:
1283 1263 INST = os.path.join(HGTMP, "install")
1284 1264 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1285 1265 PYTHONDIR = os.path.join(INST, "lib", "python")
1286 1266
1287 1267 os.environ["BINDIR"] = BINDIR
1288 1268 os.environ["PYTHON"] = PYTHON
1289 1269
1290 1270 if not options.child:
1291 1271 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1292 1272 os.environ["PATH"] = os.pathsep.join(path)
1293 1273
1294 1274 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1295 1275 # can run .../tests/run-tests.py test-foo where test-foo
1296 1276 # adds an extension to HGRC
1297 1277 pypath = [PYTHONDIR, TESTDIR]
1298 1278 # We have to augment PYTHONPATH, rather than simply replacing
1299 1279 # it, in case external libraries are only available via current
1300 1280 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1301 1281 # are in /opt/subversion.)
1302 1282 oldpypath = os.environ.get(IMPL_PATH)
1303 1283 if oldpypath:
1304 1284 pypath.append(oldpypath)
1305 1285 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1306 1286
1307 1287 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1308 1288
1309 1289 vlog("# Using TESTDIR", TESTDIR)
1310 1290 vlog("# Using HGTMP", HGTMP)
1311 1291 vlog("# Using PATH", os.environ["PATH"])
1312 1292 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1313 1293
1314 1294 try:
1315 1295 if len(tests) > 1 and options.jobs > 1:
1316 1296 runchildren(options, tests)
1317 1297 else:
1318 1298 runtests(options, tests)
1319 1299 finally:
1320 1300 time.sleep(.1)
1321 1301 cleanup(options)
1322 1302
1323 1303 if __name__ == '__main__':
1324 1304 main()
General Comments 0
You need to be logged in to leave comments. Login now