sapling/eden/scm/tests/test-fb-hgext-sampling.t

211 lines
7.8 KiB
Perl
Raw Normal View History

#chg-compatible
Setup. SCM_SAMPLING_FILEPATH needs to be cleared as some environments may
have it set.
$ unset SCM_SAMPLING_FILEPATH
$ mkcommit() {
> echo "$1" > "$1"
> hg add "$1"
> echo "add $1" > msg
> echo "" >> msg
> hg ci -l msg
> }
Init the repo
$ hg init testrepo
$ cd testrepo
$ mkcommit a
Create an extension that logs every commit and also call repo.revs twice
Create an extension that logs the call to commit
$ cat > $TESTTMP/logcommit.py << EOF
> from edenscm.mercurial import extensions, localrepo
> def cb(sample):
> return len(sample)
> def _commit(orig, repo, *args, **kwargs):
> repo.ui.log("commit", "match filter", k=1, a={"hi":"ho"})
> repo.ui.log("foo", "does not match filter", k=1, a={"hi":"ho"})
> repo.ui.log("commit", "message %s", "string", k=1, a={"hi":"ho"})
> return orig(repo, *args, **kwargs)
> def extsetup(ui):
> extensions.wrapfunction(localrepo.localrepository, 'commit', _commit)
> @ui.atexit
> def handler():
> ui._measuredtimes['atexit_measured'] += 7
> ui.warn("atexit handler executed\n")
> EOF
Set up the extension and set a log file
We whitelist only the 'commit' key, only the events with that key will be
logged
$ cat >> $HGRCPATH << EOF
> [ui]
> logmeasuredtimes=True
> [sampling]
> key.commit=commit_table
> key.measuredtimes=measuredtimes
> [extensions]
> sampling=
> EOF
$ LOGDIR=$TESTTMP/logs
$ mkdir $LOGDIR
$ echo "logcommit=$TESTTMP/logcommit.py" >> $HGRCPATH
$ echo "[sampling]" >> $HGRCPATH
$ echo "filepath = $LOGDIR/samplingpath.txt" >> $HGRCPATH
Do a couple of commits. We expect to log two messages per call to repo.commit.
$ mkdir a_topdir && cd a_topdir
$ mkcommit b
atexit handler executed
atexit handler executed
$ mkcommit c
atexit handler executed
atexit handler executed
>>> import json
>>> with open("$LOGDIR/samplingpath.txt") as f:
... data = f.read()
>>> for record in data.strip("\0").split("\0"):
... parsedrecord = json.loads(record)
... if parsedrecord['category'] == 'commit_table':
... print(' '.join([parsedrecord["data"]["msg"], parsedrecord["category"]]))
... assert len(parsedrecord["data"]) == 4
... elif parsedrecord['category'] == 'measuredtimes':
... print('atexit_measured: ', repr(sorted(parsedrecord['data'])))
filesystem: implement fs.pendingchanges() Summary: dirstate.status can be thought of as three stages: 1) checking what files have changed on disk, 2) adding nonnormal files (added, removed, copied, merged), and 3) adding clean files. The current implementation ties these together in one big pile. As part of refactoring the working copy interface into clear working copy and filesystem interfaces, this diff extracts the "check what files have changed on disk" part into the filesystem class. Note, this implements a walk function very similar to the one that exists in dirstate. The difference is that dirstate.walk cares about everything that is in the dirstate already, while the fs walk only cares about what files are different from the pristine tree, so it's easier to reason about. Note: This diff does not update the fsmonitor or eden dirstate implementations. That means those tests will break if I land this. I'm going to send two more diffs to update their implementations, then I'll be sure to land them all at once. Future cleanup: - lookup resolution should be done inside the filesystem layer, since it's an artifact of filesystem limitations. - I'd like to get rid of ignore file special casing and have that just be part of the matcher that is passed in. This might not be feasible though. - I'd like to get rid of explicit file special casing and have the caller handle that if needed. - I'd like to remove the ability to list clean files in status. Most callers don't need that capability, and a caller could compute that themselves if needed. Reviewed By: quark-zju Differential Revision: D17099990 fbshipit-source-id: d7530e26f057d8fd38aaa36512fa398a543b23c2
2019-10-09 02:43:10 +03:00
atexit_measured: [u'atexit_measured', u'command_duration', u'fswalk_time', u'metrics_type', u'stdio_blocked'] (no-fsmonitor !)
atexit_measured: [u'atexit_measured', u'command_duration', u'fsmonitorwalk_time', u'metrics_type', u'stdio_blocked', u'watchmanquery_time'] (fsmonitor !)
match filter commit_table
message string commit_table
filesystem: implement fs.pendingchanges() Summary: dirstate.status can be thought of as three stages: 1) checking what files have changed on disk, 2) adding nonnormal files (added, removed, copied, merged), and 3) adding clean files. The current implementation ties these together in one big pile. As part of refactoring the working copy interface into clear working copy and filesystem interfaces, this diff extracts the "check what files have changed on disk" part into the filesystem class. Note, this implements a walk function very similar to the one that exists in dirstate. The difference is that dirstate.walk cares about everything that is in the dirstate already, while the fs walk only cares about what files are different from the pristine tree, so it's easier to reason about. Note: This diff does not update the fsmonitor or eden dirstate implementations. That means those tests will break if I land this. I'm going to send two more diffs to update their implementations, then I'll be sure to land them all at once. Future cleanup: - lookup resolution should be done inside the filesystem layer, since it's an artifact of filesystem limitations. - I'd like to get rid of ignore file special casing and have that just be part of the matcher that is passed in. This might not be feasible though. - I'd like to get rid of explicit file special casing and have the caller handle that if needed. - I'd like to remove the ability to list clean files in status. Most callers don't need that capability, and a caller could compute that themselves if needed. Reviewed By: quark-zju Differential Revision: D17099990 fbshipit-source-id: d7530e26f057d8fd38aaa36512fa398a543b23c2
2019-10-09 02:43:10 +03:00
atexit_measured: [u'atexit_measured', u'command_duration', u'fswalk_time', u'metrics_type', u'stdio_blocked'] (no-fsmonitor !)
atexit_measured: [u'atexit_measured', u'command_duration', u'fswalk_time', u'metrics_type', u'stdio_blocked'] (no-fsmonitor !)
atexit_measured: [u'atexit_measured', u'command_duration', u'fsmonitorwalk_time', u'metrics_type', u'stdio_blocked', u'watchmanquery_time'] (fsmonitor !)
atexit_measured: [u'atexit_measured', u'command_duration', u'fsmonitorwalk_time', u'metrics_type', u'stdio_blocked', u'watchmanquery_time'] (fsmonitor !)
match filter commit_table
message string commit_table
filesystem: implement fs.pendingchanges() Summary: dirstate.status can be thought of as three stages: 1) checking what files have changed on disk, 2) adding nonnormal files (added, removed, copied, merged), and 3) adding clean files. The current implementation ties these together in one big pile. As part of refactoring the working copy interface into clear working copy and filesystem interfaces, this diff extracts the "check what files have changed on disk" part into the filesystem class. Note, this implements a walk function very similar to the one that exists in dirstate. The difference is that dirstate.walk cares about everything that is in the dirstate already, while the fs walk only cares about what files are different from the pristine tree, so it's easier to reason about. Note: This diff does not update the fsmonitor or eden dirstate implementations. That means those tests will break if I land this. I'm going to send two more diffs to update their implementations, then I'll be sure to land them all at once. Future cleanup: - lookup resolution should be done inside the filesystem layer, since it's an artifact of filesystem limitations. - I'd like to get rid of ignore file special casing and have that just be part of the matcher that is passed in. This might not be feasible though. - I'd like to get rid of explicit file special casing and have the caller handle that if needed. - I'd like to remove the ability to list clean files in status. Most callers don't need that capability, and a caller could compute that themselves if needed. Reviewed By: quark-zju Differential Revision: D17099990 fbshipit-source-id: d7530e26f057d8fd38aaa36512fa398a543b23c2
2019-10-09 02:43:10 +03:00
atexit_measured: [u'atexit_measured', u'command_duration', u'fswalk_time', u'metrics_type', u'stdio_blocked'] (no-fsmonitor !)
atexit_measured: [u'atexit_measured', u'command_duration', u'fsmonitorwalk_time', u'metrics_type', u'stdio_blocked', u'watchmanquery_time'] (fsmonitor !)
Test topdir logging:
$ setconfig sampling.logtopdir=True
$ setconfig sampling.key.command_info=command_info
$ hg st > /dev/null
atexit handler executed
>>> import json
>>> with open("$LOGDIR/samplingpath.txt") as f:
... data = f.read().strip("\0").split("\0")
>>> print([json.loads(d)["data"]["topdir"] for d in data if "topdir" in d])
[u'a_topdir']
Test env-var logging:
$ setconfig sampling.env_vars=TEST_VAR1,TEST_VAR2
$ setconfig sampling.key.env_vars=env_vars
$ export TEST_VAR1=abc
$ export TEST_VAR2=def
$ hg st > /dev/null
atexit handler executed
>>> import json, pprint
>>> with open("$LOGDIR/samplingpath.txt") as f:
... data = f.read().strip("\0").split("\0")
>>> alldata = {}
>>> for jsonstr in data:
... entry = json.loads(jsonstr)
... if entry["category"] == "env_vars":
... for k in sorted(entry["data"].keys()):
... print("%s: %s" % (k, entry["data"][k]))
env_test_var1: abc
env_test_var2: def
metrics_type: env_vars
Test exception logging:
$ setconfig sampling.key.exceptions=exceptions
$ enable rebase histedit
$ hg rebase
abort: nothing to rebase
atexit handler executed
[255]
Note: Errors raised by the dispatch logic aren't logged here:
$ hg st --nonexistant > /dev/null
abort: option --nonexistant not recognized
[255]
>>> import json, pprint
>>> with open("$LOGDIR/samplingpath.txt") as f:
... data = f.read().strip("\0").split("\0")
>>> alldata = {}
>>> for jsonstr in data:
... entry = json.loads(jsonstr)
... if entry["category"] == "exceptions":
... for k in sorted(entry["data"].keys()):
... print("%s: %s" % (k, entry["data"][k]))
exception_msg: nothing to rebase
exception_type: NoMergeDestAbort
metrics_type: exceptions
Test ui.metrics.gauge API
$ cat > $TESTTMP/a.py << EOF
> def reposetup(ui, repo):
> ui.metrics.gauge("foo_a", 1)
> ui.metrics.gauge("foo_b", 2)
> ui.metrics.gauge("foo_b", len(repo))
> ui.metrics.gauge("bar")
> ui.metrics.gauge("bar")
> EOF
$ SCM_SAMPLING_FILEPATH=$TESTTMP/a.txt hg log -r null -T '.\n' --config extensions.gauge=$TESTTMP/a.py --config sampling.key.metrics=aaa
.
atexit handler executed
>>> import os, json
>>> with open(os.path.join(os.environ["TESTTMP"], "a.txt"), "r") as f:
... lines = f.read().split("\0")
... for line in lines:
... if "foo" in line:
... obj = json.loads(line)
... category = obj["category"]
... data = obj["data"]
... print("category: %s" % category)
... for k, v in sorted(data.items()):
... print(" %s=%s" % (k, v))
category: aaa
bar=2
foo_a=1
foo_b=5
metrics_type=metrics
Metrics can be printed if devel.print-metrics is set:
$ hg log -r null -T '.\n' --config extensions.gauge=$TESTTMP/a.py --config devel.print-metrics=1
.
atexit handler executed
{ metrics : { bar : 2, foo : { a : 1, b : 5}}}
Metrics is logged to blackbox:
$ setconfig blackbox.track=metrics
$ hg log -r null -T '.\n' --config extensions.gauge=$TESTTMP/a.py
.
atexit handler executed
$ hg blackbox --no-timestamp --no-sid --pattern '{"legacy_log":{"service":"metrics"}}'
[legacy][metrics] {'metrics': {'bar': 2, 'foo': {'a': 1, 'b': 5}}}
[legacy][metrics] {'metrics': {'bar': 2, 'foo': {'a': 1, 'b': 5}}}
[legacy][metrics] {'metrics': {'bar': 2, 'foo': {'a': 1, 'b': 5}}}
atexit handler executed
Invalid format strings don't crash Mercurial
$ SCM_SAMPLING_FILEPATH=$TESTTMP/invalid.txt hg --config sampling.key.invalid=invalid debugsh -c 'repo.ui.log("invalid", "invalid format %s %s", "single")'
atexit handler executed
>>> import os, json
>>> with open(os.path.join(os.environ["TESTTMP"], "invalid.txt"), "r") as f:
... lines = f.read().split("\0")
... for line in lines:
... if "invalid" in line:
... obj = json.loads(line)
... category = obj["category"]
... data = obj["data"]
... print("category: %s" % category)
... for k, v in sorted(data.items()):
... print(" %s=%s" % (k, v))
category: invalid
metrics_type=invalid
msg=invalid format %s %s single