Commit 4c22d721 authored by maruel@chromium.org's avatar maruel@chromium.org

Refactor gcl.py to use a similar pattern for every commands.

The next step will be to generate help automatically and remove the command
check loop.

TEST=not much
BUG=me

Review URL: http://codereview.chromium.org/2096003

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@47291 0039d316-1c4b-4281-b951-d872f2087c98
parent 79692d64
...@@ -19,7 +19,9 @@ import time ...@@ -19,7 +19,9 @@ import time
from third_party import upload from third_party import upload
import urllib2 import urllib2
__pychecker__ = 'unusednames=breakpad'
import breakpad import breakpad
__pychecker__ = ''
# gcl now depends on gclient. # gcl now depends on gclient.
from scm import SVN from scm import SVN
...@@ -59,6 +61,11 @@ MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" ...@@ -59,6 +61,11 @@ MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!"
# Global cache of files cached in GetCacheDir(). # Global cache of files cached in GetCacheDir().
FILES_CACHE = {} FILES_CACHE = {}
# Valid extensions for files we want to lint.
DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
DEFAULT_LINT_IGNORE_REGEX = r"$^"
def CheckHomeForFile(filename): def CheckHomeForFile(filename):
"""Checks the users home dir for the existence of the given file. Returns """Checks the users home dir for the existence of the given file. Returns
the path to the file if it's there, or None if it is not. the path to the file if it's there, or None if it is not.
...@@ -588,8 +595,7 @@ def GetIssueDescription(issue): ...@@ -588,8 +595,7 @@ def GetIssueDescription(issue):
return SendToRietveld("/%d/description" % issue) return SendToRietveld("/%d/description" % issue)
def Opened(show_unknown_files): def ListFiles(show_unknown_files):
"""Prints a list of modified files in the current directory down."""
files = GetModifiedFiles() files = GetModifiedFiles()
cl_keys = files.keys() cl_keys = files.keys()
cl_keys.sort() cl_keys.sort()
...@@ -613,16 +619,30 @@ def Opened(show_unknown_files): ...@@ -613,16 +619,30 @@ def Opened(show_unknown_files):
if show_unknown_files: if show_unknown_files:
for filename in unknown_files: for filename in unknown_files:
print "? %s" % filename print "? %s" % filename
return 0
def CMDopened(argv):
"""Lists modified files in the current directory down."""
__pychecker__ = 'unusednames=argv'
return ListFiles(False)
def Help(argv=None): def CMDstatus(argv):
if argv: """Lists modified and unknown files in the current directory down."""
if argv[0] == 'try': __pychecker__ = 'unusednames=argv'
return ListFiles(True)
def CMDhelp(argv=None):
"""Prints this help or help for the given command."""
if len(argv) > 2:
if argv[2] == 'try':
TryChange(None, ['--help'], swallow_exception=False) TryChange(None, ['--help'], swallow_exception=False)
return return 0
if argv[0] == 'upload': if argv[2] == 'upload':
upload.RealMain(['upload.py', '--help']) upload.RealMain(['upload.py', '--help'])
return return 0
print ( print (
"""GCL is a wrapper for Subversion that simplifies working with groups of files. """GCL is a wrapper for Subversion that simplifies working with groups of files.
...@@ -697,6 +717,8 @@ Advanced commands: ...@@ -697,6 +717,8 @@ Advanced commands:
gcl help [command] gcl help [command]
Print this help menu, or help for the given command if it exists. Print this help menu, or help for the given command if it exists.
""") """)
return 0
def GetEditor(): def GetEditor():
editor = os.environ.get("SVN_EDITOR") editor = os.environ.get("SVN_EDITOR")
...@@ -722,7 +744,7 @@ def OptionallyDoPresubmitChecks(change_info, committing, args): ...@@ -722,7 +744,7 @@ def OptionallyDoPresubmitChecks(change_info, committing, args):
return DoPresubmitChecks(change_info, committing, True) return DoPresubmitChecks(change_info, committing, True)
def UploadCL(change_info, args): def CMDupload(change_info, args):
if not change_info.GetFiles(): if not change_info.GetFiles():
print "Nothing to upload, changelist is empty." print "Nothing to upload, changelist is empty."
return return
...@@ -838,17 +860,19 @@ def UploadCL(change_info, args): ...@@ -838,17 +860,19 @@ def UploadCL(change_info, args):
def PresubmitCL(change_info): def CMDpresubmit(change_info, argv):
"""Reports what presubmit checks on the change would report.""" """Runs presubmit checks on the change."""
__pychecker__ = 'unusednames=argv'
if not change_info.GetFiles(): if not change_info.GetFiles():
print "Nothing to presubmit check, changelist is empty." print "Nothing to presubmit check, changelist is empty."
return return 0
print "*** Presubmit checks for UPLOAD would report: ***" print "*** Presubmit checks for UPLOAD would report: ***"
DoPresubmitChecks(change_info, False, False) result = DoPresubmitChecks(change_info, False, False)
print "\n*** Presubmit checks for COMMIT would report: ***" print "\n*** Presubmit checks for COMMIT would report: ***"
DoPresubmitChecks(change_info, True, False) result &= DoPresubmitChecks(change_info, True, False)
return not result
def TryChange(change_info, args, swallow_exception): def TryChange(change_info, args, swallow_exception):
...@@ -878,12 +902,12 @@ def TryChange(change_info, args, swallow_exception): ...@@ -878,12 +902,12 @@ def TryChange(change_info, args, swallow_exception):
prog='gcl try') prog='gcl try')
def Commit(change_info, args): def CMDcommit(change_info, args):
if not change_info.GetFiles(): if not change_info.GetFiles():
print "Nothing to commit, changelist is empty." print "Nothing to commit, changelist is empty."
return return 1
if not OptionallyDoPresubmitChecks(change_info, True, args): if not OptionallyDoPresubmitChecks(change_info, True, args):
return return 1
# We face a problem with svn here: Let's say change 'bleh' modifies # We face a problem with svn here: Let's say change 'bleh' modifies
# svn:ignore on dir1\. but another unrelated change 'pouet' modifies # svn:ignore on dir1\. but another unrelated change 'pouet' modifies
...@@ -932,9 +956,10 @@ def Commit(change_info, args): ...@@ -932,9 +956,10 @@ def Commit(change_info, args):
change_info.description += "\nCommitted: " + viewvc_url + revision change_info.description += "\nCommitted: " + viewvc_url + revision
change_info.CloseIssue() change_info.CloseIssue()
os.chdir(previous_cwd) os.chdir(previous_cwd)
return 0
def Change(change_info, args): def CMDchange(change_info, args):
"""Creates/edits a changelist.""" """Creates/edits a changelist."""
silent = FilterFlag(args, "--silent") silent = FilterFlag(args, "--silent")
...@@ -1003,7 +1028,7 @@ def Change(change_info, args): ...@@ -1003,7 +1028,7 @@ def Change(change_info, args):
os.remove(filename) os.remove(filename)
if not result: if not result:
return return 0
split_result = result.split(separator1, 1) split_result = result.split(separator1, 1)
if len(split_result) != 2: if len(split_result) != 2:
...@@ -1045,13 +1070,10 @@ def Change(change_info, args): ...@@ -1045,13 +1070,10 @@ def Change(change_info, args):
change_info.UpdateRietveldDescription() change_info.UpdateRietveldDescription()
change_info.needs_upload = False change_info.needs_upload = False
change_info.Save() change_info.Save()
return 0
# Valid extensions for files we want to lint. def CMDlint(change_info, args):
DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
DEFAULT_LINT_IGNORE_REGEX = r"$^"
def Lint(change_info, args):
"""Runs cpplint.py on all the files in |change_info|""" """Runs cpplint.py on all the files in |change_info|"""
try: try:
import cpplint import cpplint
...@@ -1085,6 +1107,7 @@ def Lint(change_info, args): ...@@ -1085,6 +1107,7 @@ def Lint(change_info, args):
print "Total errors found: %d\n" % cpplint._cpplint_state.error_count print "Total errors found: %d\n" % cpplint._cpplint_state.error_count
os.chdir(previous_cwd) os.chdir(previous_cwd)
return 1
def DoPresubmitChecks(change_info, committing, may_prompt): def DoPresubmitChecks(change_info, committing, may_prompt):
...@@ -1110,32 +1133,111 @@ def DoPresubmitChecks(change_info, committing, may_prompt): ...@@ -1110,32 +1133,111 @@ def DoPresubmitChecks(change_info, committing, may_prompt):
return result return result
def Changes(): def CMDchanges(argv):
"""Print all the changelists and their files.""" """Lists all the changelists and their files."""
__pychecker__ = 'unusednames=argv'
for cl in GetCLs(): for cl in GetCLs():
change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True)
print "\n--- Changelist " + change_info.name + ":" print "\n--- Changelist " + change_info.name + ":"
for filename in change_info.GetFiles(): for filename in change_info.GetFiles():
print "".join(filename) print "".join(filename)
return 0
def DeleteEmptyChangeLists(): def CMDdeleteempties(argv):
"""Delete all changelists that have no files.""" """Delete all changelists that have no files."""
__pychecker__ = 'unusednames=argv'
print "\n--- Deleting:" print "\n--- Deleting:"
for cl in GetCLs(): for cl in GetCLs():
change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True) change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True)
if not len(change_info._files): if not len(change_info._files):
print change_info.name print change_info.name
change_info.Delete() change_info.Delete()
return 0
def CMDnothave(argv):
"""Lists files unknown to Subversion."""
__pychecker__ = 'unusednames=argv'
for filename in UnknownFiles(argv[2:]):
print "? " + "".join(filename)
return 0
def CMDdiff(argv):
"""Diffs all files in the changelist."""
__pychecker__ = 'unusednames=argv'
files = GetFilesNotInCL()
print GenerateDiff([x[1] for x in files])
return 0
def CMDsettings(argv):
"""Prints code review settings."""
__pychecker__ = 'unusednames=argv'
# Force load settings
GetCodeReviewSetting("UNKNOWN");
del CODEREVIEW_SETTINGS['__just_initialized']
print '\n'.join(("%s: %s" % (str(k), str(v))
for (k,v) in CODEREVIEW_SETTINGS.iteritems()))
return 0
def CMDdescription(change_info, argv):
"""Prints the description of the specified change to stdout."""
__pychecker__ = 'unusednames=argv'
print change_info.description
return 0
def CMDdelete(change_info, argv):
"""Deletes a changelist."""
__pychecker__ = 'unusednames=argv'
change_info.Delete()
return 0
def CMDtry(change_info, argv):
"""Sends the change to the tryserver so a trybot can do a test run on your
code.
To send multiple changes as one path, use a comma-separated list of
changenames. Use 'gcl help try' for more information!"""
# When the change contains no file, send the "changename" positional
# argument to trychange.py.
__pychecker__ = 'unusednames=argv'
if change_info.GetFiles():
args = argv[3:]
else:
change_info = None
args = argv[2:]
TryChange(change_info, args, swallow_exception=False)
return 0
def CMDrename(argv):
"""Renames an existing change."""
if len(argv) != 4:
ErrorExit("Usage: gcl rename <old-name> <new-name>.")
src, dst = argv[2:4]
src_file = GetChangelistInfoFile(src)
if not os.path.isfile(src_file):
ErrorExit("Change '%s' does not exist." % src)
dst_file = GetChangelistInfoFile(dst)
if os.path.isfile(dst_file):
ErrorExit("Change '%s' already exists; pick a new name." % dst)
os.rename(src_file, dst_file)
print "Change '%s' renamed '%s'." % (src, dst)
return 0
def main(argv=None): def main(argv=None):
__pychecker__ = 'maxreturns=0'
if argv is None: if argv is None:
argv = sys.argv argv = sys.argv
if len(argv) == 1: if len(argv) == 1:
Help() return CMDhelp()
return 0;
try: try:
# Create the directories where we store information about changelists if it # Create the directories where we store information about changelists if it
...@@ -1152,48 +1254,24 @@ def main(argv=None): ...@@ -1152,48 +1254,24 @@ def main(argv=None):
# Commands that don't require an argument. # Commands that don't require an argument.
command = argv[1] command = argv[1]
if command == "opened" or command == "status": if command == "opened":
Opened(command == "status") return CMDopened(argv)
return 0 if command == "status":
return CMDstatus(argv)
if command == "nothave": if command == "nothave":
__pychecker__ = 'no-returnvalues' return CMDnothave(argv)
for filename in UnknownFiles(argv[2:]):
print "? " + "".join(filename)
return 0
if command == "changes": if command == "changes":
Changes() return CMDchanges(argv)
return 0
if command == "help": if command == "help":
Help(argv[2:]) return CMDhelp(argv)
return 0
if command == "diff" and len(argv) == 2: if command == "diff" and len(argv) == 2:
files = GetFilesNotInCL() return CMDdiff(argv)
print GenerateDiff([x[1] for x in files])
return 0
if command == "settings": if command == "settings":
# Force load settings return CMDsettings(argv)
GetCodeReviewSetting("UNKNOWN");
del CODEREVIEW_SETTINGS['__just_initialized']
print '\n'.join(("%s: %s" % (str(k), str(v))
for (k,v) in CODEREVIEW_SETTINGS.iteritems()))
return 0
if command == "deleteempties": if command == "deleteempties":
DeleteEmptyChangeLists() return CMDdeleteempties(argv)
return 0
if command == "rename": if command == "rename":
if len(argv) != 4: return CMDrename(argv)
ErrorExit("Usage: gcl rename <old-name> <new-name>.")
src, dst = argv[2:4]
src_file = GetChangelistInfoFile(src)
if not os.path.isfile(src_file):
ErrorExit("Change '%s' does not exist." % src)
dst_file = GetChangelistInfoFile(dst)
if os.path.isfile(dst_file):
ErrorExit("Change '%s' already exists; pick a new name." % dst)
os.rename(src_file, dst_file)
print "Change '%s' renamed '%s'." % (src, dst)
return 0
if command == "change": if command == "change":
if len(argv) == 2: if len(argv) == 2:
# Generate a random changelist name. # Generate a random changelist name.
...@@ -1222,28 +1300,21 @@ def main(argv=None): ...@@ -1222,28 +1300,21 @@ def main(argv=None):
fail_on_not_found, True) fail_on_not_found, True)
if command == "change": if command == "change":
Change(change_info, argv[3:]) return CMDchange(change_info, argv[3:])
elif command == "description": elif command == "description":
print change_info.description return CMDdescription(change_info, argv[3:])
elif command == "lint": elif command == "lint":
Lint(change_info, argv[3:]) return CMDlint(change_info, argv[3:])
elif command == "upload": elif command == "upload":
UploadCL(change_info, argv[3:]) return CMDupload(change_info, argv[3:])
elif command == "presubmit": elif command == "presubmit":
PresubmitCL(change_info) return CMDpresubmit(change_info, argv[3:])
elif command in ("commit", "submit"): elif command in ("commit", "submit"):
Commit(change_info, argv[3:]) return CMDcommit(change_info, argv[3:])
elif command == "delete": elif command == "delete":
change_info.Delete() return CMDdelete(change_info, argv[3:])
elif command == "try": elif command == "try":
# When the change contains no file, send the "changename" positional return CMDtry(change_info, argv)
# argument to trychange.py.
if change_info.GetFiles():
args = argv[3:]
else:
change_info = None
args = argv[2:]
TryChange(change_info, args, swallow_exception=False)
else: else:
# Everything else that is passed into gcl we redirect to svn, after adding # Everything else that is passed into gcl we redirect to svn, after adding
# the files. This allows commands such as 'gcl diff xxx' to work. # the files. This allows commands such as 'gcl diff xxx' to work.
......
...@@ -31,20 +31,25 @@ class GclUnittest(GclTestsBase): ...@@ -31,20 +31,25 @@ class GclUnittest(GclTestsBase):
def testMembersChanged(self): def testMembersChanged(self):
self.mox.ReplayAll() self.mox.ReplayAll()
members = [ members = [
'CODEREVIEW_SETTINGS', 'CODEREVIEW_SETTINGS_FILE', 'Change', 'CODEREVIEW_SETTINGS', 'CODEREVIEW_SETTINGS_FILE',
'ChangeInfo', 'Changes', 'Commit', 'DEFAULT_LINT_IGNORE_REGEX', 'CMDchange', 'CMDchanges', 'CMDcommit', 'CMDdelete', 'CMDdeleteempties',
'DEFAULT_LINT_REGEX', 'CheckHomeForFile', 'DeleteEmptyChangeLists', 'CMDdescription', 'CMDdiff', 'CMDhelp', 'CMDlint', 'CMDnothave',
'CMDopened', 'CMDpresubmit', 'CMDrename', 'CMDsettings', 'CMDstatus',
'CMDtry', 'CMDupload',
'ChangeInfo', 'DEFAULT_LINT_IGNORE_REGEX',
'DEFAULT_LINT_REGEX', 'CheckHomeForFile',
'DoPresubmitChecks', 'ErrorExit', 'FILES_CACHE', 'FilterFlag', 'DoPresubmitChecks', 'ErrorExit', 'FILES_CACHE', 'FilterFlag',
'GenerateChangeName', 'GenerateDiff', 'GetCLs', 'GetCacheDir', 'GenerateChangeName', 'GenerateDiff', 'GetCLs', 'GetCacheDir',
'GetCachedFile', 'GetChangelistInfoFile', 'GetChangesDir', 'GetCachedFile', 'GetChangelistInfoFile', 'GetChangesDir',
'GetCodeReviewSetting', 'GetEditor', 'GetFilesNotInCL', 'GetInfoDir', 'GetCodeReviewSetting', 'GetEditor', 'GetFilesNotInCL', 'GetInfoDir',
'GetIssueDescription', 'GetModifiedFiles', 'GetRepositoryRoot', 'Help', 'GetIssueDescription', 'GetModifiedFiles', 'GetRepositoryRoot',
'Lint', 'LoadChangelistInfoForMultiple', 'MISSING_TEST_MSG', 'Opened', 'ListFiles',
'OptionallyDoPresubmitChecks', 'PresubmitCL', 'REPOSITORY_ROOT', 'LoadChangelistInfoForMultiple', 'MISSING_TEST_MSG',
'OptionallyDoPresubmitChecks', 'REPOSITORY_ROOT',
'RunShell', 'RunShellWithReturnCode', 'SVN', 'RunShell', 'RunShellWithReturnCode', 'SVN',
'SendToRietveld', 'TryChange', 'UnknownFiles', 'UploadCL', 'Warn', 'SendToRietveld', 'TryChange', 'UnknownFiles', 'Warn',
'breakpad', 'gclient_utils', 'getpass', 'main', 'os', 'random', 're', 'breakpad', 'gclient_utils', 'getpass', 'main', 'os', 'random', 're',
'shutil', 'string', 'subprocess', 'sys', 'tempfile', 'upload', 'shutil', 'string', 'subprocess', 'sys', 'tempfile', 'time', 'upload',
'urllib2', 'urllib2',
] ]
# If this test fails, you should add the relevant test. # If this test fails, you should add the relevant test.
...@@ -79,71 +84,11 @@ class GclUnittest(GclTestsBase): ...@@ -79,71 +84,11 @@ class GclUnittest(GclTestsBase):
self.mox.ReplayAll() self.mox.ReplayAll()
self.assertEquals(gcl.GetRepositoryRoot(), root_path + '.~') self.assertEquals(gcl.GetRepositoryRoot(), root_path + '.~')
def testGetCachedFile(self):
# TODO(maruel): TEST ME
pass
def testGetCodeReviewSetting(self):
# TODO(maruel): TEST ME
pass
def testGetChangelistInfoFile(self):
# TODO(maruel): TEST ME
pass
def testLoadChangelistInfoForMultiple(self):
# TODO(maruel): TEST ME
pass
def testGetModifiedFiles(self):
# TODO(maruel): TEST ME
pass
def testGetFilesNotInCL(self):
# TODO(maruel): TEST ME
pass
def testSendToRietveld(self):
# TODO(maruel): TEST ME
pass
def testOpened(self):
# TODO(maruel): TEST ME
pass
def testHelp(self): def testHelp(self):
gcl.sys.stdout.write(mox.StrContains('GCL is a wrapper for Subversion')) gcl.sys.stdout.write(mox.StrContains('GCL is a wrapper for Subversion'))
gcl.sys.stdout.write('\n') gcl.sys.stdout.write('\n')
self.mox.ReplayAll() self.mox.ReplayAll()
gcl.Help() gcl.CMDhelp()
def testGenerateDiff(self):
# TODO(maruel): TEST ME
pass
def testPresubmitCL(self):
# TODO(maruel): TEST ME
pass
def testTryChange(self):
# TODO(maruel): TEST ME
pass
def testCommit(self):
# TODO(maruel): TEST ME
pass
def testChange(self):
# TODO(maruel): TEST ME
pass
def testLint(self):
# TODO(maruel): TEST ME
pass
def testDoPresubmitChecks(self):
# TODO(maruel): TEST ME
pass
class ChangeInfoUnittest(GclTestsBase): class ChangeInfoUnittest(GclTestsBase):
...@@ -230,7 +175,7 @@ class ChangeInfoUnittest(GclTestsBase): ...@@ -230,7 +175,7 @@ class ChangeInfoUnittest(GclTestsBase):
change_info.Save() change_info.Save()
class UploadCLUnittest(GclTestsBase): class CMDuploadUnittest(GclTestsBase):
def setUp(self): def setUp(self):
GclTestsBase.setUp(self) GclTestsBase.setUp(self)
self.mox.StubOutWithMock(gcl, 'CheckHomeForFile') self.mox.StubOutWithMock(gcl, 'CheckHomeForFile')
...@@ -270,7 +215,7 @@ class UploadCLUnittest(GclTestsBase): ...@@ -270,7 +215,7 @@ class UploadCLUnittest(GclTestsBase):
change_info.Save() change_info.Save()
self.mox.ReplayAll() self.mox.ReplayAll()
gcl.UploadCL(change_info, args) gcl.CMDupload(change_info, args)
def testServerOverride(self): def testServerOverride(self):
change_info = gcl.ChangeInfo('naame', 0, 0, 'deescription', change_info = gcl.ChangeInfo('naame', 0, 0, 'deescription',
...@@ -298,7 +243,7 @@ class UploadCLUnittest(GclTestsBase): ...@@ -298,7 +243,7 @@ class UploadCLUnittest(GclTestsBase):
gcl.os.chdir('somewhere') gcl.os.chdir('somewhere')
self.mox.ReplayAll() self.mox.ReplayAll()
gcl.UploadCL(change_info, args) gcl.CMDupload(change_info, args)
def testNoTry(self): def testNoTry(self):
change_info = gcl.ChangeInfo('naame', 0, 0, 'deescription', change_info = gcl.ChangeInfo('naame', 0, 0, 'deescription',
...@@ -325,7 +270,7 @@ class UploadCLUnittest(GclTestsBase): ...@@ -325,7 +270,7 @@ class UploadCLUnittest(GclTestsBase):
gcl.os.chdir('somewhere') gcl.os.chdir('somewhere')
self.mox.ReplayAll() self.mox.ReplayAll()
gcl.UploadCL(change_info, args) gcl.CMDupload(change_info, args)
def testNormal(self): def testNormal(self):
change_info = gcl.ChangeInfo('naame', 0, 0, 'deescription', change_info = gcl.ChangeInfo('naame', 0, 0, 'deescription',
...@@ -355,7 +300,7 @@ class UploadCLUnittest(GclTestsBase): ...@@ -355,7 +300,7 @@ class UploadCLUnittest(GclTestsBase):
gcl.os.chdir('somewhere') gcl.os.chdir('somewhere')
self.mox.ReplayAll() self.mox.ReplayAll()
gcl.UploadCL(change_info, args) gcl.CMDupload(change_info, args)
self.assertEquals(change_info.issue, 1) self.assertEquals(change_info.issue, 1)
self.assertEquals(change_info.patchset, 2) self.assertEquals(change_info.patchset, 2)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment