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