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