fee142eef88d

cli: add the --check option
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sat, 12 Jun 2010 00:26:26 -0400
parents 2f87e72fa84e
children f6d10a9c8fda
branches/tags (none)
files review/extension_ui.py review/helps.py review/messages.py review/tests/test_check.py review/tests/util.py

Changes

--- 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)