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