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