##// END OF EJS Templates
run-tests.py: use --prefix instead of --home...
Benoit Boissinot -
r7139:bcbba59e default
parent child Browse files
Show More
@@ -1,642 +1,642 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
8 8 # of the GNU General Public License, incorporated herein by reference.
9 9
10 10 import difflib
11 11 import errno
12 12 import optparse
13 13 import os
14 14 import popen2
15 15 import shutil
16 16 import signal
17 17 import sys
18 18 import tempfile
19 19 import time
20 20
21 21 # reserved exit code to skip test (used by hghave)
22 22 SKIPPED_STATUS = 80
23 23 SKIPPED_PREFIX = 'skipped: '
24 24
25 25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
26 26
27 27 defaults = {
28 28 'jobs': ('HGTEST_JOBS', 1),
29 29 'timeout': ('HGTEST_TIMEOUT', 180),
30 30 'port': ('HGTEST_PORT', 20059),
31 31 }
32 32
33 33 parser = optparse.OptionParser("%prog [options] [tests]")
34 34 parser.add_option("-C", "--annotate", action="store_true",
35 35 help="output files annotated with coverage")
36 36 parser.add_option("--child", type="int",
37 37 help="run as child process, summary to given fd")
38 38 parser.add_option("-c", "--cover", action="store_true",
39 39 help="print a test coverage report")
40 40 parser.add_option("-f", "--first", action="store_true",
41 41 help="exit on the first test failure")
42 42 parser.add_option("-i", "--interactive", action="store_true",
43 43 help="prompt to accept changed output")
44 44 parser.add_option("-j", "--jobs", type="int",
45 45 help="number of jobs to run in parallel"
46 46 " (default: $%s or %d)" % defaults['jobs'])
47 47 parser.add_option("--keep-tmpdir", action="store_true",
48 48 help="keep temporary directory after running tests"
49 49 " (best used with --tmpdir)")
50 50 parser.add_option("-R", "--restart", action="store_true",
51 51 help="restart at last error")
52 52 parser.add_option("-p", "--port", type="int",
53 53 help="port on which servers should listen"
54 54 " (default: $%s or %d)" % defaults['port'])
55 55 parser.add_option("-r", "--retest", action="store_true",
56 56 help="retest failed tests")
57 57 parser.add_option("-s", "--cover_stdlib", action="store_true",
58 58 help="print a test coverage report inc. standard libraries")
59 59 parser.add_option("-t", "--timeout", type="int",
60 60 help="kill errant tests after TIMEOUT seconds"
61 61 " (default: $%s or %d)" % defaults['timeout'])
62 62 parser.add_option("--tmpdir", type="string",
63 63 help="run tests in the given temporary directory")
64 64 parser.add_option("-v", "--verbose", action="store_true",
65 65 help="output verbose messages")
66 66 parser.add_option("--with-hg", type="string",
67 67 help="test existing install at given location")
68 68
69 69 for option, default in defaults.items():
70 70 defaults[option] = int(os.environ.get(*default))
71 71 parser.set_defaults(**defaults)
72 72 (options, args) = parser.parse_args()
73 73 verbose = options.verbose
74 74 coverage = options.cover or options.cover_stdlib or options.annotate
75 75 python = sys.executable
76 76
77 77 if options.jobs < 1:
78 78 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
79 79 sys.exit(1)
80 80 if options.interactive and options.jobs > 1:
81 81 print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1'
82 82 sys.exit(1)
83 83
84 84 def rename(src, dst):
85 85 """Like os.rename(), trade atomicity and opened files friendliness
86 86 for existing destination support.
87 87 """
88 88 shutil.copy(src, dst)
89 89 os.remove(src)
90 90
91 91 def vlog(*msg):
92 92 if verbose:
93 93 for m in msg:
94 94 print m,
95 95 print
96 96
97 97 def splitnewlines(text):
98 98 '''like str.splitlines, but only split on newlines.
99 99 keep line endings.'''
100 100 i = 0
101 101 lines = []
102 102 while True:
103 103 n = text.find('\n', i)
104 104 if n == -1:
105 105 last = text[i:]
106 106 if last:
107 107 lines.append(last)
108 108 return lines
109 109 lines.append(text[i:n+1])
110 110 i = n + 1
111 111
112 112 def extract_missing_features(lines):
113 113 '''Extract missing/unknown features log lines as a list'''
114 114 missing = []
115 115 for line in lines:
116 116 if not line.startswith(SKIPPED_PREFIX):
117 117 continue
118 118 line = line.splitlines()[0]
119 119 missing.append(line[len(SKIPPED_PREFIX):])
120 120
121 121 return missing
122 122
123 123 def show_diff(expected, output):
124 124 for line in difflib.unified_diff(expected, output,
125 125 "Expected output", "Test output"):
126 126 sys.stdout.write(line)
127 127
128 128 def find_program(program):
129 129 """Search PATH for a executable program"""
130 130 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
131 131 name = os.path.join(p, program)
132 132 if os.access(name, os.X_OK):
133 133 return name
134 134 return None
135 135
136 136 def check_required_tools():
137 137 # Before we go any further, check for pre-requisite tools
138 138 # stuff from coreutils (cat, rm, etc) are not tested
139 139 for p in required_tools:
140 140 if os.name == 'nt':
141 141 p += '.exe'
142 142 found = find_program(p)
143 143 if found:
144 144 vlog("# Found prerequisite", p, "at", found)
145 145 else:
146 146 print "WARNING: Did not find prerequisite tool: "+p
147 147
148 148 def cleanup_exit():
149 149 if not options.keep_tmpdir:
150 150 if verbose:
151 151 print "# Cleaning up HGTMP", HGTMP
152 152 shutil.rmtree(HGTMP, True)
153 153
154 154 def use_correct_python():
155 155 # some tests run python interpreter. they must use same
156 156 # interpreter we use or bad things will happen.
157 157 exedir, exename = os.path.split(sys.executable)
158 158 if exename == 'python':
159 159 path = find_program('python')
160 160 if os.path.dirname(path) == exedir:
161 161 return
162 162 vlog('# Making python executable in test path use correct Python')
163 163 my_python = os.path.join(BINDIR, 'python')
164 164 try:
165 165 os.symlink(sys.executable, my_python)
166 166 except AttributeError:
167 167 # windows fallback
168 168 shutil.copyfile(sys.executable, my_python)
169 169 shutil.copymode(sys.executable, my_python)
170 170
171 171 def install_hg():
172 172 global python
173 173 vlog("# Performing temporary installation of HG")
174 174 installerrs = os.path.join("tests", "install.err")
175 175
176 176 # Run installer in hg root
177 177 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
178 178 cmd = ('%s setup.py clean --all'
179 ' install --force --home="%s" --install-lib="%s"'
179 ' install --force --prefix="%s" --install-lib="%s"'
180 180 ' --install-scripts="%s" >%s 2>&1'
181 181 % (sys.executable, INST, PYTHONDIR, BINDIR, installerrs))
182 182 vlog("# Running", cmd)
183 183 if os.system(cmd) == 0:
184 184 if not verbose:
185 185 os.remove(installerrs)
186 186 else:
187 187 f = open(installerrs)
188 188 for line in f:
189 189 print line,
190 190 f.close()
191 191 sys.exit(1)
192 192 os.chdir(TESTDIR)
193 193
194 194 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
195 195
196 196 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
197 197 pythonpath = os.environ.get("PYTHONPATH")
198 198 if pythonpath:
199 199 pythonpath = pydir + os.pathsep + pythonpath
200 200 else:
201 201 pythonpath = pydir
202 202 os.environ["PYTHONPATH"] = pythonpath
203 203
204 204 use_correct_python()
205 205 global hgpkg
206 206 hgpkg = _hgpath()
207 207
208 208 if coverage:
209 209 vlog("# Installing coverage wrapper")
210 210 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
211 211 if os.path.exists(COVERAGE_FILE):
212 212 os.unlink(COVERAGE_FILE)
213 213 # Create a wrapper script to invoke hg via coverage.py
214 214 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
215 215 f = open(os.path.join(BINDIR, 'hg'), 'w')
216 216 f.write('#!' + sys.executable + '\n')
217 217 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
218 218 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
219 219 (os.path.join(TESTDIR, 'coverage.py'),
220 220 os.path.join(BINDIR, '_hg.py')))
221 221 f.close()
222 222 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
223 223 python = '"%s" "%s" -x' % (sys.executable,
224 224 os.path.join(TESTDIR,'coverage.py'))
225 225
226 226 def output_coverage():
227 227 vlog("# Producing coverage report")
228 228 omit = [BINDIR, TESTDIR, PYTHONDIR]
229 229 if not options.cover_stdlib:
230 230 # Exclude as system paths (ignoring empty strings seen on win)
231 231 omit += [x for x in sys.path if x != '']
232 232 omit = ','.join(omit)
233 233 os.chdir(PYTHONDIR)
234 234 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
235 235 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
236 236 vlog("# Running: "+cmd)
237 237 os.system(cmd)
238 238 if options.annotate:
239 239 adir = os.path.join(TESTDIR, 'annotated')
240 240 if not os.path.isdir(adir):
241 241 os.mkdir(adir)
242 242 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
243 243 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
244 244 adir, omit)
245 245 vlog("# Running: "+cmd)
246 246 os.system(cmd)
247 247
248 248 class Timeout(Exception):
249 249 pass
250 250
251 251 def alarmed(signum, frame):
252 252 raise Timeout
253 253
254 254 def run(cmd):
255 255 """Run command in a sub-process, capturing the output (stdout and stderr).
256 256 Return the exist code, and output."""
257 257 # TODO: Use subprocess.Popen if we're running on Python 2.4
258 258 if os.name == 'nt':
259 259 tochild, fromchild = os.popen4(cmd)
260 260 tochild.close()
261 261 output = fromchild.read()
262 262 ret = fromchild.close()
263 263 if ret == None:
264 264 ret = 0
265 265 else:
266 266 proc = popen2.Popen4(cmd)
267 267 try:
268 268 output = ''
269 269 proc.tochild.close()
270 270 output = proc.fromchild.read()
271 271 ret = proc.wait()
272 272 if os.WIFEXITED(ret):
273 273 ret = os.WEXITSTATUS(ret)
274 274 except Timeout:
275 275 vlog('# Process %d timed out - killing it' % proc.pid)
276 276 os.kill(proc.pid, signal.SIGTERM)
277 277 ret = proc.wait()
278 278 if ret == 0:
279 279 ret = signal.SIGTERM << 8
280 280 output += ("\n### Abort: timeout after %d seconds.\n"
281 281 % options.timeout)
282 282 return ret, splitnewlines(output)
283 283
284 284 def run_one(test, skips, fails):
285 285 '''tristate output:
286 286 None -> skipped
287 287 True -> passed
288 288 False -> failed'''
289 289
290 290 def skip(msg):
291 291 if not verbose:
292 292 skips.append((test, msg))
293 293 else:
294 294 print "\nSkipping %s: %s" % (test, msg)
295 295 return None
296 296
297 297 def fail(msg):
298 298 fails.append((test, msg))
299 299 print "\nERROR: %s %s" % (test, msg)
300 300 return None
301 301
302 302 vlog("# Test", test)
303 303
304 304 # create a fresh hgrc
305 305 hgrc = file(HGRCPATH, 'w+')
306 306 hgrc.write('[ui]\n')
307 307 hgrc.write('slash = True\n')
308 308 hgrc.write('[defaults]\n')
309 309 hgrc.write('backout = -d "0 0"\n')
310 310 hgrc.write('commit = -d "0 0"\n')
311 311 hgrc.write('debugrawcommit = -d "0 0"\n')
312 312 hgrc.write('tag = -d "0 0"\n')
313 313 hgrc.close()
314 314
315 315 err = os.path.join(TESTDIR, test+".err")
316 316 ref = os.path.join(TESTDIR, test+".out")
317 317 testpath = os.path.join(TESTDIR, test)
318 318
319 319 if os.path.exists(err):
320 320 os.remove(err) # Remove any previous output files
321 321
322 322 # Make a tmp subdirectory to work in
323 323 tmpd = os.path.join(HGTMP, test)
324 324 os.mkdir(tmpd)
325 325 os.chdir(tmpd)
326 326
327 327 try:
328 328 tf = open(testpath)
329 329 firstline = tf.readline().rstrip()
330 330 tf.close()
331 331 except:
332 332 firstline = ''
333 333 lctest = test.lower()
334 334
335 335 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
336 336 cmd = '%s "%s"' % (python, testpath)
337 337 elif lctest.endswith('.bat'):
338 338 # do not run batch scripts on non-windows
339 339 if os.name != 'nt':
340 340 return skip("batch script")
341 341 # To reliably get the error code from batch files on WinXP,
342 342 # the "cmd /c call" prefix is needed. Grrr
343 343 cmd = 'cmd /c call "%s"' % testpath
344 344 else:
345 345 # do not run shell scripts on windows
346 346 if os.name == 'nt':
347 347 return skip("shell script")
348 348 # do not try to run non-executable programs
349 349 if not os.access(testpath, os.X_OK):
350 350 return skip("not executable")
351 351 cmd = '"%s"' % testpath
352 352
353 353 if options.timeout > 0:
354 354 signal.alarm(options.timeout)
355 355
356 356 vlog("# Running", cmd)
357 357 ret, out = run(cmd)
358 358 vlog("# Ret was:", ret)
359 359
360 360 if options.timeout > 0:
361 361 signal.alarm(0)
362 362
363 363 skipped = (ret == SKIPPED_STATUS)
364 364 # If reference output file exists, check test output against it
365 365 if os.path.exists(ref):
366 366 f = open(ref, "r")
367 367 ref_out = splitnewlines(f.read())
368 368 f.close()
369 369 else:
370 370 ref_out = []
371 371 if skipped:
372 372 missing = extract_missing_features(out)
373 373 if not missing:
374 374 missing = ['irrelevant']
375 375 skip(missing[-1])
376 376 elif out != ref_out:
377 377 if ret:
378 378 fail("output changed and returned error code %d" % ret)
379 379 else:
380 380 fail("output changed")
381 381 show_diff(ref_out, out)
382 382 ret = 1
383 383 elif ret:
384 384 fail("returned error code %d" % ret)
385 385
386 386 if not verbose:
387 387 sys.stdout.write(skipped and 's' or '.')
388 388 sys.stdout.flush()
389 389
390 390 if ret != 0 and not skipped:
391 391 # Save errors to a file for diagnosis
392 392 f = open(err, "wb")
393 393 for line in out:
394 394 f.write(line)
395 395 f.close()
396 396
397 397 # Kill off any leftover daemon processes
398 398 try:
399 399 fp = file(DAEMON_PIDS)
400 400 for line in fp:
401 401 try:
402 402 pid = int(line)
403 403 except ValueError:
404 404 continue
405 405 try:
406 406 os.kill(pid, 0)
407 407 vlog('# Killing daemon process %d' % pid)
408 408 os.kill(pid, signal.SIGTERM)
409 409 time.sleep(0.25)
410 410 os.kill(pid, 0)
411 411 vlog('# Daemon process %d is stuck - really killing it' % pid)
412 412 os.kill(pid, signal.SIGKILL)
413 413 except OSError, err:
414 414 if err.errno != errno.ESRCH:
415 415 raise
416 416 fp.close()
417 417 os.unlink(DAEMON_PIDS)
418 418 except IOError:
419 419 pass
420 420
421 421 os.chdir(TESTDIR)
422 422 if not options.keep_tmpdir:
423 423 shutil.rmtree(tmpd, True)
424 424 if skipped:
425 425 return None
426 426 return ret == 0
427 427
428 428 if not options.child:
429 429 os.umask(022)
430 430
431 431 check_required_tools()
432 432
433 433 # Reset some environment variables to well-known values so that
434 434 # the tests produce repeatable output.
435 435 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
436 436 os.environ['TZ'] = 'GMT'
437 437 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
438 438
439 439 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
440 440 HGTMP = os.environ['HGTMP'] = tempfile.mkdtemp('', 'hgtests.', options.tmpdir)
441 441 DAEMON_PIDS = None
442 442 HGRCPATH = None
443 443
444 444 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
445 445 os.environ["HGMERGE"] = "internal:merge"
446 446 os.environ["HGUSER"] = "test"
447 447 os.environ["HGENCODING"] = "ascii"
448 448 os.environ["HGENCODINGMODE"] = "strict"
449 449 os.environ["HGPORT"] = str(options.port)
450 450 os.environ["HGPORT1"] = str(options.port + 1)
451 451 os.environ["HGPORT2"] = str(options.port + 2)
452 452
453 453 if options.with_hg:
454 454 INST = options.with_hg
455 455 else:
456 456 INST = os.path.join(HGTMP, "install")
457 457 BINDIR = os.path.join(INST, "bin")
458 458 PYTHONDIR = os.path.join(INST, "lib", "python")
459 459 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
460 460
461 461 def _hgpath():
462 462 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
463 463 hgpath = os.popen(cmd % python)
464 464 path = hgpath.read().strip()
465 465 hgpath.close()
466 466 return path
467 467
468 468 expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
469 469 hgpkg = None
470 470
471 471 def run_children(tests):
472 472 if not options.with_hg:
473 473 install_hg()
474 474 if hgpkg != expecthg:
475 475 print '# Testing unexpected mercurial: %s' % hgpkg
476 476
477 477 optcopy = dict(options.__dict__)
478 478 optcopy['jobs'] = 1
479 479 optcopy['with_hg'] = INST
480 480 opts = []
481 481 for opt, value in optcopy.iteritems():
482 482 name = '--' + opt.replace('_', '-')
483 483 if value is True:
484 484 opts.append(name)
485 485 elif value is not None:
486 486 opts.append(name + '=' + str(value))
487 487
488 488 tests.reverse()
489 489 jobs = [[] for j in xrange(options.jobs)]
490 490 while tests:
491 491 for j in xrange(options.jobs):
492 492 if not tests: break
493 493 jobs[j].append(tests.pop())
494 494 fps = {}
495 495 for j in xrange(len(jobs)):
496 496 job = jobs[j]
497 497 if not job:
498 498 continue
499 499 rfd, wfd = os.pipe()
500 500 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
501 501 cmdline = [python, sys.argv[0]] + opts + childopts + job
502 502 vlog(' '.join(cmdline))
503 503 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
504 504 os.close(wfd)
505 505 failures = 0
506 506 tested, skipped, failed = 0, 0, 0
507 507 skips = []
508 508 fails = []
509 509 while fps:
510 510 pid, status = os.wait()
511 511 fp = fps.pop(pid)
512 512 l = fp.read().splitlines()
513 513 test, skip, fail = map(int, l[:3])
514 514 split = -fail or len(l)
515 515 for s in l[3:split]:
516 516 skips.append(s.split(" ", 1))
517 517 for s in l[split:]:
518 518 fails.append(s.split(" ", 1))
519 519 tested += test
520 520 skipped += skip
521 521 failed += fail
522 522 vlog('pid %d exited, status %d' % (pid, status))
523 523 failures |= status
524 524 print
525 525 for s in skips:
526 526 print "Skipped %s: %s" % (s[0], s[1])
527 527 for s in fails:
528 528 print "Failed %s: %s" % (s[0], s[1])
529 529
530 530 if hgpkg != expecthg:
531 531 print '# Tested unexpected mercurial: %s' % hgpkg
532 532 print "# Ran %d tests, %d skipped, %d failed." % (
533 533 tested, skipped, failed)
534 534 sys.exit(failures != 0)
535 535
536 536 def run_tests(tests):
537 537 global DAEMON_PIDS, HGRCPATH
538 538 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
539 539 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
540 540
541 541 try:
542 542 if not options.with_hg:
543 543 install_hg()
544 544
545 545 if hgpkg != expecthg:
546 546 print '# Testing unexpected mercurial: %s' % hgpkg
547 547
548 548 if options.timeout > 0:
549 549 try:
550 550 signal.signal(signal.SIGALRM, alarmed)
551 551 vlog('# Running tests with %d-second timeout' %
552 552 options.timeout)
553 553 except AttributeError:
554 554 print 'WARNING: cannot run tests with timeouts'
555 555 options.timeout = 0
556 556
557 557 tested = 0
558 558 failed = 0
559 559 skipped = 0
560 560
561 561 if options.restart:
562 562 orig = list(tests)
563 563 while tests:
564 564 if os.path.exists(tests[0] + ".err"):
565 565 break
566 566 tests.pop(0)
567 567 if not tests:
568 568 print "running all tests"
569 569 tests = orig
570 570
571 571 skips = []
572 572 fails = []
573 573 for test in tests:
574 574 if options.retest and not os.path.exists(test + ".err"):
575 575 skipped += 1
576 576 continue
577 577 ret = run_one(test, skips, fails)
578 578 if ret is None:
579 579 skipped += 1
580 580 elif not ret:
581 581 if options.interactive:
582 582 print "Accept this change? [n] ",
583 583 answer = sys.stdin.readline().strip()
584 584 if answer.lower() in "y yes".split():
585 585 rename(test + ".err", test + ".out")
586 586 tested += 1
587 587 fails.pop()
588 588 continue
589 589 failed += 1
590 590 if options.first:
591 591 break
592 592 tested += 1
593 593
594 594 if options.child:
595 595 fp = os.fdopen(options.child, 'w')
596 596 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
597 597 for s in skips:
598 598 fp.write("%s %s\n" % s)
599 599 for s in fails:
600 600 fp.write("%s %s\n" % s)
601 601 fp.close()
602 602 else:
603 603 print
604 604 for s in skips:
605 605 print "Skipped %s: %s" % s
606 606 for s in fails:
607 607 print "Failed %s: %s" % s
608 608 if hgpkg != expecthg:
609 609 print '# Tested unexpected mercurial: %s' % hgpkg
610 610 print "# Ran %d tests, %d skipped, %d failed." % (
611 611 tested, skipped, failed)
612 612
613 613 if coverage:
614 614 output_coverage()
615 615 except KeyboardInterrupt:
616 616 failed = True
617 617 print "\ninterrupted!"
618 618
619 619 if failed:
620 620 sys.exit(1)
621 621
622 622 if len(args) == 0:
623 623 args = os.listdir(".")
624 624 args.sort()
625 625
626 626 tests = []
627 627 for test in args:
628 628 if (test.startswith("test-") and '~' not in test and
629 629 ('.' not in test or test.endswith('.py') or
630 630 test.endswith('.bat'))):
631 631 tests.append(test)
632 632
633 633 vlog("# Using TESTDIR", TESTDIR)
634 634 vlog("# Using HGTMP", HGTMP)
635 635
636 636 try:
637 637 if len(tests) > 1 and options.jobs > 1:
638 638 run_children(tests)
639 639 else:
640 640 run_tests(tests)
641 641 finally:
642 642 cleanup_exit()
General Comments 0
You need to be logged in to leave comments. Login now