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