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