tests/run-tests.py
changeset 8674 0941ee76489e
parent 8673 a8066f2fd1aa
child 8687 78ab2a12b4d9
equal deleted inserted replaced
8673:a8066f2fd1aa 8674:0941ee76489e
     4 #
     4 #
     5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
     5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
     6 #
     6 #
     7 # This software may be used and distributed according to the terms of the
     7 # This software may be used and distributed according to the terms of the
     8 # GNU General Public License version 2, incorporated herein by reference.
     8 # GNU General Public License version 2, incorporated herein by reference.
       
     9 
       
    10 # Modifying this script is tricky because it has many modes:
       
    11 #   - serial (default) vs parallel (-jN, N > 1)
       
    12 #   - no coverage (default) vs coverage (-c, -C, -s)
       
    13 #   - temp install (default) vs specific hg script (--with-hg, --local)
       
    14 #   - tests are a mix of shell scripts and Python scripts
       
    15 #
       
    16 # If you change this script, it is recommended that you ensure you
       
    17 # haven't broken it by running it in various modes with a representative
       
    18 # sample of test scripts.  For example:
       
    19 # 
       
    20 #  1) serial, no coverage, temp install:
       
    21 #      ./run-tests.py test-s*
       
    22 #  2) serial, no coverage, local hg:
       
    23 #      ./run-tests.py --local test-s*
       
    24 #  3) serial, coverage, temp install:
       
    25 #      ./run-tests.py -c test-s*
       
    26 #  4) serial, coverage, local hg:
       
    27 #      ./run-tests.py -c --local test-s*      # unsupported
       
    28 #  5) parallel, no coverage, temp install:
       
    29 #      ./run-tests.py -j2 test-s*
       
    30 #  6) parallel, no coverage, local hg:
       
    31 #      ./run-tests.py -j2 --local test-s*
       
    32 #  7) parallel, coverage, temp install:
       
    33 #      ./run-tests.py -j2 -c test-s*          # currently broken
       
    34 #  8) parallel, coverage, local install
       
    35 #      ./run-tests.py -j2 -c --local test-s*  # unsupported (and broken)
       
    36 #
       
    37 # (You could use any subset of the tests: test-s* happens to match
       
    38 # enough that it's worth doing parallel runs, few enough that it
       
    39 # completes fairly quickly, includes both shell and Python scripts, and
       
    40 # includes some scripts that run daemon processes.)
     9 
    41 
    10 import difflib
    42 import difflib
    11 import errno
    43 import errno
    12 import optparse
    44 import optparse
    13 import os
    45 import os
    32 # reserved exit code to skip test (used by hghave)
    64 # reserved exit code to skip test (used by hghave)
    33 SKIPPED_STATUS = 80
    65 SKIPPED_STATUS = 80
    34 SKIPPED_PREFIX = 'skipped: '
    66 SKIPPED_PREFIX = 'skipped: '
    35 FAILED_PREFIX  = 'hghave check failed: '
    67 FAILED_PREFIX  = 'hghave check failed: '
    36 PYTHON = sys.executable
    68 PYTHON = sys.executable
    37 hgpkg = None
       
    38 
    69 
    39 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
    70 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
    40 
    71 
    41 defaults = {
    72 defaults = {
    42     'jobs': ('HGTEST_JOBS', 1),
    73     'jobs': ('HGTEST_JOBS', 1),
    79     parser.add_option("-v", "--verbose", action="store_true",
   110     parser.add_option("-v", "--verbose", action="store_true",
    80         help="output verbose messages")
   111         help="output verbose messages")
    81     parser.add_option("-n", "--nodiff", action="store_true",
   112     parser.add_option("-n", "--nodiff", action="store_true",
    82         help="skip showing test changes")
   113         help="skip showing test changes")
    83     parser.add_option("--with-hg", type="string",
   114     parser.add_option("--with-hg", type="string",
    84         help="test existing install at given location")
   115         metavar="HG",
       
   116         help="test using specified hg script rather than a "
       
   117              "temporary installation")
       
   118     parser.add_option("--local", action="store_true",
       
   119         help="shortcut for --with-hg=<testdir>/../hg")
    85     parser.add_option("--pure", action="store_true",
   120     parser.add_option("--pure", action="store_true",
    86         help="use pure Python code instead of C extensions")
   121         help="use pure Python code instead of C extensions")
    87 
   122 
    88     for option, default in defaults.items():
   123     for option, default in defaults.items():
    89         defaults[option] = int(os.environ.get(*default))
   124         defaults[option] = int(os.environ.get(*default))
    90     parser.set_defaults(**defaults)
   125     parser.set_defaults(**defaults)
    91     (options, args) = parser.parse_args()
   126     (options, args) = parser.parse_args()
    92 
   127 
    93     global vlog
   128     if options.with_hg:
       
   129         if not (os.path.isfile(options.with_hg) and
       
   130                 os.access(options.with_hg, os.X_OK)):
       
   131             parser.error('--with-hg must specify an executable hg script')
       
   132         if not os.path.basename(options.with_hg) == 'hg':
       
   133             sys.stderr.write('warning: --with-hg should specify an hg script')
       
   134     if options.local:
       
   135         testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
       
   136         hgbin = os.path.join(os.path.dirname(testdir), 'hg')
       
   137         if not os.access(hgbin, os.X_OK):
       
   138             parser.error('--local specified, but %r not found or not executable'
       
   139                          % hgbin)
       
   140         options.with_hg = hgbin
       
   141 
    94     options.anycoverage = (options.cover or
   142     options.anycoverage = (options.cover or
    95                            options.cover_stdlib or
   143                            options.cover_stdlib or
    96                            options.annotate)
   144                            options.annotate)
    97 
   145 
       
   146     if options.anycoverage and options.with_hg:
       
   147         # I'm not sure if this is a fundamental limitation or just a
       
   148         # bug.  But I don't want to waste people's time and energy doing
       
   149         # test runs that don't give the results they want.
       
   150         parser.error("sorry, coverage options do not work when --with-hg "
       
   151                      "or --local specified")
       
   152 
       
   153     global vlog
    98     if options.verbose:
   154     if options.verbose:
    99         if options.jobs > 1 or options.child is not None:
   155         if options.jobs > 1 or options.child is not None:
   100             pid = "[%d]" % os.getpid()
   156             pid = "[%d]" % os.getpid()
   101         else:
   157         else:
   102             pid = None
   158             pid = None
   224         for line in f:
   280         for line in f:
   225             print line,
   281             print line,
   226         f.close()
   282         f.close()
   227         sys.exit(1)
   283         sys.exit(1)
   228     os.chdir(TESTDIR)
   284     os.chdir(TESTDIR)
   229 
       
   230     os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
       
   231 
       
   232     pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
       
   233     pythonpath = os.environ.get("PYTHONPATH")
       
   234     if pythonpath:
       
   235         pythonpath = pydir + os.pathsep + pythonpath
       
   236     else:
       
   237         pythonpath = pydir
       
   238     os.environ["PYTHONPATH"] = pythonpath
       
   239 
   285 
   240     usecorrectpython()
   286     usecorrectpython()
   241 
   287 
   242     vlog("# Installing dummy diffstat")
   288     vlog("# Installing dummy diffstat")
   243     f = open(os.path.join(BINDIR, 'diffstat'), 'w')
   289     f = open(os.path.join(BINDIR, 'diffstat'), 'w')
   510         sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
   556         sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
   511                          '         (expected %s)\n'
   557                          '         (expected %s)\n'
   512                          % (verb, actualhg, expecthg))
   558                          % (verb, actualhg, expecthg))
   513 
   559 
   514 def runchildren(options, tests):
   560 def runchildren(options, tests):
   515     if not options.with_hg:
   561     if INST:
   516         installhg(options)
   562         installhg(options)
   517         _checkhglib("Testing")
   563         _checkhglib("Testing")
   518 
   564 
   519     optcopy = dict(options.__dict__)
   565     optcopy = dict(options.__dict__)
   520     optcopy['jobs'] = 1
   566     optcopy['jobs'] = 1
   521     optcopy['with_hg'] = INST
   567     if optcopy['with_hg'] is None:
       
   568         optcopy['with_hg'] = os.path.join(BINDIR, "hg")
   522     opts = []
   569     opts = []
   523     for opt, value in optcopy.iteritems():
   570     for opt, value in optcopy.iteritems():
   524         name = '--' + opt.replace('_', '-')
   571         name = '--' + opt.replace('_', '-')
   525         if value is True:
   572         if value is True:
   526             opts.append(name)
   573             opts.append(name)
   577     global DAEMON_PIDS, HGRCPATH
   624     global DAEMON_PIDS, HGRCPATH
   578     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
   625     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
   579     HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
   626     HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
   580 
   627 
   581     try:
   628     try:
   582         if not options.with_hg:
   629         if INST:
   583             installhg(options)
   630             installhg(options)
   584             _checkhglib("Testing")
   631             _checkhglib("Testing")
   585 
   632 
   586         if options.timeout > 0:
   633         if options.timeout > 0:
   587             try:
   634             try:
   685     os.environ["HGPORT"] = str(options.port)
   732     os.environ["HGPORT"] = str(options.port)
   686     os.environ["HGPORT1"] = str(options.port + 1)
   733     os.environ["HGPORT1"] = str(options.port + 1)
   687     os.environ["HGPORT2"] = str(options.port + 2)
   734     os.environ["HGPORT2"] = str(options.port + 2)
   688 
   735 
   689     if options.with_hg:
   736     if options.with_hg:
   690         INST = options.with_hg
   737         INST = None
       
   738         BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
       
   739 
       
   740         # This looks redundant with how Python initializes sys.path from
       
   741         # the location of the script being executed.  Needed because the
       
   742         # "hg" specified by --with-hg is not the only Python script
       
   743         # executed in the test suite that needs to import 'mercurial'
       
   744         # ... which means it's not really redundant at all.
       
   745         PYTHONDIR = BINDIR
   691     else:
   746     else:
   692         INST = os.path.join(HGTMP, "install")
   747         INST = os.path.join(HGTMP, "install")
   693     BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
   748         BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
   694     PYTHONDIR = os.path.join(INST, "lib", "python")
   749         PYTHONDIR = os.path.join(INST, "lib", "python")
       
   750 
       
   751     os.environ["BINDIR"] = BINDIR
       
   752     os.environ["PYTHON"] = PYTHON
       
   753 
       
   754     if not options.child:
       
   755         path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
       
   756         os.environ["PATH"] = os.pathsep.join(path)
       
   757 
       
   758         # Deliberately override existing PYTHONPATH: do not want success
       
   759         # to depend on what happens to be in caller's environment.
       
   760         os.environ["PYTHONPATH"] = PYTHONDIR
       
   761 
   695     COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
   762     COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
   696 
   763 
   697     if len(args) == 0:
   764     if len(args) == 0:
   698         args = os.listdir(".")
   765         args = os.listdir(".")
   699         args.sort()
   766         args.sort()
   708         print "# Ran 0 tests, 0 skipped, 0 failed."
   775         print "# Ran 0 tests, 0 skipped, 0 failed."
   709         return
   776         return
   710 
   777 
   711     vlog("# Using TESTDIR", TESTDIR)
   778     vlog("# Using TESTDIR", TESTDIR)
   712     vlog("# Using HGTMP", HGTMP)
   779     vlog("# Using HGTMP", HGTMP)
       
   780     vlog("# Using PATH", os.environ["PATH"])
       
   781     vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
   713 
   782 
   714     try:
   783     try:
   715         if len(tests) > 1 and options.jobs > 1:
   784         if len(tests) > 1 and options.jobs > 1:
   716             runchildren(options, tests)
   785             runchildren(options, tests)
   717         else:
   786         else: