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