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