--- a/hgext/acl.py Mon Jun 01 00:56:29 2009 +0200
+++ b/hgext/acl.py Mon Jun 01 01:24:01 2009 +0200
@@ -61,7 +61,7 @@
(key, len(pats), user))
if pats:
return match.match(repo.root, '', pats)
- return match.never(repo.root, '')
+ return match.exact(repo.root, '', [])
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
--- a/mercurial/cmdutil.py Mon Jun 01 00:56:29 2009 +0200
+++ b/mercurial/cmdutil.py Mon Jun 01 01:24:01 2009 +0200
@@ -256,7 +256,6 @@
opts.get('include'), opts.get('exclude'), default)
def badfn(f, msg):
repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
- return False
m.bad = badfn
return m
--- a/mercurial/commands.py Mon Jun 01 00:56:29 2009 +0200
+++ b/mercurial/commands.py Mon Jun 01 01:24:01 2009 +0200
@@ -29,24 +29,22 @@
If no names are given, add all files to the repository.
"""
- rejected = None
+ bad = []
exacts = {}
names = []
m = cmdutil.match(repo, pats, opts)
- m.bad = lambda x,y: True
- for abs in repo.walk(m):
- if m.exact(abs):
- if ui.verbose:
- ui.status(_('adding %s\n') % m.rel(abs))
- names.append(abs)
- exacts[abs] = 1
- elif abs not in repo.dirstate:
- ui.status(_('adding %s\n') % m.rel(abs))
- names.append(abs)
+ oldbad = m.bad
+ m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
+
+ for f in repo.walk(m):
+ exact = m.exact(f)
+ if exact or f not in repo.dirstate:
+ names.append(f)
+ if ui.verbose or not exact:
+ ui.status(_('adding %s\n') % m.rel(f))
if not opts.get('dry_run'):
- rejected = repo.add(names)
- rejected = [p for p in rejected if p in exacts]
- return rejected and 1 or 0
+ bad += [f for f in repo.add(names) if f in m.files()]
+ return bad and 1 or 0
def addremove(ui, repo, *pats, **opts):
"""add all new files, delete all missing files
@@ -2486,13 +2484,12 @@
def badfn(path, msg):
if path in names:
- return False
+ return
path_ = path + '/'
for f in names:
if f.startswith(path_):
- return False
+ return
ui.warn("%s: %s\n" % (m.rel(path), msg))
- return False
m = cmdutil.match(repo, pats, opts)
m.bad = badfn
--- a/mercurial/dirstate.py Mon Jun 01 00:56:29 2009 +0200
+++ b/mercurial/dirstate.py Mon Jun 01 01:24:01 2009 +0200
@@ -425,19 +425,15 @@
def fwarn(f, msg):
self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
return False
- badfn = fwarn
- if hasattr(match, 'bad'):
- badfn = match.bad
- def badtype(f, mode):
+ def badtype(mode):
kind = _('unknown')
if stat.S_ISCHR(mode): kind = _('character device')
elif stat.S_ISBLK(mode): kind = _('block device')
elif stat.S_ISFIFO(mode): kind = _('fifo')
elif stat.S_ISSOCK(mode): kind = _('socket')
elif stat.S_ISDIR(mode): kind = _('directory')
- self._ui.warn(_('%s: unsupported file type (type is %s)\n')
- % (self.pathto(f), kind))
+ return _('unsupported file type (type is %s)') % kind
ignore = self._ignore
dirignore = self._dirignore
@@ -450,6 +446,7 @@
dirignore = util.always
matchfn = match.matchfn
+ badfn = match.bad
dmap = self._map
normpath = util.normpath
normalize = self.normalize
@@ -463,22 +460,12 @@
work = []
wadd = work.append
- if match.anypats():
- #match.match with patterns
- dostep3 = True
- nomatches = False
- elif not match.files():
- #match.always or match.never
- dostep3 = matchfn('')
- nomatches = not dostep3
- else:
- #match.exact or match.match without pattern
- dostep3 = False
- nomatches = matchfn == match.exact
-
- if nomatches:
- #skip step 2
- dirignore = util.always
+ exact = skipstep3 = False
+ if matchfn == match.exact: # match.exact
+ exact = True
+ dirignore = util.always # skip step 2
+ elif match.files() and not match.anypats(): # match.match, no patterns
+ skipstep3 = True
files = set(match.files())
if not files or '.' in files:
@@ -495,7 +482,7 @@
st = lstat(join(nf))
kind = getkind(st.st_mode)
if kind == dirkind:
- dostep3 = True
+ skipstep3 = False
if nf in dmap:
#file deleted on disk but still in dirstate
results[nf] = None
@@ -504,28 +491,20 @@
elif kind == regkind or kind == lnkkind:
results[nf] = st
else:
- badtype(ff, kind)
+ badfn(ff, badtype(kind))
if nf in dmap:
results[nf] = None
except OSError, inst:
- keep = False
- prefix = nf + "/"
- for fn in dmap:
- if nf == fn:
- if matchfn(nf):
- results[nf] = None
- keep = True
- break
- elif fn.startswith(prefix):
- dostep3 = True
- keep = True
- break
- if not keep:
- if inst.errno != errno.ENOENT:
- fwarn(ff, inst.strerror)
- elif badfn(ff, inst.strerror):
- if nf not in results and not ignore(nf) and matchfn(nf):
- results[nf] = None
+ if nf in dmap: # does it exactly match a file?
+ results[nf] = None
+ else: # does it match a directory?
+ prefix = nf + "/"
+ for fn in dmap:
+ if fn.startswith(prefix):
+ skipstep3 = False
+ break
+ else:
+ badfn(ff, inst.strerror)
# step 2: visit subdirectories
while work:
@@ -562,7 +541,7 @@
results[nf] = None
# step 3: report unseen items in the dmap hash
- if dostep3 and not nomatches:
+ if not skipstep3 and not exact:
visit = sorted([f for f in dmap if f not in results and matchfn(f)])
for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
--- a/mercurial/localrepo.py Mon Jun 01 00:56:29 2009 +0200
+++ b/mercurial/localrepo.py Mon Jun 01 01:24:01 2009 +0200
@@ -948,7 +948,6 @@
def bad(f, msg):
if f not in ctx1:
self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
- return False
match.bad = bad
if working: # we need to scan the working dir
--- a/mercurial/match.py Mon Jun 01 00:56:29 2009 +0200
+++ b/mercurial/match.py Mon Jun 01 01:24:01 2009 +0200
@@ -81,7 +81,10 @@
for f in self._files:
yield f
def bad(self, f, msg):
- return True
+ '''callback for each explicit file that can't be
+ found/accessed, with an error message
+ '''
+ pass
def dir(self, f):
pass
def missing(self, f):
@@ -103,10 +106,6 @@
def __init__(self, root, cwd):
match.__init__(self, root, cwd, [])
-class never(match):
- def __init__(self, root, cwd):
- match.__init__(self, root, cwd, [], exact = True)
-
def patkind(pat):
return _patsplit(pat, None)[0]
--- a/tests/run-tests.py Mon Jun 01 00:56:29 2009 +0200
+++ b/tests/run-tests.py Mon Jun 01 01:24:01 2009 +0200
@@ -7,6 +7,38 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
+# Modifying this script is tricky because it has many modes:
+# - serial (default) vs parallel (-jN, N > 1)
+# - no coverage (default) vs coverage (-c, -C, -s)
+# - temp install (default) vs specific hg script (--with-hg, --local)
+# - tests are a mix of shell scripts and Python scripts
+#
+# 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:
+# ./run-tests.py --local test-s*
+# 3) serial, coverage, temp install:
+# ./run-tests.py -c test-s*
+# 4) serial, coverage, local hg:
+# ./run-tests.py -c --local test-s* # unsupported
+# 5) parallel, no coverage, temp install:
+# ./run-tests.py -j2 test-s*
+# 6) parallel, no coverage, local hg:
+# ./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
+# ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
+#
+# (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.)
+
import difflib
import errno
import optparse
@@ -34,7 +66,6 @@
SKIPPED_PREFIX = 'skipped: '
FAILED_PREFIX = 'hghave check failed: '
PYTHON = sys.executable
-hgpkg = None
requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
@@ -81,7 +112,11 @@
parser.add_option("-n", "--nodiff", action="store_true",
help="skip showing test changes")
parser.add_option("--with-hg", type="string",
- help="test existing install at given location")
+ metavar="HG",
+ help="test using specified hg script rather than a "
+ "temporary installation")
+ parser.add_option("--local", action="store_true",
+ help="shortcut for --with-hg=<testdir>/../hg")
parser.add_option("--pure", action="store_true",
help="use pure Python code instead of C extensions")
@@ -90,13 +125,40 @@
parser.set_defaults(**defaults)
(options, args) = parser.parse_args()
- global vlog
+ if options.with_hg:
+ if not (os.path.isfile(options.with_hg) and
+ os.access(options.with_hg, os.X_OK)):
+ parser.error('--with-hg must specify an executable hg script')
+ if not os.path.basename(options.with_hg) == 'hg':
+ sys.stderr.write('warning: --with-hg should specify an hg script')
+ if options.local:
+ testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ hgbin = os.path.join(os.path.dirname(testdir), 'hg')
+ if not os.access(hgbin, os.X_OK):
+ parser.error('--local specified, but %r not found or not executable'
+ % hgbin)
+ options.with_hg = hgbin
+
options.anycoverage = (options.cover or
options.cover_stdlib or
options.annotate)
+ if options.anycoverage and options.with_hg:
+ # I'm not sure if this is a fundamental limitation or just a
+ # bug. But I don't want to waste people's time and energy doing
+ # test runs that don't give the results they want.
+ parser.error("sorry, coverage options do not work when --with-hg "
+ "or --local specified")
+
+ global vlog
if options.verbose:
+ if options.jobs > 1 or options.child is not None:
+ pid = "[%d]" % os.getpid()
+ else:
+ pid = None
def vlog(*msg):
+ if pid:
+ print pid,
for m in msg:
print m,
print
@@ -178,8 +240,7 @@
def cleanup(options):
if not options.keep_tmpdir:
- if options.verbose:
- print "# Cleaning up HGTMP", HGTMP
+ vlog("# Cleaning up HGTMP", HGTMP)
shutil.rmtree(HGTMP, True)
def usecorrectpython():
@@ -200,7 +261,6 @@
shutil.copymode(sys.executable, mypython)
def installhg(options):
- global PYTHON
vlog("# Performing temporary installation of HG")
installerrs = os.path.join("tests", "install.err")
pure = options.pure and "--pure" or ""
@@ -223,19 +283,7 @@
sys.exit(1)
os.chdir(TESTDIR)
- os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
-
- pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
- pythonpath = os.environ.get("PYTHONPATH")
- if pythonpath:
- pythonpath = pydir + os.pathsep + pythonpath
- else:
- pythonpath = pydir
- os.environ["PYTHONPATH"] = pythonpath
-
usecorrectpython()
- global hgpkg
- hgpkg = _hgpath()
vlog("# Installing dummy diffstat")
f = open(os.path.join(BINDIR, 'diffstat'), 'w')
@@ -264,15 +312,6 @@
os.path.join(BINDIR, '_hg.py')))
f.close()
os.chmod(os.path.join(BINDIR, 'hg'), 0700)
- PYTHON = '"%s" "%s" -x -p' % (sys.executable,
- os.path.join(TESTDIR, 'coverage.py'))
-
-def _hgpath():
- cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
- hgpath = os.popen(cmd % PYTHON)
- path = hgpath.read().strip()
- hgpath.close()
- return path
def outputcoverage(options):
@@ -491,15 +530,42 @@
return None
return ret == 0
-def runchildren(options, expecthg, tests):
- if not options.with_hg:
+_hgpath = None
+
+def _gethgpath():
+ """Return the path to the mercurial package that is actually found by
+ the current Python interpreter."""
+ global _hgpath
+ if _hgpath is not None:
+ return _hgpath
+
+ cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
+ pipe = os.popen(cmd % PYTHON)
+ try:
+ _hgpath = pipe.read().strip()
+ finally:
+ pipe.close()
+ return _hgpath
+
+def _checkhglib(verb):
+ """Ensure that the 'mercurial' package imported by python is
+ the one we expect it to be. If not, print a warning to stderr."""
+ expecthg = os.path.join(PYTHONDIR, 'mercurial')
+ actualhg = _gethgpath()
+ if actualhg != expecthg:
+ sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
+ ' (expected %s)\n'
+ % (verb, actualhg, expecthg))
+
+def runchildren(options, tests):
+ if INST:
installhg(options)
- if hgpkg != expecthg:
- print '# Testing unexpected mercurial: %s' % hgpkg
+ _checkhglib("Testing")
optcopy = dict(options.__dict__)
optcopy['jobs'] = 1
- optcopy['with_hg'] = INST
+ if optcopy['with_hg'] is None:
+ optcopy['with_hg'] = os.path.join(BINDIR, "hg")
opts = []
for opt, value in optcopy.iteritems():
name = '--' + opt.replace('_', '-')
@@ -549,23 +615,20 @@
for s in fails:
print "Failed %s: %s" % (s[0], s[1])
- if hgpkg != expecthg:
- print '# Tested unexpected mercurial: %s' % hgpkg
+ _checkhglib("Tested")
print "# Ran %d tests, %d skipped, %d failed." % (
tested, skipped, failed)
sys.exit(failures != 0)
-def runtests(options, expecthg, tests):
+def runtests(options, tests):
global DAEMON_PIDS, HGRCPATH
DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
try:
- if not options.with_hg:
+ if INST:
installhg(options)
-
- if hgpkg != expecthg:
- print '# Testing unexpected mercurial: %s' % hgpkg
+ _checkhglib("Testing")
if options.timeout > 0:
try:
@@ -627,8 +690,7 @@
print "Skipped %s: %s" % s
for s in fails:
print "Failed %s: %s" % s
- if hgpkg != expecthg:
- print '# Tested unexpected mercurial: %s' % hgpkg
+ _checkhglib("Tested")
print "# Ran %d tests, %d skipped, %d failed." % (
tested, skipped, failed)
@@ -672,15 +734,33 @@
os.environ["HGPORT2"] = str(options.port + 2)
if options.with_hg:
- INST = options.with_hg
+ INST = None
+ BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
+
+ # This looks redundant with how Python initializes sys.path from
+ # the location of the script being executed. Needed because the
+ # "hg" specified by --with-hg is not the only Python script
+ # executed in the test suite that needs to import 'mercurial'
+ # ... which means it's not really redundant at all.
+ PYTHONDIR = BINDIR
else:
INST = os.path.join(HGTMP, "install")
- BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
- PYTHONDIR = os.path.join(INST, "lib", "python")
+ BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
+ PYTHONDIR = os.path.join(INST, "lib", "python")
+
+ os.environ["BINDIR"] = BINDIR
+ os.environ["PYTHON"] = PYTHON
+
+ if not options.child:
+ path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
+ os.environ["PATH"] = os.pathsep.join(path)
+
+ # Deliberately override existing PYTHONPATH: do not want success
+ # to depend on what happens to be in caller's environment.
+ os.environ["PYTHONPATH"] = PYTHONDIR
+
COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
- expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
-
if len(args) == 0:
args = os.listdir(".")
args.sort()
@@ -697,12 +777,14 @@
vlog("# Using TESTDIR", TESTDIR)
vlog("# Using HGTMP", HGTMP)
+ vlog("# Using PATH", os.environ["PATH"])
+ vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
try:
if len(tests) > 1 and options.jobs > 1:
- runchildren(options, expecthg, tests)
+ runchildren(options, tests)
else:
- runtests(options, expecthg, tests)
+ runtests(options, tests)
finally:
cleanup(options)
--- a/tests/test-add.out Mon Jun 01 00:56:29 2009 +0200
+++ b/tests/test-add.out Mon Jun 01 01:24:01 2009 +0200
@@ -30,8 +30,8 @@
? a.orig
M a
? a.orig
-c does not exist!
-d does not exist!
+c: No such file or directory
+d: No such file or directory
M a
A c
? a.orig
--- a/tests/test-convert Mon Jun 01 00:56:29 2009 +0200
+++ b/tests/test-convert Mon Jun 01 01:24:01 2009 +0200
@@ -47,4 +47,6 @@
echo % converting empty dir should fail "nicely"
mkdir emptydir
-PATH=$BINDIR hg convert emptydir 2>&1 | sed 's,file://.*/emptydir,.../emptydir,g'
+# override $PATH to ensure p4 not visible; use $PYTHON in case we're
+# running from a devel copy, not a temp installation
+PATH=$BINDIR $PYTHON $BINDIR/hg convert emptydir 2>&1 | sed 's,file://.*/emptydir,.../emptydir,g'
--- a/tests/test-merge-tools Mon Jun 01 00:56:29 2009 +0200
+++ b/tests/test-merge-tools Mon Jun 01 01:24:01 2009 +0200
@@ -60,7 +60,9 @@
echo "# default is internal merge:"
beforemerge
echo "# hg merge -r 2"
-PATH=$BINDIR hg merge -r 2
+# override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
+# running from a devel copy, not a temp installation
+PATH=$BINDIR $PYTHON $BINDIR/hg merge -r 2
aftermerge
echo "# simplest hgrc using false for merge:"