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