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