mirror of
https://github.com/facebook/sapling.git
synced 2024-12-28 07:33:10 +03:00
run-tests: add --watchman and --with-watchman flags
Summary: Similar to chg. Add flags to run tests with watchman. This is mostly moving features from `fsmonitor-run-tests.py`. The blacklist is converted to `#require no-fsmonitor`. Reviewed By: phillco Differential Revision: D8434518 fbshipit-source-id: a8512cd71c1171e9037f36dbef195f1e6210f27e
This commit is contained in:
parent
108668759f
commit
64c3b8c2ab
@ -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
|
@ -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())
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
$ hg debugextensions --excludedefault
|
||||
treedirstate (untested!)
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Test adding .hgeol
|
||||
|
||||
$ cat >> $HGRCPATH <<EOF
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Testing cloning with the EOL extension
|
||||
|
||||
$ cat >> $HGRCPATH <<EOF
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Test the EOL hook
|
||||
|
||||
$ hg init main
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Test EOL patching
|
||||
|
||||
$ cat >> $HGRCPATH <<EOF
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
https://bz.mercurial-scm.org/2493
|
||||
|
||||
Testing tagging with the EOL extension
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Test EOL update
|
||||
|
||||
$ cat >> $HGRCPATH <<EOF
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Test EOL extension
|
||||
|
||||
$ cat >> $HGRCPATH <<EOF
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
#require eol-in-paths
|
||||
|
||||
https://bz.mercurial-scm.org/352
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Test basic extension support
|
||||
|
||||
$ cat > foobar.py <<EOF
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Short help:
|
||||
|
||||
$ hg
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
$ hg init a
|
||||
$ cd a
|
||||
$ hg init b
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
$ . helpers-usechg.sh
|
||||
|
||||
$ hg init a
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Create test repository:
|
||||
|
||||
$ hg init repo
|
||||
|
@ -1,3 +1,5 @@
|
||||
#require no-fsmonitor
|
||||
|
||||
Let commit recurse into subrepos by default to match pre-2.0 behavior:
|
||||
|
||||
$ echo "[ui]" >> $HGRCPATH
|
||||
|
Loading…
Reference in New Issue
Block a user