# HG changeset patch # User Steve Losh # Date 1276316786 14400 # Node ID fee142eef88db0dde092d8eed6bc3a2cd295af22 # Parent 2f87e72fa84e09bab5a8ae051a1280eca50e7c02 cli: add the --check option diff -r 2f87e72fa84e -r fee142eef88d review/extension_ui.py --- 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)), ) diff -r 2f87e72fa84e -r fee142eef88d review/helps.py --- 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. + +""" diff -r 2f87e72fa84e -r fee142eef88d review/messages.py --- 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 +""" diff -r 2f87e72fa84e -r fee142eef88d review/tests/test_check.py --- /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. diff -r 2f87e72fa84e -r fee142eef88d review/tests/util.py --- 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)