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