##// END OF EJS Templates
run-tests: ignoring tests works again...
simon@laptop-tosh -
r19311:ad16e5c7 default
parent child Browse files
Show More
@@ -1,1257 +1,1255 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 ignore("not retesting")
908 return None
907 return ignore("not retesting")
909 908
910 909 if options.keywords:
911 910 fp = open(test)
912 911 t = fp.read().lower() + test.lower()
913 912 fp.close()
914 913 for k in options.keywords.lower().split():
915 914 if k in t:
916 915 break
917 916 else:
918 ignore("doesn't match keyword")
919 return None
917 return ignore("doesn't match keyword")
920 918
921 919 for ext, func, out in testtypes:
922 920 if lctest.startswith("test-") and lctest.endswith(ext):
923 921 runner = func
924 922 ref = os.path.join(TESTDIR, test + out)
925 923 break
926 924 else:
927 925 return skip("unknown test type")
928 926
929 927 vlog("# Test", test)
930 928
931 929 if os.path.exists(err):
932 930 os.remove(err) # Remove any previous output files
933 931
934 932 # Make a tmp subdirectory to work in
935 933 threadtmp = os.path.join(HGTMP, "child%d" % count)
936 934 testtmp = os.path.join(threadtmp, os.path.basename(test))
937 935 os.mkdir(threadtmp)
938 936 os.mkdir(testtmp)
939 937
940 938 port = options.port + count * 3
941 939 replacements = [
942 940 (r':%s\b' % port, ':$HGPORT'),
943 941 (r':%s\b' % (port + 1), ':$HGPORT1'),
944 942 (r':%s\b' % (port + 2), ':$HGPORT2'),
945 943 ]
946 944 if os.name == 'nt':
947 945 replacements.append(
948 946 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
949 947 c in '/\\' and r'[/\\]' or
950 948 c.isdigit() and c or
951 949 '\\' + c
952 950 for c in testtmp), '$TESTTMP'))
953 951 else:
954 952 replacements.append((re.escape(testtmp), '$TESTTMP'))
955 953
956 954 env = createenv(options, testtmp, threadtmp, port)
957 955 createhgrc(env['HGRCPATH'], options)
958 956
959 957 starttime = time.time()
960 958 try:
961 959 ret, out = runner(testpath, testtmp, options, replacements, env)
962 960 except KeyboardInterrupt:
963 961 endtime = time.time()
964 962 log('INTERRUPTED: %s (after %d seconds)' % (test, endtime - starttime))
965 963 raise
966 964 endtime = time.time()
967 965 times.append((test, endtime - starttime))
968 966 vlog("# Ret was:", ret)
969 967
970 968 killdaemons(env['DAEMON_PIDS'])
971 969
972 970 skipped = (ret == SKIPPED_STATUS)
973 971
974 972 # If we're not in --debug mode and reference output file exists,
975 973 # check test output against it.
976 974 if options.debug:
977 975 refout = None # to match "out is None"
978 976 elif os.path.exists(ref):
979 977 f = open(ref, "r")
980 978 refout = f.read().splitlines(True)
981 979 f.close()
982 980 else:
983 981 refout = []
984 982
985 983 if (ret != 0 or out != refout) and not skipped and not options.debug:
986 984 # Save errors to a file for diagnosis
987 985 f = open(err, "wb")
988 986 for line in out:
989 987 f.write(line)
990 988 f.close()
991 989
992 990 if skipped:
993 991 if out is None: # debug mode: nothing to parse
994 992 missing = ['unknown']
995 993 failed = None
996 994 else:
997 995 missing, failed = parsehghaveoutput(out)
998 996 if not missing:
999 997 missing = ['irrelevant']
1000 998 if failed:
1001 999 result = fail("hghave failed checking for %s" % failed[-1], ret)
1002 1000 skipped = False
1003 1001 else:
1004 1002 result = skip(missing[-1])
1005 1003 elif ret == 'timeout':
1006 1004 result = fail("timed out", ret)
1007 1005 elif out != refout:
1008 1006 if not options.nodiff:
1009 1007 iolock.acquire()
1010 1008 if options.view:
1011 1009 os.system("%s %s %s" % (options.view, ref, err))
1012 1010 else:
1013 1011 showdiff(refout, out, ref, err)
1014 1012 iolock.release()
1015 1013 if ret:
1016 1014 result = fail("output changed and " + describe(ret), ret)
1017 1015 else:
1018 1016 result = fail("output changed", ret)
1019 1017 elif ret:
1020 1018 result = fail(describe(ret), ret)
1021 1019 else:
1022 1020 result = success()
1023 1021
1024 1022 if not options.verbose:
1025 1023 iolock.acquire()
1026 1024 sys.stdout.write(result[0])
1027 1025 sys.stdout.flush()
1028 1026 iolock.release()
1029 1027
1030 1028 if not options.keep_tmpdir:
1031 1029 shutil.rmtree(threadtmp, True)
1032 1030 return result
1033 1031
1034 1032 _hgpath = None
1035 1033
1036 1034 def _gethgpath():
1037 1035 """Return the path to the mercurial package that is actually found by
1038 1036 the current Python interpreter."""
1039 1037 global _hgpath
1040 1038 if _hgpath is not None:
1041 1039 return _hgpath
1042 1040
1043 1041 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
1044 1042 pipe = os.popen(cmd % PYTHON)
1045 1043 try:
1046 1044 _hgpath = pipe.read().strip()
1047 1045 finally:
1048 1046 pipe.close()
1049 1047 return _hgpath
1050 1048
1051 1049 def _checkhglib(verb):
1052 1050 """Ensure that the 'mercurial' package imported by python is
1053 1051 the one we expect it to be. If not, print a warning to stderr."""
1054 1052 expecthg = os.path.join(PYTHONDIR, 'mercurial')
1055 1053 actualhg = _gethgpath()
1056 1054 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
1057 1055 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
1058 1056 ' (expected %s)\n'
1059 1057 % (verb, actualhg, expecthg))
1060 1058
1061 1059 results = {'.':[], '!':[], 's':[], 'i':[]}
1062 1060 times = []
1063 1061 iolock = threading.Lock()
1064 1062 abort = False
1065 1063
1066 1064 def scheduletests(options, tests):
1067 1065 jobs = options.jobs
1068 1066 done = queue.Queue()
1069 1067 running = 0
1070 1068 count = 0
1071 1069 global abort
1072 1070
1073 1071 def job(test, count):
1074 1072 try:
1075 1073 done.put(runone(options, test, count))
1076 1074 except KeyboardInterrupt:
1077 1075 pass
1078 1076
1079 1077 try:
1080 1078 while tests or running:
1081 1079 if not done.empty() or running == jobs or not tests:
1082 1080 try:
1083 1081 code, test, msg = done.get(True, 1)
1084 1082 results[code].append((test, msg))
1085 1083 if options.first and code not in '.si':
1086 1084 break
1087 1085 except queue.Empty:
1088 1086 continue
1089 1087 running -= 1
1090 1088 if tests and not running == jobs:
1091 1089 test = tests.pop(0)
1092 1090 if options.loop:
1093 1091 tests.append(test)
1094 1092 t = threading.Thread(None, job, args=(test, count))
1095 1093 t.start()
1096 1094 running += 1
1097 1095 count += 1
1098 1096 except KeyboardInterrupt:
1099 1097 abort = True
1100 1098
1101 1099 def runtests(options, tests):
1102 1100 try:
1103 1101 if INST:
1104 1102 installhg(options)
1105 1103 _checkhglib("Testing")
1106 1104 else:
1107 1105 usecorrectpython()
1108 1106
1109 1107 if options.restart:
1110 1108 orig = list(tests)
1111 1109 while tests:
1112 1110 if os.path.exists(tests[0] + ".err"):
1113 1111 break
1114 1112 tests.pop(0)
1115 1113 if not tests:
1116 1114 print "running all tests"
1117 1115 tests = orig
1118 1116
1119 1117 scheduletests(options, tests)
1120 1118
1121 1119 failed = len(results['!'])
1122 1120 tested = len(results['.']) + failed
1123 1121 skipped = len(results['s'])
1124 1122 ignored = len(results['i'])
1125 1123
1126 1124 print
1127 1125 if not options.noskips:
1128 1126 for s in results['s']:
1129 1127 print "Skipped %s: %s" % s
1130 1128 for s in results['!']:
1131 1129 print "Failed %s: %s" % s
1132 1130 _checkhglib("Tested")
1133 1131 print "# Ran %d tests, %d skipped, %d failed." % (
1134 1132 tested, skipped + ignored, failed)
1135 1133 if options.time:
1136 1134 outputtimes(options)
1137 1135
1138 1136 if options.anycoverage:
1139 1137 outputcoverage(options)
1140 1138 except KeyboardInterrupt:
1141 1139 failed = True
1142 1140 print "\ninterrupted!"
1143 1141
1144 1142 if failed:
1145 1143 sys.exit(1)
1146 1144
1147 1145 testtypes = [('.py', pytest, '.out'),
1148 1146 ('.t', tsttest, '')]
1149 1147
1150 1148 def main():
1151 1149 (options, args) = parseargs()
1152 1150 os.umask(022)
1153 1151
1154 1152 checktools()
1155 1153
1156 1154 if len(args) == 0:
1157 1155 args = [t for t in os.listdir(".")
1158 1156 if t.startswith("test-")
1159 1157 and (t.endswith(".py") or t.endswith(".t"))]
1160 1158
1161 1159 tests = args
1162 1160
1163 1161 if options.random:
1164 1162 random.shuffle(tests)
1165 1163 else:
1166 1164 # keywords for slow tests
1167 1165 slow = 'svn gendoc check-code-hg'.split()
1168 1166 def sortkey(f):
1169 1167 # run largest tests first, as they tend to take the longest
1170 1168 val = -os.stat(f).st_size
1171 1169 for kw in slow:
1172 1170 if kw in f:
1173 1171 val *= 10
1174 1172 return val
1175 1173 tests.sort(key=sortkey)
1176 1174
1177 1175 if 'PYTHONHASHSEED' not in os.environ:
1178 1176 # use a random python hash seed all the time
1179 1177 # we do the randomness ourself to know what seed is used
1180 1178 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
1181 1179 print 'python hash seed:', os.environ['PYTHONHASHSEED']
1182 1180
1183 1181 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1184 1182 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1185 1183 if options.tmpdir:
1186 1184 options.keep_tmpdir = True
1187 1185 tmpdir = options.tmpdir
1188 1186 if os.path.exists(tmpdir):
1189 1187 # Meaning of tmpdir has changed since 1.3: we used to create
1190 1188 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1191 1189 # tmpdir already exists.
1192 1190 sys.exit("error: temp dir %r already exists" % tmpdir)
1193 1191
1194 1192 # Automatically removing tmpdir sounds convenient, but could
1195 1193 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1196 1194 # or "--tmpdir=$HOME".
1197 1195 #vlog("# Removing temp dir", tmpdir)
1198 1196 #shutil.rmtree(tmpdir)
1199 1197 os.makedirs(tmpdir)
1200 1198 else:
1201 1199 d = None
1202 1200 if os.name == 'nt':
1203 1201 # without this, we get the default temp dir location, but
1204 1202 # in all lowercase, which causes troubles with paths (issue3490)
1205 1203 d = os.getenv('TMP')
1206 1204 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1207 1205 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1208 1206
1209 1207 if options.with_hg:
1210 1208 INST = None
1211 1209 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1212 1210
1213 1211 # This looks redundant with how Python initializes sys.path from
1214 1212 # the location of the script being executed. Needed because the
1215 1213 # "hg" specified by --with-hg is not the only Python script
1216 1214 # executed in the test suite that needs to import 'mercurial'
1217 1215 # ... which means it's not really redundant at all.
1218 1216 PYTHONDIR = BINDIR
1219 1217 else:
1220 1218 INST = os.path.join(HGTMP, "install")
1221 1219 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1222 1220 PYTHONDIR = os.path.join(INST, "lib", "python")
1223 1221
1224 1222 os.environ["BINDIR"] = BINDIR
1225 1223 os.environ["PYTHON"] = PYTHON
1226 1224
1227 1225 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1228 1226 os.environ["PATH"] = os.pathsep.join(path)
1229 1227
1230 1228 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1231 1229 # can run .../tests/run-tests.py test-foo where test-foo
1232 1230 # adds an extension to HGRC
1233 1231 pypath = [PYTHONDIR, TESTDIR]
1234 1232 # We have to augment PYTHONPATH, rather than simply replacing
1235 1233 # it, in case external libraries are only available via current
1236 1234 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1237 1235 # are in /opt/subversion.)
1238 1236 oldpypath = os.environ.get(IMPL_PATH)
1239 1237 if oldpypath:
1240 1238 pypath.append(oldpypath)
1241 1239 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1242 1240
1243 1241 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1244 1242
1245 1243 vlog("# Using TESTDIR", TESTDIR)
1246 1244 vlog("# Using HGTMP", HGTMP)
1247 1245 vlog("# Using PATH", os.environ["PATH"])
1248 1246 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1249 1247
1250 1248 try:
1251 1249 runtests(options, tests)
1252 1250 finally:
1253 1251 time.sleep(.1)
1254 1252 cleanup(options)
1255 1253
1256 1254 if __name__ == '__main__':
1257 1255 main()
General Comments 0
You need to be logged in to leave comments. Login now