##// END OF EJS Templates
run-tests: simplify interrupted message
Matt Mackall -
r19304:59d5281b default
parent child Browse files
Show More
@@ -1,1258 +1,1257 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 # Modifying this script is tricky because it has many modes:
11 11 # - serial (default) vs parallel (-jN, N > 1)
12 12 # - no coverage (default) vs coverage (-c, -C, -s)
13 13 # - temp install (default) vs specific hg script (--with-hg, --local)
14 14 # - tests are a mix of shell scripts and Python scripts
15 15 #
16 16 # If you change this script, it is recommended that you ensure you
17 17 # haven't broken it by running it in various modes with a representative
18 18 # sample of test scripts. For example:
19 19 #
20 20 # 1) serial, no coverage, temp install:
21 21 # ./run-tests.py test-s*
22 22 # 2) serial, no coverage, local hg:
23 23 # ./run-tests.py --local test-s*
24 24 # 3) serial, coverage, temp install:
25 25 # ./run-tests.py -c test-s*
26 26 # 4) serial, coverage, local hg:
27 27 # ./run-tests.py -c --local test-s* # unsupported
28 28 # 5) parallel, no coverage, temp install:
29 29 # ./run-tests.py -j2 test-s*
30 30 # 6) parallel, no coverage, local hg:
31 31 # ./run-tests.py -j2 --local test-s*
32 32 # 7) parallel, coverage, temp install:
33 33 # ./run-tests.py -j2 -c test-s* # currently broken
34 34 # 8) parallel, coverage, local install:
35 35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
36 36 # 9) parallel, custom tmp dir:
37 37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
38 38 #
39 39 # (You could use any subset of the tests: test-s* happens to match
40 40 # enough that it's worth doing parallel runs, few enough that it
41 41 # completes fairly quickly, includes both shell and Python scripts, and
42 42 # includes some scripts that run daemon processes.)
43 43
44 44 from distutils import version
45 45 import difflib
46 46 import errno
47 47 import optparse
48 48 import os
49 49 import shutil
50 50 import subprocess
51 51 import signal
52 52 import sys
53 53 import tempfile
54 54 import time
55 55 import random
56 56 import re
57 57 import threading
58 58 import killdaemons as killmod
59 59 import cPickle as pickle
60 60 import Queue as queue
61 61
62 62 processlock = threading.Lock()
63 63
64 64 closefds = os.name == 'posix'
65 65 def Popen4(cmd, wd, timeout, env=None):
66 66 processlock.acquire()
67 67 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd, env=env,
68 68 close_fds=closefds,
69 69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 70 stderr=subprocess.STDOUT)
71 71 processlock.release()
72 72
73 73 p.fromchild = p.stdout
74 74 p.tochild = p.stdin
75 75 p.childerr = p.stderr
76 76
77 77 p.timeout = False
78 78 if timeout:
79 79 def t():
80 80 start = time.time()
81 81 while time.time() - start < timeout and p.returncode is None:
82 82 time.sleep(.1)
83 83 p.timeout = True
84 84 if p.returncode is None:
85 85 terminate(p)
86 86 threading.Thread(target=t).start()
87 87
88 88 return p
89 89
90 90 # reserved exit code to skip test (used by hghave)
91 91 SKIPPED_STATUS = 80
92 92 SKIPPED_PREFIX = 'skipped: '
93 93 FAILED_PREFIX = 'hghave check failed: '
94 94 PYTHON = sys.executable.replace('\\', '/')
95 95 IMPL_PATH = 'PYTHONPATH'
96 96 if 'java' in sys.platform:
97 97 IMPL_PATH = 'JYTHONPATH'
98 98
99 99 requiredtools = [os.path.basename(sys.executable), "diff", "grep", "unzip",
100 100 "gunzip", "bunzip2", "sed"]
101 101
102 102 defaults = {
103 103 'jobs': ('HGTEST_JOBS', 1),
104 104 'timeout': ('HGTEST_TIMEOUT', 180),
105 105 'port': ('HGTEST_PORT', 20059),
106 106 'shell': ('HGTEST_SHELL', 'sh'),
107 107 }
108 108
109 109 def parselistfiles(files, listtype, warn=True):
110 110 entries = dict()
111 111 for filename in files:
112 112 try:
113 113 path = os.path.expanduser(os.path.expandvars(filename))
114 114 f = open(path, "r")
115 115 except IOError, err:
116 116 if err.errno != errno.ENOENT:
117 117 raise
118 118 if warn:
119 119 print "warning: no such %s file: %s" % (listtype, filename)
120 120 continue
121 121
122 122 for line in f.readlines():
123 123 line = line.split('#', 1)[0].strip()
124 124 if line:
125 125 entries[line] = filename
126 126
127 127 f.close()
128 128 return entries
129 129
130 130 def parseargs():
131 131 parser = optparse.OptionParser("%prog [options] [tests]")
132 132
133 133 # keep these sorted
134 134 parser.add_option("--blacklist", action="append",
135 135 help="skip tests listed in the specified blacklist file")
136 136 parser.add_option("--whitelist", action="append",
137 137 help="always run tests listed in the specified whitelist file")
138 138 parser.add_option("-C", "--annotate", action="store_true",
139 139 help="output files annotated with coverage")
140 140 parser.add_option("-c", "--cover", action="store_true",
141 141 help="print a test coverage report")
142 142 parser.add_option("-d", "--debug", action="store_true",
143 143 help="debug mode: write output of test scripts to console"
144 144 " rather than capturing and diff'ing it (disables timeout)")
145 145 parser.add_option("-f", "--first", action="store_true",
146 146 help="exit on the first test failure")
147 147 parser.add_option("-H", "--htmlcov", action="store_true",
148 148 help="create an HTML report of the coverage of the files")
149 149 parser.add_option("--inotify", action="store_true",
150 150 help="enable inotify extension when running tests")
151 151 parser.add_option("-i", "--interactive", action="store_true",
152 152 help="prompt to accept changed output")
153 153 parser.add_option("-j", "--jobs", type="int",
154 154 help="number of jobs to run in parallel"
155 155 " (default: $%s or %d)" % defaults['jobs'])
156 156 parser.add_option("--keep-tmpdir", action="store_true",
157 157 help="keep temporary directory after running tests")
158 158 parser.add_option("-k", "--keywords",
159 159 help="run tests matching keywords")
160 160 parser.add_option("-l", "--local", action="store_true",
161 161 help="shortcut for --with-hg=<testdir>/../hg")
162 162 parser.add_option("--loop", action="store_true",
163 163 help="loop tests repeatedly")
164 164 parser.add_option("-n", "--nodiff", action="store_true",
165 165 help="skip showing test changes")
166 166 parser.add_option("-p", "--port", type="int",
167 167 help="port on which servers should listen"
168 168 " (default: $%s or %d)" % defaults['port'])
169 169 parser.add_option("--compiler", type="string",
170 170 help="compiler to build with")
171 171 parser.add_option("--pure", action="store_true",
172 172 help="use pure Python code instead of C extensions")
173 173 parser.add_option("-R", "--restart", action="store_true",
174 174 help="restart at last error")
175 175 parser.add_option("-r", "--retest", action="store_true",
176 176 help="retest failed tests")
177 177 parser.add_option("-S", "--noskips", action="store_true",
178 178 help="don't report skip tests verbosely")
179 179 parser.add_option("--shell", type="string",
180 180 help="shell to use (default: $%s or %s)" % defaults['shell'])
181 181 parser.add_option("-t", "--timeout", type="int",
182 182 help="kill errant tests after TIMEOUT seconds"
183 183 " (default: $%s or %d)" % defaults['timeout'])
184 184 parser.add_option("--time", action="store_true",
185 185 help="time how long each test takes")
186 186 parser.add_option("--tmpdir", type="string",
187 187 help="run tests in the given temporary directory"
188 188 " (implies --keep-tmpdir)")
189 189 parser.add_option("-v", "--verbose", action="store_true",
190 190 help="output verbose messages")
191 191 parser.add_option("--view", type="string",
192 192 help="external diff viewer")
193 193 parser.add_option("--with-hg", type="string",
194 194 metavar="HG",
195 195 help="test using specified hg script rather than a "
196 196 "temporary installation")
197 197 parser.add_option("-3", "--py3k-warnings", action="store_true",
198 198 help="enable Py3k warnings on Python 2.6+")
199 199 parser.add_option('--extra-config-opt', action="append",
200 200 help='set the given config opt in the test hgrc')
201 201 parser.add_option('--random', action="store_true",
202 202 help='run tests in random order')
203 203
204 204 for option, (envvar, default) in defaults.items():
205 205 defaults[option] = type(default)(os.environ.get(envvar, default))
206 206 parser.set_defaults(**defaults)
207 207 (options, args) = parser.parse_args()
208 208
209 209 # jython is always pure
210 210 if 'java' in sys.platform or '__pypy__' in sys.modules:
211 211 options.pure = True
212 212
213 213 if options.with_hg:
214 214 options.with_hg = os.path.expanduser(options.with_hg)
215 215 if not (os.path.isfile(options.with_hg) and
216 216 os.access(options.with_hg, os.X_OK)):
217 217 parser.error('--with-hg must specify an executable hg script')
218 218 if not os.path.basename(options.with_hg) == 'hg':
219 219 sys.stderr.write('warning: --with-hg should specify an hg script\n')
220 220 if options.local:
221 221 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
222 222 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
223 223 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
224 224 parser.error('--local specified, but %r not found or not executable'
225 225 % hgbin)
226 226 options.with_hg = hgbin
227 227
228 228 options.anycoverage = options.cover or options.annotate or options.htmlcov
229 229 if options.anycoverage:
230 230 try:
231 231 import coverage
232 232 covver = version.StrictVersion(coverage.__version__).version
233 233 if covver < (3, 3):
234 234 parser.error('coverage options require coverage 3.3 or later')
235 235 except ImportError:
236 236 parser.error('coverage options now require the coverage package')
237 237
238 238 if options.anycoverage and options.local:
239 239 # this needs some path mangling somewhere, I guess
240 240 parser.error("sorry, coverage options do not work when --local "
241 241 "is specified")
242 242
243 243 global verbose
244 244 if options.verbose:
245 245 verbose = ''
246 246
247 247 if options.tmpdir:
248 248 options.tmpdir = os.path.expanduser(options.tmpdir)
249 249
250 250 if options.jobs < 1:
251 251 parser.error('--jobs must be positive')
252 252 if options.interactive and options.debug:
253 253 parser.error("-i/--interactive and -d/--debug are incompatible")
254 254 if options.debug:
255 255 if options.timeout != defaults['timeout']:
256 256 sys.stderr.write(
257 257 'warning: --timeout option ignored with --debug\n')
258 258 options.timeout = 0
259 259 if options.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 ignore("not retesting")
908 908 return None
909 909
910 910 if options.keywords:
911 911 fp = open(test)
912 912 t = fp.read().lower() + test.lower()
913 913 fp.close()
914 914 for k in options.keywords.lower().split():
915 915 if k in t:
916 916 break
917 917 else:
918 918 ignore("doesn't match keyword")
919 919 return None
920 920
921 921 for ext, func, out in testtypes:
922 922 if lctest.startswith("test-") and lctest.endswith(ext):
923 923 runner = func
924 924 ref = os.path.join(TESTDIR, test + out)
925 925 break
926 926 else:
927 927 return skip("unknown test type")
928 928
929 929 vlog("# Test", test)
930 930
931 931 if os.path.exists(err):
932 932 os.remove(err) # Remove any previous output files
933 933
934 934 # Make a tmp subdirectory to work in
935 935 threadtmp = os.path.join(HGTMP, "child%d" % count)
936 936 testtmp = os.path.join(threadtmp, os.path.basename(test))
937 937 os.mkdir(threadtmp)
938 938 os.mkdir(testtmp)
939 939
940 940 port = options.port + count * 3
941 941 replacements = [
942 942 (r':%s\b' % port, ':$HGPORT'),
943 943 (r':%s\b' % (port + 1), ':$HGPORT1'),
944 944 (r':%s\b' % (port + 2), ':$HGPORT2'),
945 945 ]
946 946 if os.name == 'nt':
947 947 replacements.append(
948 948 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
949 949 c in '/\\' and r'[/\\]' or
950 950 c.isdigit() and c or
951 951 '\\' + c
952 952 for c in testtmp), '$TESTTMP'))
953 953 else:
954 954 replacements.append((re.escape(testtmp), '$TESTTMP'))
955 955
956 956 env = createenv(options, testtmp, threadtmp, port)
957 957 createhgrc(env['HGRCPATH'], options)
958 958
959 959 starttime = time.time()
960 960 try:
961 961 ret, out = runner(testpath, testtmp, options, replacements, env)
962 962 except KeyboardInterrupt:
963 963 endtime = time.time()
964 msg = '(after %d seconds)' % (endtime - starttime)
965 log('INTERRUPTED:', test, msg)
964 log('INTERRUPTED: %s (after %d seconds)' % (test, endtime - starttime))
966 965 raise
967 966 endtime = time.time()
968 967 times.append((test, endtime - starttime))
969 968 vlog("# Ret was:", ret)
970 969
971 970 killdaemons(env['DAEMON_PIDS'])
972 971
973 972 skipped = (ret == SKIPPED_STATUS)
974 973
975 974 # If we're not in --debug mode and reference output file exists,
976 975 # check test output against it.
977 976 if options.debug:
978 977 refout = None # to match "out is None"
979 978 elif os.path.exists(ref):
980 979 f = open(ref, "r")
981 980 refout = f.read().splitlines(True)
982 981 f.close()
983 982 else:
984 983 refout = []
985 984
986 985 if (ret != 0 or out != refout) and not skipped and not options.debug:
987 986 # Save errors to a file for diagnosis
988 987 f = open(err, "wb")
989 988 for line in out:
990 989 f.write(line)
991 990 f.close()
992 991
993 992 if skipped:
994 993 if out is None: # debug mode: nothing to parse
995 994 missing = ['unknown']
996 995 failed = None
997 996 else:
998 997 missing, failed = parsehghaveoutput(out)
999 998 if not missing:
1000 999 missing = ['irrelevant']
1001 1000 if failed:
1002 1001 result = fail("hghave failed checking for %s" % failed[-1], ret)
1003 1002 skipped = False
1004 1003 else:
1005 1004 result = skip(missing[-1])
1006 1005 elif ret == 'timeout':
1007 1006 result = fail("timed out", ret)
1008 1007 elif out != refout:
1009 1008 if not options.nodiff:
1010 1009 iolock.acquire()
1011 1010 if options.view:
1012 1011 os.system("%s %s %s" % (options.view, ref, err))
1013 1012 else:
1014 1013 showdiff(refout, out, ref, err)
1015 1014 iolock.release()
1016 1015 if ret:
1017 1016 result = fail("output changed and " + describe(ret), ret)
1018 1017 else:
1019 1018 result = fail("output changed", ret)
1020 1019 elif ret:
1021 1020 result = fail(describe(ret), ret)
1022 1021 else:
1023 1022 result = success()
1024 1023
1025 1024 if not options.verbose:
1026 1025 iolock.acquire()
1027 1026 sys.stdout.write(result[0])
1028 1027 sys.stdout.flush()
1029 1028 iolock.release()
1030 1029
1031 1030 if not options.keep_tmpdir:
1032 1031 shutil.rmtree(threadtmp, True)
1033 1032 return result
1034 1033
1035 1034 _hgpath = None
1036 1035
1037 1036 def _gethgpath():
1038 1037 """Return the path to the mercurial package that is actually found by
1039 1038 the current Python interpreter."""
1040 1039 global _hgpath
1041 1040 if _hgpath is not None:
1042 1041 return _hgpath
1043 1042
1044 1043 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
1045 1044 pipe = os.popen(cmd % PYTHON)
1046 1045 try:
1047 1046 _hgpath = pipe.read().strip()
1048 1047 finally:
1049 1048 pipe.close()
1050 1049 return _hgpath
1051 1050
1052 1051 def _checkhglib(verb):
1053 1052 """Ensure that the 'mercurial' package imported by python is
1054 1053 the one we expect it to be. If not, print a warning to stderr."""
1055 1054 expecthg = os.path.join(PYTHONDIR, 'mercurial')
1056 1055 actualhg = _gethgpath()
1057 1056 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
1058 1057 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
1059 1058 ' (expected %s)\n'
1060 1059 % (verb, actualhg, expecthg))
1061 1060
1062 1061 results = {'.':[], '!':[], 's':[], 'i':[]}
1063 1062 times = []
1064 1063 iolock = threading.Lock()
1065 1064 abort = False
1066 1065
1067 1066 def scheduletests(options, tests):
1068 1067 jobs = options.jobs
1069 1068 done = queue.Queue()
1070 1069 running = 0
1071 1070 count = 0
1072 1071 global abort
1073 1072
1074 1073 def job(test, count):
1075 1074 try:
1076 1075 done.put(runone(options, test, count))
1077 1076 except KeyboardInterrupt:
1078 1077 pass
1079 1078
1080 1079 try:
1081 1080 while tests or running:
1082 1081 if not done.empty() or running == jobs or not tests:
1083 1082 try:
1084 1083 code, test, msg = done.get(True, 1)
1085 1084 results[code].append((test, msg))
1086 1085 if options.first and code not in '.si':
1087 1086 break
1088 1087 except queue.Empty:
1089 1088 continue
1090 1089 running -= 1
1091 1090 if tests and not running == jobs:
1092 1091 test = tests.pop(0)
1093 1092 if options.loop:
1094 1093 tests.append(test)
1095 1094 t = threading.Thread(None, job, args=(test, count))
1096 1095 t.start()
1097 1096 running += 1
1098 1097 count += 1
1099 1098 except KeyboardInterrupt:
1100 1099 abort = True
1101 1100
1102 1101 def runtests(options, tests):
1103 1102 try:
1104 1103 if INST:
1105 1104 installhg(options)
1106 1105 _checkhglib("Testing")
1107 1106 else:
1108 1107 usecorrectpython()
1109 1108
1110 1109 if options.restart:
1111 1110 orig = list(tests)
1112 1111 while tests:
1113 1112 if os.path.exists(tests[0] + ".err"):
1114 1113 break
1115 1114 tests.pop(0)
1116 1115 if not tests:
1117 1116 print "running all tests"
1118 1117 tests = orig
1119 1118
1120 1119 scheduletests(options, tests)
1121 1120
1122 1121 failed = len(results['!'])
1123 1122 tested = len(results['.']) + failed
1124 1123 skipped = len(results['s'])
1125 1124 ignored = len(results['i'])
1126 1125
1127 1126 print
1128 1127 if not options.noskips:
1129 1128 for s in results['s']:
1130 1129 print "Skipped %s: %s" % s
1131 1130 for s in results['!']:
1132 1131 print "Failed %s: %s" % s
1133 1132 _checkhglib("Tested")
1134 1133 print "# Ran %d tests, %d skipped, %d failed." % (
1135 1134 tested, skipped + ignored, failed)
1136 1135 if options.time:
1137 1136 outputtimes(options)
1138 1137
1139 1138 if options.anycoverage:
1140 1139 outputcoverage(options)
1141 1140 except KeyboardInterrupt:
1142 1141 failed = True
1143 1142 print "\ninterrupted!"
1144 1143
1145 1144 if failed:
1146 1145 sys.exit(1)
1147 1146
1148 1147 testtypes = [('.py', pytest, '.out'),
1149 1148 ('.t', tsttest, '')]
1150 1149
1151 1150 def main():
1152 1151 (options, args) = parseargs()
1153 1152 os.umask(022)
1154 1153
1155 1154 checktools()
1156 1155
1157 1156 if len(args) == 0:
1158 1157 args = [t for t in os.listdir(".")
1159 1158 if t.startswith("test-")
1160 1159 and (t.endswith(".py") or t.endswith(".t"))]
1161 1160
1162 1161 tests = args
1163 1162
1164 1163 if options.random:
1165 1164 random.shuffle(tests)
1166 1165 else:
1167 1166 # keywords for slow tests
1168 1167 slow = 'svn gendoc check-code-hg'.split()
1169 1168 def sortkey(f):
1170 1169 # run largest tests first, as they tend to take the longest
1171 1170 val = -os.stat(f).st_size
1172 1171 for kw in slow:
1173 1172 if kw in f:
1174 1173 val *= 10
1175 1174 return val
1176 1175 tests.sort(key=sortkey)
1177 1176
1178 1177 if 'PYTHONHASHSEED' not in os.environ:
1179 1178 # use a random python hash seed all the time
1180 1179 # we do the randomness ourself to know what seed is used
1181 1180 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
1182 1181 print 'python hash seed:', os.environ['PYTHONHASHSEED']
1183 1182
1184 1183 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1185 1184 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1186 1185 if options.tmpdir:
1187 1186 options.keep_tmpdir = True
1188 1187 tmpdir = options.tmpdir
1189 1188 if os.path.exists(tmpdir):
1190 1189 # Meaning of tmpdir has changed since 1.3: we used to create
1191 1190 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1192 1191 # tmpdir already exists.
1193 1192 sys.exit("error: temp dir %r already exists" % tmpdir)
1194 1193
1195 1194 # Automatically removing tmpdir sounds convenient, but could
1196 1195 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1197 1196 # or "--tmpdir=$HOME".
1198 1197 #vlog("# Removing temp dir", tmpdir)
1199 1198 #shutil.rmtree(tmpdir)
1200 1199 os.makedirs(tmpdir)
1201 1200 else:
1202 1201 d = None
1203 1202 if os.name == 'nt':
1204 1203 # without this, we get the default temp dir location, but
1205 1204 # in all lowercase, which causes troubles with paths (issue3490)
1206 1205 d = os.getenv('TMP')
1207 1206 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1208 1207 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1209 1208
1210 1209 if options.with_hg:
1211 1210 INST = None
1212 1211 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1213 1212
1214 1213 # This looks redundant with how Python initializes sys.path from
1215 1214 # the location of the script being executed. Needed because the
1216 1215 # "hg" specified by --with-hg is not the only Python script
1217 1216 # executed in the test suite that needs to import 'mercurial'
1218 1217 # ... which means it's not really redundant at all.
1219 1218 PYTHONDIR = BINDIR
1220 1219 else:
1221 1220 INST = os.path.join(HGTMP, "install")
1222 1221 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1223 1222 PYTHONDIR = os.path.join(INST, "lib", "python")
1224 1223
1225 1224 os.environ["BINDIR"] = BINDIR
1226 1225 os.environ["PYTHON"] = PYTHON
1227 1226
1228 1227 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1229 1228 os.environ["PATH"] = os.pathsep.join(path)
1230 1229
1231 1230 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1232 1231 # can run .../tests/run-tests.py test-foo where test-foo
1233 1232 # adds an extension to HGRC
1234 1233 pypath = [PYTHONDIR, TESTDIR]
1235 1234 # We have to augment PYTHONPATH, rather than simply replacing
1236 1235 # it, in case external libraries are only available via current
1237 1236 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1238 1237 # are in /opt/subversion.)
1239 1238 oldpypath = os.environ.get(IMPL_PATH)
1240 1239 if oldpypath:
1241 1240 pypath.append(oldpypath)
1242 1241 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1243 1242
1244 1243 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1245 1244
1246 1245 vlog("# Using TESTDIR", TESTDIR)
1247 1246 vlog("# Using HGTMP", HGTMP)
1248 1247 vlog("# Using PATH", os.environ["PATH"])
1249 1248 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1250 1249
1251 1250 try:
1252 1251 runtests(options, tests)
1253 1252 finally:
1254 1253 time.sleep(.1)
1255 1254 cleanup(options)
1256 1255
1257 1256 if __name__ == '__main__':
1258 1257 main()
General Comments 0
You need to be logged in to leave comments. Login now