##// END OF EJS Templates
run-tests: skip unnecessary operations on the args in the child proces...
Simon Heimberg -
r17908:42f8ee0e default
parent child Browse files
Show More
@@ -1,1292 +1,1292 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
59 59 processlock = threading.Lock()
60 60
61 61 closefds = os.name == 'posix'
62 62 def Popen4(cmd, wd, timeout):
63 63 processlock.acquire()
64 64 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd,
65 65 close_fds=closefds,
66 66 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
67 67 stderr=subprocess.STDOUT)
68 68 processlock.release()
69 69
70 70 p.fromchild = p.stdout
71 71 p.tochild = p.stdin
72 72 p.childerr = p.stderr
73 73
74 74 p.timeout = False
75 75 if timeout:
76 76 def t():
77 77 start = time.time()
78 78 while time.time() - start < timeout and p.returncode is None:
79 79 time.sleep(.1)
80 80 p.timeout = True
81 81 if p.returncode is None:
82 82 terminate(p)
83 83 threading.Thread(target=t).start()
84 84
85 85 return p
86 86
87 87 # reserved exit code to skip test (used by hghave)
88 88 SKIPPED_STATUS = 80
89 89 SKIPPED_PREFIX = 'skipped: '
90 90 FAILED_PREFIX = 'hghave check failed: '
91 91 PYTHON = sys.executable.replace('\\', '/')
92 92 IMPL_PATH = 'PYTHONPATH'
93 93 if 'java' in sys.platform:
94 94 IMPL_PATH = 'JYTHONPATH'
95 95
96 96 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
97 97
98 98 defaults = {
99 99 'jobs': ('HGTEST_JOBS', 1),
100 100 'timeout': ('HGTEST_TIMEOUT', 180),
101 101 'port': ('HGTEST_PORT', 20059),
102 102 'shell': ('HGTEST_SHELL', 'sh'),
103 103 }
104 104
105 105 def parselistfiles(files, listtype, warn=True):
106 106 entries = dict()
107 107 for filename in files:
108 108 try:
109 109 path = os.path.expanduser(os.path.expandvars(filename))
110 110 f = open(path, "r")
111 111 except IOError, err:
112 112 if err.errno != errno.ENOENT:
113 113 raise
114 114 if warn:
115 115 print "warning: no such %s file: %s" % (listtype, filename)
116 116 continue
117 117
118 118 for line in f.readlines():
119 119 line = line.split('#', 1)[0].strip()
120 120 if line:
121 121 entries[line] = filename
122 122
123 123 f.close()
124 124 return entries
125 125
126 126 def parseargs():
127 127 parser = optparse.OptionParser("%prog [options] [tests]")
128 128
129 129 # keep these sorted
130 130 parser.add_option("--blacklist", action="append",
131 131 help="skip tests listed in the specified blacklist file")
132 132 parser.add_option("--whitelist", action="append",
133 133 help="always run tests listed in the specified whitelist file")
134 134 parser.add_option("-C", "--annotate", action="store_true",
135 135 help="output files annotated with coverage")
136 136 parser.add_option("--child", type="int",
137 137 help="run as child process, summary to given fd")
138 138 parser.add_option("-c", "--cover", action="store_true",
139 139 help="print a test coverage report")
140 140 parser.add_option("-d", "--debug", action="store_true",
141 141 help="debug mode: write output of test scripts to console"
142 142 " rather than capturing and diff'ing it (disables timeout)")
143 143 parser.add_option("-f", "--first", action="store_true",
144 144 help="exit on the first test failure")
145 145 parser.add_option("-H", "--htmlcov", action="store_true",
146 146 help="create an HTML report of the coverage of the files")
147 147 parser.add_option("--inotify", action="store_true",
148 148 help="enable inotify extension when running tests")
149 149 parser.add_option("-i", "--interactive", action="store_true",
150 150 help="prompt to accept changed output")
151 151 parser.add_option("-j", "--jobs", type="int",
152 152 help="number of jobs to run in parallel"
153 153 " (default: $%s or %d)" % defaults['jobs'])
154 154 parser.add_option("--keep-tmpdir", action="store_true",
155 155 help="keep temporary directory after running tests")
156 156 parser.add_option("-k", "--keywords",
157 157 help="run tests matching keywords")
158 158 parser.add_option("-l", "--local", action="store_true",
159 159 help="shortcut for --with-hg=<testdir>/../hg")
160 160 parser.add_option("-n", "--nodiff", action="store_true",
161 161 help="skip showing test changes")
162 162 parser.add_option("-p", "--port", type="int",
163 163 help="port on which servers should listen"
164 164 " (default: $%s or %d)" % defaults['port'])
165 165 parser.add_option("--pure", action="store_true",
166 166 help="use pure Python code instead of C extensions")
167 167 parser.add_option("-R", "--restart", action="store_true",
168 168 help="restart at last error")
169 169 parser.add_option("-r", "--retest", action="store_true",
170 170 help="retest failed tests")
171 171 parser.add_option("-S", "--noskips", action="store_true",
172 172 help="don't report skip tests verbosely")
173 173 parser.add_option("--shell", type="string",
174 174 help="shell to use (default: $%s or %s)" % defaults['shell'])
175 175 parser.add_option("-t", "--timeout", type="int",
176 176 help="kill errant tests after TIMEOUT seconds"
177 177 " (default: $%s or %d)" % defaults['timeout'])
178 178 parser.add_option("--tmpdir", type="string",
179 179 help="run tests in the given temporary directory"
180 180 " (implies --keep-tmpdir)")
181 181 parser.add_option("-v", "--verbose", action="store_true",
182 182 help="output verbose messages")
183 183 parser.add_option("--view", type="string",
184 184 help="external diff viewer")
185 185 parser.add_option("--with-hg", type="string",
186 186 metavar="HG",
187 187 help="test using specified hg script rather than a "
188 188 "temporary installation")
189 189 parser.add_option("-3", "--py3k-warnings", action="store_true",
190 190 help="enable Py3k warnings on Python 2.6+")
191 191 parser.add_option('--extra-config-opt', action="append",
192 192 help='set the given config opt in the test hgrc')
193 193
194 194 for option, (envvar, default) in defaults.items():
195 195 defaults[option] = type(default)(os.environ.get(envvar, default))
196 196 parser.set_defaults(**defaults)
197 197 (options, args) = parser.parse_args()
198 198
199 199 # jython is always pure
200 200 if 'java' in sys.platform or '__pypy__' in sys.modules:
201 201 options.pure = True
202 202
203 203 if options.with_hg:
204 204 options.with_hg = os.path.expanduser(options.with_hg)
205 205 if not (os.path.isfile(options.with_hg) and
206 206 os.access(options.with_hg, os.X_OK)):
207 207 parser.error('--with-hg must specify an executable hg script')
208 208 if not os.path.basename(options.with_hg) == 'hg':
209 209 sys.stderr.write('warning: --with-hg should specify an hg script\n')
210 210 if options.local:
211 211 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
212 212 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
213 213 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
214 214 parser.error('--local specified, but %r not found or not executable'
215 215 % hgbin)
216 216 options.with_hg = hgbin
217 217
218 218 options.anycoverage = options.cover or options.annotate or options.htmlcov
219 219 if options.anycoverage:
220 220 try:
221 221 import coverage
222 222 covver = version.StrictVersion(coverage.__version__).version
223 223 if covver < (3, 3):
224 224 parser.error('coverage options require coverage 3.3 or later')
225 225 except ImportError:
226 226 parser.error('coverage options now require the coverage package')
227 227
228 228 if options.anycoverage and options.local:
229 229 # this needs some path mangling somewhere, I guess
230 230 parser.error("sorry, coverage options do not work when --local "
231 231 "is specified")
232 232
233 233 global vlog
234 234 if options.verbose:
235 235 if options.jobs > 1 or options.child is not None:
236 236 pid = "[%d]" % os.getpid()
237 237 else:
238 238 pid = None
239 239 def vlog(*msg):
240 240 iolock.acquire()
241 241 if pid:
242 242 print pid,
243 243 for m in msg:
244 244 print m,
245 245 print
246 246 sys.stdout.flush()
247 247 iolock.release()
248 248 else:
249 249 vlog = lambda *msg: None
250 250
251 251 if options.tmpdir:
252 252 options.tmpdir = os.path.expanduser(options.tmpdir)
253 253
254 254 if options.jobs < 1:
255 255 parser.error('--jobs must be positive')
256 256 if options.interactive and options.jobs > 1:
257 257 print '(--interactive overrides --jobs)'
258 258 options.jobs = 1
259 259 if options.interactive and options.debug:
260 260 parser.error("-i/--interactive and -d/--debug are incompatible")
261 261 if options.debug:
262 262 if options.timeout != defaults['timeout']:
263 263 sys.stderr.write(
264 264 'warning: --timeout option ignored with --debug\n')
265 265 options.timeout = 0
266 266 if options.py3k_warnings:
267 267 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
268 268 parser.error('--py3k-warnings can only be used on Python 2.6+')
269 269 if options.blacklist:
270 270 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
271 271 if options.whitelist:
272 272 options.whitelisted = parselistfiles(options.whitelist, 'whitelist',
273 273 warn=options.child is None)
274 274 else:
275 275 options.whitelisted = {}
276 276
277 277 return (options, args)
278 278
279 279 def rename(src, dst):
280 280 """Like os.rename(), trade atomicity and opened files friendliness
281 281 for existing destination support.
282 282 """
283 283 shutil.copy(src, dst)
284 284 os.remove(src)
285 285
286 286 def parsehghaveoutput(lines):
287 287 '''Parse hghave log lines.
288 288 Return tuple of lists (missing, failed):
289 289 * the missing/unknown features
290 290 * the features for which existence check failed'''
291 291 missing = []
292 292 failed = []
293 293 for line in lines:
294 294 if line.startswith(SKIPPED_PREFIX):
295 295 line = line.splitlines()[0]
296 296 missing.append(line[len(SKIPPED_PREFIX):])
297 297 elif line.startswith(FAILED_PREFIX):
298 298 line = line.splitlines()[0]
299 299 failed.append(line[len(FAILED_PREFIX):])
300 300
301 301 return missing, failed
302 302
303 303 def showdiff(expected, output, ref, err):
304 304 print
305 305 for line in difflib.unified_diff(expected, output, ref, err):
306 306 sys.stdout.write(line)
307 307
308 308 def findprogram(program):
309 309 """Search PATH for a executable program"""
310 310 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
311 311 name = os.path.join(p, program)
312 312 if os.name == 'nt' or os.access(name, os.X_OK):
313 313 return name
314 314 return None
315 315
316 316 def checktools():
317 317 # Before we go any further, check for pre-requisite tools
318 318 # stuff from coreutils (cat, rm, etc) are not tested
319 319 for p in requiredtools:
320 320 if os.name == 'nt':
321 321 p += '.exe'
322 322 found = findprogram(p)
323 323 if found:
324 324 vlog("# Found prerequisite", p, "at", found)
325 325 else:
326 326 print "WARNING: Did not find prerequisite tool: "+p
327 327
328 328 def terminate(proc):
329 329 """Terminate subprocess (with fallback for Python versions < 2.6)"""
330 330 vlog('# Terminating process %d' % proc.pid)
331 331 try:
332 332 getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
333 333 except OSError:
334 334 pass
335 335
336 336 def killdaemons():
337 337 return killmod.killdaemons(DAEMON_PIDS, tryhard=False, remove=True,
338 338 logfn=vlog)
339 339
340 340 def cleanup(options):
341 341 if not options.keep_tmpdir:
342 342 vlog("# Cleaning up HGTMP", HGTMP)
343 343 shutil.rmtree(HGTMP, True)
344 344
345 345 def usecorrectpython():
346 346 # some tests run python interpreter. they must use same
347 347 # interpreter we use or bad things will happen.
348 348 exedir, exename = os.path.split(sys.executable)
349 349 if exename in ('python', 'python.exe'):
350 350 path = findprogram(exename)
351 351 if os.path.dirname(path) == exedir:
352 352 return
353 353 else:
354 354 exename = 'python'
355 355 vlog('# Making python executable in test path use correct Python')
356 356 mypython = os.path.join(BINDIR, exename)
357 357 try:
358 358 os.symlink(sys.executable, mypython)
359 359 except AttributeError:
360 360 # windows fallback
361 361 shutil.copyfile(sys.executable, mypython)
362 362 shutil.copymode(sys.executable, mypython)
363 363
364 364 def installhg(options):
365 365 vlog("# Performing temporary installation of HG")
366 366 installerrs = os.path.join("tests", "install.err")
367 367 pure = options.pure and "--pure" or ""
368 368
369 369 # Run installer in hg root
370 370 script = os.path.realpath(sys.argv[0])
371 371 hgroot = os.path.dirname(os.path.dirname(script))
372 372 os.chdir(hgroot)
373 373 nohome = '--home=""'
374 374 if os.name == 'nt':
375 375 # The --home="" trick works only on OS where os.sep == '/'
376 376 # because of a distutils convert_path() fast-path. Avoid it at
377 377 # least on Windows for now, deal with .pydistutils.cfg bugs
378 378 # when they happen.
379 379 nohome = ''
380 380 cmd = ('%s setup.py %s clean --all'
381 381 ' build --build-base="%s"'
382 382 ' install --force --prefix="%s" --install-lib="%s"'
383 383 ' --install-scripts="%s" %s >%s 2>&1'
384 384 % (sys.executable, pure, os.path.join(HGTMP, "build"),
385 385 INST, PYTHONDIR, BINDIR, nohome, installerrs))
386 386 vlog("# Running", cmd)
387 387 if os.system(cmd) == 0:
388 388 if not options.verbose:
389 389 os.remove(installerrs)
390 390 else:
391 391 f = open(installerrs)
392 392 for line in f:
393 393 print line,
394 394 f.close()
395 395 sys.exit(1)
396 396 os.chdir(TESTDIR)
397 397
398 398 usecorrectpython()
399 399
400 400 vlog("# Installing dummy diffstat")
401 401 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
402 402 f.write('#!' + sys.executable + '\n'
403 403 'import sys\n'
404 404 'files = 0\n'
405 405 'for line in sys.stdin:\n'
406 406 ' if line.startswith("diff "):\n'
407 407 ' files += 1\n'
408 408 'sys.stdout.write("files patched: %d\\n" % files)\n')
409 409 f.close()
410 410 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
411 411
412 412 if options.py3k_warnings and not options.anycoverage:
413 413 vlog("# Updating hg command to enable Py3k Warnings switch")
414 414 f = open(os.path.join(BINDIR, 'hg'), 'r')
415 415 lines = [line.rstrip() for line in f]
416 416 lines[0] += ' -3'
417 417 f.close()
418 418 f = open(os.path.join(BINDIR, 'hg'), 'w')
419 419 for line in lines:
420 420 f.write(line + '\n')
421 421 f.close()
422 422
423 423 hgbat = os.path.join(BINDIR, 'hg.bat')
424 424 if os.path.isfile(hgbat):
425 425 # hg.bat expects to be put in bin/scripts while run-tests.py
426 426 # installation layout put it in bin/ directly. Fix it
427 427 f = open(hgbat, 'rb')
428 428 data = f.read()
429 429 f.close()
430 430 if '"%~dp0..\python" "%~dp0hg" %*' in data:
431 431 data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
432 432 '"%~dp0python" "%~dp0hg" %*')
433 433 f = open(hgbat, 'wb')
434 434 f.write(data)
435 435 f.close()
436 436 else:
437 437 print 'WARNING: cannot fix hg.bat reference to python.exe'
438 438
439 439 if options.anycoverage:
440 440 custom = os.path.join(TESTDIR, 'sitecustomize.py')
441 441 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
442 442 vlog('# Installing coverage trigger to %s' % target)
443 443 shutil.copyfile(custom, target)
444 444 rc = os.path.join(TESTDIR, '.coveragerc')
445 445 vlog('# Installing coverage rc to %s' % rc)
446 446 os.environ['COVERAGE_PROCESS_START'] = rc
447 447 fn = os.path.join(INST, '..', '.coverage')
448 448 os.environ['COVERAGE_FILE'] = fn
449 449
450 450 def outputcoverage(options):
451 451
452 452 vlog('# Producing coverage report')
453 453 os.chdir(PYTHONDIR)
454 454
455 455 def covrun(*args):
456 456 cmd = 'coverage %s' % ' '.join(args)
457 457 vlog('# Running: %s' % cmd)
458 458 os.system(cmd)
459 459
460 460 if options.child:
461 461 return
462 462
463 463 covrun('-c')
464 464 omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
465 465 covrun('-i', '-r', '"--omit=%s"' % omit) # report
466 466 if options.htmlcov:
467 467 htmldir = os.path.join(TESTDIR, 'htmlcov')
468 468 covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
469 469 if options.annotate:
470 470 adir = os.path.join(TESTDIR, 'annotated')
471 471 if not os.path.isdir(adir):
472 472 os.mkdir(adir)
473 473 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
474 474
475 475 def pytest(test, wd, options, replacements):
476 476 py3kswitch = options.py3k_warnings and ' -3' or ''
477 477 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
478 478 vlog("# Running", cmd)
479 479 if os.name == 'nt':
480 480 replacements.append((r'\r\n', '\n'))
481 481 return run(cmd, wd, options, replacements)
482 482
483 483 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
484 484 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
485 485 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
486 486 escapemap.update({'\\': '\\\\', '\r': r'\r'})
487 487 def escapef(m):
488 488 return escapemap[m.group(0)]
489 489 def stringescape(s):
490 490 return escapesub(escapef, s)
491 491
492 492 def rematch(el, l):
493 493 try:
494 494 # use \Z to ensure that the regex matches to the end of the string
495 495 if os.name == 'nt':
496 496 return re.match(el + r'\r?\n\Z', l)
497 497 return re.match(el + r'\n\Z', l)
498 498 except re.error:
499 499 # el is an invalid regex
500 500 return False
501 501
502 502 def globmatch(el, l):
503 503 # The only supported special characters are * and ? plus / which also
504 504 # matches \ on windows. Escaping of these caracters is supported.
505 505 i, n = 0, len(el)
506 506 res = ''
507 507 while i < n:
508 508 c = el[i]
509 509 i += 1
510 510 if c == '\\' and el[i] in '*?\\/':
511 511 res += el[i - 1:i + 1]
512 512 i += 1
513 513 elif c == '*':
514 514 res += '.*'
515 515 elif c == '?':
516 516 res += '.'
517 517 elif c == '/' and os.name == 'nt':
518 518 res += '[/\\\\]'
519 519 else:
520 520 res += re.escape(c)
521 521 return rematch(res, l)
522 522
523 523 def linematch(el, l):
524 524 if el == l: # perfect match (fast)
525 525 return True
526 526 if el:
527 527 if el.endswith(" (esc)\n"):
528 528 el = el[:-7].decode('string-escape') + '\n'
529 529 if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l:
530 530 return True
531 531 if (el.endswith(" (re)\n") and rematch(el[:-6], l) or
532 532 el.endswith(" (glob)\n") and globmatch(el[:-8], l)):
533 533 return True
534 534 return False
535 535
536 536 def tsttest(test, wd, options, replacements):
537 537 # We generate a shell script which outputs unique markers to line
538 538 # up script results with our source. These markers include input
539 539 # line number and the last return code
540 540 salt = "SALT" + str(time.time())
541 541 def addsalt(line, inpython):
542 542 if inpython:
543 543 script.append('%s %d 0\n' % (salt, line))
544 544 else:
545 545 script.append('echo %s %s $?\n' % (salt, line))
546 546
547 547 # After we run the shell script, we re-unify the script output
548 548 # with non-active parts of the source, with synchronization by our
549 549 # SALT line number markers. The after table contains the
550 550 # non-active components, ordered by line number
551 551 after = {}
552 552 pos = prepos = -1
553 553
554 554 # Expected shellscript output
555 555 expected = {}
556 556
557 557 # We keep track of whether or not we're in a Python block so we
558 558 # can generate the surrounding doctest magic
559 559 inpython = False
560 560
561 561 # True or False when in a true or false conditional section
562 562 skipping = None
563 563
564 564 def hghave(reqs):
565 565 # TODO: do something smarter when all other uses of hghave is gone
566 566 tdir = TESTDIR.replace('\\', '/')
567 567 proc = Popen4('%s -c "%s/hghave %s"' %
568 568 (options.shell, tdir, ' '.join(reqs)), wd, 0)
569 569 proc.communicate()
570 570 ret = proc.wait()
571 571 if wifexited(ret):
572 572 ret = os.WEXITSTATUS(ret)
573 573 return ret == 0
574 574
575 575 f = open(test)
576 576 t = f.readlines()
577 577 f.close()
578 578
579 579 script = []
580 580 if options.debug:
581 581 script.append('set -x\n')
582 582 if os.getenv('MSYSTEM'):
583 583 script.append('alias pwd="pwd -W"\n')
584 584 for n, l in enumerate(t):
585 585 if not l.endswith('\n'):
586 586 l += '\n'
587 587 if l.startswith('#if'):
588 588 if skipping is not None:
589 589 after.setdefault(pos, []).append(' !!! nested #if\n')
590 590 skipping = not hghave(l.split()[1:])
591 591 after.setdefault(pos, []).append(l)
592 592 elif l.startswith('#else'):
593 593 if skipping is None:
594 594 after.setdefault(pos, []).append(' !!! missing #if\n')
595 595 skipping = not skipping
596 596 after.setdefault(pos, []).append(l)
597 597 elif l.startswith('#endif'):
598 598 if skipping is None:
599 599 after.setdefault(pos, []).append(' !!! missing #if\n')
600 600 skipping = None
601 601 after.setdefault(pos, []).append(l)
602 602 elif skipping:
603 603 after.setdefault(pos, []).append(l)
604 604 elif l.startswith(' >>> '): # python inlines
605 605 after.setdefault(pos, []).append(l)
606 606 prepos = pos
607 607 pos = n
608 608 if not inpython:
609 609 # we've just entered a Python block, add the header
610 610 inpython = True
611 611 addsalt(prepos, False) # make sure we report the exit code
612 612 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
613 613 addsalt(n, True)
614 614 script.append(l[2:])
615 615 elif l.startswith(' ... '): # python inlines
616 616 after.setdefault(prepos, []).append(l)
617 617 script.append(l[2:])
618 618 elif l.startswith(' $ '): # commands
619 619 if inpython:
620 620 script.append("EOF\n")
621 621 inpython = False
622 622 after.setdefault(pos, []).append(l)
623 623 prepos = pos
624 624 pos = n
625 625 addsalt(n, False)
626 626 cmd = l[4:].split()
627 627 if len(cmd) == 2 and cmd[0] == 'cd':
628 628 l = ' $ cd %s || exit 1\n' % cmd[1]
629 629 script.append(l[4:])
630 630 elif l.startswith(' > '): # continuations
631 631 after.setdefault(prepos, []).append(l)
632 632 script.append(l[4:])
633 633 elif l.startswith(' '): # results
634 634 # queue up a list of expected results
635 635 expected.setdefault(pos, []).append(l[2:])
636 636 else:
637 637 if inpython:
638 638 script.append("EOF\n")
639 639 inpython = False
640 640 # non-command/result - queue up for merged output
641 641 after.setdefault(pos, []).append(l)
642 642
643 643 if inpython:
644 644 script.append("EOF\n")
645 645 if skipping is not None:
646 646 after.setdefault(pos, []).append(' !!! missing #endif\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 l in 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 not lout.endswith('\n'):
678 678 lout += ' (no-eol)\n'
679 679
680 680 # find the expected output at the current position
681 681 el = None
682 682 if pos in expected and expected[pos]:
683 683 el = expected[pos].pop(0)
684 684
685 685 if linematch(el, lout):
686 686 postout.append(" " + el)
687 687 else:
688 688 if needescape(lout):
689 689 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
690 690 postout.append(" " + lout) # let diff deal with it
691 691
692 692 if lcmd:
693 693 # add on last return code
694 694 ret = int(lcmd.split()[1])
695 695 if ret != 0:
696 696 postout.append(" [%s]\n" % ret)
697 697 if pos in after:
698 698 # merge in non-active test bits
699 699 postout += after.pop(pos)
700 700 pos = int(lcmd.split()[0])
701 701
702 702 if pos in after:
703 703 postout += after.pop(pos)
704 704
705 705 return exitcode, postout
706 706
707 707 wifexited = getattr(os, "WIFEXITED", lambda x: False)
708 708 def run(cmd, wd, options, replacements):
709 709 """Run command in a sub-process, capturing the output (stdout and stderr).
710 710 Return a tuple (exitcode, output). output is None in debug mode."""
711 711 # TODO: Use subprocess.Popen if we're running on Python 2.4
712 712 if options.debug:
713 713 proc = subprocess.Popen(cmd, shell=True, cwd=wd)
714 714 ret = proc.wait()
715 715 return (ret, None)
716 716
717 717 proc = Popen4(cmd, wd, options.timeout)
718 718 def cleanup():
719 719 terminate(proc)
720 720 ret = proc.wait()
721 721 if ret == 0:
722 722 ret = signal.SIGTERM << 8
723 723 killdaemons()
724 724 return ret
725 725
726 726 output = ''
727 727 proc.tochild.close()
728 728
729 729 try:
730 730 output = proc.fromchild.read()
731 731 except KeyboardInterrupt:
732 732 vlog('# Handling keyboard interrupt')
733 733 cleanup()
734 734 raise
735 735
736 736 ret = proc.wait()
737 737 if wifexited(ret):
738 738 ret = os.WEXITSTATUS(ret)
739 739
740 740 if proc.timeout:
741 741 ret = 'timeout'
742 742
743 743 if ret:
744 744 killdaemons()
745 745
746 746 for s, r in replacements:
747 747 output = re.sub(s, r, output)
748 748 return ret, output.splitlines(True)
749 749
750 750 def runone(options, test):
751 751 '''tristate output:
752 752 None -> skipped
753 753 True -> passed
754 754 False -> failed'''
755 755
756 756 global results, resultslock, iolock
757 757
758 758 testpath = os.path.join(TESTDIR, test)
759 759
760 760 def result(l, e):
761 761 resultslock.acquire()
762 762 results[l].append(e)
763 763 resultslock.release()
764 764
765 765 def skip(msg):
766 766 if not options.verbose:
767 767 result('s', (test, msg))
768 768 else:
769 769 iolock.acquire()
770 770 print "\nSkipping %s: %s" % (testpath, msg)
771 771 iolock.release()
772 772 return None
773 773
774 774 def fail(msg, ret):
775 775 if not options.nodiff:
776 776 iolock.acquire()
777 777 print "\nERROR: %s %s" % (testpath, msg)
778 778 iolock.release()
779 779 if (not ret and options.interactive
780 780 and os.path.exists(testpath + ".err")):
781 781 iolock.acquire()
782 782 print "Accept this change? [n] ",
783 783 answer = sys.stdin.readline().strip()
784 784 iolock.release()
785 785 if answer.lower() in "y yes".split():
786 786 if test.endswith(".t"):
787 787 rename(testpath + ".err", testpath)
788 788 else:
789 789 rename(testpath + ".err", testpath + ".out")
790 790 result('p', test)
791 791 return
792 792 result('f', (test, msg))
793 793
794 794 def success():
795 795 result('p', test)
796 796
797 797 def ignore(msg):
798 798 result('i', (test, msg))
799 799
800 800 if (os.path.basename(test).startswith("test-") and '~' not in test and
801 801 ('.' not in test or test.endswith('.py') or
802 802 test.endswith('.bat') or test.endswith('.t'))):
803 803 if not os.path.exists(test):
804 804 skip("doesn't exist")
805 805 return None
806 806 else:
807 807 vlog('# Test file', test, 'not supported, ignoring')
808 808 return None # not a supported test, don't record
809 809
810 810 if not (options.whitelisted and test in options.whitelisted):
811 811 if options.blacklist and test in options.blacklist:
812 812 skip("blacklisted")
813 813 return None
814 814
815 815 if options.retest and not os.path.exists(test + ".err"):
816 816 ignore("not retesting")
817 817 return None
818 818
819 819 if options.keywords:
820 820 fp = open(test)
821 821 t = fp.read().lower() + test.lower()
822 822 fp.close()
823 823 for k in options.keywords.lower().split():
824 824 if k in t:
825 825 break
826 826 else:
827 827 ignore("doesn't match keyword")
828 828 return None
829 829
830 830 vlog("# Test", test)
831 831
832 832 # create a fresh hgrc
833 833 hgrc = open(HGRCPATH, 'w+')
834 834 hgrc.write('[ui]\n')
835 835 hgrc.write('slash = True\n')
836 836 hgrc.write('[defaults]\n')
837 837 hgrc.write('backout = -d "0 0"\n')
838 838 hgrc.write('commit = -d "0 0"\n')
839 839 hgrc.write('tag = -d "0 0"\n')
840 840 if options.inotify:
841 841 hgrc.write('[extensions]\n')
842 842 hgrc.write('inotify=\n')
843 843 hgrc.write('[inotify]\n')
844 844 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
845 845 hgrc.write('appendpid=True\n')
846 846 if options.extra_config_opt:
847 847 for opt in options.extra_config_opt:
848 848 section, key = opt.split('.', 1)
849 849 assert '=' in key, ('extra config opt %s must '
850 850 'have an = for assignment' % opt)
851 851 hgrc.write('[%s]\n%s\n' % (section, key))
852 852 hgrc.close()
853 853
854 854 ref = os.path.join(TESTDIR, test+".out")
855 855 err = os.path.join(TESTDIR, test+".err")
856 856 if os.path.exists(err):
857 857 os.remove(err) # Remove any previous output files
858 858 try:
859 859 tf = open(testpath)
860 860 firstline = tf.readline().rstrip()
861 861 tf.close()
862 862 except IOError:
863 863 firstline = ''
864 864 lctest = test.lower()
865 865
866 866 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
867 867 runner = pytest
868 868 elif lctest.endswith('.t'):
869 869 runner = tsttest
870 870 ref = testpath
871 871 else:
872 872 return skip("unknown test type")
873 873
874 874 # Make a tmp subdirectory to work in
875 875 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
876 876 os.path.join(HGTMP, os.path.basename(test))
877 877
878 878 replacements = [
879 879 (r':%s\b' % options.port, ':$HGPORT'),
880 880 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
881 881 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
882 882 ]
883 883 if os.name == 'nt':
884 884 replacements.append(
885 885 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
886 886 c in '/\\' and r'[/\\]' or
887 887 c.isdigit() and c or
888 888 '\\' + c
889 889 for c in testtmp), '$TESTTMP'))
890 890 else:
891 891 replacements.append((re.escape(testtmp), '$TESTTMP'))
892 892
893 893 os.mkdir(testtmp)
894 894 ret, out = runner(testpath, testtmp, options, replacements)
895 895 vlog("# Ret was:", ret)
896 896
897 897 mark = '.'
898 898
899 899 skipped = (ret == SKIPPED_STATUS)
900 900
901 901 # If we're not in --debug mode and reference output file exists,
902 902 # check test output against it.
903 903 if options.debug:
904 904 refout = None # to match "out is None"
905 905 elif os.path.exists(ref):
906 906 f = open(ref, "r")
907 907 refout = f.read().splitlines(True)
908 908 f.close()
909 909 else:
910 910 refout = []
911 911
912 912 if (ret != 0 or out != refout) and not skipped and not options.debug:
913 913 # Save errors to a file for diagnosis
914 914 f = open(err, "wb")
915 915 for line in out:
916 916 f.write(line)
917 917 f.close()
918 918
919 919 def describe(ret):
920 920 if ret < 0:
921 921 return 'killed by signal %d' % -ret
922 922 return 'returned error code %d' % ret
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 " + describe(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(describe(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 if len(args) == 0:
1174 args = os.listdir(".")
1175 args.sort()
1173 if len(args) == 0:
1174 args = os.listdir(".")
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 if 'HG' in os.environ:
1199 1199 # can't remove on solaris
1200 1200 os.environ['HG'] = ''
1201 1201 del os.environ['HG']
1202 1202
1203 1203 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1204 1204 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1205 1205 if options.tmpdir:
1206 1206 options.keep_tmpdir = True
1207 1207 tmpdir = options.tmpdir
1208 1208 if os.path.exists(tmpdir):
1209 1209 # Meaning of tmpdir has changed since 1.3: we used to create
1210 1210 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1211 1211 # tmpdir already exists.
1212 1212 sys.exit("error: temp dir %r already exists" % tmpdir)
1213 1213
1214 1214 # Automatically removing tmpdir sounds convenient, but could
1215 1215 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1216 1216 # or "--tmpdir=$HOME".
1217 1217 #vlog("# Removing temp dir", tmpdir)
1218 1218 #shutil.rmtree(tmpdir)
1219 1219 os.makedirs(tmpdir)
1220 1220 else:
1221 1221 d = None
1222 1222 if os.name == 'nt':
1223 1223 # without this, we get the default temp dir location, but
1224 1224 # in all lowercase, which causes troubles with paths (issue3490)
1225 1225 d = os.getenv('TMP')
1226 1226 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1227 1227 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1228 1228 DAEMON_PIDS = None
1229 1229 HGRCPATH = None
1230 1230
1231 1231 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1232 1232 os.environ["HGMERGE"] = "internal:merge"
1233 1233 os.environ["HGUSER"] = "test"
1234 1234 os.environ["HGENCODING"] = "ascii"
1235 1235 os.environ["HGENCODINGMODE"] = "strict"
1236 1236 os.environ["HGPORT"] = str(options.port)
1237 1237 os.environ["HGPORT1"] = str(options.port + 1)
1238 1238 os.environ["HGPORT2"] = str(options.port + 2)
1239 1239
1240 1240 if options.with_hg:
1241 1241 INST = None
1242 1242 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1243 1243
1244 1244 # This looks redundant with how Python initializes sys.path from
1245 1245 # the location of the script being executed. Needed because the
1246 1246 # "hg" specified by --with-hg is not the only Python script
1247 1247 # executed in the test suite that needs to import 'mercurial'
1248 1248 # ... which means it's not really redundant at all.
1249 1249 PYTHONDIR = BINDIR
1250 1250 else:
1251 1251 INST = os.path.join(HGTMP, "install")
1252 1252 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1253 1253 PYTHONDIR = os.path.join(INST, "lib", "python")
1254 1254
1255 1255 os.environ["BINDIR"] = BINDIR
1256 1256 os.environ["PYTHON"] = PYTHON
1257 1257
1258 1258 if not options.child:
1259 1259 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1260 1260 os.environ["PATH"] = os.pathsep.join(path)
1261 1261
1262 1262 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1263 1263 # can run .../tests/run-tests.py test-foo where test-foo
1264 1264 # adds an extension to HGRC
1265 1265 pypath = [PYTHONDIR, TESTDIR]
1266 1266 # We have to augment PYTHONPATH, rather than simply replacing
1267 1267 # it, in case external libraries are only available via current
1268 1268 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1269 1269 # are in /opt/subversion.)
1270 1270 oldpypath = os.environ.get(IMPL_PATH)
1271 1271 if oldpypath:
1272 1272 pypath.append(oldpypath)
1273 1273 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1274 1274
1275 1275 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1276 1276
1277 1277 vlog("# Using TESTDIR", TESTDIR)
1278 1278 vlog("# Using HGTMP", HGTMP)
1279 1279 vlog("# Using PATH", os.environ["PATH"])
1280 1280 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1281 1281
1282 1282 try:
1283 1283 if len(tests) > 1 and options.jobs > 1:
1284 1284 runchildren(options, tests)
1285 1285 else:
1286 1286 runtests(options, tests)
1287 1287 finally:
1288 1288 time.sleep(.1)
1289 1289 cleanup(options)
1290 1290
1291 1291 if __name__ == '__main__':
1292 1292 main()
General Comments 0
You need to be logged in to leave comments. Login now