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