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