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