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