(stable) merge stable
authorChristian Ebert <blacktrash@gmx.net>
Thu, 24 Dec 2009 10:15:49 +0000
branchstable
changeset 671 629956d13cbc
parent 665 06ccaeb6cfc1 (current diff)
parent 670 80d0ed025a02 (diff)
child 674 0efa9d6ac11f
(stable) merge
--- a/hgkw/keyword.py	Fri Nov 27 18:26:25 2009 +0000
+++ b/hgkw/keyword.py	Thu Dec 24 10:15:49 2009 +0000
@@ -93,7 +93,8 @@
 
 # hg commands that trigger expansion only when writing to working dir,
 # not when reading filelog, and unexpand when reading from working dir
-restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
+restricted = ('merge record resolve qfold qimport qnew qpush qrefresh qrecord'
+              ' transplant')
 
 # provide cvs-like UTC date filter
 utcdate = lambda x: util.datestr(x, '%Y/%m/%d %H:%M:%S')
@@ -537,7 +538,8 @@
     repo.__class__ = kwrepo
 
     extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
-    extensions.wrapfunction(patch, 'diff', kw_diff)
+    if not kwt.restrict:
+        extensions.wrapfunction(patch, 'diff', kw_diff)
     for c in 'annotate changeset rev filediff diff'.split():
         extensions.wrapfunction(webcommands, c, kwweb_skip)
 
--- a/tests/run-tests.py	Fri Nov 27 18:26:25 2009 +0000
+++ b/tests/run-tests.py	Thu Dec 24 10:15:49 2009 +0000
@@ -16,7 +16,7 @@
 # If you change this script, it is recommended that you ensure you
 # haven't broken it by running it in various modes with a representative
 # sample of test scripts.  For example:
-# 
+#
 #  1) serial, no coverage, temp install:
 #      ./run-tests.py test-s*
 #  2) serial, no coverage, local hg:
@@ -31,14 +31,17 @@
 #      ./run-tests.py -j2 --local test-s*
 #  7) parallel, coverage, temp install:
 #      ./run-tests.py -j2 -c test-s*          # currently broken
-#  8) parallel, coverage, local install
+#  8) parallel, coverage, local install:
 #      ./run-tests.py -j2 -c --local test-s*  # unsupported (and broken)
+#  9) parallel, custom tmp dir:
+#      ./run-tests.py -j2 --tmpdir /tmp/myhgtests
 #
 # (You could use any subset of the tests: test-s* happens to match
 # enough that it's worth doing parallel runs, few enough that it
 # completes fairly quickly, includes both shell and Python scripts, and
 # includes some scripts that run daemon processes.)
 
+from ConfigParser import ConfigParser
 import difflib
 import errno
 import optparse
@@ -90,9 +93,16 @@
     parser.add_option("-j", "--jobs", type="int",
         help="number of jobs to run in parallel"
              " (default: $%s or %d)" % defaults['jobs'])
+    parser.add_option("-k", "--keywords",
+        help="run tests matching keywords")
     parser.add_option("--keep-tmpdir", action="store_true",
-        help="keep temporary directory after running tests"
-             " (best used with --tmpdir)")
+        help="keep temporary directory after running tests")
+    parser.add_option("--tmpdir", type="string",
+        help="run tests in the given temporary directory"
+             " (implies --keep-tmpdir)")
+    parser.add_option("-d", "--debug", action="store_true",
+        help="debug mode: write output of test scripts to console"
+             " rather than capturing and diff'ing it (disables timeout)")
     parser.add_option("-R", "--restart", action="store_true",
         help="restart at last error")
     parser.add_option("-p", "--port", type="int",
@@ -102,11 +112,11 @@
         help="retest failed tests")
     parser.add_option("-s", "--cover_stdlib", action="store_true",
         help="print a test coverage report inc. standard libraries")
+    parser.add_option("-S", "--noskips", action="store_true",
+        help="don't report skip tests verbosely")
     parser.add_option("-t", "--timeout", type="int",
         help="kill errant tests after TIMEOUT seconds"
              " (default: $%s or %d)" % defaults['timeout'])
-    parser.add_option("--tmpdir", type="string",
-        help="run tests in the given temporary directory")
     parser.add_option("-v", "--verbose", action="store_true",
         help="output verbose messages")
     parser.add_option("-n", "--nodiff", action="store_true",
@@ -119,6 +129,13 @@
         help="shortcut for --with-hg=<testdir>/../hg")
     parser.add_option("--pure", action="store_true",
         help="use pure Python code instead of C extensions")
+    parser.add_option("-3", "--py3k-warnings", action="store_true",
+        help="enable Py3k warnings on Python 2.6+")
+    parser.add_option("--inotify", action="store_true",
+        help="enable inotify extension when running tests")
+    parser.add_option("--blacklist", action="append",
+        help="skip tests listed in the specified section of "
+             "the blacklist file")
 
     for option, default in defaults.items():
         defaults[option] = int(os.environ.get(*default))
@@ -162,15 +179,36 @@
             for m in msg:
                 print m,
             print
+            sys.stdout.flush()
     else:
         vlog = lambda *msg: None
 
+    if options.tmpdir:
+        options.tmpdir = os.path.expanduser(options.tmpdir)
+
     if options.jobs < 1:
-        print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
-        sys.exit(1)
+        parser.error('--jobs must be positive')
     if options.interactive and options.jobs > 1:
         print '(--interactive overrides --jobs)'
         options.jobs = 1
+    if options.interactive and options.debug:
+        parser.error("-i/--interactive and -d/--debug are incompatible")
+    if options.debug:
+        if options.timeout != defaults['timeout']:
+            sys.stderr.write(
+                'warning: --timeout option ignored with --debug\n')
+        options.timeout = 0
+    if options.py3k_warnings:
+        if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
+            parser.error('--py3k-warnings can only be used on Python 2.6+')
+    if options.blacklist:
+        configparser = ConfigParser()
+        configparser.read("blacklist")
+        blacklist = dict()
+        for section in options.blacklist:
+            for (item, value) in configparser.items(section):
+                blacklist["test-" + item] = section
+        options.blacklist = blacklist
 
     return (options, args)
 
@@ -213,9 +251,8 @@
 
     return missing, failed
 
-def showdiff(expected, output):
-    for line in difflib.unified_diff(expected, output,
-            "Expected output", "Test output"):
+def showdiff(expected, output, ref, err):
+    for line in difflib.unified_diff(expected, output, ref, err):
         sys.stdout.write(line)
 
 def findprogram(program):
@@ -266,11 +303,21 @@
     pure = options.pure and "--pure" or ""
 
     # Run installer in hg root
-    os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
+    script = os.path.realpath(sys.argv[0])
+    hgroot = os.path.dirname(os.path.dirname(script))
+    os.chdir(hgroot)
+    nohome = '--home=""'
+    if os.name == 'nt':
+        # The --home="" trick works only on OS where os.sep == '/'
+        # because of a distutils convert_path() fast-path. Avoid it at
+        # least on Windows for now, deal with .pydistutils.cfg bugs
+        # when they happen.
+        nohome = ''
     cmd = ('%s setup.py %s clean --all'
            ' install --force --prefix="%s" --install-lib="%s"'
-           ' --install-scripts="%s" >%s 2>&1'
-           % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
+           ' --install-scripts="%s" %s >%s 2>&1'
+           % (sys.executable, pure, INST, PYTHONDIR, BINDIR, nohome,
+              installerrs))
     vlog("# Running", cmd)
     if os.system(cmd) == 0:
         if not options.verbose:
@@ -297,6 +344,17 @@
     f.close()
     os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
 
+    if options.py3k_warnings and not options.anycoverage:
+        vlog("# Updating hg command to enable Py3k Warnings switch")
+        f = open(os.path.join(BINDIR, 'hg'), 'r')
+        lines = [line.rstrip() for line in f]
+        lines[0] += ' -3'
+        f.close()
+        f = open(os.path.join(BINDIR, 'hg'), 'w')
+        for line in lines:
+            f.write(line + '\n')
+        f.close()
+
     if options.anycoverage:
         vlog("# Installing coverage wrapper")
         os.environ['COVERAGE_FILE'] = COVERAGE_FILE
@@ -350,8 +408,13 @@
 
 def run(cmd, options):
     """Run command in a sub-process, capturing the output (stdout and stderr).
-    Return the exist code, and output."""
+    Return a tuple (exitcode, output).  output is None in debug mode."""
     # TODO: Use subprocess.Popen if we're running on Python 2.4
+    if options.debug:
+        proc = subprocess.Popen(cmd, shell=True)
+        ret = proc.wait()
+        return (ret, None)
+
     if os.name == 'nt' or sys.platform.startswith('java'):
         tochild, fromchild = os.popen4(cmd)
         tochild.close()
@@ -388,25 +451,31 @@
         if not options.verbose:
             skips.append((test, msg))
         else:
-            print "\nSkipping %s: %s" % (test, msg)
+            print "\nSkipping %s: %s" % (testpath, msg)
         return None
 
     def fail(msg):
         fails.append((test, msg))
         if not options.nodiff:
-            print "\nERROR: %s %s" % (test, msg)
+            print "\nERROR: %s %s" % (testpath, msg)
         return None
 
     vlog("# Test", test)
 
     # create a fresh hgrc
-    hgrc = file(HGRCPATH, 'w+')
+    hgrc = open(HGRCPATH, 'w+')
     hgrc.write('[ui]\n')
     hgrc.write('slash = True\n')
     hgrc.write('[defaults]\n')
     hgrc.write('backout = -d "0 0"\n')
     hgrc.write('commit = -d "0 0"\n')
     hgrc.write('tag = -d "0 0"\n')
+    if options.inotify:
+        hgrc.write('[extensions]\n')
+        hgrc.write('inotify=\n')
+        hgrc.write('[inotify]\n')
+        hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
+        hgrc.write('appendpid=True\n')
     hgrc.close()
 
     err = os.path.join(TESTDIR, test+".err")
@@ -430,7 +499,8 @@
     lctest = test.lower()
 
     if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
-        cmd = '%s "%s"' % (PYTHON, testpath)
+        py3kswitch = options.py3k_warnings and ' -3' or ''
+        cmd = '%s%s "%s"' % (PYTHON, py3kswitch, testpath)
     elif lctest.endswith('.bat'):
         # do not run batch scripts on non-windows
         if os.name != 'nt':
@@ -462,16 +532,24 @@
     mark = '.'
 
     skipped = (ret == SKIPPED_STATUS)
-    # If reference output file exists, check test output against it
-    if os.path.exists(ref):
+    # If we're not in --debug mode and reference output file exists,
+    # check test output against it.
+    if options.debug:
+        refout = None                   # to match out == None
+    elif os.path.exists(ref):
         f = open(ref, "r")
         refout = splitnewlines(f.read())
         f.close()
     else:
         refout = []
+
     if skipped:
         mark = 's'
-        missing, failed = parsehghaveoutput(out)
+        if out is None:                 # debug mode: nothing to parse
+            missing = ['unknown']
+            failed = None
+        else:
+            missing, failed = parsehghaveoutput(out)
         if not missing:
             missing = ['irrelevant']
         if failed:
@@ -486,7 +564,7 @@
         else:
             fail("output changed")
         if not options.nodiff:
-            showdiff(refout, out)
+            showdiff(refout, out, ref, err)
         ret = 1
     elif ret:
         mark = '!'
@@ -496,7 +574,7 @@
         sys.stdout.write(mark)
         sys.stdout.flush()
 
-    if ret != 0 and not skipped:
+    if ret != 0 and not skipped and not options.debug:
         # Save errors to a file for diagnosis
         f = open(err, "wb")
         for line in out:
@@ -505,7 +583,7 @@
 
     # Kill off any leftover daemon processes
     try:
-        fp = file(DAEMON_PIDS)
+        fp = open(DAEMON_PIDS)
         for line in fp:
             try:
                 pid = int(line)
@@ -590,6 +668,8 @@
             continue
         rfd, wfd = os.pipe()
         childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
+        childtmp = os.path.join(HGTMP, 'child%d' % j)
+        childopts += ['--tmpdir', childtmp]
         cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
         vlog(' '.join(cmdline))
         fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
@@ -614,8 +694,9 @@
         vlog('pid %d exited, status %d' % (pid, status))
         failures |= status
     print
-    for s in skips:
-        print "Skipped %s: %s" % (s[0], s[1])
+    if not options.noskips:
+        for s in skips:
+            print "Skipped %s: %s" % (s[0], s[1])
     for s in fails:
         print "Failed %s: %s" % (s[0], s[1])
 
@@ -659,10 +740,28 @@
 
         skips = []
         fails = []
+
         for test in tests:
+            if options.blacklist:
+                section = options.blacklist.get(test)
+                if section is not None:
+                    skips.append((test, "blacklisted (%s section)" % section))
+                    skipped += 1
+                    continue
+
             if options.retest and not os.path.exists(test + ".err"):
                 skipped += 1
                 continue
+
+            if options.keywords:
+                t = open(test).read().lower() + test.lower()
+                for k in options.keywords.lower().split():
+                    if k in t:
+                        break
+                else:
+                    skipped +=1
+                    continue
+
             ret = runone(options, test, skips, fails)
             if ret is None:
                 skipped += 1
@@ -716,15 +815,32 @@
 
     # Reset some environment variables to well-known values so that
     # the tests produce repeatable output.
-    os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
+    os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
     os.environ['TZ'] = 'GMT'
     os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
     os.environ['CDPATH'] = ''
+    os.environ['COLUMNS'] = '80'
 
     global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
     TESTDIR = os.environ["TESTDIR"] = os.getcwd()
-    HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
-                                                   options.tmpdir))
+    if options.tmpdir:
+        options.keep_tmpdir = True
+        tmpdir = options.tmpdir
+        if os.path.exists(tmpdir):
+            # Meaning of tmpdir has changed since 1.3: we used to create
+            # HGTMP inside tmpdir; now HGTMP is tmpdir.  So fail if
+            # tmpdir already exists.
+            sys.exit("error: temp dir %r already exists" % tmpdir)
+
+            # Automatically removing tmpdir sounds convenient, but could
+            # really annoy anyone in the habit of using "--tmpdir=/tmp"
+            # or "--tmpdir=$HOME".
+            #vlog("# Removing temp dir", tmpdir)
+            #shutil.rmtree(tmpdir)
+        os.makedirs(tmpdir)
+    else:
+        tmpdir = tempfile.mkdtemp('', 'hgtests.')
+    HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
     DAEMON_PIDS = None
     HGRCPATH = None
 
--- a/tests/test-keyword	Fri Nov 27 18:26:25 2009 +0000
+++ b/tests/test-keyword	Thu Dec 24 10:15:49 2009 +0000
@@ -2,9 +2,10 @@
 
 cat <<EOF >> $HGRCPATH
 [extensions]
-hgext.keyword =
-hgext.mq =
-hgext.notify =
+keyword =
+mq =
+notify =
+transplant =
 EOF
 
 # demo before [keyword] files are set up
@@ -337,6 +338,23 @@
 hg commit -m localresolve
 cat m
 
+echo % test restricted mode with transplant -b
+hg update 6
+hg branch foo
+mv a a.bak
+echo foobranch > a
+cat a.bak >> a
+rm a.bak
+hg commit -m 9foobranch
+hg update default
+hg -y transplant -b foo tip
+echo % no expansion in changeset
+hg tip -p
+echo % expansion in file
+head -n 2 a
+hg -q rollback
+hg -q update -C
+
 echo % switch off expansion
 echo % kwshrink with unknown file u
 cp a u
--- a/tests/test-keyword.out	Fri Nov 27 18:26:25 2009 +0000
+++ b/tests/test-keyword.out	Thu Dec 24 10:15:49 2009 +0000
@@ -1,6 +1,6 @@
 % hg kwdemo
 [extensions]
-hgext.keyword = 
+keyword = 
 [keyword]
 demo.txt = 
 [keywordmaps]
@@ -21,7 +21,7 @@
 $Revision: xxxxxxxxxxxx $
 $Source: /TMP/demo.txt,v $
 [extensions]
-hgext.keyword = 
+keyword = 
 [keyword]
 demo.txt = 
 [keywordmaps]
@@ -206,7 +206,7 @@
 % custom keyword expansion
 % try with kwdemo
 [extensions]
-hgext.keyword = 
+keyword = 
 [keyword]
 * = 
 b = ignore
@@ -420,6 +420,33 @@
 % resolve to local
 $Id: m 43dfd2854b5b Thu, 01 Jan 1970 00:00:00 +0000 test $
 bar
+% test restricted mode with transplant -b
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+marked working directory as branch foo
+created new head
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying 1c4378f51c4d
+1c4378f51c4d transplanted to 7d855abcab87
+% no expansion in changeset
+changeset:   11:7d855abcab87
+tag:         tip
+parent:      9:43dfd2854b5b
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     9foobranch
+
+diff -r 43dfd2854b5b -r 7d855abcab87 a
+--- a/a	Thu Jan 01 00:00:00 1970 +0000
++++ b/a	Thu Jan 01 00:00:00 1970 +0000
+@@ -1,3 +1,4 @@
++foobranch
+ expand $Id$
+ do not process $Id:
+ xxx $
+
+% expansion in file
+foobranch
+expand $Id: a 7d855abcab87 Thu, 01 Jan 1970 00:00:00 +0000 test $
 % switch off expansion
 % kwshrink with unknown file u
 overwriting a shrinking keywords