def testSuggestReviewers(self): change_info = self.fakeChange() output = presubmit_support.PresubmitOutput() output.reviewers = ['*****@*****.**', '*****@*****.**'] gcl.DoPresubmitChecks(change_info, False, True).AndReturn(output) #gcl.GetCodeReviewSetting('CODE_REVIEW_SERVER').AndReturn('my_server') gcl.os.getcwd().AndReturn('somewhere') gcl.os.chdir('proout') gcl.GenerateDiff(change_info.GetFileNames()) gcl.upload.RealMain([ 'upload.py', '-y', '--server=https://my_server', '[email protected],[email protected]', '--issue=1', '--title= ' ], change_info.patch).AndReturn(("1", "2")) change_info.get_reviewers().AndReturn( ['[email protected],[email protected]']) change_info.Save() change_info.PrimeLint() gcl.os.chdir('somewhere') gcl.sys.stdout.write("*** Upload does not submit a try; use gcl try to" " submit a try. ***") gcl.sys.stdout.write("\n") gcl.GetRepositoryRoot().AndReturn(self.fake_root_dir) gcl.ChangeInfo.Load('naame', self.fake_root_dir, True, True).AndReturn(change_info) self.mox.ReplayAll() gcl.CMDupload(['naame']) self.checkstdout( '*** Upload does not submit a try; use gcl try to submit ' 'a try. ***\n' '*** Upload does not submit a try; use gcl try to submit a try. ***\n' )
def mockLoad(self, files=None): self.mox.StubOutWithMock(gcl, 'GetRepositoryRoot') self.mox.StubOutWithMock(gcl.ChangeInfo, 'Load') gcl.GetRepositoryRoot().AndReturn(self.fake_root_dir) change_info = self.fakeChange(files) gcl.ChangeInfo.Load('naame', self.fake_root_dir, True, True).AndReturn(change_info) return change_info
def ProcessOptions(self): if not self.options.diff: # Generate the diff with svn and write it to the submit queue path. The # files are relative to the repository root, but we need patches relative # to one level up from there (i.e., 'src'), so adjust both the file # paths and the root of the diff. source_root = GetSourceRoot() prefix = PathDifference(source_root, gcl.GetRepositoryRoot()) adjusted_paths = [ os.path.join(prefix, x) for x in self.options.files ] self.options.diff = self.GenerateDiff(adjusted_paths, root=source_root) self.change_info = gcl.LoadChangelistInfoForMultiple( self.options.name, gcl.GetRepositoryRoot(), True, True) if not self.options.email: self.options.email = scm.SVN.GetEmail(gcl.GetRepositoryRoot())
def testGetRepositoryRootGood(self): gcl.REPOSITORY_ROOT = None root_path = gcl.os.path.join('bleh', 'prout', 'pouet') gcl.os.getcwd().AndReturn(root_path) result1 = {"Repository Root": "Some root"} gcl.SVN.CaptureInfo(root_path, print_error=False).AndReturn(result1) gcl.os.getcwd().AndReturn(root_path) results2 = {"Repository Root": "A different root"} gcl.SVN.CaptureInfo(gcl.os.path.dirname(root_path), print_error=False).AndReturn(results2) self.mox.ReplayAll() self.assertEquals(gcl.GetRepositoryRoot(), root_path)
def testNoServer(self): self.mox.StubOutWithMock(gcl.sys, 'stderr') gcl.sys.stderr.write( 'Don\'t use the -s flag, fix codereview.settings instead') gcl.sys.stderr.write('\n') gcl.GetRepositoryRoot().AndReturn(self.fake_root_dir) gcl.ChangeInfo.Load('naame', self.fake_root_dir, True, True).AndReturn(1) self.mox.ReplayAll() try: gcl.CMDupload(['naame', '-s', 'foo']) self.fail() except SystemExit: pass
def GenerateDiff(self, files, root): """Returns a string containing the diff for the given file list. The files in the list should either be absolute paths or relative to the given root. If no root directory is provided, the repository root will be used. """ previous_cwd = os.getcwd() if root is None: os.chdir(gcl.GetRepositoryRoot()) else: os.chdir(root) # Directories will return None so filter them out. diff = filter(None, [scm.SVN.DiffItem(f) for f in files]) os.chdir(previous_cwd) return "".join(diff)
def testNormal(self): change_info = gcl.ChangeInfo( 'naame', 0, 0, 'deescription', [('A', 'aa'), ('M', 'bb')], self.fake_root_dir, 'my_server', False) self.mox.StubOutWithMock(change_info, 'Save') change_info.Save() output = presubmit_support.PresubmitOutput() gcl.DoPresubmitChecks(change_info, False, True).AndReturn(output) gcl.tempfile.mkstemp(text=True).AndReturn((42, 'descfile')) gcl.os.write(42, change_info.description) gcl.os.close(42) gcl.GetCodeReviewSetting('CC_LIST') gcl.GetCodeReviewSetting('PRIVATE') gcl.GetCodeReviewSetting('PROJECT') gcl.os.getcwd().AndReturn('somewhere') gcl.os.chdir(change_info.GetLocalRoot()) gcl.GenerateDiff(change_info.GetFileNames()) gcl.upload.RealMain( ['upload.py', '-y', '--server=https://my_server', "--file=descfile" ], change_info.patch).AndReturn(("1", "2")) gcl.os.remove('descfile') change_info.SendToRietveld("/lint/issue%s_%s" % ('1', '2'), timeout=60) gcl.os.chdir('somewhere') gcl.sys.stdout.write("*** Upload does not submit a try; use gcl try to" " submit a try. ***") gcl.sys.stdout.write("\n") gcl.GetRepositoryRoot().AndReturn(self.fake_root_dir) gcl.ChangeInfo.Load('naame', self.fake_root_dir, True, True ).AndReturn(change_info) self.mox.ReplayAll() gcl.CMDupload(['naame', '--no_watchlists']) self.assertEquals(change_info.issue, 1) self.assertEquals(change_info.patchset, 2) self.checkstdout('*** Upload does not submit a try; use gcl try to submit ' 'a try. ***\n' '*** Upload does not submit a try; use gcl try to submit a try. ***\n')
def testReviewersInDescription(self): change_info = self.mox.CreateMock(gcl.ChangeInfo) change_info.name = 'naame' change_info.issue = 1 change_info.patchset = 0 change_info.description = 'deescription\n\[email protected]', change_info.files = [('A', 'aa'), ('M', 'bb')] change_info.patch = None change_info.rietveld = 'https://my_server' files = [item[1] for item in change_info.files] output = presubmit_support.PresubmitOutput() gcl.DoPresubmitChecks(change_info, False, True).AndReturn(output) #gcl.GetCodeReviewSetting('CODE_REVIEW_SERVER').AndReturn('my_server') gcl.os.getcwd().AndReturn('somewhere') change_info.GetFiles().AndReturn(change_info.files) change_info.get_reviewers().AndReturn(['*****@*****.**']) change_info.GetFileNames().AndReturn(files) change_info.GetLocalRoot().AndReturn('proout') gcl.os.chdir('proout') gcl.GenerateDiff(files) gcl.upload.RealMain([ 'upload.py', '-y', '--server=https://my_server', '[email protected]', '--issue=1', '--title= ' ], change_info.patch).AndReturn(("1", "2")) change_info.Save() change_info.PrimeLint() gcl.os.chdir('somewhere') gcl.sys.stdout.write("*** Upload does not submit a try; use gcl try to" " submit a try. ***") gcl.sys.stdout.write("\n") gcl.GetRepositoryRoot().AndReturn(self.fake_root_dir) gcl.ChangeInfo.Load('naame', self.fake_root_dir, True, True).AndReturn(change_info) self.mox.ReplayAll() gcl.CMDupload(['naame']) self.checkstdout( '*** Upload does not submit a try; use gcl try to submit ' 'a try. ***\n' '*** Upload does not submit a try; use gcl try to submit a try. ***\n' )
def testGetRepositoryRootGood(self): root_path = gcl.os.path.join('bleh', 'prout', 'pouet') gcl.os.getcwd().AndReturn(root_path) gcl.SVN.GetCheckoutRoot(root_path).AndReturn(root_path + '.~') self.mox.ReplayAll() self.assertEquals(gcl.GetRepositoryRoot(), root_path + '.~')
def GetSourceRoot(): """Returns the absolute directory one level up from the repository root.""" # TODO(maruel): This is odd to assume that '..' is the source root. return os.path.abspath(os.path.join(gcl.GetRepositoryRoot(), '..'))
def Revert(revisions, force=False, commit=True, send_email=True, message=None, reviewers=None): """Reverts many revisions in one change list. If force is True, it will override local modifications. If commit is True, a commit is done after the revert. If send_mail is True, a review email is sent. If message is True, it is used as the change description. reviewers overrides the blames email addresses for review email.""" # Use the oldest revision as the primary revision. changename = "revert%d" % revisions[len(revisions) - 1] if not force and os.path.exists(gcl.GetChangelistInfoFile(changename)): print "Error, change %s already exist." % changename return 1 # Move to the repository root and make the revision numbers sorted in # decreasing order. local_root = gcl.GetRepositoryRoot() os.chdir(local_root) revisions.sort(reverse=True) revisions_string = ",".join([str(rev) for rev in revisions]) revisions_string_rev = ",".join([str(-rev) for rev in revisions]) # Get all the modified files by the revision. We'll use this list to optimize # the svn merge. logs = [] for revision in revisions: logs.extend(CaptureSVNLog(["-r", str(revision), "-v"])) files = [] blames = [] repo_base = GetRepoBase() for log in logs: for file in log['paths']: file_name = file['path'] # Remove the /trunk/src/ part. The + 1 is for the last slash. if not file_name.startswith(repo_base): raise OutsideOfCheckout(file_name) files.append(file_name[len(repo_base):]) blames.append(log['author']) # On Windows, we need to fix the slashes once they got the url part removed. if sys.platform == 'win32': # On Windows, gcl expect the correct slashes. files = [file.replace('/', os.sep) for file in files] # Keep unique. files = UniqueFast(files) blames = UniqueFast(blames) if not reviewers: reviewers = blames else: reviewers = UniqueFast(reviewers) # Make sure there's something to revert. if not files: raise NoModifiedFile if not reviewers: raise NoBlameList if blames: print "Blaming %s\n" % ",".join(blames) if reviewers != blames: print "Emailing %s\n" % ",".join(reviewers) print "These files were modified in %s:" % revisions_string print "\n".join(files) print "" # Make sure these files are unmodified with svn status. status = gclient_scm.scm.SVN.CaptureStatus(files) if status: if force: # TODO(maruel): Use the tool to correctly revert '?' files. gcl.RunShell(["svn", "revert"] + files) else: raise ModifiedFile(status) # svn up on each of these files gcl.RunShell(["svn", "up"] + files) files_status = {} # Extract the first level subpaths. Subversion seems to degrade # exponentially w.r.t. repository size during merges. Working at the root # directory is too rough for svn due to the repository size. roots = UniqueFast([file.split(os.sep)[0] for file in files]) for root in roots: # Is it a subdirectory or a files? is_root_subdir = os.path.isdir(root) need_to_update = False if is_root_subdir: os.chdir(root) file_list = [] # List the file directly since it is faster when there is only one file. for file in files: if file.startswith(root): file_list.append(file[len(root) + 1:]) if len(file_list) > 1: # Listing multiple files is not supported by svn merge. file_list = ['.'] need_to_update = True else: # Oops, root was in fact a file in the root directory. file_list = [root] root = "." print "Reverting %s in %s/" % (revisions_string, root) if need_to_update: # Make sure '.' revision is high enough otherwise merge will be # unhappy. retcode = gcl.RunShellWithReturnCode(['svn', 'up', '.', '-N'])[1] if retcode: print 'svn up . -N failed in %s/.' % root return retcode command = ["svn", "merge", "-c", revisions_string_rev] command.extend(file_list) (output, retcode) = gcl.RunShellWithReturnCode(command, print_output=True) if retcode: print "'%s' failed:" % command return retcode # Grab the status lines = output.split('\n') for line in lines: if line.startswith('---'): continue if line.startswith('Skipped'): print "" raise ModifiedFile(line[9:-1]) # Update the status. status = line[:5] + ' ' file = line[5:] if is_root_subdir: files_status[root + os.sep + file] = status else: files_status[file] = status if is_root_subdir: os.chdir('..') # Transform files_status from a dictionary to a list of tuple. files_status = [(files_status[file], file) for file in files] description = "Reverting %s." % revisions_string if message: description += "\n\n" description += message # Don't use gcl.Change() since it prompts the user for infos. change_info = gcl.ChangeInfo(changename, 0, 0, description, files_status, local_root) change_info.Save() upload_args = ['--no_presubmit', '-r', ",".join(reviewers)] if send_email: upload_args.append('--send_mail') if commit: upload_args.append('--no_try') gcl.UploadCL(change_info, upload_args) retcode = 0 if commit: gcl.Commit(change_info, ['--no_presubmit', '--force']) # TODO(maruel): gclient sync (to leave the local checkout in an usable # state) retcode = gclient.Main(["gclient.py", "sync"]) return retcode