# HG changeset patch # User Christian Ebert # Date 1320846297 0 # Node ID 4fad25af75dd756ee260a0d6bc4d262f5fe79832 # Parent 826681fc0976d957c28ebed98845fccc69900229 Add hghave test as of 5635a4017061 diff -r 826681fc0976 -r 4fad25af75dd tests/hghave --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/hghave Wed Nov 09 13:44:57 2011 +0000 @@ -0,0 +1,309 @@ +#!/usr/bin/env python +"""Test the running system for features availability. Exit with zero +if all features are there, non-zero otherwise. If a feature name is +prefixed with "no-", the absence of feature is tested. +""" +import optparse +import os +import re +import sys +import tempfile + +tempprefix = 'hg-hghave-' + +def matchoutput(cmd, regexp, ignorestatus=False): + """Return True if cmd executes successfully and its output + is matched by the supplied regular expression. + """ + r = re.compile(regexp) + fh = os.popen(cmd) + s = fh.read() + try: + ret = fh.close() + except IOError: + # Happen in Windows test environment + ret = 1 + return (ignorestatus or ret is None) and r.search(s) + +def has_baz(): + return matchoutput('baz --version 2>&1', r'baz Bazaar version') + +def has_bzr(): + try: + import bzrlib + return bzrlib.__doc__ != None + except ImportError: + return False + +def has_bzr114(): + try: + import bzrlib + return (bzrlib.__doc__ != None + and bzrlib.version_info[:2] >= (1, 14)) + except ImportError: + return False + +def has_cvs(): + re = r'Concurrent Versions System.*?server' + return matchoutput('cvs --version 2>&1', re) + +def has_darcs(): + return matchoutput('darcs --version', r'2\.[2-9]', True) + +def has_mtn(): + return matchoutput('mtn --version', r'monotone', True) and not matchoutput( + 'mtn --version', r'monotone 0\.', True) + +def has_eol_in_paths(): + try: + fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r') + os.close(fd) + os.remove(path) + return True + except: + return False + +def has_executablebit(): + fd, path = tempfile.mkstemp(prefix=tempprefix) + os.close(fd) + try: + s = os.lstat(path).st_mode + os.chmod(path, s | 0100) + return (os.lstat(path).st_mode & 0100 != 0) + finally: + os.remove(path) + +def has_icasefs(): + # Stolen from mercurial.util + fd, path = tempfile.mkstemp(prefix=tempprefix, dir='.') + os.close(fd) + try: + s1 = os.stat(path) + d, b = os.path.split(path) + p2 = os.path.join(d, b.upper()) + if path == p2: + p2 = os.path.join(d, b.lower()) + try: + s2 = os.stat(p2) + return s2 == s1 + except: + return False + finally: + os.remove(path) + +def has_inotify(): + try: + import hgext.inotify.linux.watcher + return True + except ImportError: + return False + +def has_fifo(): + return hasattr(os, "mkfifo") + +def has_cacheable_fs(): + from mercurial import util + + fd, path = tempfile.mkstemp(prefix=tempprefix) + os.close(fd) + try: + return util.cachestat(path).cacheable() + finally: + os.remove(path) + +def has_lsprof(): + try: + import _lsprof + return True + except ImportError: + return False + +def has_gettext(): + return matchoutput('msgfmt --version', 'GNU gettext-tools') + +def has_git(): + return matchoutput('git --version 2>&1', r'^git version') + +def has_docutils(): + try: + from docutils.core import publish_cmdline + return True + except ImportError: + return False + +def getsvnversion(): + m = matchoutput('svn --version 2>&1', r'^svn,\s+version\s+(\d+)\.(\d+)') + if not m: + return (0, 0) + return (int(m.group(1)), int(m.group(2))) + +def has_svn15(): + return getsvnversion() >= (1, 5) + +def has_svn13(): + return getsvnversion() >= (1, 3) + +def has_svn(): + return matchoutput('svn --version 2>&1', r'^svn, version') and \ + matchoutput('svnadmin --version 2>&1', r'^svnadmin, version') + +def has_svn_bindings(): + try: + import svn.core + version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR + if version < (1, 4): + return False + return True + except ImportError: + return False + +def has_p4(): + return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/') + +def has_symlink(): + return hasattr(os, "symlink") + +def has_tla(): + return matchoutput('tla --version 2>&1', r'The GNU Arch Revision') + +def has_gpg(): + return matchoutput('gpg --version 2>&1', r'GnuPG') + +def has_unix_permissions(): + d = tempfile.mkdtemp(prefix=tempprefix, dir=".") + try: + fname = os.path.join(d, 'foo') + for umask in (077, 007, 022): + os.umask(umask) + f = open(fname, 'w') + f.close() + mode = os.stat(fname).st_mode + os.unlink(fname) + if mode & 0777 != ~umask & 0666: + return False + return True + finally: + os.rmdir(d) + +def has_pyflakes(): + return matchoutput('echo "import re" 2>&1 | pyflakes', + r":1: 're' imported but unused", + True) + +def has_pygments(): + try: + import pygments + return True + except ImportError: + return False + +def has_outer_repo(): + return matchoutput('hg root 2>&1', r'') + +def has_ssl(): + try: + import ssl + import OpenSSL + OpenSSL.SSL.Context + return True + except ImportError: + return False + +checks = { + "baz": (has_baz, "GNU Arch baz client"), + "bzr": (has_bzr, "Canonical's Bazaar client"), + "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"), + "cacheable": (has_cacheable_fs, "cacheable filesystem"), + "cvs": (has_cvs, "cvs client/server"), + "darcs": (has_darcs, "darcs client"), + "docutils": (has_docutils, "Docutils text processing library"), + "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"), + "execbit": (has_executablebit, "executable bit"), + "fifo": (has_fifo, "named pipes"), + "gettext": (has_gettext, "GNU Gettext (msgfmt)"), + "git": (has_git, "git command line client"), + "gpg": (has_gpg, "gpg client"), + "icasefs": (has_icasefs, "case insensitive file system"), + "inotify": (has_inotify, "inotify extension support"), + "lsprof": (has_lsprof, "python lsprof module"), + "mtn": (has_mtn, "monotone client (>= 1.0)"), + "outer-repo": (has_outer_repo, "outer repo"), + "p4": (has_p4, "Perforce server and client"), + "pyflakes": (has_pyflakes, "Pyflakes python linter"), + "pygments": (has_pygments, "Pygments source highlighting library"), + "ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"), + "svn": (has_svn, "subversion client and admin tools"), + "svn13": (has_svn13, "subversion client and admin tools >= 1.3"), + "svn15": (has_svn15, "subversion client and admin tools >= 1.5"), + "svn-bindings": (has_svn_bindings, "subversion python bindings"), + "symlink": (has_symlink, "symbolic links"), + "tla": (has_tla, "GNU Arch tla client"), + "unix-permissions": (has_unix_permissions, "unix-style permissions"), +} + +def list_features(): + for name, feature in checks.iteritems(): + desc = feature[1] + print name + ':', desc + +def test_features(): + failed = 0 + for name, feature in checks.iteritems(): + check, _ = feature + try: + check() + except Exception, e: + print "feature %s failed: %s" % (name, e) + failed += 1 + return failed + +parser = optparse.OptionParser("%prog [options] [features]") +parser.add_option("--test-features", action="store_true", + help="test available features") +parser.add_option("--list-features", action="store_true", + help="list available features") +parser.add_option("-q", "--quiet", action="store_true", + help="check features silently") + +if __name__ == '__main__': + options, args = parser.parse_args() + if options.list_features: + list_features() + sys.exit(0) + + if options.test_features: + sys.exit(test_features()) + + quiet = options.quiet + + failures = 0 + + def error(msg): + global failures + if not quiet: + sys.stderr.write(msg + '\n') + failures += 1 + + for feature in args: + negate = feature.startswith('no-') + if negate: + feature = feature[3:] + + if feature not in checks: + error('skipped: unknown feature: ' + feature) + continue + + check, desc = checks[feature] + try: + available = check() + except Exception, e: + error('hghave check failed: ' + feature) + continue + + if not negate and not available: + error('skipped: missing feature: ' + desc) + elif negate and available: + error('skipped: system supports %s' % desc) + + if failures != 0: + sys.exit(1)