##// END OF EJS Templates
tests: ignore \r on windows
Mads Kiilerich -
r15449:f71d60da default
parent child Browse files
Show More
@@ -1,1259 +1,1265 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 el[:-7].decode('string-escape') + '\n' == l)):
560 (el[:-7].decode('string-escape') + '\n' == l or
561 el[:-7].decode('string-escape').replace('\r', '') +
562 '\n' == l and os.name == 'nt'))):
561 563 return True
562 564 return False
563 565
564 566 def tsttest(test, wd, options, replacements):
565 567 # We generate a shell script which outputs unique markers to line
566 568 # up script results with our source. These markers include input
567 569 # line number and the last return code
568 570 salt = "SALT" + str(time.time())
569 571 def addsalt(line, inpython):
570 572 if inpython:
571 573 script.append('%s %d 0\n' % (salt, line))
572 574 else:
573 575 script.append('echo %s %s $?\n' % (salt, line))
574 576
575 577 # After we run the shell script, we re-unify the script output
576 578 # with non-active parts of the source, with synchronization by our
577 579 # SALT line number markers. The after table contains the
578 580 # non-active components, ordered by line number
579 581 after = {}
580 582 pos = prepos = -1
581 583
582 584 # Expected shellscript output
583 585 expected = {}
584 586
585 587 # We keep track of whether or not we're in a Python block so we
586 588 # can generate the surrounding doctest magic
587 589 inpython = False
588 590
589 591 f = open(test)
590 592 t = f.readlines()
591 593 f.close()
592 594
593 595 script = []
594 596 for n, l in enumerate(t):
595 597 if not l.endswith('\n'):
596 598 l += '\n'
597 599 if l.startswith(' >>> '): # python inlines
598 600 after.setdefault(pos, []).append(l)
599 601 prepos = pos
600 602 pos = n
601 603 if not inpython:
602 604 # we've just entered a Python block, add the header
603 605 inpython = True
604 606 addsalt(prepos, False) # make sure we report the exit code
605 607 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
606 608 addsalt(n, True)
607 609 script.append(l[2:])
608 610 if l.startswith(' ... '): # python inlines
609 611 after.setdefault(prepos, []).append(l)
610 612 script.append(l[2:])
611 613 elif l.startswith(' $ '): # commands
612 614 if inpython:
613 615 script.append("EOF\n")
614 616 inpython = False
615 617 after.setdefault(pos, []).append(l)
616 618 prepos = pos
617 619 pos = n
618 620 addsalt(n, False)
619 621 script.append(l[4:])
620 622 elif l.startswith(' > '): # continuations
621 623 after.setdefault(prepos, []).append(l)
622 624 script.append(l[4:])
623 625 elif l.startswith(' '): # results
624 626 # queue up a list of expected results
625 627 expected.setdefault(pos, []).append(l[2:])
626 628 else:
627 629 if inpython:
628 630 script.append("EOF\n")
629 631 inpython = False
630 632 # non-command/result - queue up for merged output
631 633 after.setdefault(pos, []).append(l)
632 634
633 635 if inpython:
634 636 script.append("EOF\n")
635 637 addsalt(n + 1, False)
636 638
637 639 # Write out the script and execute it
638 640 fd, name = tempfile.mkstemp(suffix='hg-tst')
639 641 try:
640 642 for l in script:
641 643 os.write(fd, l)
642 644 os.close(fd)
643 645
644 646 cmd = '"%s" "%s"' % (options.shell, name)
645 647 vlog("# Running", cmd)
646 648 exitcode, output = run(cmd, wd, options, replacements)
647 649 # do not merge output if skipped, return hghave message instead
648 650 # similarly, with --debug, output is None
649 651 if exitcode == SKIPPED_STATUS or output is None:
650 652 return exitcode, output
651 653 finally:
652 654 os.remove(name)
653 655
654 656 # Merge the script output back into a unified test
655 657
656 658 pos = -1
657 659 postout = []
658 660 ret = 0
659 661 for n, l in enumerate(output):
660 662 lout, lcmd = l, None
661 663 if salt in l:
662 664 lout, lcmd = l.split(salt, 1)
663 665
664 666 if lout:
665 667 if lcmd:
666 668 # output block had no trailing newline, clean up
667 669 lout += ' (no-eol)\n'
668 670
669 671 # find the expected output at the current position
670 672 el = None
671 673 if pos in expected and expected[pos]:
672 674 el = expected[pos].pop(0)
673 675
674 676 if linematch(el, lout):
675 677 postout.append(" " + el)
676 678 else:
677 679 if needescape(lout):
678 680 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
679 681 postout.append(" " + lout) # let diff deal with it
680 682
681 683 if lcmd:
682 684 # add on last return code
683 685 ret = int(lcmd.split()[1])
684 686 if ret != 0:
685 687 postout.append(" [%s]\n" % ret)
686 688 if pos in after:
687 689 # merge in non-active test bits
688 690 postout += after.pop(pos)
689 691 pos = int(lcmd.split()[0])
690 692
691 693 if pos in after:
692 694 postout += after.pop(pos)
693 695
694 696 return exitcode, postout
695 697
696 698 wifexited = getattr(os, "WIFEXITED", lambda x: False)
697 699 def run(cmd, wd, options, replacements):
698 700 """Run command in a sub-process, capturing the output (stdout and stderr).
699 701 Return a tuple (exitcode, output). output is None in debug mode."""
700 702 # TODO: Use subprocess.Popen if we're running on Python 2.4
701 703 if options.debug:
702 704 proc = subprocess.Popen(cmd, shell=True, cwd=wd)
703 705 ret = proc.wait()
704 706 return (ret, None)
705 707
706 708 proc = Popen4(cmd, wd, options.timeout)
707 709 def cleanup():
708 710 terminate(proc)
709 711 ret = proc.wait()
710 712 if ret == 0:
711 713 ret = signal.SIGTERM << 8
712 714 killdaemons()
713 715 return ret
714 716
715 717 output = ''
716 718 proc.tochild.close()
717 719
718 720 try:
719 721 output = proc.fromchild.read()
720 722 except KeyboardInterrupt:
721 723 vlog('# Handling keyboard interrupt')
722 724 cleanup()
723 725 raise
724 726
725 727 ret = proc.wait()
726 728 if wifexited(ret):
727 729 ret = os.WEXITSTATUS(ret)
728 730
729 731 if proc.timeout:
730 732 ret = 'timeout'
731 733
732 734 if ret:
733 735 killdaemons()
734 736
735 737 for s, r in replacements:
736 738 output = re.sub(s, r, output)
737 739 return ret, splitnewlines(output)
738 740
739 741 def runone(options, test):
740 742 '''tristate output:
741 743 None -> skipped
742 744 True -> passed
743 745 False -> failed'''
744 746
745 747 global results, resultslock, iolock
746 748
747 749 testpath = os.path.join(TESTDIR, test)
748 750
749 751 def result(l, e):
750 752 resultslock.acquire()
751 753 results[l].append(e)
752 754 resultslock.release()
753 755
754 756 def skip(msg):
755 757 if not options.verbose:
756 758 result('s', (test, msg))
757 759 else:
758 760 iolock.acquire()
759 761 print "\nSkipping %s: %s" % (testpath, msg)
760 762 iolock.release()
761 763 return None
762 764
763 765 def fail(msg, ret):
764 766 if not options.nodiff:
765 767 iolock.acquire()
766 768 print "\nERROR: %s %s" % (testpath, msg)
767 769 iolock.release()
768 770 if (not ret and options.interactive
769 771 and os.path.exists(testpath + ".err")):
770 772 iolock.acquire()
771 773 print "Accept this change? [n] ",
772 774 answer = sys.stdin.readline().strip()
773 775 iolock.release()
774 776 if answer.lower() in "y yes".split():
775 777 if test.endswith(".t"):
776 778 rename(testpath + ".err", testpath)
777 779 else:
778 780 rename(testpath + ".err", testpath + ".out")
779 781 result('p', test)
780 782 return
781 783 result('f', (test, msg))
782 784
783 785 def success():
784 786 result('p', test)
785 787
786 788 def ignore(msg):
787 789 result('i', (test, msg))
788 790
789 791 if (os.path.basename(test).startswith("test-") and '~' not in test and
790 792 ('.' not in test or test.endswith('.py') or
791 793 test.endswith('.bat') or test.endswith('.t'))):
792 794 if not os.path.exists(test):
793 795 skip("doesn't exist")
794 796 return None
795 797 else:
796 798 vlog('# Test file', test, 'not supported, ignoring')
797 799 return None # not a supported test, don't record
798 800
799 801 if not (options.whitelisted and test in options.whitelisted):
800 802 if options.blacklist and test in options.blacklist:
801 803 skip("blacklisted")
802 804 return None
803 805
804 806 if options.retest and not os.path.exists(test + ".err"):
805 807 ignore("not retesting")
806 808 return None
807 809
808 810 if options.keywords:
809 811 fp = open(test)
810 812 t = fp.read().lower() + test.lower()
811 813 fp.close()
812 814 for k in options.keywords.lower().split():
813 815 if k in t:
814 816 break
815 817 else:
816 818 ignore("doesn't match keyword")
817 819 return None
818 820
819 821 vlog("# Test", test)
820 822
821 823 # create a fresh hgrc
822 824 hgrc = open(HGRCPATH, 'w+')
823 825 hgrc.write('[ui]\n')
824 826 hgrc.write('slash = True\n')
825 827 hgrc.write('[defaults]\n')
826 828 hgrc.write('backout = -d "0 0"\n')
827 829 hgrc.write('commit = -d "0 0"\n')
828 830 hgrc.write('tag = -d "0 0"\n')
829 831 if options.inotify:
830 832 hgrc.write('[extensions]\n')
831 833 hgrc.write('inotify=\n')
832 834 hgrc.write('[inotify]\n')
833 835 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
834 836 hgrc.write('appendpid=True\n')
835 837 if options.extra_config_opt:
836 838 for opt in options.extra_config_opt:
837 839 section, key = opt.split('.', 1)
838 840 assert '=' in key, ('extra config opt %s must '
839 841 'have an = for assignment' % opt)
840 842 hgrc.write('[%s]\n%s\n' % (section, key))
841 843 hgrc.close()
842 844
843 845 ref = os.path.join(TESTDIR, test+".out")
844 846 err = os.path.join(TESTDIR, test+".err")
845 847 if os.path.exists(err):
846 848 os.remove(err) # Remove any previous output files
847 849 try:
848 850 tf = open(testpath)
849 851 firstline = tf.readline().rstrip()
850 852 tf.close()
851 853 except:
852 854 firstline = ''
853 855 lctest = test.lower()
854 856
855 857 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
856 858 runner = pytest
857 859 elif lctest.endswith('.t'):
858 860 runner = tsttest
859 861 ref = testpath
860 862 else:
861 863 # do not try to run non-executable programs
862 864 if not os.access(testpath, os.X_OK):
863 865 return skip("not executable")
864 866 runner = shtest
865 867
866 868 # Make a tmp subdirectory to work in
867 869 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
868 870 os.path.join(HGTMP, os.path.basename(test)).replace('\\', '/')
869 871
870 os.mkdir(testtmp)
871 ret, out = runner(testpath, testtmp, options, [
872 replacements = [
872 873 (re.escape(testtmp), '$TESTTMP'),
873 874 (r':%s\b' % options.port, ':$HGPORT'),
874 875 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
875 876 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
876 ])
877 ]
878 if os.name == 'nt':
879 replacements.append((r'\r\n', '\n'))
880
881 os.mkdir(testtmp)
882 ret, out = runner(testpath, testtmp, options, replacements)
877 883 vlog("# Ret was:", ret)
878 884
879 885 mark = '.'
880 886
881 887 skipped = (ret == SKIPPED_STATUS)
882 888
883 889 # If we're not in --debug mode and reference output file exists,
884 890 # check test output against it.
885 891 if options.debug:
886 892 refout = None # to match "out is None"
887 893 elif os.path.exists(ref):
888 894 f = open(ref, "r")
889 895 refout = list(splitnewlines(f.read()))
890 896 f.close()
891 897 else:
892 898 refout = []
893 899
894 900 if (ret != 0 or out != refout) and not skipped and not options.debug:
895 901 # Save errors to a file for diagnosis
896 902 f = open(err, "wb")
897 903 for line in out:
898 904 f.write(line)
899 905 f.close()
900 906
901 907 if skipped:
902 908 mark = 's'
903 909 if out is None: # debug mode: nothing to parse
904 910 missing = ['unknown']
905 911 failed = None
906 912 else:
907 913 missing, failed = parsehghaveoutput(out)
908 914 if not missing:
909 915 missing = ['irrelevant']
910 916 if failed:
911 917 fail("hghave failed checking for %s" % failed[-1], ret)
912 918 skipped = False
913 919 else:
914 920 skip(missing[-1])
915 921 elif ret == 'timeout':
916 922 mark = 't'
917 923 fail("timed out", ret)
918 924 elif out != refout:
919 925 mark = '!'
920 926 if not options.nodiff:
921 927 iolock.acquire()
922 928 if options.view:
923 929 os.system("%s %s %s" % (options.view, ref, err))
924 930 else:
925 931 showdiff(refout, out, ref, err)
926 932 iolock.release()
927 933 if ret:
928 934 fail("output changed and returned error code %d" % ret, ret)
929 935 else:
930 936 fail("output changed", ret)
931 937 ret = 1
932 938 elif ret:
933 939 mark = '!'
934 940 fail("returned error code %d" % ret, ret)
935 941 else:
936 942 success()
937 943
938 944 if not options.verbose:
939 945 iolock.acquire()
940 946 sys.stdout.write(mark)
941 947 sys.stdout.flush()
942 948 iolock.release()
943 949
944 950 killdaemons()
945 951
946 952 if not options.keep_tmpdir:
947 953 shutil.rmtree(testtmp, True)
948 954 if skipped:
949 955 return None
950 956 return ret == 0
951 957
952 958 _hgpath = None
953 959
954 960 def _gethgpath():
955 961 """Return the path to the mercurial package that is actually found by
956 962 the current Python interpreter."""
957 963 global _hgpath
958 964 if _hgpath is not None:
959 965 return _hgpath
960 966
961 967 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
962 968 pipe = os.popen(cmd % PYTHON)
963 969 try:
964 970 _hgpath = pipe.read().strip()
965 971 finally:
966 972 pipe.close()
967 973 return _hgpath
968 974
969 975 def _checkhglib(verb):
970 976 """Ensure that the 'mercurial' package imported by python is
971 977 the one we expect it to be. If not, print a warning to stderr."""
972 978 expecthg = os.path.join(PYTHONDIR, 'mercurial')
973 979 actualhg = _gethgpath()
974 980 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
975 981 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
976 982 ' (expected %s)\n'
977 983 % (verb, actualhg, expecthg))
978 984
979 985 def runchildren(options, tests):
980 986 if INST:
981 987 installhg(options)
982 988 _checkhglib("Testing")
983 989
984 990 optcopy = dict(options.__dict__)
985 991 optcopy['jobs'] = 1
986 992
987 993 # Because whitelist has to override keyword matches, we have to
988 994 # actually load the whitelist in the children as well, so we allow
989 995 # the list of whitelist files to pass through and be parsed in the
990 996 # children, but not the dict of whitelisted tests resulting from
991 997 # the parse, used here to override blacklisted tests.
992 998 whitelist = optcopy['whitelisted'] or []
993 999 del optcopy['whitelisted']
994 1000
995 1001 blacklist = optcopy['blacklist'] or []
996 1002 del optcopy['blacklist']
997 1003 blacklisted = []
998 1004
999 1005 if optcopy['with_hg'] is None:
1000 1006 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
1001 1007 optcopy.pop('anycoverage', None)
1002 1008
1003 1009 opts = []
1004 1010 for opt, value in optcopy.iteritems():
1005 1011 name = '--' + opt.replace('_', '-')
1006 1012 if value is True:
1007 1013 opts.append(name)
1008 1014 elif isinstance(value, list):
1009 1015 for v in value:
1010 1016 opts.append(name + '=' + str(v))
1011 1017 elif value is not None:
1012 1018 opts.append(name + '=' + str(value))
1013 1019
1014 1020 tests.reverse()
1015 1021 jobs = [[] for j in xrange(options.jobs)]
1016 1022 while tests:
1017 1023 for job in jobs:
1018 1024 if not tests:
1019 1025 break
1020 1026 test = tests.pop()
1021 1027 if test not in whitelist and test in blacklist:
1022 1028 blacklisted.append(test)
1023 1029 else:
1024 1030 job.append(test)
1025 1031 fps = {}
1026 1032
1027 1033 for j, job in enumerate(jobs):
1028 1034 if not job:
1029 1035 continue
1030 1036 rfd, wfd = os.pipe()
1031 1037 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
1032 1038 childtmp = os.path.join(HGTMP, 'child%d' % j)
1033 1039 childopts += ['--tmpdir', childtmp]
1034 1040 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
1035 1041 vlog(' '.join(cmdline))
1036 1042 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
1037 1043 os.close(wfd)
1038 1044 signal.signal(signal.SIGINT, signal.SIG_IGN)
1039 1045 failures = 0
1040 1046 tested, skipped, failed = 0, 0, 0
1041 1047 skips = []
1042 1048 fails = []
1043 1049 while fps:
1044 1050 pid, status = os.wait()
1045 1051 fp = fps.pop(pid)
1046 1052 l = fp.read().splitlines()
1047 1053 try:
1048 1054 test, skip, fail = map(int, l[:3])
1049 1055 except ValueError:
1050 1056 test, skip, fail = 0, 0, 0
1051 1057 split = -fail or len(l)
1052 1058 for s in l[3:split]:
1053 1059 skips.append(s.split(" ", 1))
1054 1060 for s in l[split:]:
1055 1061 fails.append(s.split(" ", 1))
1056 1062 tested += test
1057 1063 skipped += skip
1058 1064 failed += fail
1059 1065 vlog('pid %d exited, status %d' % (pid, status))
1060 1066 failures |= status
1061 1067 print
1062 1068 skipped += len(blacklisted)
1063 1069 if not options.noskips:
1064 1070 for s in skips:
1065 1071 print "Skipped %s: %s" % (s[0], s[1])
1066 1072 for s in blacklisted:
1067 1073 print "Skipped %s: blacklisted" % s
1068 1074 for s in fails:
1069 1075 print "Failed %s: %s" % (s[0], s[1])
1070 1076
1071 1077 _checkhglib("Tested")
1072 1078 print "# Ran %d tests, %d skipped, %d failed." % (
1073 1079 tested, skipped, failed)
1074 1080
1075 1081 if options.anycoverage:
1076 1082 outputcoverage(options)
1077 1083 sys.exit(failures != 0)
1078 1084
1079 1085 results = dict(p=[], f=[], s=[], i=[])
1080 1086 resultslock = threading.Lock()
1081 1087 iolock = threading.Lock()
1082 1088
1083 1089 def runqueue(options, tests, results):
1084 1090 for test in tests:
1085 1091 ret = runone(options, test)
1086 1092 if options.first and ret is not None and not ret:
1087 1093 break
1088 1094
1089 1095 def runtests(options, tests):
1090 1096 global DAEMON_PIDS, HGRCPATH
1091 1097 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1092 1098 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1093 1099
1094 1100 try:
1095 1101 if INST:
1096 1102 installhg(options)
1097 1103 _checkhglib("Testing")
1098 1104
1099 1105 if options.restart:
1100 1106 orig = list(tests)
1101 1107 while tests:
1102 1108 if os.path.exists(tests[0] + ".err"):
1103 1109 break
1104 1110 tests.pop(0)
1105 1111 if not tests:
1106 1112 print "running all tests"
1107 1113 tests = orig
1108 1114
1109 1115 runqueue(options, tests, results)
1110 1116
1111 1117 failed = len(results['f'])
1112 1118 tested = len(results['p']) + failed
1113 1119 skipped = len(results['s'])
1114 1120 ignored = len(results['i'])
1115 1121
1116 1122 if options.child:
1117 1123 fp = os.fdopen(options.child, 'w')
1118 1124 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1119 1125 for s in results['s']:
1120 1126 fp.write("%s %s\n" % s)
1121 1127 for s in results['f']:
1122 1128 fp.write("%s %s\n" % s)
1123 1129 fp.close()
1124 1130 else:
1125 1131 print
1126 1132 for s in results['s']:
1127 1133 print "Skipped %s: %s" % s
1128 1134 for s in results['f']:
1129 1135 print "Failed %s: %s" % s
1130 1136 _checkhglib("Tested")
1131 1137 print "# Ran %d tests, %d skipped, %d failed." % (
1132 1138 tested, skipped + ignored, failed)
1133 1139
1134 1140 if options.anycoverage:
1135 1141 outputcoverage(options)
1136 1142 except KeyboardInterrupt:
1137 1143 failed = True
1138 1144 print "\ninterrupted!"
1139 1145
1140 1146 if failed:
1141 1147 sys.exit(1)
1142 1148
1143 1149 def main():
1144 1150 (options, args) = parseargs()
1145 1151 if not options.child:
1146 1152 os.umask(022)
1147 1153
1148 1154 checktools()
1149 1155
1150 1156 if len(args) == 0:
1151 1157 args = os.listdir(".")
1152 1158 args.sort()
1153 1159
1154 1160 tests = args
1155 1161
1156 1162 # Reset some environment variables to well-known values so that
1157 1163 # the tests produce repeatable output.
1158 1164 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1159 1165 os.environ['TZ'] = 'GMT'
1160 1166 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1161 1167 os.environ['CDPATH'] = ''
1162 1168 os.environ['COLUMNS'] = '80'
1163 1169 os.environ['GREP_OPTIONS'] = ''
1164 1170 os.environ['http_proxy'] = ''
1165 1171 os.environ['no_proxy'] = ''
1166 1172 os.environ['NO_PROXY'] = ''
1167 1173
1168 1174 # unset env related to hooks
1169 1175 for k in os.environ.keys():
1170 1176 if k.startswith('HG_'):
1171 1177 # can't remove on solaris
1172 1178 os.environ[k] = ''
1173 1179 del os.environ[k]
1174 1180
1175 1181 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1176 1182 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1177 1183 if options.tmpdir:
1178 1184 options.keep_tmpdir = True
1179 1185 tmpdir = options.tmpdir
1180 1186 if os.path.exists(tmpdir):
1181 1187 # Meaning of tmpdir has changed since 1.3: we used to create
1182 1188 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1183 1189 # tmpdir already exists.
1184 1190 sys.exit("error: temp dir %r already exists" % tmpdir)
1185 1191
1186 1192 # Automatically removing tmpdir sounds convenient, but could
1187 1193 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1188 1194 # or "--tmpdir=$HOME".
1189 1195 #vlog("# Removing temp dir", tmpdir)
1190 1196 #shutil.rmtree(tmpdir)
1191 1197 os.makedirs(tmpdir)
1192 1198 else:
1193 1199 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1194 1200 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1195 1201 DAEMON_PIDS = None
1196 1202 HGRCPATH = None
1197 1203
1198 1204 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1199 1205 os.environ["HGMERGE"] = "internal:merge"
1200 1206 os.environ["HGUSER"] = "test"
1201 1207 os.environ["HGENCODING"] = "ascii"
1202 1208 os.environ["HGENCODINGMODE"] = "strict"
1203 1209 os.environ["HGPORT"] = str(options.port)
1204 1210 os.environ["HGPORT1"] = str(options.port + 1)
1205 1211 os.environ["HGPORT2"] = str(options.port + 2)
1206 1212
1207 1213 if options.with_hg:
1208 1214 INST = None
1209 1215 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1210 1216
1211 1217 # This looks redundant with how Python initializes sys.path from
1212 1218 # the location of the script being executed. Needed because the
1213 1219 # "hg" specified by --with-hg is not the only Python script
1214 1220 # executed in the test suite that needs to import 'mercurial'
1215 1221 # ... which means it's not really redundant at all.
1216 1222 PYTHONDIR = BINDIR
1217 1223 else:
1218 1224 INST = os.path.join(HGTMP, "install")
1219 1225 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1220 1226 PYTHONDIR = os.path.join(INST, "lib", "python")
1221 1227
1222 1228 os.environ["BINDIR"] = BINDIR
1223 1229 os.environ["PYTHON"] = PYTHON
1224 1230
1225 1231 if not options.child:
1226 1232 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1227 1233 os.environ["PATH"] = os.pathsep.join(path)
1228 1234
1229 1235 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1230 1236 # can run .../tests/run-tests.py test-foo where test-foo
1231 1237 # adds an extension to HGRC
1232 1238 pypath = [PYTHONDIR, TESTDIR]
1233 1239 # We have to augment PYTHONPATH, rather than simply replacing
1234 1240 # it, in case external libraries are only available via current
1235 1241 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1236 1242 # are in /opt/subversion.)
1237 1243 oldpypath = os.environ.get(IMPL_PATH)
1238 1244 if oldpypath:
1239 1245 pypath.append(oldpypath)
1240 1246 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1241 1247
1242 1248 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1243 1249
1244 1250 vlog("# Using TESTDIR", TESTDIR)
1245 1251 vlog("# Using HGTMP", HGTMP)
1246 1252 vlog("# Using PATH", os.environ["PATH"])
1247 1253 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1248 1254
1249 1255 try:
1250 1256 if len(tests) > 1 and options.jobs > 1:
1251 1257 runchildren(options, tests)
1252 1258 else:
1253 1259 runtests(options, tests)
1254 1260 finally:
1255 1261 time.sleep(1)
1256 1262 cleanup(options)
1257 1263
1258 1264 if __name__ == '__main__':
1259 1265 main()
General Comments 0
You need to be logged in to leave comments. Login now