--- a/review/extension_ui.py Fri Jun 11 21:44:34 2010 -0400
+++ b/review/extension_ui.py Sat Jun 12 00:26:26 2010 -0400
@@ -93,6 +93,26 @@
except SignoffExists:
raise util.Abort(messages.SIGNOFF_EXISTS)
+def _check_command(ui, repo, **opts):
+ rd = ReviewDatastore(ui, repo)
+ rcset = rd[opts.pop('rev')]
+
+ if opts.pop('no_nos'):
+ if any(filter(lambda s: s.opinion == "no", rcset.signoffs)):
+ raise util.Abort(messages.CHECK_HAS_NOS)
+
+ yes_count = opts.pop('yeses')
+ if yes_count:
+ yes_count = int(yes_count)
+ if len(filter(lambda s: s.opinion == "yes", rcset.signoffs)) < yes_count:
+ raise util.Abort(messages.CHECK_TOO_FEW_YESES)
+
+ if opts.pop('seen'):
+ if not rcset.signoffs and not rcset.comments:
+ raise util.Abort(messages.CHECK_UNSEEN)
+
+ ui.note(messages.CHECK_SUCCESS)
+
def _review_command(ui, repo, *fnames, **opts):
rev = opts.pop('rev')
context = int(opts.pop('unified'))
@@ -282,6 +302,7 @@
hg help review-review
hg help review-comment
hg help review-signoff
+ hg help review-check
Once you've reviewed some changesets, don't forget to push your
comments and signoffs so other people can view them.
@@ -295,6 +316,8 @@
return _comment_command(ui, repo, *fnames, **opts)
elif opts.pop('signoff'):
return _signoff_command(ui, repo, **opts)
+ elif opts.pop('check'):
+ return _check_command(ui, repo, **opts)
else:
return _review_command(ui, repo, *fnames, **opts)
@@ -306,6 +329,9 @@
('r', 'rev', '.', 'the revision to review'),
('', 'check', False, 'check the review status of the given revision'),
+ ('', 'no-nos', False, 'ensure this revision does NOT have signoffs of "no"'),
+ ('', 'yeses', '', 'ensure this revision has at least NUM signoffs of "yes"'),
+ ('', 'seen', False, 'ensure this revision has a comment or signoff'),
('i', 'init', False, 'start code reviewing this repository'),
('', 'remote-path', '', 'the remote path to code review data'),
@@ -331,4 +357,5 @@
(['review-review'], ('Viewing code review data for changesets'), (helps.REVIEW)),
(['review-comment'], ('Adding code review comments for changesets'), (helps.COMMENT)),
(['review-signoff'], ('Adding code review signoffs for changesets'), (helps.SIGNOFF)),
+ (['review-check'], ('Checking the review status of changesets'), (helps.CHECK)),
)
--- a/review/helps.py Fri Jun 11 21:44:34 2010 -0400
+++ b/review/helps.py Sat Jun 12 00:26:26 2010 -0400
@@ -101,3 +101,29 @@
hg review --signoff --yes --force -m 'Nevermind, this is fine.'
"""
+
+CHECK = r"""
+hg review --check [-r REV] [--no-nos] [--yeses NUM] [--seen]
+
+If no revision is given, the current parent of the working directory
+will be used.
+
+Check that the changeset "passes" the given tests of review status. If no tests
+are given an error is returned.
+
+Tests are checked in order. If any tests fail the command returns a status of
+1 with a message describing the failure on stderr, otherwise it returns 0 and
+prints nothing.
+
+The following tests are available:
+
+ --no-nos ensures that there are not any "no" signoffs on the changeset.
+
+ --yeses NUM ensures that at least NUM people have signed off as "yes" on
+ the changeset.
+
+ --seen ensures that someone has commented or signed off on the changeset,
+ and can be used to make sure someone has at least taken a look at it. Note
+ that a signoff of "no" will make this test succeed.
+
+"""
--- a/review/messages.py Fri Jun 11 21:44:34 2010 -0400
+++ b/review/messages.py Sat Jun 12 00:26:26 2010 -0400
@@ -112,3 +112,19 @@
WEB_START = """\
starting CherryPy web server
"""
+
+CHECK_SUCCESS = """\
+changeset passed all tests
+"""
+
+CHECK_HAS_NOS = """\
+changeset has a signoff of "no"
+"""
+
+CHECK_TOO_FEW_YESES = """\
+changeset does not have enough signoffs of "yes"
+"""
+
+CHECK_UNSEEN = """\
+changeset has no comments or signoffs
+"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/tests/test_check.py Sat Jun 12 00:26:26 2010 -0400
@@ -0,0 +1,128 @@
+from nose import *
+from util import *
+from mercurial import util as hgutil
+from mercurial.node import hex
+from .. import messages
+
+BAD_ERROR = 'The correct error message was not printed.'
+def _check_e(e, m):
+ error = str(e)
+ assert m in e
+
+def _should_fail_with(m, **kwargs):
+ try:
+ output = review(**kwargs)
+ except hgutil.Abort, e:
+ _check_e(e, m)
+ else:
+ assert False, BAD_ERROR
+
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_empty():
+ review(check=True)
+
+ output = review(check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=True, no=True, message='.')
+ output = review(check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=True, yes=False, message='.', force=True)
+ output = review(check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(comment=True, message='.')
+ output = review(check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_empty_non_tip():
+ review(rev='0', check=True)
+
+ output = review(rev='0', check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=False, message='.')
+ output = review(rev='0', check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=True, message='.')
+ output = review(rev='0', check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(comment=True, message='.')
+ output = review(rev='0', check=True, verbose=True)
+ assert messages.CHECK_SUCCESS in output
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_no_nos():
+ output = review(check=True, verbose=True, no_nos=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=True, no=True, message='.')
+ _should_fail_with(messages.CHECK_HAS_NOS, check=True, verbose=True, no_nos=True)
+
+ review(signoff=True, yes=True, message='.', force=True)
+ output = review(check=True, verbose=True, no_nos=True)
+ assert messages.CHECK_SUCCESS in output
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_yeses():
+ _should_fail_with(messages.CHECK_TOO_FEW_YESES, check=True, verbose=True, yeses='1')
+
+ output = review(check=True, verbose=True, yeses='0')
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=True, yes=True, message='.')
+ output = review(check=True, verbose=True, yeses='1')
+ assert messages.CHECK_SUCCESS in output
+
+ _should_fail_with(messages.CHECK_TOO_FEW_YESES, check=True, verbose=True, yeses='2')
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_seen():
+ _should_fail_with(messages.CHECK_UNSEEN, check=True, verbose=True, seen=True)
+
+ review(signoff=True, yes=True, message='.')
+ output = review(check=True, verbose=True, seen=True)
+ assert messages.CHECK_SUCCESS in output
+
+ review(signoff=True, no=True, message='.', force=True)
+ output = review(check=True, verbose=True, seen=True)
+ assert messages.CHECK_SUCCESS in output
+
+ _should_fail_with(messages.CHECK_UNSEEN, rev='0', check=True, verbose=True, seen=True)
+
+ review(rev='0', comment=True, message='.')
+ output = review(check=True, verbose=True, seen=True)
+ assert messages.CHECK_SUCCESS in output
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_priority_no_nos():
+ review(signoff=True, no=True, message='.')
+ _should_fail_with(messages.CHECK_HAS_NOS, check=True, verbose=True, no_nos=True, yeses='0')
+ _should_fail_with(messages.CHECK_HAS_NOS, check=True, verbose=True, no_nos=True, seen=True)
+ _should_fail_with(messages.CHECK_HAS_NOS, check=True, verbose=True, no_nos=True, seen=True, yeses='0')
+
+ review(signoff=True, yes=True, message='.', force=True)
+ output = review(check=True, verbose=True, no_nos=True, seen=True, yeses='0')
+ assert messages.CHECK_SUCCESS in output
+
+ review(rev='0', signoff=True, no=True, message='.')
+ review(rev='0', comment=True, message='.')
+ _should_fail_with(messages.CHECK_HAS_NOS, rev='0', check=True, verbose=True, no_nos=True)
+ _should_fail_with(messages.CHECK_HAS_NOS, rev='0', check=True, verbose=True, no_nos=True, seen=True)
+ _should_fail_with(messages.CHECK_HAS_NOS, rev='0', check=True, verbose=True, no_nos=True, seen=True, yeses='0')
+
+@with_setup(setup_reviewed_sandbox, teardown_sandbox)
+def test_check_priority_yeses():
+ review(comment=True, message='.')
+ _should_fail_with(messages.CHECK_TOO_FEW_YESES, check=True, verbose=True, yeses='1', seen=True)
+
+ review(signoff=True, yes=True, message='.')
+ output = review(check=True, verbose=True, yeses='1', seen=True)
+ assert messages.CHECK_SUCCESS in output
+
+ # TODO: For multiple signoffs, no_nos should take priority over "yes" signoffs.
--- a/review/tests/util.py Fri Jun 11 21:44:34 2010 -0400
+++ b/review/tests/util.py Sat Jun 12 00:26:26 2010 -0400
@@ -8,9 +8,11 @@
from .. import api, extension_ui
_ui = ui.ui()
-def review(init=False, comment=False, signoff=False, yes=False, no=False,
- force=False, message='', rev='.', remote_path='', lines='', files=None,
- unified='5', web=False, verbose=False, debug=False):
+_ui.setconfig('extensions', 'progress', '!')
+def review(init=False, comment=False, signoff=False, check=False, yes=False,
+ no=False, force=False, message='', rev='.', remote_path='', lines='',
+ files=None, unified='5', web=False, verbose=False, debug=False, seen=False,
+ yeses='', no_nos=False):
if not files:
files = []
@@ -23,9 +25,10 @@
extension_ui.review(
_ui, get_sandbox_repo(), *files,
**dict(
- init=init, comment=comment, signoff=signoff, yes=yes, no=no,
- force=force, message=message, rev=rev, remote_path=remote_path,
- lines=lines, unified=unified, web=web
+ init=init, comment=comment, signoff=signoff, check=check, yes=yes,
+ no=no, force=force, message=message, rev=rev, remote_path=remote_path,
+ lines=lines, unified=unified, web=web, seen=seen, yeses=yeses,
+ no_nos=no_nos
)
)
_ui.verbose, _ui.debugflag = False, False
@@ -47,6 +50,11 @@
os.chdir(sandbox_repo_path)
commands.init(_ui)
+
+ sandbox_hg_path = os.path.join(sandbox_repo_path, '.hg')
+ with open(os.path.join(sandbox_hg_path, 'hgrc'), 'w') as hgrc:
+ hgrc.write('[extensions]\nprogress=!')
+
sandbox = get_sandbox_repo()
opts = { 'addremove': True, 'date': None, 'user': 'Review Tester',
@@ -76,6 +84,10 @@
rpath = os.path.join(sandbox.root, api.DEFAULT_DATASTORE_DIRNAME)
review(init=True, remote_path=rpath)
+ review_hg_path = os.path.join(rpath, '.hg')
+ with open(os.path.join(review_hg_path, 'hgrc'), 'w') as hgrc:
+ hgrc.write('[extensions]\nprogress=!')
+
opts = { 'addremove': True, 'date': None, 'user': 'Review Tester',
'logfile': None, 'message': "Add the code review.", }
commands.commit(_ui, sandbox, **opts)