##// END OF EJS Templates
run-tests: make --noskips work
Matt Mackall -
r19294:3c3f6b83 default
parent child Browse files
Show More
@@ -1,1257 +1,1258 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.time:
260 260 sys.stderr.write(
261 261 'warning: --time option ignored with --debug\n')
262 262 options.time = False
263 263 if options.py3k_warnings:
264 264 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
265 265 parser.error('--py3k-warnings can only be used on Python 2.6+')
266 266 if options.blacklist:
267 267 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
268 268 if options.whitelist:
269 269 options.whitelisted = parselistfiles(options.whitelist, 'whitelist')
270 270 else:
271 271 options.whitelisted = {}
272 272
273 273 return (options, args)
274 274
275 275 def rename(src, dst):
276 276 """Like os.rename(), trade atomicity and opened files friendliness
277 277 for existing destination support.
278 278 """
279 279 shutil.copy(src, dst)
280 280 os.remove(src)
281 281
282 282 def parsehghaveoutput(lines):
283 283 '''Parse hghave log lines.
284 284 Return tuple of lists (missing, failed):
285 285 * the missing/unknown features
286 286 * the features for which existence check failed'''
287 287 missing = []
288 288 failed = []
289 289 for line in lines:
290 290 if line.startswith(SKIPPED_PREFIX):
291 291 line = line.splitlines()[0]
292 292 missing.append(line[len(SKIPPED_PREFIX):])
293 293 elif line.startswith(FAILED_PREFIX):
294 294 line = line.splitlines()[0]
295 295 failed.append(line[len(FAILED_PREFIX):])
296 296
297 297 return missing, failed
298 298
299 299 def showdiff(expected, output, ref, err):
300 300 print
301 301 for line in difflib.unified_diff(expected, output, ref, err):
302 302 sys.stdout.write(line)
303 303
304 304 verbose = False
305 305 def vlog(*msg):
306 306 if verbose is not False:
307 307 iolock.acquire()
308 308 if verbose:
309 309 print verbose,
310 310 for m in msg:
311 311 print m,
312 312 print
313 313 sys.stdout.flush()
314 314 iolock.release()
315 315
316 316 def log(*msg):
317 317 iolock.acquire()
318 318 if verbose:
319 319 print verbose,
320 320 for m in msg:
321 321 print m,
322 322 print
323 323 sys.stdout.flush()
324 324 iolock.release()
325 325
326 326 def findprogram(program):
327 327 """Search PATH for a executable program"""
328 328 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
329 329 name = os.path.join(p, program)
330 330 if os.name == 'nt' or os.access(name, os.X_OK):
331 331 return name
332 332 return None
333 333
334 334 def createhgrc(path, options):
335 335 # create a fresh hgrc
336 336 hgrc = open(path, 'w+')
337 337 hgrc.write('[ui]\n')
338 338 hgrc.write('slash = True\n')
339 339 hgrc.write('interactive = False\n')
340 340 hgrc.write('[defaults]\n')
341 341 hgrc.write('backout = -d "0 0"\n')
342 342 hgrc.write('commit = -d "0 0"\n')
343 343 hgrc.write('tag = -d "0 0"\n')
344 344 if options.inotify:
345 345 hgrc.write('[extensions]\n')
346 346 hgrc.write('inotify=\n')
347 347 hgrc.write('[inotify]\n')
348 348 hgrc.write('pidfile=daemon.pids')
349 349 hgrc.write('appendpid=True\n')
350 350 if options.extra_config_opt:
351 351 for opt in options.extra_config_opt:
352 352 section, key = opt.split('.', 1)
353 353 assert '=' in key, ('extra config opt %s must '
354 354 'have an = for assignment' % opt)
355 355 hgrc.write('[%s]\n%s\n' % (section, key))
356 356 hgrc.close()
357 357
358 358 def createenv(options, testtmp, threadtmp, port):
359 359 env = os.environ.copy()
360 360 env['TESTTMP'] = testtmp
361 361 env['HOME'] = testtmp
362 362 env["HGPORT"] = str(port)
363 363 env["HGPORT1"] = str(port + 1)
364 364 env["HGPORT2"] = str(port + 2)
365 365 env["HGRCPATH"] = os.path.join(threadtmp, '.hgrc')
366 366 env["DAEMON_PIDS"] = os.path.join(threadtmp, 'daemon.pids')
367 367 env["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
368 368 env["HGMERGE"] = "internal:merge"
369 369 env["HGUSER"] = "test"
370 370 env["HGENCODING"] = "ascii"
371 371 env["HGENCODINGMODE"] = "strict"
372 372
373 373 # Reset some environment variables to well-known values so that
374 374 # the tests produce repeatable output.
375 375 env['LANG'] = env['LC_ALL'] = env['LANGUAGE'] = 'C'
376 376 env['TZ'] = 'GMT'
377 377 env["EMAIL"] = "Foo Bar <foo.bar@example.com>"
378 378 env['COLUMNS'] = '80'
379 379 env['TERM'] = 'xterm'
380 380
381 381 for k in ('HG HGPROF CDPATH GREP_OPTIONS http_proxy no_proxy ' +
382 382 'NO_PROXY').split():
383 383 if k in env:
384 384 del env[k]
385 385
386 386 # unset env related to hooks
387 387 for k in env.keys():
388 388 if k.startswith('HG_'):
389 389 del env[k]
390 390
391 391 return env
392 392
393 393 def checktools():
394 394 # Before we go any further, check for pre-requisite tools
395 395 # stuff from coreutils (cat, rm, etc) are not tested
396 396 for p in requiredtools:
397 397 if os.name == 'nt' and not p.endswith('.exe'):
398 398 p += '.exe'
399 399 found = findprogram(p)
400 400 if found:
401 401 vlog("# Found prerequisite", p, "at", found)
402 402 else:
403 403 print "WARNING: Did not find prerequisite tool: "+p
404 404
405 405 def terminate(proc):
406 406 """Terminate subprocess (with fallback for Python versions < 2.6)"""
407 407 vlog('# Terminating process %d' % proc.pid)
408 408 try:
409 409 getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
410 410 except OSError:
411 411 pass
412 412
413 413 def killdaemons(pidfile):
414 414 return killmod.killdaemons(pidfile, tryhard=False, remove=True,
415 415 logfn=vlog)
416 416
417 417 def cleanup(options):
418 418 if not options.keep_tmpdir:
419 419 vlog("# Cleaning up HGTMP", HGTMP)
420 420 shutil.rmtree(HGTMP, True)
421 421
422 422 def usecorrectpython():
423 423 # some tests run python interpreter. they must use same
424 424 # interpreter we use or bad things will happen.
425 425 pyexename = sys.platform == 'win32' and 'python.exe' or 'python'
426 426 if getattr(os, 'symlink', None):
427 427 vlog("# Making python executable in test path a symlink to '%s'" %
428 428 sys.executable)
429 429 mypython = os.path.join(BINDIR, pyexename)
430 430 try:
431 431 if os.readlink(mypython) == sys.executable:
432 432 return
433 433 os.unlink(mypython)
434 434 except OSError, err:
435 435 if err.errno != errno.ENOENT:
436 436 raise
437 437 if findprogram(pyexename) != sys.executable:
438 438 try:
439 439 os.symlink(sys.executable, mypython)
440 440 except OSError, err:
441 441 # child processes may race, which is harmless
442 442 if err.errno != errno.EEXIST:
443 443 raise
444 444 else:
445 445 exedir, exename = os.path.split(sys.executable)
446 446 vlog("# Modifying search path to find %s as %s in '%s'" %
447 447 (exename, pyexename, exedir))
448 448 path = os.environ['PATH'].split(os.pathsep)
449 449 while exedir in path:
450 450 path.remove(exedir)
451 451 os.environ['PATH'] = os.pathsep.join([exedir] + path)
452 452 if not findprogram(pyexename):
453 453 print "WARNING: Cannot find %s in search path" % pyexename
454 454
455 455 def installhg(options):
456 456 vlog("# Performing temporary installation of HG")
457 457 installerrs = os.path.join("tests", "install.err")
458 458 compiler = ''
459 459 if options.compiler:
460 460 compiler = '--compiler ' + options.compiler
461 461 pure = options.pure and "--pure" or ""
462 462
463 463 # Run installer in hg root
464 464 script = os.path.realpath(sys.argv[0])
465 465 hgroot = os.path.dirname(os.path.dirname(script))
466 466 os.chdir(hgroot)
467 467 nohome = '--home=""'
468 468 if os.name == 'nt':
469 469 # The --home="" trick works only on OS where os.sep == '/'
470 470 # because of a distutils convert_path() fast-path. Avoid it at
471 471 # least on Windows for now, deal with .pydistutils.cfg bugs
472 472 # when they happen.
473 473 nohome = ''
474 474 cmd = ('%(exe)s setup.py %(pure)s clean --all'
475 475 ' build %(compiler)s --build-base="%(base)s"'
476 476 ' install --force --prefix="%(prefix)s" --install-lib="%(libdir)s"'
477 477 ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
478 478 % dict(exe=sys.executable, pure=pure, compiler=compiler,
479 479 base=os.path.join(HGTMP, "build"),
480 480 prefix=INST, libdir=PYTHONDIR, bindir=BINDIR,
481 481 nohome=nohome, logfile=installerrs))
482 482 vlog("# Running", cmd)
483 483 if os.system(cmd) == 0:
484 484 if not options.verbose:
485 485 os.remove(installerrs)
486 486 else:
487 487 f = open(installerrs)
488 488 for line in f:
489 489 print line,
490 490 f.close()
491 491 sys.exit(1)
492 492 os.chdir(TESTDIR)
493 493
494 494 usecorrectpython()
495 495
496 496 vlog("# Installing dummy diffstat")
497 497 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
498 498 f.write('#!' + sys.executable + '\n'
499 499 'import sys\n'
500 500 'files = 0\n'
501 501 'for line in sys.stdin:\n'
502 502 ' if line.startswith("diff "):\n'
503 503 ' files += 1\n'
504 504 'sys.stdout.write("files patched: %d\\n" % files)\n')
505 505 f.close()
506 506 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
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.name == 'nt':
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.name == 'nt':
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 637 if (el.endswith(" (re)\n") and rematch(el[:-6], l) or
638 638 el.endswith(" (glob)\n") and globmatch(el[:-8], l)):
639 639 return True
640 640 return False
641 641
642 642 def tsttest(test, wd, options, replacements, env):
643 643 # We generate a shell script which outputs unique markers to line
644 644 # up script results with our source. These markers include input
645 645 # line number and the last return code
646 646 salt = "SALT" + str(time.time())
647 647 def addsalt(line, inpython):
648 648 if inpython:
649 649 script.append('%s %d 0\n' % (salt, line))
650 650 else:
651 651 script.append('echo %s %s $?\n' % (salt, line))
652 652
653 653 # After we run the shell script, we re-unify the script output
654 654 # with non-active parts of the source, with synchronization by our
655 655 # SALT line number markers. The after table contains the
656 656 # non-active components, ordered by line number
657 657 after = {}
658 658 pos = prepos = -1
659 659
660 660 # Expected shellscript output
661 661 expected = {}
662 662
663 663 # We keep track of whether or not we're in a Python block so we
664 664 # can generate the surrounding doctest magic
665 665 inpython = False
666 666
667 667 # True or False when in a true or false conditional section
668 668 skipping = None
669 669
670 670 def hghave(reqs):
671 671 # TODO: do something smarter when all other uses of hghave is gone
672 672 tdir = TESTDIR.replace('\\', '/')
673 673 proc = Popen4('%s -c "%s/hghave %s"' %
674 674 (options.shell, tdir, ' '.join(reqs)), wd, 0)
675 675 stdout, stderr = proc.communicate()
676 676 ret = proc.wait()
677 677 if wifexited(ret):
678 678 ret = os.WEXITSTATUS(ret)
679 679 if ret == 2:
680 680 print stdout
681 681 sys.exit(1)
682 682 return ret == 0
683 683
684 684 f = open(test)
685 685 t = f.readlines()
686 686 f.close()
687 687
688 688 script = []
689 689 if options.debug:
690 690 script.append('set -x\n')
691 691 if os.getenv('MSYSTEM'):
692 692 script.append('alias pwd="pwd -W"\n')
693 693 n = 0
694 694 for n, l in enumerate(t):
695 695 if not l.endswith('\n'):
696 696 l += '\n'
697 697 if l.startswith('#if'):
698 698 if skipping is not None:
699 699 after.setdefault(pos, []).append(' !!! nested #if\n')
700 700 skipping = not hghave(l.split()[1:])
701 701 after.setdefault(pos, []).append(l)
702 702 elif l.startswith('#else'):
703 703 if skipping is None:
704 704 after.setdefault(pos, []).append(' !!! missing #if\n')
705 705 skipping = not skipping
706 706 after.setdefault(pos, []).append(l)
707 707 elif l.startswith('#endif'):
708 708 if skipping is None:
709 709 after.setdefault(pos, []).append(' !!! missing #if\n')
710 710 skipping = None
711 711 after.setdefault(pos, []).append(l)
712 712 elif skipping:
713 713 after.setdefault(pos, []).append(l)
714 714 elif l.startswith(' >>> '): # python inlines
715 715 after.setdefault(pos, []).append(l)
716 716 prepos = pos
717 717 pos = n
718 718 if not inpython:
719 719 # we've just entered a Python block, add the header
720 720 inpython = True
721 721 addsalt(prepos, False) # make sure we report the exit code
722 722 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
723 723 addsalt(n, True)
724 724 script.append(l[2:])
725 725 elif l.startswith(' ... '): # python inlines
726 726 after.setdefault(prepos, []).append(l)
727 727 script.append(l[2:])
728 728 elif l.startswith(' $ '): # commands
729 729 if inpython:
730 730 script.append("EOF\n")
731 731 inpython = False
732 732 after.setdefault(pos, []).append(l)
733 733 prepos = pos
734 734 pos = n
735 735 addsalt(n, False)
736 736 cmd = l[4:].split()
737 737 if len(cmd) == 2 and cmd[0] == 'cd':
738 738 l = ' $ cd %s || exit 1\n' % cmd[1]
739 739 script.append(l[4:])
740 740 elif l.startswith(' > '): # continuations
741 741 after.setdefault(prepos, []).append(l)
742 742 script.append(l[4:])
743 743 elif l.startswith(' '): # results
744 744 # queue up a list of expected results
745 745 expected.setdefault(pos, []).append(l[2:])
746 746 else:
747 747 if inpython:
748 748 script.append("EOF\n")
749 749 inpython = False
750 750 # non-command/result - queue up for merged output
751 751 after.setdefault(pos, []).append(l)
752 752
753 753 if inpython:
754 754 script.append("EOF\n")
755 755 if skipping is not None:
756 756 after.setdefault(pos, []).append(' !!! missing #endif\n')
757 757 addsalt(n + 1, False)
758 758
759 759 # Write out the script and execute it
760 760 fd, name = tempfile.mkstemp(suffix='hg-tst')
761 761 try:
762 762 for l in script:
763 763 os.write(fd, l)
764 764 os.close(fd)
765 765
766 766 cmd = '%s "%s"' % (options.shell, name)
767 767 vlog("# Running", cmd)
768 768 exitcode, output = run(cmd, wd, options, replacements, env)
769 769 # do not merge output if skipped, return hghave message instead
770 770 # similarly, with --debug, output is None
771 771 if exitcode == SKIPPED_STATUS or output is None:
772 772 return exitcode, output
773 773 finally:
774 774 os.remove(name)
775 775
776 776 # Merge the script output back into a unified test
777 777
778 778 pos = -1
779 779 postout = []
780 780 ret = 0
781 781 for l in output:
782 782 lout, lcmd = l, None
783 783 if salt in l:
784 784 lout, lcmd = l.split(salt, 1)
785 785
786 786 if lout:
787 787 if not lout.endswith('\n'):
788 788 lout += ' (no-eol)\n'
789 789
790 790 # find the expected output at the current position
791 791 el = None
792 792 if pos in expected and expected[pos]:
793 793 el = expected[pos].pop(0)
794 794
795 795 if linematch(el, lout):
796 796 postout.append(" " + el)
797 797 else:
798 798 if needescape(lout):
799 799 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
800 800 postout.append(" " + lout) # let diff deal with it
801 801
802 802 if lcmd:
803 803 # add on last return code
804 804 ret = int(lcmd.split()[1])
805 805 if ret != 0:
806 806 postout.append(" [%s]\n" % ret)
807 807 if pos in after:
808 808 # merge in non-active test bits
809 809 postout += after.pop(pos)
810 810 pos = int(lcmd.split()[0])
811 811
812 812 if pos in after:
813 813 postout += after.pop(pos)
814 814
815 815 return exitcode, postout
816 816
817 817 wifexited = getattr(os, "WIFEXITED", lambda x: False)
818 818 def run(cmd, wd, options, replacements, env):
819 819 """Run command in a sub-process, capturing the output (stdout and stderr).
820 820 Return a tuple (exitcode, output). output is None in debug mode."""
821 821 # TODO: Use subprocess.Popen if we're running on Python 2.4
822 822 if options.debug:
823 823 proc = subprocess.Popen(cmd, shell=True, cwd=wd, env=env)
824 824 ret = proc.wait()
825 825 return (ret, None)
826 826
827 827 proc = Popen4(cmd, wd, options.timeout, env)
828 828 def cleanup():
829 829 terminate(proc)
830 830 ret = proc.wait()
831 831 if ret == 0:
832 832 ret = signal.SIGTERM << 8
833 833 killdaemons(env['DAEMON_PIDS'])
834 834 return ret
835 835
836 836 output = ''
837 837 proc.tochild.close()
838 838
839 839 try:
840 840 output = proc.fromchild.read()
841 841 except KeyboardInterrupt:
842 842 vlog('# Handling keyboard interrupt')
843 843 cleanup()
844 844 raise
845 845
846 846 ret = proc.wait()
847 847 if wifexited(ret):
848 848 ret = os.WEXITSTATUS(ret)
849 849
850 850 if proc.timeout:
851 851 ret = 'timeout'
852 852
853 853 if ret:
854 854 killdaemons(env['DAEMON_PIDS'])
855 855
856 856 if abort:
857 857 raise KeyboardInterrupt()
858 858
859 859 for s, r in replacements:
860 860 output = re.sub(s, r, output)
861 861 return ret, output.splitlines(True)
862 862
863 863 def runone(options, test, count):
864 864 '''returns a result element: (code, test, msg)'''
865 865
866 866 def skip(msg):
867 867 if options.verbose:
868 868 log("\nSkipping %s: %s" % (testpath, msg))
869 869 return 's', test, msg
870 870
871 871 def fail(msg, ret):
872 872 if not options.nodiff:
873 873 log("\nERROR: %s %s" % (testpath, msg))
874 874 if (not ret and options.interactive
875 875 and os.path.exists(testpath + ".err")):
876 876 iolock.acquire()
877 877 print "Accept this change? [n] ",
878 878 answer = sys.stdin.readline().strip()
879 879 iolock.release()
880 880 if answer.lower() in "y yes".split():
881 881 if test.endswith(".t"):
882 882 rename(testpath + ".err", testpath)
883 883 else:
884 884 rename(testpath + ".err", testpath + ".out")
885 885 return '.', test, ''
886 886 return '!', test, msg
887 887
888 888 def success():
889 889 return '.', test, ''
890 890
891 891 def ignore(msg):
892 892 return 'i', test, msg
893 893
894 894 def describe(ret):
895 895 if ret < 0:
896 896 return 'killed by signal %d' % -ret
897 897 return 'returned error code %d' % ret
898 898
899 899 testpath = os.path.join(TESTDIR, test)
900 900 err = os.path.join(TESTDIR, test + ".err")
901 901 lctest = test.lower()
902 902
903 903 if not os.path.exists(testpath):
904 904 return skip("doesn't exist")
905 905
906 906 if not (options.whitelisted and test in options.whitelisted):
907 907 if options.blacklist and test in options.blacklist:
908 908 return skip("blacklisted")
909 909
910 910 if options.retest and not os.path.exists(test + ".err"):
911 911 ignore("not retesting")
912 912 return None
913 913
914 914 if options.keywords:
915 915 fp = open(test)
916 916 t = fp.read().lower() + test.lower()
917 917 fp.close()
918 918 for k in options.keywords.lower().split():
919 919 if k in t:
920 920 break
921 921 else:
922 922 ignore("doesn't match keyword")
923 923 return None
924 924
925 925 for ext, func, out in testtypes:
926 926 if lctest.startswith("test-") and lctest.endswith(ext):
927 927 runner = func
928 928 ref = os.path.join(TESTDIR, test + out)
929 929 break
930 930 else:
931 931 return skip("unknown test type")
932 932
933 933 vlog("# Test", test)
934 934
935 935 if os.path.exists(err):
936 936 os.remove(err) # Remove any previous output files
937 937
938 938 # Make a tmp subdirectory to work in
939 939 threadtmp = os.path.join(HGTMP, "child%d" % count)
940 940 testtmp = os.path.join(threadtmp, os.path.basename(test))
941 941 os.mkdir(threadtmp)
942 942 os.mkdir(testtmp)
943 943
944 944 port = options.port + count * 3
945 945 replacements = [
946 946 (r':%s\b' % port, ':$HGPORT'),
947 947 (r':%s\b' % (port + 1), ':$HGPORT1'),
948 948 (r':%s\b' % (port + 2), ':$HGPORT2'),
949 949 ]
950 950 if os.name == 'nt':
951 951 replacements.append(
952 952 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
953 953 c in '/\\' and r'[/\\]' or
954 954 c.isdigit() and c or
955 955 '\\' + c
956 956 for c in testtmp), '$TESTTMP'))
957 957 else:
958 958 replacements.append((re.escape(testtmp), '$TESTTMP'))
959 959
960 960 env = createenv(options, testtmp, threadtmp, port)
961 961 createhgrc(env['HGRCPATH'], options)
962 962
963 963 if options.time:
964 964 starttime = time.time()
965 965 ret, out = runner(testpath, testtmp, options, replacements, env)
966 966 if options.time:
967 967 endtime = time.time()
968 968 times.append((test, endtime - starttime))
969 969 vlog("# Ret was:", ret)
970 970
971 971 killdaemons(env['DAEMON_PIDS'])
972 972
973 973 skipped = (ret == SKIPPED_STATUS)
974 974
975 975 # If we're not in --debug mode and reference output file exists,
976 976 # check test output against it.
977 977 if options.debug:
978 978 refout = None # to match "out is None"
979 979 elif os.path.exists(ref):
980 980 f = open(ref, "r")
981 981 refout = f.read().splitlines(True)
982 982 f.close()
983 983 else:
984 984 refout = []
985 985
986 986 if (ret != 0 or out != refout) and not skipped and not options.debug:
987 987 # Save errors to a file for diagnosis
988 988 f = open(err, "wb")
989 989 for line in out:
990 990 f.write(line)
991 991 f.close()
992 992
993 993 if skipped:
994 994 if out is None: # debug mode: nothing to parse
995 995 missing = ['unknown']
996 996 failed = None
997 997 else:
998 998 missing, failed = parsehghaveoutput(out)
999 999 if not missing:
1000 1000 missing = ['irrelevant']
1001 1001 if failed:
1002 1002 result = fail("hghave failed checking for %s" % failed[-1], ret)
1003 1003 skipped = False
1004 1004 else:
1005 1005 result = skip(missing[-1])
1006 1006 elif ret == 'timeout':
1007 1007 result = fail("timed out", ret)
1008 1008 elif out != refout:
1009 1009 if not options.nodiff:
1010 1010 iolock.acquire()
1011 1011 if options.view:
1012 1012 os.system("%s %s %s" % (options.view, ref, err))
1013 1013 else:
1014 1014 showdiff(refout, out, ref, err)
1015 1015 iolock.release()
1016 1016 if ret:
1017 1017 result = fail("output changed and " + describe(ret), ret)
1018 1018 else:
1019 1019 result = fail("output changed", ret)
1020 1020 elif ret:
1021 1021 result = fail(describe(ret), ret)
1022 1022 else:
1023 1023 result = success()
1024 1024
1025 1025 if not options.verbose:
1026 1026 iolock.acquire()
1027 1027 sys.stdout.write(result[0])
1028 1028 sys.stdout.flush()
1029 1029 iolock.release()
1030 1030
1031 1031 if not options.keep_tmpdir:
1032 1032 shutil.rmtree(threadtmp, True)
1033 1033 return result
1034 1034
1035 1035 _hgpath = None
1036 1036
1037 1037 def _gethgpath():
1038 1038 """Return the path to the mercurial package that is actually found by
1039 1039 the current Python interpreter."""
1040 1040 global _hgpath
1041 1041 if _hgpath is not None:
1042 1042 return _hgpath
1043 1043
1044 1044 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
1045 1045 pipe = os.popen(cmd % PYTHON)
1046 1046 try:
1047 1047 _hgpath = pipe.read().strip()
1048 1048 finally:
1049 1049 pipe.close()
1050 1050 return _hgpath
1051 1051
1052 1052 def _checkhglib(verb):
1053 1053 """Ensure that the 'mercurial' package imported by python is
1054 1054 the one we expect it to be. If not, print a warning to stderr."""
1055 1055 expecthg = os.path.join(PYTHONDIR, 'mercurial')
1056 1056 actualhg = _gethgpath()
1057 1057 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
1058 1058 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
1059 1059 ' (expected %s)\n'
1060 1060 % (verb, actualhg, expecthg))
1061 1061
1062 1062 results = {'.':[], '!':[], 's':[], 'i':[]}
1063 1063 times = []
1064 1064 iolock = threading.Lock()
1065 1065 abort = False
1066 1066
1067 1067 def scheduletests(options, tests):
1068 1068 jobs = options.jobs
1069 1069 done = queue.Queue()
1070 1070 running = 0
1071 1071 count = 0
1072 1072 global abort
1073 1073
1074 1074 def job(test, count):
1075 1075 try:
1076 1076 done.put(runone(options, test, count))
1077 1077 except KeyboardInterrupt:
1078 1078 pass
1079 1079
1080 1080 try:
1081 1081 while tests or running:
1082 1082 if not done.empty() or running == jobs or not tests:
1083 1083 try:
1084 1084 code, test, msg = done.get(True, 1)
1085 1085 results[code].append((test, msg))
1086 1086 if options.first and code not in '.si':
1087 1087 break
1088 1088 except queue.Empty:
1089 1089 continue
1090 1090 running -= 1
1091 1091 if tests and not running == jobs:
1092 1092 test = tests.pop(0)
1093 1093 if options.loop:
1094 1094 tests.append(test)
1095 1095 t = threading.Thread(None, job, args=(test, count))
1096 1096 t.start()
1097 1097 running += 1
1098 1098 count += 1
1099 1099 except KeyboardInterrupt:
1100 1100 abort = True
1101 1101
1102 1102 def runtests(options, tests):
1103 1103 try:
1104 1104 if INST:
1105 1105 installhg(options)
1106 1106 _checkhglib("Testing")
1107 1107 else:
1108 1108 usecorrectpython()
1109 1109
1110 1110 if options.restart:
1111 1111 orig = list(tests)
1112 1112 while tests:
1113 1113 if os.path.exists(tests[0] + ".err"):
1114 1114 break
1115 1115 tests.pop(0)
1116 1116 if not tests:
1117 1117 print "running all tests"
1118 1118 tests = orig
1119 1119
1120 1120 scheduletests(options, tests)
1121 1121
1122 1122 failed = len(results['!'])
1123 1123 tested = len(results['.']) + failed
1124 1124 skipped = len(results['s'])
1125 1125 ignored = len(results['i'])
1126 1126
1127 1127 print
1128 for s in results['s']:
1129 print "Skipped %s: %s" % s
1128 if not options.noskips:
1129 for s in results['s']:
1130 print "Skipped %s: %s" % s
1130 1131 for s in results['!']:
1131 1132 print "Failed %s: %s" % s
1132 1133 _checkhglib("Tested")
1133 1134 print "# Ran %d tests, %d skipped, %d failed." % (
1134 1135 tested, skipped + ignored, failed)
1135 1136 if options.time:
1136 1137 outputtimes(options)
1137 1138
1138 1139 if options.anycoverage:
1139 1140 outputcoverage(options)
1140 1141 except KeyboardInterrupt:
1141 1142 failed = True
1142 1143 print "\ninterrupted!"
1143 1144
1144 1145 if failed:
1145 1146 sys.exit(1)
1146 1147
1147 1148 testtypes = [('.py', pytest, '.out'),
1148 1149 ('.t', tsttest, '')]
1149 1150
1150 1151 def main():
1151 1152 (options, args) = parseargs()
1152 1153 os.umask(022)
1153 1154
1154 1155 checktools()
1155 1156
1156 1157 if len(args) == 0:
1157 1158 args = [t for t in os.listdir(".")
1158 1159 if t.startswith("test-")
1159 1160 and (t.endswith(".py") or t.endswith(".t"))]
1160 1161
1161 1162 tests = args
1162 1163
1163 1164 if options.random:
1164 1165 random.shuffle(tests)
1165 1166 else:
1166 1167 # keywords for slow tests
1167 1168 slow = 'svn gendoc check-code-hg'.split()
1168 1169 def sortkey(f):
1169 1170 # run largest tests first, as they tend to take the longest
1170 1171 val = -os.stat(f).st_size
1171 1172 for kw in slow:
1172 1173 if kw in f:
1173 1174 val *= 10
1174 1175 return val
1175 1176 tests.sort(key=sortkey)
1176 1177
1177 1178 if 'PYTHONHASHSEED' not in os.environ:
1178 1179 # use a random python hash seed all the time
1179 1180 # we do the randomness ourself to know what seed is used
1180 1181 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
1181 1182 print 'python hash seed:', os.environ['PYTHONHASHSEED']
1182 1183
1183 1184 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1184 1185 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1185 1186 if options.tmpdir:
1186 1187 options.keep_tmpdir = True
1187 1188 tmpdir = options.tmpdir
1188 1189 if os.path.exists(tmpdir):
1189 1190 # Meaning of tmpdir has changed since 1.3: we used to create
1190 1191 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1191 1192 # tmpdir already exists.
1192 1193 sys.exit("error: temp dir %r already exists" % tmpdir)
1193 1194
1194 1195 # Automatically removing tmpdir sounds convenient, but could
1195 1196 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1196 1197 # or "--tmpdir=$HOME".
1197 1198 #vlog("# Removing temp dir", tmpdir)
1198 1199 #shutil.rmtree(tmpdir)
1199 1200 os.makedirs(tmpdir)
1200 1201 else:
1201 1202 d = None
1202 1203 if os.name == 'nt':
1203 1204 # without this, we get the default temp dir location, but
1204 1205 # in all lowercase, which causes troubles with paths (issue3490)
1205 1206 d = os.getenv('TMP')
1206 1207 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1207 1208 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1208 1209
1209 1210 if options.with_hg:
1210 1211 INST = None
1211 1212 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1212 1213
1213 1214 # This looks redundant with how Python initializes sys.path from
1214 1215 # the location of the script being executed. Needed because the
1215 1216 # "hg" specified by --with-hg is not the only Python script
1216 1217 # executed in the test suite that needs to import 'mercurial'
1217 1218 # ... which means it's not really redundant at all.
1218 1219 PYTHONDIR = BINDIR
1219 1220 else:
1220 1221 INST = os.path.join(HGTMP, "install")
1221 1222 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1222 1223 PYTHONDIR = os.path.join(INST, "lib", "python")
1223 1224
1224 1225 os.environ["BINDIR"] = BINDIR
1225 1226 os.environ["PYTHON"] = PYTHON
1226 1227
1227 1228 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1228 1229 os.environ["PATH"] = os.pathsep.join(path)
1229 1230
1230 1231 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1231 1232 # can run .../tests/run-tests.py test-foo where test-foo
1232 1233 # adds an extension to HGRC
1233 1234 pypath = [PYTHONDIR, TESTDIR]
1234 1235 # We have to augment PYTHONPATH, rather than simply replacing
1235 1236 # it, in case external libraries are only available via current
1236 1237 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1237 1238 # are in /opt/subversion.)
1238 1239 oldpypath = os.environ.get(IMPL_PATH)
1239 1240 if oldpypath:
1240 1241 pypath.append(oldpypath)
1241 1242 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1242 1243
1243 1244 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1244 1245
1245 1246 vlog("# Using TESTDIR", TESTDIR)
1246 1247 vlog("# Using HGTMP", HGTMP)
1247 1248 vlog("# Using PATH", os.environ["PATH"])
1248 1249 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1249 1250
1250 1251 try:
1251 1252 runtests(options, tests)
1252 1253 finally:
1253 1254 time.sleep(.1)
1254 1255 cleanup(options)
1255 1256
1256 1257 if __name__ == '__main__':
1257 1258 main()
General Comments 0
You need to be logged in to leave comments. Login now