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