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