diff --git a/tests/blacklists/fsmonitor b/tests/blacklists/fsmonitor deleted file mode 100644 index 6ade78c47d..0000000000 --- a/tests/blacklists/fsmonitor +++ /dev/null @@ -1,28 +0,0 @@ -# Blacklist for a full testsuite run with fsmonitor enabled. -# Used by fsmonitor-run-tests. -# The following tests all fail because they either use extensions that conflict -# with fsmonitor, use subrepositories, or don't anticipate the extra file in -# the .hg directory that fsmonitor adds. - -#### mainly testing eol extension -test-eol-add.t -test-eol-clone.t -test-eol-hook.t -test-eol-patch.t -test-eol-tag.t -test-eol-update.t -test-eol.t -test-eolfilename.t - -#### mainly testing nested repositories -test-nested-repo.t -test-push-warn.t -test-subrepo-deep-nested-change.t -test-subrepo-recursion.t -test-subrepo.t - -#### fixing these seems redundant, because these don't focus on -#### operations in the working directory or .hg -test-debugextensions.t -test-extension.t -test-help.t diff --git a/tests/fsmonitor-run-tests.py b/tests/fsmonitor-run-tests.py deleted file mode 100755 index e61487aa8c..0000000000 --- a/tests/fsmonitor-run-tests.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python - -# fsmonitor-run-tests.py - Run Mercurial tests with fsmonitor enabled -# -# Copyright 2017 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -# -# This is a wrapper around run-tests.py that spins up an isolated instance of -# Watchman and runs the Mercurial tests against it. This ensures that the global -# version of Watchman isn't affected by anything this test does. - -from __future__ import absolute_import, print_function - -import argparse -import contextlib -import json -import os -import shutil -import subprocess -import sys -import tempfile -import uuid - - -osenvironb = getattr(os, "environb", os.environ) - -if sys.version_info > (3, 5, 0): - PYTHON3 = True - xrange = range # we use xrange in one place, and we'd rather not use range - - def _bytespath(p): - return p.encode("utf-8") - - -elif sys.version_info >= (3, 0, 0): - print( - "%s is only supported on Python 3.5+ and 2.7, not %s" - % (sys.argv[0], ".".join(str(v) for v in sys.version_info[:3])) - ) - sys.exit(70) # EX_SOFTWARE from `man 3 sysexit` -else: - PYTHON3 = False - - # In python 2.x, path operations are generally done using - # bytestrings by default, so we don't have to do any extra - # fiddling there. We define the wrapper functions anyway just to - # help keep code consistent between platforms. - def _bytespath(p): - return p - - -def getparser(): - """Obtain the argument parser used by the CLI.""" - parser = argparse.ArgumentParser( - description="Run tests with fsmonitor enabled.", - epilog="Unrecognized options are passed to run-tests.py.", - ) - # - keep these sorted - # - none of these options should conflict with any in run-tests.py - parser.add_argument( - "--keep-fsmonitor-tmpdir", - action="store_true", - help="keep temporary directory with fsmonitor state", - ) - parser.add_argument( - "--watchman", - help="location of watchman binary (default: watchman in PATH)", - default="watchman", - ) - - return parser - - -@contextlib.contextmanager -def watchman(args): - basedir = tempfile.mkdtemp(prefix="hg-fsmonitor") - try: - # Much of this configuration is borrowed from Watchman's test harness. - cfgfile = os.path.join(basedir, "config.json") - # TODO: allow setting a config - with open(cfgfile, "w") as f: - f.write(json.dumps({})) - - logfile = os.path.join(basedir, "log") - clilogfile = os.path.join(basedir, "cli-log") - if os.name == "nt": - sockfile = "\\\\.\\pipe\\watchman-test-%s" % uuid.uuid4().hex - else: - sockfile = os.path.join(basedir, "sock") - pidfile = os.path.join(basedir, "pid") - statefile = os.path.join(basedir, "state") - - argv = [ - args.watchman, - "--sockname", - sockfile, - "--logfile", - logfile, - "--pidfile", - pidfile, - "--statefile", - statefile, - "--foreground", - "--log-level=2", # debug logging for watchman - ] - - envb = osenvironb.copy() - envb[b"WATCHMAN_CONFIG_FILE"] = _bytespath(cfgfile) - with open(clilogfile, "wb") as f: - proc = subprocess.Popen(argv, env=envb, stdin=None, stdout=f, stderr=f) - try: - yield sockfile - finally: - proc.terminate() - proc.kill() - finally: - if args.keep_fsmonitor_tmpdir: - print("fsmonitor dir available at %s" % basedir) - else: - shutil.rmtree(basedir, ignore_errors=True) - - -def run(): - parser = getparser() - args, runtestsargv = parser.parse_known_args() - - with watchman(args) as sockfile: - osenvironb[b"WATCHMAN_SOCK"] = _bytespath(sockfile) - # Indicate to hghave that we're running with fsmonitor enabled. - osenvironb[b"HGFSMONITOR_TESTS"] = b"1" - - runtestdir = os.path.dirname(__file__) - runtests = os.path.join(runtestdir, "run-tests.py") - blacklist = os.path.join(runtestdir, "blacklists", "fsmonitor") - - runtestsargv.insert(0, runtests) - runtestsargv.extend( - ["--extra-config", "extensions.fsmonitor=", "--blacklist", blacklist] - ) - - return subprocess.call(runtestsargv) - - -if __name__ == "__main__": - sys.exit(run()) diff --git a/tests/run-tests.py b/tests/run-tests.py index fe76701b64..82ae28a9db 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -65,6 +65,7 @@ import tempfile import threading import time import unittest +import uuid import xml.dom.minidom as minidom @@ -362,7 +363,7 @@ def getparser(): harness.add_argument( "--bisect-repo", metavar="bisect_repo", - help=("Path of a repo to bisect. Use together with " "--known-good-rev"), + help=("Path of a repo to bisect. Use together with --known-good-rev"), ) harness.add_argument( "-d", @@ -448,7 +449,7 @@ def getparser(): ) harness.add_argument( "--tmpdir", - help="run tests in the given temporary directory" " (implies --keep-tmpdir)", + help="run tests in the given temporary directory (implies --keep-tmpdir)", ) harness.add_argument( "-v", "--verbose", action="store_true", help="output verbose messages" @@ -458,6 +459,9 @@ def getparser(): hgconf.add_argument( "--chg", action="store_true", help="install and use chg wrapper in place of hg" ) + hgconf.add_argument( + "--watchman", action="store_true", help="shortcut for --with-watchman=watchman" + ) hgconf.add_argument("--compiler", help="compiler to build with") hgconf.add_argument( "--extra-config-opt", @@ -509,14 +513,17 @@ def getparser(): hgconf.add_argument( "--with-hg", metavar="HG", - help="test using specified hg script rather than a " "temporary installation", + help="test using specified hg script rather than a temporary installation", + ) + hgconf.add_argument( + "--with-watchman", metavar="WATCHMAN", help="test using specified watchman" ) # This option should be deleted once test-check-py3-compat.t and other # Python 3 tests run with Python 3. hgconf.add_argument( "--with-python3", metavar="PYTHON3", - help="Python 3 interpreter (if running under Python 2)" " (TEMPORARY)", + help="Python 3 interpreter (if running under Python 2) (TEMPORARY)", ) reporting = parser.add_argument_group("Results Reporting") @@ -599,7 +606,7 @@ def parseargs(args, parser): binpath = os.path.join(reporootdir, relpath) if os.name != "nt" and not os.access(binpath, os.X_OK): parser.error( - "--local specified, but %r not found or " "not executable" % binpath + "--local specified, but %r not found or not executable" % binpath ) setattr(options, attr, binpath) @@ -618,10 +625,15 @@ def parseargs(args, parser): "--chg does not work when --with-hg is specified " "(use --with-chg instead)" ) + if options.watchman and options.with_watchman: + parser.error( + "--watchman does not work when --with-watchman is specified " + "(use --with-watchman instead)" + ) if options.color == "always" and not pygmentspresent: sys.stderr.write( - "warning: --color=always ignored because " "pygments is not installed\n" + "warning: --color=always ignored because pygments is not installed\n" ) if options.bisect_repo and not options.known_good_rev: @@ -647,12 +659,10 @@ def parseargs(args, parser): if options.anycoverage and options.local: # this needs some path mangling somewhere, I guess - parser.error("sorry, coverage options do not work when --local " "is specified") + parser.error("sorry, coverage options do not work when --local is specified") if options.anycoverage and options.with_hg: - parser.error( - "sorry, coverage options do not work when --with-hg " "is specified" - ) + parser.error("sorry, coverage options do not work when --with-hg is specified") global verbose if options.verbose: @@ -677,9 +687,7 @@ def parseargs(args, parser): parser.error("--py3k-warnings can only be used on Python 2.7") if options.with_python3: if PYTHON3: - parser.error( - "--with-python3 cannot be used when executing with " "Python 3" - ) + parser.error("--with-python3 cannot be used when executing with Python 3") options.with_python3 = canonpath(options.with_python3) # Verify Python3 executable is acceptable. @@ -697,7 +705,7 @@ def parseargs(args, parser): vers = version.LooseVersion(out[len("Python ") :]) if vers < version.LooseVersion("3.5.0"): parser.error( - "--with-python3 version must be 3.5.0 or greater; " "got %s" % out + "--with-python3 version must be 3.5.0 or greater; got %s" % out ) if options.blacklist: @@ -854,6 +862,7 @@ class Test(unittest.TestCase): slowtimeout=None, usechg=False, useipv6=False, + watchman=None, ): """Create a test from parameters. @@ -916,6 +925,7 @@ class Test(unittest.TestCase): self._hgcommand = hgcommand or b"hg" self._usechg = usechg self._useipv6 = useipv6 + self._watchman = watchman self._aborted = False self._daemonpids = [] @@ -983,6 +993,49 @@ class Test(unittest.TestCase): self._chgsockdir = os.path.join(self._threadtmp, b"%s.chgsock" % name) os.mkdir(self._chgsockdir) + if self._watchman: + self._watchmandir = os.path.join(self._threadtmp, b"%s.watchman" % name) + os.mkdir(self._watchmandir) + cfgfile = os.path.join(self._watchmandir, b"config.json") + + if os.name == "nt": + sockfile = "\\\\.\\pipe\\watchman-test-%s" % uuid.uuid4().hex + else: + sockfile = os.path.join(self._watchmandir, b"sock") + + self._watchmansock = sockfile + + clilogfile = os.path.join(self._watchmandir, "cli-log") + logfile = os.path.join(self._watchmandir, b"log") + pidfile = os.path.join(self._watchmandir, b"pid") + statefile = os.path.join(self._watchmandir, b"state") + + with open(cfgfile, "w") as f: + f.write(json.dumps({})) + + envb = osenvironb.copy() + envb[b"WATCHMAN_CONFIG_FILE"] = _bytespath(cfgfile) + envb[b"WATCHMAN_SOCK"] = _bytespath(sockfile) + + argv = [ + self._watchman, + "--sockname", + sockfile, + "--logfile", + logfile, + "--pidfile", + pidfile, + "--statefile", + statefile, + "--foreground", + "--log-level=2", # debug logging for watchman + ] + + with open(clilogfile, "wb") as f: + self._watchmanproc = subprocess.Popen( + argv, env=envb, stdin=None, stdout=f, stderr=f + ) + def run(self, result): """Run this test and report results against a TestResult instance.""" # This function is extremely similar to unittest.TestCase.run(). Once @@ -1130,6 +1183,19 @@ class Test(unittest.TestCase): # files are deleted shutil.rmtree(self._chgsockdir, True) + if self._watchman: + try: + self._watchmanproc.terminate() + self._watchmanproc.kill() + if self._keeptmpdir: + log( + "Keeping watchman dir: %s\n" % self._watchmandir.decode("utf-8") + ) + else: + shutil.rmtree(self._watchmandir, ignore_errors=True) + except Exception: + pass + if ( (self._ret != 0 or self._out != self._refout) and not self._skipped @@ -1289,6 +1355,10 @@ class Test(unittest.TestCase): if self._usechg: env["CHGSOCKNAME"] = os.path.join(self._chgsockdir, b"server") + if self._watchman: + env["WATCHMAN_SOCK"] = self._watchmansock + env["HGFSMONITOR_TESTS"] = "1" + return env def _createhgrc(self, path): @@ -1307,6 +1377,8 @@ class Test(unittest.TestCase): hgrc.write( b"usercache = %s\n" % (os.path.join(self._testtmp, b".cache/lfs")) ) + if self._watchman: + hgrc.write(b"[extensions]\nfsmonitor=\n") hgrc.write(b"[web]\n") hgrc.write(b"address = localhost\n") hgrc.write(b"ipv6 = %s\n" % str(self._useipv6).encode("ascii")) @@ -1314,7 +1386,7 @@ class Test(unittest.TestCase): for opt in self._extraconfigopts: section, key = opt.encode("utf-8").split(b".", 1) assert b"=" in key, ( - "extra config opt %s must " "have an = for assignment" % opt + "extra config opt %s must have an = for assignment" % opt ) hgrc.write(b"[%s]\n%s\n" % (section, key)) @@ -2024,7 +2096,7 @@ class TestResult(unittest._TextTestResult): if self._options.interactive: if test.readrefout() != expected: self.stream.write( - "Reference output has changed (run again to prompt " "changes)" + "Reference output has changed (run again to prompt changes)" ) else: self.stream.write("Accept this change? [n] ") @@ -2745,6 +2817,14 @@ class TestRunner(object): elif self.options.with_chg: chgbindir = os.path.dirname(os.path.realpath(self.options.with_chg)) self._hgcommand = os.path.basename(self.options.with_chg) + if self.options.with_watchman or self.options.watchman: + self._watchman = self.options.with_watchman or "watchman" + osenvironb[b"HGFSMONITOR_TESTS"] = b"1" + else: + osenvironb[b"BINDIR"] = self._bindir + self._watchman = None + if b"HGFSMONITOR_TESTS" in osenvironb: + del osenvironb[b"HGFSMONITOR_TESTS"] osenvironb[b"BINDIR"] = self._bindir osenvironb[b"PYTHON"] = PYTHON @@ -2822,6 +2902,8 @@ class TestRunner(object): vlog("# Using HGTMP", self._hgtmp) vlog("# Using PATH", os.environ["PATH"]) vlog("# Using", IMPL_PATH, osenvironb[IMPL_PATH]) + if self._watchman: + vlog("# Using watchman", self._watchman) vlog("# Writing to directory", self._outputdir) try: @@ -3014,6 +3096,7 @@ class TestRunner(object): hgcommand=self._hgcommand, usechg=bool(self.options.with_chg or self.options.chg), useipv6=useipv6, + watchman=self._watchman, **kwds ) t.should_reload = True diff --git a/tests/test-debugextensions.t b/tests/test-debugextensions.t index 64523ccd9b..86e677a904 100644 --- a/tests/test-debugextensions.t +++ b/tests/test-debugextensions.t @@ -1,3 +1,5 @@ +#require no-fsmonitor + $ hg debugextensions --excludedefault treedirstate (untested!) diff --git a/tests/test-eol-add.t b/tests/test-eol-add.t index aa06fede18..b24f862e10 100644 --- a/tests/test-eol-add.t +++ b/tests/test-eol-add.t @@ -1,3 +1,5 @@ +#require no-fsmonitor + Test adding .hgeol $ cat >> $HGRCPATH <> $HGRCPATH <> $HGRCPATH <> $HGRCPATH <> $HGRCPATH < foobar.py <> $HGRCPATH