tests/run-tests.py
changeset 917 6f25a5834e14
parent 916 dd5c50a018b0
child 918 3071ec5d085c
equal deleted inserted replaced
911:f3ab526d8761 917:6f25a5834e14
    51 import signal
    51 import signal
    52 import sys
    52 import sys
    53 import tempfile
    53 import tempfile
    54 import time
    54 import time
    55 import re
    55 import re
       
    56 import threading
    56 
    57 
    57 closefds = os.name == 'posix'
    58 closefds = os.name == 'posix'
    58 def Popen4(cmd, bufsize=-1):
    59 def Popen4(cmd, timeout):
    59     p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
    60     p = subprocess.Popen(cmd, shell=True, bufsize=-1,
    60                          close_fds=closefds,
    61                          close_fds=closefds,
    61                          stdin=subprocess.PIPE, stdout=subprocess.PIPE,
    62                          stdin=subprocess.PIPE, stdout=subprocess.PIPE,
    62                          stderr=subprocess.STDOUT)
    63                          stderr=subprocess.STDOUT)
    63     p.fromchild = p.stdout
    64     p.fromchild = p.stdout
    64     p.tochild = p.stdin
    65     p.tochild = p.stdin
    65     p.childerr = p.stderr
    66     p.childerr = p.stderr
       
    67 
       
    68     if timeout:
       
    69         p.timeout = False
       
    70         def t():
       
    71             start = time.time()
       
    72             while time.time() - start < timeout and p.returncode is None:
       
    73                 time.sleep(1)
       
    74             p.timeout = True
       
    75             if p.returncode is None:
       
    76                 try:
       
    77                     p.terminate()
       
    78                 except OSError:
       
    79                     pass
       
    80         threading.Thread(target=t).start()
       
    81 
    66     return p
    82     return p
    67 
    83 
    68 # reserved exit code to skip test (used by hghave)
    84 # reserved exit code to skip test (used by hghave)
    69 SKIPPED_STATUS = 80
    85 SKIPPED_STATUS = 80
    70 SKIPPED_PREFIX = 'skipped: '
    86 SKIPPED_PREFIX = 'skipped: '
   437         adir = os.path.join(TESTDIR, 'annotated')
   453         adir = os.path.join(TESTDIR, 'annotated')
   438         if not os.path.isdir(adir):
   454         if not os.path.isdir(adir):
   439             os.mkdir(adir)
   455             os.mkdir(adir)
   440         covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
   456         covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
   441 
   457 
   442 class Timeout(Exception):
       
   443     pass
       
   444 
       
   445 def alarmed(signum, frame):
       
   446     raise Timeout
       
   447 
       
   448 def pytest(test, options, replacements):
   458 def pytest(test, options, replacements):
   449     py3kswitch = options.py3k_warnings and ' -3' or ''
   459     py3kswitch = options.py3k_warnings and ' -3' or ''
   450     cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
   460     cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
   451     vlog("# Running", cmd)
   461     vlog("# Running", cmd)
   452     return run(cmd, options, replacements)
   462     return run(cmd, options, replacements)
   600         output = fromchild.read()
   610         output = fromchild.read()
   601         ret = fromchild.close()
   611         ret = fromchild.close()
   602         if ret is None:
   612         if ret is None:
   603             ret = 0
   613             ret = 0
   604     else:
   614     else:
   605         proc = Popen4(cmd)
   615         proc = Popen4(cmd, options.timeout)
   606         def cleanup():
   616         def cleanup():
   607             os.kill(proc.pid, signal.SIGTERM)
   617             try:
       
   618                 proc.terminate()
       
   619             except OSError:
       
   620                 pass
   608             ret = proc.wait()
   621             ret = proc.wait()
   609             if ret == 0:
   622             if ret == 0:
   610                 ret = signal.SIGTERM << 8
   623                 ret = signal.SIGTERM << 8
   611             killdaemons()
   624             killdaemons()
   612             return ret
   625             return ret
   613 
   626 
       
   627         output = ''
       
   628         proc.tochild.close()
       
   629 
   614         try:
   630         try:
   615             output = ''
       
   616             proc.tochild.close()
       
   617             output = proc.fromchild.read()
   631             output = proc.fromchild.read()
   618             ret = proc.wait()
       
   619             if wifexited(ret):
       
   620                 ret = os.WEXITSTATUS(ret)
       
   621         except Timeout:
       
   622             vlog('# Process %d timed out - killing it' % proc.pid)
       
   623             cleanup()
       
   624             ret = 'timeout'
       
   625             output += ("\n### Abort: timeout after %d seconds.\n"
       
   626                        % options.timeout)
       
   627         except KeyboardInterrupt:
   632         except KeyboardInterrupt:
   628             vlog('# Handling keyboard interrupt')
   633             vlog('# Handling keyboard interrupt')
   629             cleanup()
   634             cleanup()
   630             raise
   635             raise
   631 
   636 
       
   637         ret = proc.wait()
       
   638         if wifexited(ret):
       
   639             ret = os.WEXITSTATUS(ret)
       
   640 
       
   641         if proc.timeout:
       
   642             ret = 'timeout'
       
   643 
       
   644         if ret:
       
   645             killdaemons()
       
   646 
   632     for s, r in replacements:
   647     for s, r in replacements:
   633         output = re.sub(s, r, output)
   648         output = re.sub(s, r, output)
   634     return ret, splitnewlines(output)
   649     return ret, splitnewlines(output)
   635 
   650 
   636 def runone(options, test, results):
   651 def runone(options, test):
   637     '''tristate output:
   652     '''tristate output:
   638     None -> skipped
   653     None -> skipped
   639     True -> passed
   654     True -> passed
   640     False -> failed'''
   655     False -> failed'''
   641 
   656 
       
   657     global results, resultslock, iolock
       
   658 
   642     testpath = os.path.join(TESTDIR, test)
   659     testpath = os.path.join(TESTDIR, test)
       
   660 
       
   661     def result(l, e):
       
   662         resultslock.acquire()
       
   663         results[l].append(e)
       
   664         resultslock.release()
   643 
   665 
   644     def skip(msg):
   666     def skip(msg):
   645         if not options.verbose:
   667         if not options.verbose:
   646             results['s'].append((test, msg))
   668             result('s', (test, msg))
   647         else:
   669         else:
       
   670             iolock.acquire()
   648             print "\nSkipping %s: %s" % (testpath, msg)
   671             print "\nSkipping %s: %s" % (testpath, msg)
       
   672             iolock.release()
   649         return None
   673         return None
   650 
   674 
   651     def fail(msg, ret):
   675     def fail(msg, ret):
   652         if not options.nodiff:
   676         if not options.nodiff:
       
   677             iolock.acquire()
   653             print "\nERROR: %s %s" % (testpath, msg)
   678             print "\nERROR: %s %s" % (testpath, msg)
       
   679             iolock.release()
   654         if (not ret and options.interactive
   680         if (not ret and options.interactive
   655             and os.path.exists(testpath + ".err")):
   681             and os.path.exists(testpath + ".err")):
       
   682             iolock.acquire()
   656             print "Accept this change? [n] ",
   683             print "Accept this change? [n] ",
   657             answer = sys.stdin.readline().strip()
   684             answer = sys.stdin.readline().strip()
       
   685             iolock.release()
   658             if answer.lower() in "y yes".split():
   686             if answer.lower() in "y yes".split():
   659                 if test.endswith(".t"):
   687                 if test.endswith(".t"):
   660                     rename(testpath + ".err", testpath)
   688                     rename(testpath + ".err", testpath)
   661                 else:
   689                 else:
   662                     rename(testpath + ".err", testpath + ".out")
   690                     rename(testpath + ".err", testpath + ".out")
   663                 return
   691                 return
   664         results['f'].append((test, msg))
   692         result('f', (test, msg))
   665 
   693 
   666     def success():
   694     def success():
   667         results['p'].append(test)
   695         result('p', test)
   668 
   696 
   669     def ignore(msg):
   697     def ignore(msg):
   670         results['i'].append((test, msg))
   698         result('i', (test, msg))
   671 
   699 
   672     if (test.startswith("test-") and '~' not in test and
   700     if (test.startswith("test-") and '~' not in test and
   673         ('.' not in test or test.endswith('.py') or
   701         ('.' not in test or test.endswith('.py') or
   674          test.endswith('.bat') or test.endswith('.t'))):
   702          test.endswith('.bat') or test.endswith('.t'))):
   675         if not os.path.exists(test):
   703         if not os.path.exists(test):
   679         return None # not a supported test, don't record
   707         return None # not a supported test, don't record
   680 
   708 
   681     if options.blacklist:
   709     if options.blacklist:
   682         filename = options.blacklist.get(test)
   710         filename = options.blacklist.get(test)
   683         if filename is not None:
   711         if filename is not None:
   684             skipped.append((test, "blacklisted (%s)" % filename))
   712             skip("blacklisted")
   685             return None
   713             return None
   686 
   714 
   687     if options.retest and not os.path.exists(test + ".err"):
   715     if options.retest and not os.path.exists(test + ".err"):
   688         ignore("not retesting")
   716         ignore("not retesting")
   689         return None
   717         return None
   745         os.path.join(HGTMP, test)
   773         os.path.join(HGTMP, test)
   746 
   774 
   747     os.mkdir(testtmp)
   775     os.mkdir(testtmp)
   748     os.chdir(testtmp)
   776     os.chdir(testtmp)
   749 
   777 
   750     if options.timeout > 0:
       
   751         signal.alarm(options.timeout)
       
   752 
       
   753     ret, out = runner(testpath, options, [
   778     ret, out = runner(testpath, options, [
   754         (re.escape(testtmp), '$TESTTMP'),
   779         (re.escape(testtmp), '$TESTTMP'),
   755         (r':%s\b' % options.port, ':$HGPORT'),
   780         (r':%s\b' % options.port, ':$HGPORT'),
   756         (r':%s\b' % (options.port + 1), ':$HGPORT1'),
   781         (r':%s\b' % (options.port + 1), ':$HGPORT1'),
   757         (r':%s\b' % (options.port + 2), ':$HGPORT2'),
   782         (r':%s\b' % (options.port + 2), ':$HGPORT2'),
   758         ])
   783         ])
   759     vlog("# Ret was:", ret)
   784     vlog("# Ret was:", ret)
   760 
       
   761     if options.timeout > 0:
       
   762         signal.alarm(0)
       
   763 
   785 
   764     mark = '.'
   786     mark = '.'
   765     if ret == 0:
   787     if ret == 0:
   766         success()
   788         success()
   767 
   789 
   797         if failed:
   819         if failed:
   798             fail("hghave failed checking for %s" % failed[-1], ret)
   820             fail("hghave failed checking for %s" % failed[-1], ret)
   799             skipped = False
   821             skipped = False
   800         else:
   822         else:
   801             skip(missing[-1])
   823             skip(missing[-1])
       
   824     elif ret == 'timeout':
       
   825         mark = 't'
       
   826         fail("timed out", ret)
   802     elif out != refout:
   827     elif out != refout:
   803         mark = '!'
   828         mark = '!'
   804         if ret == 'timeout':
   829         if not options.nodiff:
   805             fail("timed out", ret)
   830             iolock.acquire()
   806         elif ret:
       
   807             fail("output changed and returned error code %d" % ret, ret)
       
   808         else:
       
   809             fail("output changed", ret)
       
   810         if ret != 'timeout' and not options.nodiff:
       
   811             if options.view:
   831             if options.view:
   812                 os.system("%s %s %s" % (options.view, ref, err))
   832                 os.system("%s %s %s" % (options.view, ref, err))
   813             else:
   833             else:
   814                 showdiff(refout, out, ref, err)
   834                 showdiff(refout, out, ref, err)
       
   835             iolock.release()
       
   836         if ret:
       
   837             fail("output changed and returned error code %d" % ret, ret)
       
   838         else:
       
   839             fail("output changed", ret)
   815         ret = 1
   840         ret = 1
   816     elif ret:
   841     elif ret:
   817         mark = '!'
   842         mark = '!'
   818         fail("returned error code %d" % ret, ret)
   843         fail("returned error code %d" % ret, ret)
   819 
   844 
   820     if not options.verbose:
   845     if not options.verbose:
       
   846         iolock.acquire()
   821         sys.stdout.write(mark)
   847         sys.stdout.write(mark)
   822         sys.stdout.flush()
   848         sys.stdout.flush()
       
   849         iolock.release()
   823 
   850 
   824     killdaemons()
   851     killdaemons()
   825 
   852 
   826     os.chdir(TESTDIR)
   853     os.chdir(TESTDIR)
   827     if not options.keep_tmpdir:
   854     if not options.keep_tmpdir:
   933 
   960 
   934     if options.anycoverage:
   961     if options.anycoverage:
   935         outputcoverage(options)
   962         outputcoverage(options)
   936     sys.exit(failures != 0)
   963     sys.exit(failures != 0)
   937 
   964 
       
   965 results = dict(p=[], f=[], s=[], i=[])
       
   966 resultslock = threading.Lock()
       
   967 iolock = threading.Lock()
       
   968 
   938 def runqueue(options, tests, results):
   969 def runqueue(options, tests, results):
   939     for test in tests:
   970     for test in tests:
   940         ret = runone(options, test, results)
   971         ret = runone(options, test)
   941         if options.first and ret is not None and not ret:
   972         if options.first and ret is not None and not ret:
   942             break
   973             break
   943 
   974 
   944 def runtests(options, tests):
   975 def runtests(options, tests):
   945     global DAEMON_PIDS, HGRCPATH
   976     global DAEMON_PIDS, HGRCPATH
   946     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
   977     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
   947     HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
   978     HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
   948 
   979 
   949     results = dict(p=[], f=[], s=[], i=[])
       
   950 
       
   951     try:
   980     try:
   952         if INST:
   981         if INST:
   953             installhg(options)
   982             installhg(options)
   954             _checkhglib("Testing")
   983             _checkhglib("Testing")
   955 
       
   956         if options.timeout > 0:
       
   957             try:
       
   958                 signal.signal(signal.SIGALRM, alarmed)
       
   959                 vlog('# Running each test with %d second timeout' %
       
   960                      options.timeout)
       
   961             except AttributeError:
       
   962                 print 'WARNING: cannot run tests with timeouts'
       
   963                 options.timeout = 0
       
   964 
   984 
   965         if options.restart:
   985         if options.restart:
   966             orig = list(tests)
   986             orig = list(tests)
   967             while tests:
   987             while tests:
   968                 if os.path.exists(tests[0] + ".err"):
   988                 if os.path.exists(tests[0] + ".err"):