tests/run-tests.py
changeset 1262 f06138614934
parent 1261 13890d7a70b1
child 1263 f31eefe0ab4c
equal deleted inserted replaced
1225:19fe7b202b38 1262:f06138614934
    60 import Queue as queue
    60 import Queue as queue
    61 
    61 
    62 processlock = threading.Lock()
    62 processlock = threading.Lock()
    63 
    63 
    64 closefds = os.name == 'posix'
    64 closefds = os.name == 'posix'
    65 def Popen4(cmd, wd, timeout):
    65 def Popen4(cmd, wd, timeout, env=None):
    66     processlock.acquire()
    66     processlock.acquire()
    67     p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd,
    67     p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd, env=env,
    68                          close_fds=closefds,
    68                          close_fds=closefds,
    69                          stdin=subprocess.PIPE, stdout=subprocess.PIPE,
    69                          stdin=subprocess.PIPE, stdout=subprocess.PIPE,
    70                          stderr=subprocess.STDOUT)
    70                          stderr=subprocess.STDOUT)
    71     processlock.release()
    71     processlock.release()
    72 
    72 
   135         help="skip tests listed in the specified blacklist file")
   135         help="skip tests listed in the specified blacklist file")
   136     parser.add_option("--whitelist", action="append",
   136     parser.add_option("--whitelist", action="append",
   137         help="always run tests listed in the specified whitelist file")
   137         help="always run tests listed in the specified whitelist file")
   138     parser.add_option("-C", "--annotate", action="store_true",
   138     parser.add_option("-C", "--annotate", action="store_true",
   139         help="output files annotated with coverage")
   139         help="output files annotated with coverage")
   140     parser.add_option("--child", type="int",
       
   141         help="run as child process, summary to given fd")
       
   142     parser.add_option("-c", "--cover", action="store_true",
   140     parser.add_option("-c", "--cover", action="store_true",
   143         help="print a test coverage report")
   141         help="print a test coverage report")
   144     parser.add_option("-d", "--debug", action="store_true",
   142     parser.add_option("-d", "--debug", action="store_true",
   145         help="debug mode: write output of test scripts to console"
   143         help="debug mode: write output of test scripts to console"
   146              " rather than capturing and diff'ing it (disables timeout)")
   144              " rather than capturing and diff'ing it (disables timeout)")
   159         help="keep temporary directory after running tests")
   157         help="keep temporary directory after running tests")
   160     parser.add_option("-k", "--keywords",
   158     parser.add_option("-k", "--keywords",
   161         help="run tests matching keywords")
   159         help="run tests matching keywords")
   162     parser.add_option("-l", "--local", action="store_true",
   160     parser.add_option("-l", "--local", action="store_true",
   163         help="shortcut for --with-hg=<testdir>/../hg")
   161         help="shortcut for --with-hg=<testdir>/../hg")
       
   162     parser.add_option("--loop", action="store_true",
       
   163         help="loop tests repeatedly")
   164     parser.add_option("-n", "--nodiff", action="store_true",
   164     parser.add_option("-n", "--nodiff", action="store_true",
   165         help="skip showing test changes")
   165         help="skip showing test changes")
   166     parser.add_option("-p", "--port", type="int",
   166     parser.add_option("-p", "--port", type="int",
   167         help="port on which servers should listen"
   167         help="port on which servers should listen"
   168              " (default: $%s or %d)" % defaults['port'])
   168              " (default: $%s or %d)" % defaults['port'])
   238     if options.anycoverage and options.local:
   238     if options.anycoverage and options.local:
   239         # this needs some path mangling somewhere, I guess
   239         # this needs some path mangling somewhere, I guess
   240         parser.error("sorry, coverage options do not work when --local "
   240         parser.error("sorry, coverage options do not work when --local "
   241                      "is specified")
   241                      "is specified")
   242 
   242 
   243     global vlog
   243     global verbose
   244     if options.verbose:
   244     if options.verbose:
   245         if options.jobs > 1 or options.child is not None:
   245         verbose = ''
   246             pid = "[%d]" % os.getpid()
       
   247         else:
       
   248             pid = None
       
   249         def vlog(*msg):
       
   250             iolock.acquire()
       
   251             if pid:
       
   252                 print pid,
       
   253             for m in msg:
       
   254                 print m,
       
   255             print
       
   256             sys.stdout.flush()
       
   257             iolock.release()
       
   258     else:
       
   259         vlog = lambda *msg: None
       
   260 
   246 
   261     if options.tmpdir:
   247     if options.tmpdir:
   262         options.tmpdir = os.path.expanduser(options.tmpdir)
   248         options.tmpdir = os.path.expanduser(options.tmpdir)
   263 
   249 
   264     if options.jobs < 1:
   250     if options.jobs < 1:
   265         parser.error('--jobs must be positive')
   251         parser.error('--jobs must be positive')
   266     if options.interactive and options.jobs > 1:
       
   267         print '(--interactive overrides --jobs)'
       
   268         options.jobs = 1
       
   269     if options.interactive and options.debug:
   252     if options.interactive and options.debug:
   270         parser.error("-i/--interactive and -d/--debug are incompatible")
   253         parser.error("-i/--interactive and -d/--debug are incompatible")
   271     if options.debug:
   254     if options.debug:
   272         if options.timeout != defaults['timeout']:
   255         if options.timeout != defaults['timeout']:
   273             sys.stderr.write(
   256             sys.stderr.write(
   281         if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
   264         if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
   282             parser.error('--py3k-warnings can only be used on Python 2.6+')
   265             parser.error('--py3k-warnings can only be used on Python 2.6+')
   283     if options.blacklist:
   266     if options.blacklist:
   284         options.blacklist = parselistfiles(options.blacklist, 'blacklist')
   267         options.blacklist = parselistfiles(options.blacklist, 'blacklist')
   285     if options.whitelist:
   268     if options.whitelist:
   286         options.whitelisted = parselistfiles(options.whitelist, 'whitelist',
   269         options.whitelisted = parselistfiles(options.whitelist, 'whitelist')
   287                                              warn=options.child is None)
       
   288     else:
   270     else:
   289         options.whitelisted = {}
   271         options.whitelisted = {}
   290 
   272 
   291     return (options, args)
   273     return (options, args)
   292 
   274 
   317 def showdiff(expected, output, ref, err):
   299 def showdiff(expected, output, ref, err):
   318     print
   300     print
   319     for line in difflib.unified_diff(expected, output, ref, err):
   301     for line in difflib.unified_diff(expected, output, ref, err):
   320         sys.stdout.write(line)
   302         sys.stdout.write(line)
   321 
   303 
       
   304 verbose = False
       
   305 def vlog(*msg):
       
   306     if verbose is not False:
       
   307         iolock.acquire()
       
   308         if verbose:
       
   309             print verbose,
       
   310         for m in msg:
       
   311             print m,
       
   312         print
       
   313         sys.stdout.flush()
       
   314         iolock.release()
       
   315 
       
   316 def log(*msg):
       
   317     iolock.acquire()
       
   318     if verbose:
       
   319         print verbose,
       
   320     for m in msg:
       
   321         print m,
       
   322     print
       
   323     sys.stdout.flush()
       
   324     iolock.release()
       
   325 
   322 def findprogram(program):
   326 def findprogram(program):
   323     """Search PATH for a executable program"""
   327     """Search PATH for a executable program"""
   324     for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
   328     for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
   325         name = os.path.join(p, program)
   329         name = os.path.join(p, program)
   326         if os.name == 'nt' or os.access(name, os.X_OK):
   330         if os.name == 'nt' or os.access(name, os.X_OK):
   327             return name
   331             return name
   328     return None
   332     return None
       
   333 
       
   334 def createhgrc(path, options):
       
   335     # create a fresh hgrc
       
   336     hgrc = open(path, 'w+')
       
   337     hgrc.write('[ui]\n')
       
   338     hgrc.write('slash = True\n')
       
   339     hgrc.write('interactive = False\n')
       
   340     hgrc.write('[defaults]\n')
       
   341     hgrc.write('backout = -d "0 0"\n')
       
   342     hgrc.write('commit = -d "0 0"\n')
       
   343     hgrc.write('tag = -d "0 0"\n')
       
   344     if options.inotify:
       
   345         hgrc.write('[extensions]\n')
       
   346         hgrc.write('inotify=\n')
       
   347         hgrc.write('[inotify]\n')
       
   348         hgrc.write('pidfile=daemon.pids')
       
   349         hgrc.write('appendpid=True\n')
       
   350     if options.extra_config_opt:
       
   351         for opt in options.extra_config_opt:
       
   352             section, key = opt.split('.', 1)
       
   353             assert '=' in key, ('extra config opt %s must '
       
   354                                 'have an = for assignment' % opt)
       
   355             hgrc.write('[%s]\n%s\n' % (section, key))
       
   356     hgrc.close()
       
   357 
       
   358 def createenv(options, testtmp, threadtmp, port):
       
   359     env = os.environ.copy()
       
   360     env['TESTTMP'] = testtmp
       
   361     env['HOME'] = testtmp
       
   362     env["HGPORT"] = str(port)
       
   363     env["HGPORT1"] = str(port + 1)
       
   364     env["HGPORT2"] = str(port + 2)
       
   365     env["HGRCPATH"] = os.path.join(threadtmp, '.hgrc')
       
   366     env["DAEMON_PIDS"] = os.path.join(threadtmp, 'daemon.pids')
       
   367     env["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
       
   368     env["HGMERGE"] = "internal:merge"
       
   369     env["HGUSER"]   = "test"
       
   370     env["HGENCODING"] = "ascii"
       
   371     env["HGENCODINGMODE"] = "strict"
       
   372 
       
   373     # Reset some environment variables to well-known values so that
       
   374     # the tests produce repeatable output.
       
   375     env['LANG'] = env['LC_ALL'] = env['LANGUAGE'] = 'C'
       
   376     env['TZ'] = 'GMT'
       
   377     env["EMAIL"] = "Foo Bar <foo.bar@example.com>"
       
   378     env['COLUMNS'] = '80'
       
   379     env['TERM'] = 'xterm'
       
   380 
       
   381     for k in ('HG HGPROF CDPATH GREP_OPTIONS http_proxy no_proxy ' +
       
   382               'NO_PROXY').split():
       
   383         if k in env:
       
   384             del env[k]
       
   385 
       
   386     # unset env related to hooks
       
   387     for k in env.keys():
       
   388         if k.startswith('HG_'):
       
   389             del env[k]
       
   390 
       
   391     return env
   329 
   392 
   330 def checktools():
   393 def checktools():
   331     # Before we go any further, check for pre-requisite tools
   394     # Before we go any further, check for pre-requisite tools
   332     # stuff from coreutils (cat, rm, etc) are not tested
   395     # stuff from coreutils (cat, rm, etc) are not tested
   333     for p in requiredtools:
   396     for p in requiredtools:
   345     try:
   408     try:
   346         getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
   409         getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
   347     except OSError:
   410     except OSError:
   348         pass
   411         pass
   349 
   412 
   350 def killdaemons():
   413 def killdaemons(pidfile):
   351     return killmod.killdaemons(DAEMON_PIDS, tryhard=False, remove=True,
   414     return killmod.killdaemons(pidfile, tryhard=False, remove=True,
   352                                logfn=vlog)
   415                                logfn=vlog)
   353 
   416 
   354 def cleanup(options):
   417 def cleanup(options):
   355     if not options.keep_tmpdir:
   418     if not options.keep_tmpdir:
   356         vlog("# Cleaning up HGTMP", HGTMP)
   419         vlog("# Cleaning up HGTMP", HGTMP)
   496     def covrun(*args):
   559     def covrun(*args):
   497         cmd = 'coverage %s' % ' '.join(args)
   560         cmd = 'coverage %s' % ' '.join(args)
   498         vlog('# Running: %s' % cmd)
   561         vlog('# Running: %s' % cmd)
   499         os.system(cmd)
   562         os.system(cmd)
   500 
   563 
   501     if options.child:
       
   502         return
       
   503 
       
   504     covrun('-c')
   564     covrun('-c')
   505     omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
   565     omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
   506     covrun('-i', '-r', '"--omit=%s"' % omit) # report
   566     covrun('-i', '-r', '"--omit=%s"' % omit) # report
   507     if options.htmlcov:
   567     if options.htmlcov:
   508         htmldir = os.path.join(TESTDIR, 'htmlcov')
   568         htmldir = os.path.join(TESTDIR, 'htmlcov')
   511         adir = os.path.join(TESTDIR, 'annotated')
   571         adir = os.path.join(TESTDIR, 'annotated')
   512         if not os.path.isdir(adir):
   572         if not os.path.isdir(adir):
   513             os.mkdir(adir)
   573             os.mkdir(adir)
   514         covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
   574         covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
   515 
   575 
   516 def pytest(test, wd, options, replacements):
   576 def pytest(test, wd, options, replacements, env):
   517     py3kswitch = options.py3k_warnings and ' -3' or ''
   577     py3kswitch = options.py3k_warnings and ' -3' or ''
   518     cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
   578     cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
   519     vlog("# Running", cmd)
   579     vlog("# Running", cmd)
   520     if os.name == 'nt':
   580     if os.name == 'nt':
   521         replacements.append((r'\r\n', '\n'))
   581         replacements.append((r'\r\n', '\n'))
   522     return run(cmd, wd, options, replacements)
   582     return run(cmd, wd, options, replacements, env)
   523 
   583 
   524 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
   584 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
   525 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
   585 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
   526 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
   586 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
   527 escapemap.update({'\\': '\\\\', '\r': r'\r'})
   587 escapemap.update({'\\': '\\\\', '\r': r'\r'})
   544     # The only supported special characters are * and ? plus / which also
   604     # The only supported special characters are * and ? plus / which also
   545     # matches \ on windows. Escaping of these caracters is supported.
   605     # matches \ on windows. Escaping of these caracters is supported.
   546     if el + '\n' == l:
   606     if el + '\n' == l:
   547         if os.name == 'nt':
   607         if os.name == 'nt':
   548             # matching on "/" is not needed for this line
   608             # matching on "/" is not needed for this line
   549             iolock.acquire()
   609             log("\nInfo, unnecessary glob: %s (glob)" % el)
   550             print "\nInfo, unnecessary glob: %s (glob)" % el
       
   551             iolock.release()
       
   552         return True
   610         return True
   553     i, n = 0, len(el)
   611     i, n = 0, len(el)
   554     res = ''
   612     res = ''
   555     while i < n:
   613     while i < n:
   556         c = el[i]
   614         c = el[i]
   579         if (el.endswith(" (re)\n") and rematch(el[:-6], l) or
   637         if (el.endswith(" (re)\n") and rematch(el[:-6], l) or
   580             el.endswith(" (glob)\n") and globmatch(el[:-8], l)):
   638             el.endswith(" (glob)\n") and globmatch(el[:-8], l)):
   581             return True
   639             return True
   582     return False
   640     return False
   583 
   641 
   584 def tsttest(test, wd, options, replacements):
   642 def tsttest(test, wd, options, replacements, env):
   585     # We generate a shell script which outputs unique markers to line
   643     # We generate a shell script which outputs unique markers to line
   586     # up script results with our source. These markers include input
   644     # up script results with our source. These markers include input
   587     # line number and the last return code
   645     # line number and the last return code
   588     salt = "SALT" + str(time.time())
   646     salt = "SALT" + str(time.time())
   589     def addsalt(line, inpython):
   647     def addsalt(line, inpython):
   705             os.write(fd, l)
   763             os.write(fd, l)
   706         os.close(fd)
   764         os.close(fd)
   707 
   765 
   708         cmd = '%s "%s"' % (options.shell, name)
   766         cmd = '%s "%s"' % (options.shell, name)
   709         vlog("# Running", cmd)
   767         vlog("# Running", cmd)
   710         exitcode, output = run(cmd, wd, options, replacements)
   768         exitcode, output = run(cmd, wd, options, replacements, env)
   711         # do not merge output if skipped, return hghave message instead
   769         # do not merge output if skipped, return hghave message instead
   712         # similarly, with --debug, output is None
   770         # similarly, with --debug, output is None
   713         if exitcode == SKIPPED_STATUS or output is None:
   771         if exitcode == SKIPPED_STATUS or output is None:
   714             return exitcode, output
   772             return exitcode, output
   715     finally:
   773     finally:
   755         postout += after.pop(pos)
   813         postout += after.pop(pos)
   756 
   814 
   757     return exitcode, postout
   815     return exitcode, postout
   758 
   816 
   759 wifexited = getattr(os, "WIFEXITED", lambda x: False)
   817 wifexited = getattr(os, "WIFEXITED", lambda x: False)
   760 def run(cmd, wd, options, replacements):
   818 def run(cmd, wd, options, replacements, env):
   761     """Run command in a sub-process, capturing the output (stdout and stderr).
   819     """Run command in a sub-process, capturing the output (stdout and stderr).
   762     Return a tuple (exitcode, output).  output is None in debug mode."""
   820     Return a tuple (exitcode, output).  output is None in debug mode."""
   763     # TODO: Use subprocess.Popen if we're running on Python 2.4
   821     # TODO: Use subprocess.Popen if we're running on Python 2.4
   764     if options.debug:
   822     if options.debug:
   765         proc = subprocess.Popen(cmd, shell=True, cwd=wd)
   823         proc = subprocess.Popen(cmd, shell=True, cwd=wd, env=env)
   766         ret = proc.wait()
   824         ret = proc.wait()
   767         return (ret, None)
   825         return (ret, None)
   768 
   826 
   769     proc = Popen4(cmd, wd, options.timeout)
   827     proc = Popen4(cmd, wd, options.timeout, env)
   770     def cleanup():
   828     def cleanup():
   771         terminate(proc)
   829         terminate(proc)
   772         ret = proc.wait()
   830         ret = proc.wait()
   773         if ret == 0:
   831         if ret == 0:
   774             ret = signal.SIGTERM << 8
   832             ret = signal.SIGTERM << 8
   775         killdaemons()
   833         killdaemons(env['DAEMON_PIDS'])
   776         return ret
   834         return ret
   777 
   835 
   778     output = ''
   836     output = ''
   779     proc.tochild.close()
   837     proc.tochild.close()
   780 
   838 
   791 
   849 
   792     if proc.timeout:
   850     if proc.timeout:
   793         ret = 'timeout'
   851         ret = 'timeout'
   794 
   852 
   795     if ret:
   853     if ret:
   796         killdaemons()
   854         killdaemons(env['DAEMON_PIDS'])
       
   855 
       
   856     if abort:
       
   857         raise KeyboardInterrupt()
   797 
   858 
   798     for s, r in replacements:
   859     for s, r in replacements:
   799         output = re.sub(s, r, output)
   860         output = re.sub(s, r, output)
   800     return ret, output.splitlines(True)
   861     return ret, output.splitlines(True)
   801 
   862 
   802 def runone(options, test):
   863 def runone(options, test, count):
   803     '''tristate output:
   864     '''returns a result element: (code, test, msg)'''
   804     None -> skipped
       
   805     True -> passed
       
   806     False -> failed'''
       
   807 
       
   808     global results, resultslock, iolock
       
   809 
       
   810     testpath = os.path.join(TESTDIR, test)
       
   811 
       
   812     def result(l, e):
       
   813         resultslock.acquire()
       
   814         results[l].append(e)
       
   815         resultslock.release()
       
   816 
   865 
   817     def skip(msg):
   866     def skip(msg):
   818         if not options.verbose:
   867         if options.verbose:
   819             result('s', (test, msg))
   868             log("\nSkipping %s: %s" % (testpath, msg))
   820         else:
   869         return 's', test, msg
   821             iolock.acquire()
       
   822             print "\nSkipping %s: %s" % (testpath, msg)
       
   823             iolock.release()
       
   824         return None
       
   825 
   870 
   826     def fail(msg, ret):
   871     def fail(msg, ret):
   827         if not options.nodiff:
   872         if not options.nodiff:
   828             iolock.acquire()
   873             log("\nERROR: %s %s" % (testpath, msg))
   829             print "\nERROR: %s %s" % (testpath, msg)
       
   830             iolock.release()
       
   831         if (not ret and options.interactive
   874         if (not ret and options.interactive
   832             and os.path.exists(testpath + ".err")):
   875             and os.path.exists(testpath + ".err")):
   833             iolock.acquire()
   876             iolock.acquire()
   834             print "Accept this change? [n] ",
   877             print "Accept this change? [n] ",
   835             answer = sys.stdin.readline().strip()
   878             answer = sys.stdin.readline().strip()
   837             if answer.lower() in "y yes".split():
   880             if answer.lower() in "y yes".split():
   838                 if test.endswith(".t"):
   881                 if test.endswith(".t"):
   839                     rename(testpath + ".err", testpath)
   882                     rename(testpath + ".err", testpath)
   840                 else:
   883                 else:
   841                     rename(testpath + ".err", testpath + ".out")
   884                     rename(testpath + ".err", testpath + ".out")
   842                 result('p', test)
   885                 return '.', test, ''
   843                 return
   886         return '!', test, msg
   844         result('f', (test, msg))
       
   845 
   887 
   846     def success():
   888     def success():
   847         result('p', test)
   889         return '.', test, ''
   848 
   890 
   849     def ignore(msg):
   891     def ignore(msg):
   850         result('i', (test, msg))
   892         return 'i', test, msg
   851 
   893 
   852     if (os.path.basename(test).startswith("test-") and '~' not in test and
   894     def describe(ret):
   853         ('.' not in test or test.endswith('.py') or
   895         if ret < 0:
   854          test.endswith('.bat') or test.endswith('.t'))):
   896             return 'killed by signal %d' % -ret
   855         if not os.path.exists(test):
   897         return 'returned error code %d' % ret
   856             skip("doesn't exist")
   898 
   857             return None
   899     testpath = os.path.join(TESTDIR, test)
   858     else:
   900     err = os.path.join(TESTDIR, test + ".err")
   859         vlog('# Test file', test, 'not supported, ignoring')
   901     lctest = test.lower()
   860         return None # not a supported test, don't record
   902 
       
   903     if not os.path.exists(testpath):
       
   904             return skip("doesn't exist")
   861 
   905 
   862     if not (options.whitelisted and test in options.whitelisted):
   906     if not (options.whitelisted and test in options.whitelisted):
   863         if options.blacklist and test in options.blacklist:
   907         if options.blacklist and test in options.blacklist:
   864             skip("blacklisted")
   908             return skip("blacklisted")
   865             return None
       
   866 
   909 
   867         if options.retest and not os.path.exists(test + ".err"):
   910         if options.retest and not os.path.exists(test + ".err"):
   868             ignore("not retesting")
   911             ignore("not retesting")
   869             return None
   912             return None
   870 
   913 
   877                     break
   920                     break
   878                 else:
   921                 else:
   879                     ignore("doesn't match keyword")
   922                     ignore("doesn't match keyword")
   880                     return None
   923                     return None
   881 
   924 
       
   925     for ext, func, out in testtypes:
       
   926         if lctest.startswith("test-") and lctest.endswith(ext):
       
   927             runner = func
       
   928             ref = os.path.join(TESTDIR, test + out)
       
   929             break
       
   930     else:
       
   931         return skip("unknown test type")
       
   932 
   882     vlog("# Test", test)
   933     vlog("# Test", test)
   883 
   934 
   884     # create a fresh hgrc
       
   885     hgrc = open(HGRCPATH, 'w+')
       
   886     hgrc.write('[ui]\n')
       
   887     hgrc.write('slash = True\n')
       
   888     hgrc.write('interactive = False\n')
       
   889     hgrc.write('[defaults]\n')
       
   890     hgrc.write('backout = -d "0 0"\n')
       
   891     hgrc.write('commit = -d "0 0"\n')
       
   892     hgrc.write('tag = -d "0 0"\n')
       
   893     if options.inotify:
       
   894         hgrc.write('[extensions]\n')
       
   895         hgrc.write('inotify=\n')
       
   896         hgrc.write('[inotify]\n')
       
   897         hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
       
   898         hgrc.write('appendpid=True\n')
       
   899     if options.extra_config_opt:
       
   900         for opt in options.extra_config_opt:
       
   901             section, key = opt.split('.', 1)
       
   902             assert '=' in key, ('extra config opt %s must '
       
   903                                 'have an = for assignment' % opt)
       
   904             hgrc.write('[%s]\n%s\n' % (section, key))
       
   905     hgrc.close()
       
   906 
       
   907     ref = os.path.join(TESTDIR, test+".out")
       
   908     err = os.path.join(TESTDIR, test+".err")
       
   909     if os.path.exists(err):
   935     if os.path.exists(err):
   910         os.remove(err)       # Remove any previous output files
   936         os.remove(err)       # Remove any previous output files
   911     try:
       
   912         tf = open(testpath)
       
   913         firstline = tf.readline().rstrip()
       
   914         tf.close()
       
   915     except IOError:
       
   916         firstline = ''
       
   917     lctest = test.lower()
       
   918 
       
   919     if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
       
   920         runner = pytest
       
   921     elif lctest.endswith('.t'):
       
   922         runner = tsttest
       
   923         ref = testpath
       
   924     else:
       
   925         return skip("unknown test type")
       
   926 
   937 
   927     # Make a tmp subdirectory to work in
   938     # Make a tmp subdirectory to work in
   928     testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
   939     threadtmp = os.path.join(HGTMP, "child%d" % count)
   929         os.path.join(HGTMP, os.path.basename(test))
   940     testtmp = os.path.join(threadtmp, os.path.basename(test))
   930 
   941     os.mkdir(threadtmp)
       
   942     os.mkdir(testtmp)
       
   943 
       
   944     port = options.port + count * 3
   931     replacements = [
   945     replacements = [
   932         (r':%s\b' % options.port, ':$HGPORT'),
   946         (r':%s\b' % port, ':$HGPORT'),
   933         (r':%s\b' % (options.port + 1), ':$HGPORT1'),
   947         (r':%s\b' % (port + 1), ':$HGPORT1'),
   934         (r':%s\b' % (options.port + 2), ':$HGPORT2'),
   948         (r':%s\b' % (port + 2), ':$HGPORT2'),
   935         ]
   949         ]
   936     if os.name == 'nt':
   950     if os.name == 'nt':
   937         replacements.append(
   951         replacements.append(
   938             (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
   952             (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
   939                      c in '/\\' and r'[/\\]' or
   953                      c in '/\\' and r'[/\\]' or
   941                      '\\' + c
   955                      '\\' + c
   942                      for c in testtmp), '$TESTTMP'))
   956                      for c in testtmp), '$TESTTMP'))
   943     else:
   957     else:
   944         replacements.append((re.escape(testtmp), '$TESTTMP'))
   958         replacements.append((re.escape(testtmp), '$TESTTMP'))
   945 
   959 
   946     os.mkdir(testtmp)
   960     env = createenv(options, testtmp, threadtmp, port)
       
   961     createhgrc(env['HGRCPATH'], options)
       
   962 
   947     if options.time:
   963     if options.time:
   948         starttime = time.time()
   964         starttime = time.time()
   949     ret, out = runner(testpath, testtmp, options, replacements)
   965     ret, out = runner(testpath, testtmp, options, replacements, env)
   950     if options.time:
   966     if options.time:
   951         endtime = time.time()
   967         endtime = time.time()
   952         times.append((test, endtime - starttime))
   968         times.append((test, endtime - starttime))
   953     vlog("# Ret was:", ret)
   969     vlog("# Ret was:", ret)
   954 
   970 
   955     killdaemons()
   971     killdaemons(env['DAEMON_PIDS'])
   956 
       
   957     mark = '.'
       
   958 
   972 
   959     skipped = (ret == SKIPPED_STATUS)
   973     skipped = (ret == SKIPPED_STATUS)
   960 
   974 
   961     # If we're not in --debug mode and reference output file exists,
   975     # If we're not in --debug mode and reference output file exists,
   962     # check test output against it.
   976     # check test output against it.
   974         f = open(err, "wb")
   988         f = open(err, "wb")
   975         for line in out:
   989         for line in out:
   976             f.write(line)
   990             f.write(line)
   977         f.close()
   991         f.close()
   978 
   992 
   979     def describe(ret):
       
   980         if ret < 0:
       
   981             return 'killed by signal %d' % -ret
       
   982         return 'returned error code %d' % ret
       
   983 
       
   984     if skipped:
   993     if skipped:
   985         mark = 's'
       
   986         if out is None:                 # debug mode: nothing to parse
   994         if out is None:                 # debug mode: nothing to parse
   987             missing = ['unknown']
   995             missing = ['unknown']
   988             failed = None
   996             failed = None
   989         else:
   997         else:
   990             missing, failed = parsehghaveoutput(out)
   998             missing, failed = parsehghaveoutput(out)
   991         if not missing:
   999         if not missing:
   992             missing = ['irrelevant']
  1000             missing = ['irrelevant']
   993         if failed:
  1001         if failed:
   994             fail("hghave failed checking for %s" % failed[-1], ret)
  1002             result = fail("hghave failed checking for %s" % failed[-1], ret)
   995             skipped = False
  1003             skipped = False
   996         else:
  1004         else:
   997             skip(missing[-1])
  1005             result = skip(missing[-1])
   998     elif ret == 'timeout':
  1006     elif ret == 'timeout':
   999         mark = 't'
  1007         result = fail("timed out", ret)
  1000         fail("timed out", ret)
       
  1001     elif out != refout:
  1008     elif out != refout:
  1002         mark = '!'
       
  1003         if not options.nodiff:
  1009         if not options.nodiff:
  1004             iolock.acquire()
  1010             iolock.acquire()
  1005             if options.view:
  1011             if options.view:
  1006                 os.system("%s %s %s" % (options.view, ref, err))
  1012                 os.system("%s %s %s" % (options.view, ref, err))
  1007             else:
  1013             else:
  1008                 showdiff(refout, out, ref, err)
  1014                 showdiff(refout, out, ref, err)
  1009             iolock.release()
  1015             iolock.release()
  1010         if ret:
  1016         if ret:
  1011             fail("output changed and " + describe(ret), ret)
  1017             result = fail("output changed and " + describe(ret), ret)
  1012         else:
  1018         else:
  1013             fail("output changed", ret)
  1019             result = fail("output changed", ret)
  1014         ret = 1
       
  1015     elif ret:
  1020     elif ret:
  1016         mark = '!'
  1021         result = fail(describe(ret), ret)
  1017         fail(describe(ret), ret)
       
  1018     else:
  1022     else:
  1019         success()
  1023         result = success()
  1020 
  1024 
  1021     if not options.verbose:
  1025     if not options.verbose:
  1022         iolock.acquire()
  1026         iolock.acquire()
  1023         sys.stdout.write(mark)
  1027         sys.stdout.write(result[0])
  1024         sys.stdout.flush()
  1028         sys.stdout.flush()
  1025         iolock.release()
  1029         iolock.release()
  1026 
  1030 
  1027     if not options.keep_tmpdir:
  1031     if not options.keep_tmpdir:
  1028         shutil.rmtree(testtmp, True)
  1032         shutil.rmtree(threadtmp, True)
  1029     if skipped:
  1033     return result
  1030         return None
       
  1031     return ret == 0
       
  1032 
  1034 
  1033 _hgpath = None
  1035 _hgpath = None
  1034 
  1036 
  1035 def _gethgpath():
  1037 def _gethgpath():
  1036     """Return the path to the mercurial package that is actually found by
  1038     """Return the path to the mercurial package that is actually found by
  1055     if os.path.abspath(actualhg) != os.path.abspath(expecthg):
  1057     if os.path.abspath(actualhg) != os.path.abspath(expecthg):
  1056         sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
  1058         sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
  1057                          '         (expected %s)\n'
  1059                          '         (expected %s)\n'
  1058                          % (verb, actualhg, expecthg))
  1060                          % (verb, actualhg, expecthg))
  1059 
  1061 
  1060 def runchildren(options, tests):
  1062 results = {'.':[], '!':[], 's':[], 'i':[]}
  1061     if INST:
       
  1062         installhg(options)
       
  1063         _checkhglib("Testing")
       
  1064     else:
       
  1065         usecorrectpython()
       
  1066 
       
  1067     optcopy = dict(options.__dict__)
       
  1068     optcopy['jobs'] = 1
       
  1069 
       
  1070     # Because whitelist has to override keyword matches, we have to
       
  1071     # actually load the whitelist in the children as well, so we allow
       
  1072     # the list of whitelist files to pass through and be parsed in the
       
  1073     # children, but not the dict of whitelisted tests resulting from
       
  1074     # the parse, used here to override blacklisted tests.
       
  1075     whitelist = optcopy['whitelisted'] or []
       
  1076     del optcopy['whitelisted']
       
  1077 
       
  1078     blacklist = optcopy['blacklist'] or []
       
  1079     del optcopy['blacklist']
       
  1080     blacklisted = []
       
  1081 
       
  1082     if optcopy['with_hg'] is None:
       
  1083         optcopy['with_hg'] = os.path.join(BINDIR, "hg")
       
  1084     optcopy.pop('anycoverage', None)
       
  1085 
       
  1086     opts = []
       
  1087     for opt, value in optcopy.iteritems():
       
  1088         name = '--' + opt.replace('_', '-')
       
  1089         if value is True:
       
  1090             opts.append(name)
       
  1091         elif isinstance(value, list):
       
  1092             for v in value:
       
  1093                 opts.append(name + '=' + str(v))
       
  1094         elif value is not None:
       
  1095             opts.append(name + '=' + str(value))
       
  1096 
       
  1097     tests.reverse()
       
  1098     jobs = [[] for j in xrange(options.jobs)]
       
  1099     while tests:
       
  1100         for job in jobs:
       
  1101             if not tests:
       
  1102                 break
       
  1103             test = tests.pop()
       
  1104             if test not in whitelist and test in blacklist:
       
  1105                 blacklisted.append(test)
       
  1106             else:
       
  1107                 job.append(test)
       
  1108 
       
  1109     waitq = queue.Queue()
       
  1110 
       
  1111     # windows lacks os.wait, so we must emulate it
       
  1112     def waitfor(proc, rfd):
       
  1113         fp = os.fdopen(rfd, 'rb')
       
  1114         return lambda: waitq.put((proc.pid, proc.wait(), fp))
       
  1115 
       
  1116     for j, job in enumerate(jobs):
       
  1117         if not job:
       
  1118             continue
       
  1119         rfd, wfd = os.pipe()
       
  1120         childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
       
  1121         childtmp = os.path.join(HGTMP, 'child%d' % j)
       
  1122         childopts += ['--tmpdir', childtmp]
       
  1123         if options.keep_tmpdir:
       
  1124             childopts.append('--keep-tmpdir')
       
  1125         cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
       
  1126         vlog(' '.join(cmdline))
       
  1127         proc = subprocess.Popen(cmdline, executable=cmdline[0])
       
  1128         threading.Thread(target=waitfor(proc, rfd)).start()
       
  1129         os.close(wfd)
       
  1130     signal.signal(signal.SIGINT, signal.SIG_IGN)
       
  1131     failures = 0
       
  1132     passed, skipped, failed = 0, 0, 0
       
  1133     skips = []
       
  1134     fails = []
       
  1135     for job in jobs:
       
  1136         if not job:
       
  1137             continue
       
  1138         pid, status, fp = waitq.get()
       
  1139         try:
       
  1140             childresults = pickle.load(fp)
       
  1141         except (pickle.UnpicklingError, EOFError):
       
  1142             sys.exit(255)
       
  1143         else:
       
  1144             passed += len(childresults['p'])
       
  1145             skipped += len(childresults['s'])
       
  1146             failed += len(childresults['f'])
       
  1147             skips.extend(childresults['s'])
       
  1148             fails.extend(childresults['f'])
       
  1149         if options.time:
       
  1150             childtimes = pickle.load(fp)
       
  1151             times.extend(childtimes)
       
  1152 
       
  1153         vlog('pid %d exited, status %d' % (pid, status))
       
  1154         failures |= status
       
  1155     print
       
  1156     skipped += len(blacklisted)
       
  1157     if not options.noskips:
       
  1158         for s in skips:
       
  1159             print "Skipped %s: %s" % (s[0], s[1])
       
  1160         for s in blacklisted:
       
  1161             print "Skipped %s: blacklisted" % s
       
  1162     for s in fails:
       
  1163         print "Failed %s: %s" % (s[0], s[1])
       
  1164 
       
  1165     _checkhglib("Tested")
       
  1166     print "# Ran %d tests, %d skipped, %d failed." % (
       
  1167         passed + failed, skipped, failed)
       
  1168 
       
  1169     if options.time:
       
  1170         outputtimes(options)
       
  1171     if options.anycoverage:
       
  1172         outputcoverage(options)
       
  1173     sys.exit(failures != 0)
       
  1174 
       
  1175 results = dict(p=[], f=[], s=[], i=[])
       
  1176 resultslock = threading.Lock()
       
  1177 times = []
  1063 times = []
  1178 iolock = threading.Lock()
  1064 iolock = threading.Lock()
  1179 
  1065 abort = False
  1180 def runqueue(options, tests):
  1066 
  1181     for test in tests:
  1067 def scheduletests(options, tests):
  1182         ret = runone(options, test)
  1068     jobs = options.jobs
  1183         if options.first and ret is not None and not ret:
  1069     done = queue.Queue()
  1184             break
  1070     running = 0
       
  1071     count = 0
       
  1072     global abort
       
  1073 
       
  1074     def job(test, count):
       
  1075         try:
       
  1076             done.put(runone(options, test, count))
       
  1077         except KeyboardInterrupt:
       
  1078             pass
       
  1079 
       
  1080     try:
       
  1081         while tests or running:
       
  1082             if not done.empty() or running == jobs or not tests:
       
  1083                 try:
       
  1084                     code, test, msg = done.get(True, 1)
       
  1085                     results[code].append((test, msg))
       
  1086                     if options.first and code not in '.si':
       
  1087                         break
       
  1088                 except queue.Empty:
       
  1089                     continue
       
  1090                 running -= 1
       
  1091             if tests and not running == jobs:
       
  1092                 test = tests.pop(0)
       
  1093                 if options.loop:
       
  1094                     tests.append(test)
       
  1095                 t = threading.Thread(None, job, args=(test, count))
       
  1096                 t.start()
       
  1097                 running += 1
       
  1098                 count += 1
       
  1099     except KeyboardInterrupt:
       
  1100         abort = True
  1185 
  1101 
  1186 def runtests(options, tests):
  1102 def runtests(options, tests):
  1187     global DAEMON_PIDS, HGRCPATH
       
  1188     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
       
  1189     HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
       
  1190 
       
  1191     try:
  1103     try:
  1192         if INST:
  1104         if INST:
  1193             installhg(options)
  1105             installhg(options)
  1194             _checkhglib("Testing")
  1106             _checkhglib("Testing")
  1195         else:
  1107         else:
  1203                 tests.pop(0)
  1115                 tests.pop(0)
  1204             if not tests:
  1116             if not tests:
  1205                 print "running all tests"
  1117                 print "running all tests"
  1206                 tests = orig
  1118                 tests = orig
  1207 
  1119 
  1208         runqueue(options, tests)
  1120         scheduletests(options, tests)
  1209 
  1121 
  1210         failed = len(results['f'])
  1122         failed = len(results['!'])
  1211         tested = len(results['p']) + failed
  1123         tested = len(results['.']) + failed
  1212         skipped = len(results['s'])
  1124         skipped = len(results['s'])
  1213         ignored = len(results['i'])
  1125         ignored = len(results['i'])
  1214 
  1126 
  1215         if options.child:
  1127         print
  1216             fp = os.fdopen(options.child, 'wb')
  1128         for s in results['s']:
  1217             pickle.dump(results, fp, pickle.HIGHEST_PROTOCOL)
  1129             print "Skipped %s: %s" % s
  1218             if options.time:
  1130         for s in results['!']:
  1219                 pickle.dump(times, fp, pickle.HIGHEST_PROTOCOL)
  1131             print "Failed %s: %s" % s
  1220             fp.close()
  1132         _checkhglib("Tested")
  1221         else:
  1133         print "# Ran %d tests, %d skipped, %d failed." % (
  1222             print
  1134             tested, skipped + ignored, failed)
  1223             for s in results['s']:
  1135         if options.time:
  1224                 print "Skipped %s: %s" % s
  1136             outputtimes(options)
  1225             for s in results['f']:
       
  1226                 print "Failed %s: %s" % s
       
  1227             _checkhglib("Tested")
       
  1228             print "# Ran %d tests, %d skipped, %d failed." % (
       
  1229                 tested, skipped + ignored, failed)
       
  1230             if options.time:
       
  1231                 outputtimes(options)
       
  1232 
  1137 
  1233         if options.anycoverage:
  1138         if options.anycoverage:
  1234             outputcoverage(options)
  1139             outputcoverage(options)
  1235     except KeyboardInterrupt:
  1140     except KeyboardInterrupt:
  1236         failed = True
  1141         failed = True
  1237         if not options.child:
  1142         print "\ninterrupted!"
  1238             print "\ninterrupted!"
       
  1239 
  1143 
  1240     if failed:
  1144     if failed:
  1241         sys.exit(1)
  1145         sys.exit(1)
  1242 
  1146 
       
  1147 testtypes = [('.py', pytest, '.out'),
       
  1148              ('.t', tsttest, '')]
       
  1149 
  1243 def main():
  1150 def main():
  1244     (options, args) = parseargs()
  1151     (options, args) = parseargs()
  1245     if not options.child:
  1152     os.umask(022)
  1246         os.umask(022)
  1153 
  1247 
  1154     checktools()
  1248         checktools()
  1155 
  1249 
  1156     if len(args) == 0:
  1250         if len(args) == 0:
  1157         args = [t for t in os.listdir(".")
  1251             args = sorted(os.listdir("."))
  1158                 if t.startswith("test-")
       
  1159                 and (t.endswith(".py") or t.endswith(".t"))]
  1252 
  1160 
  1253     tests = args
  1161     tests = args
  1254 
  1162 
  1255     if options.random:
  1163     if options.random:
  1256         random.shuffle(tests)
  1164         random.shuffle(tests)
  1257 
  1165     else:
  1258     # Reset some environment variables to well-known values so that
  1166         # keywords for slow tests
  1259     # the tests produce repeatable output.
  1167         slow = 'svn gendoc check-code-hg'.split()
  1260     os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
  1168         def sortkey(f):
  1261     os.environ['TZ'] = 'GMT'
  1169             # run largest tests first, as they tend to take the longest
  1262     os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
  1170             val = -os.stat(f).st_size
  1263     os.environ['CDPATH'] = ''
  1171             for kw in slow:
  1264     os.environ['COLUMNS'] = '80'
  1172                 if kw in f:
  1265     os.environ['GREP_OPTIONS'] = ''
  1173                     val *= 10
  1266     os.environ['http_proxy'] = ''
  1174             return val
  1267     os.environ['no_proxy'] = ''
  1175         tests.sort(key=sortkey)
  1268     os.environ['NO_PROXY'] = ''
  1176 
  1269     os.environ['TERM'] = 'xterm'
       
  1270     if 'PYTHONHASHSEED' not in os.environ:
  1177     if 'PYTHONHASHSEED' not in os.environ:
  1271         # use a random python hash seed all the time
  1178         # use a random python hash seed all the time
  1272         # we do the randomness ourself to know what seed is used
  1179         # we do the randomness ourself to know what seed is used
  1273         os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
  1180         os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
  1274         print 'python hash seed:', os.environ['PYTHONHASHSEED']
  1181         print 'python hash seed:', os.environ['PYTHONHASHSEED']
  1275 
  1182 
  1276     # unset env related to hooks
       
  1277     for k in os.environ.keys():
       
  1278         if k.startswith('HG_'):
       
  1279             # can't remove on solaris
       
  1280             os.environ[k] = ''
       
  1281             del os.environ[k]
       
  1282     if 'HG' in os.environ:
       
  1283         # can't remove on solaris
       
  1284         os.environ['HG'] = ''
       
  1285         del os.environ['HG']
       
  1286     if 'HGPROF' in os.environ:
       
  1287         os.environ['HGPROF'] = ''
       
  1288         del os.environ['HGPROF']
       
  1289 
       
  1290     global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
  1183     global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
  1291     TESTDIR = os.environ["TESTDIR"] = os.getcwd()
  1184     TESTDIR = os.environ["TESTDIR"] = os.getcwd()
  1292     if options.tmpdir:
  1185     if options.tmpdir:
  1293         if not options.child:
  1186         options.keep_tmpdir = True
  1294             options.keep_tmpdir = True
       
  1295         tmpdir = options.tmpdir
  1187         tmpdir = options.tmpdir
  1296         if os.path.exists(tmpdir):
  1188         if os.path.exists(tmpdir):
  1297             # Meaning of tmpdir has changed since 1.3: we used to create
  1189             # Meaning of tmpdir has changed since 1.3: we used to create
  1298             # HGTMP inside tmpdir; now HGTMP is tmpdir.  So fail if
  1190             # HGTMP inside tmpdir; now HGTMP is tmpdir.  So fail if
  1299             # tmpdir already exists.
  1191             # tmpdir already exists.
  1311             # without this, we get the default temp dir location, but
  1203             # without this, we get the default temp dir location, but
  1312             # in all lowercase, which causes troubles with paths (issue3490)
  1204             # in all lowercase, which causes troubles with paths (issue3490)
  1313             d = os.getenv('TMP')
  1205             d = os.getenv('TMP')
  1314         tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
  1206         tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
  1315     HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
  1207     HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
  1316     DAEMON_PIDS = None
       
  1317     HGRCPATH = None
       
  1318 
       
  1319     os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
       
  1320     os.environ["HGMERGE"] = "internal:merge"
       
  1321     os.environ["HGUSER"]   = "test"
       
  1322     os.environ["HGENCODING"] = "ascii"
       
  1323     os.environ["HGENCODINGMODE"] = "strict"
       
  1324     os.environ["HGPORT"] = str(options.port)
       
  1325     os.environ["HGPORT1"] = str(options.port + 1)
       
  1326     os.environ["HGPORT2"] = str(options.port + 2)
       
  1327 
  1208 
  1328     if options.with_hg:
  1209     if options.with_hg:
  1329         INST = None
  1210         INST = None
  1330         BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
  1211         BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
  1331 
  1212 
  1341         PYTHONDIR = os.path.join(INST, "lib", "python")
  1222         PYTHONDIR = os.path.join(INST, "lib", "python")
  1342 
  1223 
  1343     os.environ["BINDIR"] = BINDIR
  1224     os.environ["BINDIR"] = BINDIR
  1344     os.environ["PYTHON"] = PYTHON
  1225     os.environ["PYTHON"] = PYTHON
  1345 
  1226 
  1346     if not options.child:
  1227     path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
  1347         path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
  1228     os.environ["PATH"] = os.pathsep.join(path)
  1348         os.environ["PATH"] = os.pathsep.join(path)
  1229 
  1349 
  1230     # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
  1350         # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
  1231     # can run .../tests/run-tests.py test-foo where test-foo
  1351         # can run .../tests/run-tests.py test-foo where test-foo
  1232     # adds an extension to HGRC
  1352         # adds an extension to HGRC
  1233     pypath = [PYTHONDIR, TESTDIR]
  1353         pypath = [PYTHONDIR, TESTDIR]
  1234     # We have to augment PYTHONPATH, rather than simply replacing
  1354         # We have to augment PYTHONPATH, rather than simply replacing
  1235     # it, in case external libraries are only available via current
  1355         # it, in case external libraries are only available via current
  1236     # PYTHONPATH.  (In particular, the Subversion bindings on OS X
  1356         # PYTHONPATH.  (In particular, the Subversion bindings on OS X
  1237     # are in /opt/subversion.)
  1357         # are in /opt/subversion.)
  1238     oldpypath = os.environ.get(IMPL_PATH)
  1358         oldpypath = os.environ.get(IMPL_PATH)
  1239     if oldpypath:
  1359         if oldpypath:
  1240         pypath.append(oldpypath)
  1360             pypath.append(oldpypath)
  1241     os.environ[IMPL_PATH] = os.pathsep.join(pypath)
  1361         os.environ[IMPL_PATH] = os.pathsep.join(pypath)
       
  1362 
  1242 
  1363     COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
  1243     COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
  1364 
  1244 
  1365     vlog("# Using TESTDIR", TESTDIR)
  1245     vlog("# Using TESTDIR", TESTDIR)
  1366     vlog("# Using HGTMP", HGTMP)
  1246     vlog("# Using HGTMP", HGTMP)
  1367     vlog("# Using PATH", os.environ["PATH"])
  1247     vlog("# Using PATH", os.environ["PATH"])
  1368     vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
  1248     vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
  1369 
  1249 
  1370     try:
  1250     try:
  1371         if len(tests) > 1 and options.jobs > 1:
  1251         runtests(options, tests)
  1372             runchildren(options, tests)
       
  1373         else:
       
  1374             runtests(options, tests)
       
  1375     finally:
  1252     finally:
  1376         time.sleep(.1)
  1253         time.sleep(.1)
  1377         cleanup(options)
  1254         cleanup(options)
  1378 
  1255 
  1379 if __name__ == '__main__':
  1256 if __name__ == '__main__':