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