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