##// END OF EJS Templates
run-tests: replace inline python handling with more native scheme...
Matt Mackall -
r15434:5635a401 default
parent child Browse files
Show More
@@ -1,16 +1,19
1 import doctest, tempfile, os, sys
2
3 if __name__ == "__main__":
4 if 'TERM' in os.environ:
5 del os.environ['TERM']
6
7 fd, name = tempfile.mkstemp(suffix='hg-tst')
1 import sys
8 2
9 try:
10 os.write(fd, sys.stdin.read())
11 os.close(fd)
12 failures, _ = doctest.testfile(name, module_relative=False)
13 if failures:
14 sys.exit(1)
15 finally:
16 os.remove(name)
3 globalvars = {}
4 localvars = {}
5 lines = sys.stdin.readlines()
6 while lines:
7 l = lines.pop(0)
8 if l.startswith('SALT'):
9 print l[:-1]
10 elif l.startswith('>>> '):
11 snippet = l[4:]
12 while lines and lines[0].startswith('... '):
13 l = lines.pop(0)
14 snippet += "\n" + l[4:]
15 c = compile(snippet, '<heredoc>', 'single')
16 try:
17 exec c in globalvars, localvars
18 except Exception, inst:
19 print repr(inst)
@@ -1,1253 +1,1256
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 562 # We generate a shell script which outputs unique markers to line
563 563 # up script results with our source. These markers include input
564 564 # line number and the last return code
565 565 salt = "SALT" + str(time.time())
566 def addsalt(line):
567 script.append('echo %s %s $?\n' % (salt, line))
566 def addsalt(line, inpython):
567 if inpython:
568 script.append('%s %d 0\n' % (salt, line))
569 else:
570 script.append('echo %s %s $?\n' % (salt, line))
568 571
569 572 # After we run the shell script, we re-unify the script output
570 573 # with non-active parts of the source, with synchronization by our
571 574 # SALT line number markers. The after table contains the
572 575 # non-active components, ordered by line number
573 576 after = {}
574 577 pos = prepos = -1
575 578
576 579 # Expected shellscript output
577 580 expected = {}
578 581
579 582 # We keep track of whether or not we're in a Python block so we
580 583 # can generate the surrounding doctest magic
581 584 inpython = False
582 585
583 586 f = open(test)
584 587 t = f.readlines()
585 588 f.close()
586 589
587 590 script = []
588 591 for n, l in enumerate(t):
589 592 if not l.endswith('\n'):
590 593 l += '\n'
591 594 if l.startswith(' >>> '): # python inlines
595 after.setdefault(pos, []).append(l)
596 prepos = pos
597 pos = n
592 598 if not inpython:
593 599 # we've just entered a Python block, add the header
594 600 inpython = True
595 addsalt(n)
601 addsalt(prepos, False) # make sure we report the exit code
596 602 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
597 prepos = pos
598 pos = n
603 addsalt(n, True)
604 script.append(l[2:])
605 if l.startswith(' ... '): # python inlines
599 606 after.setdefault(prepos, []).append(l)
600 607 script.append(l[2:])
601 608 elif l.startswith(' $ '): # commands
602 609 if inpython:
603 610 script.append("EOF\n")
604 611 inpython = False
605 612 after.setdefault(pos, []).append(l)
606 613 prepos = pos
607 614 pos = n
608 addsalt(n)
615 addsalt(n, False)
609 616 script.append(l[4:])
610 617 elif l.startswith(' > '): # continuations
611 618 after.setdefault(prepos, []).append(l)
612 619 script.append(l[4:])
613 620 elif l.startswith(' '): # results
614 if inpython:
615 script.append(l[2:])
616 after.setdefault(prepos, []).append(l)
617 else:
618 # queue up a list of expected results
619 expected.setdefault(pos, []).append(l[2:])
621 # queue up a list of expected results
622 expected.setdefault(pos, []).append(l[2:])
620 623 else:
621 624 if inpython:
622 625 script.append("EOF\n")
623 626 inpython = False
624 627 # non-command/result - queue up for merged output
625 628 after.setdefault(pos, []).append(l)
626 629
627 630 if inpython:
628 631 script.append("EOF\n")
629 addsalt(n + 1)
632 addsalt(n + 1, False)
630 633
631 634 # Write out the script and execute it
632 635 fd, name = tempfile.mkstemp(suffix='hg-tst')
633 636 try:
634 637 for l in script:
635 638 os.write(fd, l)
636 639 os.close(fd)
637 640
638 641 cmd = '"%s" "%s"' % (options.shell, name)
639 642 vlog("# Running", cmd)
640 643 exitcode, output = run(cmd, wd, options, replacements)
641 644 # do not merge output if skipped, return hghave message instead
642 645 # similarly, with --debug, output is None
643 646 if exitcode == SKIPPED_STATUS or output is None:
644 647 return exitcode, output
645 648 finally:
646 649 os.remove(name)
647 650
648 651 # Merge the script output back into a unified test
649 652
650 653 pos = -1
651 654 postout = []
652 655 ret = 0
653 656 for n, l in enumerate(output):
654 657 lout, lcmd = l, None
655 658 if salt in l:
656 659 lout, lcmd = l.split(salt, 1)
657 660
658 661 if lout:
659 662 if lcmd:
660 663 # output block had no trailing newline, clean up
661 664 lout += ' (no-eol)\n'
662 665
663 666 # find the expected output at the current position
664 667 el = None
665 668 if pos in expected and expected[pos]:
666 669 el = expected[pos].pop(0)
667 670
668 671 if linematch(el, lout):
669 672 postout.append(" " + el)
670 673 else:
671 674 if needescape(lout):
672 675 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
673 676 postout.append(" " + lout) # let diff deal with it
674 677
675 678 if lcmd:
676 679 # add on last return code
677 680 ret = int(lcmd.split()[1])
678 681 if ret != 0:
679 682 postout.append(" [%s]\n" % ret)
680 683 if pos in after:
681 684 # merge in non-active test bits
682 685 postout += after.pop(pos)
683 686 pos = int(lcmd.split()[0])
684 687
685 688 if pos in after:
686 689 postout += after.pop(pos)
687 690
688 691 return exitcode, postout
689 692
690 693 wifexited = getattr(os, "WIFEXITED", lambda x: False)
691 694 def run(cmd, wd, options, replacements):
692 695 """Run command in a sub-process, capturing the output (stdout and stderr).
693 696 Return a tuple (exitcode, output). output is None in debug mode."""
694 697 # TODO: Use subprocess.Popen if we're running on Python 2.4
695 698 if options.debug:
696 699 proc = subprocess.Popen(cmd, shell=True, cwd=wd)
697 700 ret = proc.wait()
698 701 return (ret, None)
699 702
700 703 proc = Popen4(cmd, wd, options.timeout)
701 704 def cleanup():
702 705 terminate(proc)
703 706 ret = proc.wait()
704 707 if ret == 0:
705 708 ret = signal.SIGTERM << 8
706 709 killdaemons()
707 710 return ret
708 711
709 712 output = ''
710 713 proc.tochild.close()
711 714
712 715 try:
713 716 output = proc.fromchild.read()
714 717 except KeyboardInterrupt:
715 718 vlog('# Handling keyboard interrupt')
716 719 cleanup()
717 720 raise
718 721
719 722 ret = proc.wait()
720 723 if wifexited(ret):
721 724 ret = os.WEXITSTATUS(ret)
722 725
723 726 if proc.timeout:
724 727 ret = 'timeout'
725 728
726 729 if ret:
727 730 killdaemons()
728 731
729 732 for s, r in replacements:
730 733 output = re.sub(s, r, output)
731 734 return ret, splitnewlines(output)
732 735
733 736 def runone(options, test):
734 737 '''tristate output:
735 738 None -> skipped
736 739 True -> passed
737 740 False -> failed'''
738 741
739 742 global results, resultslock, iolock
740 743
741 744 testpath = os.path.join(TESTDIR, test)
742 745
743 746 def result(l, e):
744 747 resultslock.acquire()
745 748 results[l].append(e)
746 749 resultslock.release()
747 750
748 751 def skip(msg):
749 752 if not options.verbose:
750 753 result('s', (test, msg))
751 754 else:
752 755 iolock.acquire()
753 756 print "\nSkipping %s: %s" % (testpath, msg)
754 757 iolock.release()
755 758 return None
756 759
757 760 def fail(msg, ret):
758 761 if not options.nodiff:
759 762 iolock.acquire()
760 763 print "\nERROR: %s %s" % (testpath, msg)
761 764 iolock.release()
762 765 if (not ret and options.interactive
763 766 and os.path.exists(testpath + ".err")):
764 767 iolock.acquire()
765 768 print "Accept this change? [n] ",
766 769 answer = sys.stdin.readline().strip()
767 770 iolock.release()
768 771 if answer.lower() in "y yes".split():
769 772 if test.endswith(".t"):
770 773 rename(testpath + ".err", testpath)
771 774 else:
772 775 rename(testpath + ".err", testpath + ".out")
773 776 result('p', test)
774 777 return
775 778 result('f', (test, msg))
776 779
777 780 def success():
778 781 result('p', test)
779 782
780 783 def ignore(msg):
781 784 result('i', (test, msg))
782 785
783 786 if (os.path.basename(test).startswith("test-") and '~' not in test and
784 787 ('.' not in test or test.endswith('.py') or
785 788 test.endswith('.bat') or test.endswith('.t'))):
786 789 if not os.path.exists(test):
787 790 skip("doesn't exist")
788 791 return None
789 792 else:
790 793 vlog('# Test file', test, 'not supported, ignoring')
791 794 return None # not a supported test, don't record
792 795
793 796 if not (options.whitelisted and test in options.whitelisted):
794 797 if options.blacklist and test in options.blacklist:
795 798 skip("blacklisted")
796 799 return None
797 800
798 801 if options.retest and not os.path.exists(test + ".err"):
799 802 ignore("not retesting")
800 803 return None
801 804
802 805 if options.keywords:
803 806 fp = open(test)
804 807 t = fp.read().lower() + test.lower()
805 808 fp.close()
806 809 for k in options.keywords.lower().split():
807 810 if k in t:
808 811 break
809 812 else:
810 813 ignore("doesn't match keyword")
811 814 return None
812 815
813 816 vlog("# Test", test)
814 817
815 818 # create a fresh hgrc
816 819 hgrc = open(HGRCPATH, 'w+')
817 820 hgrc.write('[ui]\n')
818 821 hgrc.write('slash = True\n')
819 822 hgrc.write('[defaults]\n')
820 823 hgrc.write('backout = -d "0 0"\n')
821 824 hgrc.write('commit = -d "0 0"\n')
822 825 hgrc.write('tag = -d "0 0"\n')
823 826 if options.inotify:
824 827 hgrc.write('[extensions]\n')
825 828 hgrc.write('inotify=\n')
826 829 hgrc.write('[inotify]\n')
827 830 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
828 831 hgrc.write('appendpid=True\n')
829 832 if options.extra_config_opt:
830 833 for opt in options.extra_config_opt:
831 834 section, key = opt.split('.', 1)
832 835 assert '=' in key, ('extra config opt %s must '
833 836 'have an = for assignment' % opt)
834 837 hgrc.write('[%s]\n%s\n' % (section, key))
835 838 hgrc.close()
836 839
837 840 ref = os.path.join(TESTDIR, test+".out")
838 841 err = os.path.join(TESTDIR, test+".err")
839 842 if os.path.exists(err):
840 843 os.remove(err) # Remove any previous output files
841 844 try:
842 845 tf = open(testpath)
843 846 firstline = tf.readline().rstrip()
844 847 tf.close()
845 848 except:
846 849 firstline = ''
847 850 lctest = test.lower()
848 851
849 852 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
850 853 runner = pytest
851 854 elif lctest.endswith('.t'):
852 855 runner = tsttest
853 856 ref = testpath
854 857 else:
855 858 # do not try to run non-executable programs
856 859 if not os.access(testpath, os.X_OK):
857 860 return skip("not executable")
858 861 runner = shtest
859 862
860 863 # Make a tmp subdirectory to work in
861 864 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
862 865 os.path.join(HGTMP, os.path.basename(test))
863 866
864 867 os.mkdir(testtmp)
865 868 ret, out = runner(testpath, testtmp, options, [
866 869 (re.escape(testtmp), '$TESTTMP'),
867 870 (r':%s\b' % options.port, ':$HGPORT'),
868 871 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
869 872 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
870 873 ])
871 874 vlog("# Ret was:", ret)
872 875
873 876 mark = '.'
874 877
875 878 skipped = (ret == SKIPPED_STATUS)
876 879
877 880 # If we're not in --debug mode and reference output file exists,
878 881 # check test output against it.
879 882 if options.debug:
880 883 refout = None # to match "out is None"
881 884 elif os.path.exists(ref):
882 885 f = open(ref, "r")
883 886 refout = list(splitnewlines(f.read()))
884 887 f.close()
885 888 else:
886 889 refout = []
887 890
888 891 if (ret != 0 or out != refout) and not skipped and not options.debug:
889 892 # Save errors to a file for diagnosis
890 893 f = open(err, "wb")
891 894 for line in out:
892 895 f.write(line)
893 896 f.close()
894 897
895 898 if skipped:
896 899 mark = 's'
897 900 if out is None: # debug mode: nothing to parse
898 901 missing = ['unknown']
899 902 failed = None
900 903 else:
901 904 missing, failed = parsehghaveoutput(out)
902 905 if not missing:
903 906 missing = ['irrelevant']
904 907 if failed:
905 908 fail("hghave failed checking for %s" % failed[-1], ret)
906 909 skipped = False
907 910 else:
908 911 skip(missing[-1])
909 912 elif ret == 'timeout':
910 913 mark = 't'
911 914 fail("timed out", ret)
912 915 elif out != refout:
913 916 mark = '!'
914 917 if not options.nodiff:
915 918 iolock.acquire()
916 919 if options.view:
917 920 os.system("%s %s %s" % (options.view, ref, err))
918 921 else:
919 922 showdiff(refout, out, ref, err)
920 923 iolock.release()
921 924 if ret:
922 925 fail("output changed and returned error code %d" % ret, ret)
923 926 else:
924 927 fail("output changed", ret)
925 928 ret = 1
926 929 elif ret:
927 930 mark = '!'
928 931 fail("returned error code %d" % ret, ret)
929 932 else:
930 933 success()
931 934
932 935 if not options.verbose:
933 936 iolock.acquire()
934 937 sys.stdout.write(mark)
935 938 sys.stdout.flush()
936 939 iolock.release()
937 940
938 941 killdaemons()
939 942
940 943 if not options.keep_tmpdir:
941 944 shutil.rmtree(testtmp, True)
942 945 if skipped:
943 946 return None
944 947 return ret == 0
945 948
946 949 _hgpath = None
947 950
948 951 def _gethgpath():
949 952 """Return the path to the mercurial package that is actually found by
950 953 the current Python interpreter."""
951 954 global _hgpath
952 955 if _hgpath is not None:
953 956 return _hgpath
954 957
955 958 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
956 959 pipe = os.popen(cmd % PYTHON)
957 960 try:
958 961 _hgpath = pipe.read().strip()
959 962 finally:
960 963 pipe.close()
961 964 return _hgpath
962 965
963 966 def _checkhglib(verb):
964 967 """Ensure that the 'mercurial' package imported by python is
965 968 the one we expect it to be. If not, print a warning to stderr."""
966 969 expecthg = os.path.join(PYTHONDIR, 'mercurial')
967 970 actualhg = _gethgpath()
968 971 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
969 972 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
970 973 ' (expected %s)\n'
971 974 % (verb, actualhg, expecthg))
972 975
973 976 def runchildren(options, tests):
974 977 if INST:
975 978 installhg(options)
976 979 _checkhglib("Testing")
977 980
978 981 optcopy = dict(options.__dict__)
979 982 optcopy['jobs'] = 1
980 983
981 984 # Because whitelist has to override keyword matches, we have to
982 985 # actually load the whitelist in the children as well, so we allow
983 986 # the list of whitelist files to pass through and be parsed in the
984 987 # children, but not the dict of whitelisted tests resulting from
985 988 # the parse, used here to override blacklisted tests.
986 989 whitelist = optcopy['whitelisted'] or []
987 990 del optcopy['whitelisted']
988 991
989 992 blacklist = optcopy['blacklist'] or []
990 993 del optcopy['blacklist']
991 994 blacklisted = []
992 995
993 996 if optcopy['with_hg'] is None:
994 997 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
995 998 optcopy.pop('anycoverage', None)
996 999
997 1000 opts = []
998 1001 for opt, value in optcopy.iteritems():
999 1002 name = '--' + opt.replace('_', '-')
1000 1003 if value is True:
1001 1004 opts.append(name)
1002 1005 elif isinstance(value, list):
1003 1006 for v in value:
1004 1007 opts.append(name + '=' + str(v))
1005 1008 elif value is not None:
1006 1009 opts.append(name + '=' + str(value))
1007 1010
1008 1011 tests.reverse()
1009 1012 jobs = [[] for j in xrange(options.jobs)]
1010 1013 while tests:
1011 1014 for job in jobs:
1012 1015 if not tests:
1013 1016 break
1014 1017 test = tests.pop()
1015 1018 if test not in whitelist and test in blacklist:
1016 1019 blacklisted.append(test)
1017 1020 else:
1018 1021 job.append(test)
1019 1022 fps = {}
1020 1023
1021 1024 for j, job in enumerate(jobs):
1022 1025 if not job:
1023 1026 continue
1024 1027 rfd, wfd = os.pipe()
1025 1028 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
1026 1029 childtmp = os.path.join(HGTMP, 'child%d' % j)
1027 1030 childopts += ['--tmpdir', childtmp]
1028 1031 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
1029 1032 vlog(' '.join(cmdline))
1030 1033 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
1031 1034 os.close(wfd)
1032 1035 signal.signal(signal.SIGINT, signal.SIG_IGN)
1033 1036 failures = 0
1034 1037 tested, skipped, failed = 0, 0, 0
1035 1038 skips = []
1036 1039 fails = []
1037 1040 while fps:
1038 1041 pid, status = os.wait()
1039 1042 fp = fps.pop(pid)
1040 1043 l = fp.read().splitlines()
1041 1044 try:
1042 1045 test, skip, fail = map(int, l[:3])
1043 1046 except ValueError:
1044 1047 test, skip, fail = 0, 0, 0
1045 1048 split = -fail or len(l)
1046 1049 for s in l[3:split]:
1047 1050 skips.append(s.split(" ", 1))
1048 1051 for s in l[split:]:
1049 1052 fails.append(s.split(" ", 1))
1050 1053 tested += test
1051 1054 skipped += skip
1052 1055 failed += fail
1053 1056 vlog('pid %d exited, status %d' % (pid, status))
1054 1057 failures |= status
1055 1058 print
1056 1059 skipped += len(blacklisted)
1057 1060 if not options.noskips:
1058 1061 for s in skips:
1059 1062 print "Skipped %s: %s" % (s[0], s[1])
1060 1063 for s in blacklisted:
1061 1064 print "Skipped %s: blacklisted" % s
1062 1065 for s in fails:
1063 1066 print "Failed %s: %s" % (s[0], s[1])
1064 1067
1065 1068 _checkhglib("Tested")
1066 1069 print "# Ran %d tests, %d skipped, %d failed." % (
1067 1070 tested, skipped, failed)
1068 1071
1069 1072 if options.anycoverage:
1070 1073 outputcoverage(options)
1071 1074 sys.exit(failures != 0)
1072 1075
1073 1076 results = dict(p=[], f=[], s=[], i=[])
1074 1077 resultslock = threading.Lock()
1075 1078 iolock = threading.Lock()
1076 1079
1077 1080 def runqueue(options, tests, results):
1078 1081 for test in tests:
1079 1082 ret = runone(options, test)
1080 1083 if options.first and ret is not None and not ret:
1081 1084 break
1082 1085
1083 1086 def runtests(options, tests):
1084 1087 global DAEMON_PIDS, HGRCPATH
1085 1088 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1086 1089 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1087 1090
1088 1091 try:
1089 1092 if INST:
1090 1093 installhg(options)
1091 1094 _checkhglib("Testing")
1092 1095
1093 1096 if options.restart:
1094 1097 orig = list(tests)
1095 1098 while tests:
1096 1099 if os.path.exists(tests[0] + ".err"):
1097 1100 break
1098 1101 tests.pop(0)
1099 1102 if not tests:
1100 1103 print "running all tests"
1101 1104 tests = orig
1102 1105
1103 1106 runqueue(options, tests, results)
1104 1107
1105 1108 failed = len(results['f'])
1106 1109 tested = len(results['p']) + failed
1107 1110 skipped = len(results['s'])
1108 1111 ignored = len(results['i'])
1109 1112
1110 1113 if options.child:
1111 1114 fp = os.fdopen(options.child, 'w')
1112 1115 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1113 1116 for s in results['s']:
1114 1117 fp.write("%s %s\n" % s)
1115 1118 for s in results['f']:
1116 1119 fp.write("%s %s\n" % s)
1117 1120 fp.close()
1118 1121 else:
1119 1122 print
1120 1123 for s in results['s']:
1121 1124 print "Skipped %s: %s" % s
1122 1125 for s in results['f']:
1123 1126 print "Failed %s: %s" % s
1124 1127 _checkhglib("Tested")
1125 1128 print "# Ran %d tests, %d skipped, %d failed." % (
1126 1129 tested, skipped + ignored, failed)
1127 1130
1128 1131 if options.anycoverage:
1129 1132 outputcoverage(options)
1130 1133 except KeyboardInterrupt:
1131 1134 failed = True
1132 1135 print "\ninterrupted!"
1133 1136
1134 1137 if failed:
1135 1138 sys.exit(1)
1136 1139
1137 1140 def main():
1138 1141 (options, args) = parseargs()
1139 1142 if not options.child:
1140 1143 os.umask(022)
1141 1144
1142 1145 checktools()
1143 1146
1144 1147 if len(args) == 0:
1145 1148 args = os.listdir(".")
1146 1149 args.sort()
1147 1150
1148 1151 tests = args
1149 1152
1150 1153 # Reset some environment variables to well-known values so that
1151 1154 # the tests produce repeatable output.
1152 1155 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1153 1156 os.environ['TZ'] = 'GMT'
1154 1157 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1155 1158 os.environ['CDPATH'] = ''
1156 1159 os.environ['COLUMNS'] = '80'
1157 1160 os.environ['GREP_OPTIONS'] = ''
1158 1161 os.environ['http_proxy'] = ''
1159 1162 os.environ['no_proxy'] = ''
1160 1163 os.environ['NO_PROXY'] = ''
1161 1164
1162 1165 # unset env related to hooks
1163 1166 for k in os.environ.keys():
1164 1167 if k.startswith('HG_'):
1165 1168 # can't remove on solaris
1166 1169 os.environ[k] = ''
1167 1170 del os.environ[k]
1168 1171
1169 1172 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1170 1173 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1171 1174 if options.tmpdir:
1172 1175 options.keep_tmpdir = True
1173 1176 tmpdir = options.tmpdir
1174 1177 if os.path.exists(tmpdir):
1175 1178 # Meaning of tmpdir has changed since 1.3: we used to create
1176 1179 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1177 1180 # tmpdir already exists.
1178 1181 sys.exit("error: temp dir %r already exists" % tmpdir)
1179 1182
1180 1183 # Automatically removing tmpdir sounds convenient, but could
1181 1184 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1182 1185 # or "--tmpdir=$HOME".
1183 1186 #vlog("# Removing temp dir", tmpdir)
1184 1187 #shutil.rmtree(tmpdir)
1185 1188 os.makedirs(tmpdir)
1186 1189 else:
1187 1190 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1188 1191 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1189 1192 DAEMON_PIDS = None
1190 1193 HGRCPATH = None
1191 1194
1192 1195 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1193 1196 os.environ["HGMERGE"] = "internal:merge"
1194 1197 os.environ["HGUSER"] = "test"
1195 1198 os.environ["HGENCODING"] = "ascii"
1196 1199 os.environ["HGENCODINGMODE"] = "strict"
1197 1200 os.environ["HGPORT"] = str(options.port)
1198 1201 os.environ["HGPORT1"] = str(options.port + 1)
1199 1202 os.environ["HGPORT2"] = str(options.port + 2)
1200 1203
1201 1204 if options.with_hg:
1202 1205 INST = None
1203 1206 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1204 1207
1205 1208 # This looks redundant with how Python initializes sys.path from
1206 1209 # the location of the script being executed. Needed because the
1207 1210 # "hg" specified by --with-hg is not the only Python script
1208 1211 # executed in the test suite that needs to import 'mercurial'
1209 1212 # ... which means it's not really redundant at all.
1210 1213 PYTHONDIR = BINDIR
1211 1214 else:
1212 1215 INST = os.path.join(HGTMP, "install")
1213 1216 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1214 1217 PYTHONDIR = os.path.join(INST, "lib", "python")
1215 1218
1216 1219 os.environ["BINDIR"] = BINDIR
1217 1220 os.environ["PYTHON"] = PYTHON
1218 1221
1219 1222 if not options.child:
1220 1223 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1221 1224 os.environ["PATH"] = os.pathsep.join(path)
1222 1225
1223 1226 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1224 1227 # can run .../tests/run-tests.py test-foo where test-foo
1225 1228 # adds an extension to HGRC
1226 1229 pypath = [PYTHONDIR, TESTDIR]
1227 1230 # We have to augment PYTHONPATH, rather than simply replacing
1228 1231 # it, in case external libraries are only available via current
1229 1232 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1230 1233 # are in /opt/subversion.)
1231 1234 oldpypath = os.environ.get(IMPL_PATH)
1232 1235 if oldpypath:
1233 1236 pypath.append(oldpypath)
1234 1237 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1235 1238
1236 1239 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1237 1240
1238 1241 vlog("# Using TESTDIR", TESTDIR)
1239 1242 vlog("# Using HGTMP", HGTMP)
1240 1243 vlog("# Using PATH", os.environ["PATH"])
1241 1244 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1242 1245
1243 1246 try:
1244 1247 if len(tests) > 1 and options.jobs > 1:
1245 1248 runchildren(options, tests)
1246 1249 else:
1247 1250 runtests(options, tests)
1248 1251 finally:
1249 1252 time.sleep(1)
1250 1253 cleanup(options)
1251 1254
1252 1255 if __name__ == '__main__':
1253 1256 main()
@@ -1,53 +1,58
1 1 Simple commands:
2 2
3 3 $ echo foo
4 4 foo
5 5 $ printf 'oh no'
6 6 oh no (no-eol)
7 7 $ printf 'bar\nbaz\n' | cat
8 8 bar
9 9 baz
10 10
11 11 Multi-line command:
12 12
13 13 $ foo() {
14 14 > echo bar
15 15 > }
16 16 $ foo
17 17 bar
18 18
19 Return codes before inline python:
20
21 $ false
22 [1]
23
19 24 Doctest commands:
20 25
21 26 >>> print 'foo'
22 27 foo
23 28 $ echo interleaved
24 29 interleaved
25 30 >>> for c in 'xyz':
26 31 ... print c
27 32 x
28 33 y
29 34 z
30 35 >>> print
31 <BLANKLINE>
36
32 37
33 38 Regular expressions:
34 39
35 40 $ echo foobarbaz
36 41 foobar.* (re)
37 42 $ echo barbazquux
38 43 .*quux.* (re)
39 44
40 45 Globs:
41 46
42 47 $ printf '* \\foobarbaz {10}\n'
43 48 \* \\fo?bar* {10} (glob)
44 49
45 50 Literal match ending in " (re)":
46 51
47 52 $ echo 'foo (re)'
48 53 foo (re)
49 54
50 55 Exit code:
51 56
52 57 $ (exit 1)
53 58 [1]
General Comments 0
You need to be logged in to leave comments. Login now