mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 06:47:41 +03:00
tests: add a way to fix revision number usages automatically in tests
Summary: Some tests use too many revision numbers (ex. test-rebase-scenario-global.t). It's time consuming to fix it manually. Write a script to help with that. This will be used by the next diff. Reviewed By: DurhamG Differential Revision: D22174956 fbshipit-source-id: a873ad326ecbd9cdfa2df58839b0ef21626e1506
This commit is contained in:
parent
5265f6d372
commit
473b07c328
@ -197,6 +197,7 @@ def stringset(repo, subset, x, order):
|
||||
if x.startswith("-") or x == str(i):
|
||||
# 'x' was used as a revision number. Maybe warn and log it.
|
||||
_warnrevnum(repo.ui, x)
|
||||
scmutil.trackrevnumfortests(repo, [x])
|
||||
|
||||
x = i
|
||||
if x in subset or x == node.nullrev and isinstance(subset, fullreposet):
|
||||
|
@ -1384,3 +1384,58 @@ def contextnodesupportingwdir(ctx):
|
||||
)
|
||||
|
||||
return ctx.node()
|
||||
|
||||
|
||||
def trackrevnumfortests(repo, specs):
|
||||
"""Attempt to collect information to replace revision number with revset
|
||||
expressions in tests.
|
||||
|
||||
This works with the TESTFILE and TESTLINE environment variable set by
|
||||
run-tests.py.
|
||||
|
||||
Information will be written to $TESTDIR/.testrevnum.
|
||||
"""
|
||||
if not util.istest():
|
||||
return
|
||||
|
||||
trackrevnum = encoding.environ.get("TRACKREVNUM")
|
||||
testline = encoding.environ.get("TESTLINE")
|
||||
testfile = encoding.environ.get("TESTFILE")
|
||||
testdir = encoding.environ.get("TESTDIR")
|
||||
if not trackrevnum or not testline or not testfile or not testdir:
|
||||
return
|
||||
|
||||
for spec in specs:
|
||||
# 'spec' should be in sys.argv
|
||||
if not any(spec in a for a in pycompat.sysargv):
|
||||
continue
|
||||
# Consider 'spec' as a revision number.
|
||||
rev = int(spec)
|
||||
if rev < -1:
|
||||
continue
|
||||
ctx = repo[rev]
|
||||
if not ctx:
|
||||
return
|
||||
|
||||
# Check candidate revset expressions.
|
||||
candidates = []
|
||||
if rev == -1:
|
||||
candidates.append("null")
|
||||
desc = ctx.description()
|
||||
if desc:
|
||||
candidates.append("desc(%s)" % desc.split()[0])
|
||||
candidates.append("max(desc(%s))" % desc.split()[0])
|
||||
candidates.append("%s" % ctx.hex())
|
||||
|
||||
repo = repo.unfiltered()
|
||||
for candidate in candidates:
|
||||
try:
|
||||
nodes = list(repo.nodes(candidate))
|
||||
except Exception:
|
||||
continue
|
||||
if nodes == [ctx.node()]:
|
||||
with open(testdir + "/.testrevnum", "ab") as f:
|
||||
f.write(
|
||||
"fix(%r, %s, %r, %r)\n" % (testfile, testline, spec, candidate)
|
||||
)
|
||||
break
|
||||
|
125
eden/scm/tests/fix-revnum.py
Executable file
125
eden/scm/tests/fix-revnum.py
Executable file
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This software may be used and distributed according to the terms of the
|
||||
# GNU General Public License version 2.
|
||||
|
||||
"""
|
||||
Rewrite rev numbers in tests reported by scmutil.trackrevnumfortests
|
||||
|
||||
Usage:
|
||||
- Delete `.testrevnum`.
|
||||
- Set `TRACKREVNUM`.
|
||||
- Run tests to fix. Rev number usages are written in `.testrevnum`.
|
||||
- Run this script.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
contents = {} # {path: lines}
|
||||
fixed = set()
|
||||
|
||||
commithashre = re.compile(r"\A[0-9a-f]{6,40}\Z")
|
||||
|
||||
quote = repr
|
||||
|
||||
progress = 0
|
||||
|
||||
|
||||
def fix(path, linenum, numstr, spec):
|
||||
"""Attempt to replace numstr to spec at the given file and line"""
|
||||
# Do not fix the same thing multiple times.
|
||||
key = (path, linenum, numstr)
|
||||
if key in fixed:
|
||||
return
|
||||
else:
|
||||
fixed.add(key)
|
||||
|
||||
if path not in contents:
|
||||
with open(path, "rb") as f:
|
||||
contents[path] = f.read().splitlines(True)
|
||||
lines = contents[path]
|
||||
line = lines[linenum].decode("utf-8")
|
||||
newline = processline(line, numstr, spec)
|
||||
lines[linenum] = newline.encode("utf-8")
|
||||
|
||||
global progress
|
||||
progress += 1
|
||||
sys.stderr.write("\r%d fixes" % progress)
|
||||
sys.stderr.flush()
|
||||
|
||||
|
||||
def processline(line, numstr, spec):
|
||||
"""Replace numstr with spec in line, with some care about escaping"""
|
||||
# Do not replace '1' in "hg commit -m 'public 1'".
|
||||
# Do not replace '1' in "hg clone repo1 repo2".
|
||||
# Do not replace '1' in "hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f'"
|
||||
# Do not rewrite non-hg commands, like "initrepo repo1".
|
||||
if (
|
||||
any(s in line for s in ["hg commit", "hg clone", "debugbuilddag"])
|
||||
or "hg" not in line
|
||||
):
|
||||
return line
|
||||
|
||||
alnumstr = "abcdefghijklmnopqrstuvwxyz0123456789()"
|
||||
if numstr.startswith("-"):
|
||||
# A negative rev number.
|
||||
alnumstr += "-"
|
||||
alnumstr = set(alnumstr)
|
||||
|
||||
newline = ""
|
||||
buf = ""
|
||||
singlequoted = False
|
||||
doublequoted = False
|
||||
|
||||
for ch in line:
|
||||
if ch in alnumstr:
|
||||
buf += ch
|
||||
else:
|
||||
# A separator. Append 'buf'.
|
||||
# Do not rewrite a commit hash, even if it has numbers in it.
|
||||
# Do not replace '1' in 'HGPLAIN=1'.
|
||||
# Do not replace '1' in '-R repo1' or '--cwd repo1'.
|
||||
# Do not rewrite bookmark-like name (ex. foo@1).
|
||||
# Do not rewrite redirections like `2>&1`.
|
||||
# Do not rewrite recursively (ex. 2 -> desc(a2) -> desc(adesc(2))).
|
||||
if (
|
||||
not commithashre.match(buf)
|
||||
and not any(
|
||||
newline.endswith(c)
|
||||
for c in ("=", "&", ">", "<", "@", "-R ", "--cwd ")
|
||||
)
|
||||
and ch not in {">", "<"}
|
||||
and spec not in buf
|
||||
and "desc(" not in buf
|
||||
):
|
||||
if singlequoted or doublequoted or "(" not in spec:
|
||||
buf = buf.replace(numstr, spec)
|
||||
else:
|
||||
buf = buf.replace(numstr, quote(spec))
|
||||
newline += buf
|
||||
buf = ""
|
||||
|
||||
if ch == "'" and not newline.endswith("\\"):
|
||||
singlequoted = not singlequoted
|
||||
elif ch == '"' and not newline.endswith("\\"):
|
||||
doublequoted = not doublequoted
|
||||
|
||||
newline += ch
|
||||
|
||||
return newline
|
||||
|
||||
|
||||
with open(".testrevnum", "rb") as f:
|
||||
exec(f.read().decode("utf-8"))
|
||||
|
||||
for path, lines in contents.items():
|
||||
sys.stderr.write("\rwriting %s\n" % path)
|
||||
sys.stderr.flush()
|
||||
with open(path, "wb") as f:
|
||||
f.write(b"".join(lines))
|
||||
|
||||
with open(".testrevnum", "wb") as f:
|
||||
f.write(b"")
|
@ -1488,6 +1488,7 @@ class Test(unittest.TestCase):
|
||||
env["PYTHONUSERBASE"] = sysconfig.get_config_var("userbase")
|
||||
env["HGEMITWARNINGS"] = "1"
|
||||
env["TESTTMP"] = self._testtmp
|
||||
env["TESTFILE"] = self.path
|
||||
env["HOME"] = self._testtmp
|
||||
if self._usechg:
|
||||
env["CHGDISABLE"] = "0"
|
||||
@ -2044,6 +2045,7 @@ class TTest(Test):
|
||||
cmd = l[4:].split()
|
||||
if len(cmd) == 2 and cmd[0] == b"cd":
|
||||
l = b" $ cd %s || exit 1\n" % cmd[1]
|
||||
script.append(b"export TESTLINE=%d\n" % n)
|
||||
script.append(l[4:])
|
||||
elif l.startswith(b" > "): # continuations
|
||||
after.setdefault(prepos, []).append(l)
|
||||
|
Loading…
Reference in New Issue
Block a user