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