Commit 5aeb7dd5 authored by maruel@chromium.org's avatar maruel@chromium.org

Reapply 32057, 32058, 32059, 32062 and fixes problems introduced by these changes.

Noteworthy change is scm.SVN.GetFileProperty calls Capture instead of Run.

TEST=unit tests
BUG=none

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@32181 0039d316-1c4b-4281-b951-d872f2087c98
parent 26970fa9
...@@ -19,10 +19,10 @@ import upload ...@@ -19,10 +19,10 @@ import upload
import urllib2 import urllib2
# gcl now depends on gclient. # gcl now depends on gclient.
import gclient_scm from scm import SVN
import gclient_utils import gclient_utils
__version__ = '1.1.1' __version__ = '1.1.2'
CODEREVIEW_SETTINGS = { CODEREVIEW_SETTINGS = {
...@@ -46,43 +46,13 @@ MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" ...@@ -46,43 +46,13 @@ MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!"
FILES_CACHE = {} FILES_CACHE = {}
### SVN Functions
def IsSVNMoved(filename):
"""Determine if a file has been added through svn mv"""
info = gclient_scm.CaptureSVNInfo(filename)
return (info.get('Copied From URL') and
info.get('Copied From Rev') and
info.get('Schedule') == 'add')
def GetSVNFileProperty(file, property_name):
"""Returns the value of an SVN property for the given file.
Args:
file: The file to check
property_name: The name of the SVN property, e.g. "svn:mime-type"
Returns:
The value of the property, which will be the empty string if the property
is not set on the file. If the file is not under version control, the
empty string is also returned.
"""
output = RunShell(["svn", "propget", property_name, file])
if (output.startswith("svn: ") and
output.endswith("is not under version control")):
return ""
else:
return output
def UnknownFiles(extra_args): def UnknownFiles(extra_args):
"""Runs svn status and prints unknown files. """Runs svn status and returns unknown files.
Any args in |extra_args| are passed to the tool to support giving alternate Any args in |extra_args| are passed to the tool to support giving alternate
code locations. code locations.
""" """
return [item[1] for item in gclient_scm.CaptureSVNStatus(extra_args) return [item[1] for item in SVN.CaptureStatus(extra_args)
if item[0][0] == '?'] if item[0][0] == '?']
...@@ -93,7 +63,7 @@ def GetRepositoryRoot(): ...@@ -93,7 +63,7 @@ def GetRepositoryRoot():
""" """
global REPOSITORY_ROOT global REPOSITORY_ROOT
if not REPOSITORY_ROOT: if not REPOSITORY_ROOT:
infos = gclient_scm.CaptureSVNInfo(os.getcwd(), print_error=False) infos = SVN.CaptureInfo(os.getcwd(), print_error=False)
cur_dir_repo_root = infos.get("Repository Root") cur_dir_repo_root = infos.get("Repository Root")
if not cur_dir_repo_root: if not cur_dir_repo_root:
raise gclient_utils.Error("gcl run outside of repository") raise gclient_utils.Error("gcl run outside of repository")
...@@ -101,7 +71,7 @@ def GetRepositoryRoot(): ...@@ -101,7 +71,7 @@ def GetRepositoryRoot():
REPOSITORY_ROOT = os.getcwd() REPOSITORY_ROOT = os.getcwd()
while True: while True:
parent = os.path.dirname(REPOSITORY_ROOT) parent = os.path.dirname(REPOSITORY_ROOT)
if (gclient_scm.CaptureSVNInfo(parent, print_error=False).get( if (SVN.CaptureInfo(parent, print_error=False).get(
"Repository Root") != cur_dir_repo_root): "Repository Root") != cur_dir_repo_root):
break break
REPOSITORY_ROOT = parent REPOSITORY_ROOT = parent
...@@ -146,7 +116,7 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False): ...@@ -146,7 +116,7 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False):
os.stat(cached_file).st_mtime > max_age): os.stat(cached_file).st_mtime > max_age):
local_dir = os.path.dirname(os.path.abspath(filename)) local_dir = os.path.dirname(os.path.abspath(filename))
local_base = os.path.basename(filename) local_base = os.path.basename(filename)
dir_info = gclient_scm.CaptureSVNInfo(".") dir_info = SVN.CaptureInfo(".")
repo_root = dir_info["Repository Root"] repo_root = dir_info["Repository Root"]
if use_root: if use_root:
url_path = repo_root url_path = repo_root
...@@ -158,7 +128,7 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False): ...@@ -158,7 +128,7 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False):
r = "" r = ""
if not use_root: if not use_root:
local_path = os.path.join(local_dir, local_base) local_path = os.path.join(local_dir, local_base)
r = gclient_scm.CaptureSVNStatus((local_path,)) r = SVN.CaptureStatus((local_path,))
rc = -1 rc = -1
if r: if r:
status = r[0][0] status = r[0][0]
...@@ -478,7 +448,7 @@ class ChangeInfo(object): ...@@ -478,7 +448,7 @@ class ChangeInfo(object):
if update_status: if update_status:
for item in files: for item in files:
filename = os.path.join(local_root, item[1]) filename = os.path.join(local_root, item[1])
status_result = gclient_scm.CaptureSVNStatus(filename) status_result = SVN.CaptureStatus(filename)
if not status_result or not status_result[0][0]: if not status_result or not status_result[0][0]:
# File has been reverted. # File has been reverted.
save = True save = True
...@@ -562,7 +532,7 @@ def GetModifiedFiles(): ...@@ -562,7 +532,7 @@ def GetModifiedFiles():
files_in_cl[filename] = change_info.name files_in_cl[filename] = change_info.name
# Get all the modified files. # Get all the modified files.
status_result = gclient_scm.CaptureSVNStatus(None) status_result = SVN.CaptureStatus(None)
for line in status_result: for line in status_result:
status = line[0] status = line[0]
filename = line[1] filename = line[1]
...@@ -749,10 +719,10 @@ def GenerateDiff(files, root=None): ...@@ -749,10 +719,10 @@ def GenerateDiff(files, root=None):
diff = [] diff = []
for filename in files: for filename in files:
# TODO(maruel): Use SVN.DiffItem().
# Use svn info output instead of os.path.isdir because the latter fails # Use svn info output instead of os.path.isdir because the latter fails
# when the file is deleted. # when the file is deleted.
if gclient_scm.CaptureSVNInfo(filename).get("Node Kind") in ("dir", if SVN.CaptureInfo(filename).get('Node Kind') == 'directory':
"directory"):
continue continue
# If the user specified a custom diff command in their svn config file, # If the user specified a custom diff command in their svn config file,
# then it'll be used when we do svn diff, which we don't want to happen # then it'll be used when we do svn diff, which we don't want to happen
...@@ -770,7 +740,7 @@ def GenerateDiff(files, root=None): ...@@ -770,7 +740,7 @@ def GenerateDiff(files, root=None):
output = RunShell(["svn", "diff", "--config-dir", bogus_dir, filename]) output = RunShell(["svn", "diff", "--config-dir", bogus_dir, filename])
if output: if output:
diff.append(output) diff.append(output)
elif IsSVNMoved(filename): elif SVN.IsMoved(filename):
# svn diff on a mv/cp'd file outputs nothing. # svn diff on a mv/cp'd file outputs nothing.
# We put in an empty Index entry so upload.py knows about them. # We put in an empty Index entry so upload.py knows about them.
diff.append("\nIndex: %s\n" % filename) diff.append("\nIndex: %s\n" % filename)
...@@ -996,7 +966,7 @@ def Change(change_info, args): ...@@ -996,7 +966,7 @@ def Change(change_info, args):
silent = FilterFlag(args, "--silent") silent = FilterFlag(args, "--silent")
# Verify the user is running the change command from a read-write checkout. # Verify the user is running the change command from a read-write checkout.
svn_info = gclient_scm.CaptureSVNInfo('.') svn_info = SVN.CaptureInfo('.')
if not svn_info: if not svn_info:
ErrorExit("Current checkout is unversioned. Please retry with a versioned " ErrorExit("Current checkout is unversioned. Please retry with a versioned "
"directory.") "directory.")
...@@ -1008,7 +978,7 @@ def Change(change_info, args): ...@@ -1008,7 +978,7 @@ def Change(change_info, args):
f.close() f.close()
else: else:
override_description = None override_description = None
if change_info.issue: if change_info.issue:
try: try:
description = GetIssueDescription(change_info.issue) description = GetIssueDescription(change_info.issue)
......
...@@ -66,7 +66,7 @@ Hooks ...@@ -66,7 +66,7 @@ Hooks
""" """
__author__ = "darinf@gmail.com (Darin Fisher)" __author__ = "darinf@gmail.com (Darin Fisher)"
__version__ = "0.3.3" __version__ = "0.3.4"
import errno import errno
import logging import logging
...@@ -747,9 +747,9 @@ class GClient(object): ...@@ -747,9 +747,9 @@ class GClient(object):
# Use entry and not entry_fixed there. # Use entry and not entry_fixed there.
if entry not in entries and os.path.exists(e_dir): if entry not in entries and os.path.exists(e_dir):
modified_files = False modified_files = False
if isinstance(prev_entries,list): if isinstance(prev_entries, list):
# old .gclient_entries format was list, now dict # old .gclient_entries format was list, now dict
modified_files = gclient_scm.CaptureSVNStatus(e_dir) modified_files = gclient_scm.scm.SVN.CaptureStatus(e_dir)
else: else:
file_list = [] file_list = []
scm = gclient_scm.CreateSCM(prev_entries[entry], self._root_dir, scm = gclient_scm.CreateSCM(prev_entries[entry], self._root_dir,
...@@ -830,7 +830,7 @@ class GClient(object): ...@@ -830,7 +830,7 @@ class GClient(object):
(url, rev) = GetURLAndRev(name, solution["url"]) (url, rev) = GetURLAndRev(name, solution["url"])
entries[name] = "%s@%s" % (url, rev) entries[name] = "%s@%s" % (url, rev)
# TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset)
entries_deps_content[name] = gclient_scm.CaptureSVN( entries_deps_content[name] = gclient_scm.scm.SVN.Capture(
["cat", ["cat",
"%s/%s@%s" % (url, "%s/%s@%s" % (url,
self._options.deps_file, self._options.deps_file,
......
# Copyright 2009 Google Inc. All Rights Reserved. # Copyright (c) 2009 The Chromium Authors. All rights reserved.
# # Use of this source code is governed by a BSD-style license that can be
# Licensed under the Apache License, Version 2.0 (the "License"); # found in the LICENSE file.
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Gclient-specific SCM-specific operations.""" """Gclient-specific SCM-specific operations."""
...@@ -18,19 +8,13 @@ import logging ...@@ -18,19 +8,13 @@ import logging
import os import os
import re import re
import subprocess import subprocess
import sys
import xml.dom.minidom
import scm
import gclient_utils import gclient_utils
# TODO(maruel): Temporary.
from scm import CaptureGit, CaptureGitStatus, CaptureSVN
from scm import CaptureSVNHeadRevision, CaptureSVNInfo, CaptureSVNStatus
from scm import RunSVN, RunSVNAndFilterOutput, RunSVNAndGetFileList
### SCM abstraction layer ### SCM abstraction layer
# Factory Method for SCM wrapper creation # Factory Method for SCM wrapper creation
def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'): def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'):
...@@ -93,20 +77,20 @@ class SCMWrapper(object): ...@@ -93,20 +77,20 @@ class SCMWrapper(object):
return getattr(self, command)(options, args, file_list) return getattr(self, command)(options, args, file_list)
class GitWrapper(SCMWrapper): class GitWrapper(SCMWrapper, scm.GIT):
"""Wrapper for Git""" """Wrapper for Git"""
def cleanup(self, options, args, file_list): def cleanup(self, options, args, file_list):
"""Cleanup working copy.""" """Cleanup working copy."""
__pychecker__ = 'unusednames=args,file_list,options' __pychecker__ = 'unusednames=args,file_list,options'
self._RunGit(['prune'], redirect_stdout=False) self._Run(['prune'], redirect_stdout=False)
self._RunGit(['fsck'], redirect_stdout=False) self._Run(['fsck'], redirect_stdout=False)
self._RunGit(['gc'], redirect_stdout=False) self._Run(['gc'], redirect_stdout=False)
def diff(self, options, args, file_list): def diff(self, options, args, file_list):
__pychecker__ = 'unusednames=args,file_list,options' __pychecker__ = 'unusednames=args,file_list,options'
merge_base = self._RunGit(['merge-base', 'HEAD', 'origin']) merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
self._RunGit(['diff', merge_base], redirect_stdout=False) self._Run(['diff', merge_base], redirect_stdout=False)
def export(self, options, args, file_list): def export(self, options, args, file_list):
__pychecker__ = 'unusednames=file_list,options' __pychecker__ = 'unusednames=file_list,options'
...@@ -114,8 +98,8 @@ class GitWrapper(SCMWrapper): ...@@ -114,8 +98,8 @@ class GitWrapper(SCMWrapper):
export_path = os.path.abspath(os.path.join(args[0], self.relpath)) export_path = os.path.abspath(os.path.join(args[0], self.relpath))
if not os.path.exists(export_path): if not os.path.exists(export_path):
os.makedirs(export_path) os.makedirs(export_path)
self._RunGit(['checkout-index', '-a', '--prefix=%s/' % export_path], self._Run(['checkout-index', '-a', '--prefix=%s/' % export_path],
redirect_stdout=False) redirect_stdout=False)
def update(self, options, args, file_list): def update(self, options, args, file_list):
"""Runs git to update or transparently checkout the working copy. """Runs git to update or transparently checkout the working copy.
...@@ -142,21 +126,21 @@ class GitWrapper(SCMWrapper): ...@@ -142,21 +126,21 @@ class GitWrapper(SCMWrapper):
print("\n_____ %s%s" % (self.relpath, rev_str)) print("\n_____ %s%s" % (self.relpath, rev_str))
if not os.path.exists(self.checkout_path): if not os.path.exists(self.checkout_path):
self._RunGit(['clone', url, self.checkout_path], self._Run(['clone', url, self.checkout_path],
cwd=self._root_dir, redirect_stdout=False) cwd=self._root_dir, redirect_stdout=False)
if revision: if revision:
self._RunGit(['reset', '--hard', revision], redirect_stdout=False) self._Run(['reset', '--hard', revision], redirect_stdout=False)
files = self._RunGit(['ls-files']).split() files = self._Run(['ls-files']).split()
file_list.extend([os.path.join(self.checkout_path, f) for f in files]) file_list.extend([os.path.join(self.checkout_path, f) for f in files])
return return
self._RunGit(['remote', 'update'], redirect_stdout=False) self._Run(['remote', 'update'], redirect_stdout=False)
new_base = 'origin' new_base = 'origin'
if revision: if revision:
new_base = revision new_base = revision
files = self._RunGit(['diff', new_base, '--name-only']).split() files = self._Run(['diff', new_base, '--name-only']).split()
file_list.extend([os.path.join(self.checkout_path, f) for f in files]) file_list.extend([os.path.join(self.checkout_path, f) for f in files])
self._RunGit(['rebase', '-v', new_base], redirect_stdout=False) self._Run(['rebase', '-v', new_base], redirect_stdout=False)
print "Checked out revision %s." % self.revinfo(options, (), None) print "Checked out revision %s." % self.revinfo(options, (), None)
def revert(self, options, args, file_list): def revert(self, options, args, file_list):
...@@ -172,15 +156,15 @@ class GitWrapper(SCMWrapper): ...@@ -172,15 +156,15 @@ class GitWrapper(SCMWrapper):
print("\n_____ %s is missing, synching instead" % self.relpath) print("\n_____ %s is missing, synching instead" % self.relpath)
# Don't reuse the args. # Don't reuse the args.
return self.update(options, [], file_list) return self.update(options, [], file_list)
merge_base = self._RunGit(['merge-base', 'HEAD', 'origin']) merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
files = self._RunGit(['diff', merge_base, '--name-only']).split() files = self._Run(['diff', merge_base, '--name-only']).split()
self._RunGit(['reset', '--hard', merge_base], redirect_stdout=False) self._Run(['reset', '--hard', merge_base], redirect_stdout=False)
file_list.extend([os.path.join(self.checkout_path, f) for f in files]) file_list.extend([os.path.join(self.checkout_path, f) for f in files])
def revinfo(self, options, args, file_list): def revinfo(self, options, args, file_list):
"""Display revision""" """Display revision"""
__pychecker__ = 'unusednames=args,file_list,options' __pychecker__ = 'unusednames=args,file_list,options'
return self._RunGit(['rev-parse', 'HEAD']) return self._Run(['rev-parse', 'HEAD'])
def runhooks(self, options, args, file_list): def runhooks(self, options, args, file_list):
self.status(options, args, file_list) self.status(options, args, file_list)
...@@ -192,29 +176,30 @@ class GitWrapper(SCMWrapper): ...@@ -192,29 +176,30 @@ class GitWrapper(SCMWrapper):
print('\n________ couldn\'t run status in %s:\nThe directory ' print('\n________ couldn\'t run status in %s:\nThe directory '
'does not exist.' % self.checkout_path) 'does not exist.' % self.checkout_path)
else: else:
merge_base = self._RunGit(['merge-base', 'HEAD', 'origin']) merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
self._RunGit(['diff', '--name-status', merge_base], redirect_stdout=False) self._Run(['diff', '--name-status', merge_base], redirect_stdout=False)
files = self._RunGit(['diff', '--name-only', merge_base]).split() files = self._Run(['diff', '--name-only', merge_base]).split()
file_list.extend([os.path.join(self.checkout_path, f) for f in files]) file_list.extend([os.path.join(self.checkout_path, f) for f in files])
def _RunGit(self, args, cwd=None, checkrc=True, redirect_stdout=True): def _Run(self, args, cwd=None, checkrc=True, redirect_stdout=True):
# TODO(maruel): Merge with Capture?
stdout=None stdout=None
if redirect_stdout: if redirect_stdout:
stdout=subprocess.PIPE stdout=subprocess.PIPE
if cwd == None: if cwd == None:
cwd = self.checkout_path cwd = self.checkout_path
cmd = ['git'] cmd = [self.COMMAND]
cmd.extend(args) cmd.extend(args)
sp = subprocess.Popen(cmd, cwd=cwd, stdout=stdout) sp = subprocess.Popen(cmd, cwd=cwd, stdout=stdout)
if checkrc and sp.returncode: if checkrc and sp.returncode:
raise gclient_utils.Error('git command %s returned %d' % raise gclient_utils.Error('git command %s returned %d' %
(args[0], sp.returncode)) (args[0], sp.returncode))
output = sp.communicate()[0] output = sp.communicate()[0]
if output != None: if output is not None:
return output.strip() return output.strip()
class SVNWrapper(SCMWrapper): class SVNWrapper(SCMWrapper, scm.SVN):
""" Wrapper for SVN """ """ Wrapper for SVN """
def cleanup(self, options, args, file_list): def cleanup(self, options, args, file_list):
...@@ -222,14 +207,14 @@ class SVNWrapper(SCMWrapper): ...@@ -222,14 +207,14 @@ class SVNWrapper(SCMWrapper):
__pychecker__ = 'unusednames=file_list,options' __pychecker__ = 'unusednames=file_list,options'
command = ['cleanup'] command = ['cleanup']
command.extend(args) command.extend(args)
RunSVN(command, os.path.join(self._root_dir, self.relpath)) self.Run(command, os.path.join(self._root_dir, self.relpath))
def diff(self, options, args, file_list): def diff(self, options, args, file_list):
# NOTE: This function does not currently modify file_list. # NOTE: This function does not currently modify file_list.
__pychecker__ = 'unusednames=file_list,options' __pychecker__ = 'unusednames=file_list,options'
command = ['diff'] command = ['diff']
command.extend(args) command.extend(args)
RunSVN(command, os.path.join(self._root_dir, self.relpath)) self.Run(command, os.path.join(self._root_dir, self.relpath))
def export(self, options, args, file_list): def export(self, options, args, file_list):
__pychecker__ = 'unusednames=file_list,options' __pychecker__ = 'unusednames=file_list,options'
...@@ -242,7 +227,7 @@ class SVNWrapper(SCMWrapper): ...@@ -242,7 +227,7 @@ class SVNWrapper(SCMWrapper):
assert os.path.exists(export_path) assert os.path.exists(export_path)
command = ['export', '--force', '.'] command = ['export', '--force', '.']
command.append(export_path) command.append(export_path)
RunSVN(command, os.path.join(self._root_dir, self.relpath)) self.Run(command, os.path.join(self._root_dir, self.relpath))
def update(self, options, args, file_list): def update(self, options, args, file_list):
"""Runs SCM to update or transparently checkout the working copy. """Runs SCM to update or transparently checkout the working copy.
...@@ -279,11 +264,11 @@ class SVNWrapper(SCMWrapper): ...@@ -279,11 +264,11 @@ class SVNWrapper(SCMWrapper):
command = ['checkout', url, checkout_path] command = ['checkout', url, checkout_path]
if revision: if revision:
command.extend(['--revision', str(revision)]) command.extend(['--revision', str(revision)])
RunSVNAndGetFileList(options, command, self._root_dir, file_list) self.RunAndGetFileList(options, command, self._root_dir, file_list)
return return
# Get the existing scm url and the revision number of the current checkout. # Get the existing scm url and the revision number of the current checkout.
from_info = CaptureSVNInfo(os.path.join(checkout_path, '.'), '.') from_info = self.CaptureInfo(os.path.join(checkout_path, '.'), '.')
if not from_info: if not from_info:
raise gclient_utils.Error("Can't update/checkout %r if an unversioned " raise gclient_utils.Error("Can't update/checkout %r if an unversioned "
"directory is present. Delete the directory " "directory is present. Delete the directory "
...@@ -293,12 +278,12 @@ class SVNWrapper(SCMWrapper): ...@@ -293,12 +278,12 @@ class SVNWrapper(SCMWrapper):
if options.manually_grab_svn_rev: if options.manually_grab_svn_rev:
# Retrieve the current HEAD version because svn is slow at null updates. # Retrieve the current HEAD version because svn is slow at null updates.
if not revision: if not revision:
from_info_live = CaptureSVNInfo(from_info['URL'], '.') from_info_live = self.CaptureInfo(from_info['URL'], '.')
revision = str(from_info_live['Revision']) revision = str(from_info_live['Revision'])
rev_str = ' at %s' % revision rev_str = ' at %s' % revision
if from_info['URL'] != base_url: if from_info['URL'] != base_url:
to_info = CaptureSVNInfo(url, '.') to_info = self.CaptureInfo(url, '.')
if not to_info.get('Repository Root') or not to_info.get('UUID'): if not to_info.get('Repository Root') or not to_info.get('UUID'):
# The url is invalid or the server is not accessible, it's safer to bail # The url is invalid or the server is not accessible, it's safer to bail
# out right now. # out right now.
...@@ -320,12 +305,12 @@ class SVNWrapper(SCMWrapper): ...@@ -320,12 +305,12 @@ class SVNWrapper(SCMWrapper):
from_info['Repository Root'], from_info['Repository Root'],
to_info['Repository Root'], to_info['Repository Root'],
self.relpath] self.relpath]
RunSVN(command, self._root_dir) self.Run(command, self._root_dir)
from_info['URL'] = from_info['URL'].replace( from_info['URL'] = from_info['URL'].replace(
from_info['Repository Root'], from_info['Repository Root'],
to_info['Repository Root']) to_info['Repository Root'])
else: else:
if CaptureSVNStatus(checkout_path): if self.CaptureStatus(checkout_path):
raise gclient_utils.Error("Can't switch the checkout to %s; UUID " raise gclient_utils.Error("Can't switch the checkout to %s; UUID "
"don't match and there is local changes " "don't match and there is local changes "
"in %s. Delete the directory and " "in %s. Delete the directory and "
...@@ -337,7 +322,7 @@ class SVNWrapper(SCMWrapper): ...@@ -337,7 +322,7 @@ class SVNWrapper(SCMWrapper):
command = ['checkout', url, checkout_path] command = ['checkout', url, checkout_path]
if revision: if revision:
command.extend(['--revision', str(revision)]) command.extend(['--revision', str(revision)])
RunSVNAndGetFileList(options, command, self._root_dir, file_list) self.RunAndGetFileList(options, command, self._root_dir, file_list)
return return
...@@ -351,7 +336,7 @@ class SVNWrapper(SCMWrapper): ...@@ -351,7 +336,7 @@ class SVNWrapper(SCMWrapper):
command = ["update", checkout_path] command = ["update", checkout_path]
if revision: if revision:
command.extend(['--revision', str(revision)]) command.extend(['--revision', str(revision)])
RunSVNAndGetFileList(options, command, self._root_dir, file_list) self.RunAndGetFileList(options, command, self._root_dir, file_list)
def revert(self, options, args, file_list): def revert(self, options, args, file_list):
"""Reverts local modifications. Subversion specific. """Reverts local modifications. Subversion specific.
...@@ -368,7 +353,7 @@ class SVNWrapper(SCMWrapper): ...@@ -368,7 +353,7 @@ class SVNWrapper(SCMWrapper):
# Don't reuse the args. # Don't reuse the args.
return self.update(options, [], file_list) return self.update(options, [], file_list)
for file_status in CaptureSVNStatus(path): for file_status in self.CaptureStatus(path):
file_path = os.path.join(path, file_status[1]) file_path = os.path.join(path, file_status[1])
if file_status[0][0] == 'X': if file_status[0][0] == 'X':
# Ignore externals. # Ignore externals.
...@@ -403,7 +388,7 @@ class SVNWrapper(SCMWrapper): ...@@ -403,7 +388,7 @@ class SVNWrapper(SCMWrapper):
try: try:
# svn revert is so broken we don't even use it. Using # svn revert is so broken we don't even use it. Using
# "svn up --revision BASE" achieve the same effect. # "svn up --revision BASE" achieve the same effect.
RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'], path, self.RunAndGetFileList(options, ['update', '--revision', 'BASE'], path,
file_list) file_list)
except OSError, e: except OSError, e:
# Maybe the directory disapeared meanwhile. We don't want it to throw an # Maybe the directory disapeared meanwhile. We don't want it to throw an
...@@ -413,7 +398,7 @@ class SVNWrapper(SCMWrapper): ...@@ -413,7 +398,7 @@ class SVNWrapper(SCMWrapper):
def revinfo(self, options, args, file_list): def revinfo(self, options, args, file_list):
"""Display revision""" """Display revision"""
__pychecker__ = 'unusednames=args,file_list,options' __pychecker__ = 'unusednames=args,file_list,options'
return CaptureSVNHeadRevision(self.url) return self.CaptureHeadRevision(self.url)
def runhooks(self, options, args, file_list): def runhooks(self, options, args, file_list):
self.status(options, args, file_list) self.status(options, args, file_list)
...@@ -430,7 +415,7 @@ class SVNWrapper(SCMWrapper): ...@@ -430,7 +415,7 @@ class SVNWrapper(SCMWrapper):
% (' '.join(command), path)) % (' '.join(command), path))
# There's no file list to retrieve. # There's no file list to retrieve.
else: else:
RunSVNAndGetFileList(options, command, path, file_list) self.RunAndGetFileList(options, command, path, file_list)
def pack(self, options, args, file_list): def pack(self, options, args, file_list):
"""Generates a patch file which can be applied to the root of the """Generates a patch file which can be applied to the root of the
...@@ -475,4 +460,4 @@ class SVNWrapper(SCMWrapper): ...@@ -475,4 +460,4 @@ class SVNWrapper(SCMWrapper):
print line print line
filterer = DiffFilterer(self.relpath) filterer = DiffFilterer(self.relpath)
RunSVNAndFilterOutput(command, path, False, False, filterer.Filter) self.RunAndFilterOutput(command, path, False, False, filterer.Filter)
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
"""Generic utils."""
import errno import errno
import os import os
import re import re
...@@ -22,8 +24,6 @@ import time ...@@ -22,8 +24,6 @@ import time
import xml.dom.minidom import xml.dom.minidom
import xml.parsers.expat import xml.parsers.expat
## Generic utils
def SplitUrlRevision(url): def SplitUrlRevision(url):
"""Splits url and returns a two-tuple: url, rev""" """Splits url and returns a two-tuple: url, rev"""
...@@ -76,9 +76,9 @@ class PrintableObject(object): ...@@ -76,9 +76,9 @@ class PrintableObject(object):
return output return output
def FileRead(filename): def FileRead(filename, mode='rU'):
content = None content = None
f = open(filename, "rU") f = open(filename, mode)
try: try:
content = f.read() content = f.read()
finally: finally:
...@@ -86,8 +86,8 @@ def FileRead(filename): ...@@ -86,8 +86,8 @@ def FileRead(filename):
return content return content
def FileWrite(filename, content): def FileWrite(filename, content, mode='w'):
f = open(filename, "w") f = open(filename, mode)
try: try:
f.write(content) f.write(content)
finally: finally:
...@@ -201,9 +201,9 @@ def SubprocessCallAndFilter(command, ...@@ -201,9 +201,9 @@ def SubprocessCallAndFilter(command,
only if we actually need to print something else as well, so you can only if we actually need to print something else as well, so you can
get the context of the output. If print_messages is false and print_stdout get the context of the output. If print_messages is false and print_stdout
is false, no output at all is generated. is false, no output at all is generated.
Also, if print_stdout is true, the command's stdout is also forwarded Also, if print_stdout is true, the command's stdout is also forwarded
to stdout. to stdout.
If a filter function is specified, it is expected to take a single If a filter function is specified, it is expected to take a single
string argument, and it will be called with each line of the string argument, and it will be called with each line of the
...@@ -223,7 +223,7 @@ def SubprocessCallAndFilter(command, ...@@ -223,7 +223,7 @@ def SubprocessCallAndFilter(command,
# executable, but shell=True makes subprocess on Linux fail when it's called # executable, but shell=True makes subprocess on Linux fail when it's called
# with a list because it only tries to execute the first item in the list. # with a list because it only tries to execute the first item in the list.
kid = subprocess.Popen(command, bufsize=0, cwd=in_directory, kid = subprocess.Popen(command, bufsize=0, cwd=in_directory,
shell=(sys.platform == 'win32'), stdout=subprocess.PIPE, shell=(sys.platform == 'win32'), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
# Also, we need to forward stdout to prevent weird re-ordering of output. # Also, we need to forward stdout to prevent weird re-ordering of output.
...@@ -238,7 +238,7 @@ def SubprocessCallAndFilter(command, ...@@ -238,7 +238,7 @@ def SubprocessCallAndFilter(command,
if not print_messages: if not print_messages:
print("\n________ running \'%s\' in \'%s\'" print("\n________ running \'%s\' in \'%s\'"
% (' '.join(command), in_directory)) % (' '.join(command), in_directory))
print_messages = True print_messages = True
sys.stdout.write(in_byte) sys.stdout.write(in_byte)
if in_byte != "\n": if in_byte != "\n":
in_line += in_byte in_line += in_byte
......
...@@ -7,9 +7,8 @@ import re ...@@ -7,9 +7,8 @@ import re
import subprocess import subprocess
import sys import sys
# Imported from depot_tools.
import gclient_scm
import presubmit_support import presubmit_support
import scm
def Backquote(cmd, cwd=None): def Backquote(cmd, cwd=None):
"""Like running `cmd` in a shell script.""" """Like running `cmd` in a shell script."""
...@@ -35,7 +34,7 @@ class ChangeOptions: ...@@ -35,7 +34,7 @@ class ChangeOptions:
raise Exception("Could not parse log message: %s" % log) raise Exception("Could not parse log message: %s" % log)
name = m.group(1) name = m.group(1)
description = m.group(2) description = m.group(2)
files = gclient_scm.CaptureGitStatus([root], upstream_branch) files = scm.GIT.CaptureStatus([root], upstream_branch)
issue = Backquote(['git', 'cl', 'status', '--field=id']) issue = Backquote(['git', 'cl', 'status', '--field=id'])
patchset = None patchset = None
self.change = presubmit_support.GitChange(name, description, root, files, self.change = presubmit_support.GitChange(name, description, root, files,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"""Enables directory-specific presubmit checks to run at upload and/or commit. """Enables directory-specific presubmit checks to run at upload and/or commit.
""" """
__version__ = '1.3.3' __version__ = '1.3.4'
# TODO(joi) Add caching where appropriate/needed. The API is designed to allow # TODO(joi) Add caching where appropriate/needed. The API is designed to allow
# caching (between all different invocations of presubmit scripts for a given # caching (between all different invocations of presubmit scripts for a given
...@@ -35,11 +35,10 @@ import urllib2 # Exposed through the API. ...@@ -35,11 +35,10 @@ import urllib2 # Exposed through the API.
import warnings import warnings
# Local imports. # Local imports.
# TODO(joi) Would be cleaner to factor out utils in gcl to separate module, but
# for now it would only be a couple of functions so hardly worth it.
import gcl import gcl
import gclient_scm import gclient_utils
import presubmit_canned_checks import presubmit_canned_checks
import scm
# Ask for feedback only once in program lifetime. # Ask for feedback only once in program lifetime.
...@@ -241,7 +240,7 @@ class InputApi(object): ...@@ -241,7 +240,7 @@ class InputApi(object):
Remember to check for the None case and show an appropriate error! Remember to check for the None case and show an appropriate error!
""" """
local_path = gclient_scm.CaptureSVNInfo(depot_path).get('Path') local_path = scm.SVN.CaptureInfo(depot_path).get('Path')
if local_path: if local_path:
return local_path return local_path
...@@ -254,7 +253,7 @@ class InputApi(object): ...@@ -254,7 +253,7 @@ class InputApi(object):
Returns: Returns:
The depot path (SVN URL) of the file if mapped, otherwise None. The depot path (SVN URL) of the file if mapped, otherwise None.
""" """
depot_path = gclient_scm.CaptureSVNInfo(local_path).get('URL') depot_path = scm.SVN.CaptureInfo(local_path).get('URL')
if depot_path: if depot_path:
return depot_path return depot_path
...@@ -354,7 +353,7 @@ class InputApi(object): ...@@ -354,7 +353,7 @@ class InputApi(object):
file_item = file_item.AbsoluteLocalPath() file_item = file_item.AbsoluteLocalPath()
if not file_item.startswith(self.change.RepositoryRoot()): if not file_item.startswith(self.change.RepositoryRoot()):
raise IOError('Access outside the repository root is denied.') raise IOError('Access outside the repository root is denied.')
return gcl.ReadFile(file_item, mode) return gclient_utils.FileRead(file_item, mode)
@staticmethod @staticmethod
def _RightHandSideLinesImpl(affected_files): def _RightHandSideLinesImpl(affected_files):
...@@ -432,7 +431,8 @@ class AffectedFile(object): ...@@ -432,7 +431,8 @@ class AffectedFile(object):
if self.IsDirectory(): if self.IsDirectory():
return [] return []
else: else:
return gcl.ReadFile(self.AbsoluteLocalPath()).splitlines() return gclient_utils.FileRead(self.AbsoluteLocalPath(),
'rU').splitlines()
def OldContents(self): def OldContents(self):
"""Returns an iterator over the lines in the old version of file. """Returns an iterator over the lines in the old version of file.
...@@ -464,7 +464,7 @@ class SvnAffectedFile(AffectedFile): ...@@ -464,7 +464,7 @@ class SvnAffectedFile(AffectedFile):
def ServerPath(self): def ServerPath(self):
if self._server_path is None: if self._server_path is None:
self._server_path = gclient_scm.CaptureSVNInfo( self._server_path = scm.SVN.CaptureInfo(
self.AbsoluteLocalPath()).get('URL', '') self.AbsoluteLocalPath()).get('URL', '')
return self._server_path return self._server_path
...@@ -476,13 +476,13 @@ class SvnAffectedFile(AffectedFile): ...@@ -476,13 +476,13 @@ class SvnAffectedFile(AffectedFile):
# querying subversion, especially on Windows. # querying subversion, especially on Windows.
self._is_directory = os.path.isdir(path) self._is_directory = os.path.isdir(path)
else: else:
self._is_directory = gclient_scm.CaptureSVNInfo( self._is_directory = scm.SVN.CaptureInfo(
path).get('Node Kind') in ('dir', 'directory') path).get('Node Kind') in ('dir', 'directory')
return self._is_directory return self._is_directory
def Property(self, property_name): def Property(self, property_name):
if not property_name in self._properties: if not property_name in self._properties:
self._properties[property_name] = gcl.GetSVNFileProperty( self._properties[property_name] = scm.SVN.GetFileProperty(
self.AbsoluteLocalPath(), property_name).rstrip() self.AbsoluteLocalPath(), property_name).rstrip()
return self._properties[property_name] return self._properties[property_name]
...@@ -494,8 +494,8 @@ class SvnAffectedFile(AffectedFile): ...@@ -494,8 +494,8 @@ class SvnAffectedFile(AffectedFile):
elif self.IsDirectory(): elif self.IsDirectory():
self._is_text_file = False self._is_text_file = False
else: else:
mime_type = gcl.GetSVNFileProperty(self.AbsoluteLocalPath(), mime_type = scm.SVN.GetFileProperty(self.AbsoluteLocalPath(),
'svn:mime-type') 'svn:mime-type')
self._is_text_file = (not mime_type or mime_type.startswith('text/')) self._is_text_file = (not mime_type or mime_type.startswith('text/'))
return self._is_text_file return self._is_text_file
...@@ -809,7 +809,7 @@ def DoGetTrySlaves(changed_files, ...@@ -809,7 +809,7 @@ def DoGetTrySlaves(changed_files,
if verbose: if verbose:
output_stream.write("Running %s\n" % filename) output_stream.write("Running %s\n" % filename)
# Accept CRLF presubmit script. # Accept CRLF presubmit script.
presubmit_script = gcl.ReadFile(filename, 'rU') presubmit_script = gclient_utils.FileRead(filename, 'rU')
results += executer.ExecPresubmitScript(presubmit_script) results += executer.ExecPresubmitScript(presubmit_script)
slaves = list(set(results)) slaves = list(set(results))
...@@ -925,7 +925,7 @@ def DoPresubmitChecks(change, ...@@ -925,7 +925,7 @@ def DoPresubmitChecks(change,
if verbose: if verbose:
output_stream.write("Running %s\n" % filename) output_stream.write("Running %s\n" % filename)
# Accept CRLF presubmit script. # Accept CRLF presubmit script.
presubmit_script = gcl.ReadFile(filename, 'rU') presubmit_script = gclient_utils.FileRead(filename, 'rU')
results += executer.ExecPresubmitScript(presubmit_script, filename) results += executer.ExecPresubmitScript(presubmit_script, filename)
errors = [] errors = []
...@@ -1022,7 +1022,7 @@ def Main(argv): ...@@ -1022,7 +1022,7 @@ def Main(argv):
options.files = ParseFiles(args, options.recursive) options.files = ParseFiles(args, options.recursive)
else: else:
# Grab modified files. # Grab modified files.
options.files = gclient_scm.CaptureGitStatus([options.root]) options.files = scm.GIT.CaptureStatus([options.root])
elif os.path.isdir(os.path.join(options.root, '.svn')): elif os.path.isdir(os.path.join(options.root, '.svn')):
change_class = SvnChange change_class = SvnChange
if not options.files: if not options.files:
...@@ -1030,7 +1030,7 @@ def Main(argv): ...@@ -1030,7 +1030,7 @@ def Main(argv):
options.files = ParseFiles(args, options.recursive) options.files = ParseFiles(args, options.recursive)
else: else:
# Grab modified files. # Grab modified files.
options.files = gclient_scm.CaptureSVNStatus([options.root]) options.files = scm.SVN.CaptureStatus([options.root])
else: else:
# Doesn't seem under source control. # Doesn't seem under source control.
change_class = Change change_class = Change
......
...@@ -146,7 +146,7 @@ def Revert(revisions, force=False, commit=True, send_email=True, message=None, ...@@ -146,7 +146,7 @@ def Revert(revisions, force=False, commit=True, send_email=True, message=None,
print "" print ""
# Make sure these files are unmodified with svn status. # Make sure these files are unmodified with svn status.
status = gclient_scm.CaptureSVNStatus(files) status = gclient_scm.scm.SVN.CaptureStatus(files)
if status: if status:
if force: if force:
# TODO(maruel): Use the tool to correctly revert '?' files. # TODO(maruel): Use the tool to correctly revert '?' files.
......
...@@ -2,335 +2,414 @@ ...@@ -2,335 +2,414 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
"""SCM-specific functions.""" """SCM-specific utility classes."""
import os import os
import re import re
import subprocess import subprocess
import sys import sys
import tempfile
import xml.dom.minidom import xml.dom.minidom
import gclient_utils import gclient_utils
SVN_COMMAND = "svn" class GIT(object):
GIT_COMMAND = "git" COMMAND = "git"
# ----------------------------------------------------------------------------- @staticmethod
# Git utils: def Capture(args, in_directory=None, print_error=True):
"""Runs git, capturing output sent to stdout as a string.
def CaptureGit(args, in_directory=None, print_error=True): Args:
"""Runs git, capturing output sent to stdout as a string. args: A sequence of command line parameters to be passed to git.
in_directory: The directory where git is to be run.
Args:
args: A sequence of command line parameters to be passed to git. Returns:
in_directory: The directory where git is to be run. The output sent to stdout as a string.
"""
Returns: c = [GIT.COMMAND]
The output sent to stdout as a string. c.extend(args)
"""
c = [GIT_COMMAND] # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
c.extend(args) # the git.exe executable, but shell=True makes subprocess on Linux fail
# when it's called with a list because it only tries to execute the
# *Sigh*: Windows needs shell=True, or else it won't search %PATH% for # first string ("git").
# the git.exe executable, but shell=True makes subprocess on Linux fail stderr = None
# when it's called with a list because it only tries to execute the if not print_error:
# first string ("git"). stderr = subprocess.PIPE
stderr = None return subprocess.Popen(c,
if not print_error: cwd=in_directory,
stderr = subprocess.PIPE shell=sys.platform.startswith('win'),
return subprocess.Popen(c, stdout=subprocess.PIPE,
cwd=in_directory, stderr=stderr).communicate()[0]
shell=sys.platform.startswith('win'),
stdout=subprocess.PIPE,
stderr=stderr).communicate()[0] @staticmethod
def CaptureStatus(files, upstream_branch='origin'):
"""Returns git status.
def CaptureGitStatus(files, upstream_branch='origin'):
"""Returns git status. @files can be a string (one file) or a list of files.
@files can be a string (one file) or a list of files. Returns an array of (status, file) tuples."""
command = ["diff", "--name-status", "-r", "%s.." % upstream_branch]
Returns an array of (status, file) tuples.""" if not files:
command = ["diff", "--name-status", "-r", "%s.." % upstream_branch] pass
if not files: elif isinstance(files, basestring):
pass command.append(files)
elif isinstance(files, basestring): else:
command.append(files) command.extend(files)
else:
command.extend(files) status = GIT.Capture(command).rstrip()
results = []
status = CaptureGit(command).rstrip() if status:
results = [] for statusline in status.split('\n'):
if status: m = re.match('^(\w)\t(.+)$', statusline)
for statusline in status.split('\n'): if not m:
m = re.match('^(\w)\t(.+)$', statusline) raise Exception("status currently unsupported: %s" % statusline)
if not m: results.append(('%s ' % m.group(1), m.group(2)))
raise Exception("status currently unsupported: %s" % statusline) return results
results.append(('%s ' % m.group(1), m.group(2)))
return results
class SVN(object):
COMMAND = "svn"
# -----------------------------------------------------------------------------
# SVN utils: @staticmethod
def Run(args, in_directory):
"""Runs svn, sending output to stdout.
def RunSVN(args, in_directory):
"""Runs svn, sending output to stdout. Args:
args: A sequence of command line parameters to be passed to svn.
Args: in_directory: The directory where svn is to be run.
args: A sequence of command line parameters to be passed to svn.
in_directory: The directory where svn is to be run. Raises:
Error: An error occurred while running the svn command.
Raises: """
Error: An error occurred while running the svn command. c = [SVN.COMMAND]
""" c.extend(args)
c = [SVN_COMMAND]
c.extend(args) gclient_utils.SubprocessCall(c, in_directory)
gclient_utils.SubprocessCall(c, in_directory) @staticmethod
def Capture(args, in_directory=None, print_error=True):
"""Runs svn, capturing output sent to stdout as a string.
def CaptureSVN(args, in_directory=None, print_error=True):
"""Runs svn, capturing output sent to stdout as a string. Args:
args: A sequence of command line parameters to be passed to svn.
Args: in_directory: The directory where svn is to be run.
args: A sequence of command line parameters to be passed to svn.
in_directory: The directory where svn is to be run. Returns:
The output sent to stdout as a string.
Returns: """
The output sent to stdout as a string. c = [SVN.COMMAND]
""" c.extend(args)
c = [SVN_COMMAND]
c.extend(args) # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
# the svn.exe executable, but shell=True makes subprocess on Linux fail
# *Sigh*: Windows needs shell=True, or else it won't search %PATH% for # when it's called with a list because it only tries to execute the
# the svn.exe executable, but shell=True makes subprocess on Linux fail # first string ("svn").
# when it's called with a list because it only tries to execute the stderr = None
# first string ("svn"). if not print_error:
stderr = None stderr = subprocess.PIPE
if not print_error: return subprocess.Popen(c,
stderr = subprocess.PIPE cwd=in_directory,
return subprocess.Popen(c, shell=(sys.platform == 'win32'),
cwd=in_directory, stdout=subprocess.PIPE,
shell=(sys.platform == 'win32'), stderr=stderr).communicate()[0]
stdout=subprocess.PIPE,
stderr=stderr).communicate()[0] @staticmethod
def RunAndGetFileList(options, args, in_directory, file_list):
"""Runs svn checkout, update, or status, output to stdout.
def RunSVNAndGetFileList(options, args, in_directory, file_list):
"""Runs svn checkout, update, or status, output to stdout. The first item in args must be either "checkout", "update", or "status".
The first item in args must be either "checkout", "update", or "status". svn's stdout is parsed to collect a list of files checked out or updated.
These files are appended to file_list. svn's stdout is also printed to
svn's stdout is parsed to collect a list of files checked out or updated. sys.stdout as in Run.
These files are appended to file_list. svn's stdout is also printed to
sys.stdout as in RunSVN. Args:
options: command line options to gclient
Args: args: A sequence of command line parameters to be passed to svn.
options: command line options to gclient in_directory: The directory where svn is to be run.
args: A sequence of command line parameters to be passed to svn.
in_directory: The directory where svn is to be run. Raises:
Error: An error occurred while running the svn command.
Raises: """
Error: An error occurred while running the svn command. command = [SVN.COMMAND]
""" command.extend(args)
command = [SVN_COMMAND]
command.extend(args) # svn update and svn checkout use the same pattern: the first three columns
# are for file status, property status, and lock status. This is followed
# svn update and svn checkout use the same pattern: the first three columns # by two spaces, and then the path to the file.
# are for file status, property status, and lock status. This is followed update_pattern = '^... (.*)$'
# by two spaces, and then the path to the file.
update_pattern = '^... (.*)$' # The first three columns of svn status are the same as for svn update and
# svn checkout. The next three columns indicate addition-with-history,
# The first three columns of svn status are the same as for svn update and # switch, and remote lock status. This is followed by one space, and then
# svn checkout. The next three columns indicate addition-with-history, # the path to the file.
# switch, and remote lock status. This is followed by one space, and then status_pattern = '^...... (.*)$'
# the path to the file.
status_pattern = '^...... (.*)$' # args[0] must be a supported command. This will blow up if it's something
# else, which is good. Note that the patterns are only effective when
# args[0] must be a supported command. This will blow up if it's something # these commands are used in their ordinary forms, the patterns are invalid
# else, which is good. Note that the patterns are only effective when # for "svn status --show-updates", for example.
# these commands are used in their ordinary forms, the patterns are invalid pattern = {
# for "svn status --show-updates", for example. 'checkout': update_pattern,
pattern = { 'status': status_pattern,
'checkout': update_pattern, 'update': update_pattern,
'status': status_pattern, }[args[0]]
'update': update_pattern,
}[args[0]] compiled_pattern = re.compile(pattern)
compiled_pattern = re.compile(pattern) def CaptureMatchingLines(line):
match = compiled_pattern.search(line)
def CaptureMatchingLines(line): if match:
match = compiled_pattern.search(line) file_list.append(match.group(1))
if match:
file_list.append(match.group(1)) SVN.RunAndFilterOutput(args,
in_directory,
RunSVNAndFilterOutput(args, options.verbose,
in_directory, True,
options.verbose, CaptureMatchingLines)
True,
CaptureMatchingLines) @staticmethod
def RunAndFilterOutput(args,
def RunSVNAndFilterOutput(args, in_directory,
in_directory, print_messages,
print_messages, print_stdout,
print_stdout, filter):
filter): """Runs svn checkout, update, status, or diff, optionally outputting
"""Runs svn checkout, update, status, or diff, optionally outputting to stdout.
to stdout.
The first item in args must be either "checkout", "update",
The first item in args must be either "checkout", "update", "status", or "diff".
"status", or "diff".
svn's stdout is passed line-by-line to the given filter function. If
svn's stdout is passed line-by-line to the given filter function. If print_stdout is true, it is also printed to sys.stdout as in Run.
print_stdout is true, it is also printed to sys.stdout as in RunSVN.
Args:
Args: args: A sequence of command line parameters to be passed to svn.
args: A sequence of command line parameters to be passed to svn. in_directory: The directory where svn is to be run.
in_directory: The directory where svn is to be run. print_messages: Whether to print status messages to stdout about
print_messages: Whether to print status messages to stdout about which Subversion commands are being run.
which Subversion commands are being run. print_stdout: Whether to forward Subversion's output to stdout.
print_stdout: Whether to forward Subversion's output to stdout. filter: A function taking one argument (a string) which will be
filter: A function taking one argument (a string) which will be passed each line (with the ending newline character removed) of
passed each line (with the ending newline character removed) of Subversion's output for filtering.
Subversion's output for filtering.
Raises:
Raises: Error: An error occurred while running the svn command.
Error: An error occurred while running the svn command. """
""" command = [SVN.COMMAND]
command = [SVN_COMMAND] command.extend(args)
command.extend(args)
gclient_utils.SubprocessCallAndFilter(command,
gclient_utils.SubprocessCallAndFilter(command, in_directory,
in_directory, print_messages,
print_messages, print_stdout,
print_stdout, filter=filter)
filter=filter)
@staticmethod
def CaptureSVNInfo(relpath, in_directory=None, print_error=True): def CaptureInfo(relpath, in_directory=None, print_error=True):
"""Returns a dictionary from the svn info output for the given file. """Returns a dictionary from the svn info output for the given file.
Args: Args:
relpath: The directory where the working copy resides relative to relpath: The directory where the working copy resides relative to
the directory given by in_directory. the directory given by in_directory.
in_directory: The directory where svn is to be run. in_directory: The directory where svn is to be run.
""" """
output = CaptureSVN(["info", "--xml", relpath], in_directory, print_error) output = SVN.Capture(["info", "--xml", relpath], in_directory, print_error)
dom = gclient_utils.ParseXML(output) dom = gclient_utils.ParseXML(output)
result = {} result = {}
if dom: if dom:
GetNamedNodeText = gclient_utils.GetNamedNodeText GetNamedNodeText = gclient_utils.GetNamedNodeText
GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText
def C(item, f): def C(item, f):
if item is not None: return f(item) if item is not None: return f(item)
# /info/entry/ # /info/entry/
# url # url
# reposityory/(root|uuid) # reposityory/(root|uuid)
# wc-info/(schedule|depth) # wc-info/(schedule|depth)
# commit/(author|date) # commit/(author|date)
# str() the results because they may be returned as Unicode, which # str() the results because they may be returned as Unicode, which
# interferes with the higher layers matching up things in the deps # interferes with the higher layers matching up things in the deps
# dictionary. # dictionary.
# TODO(maruel): Fix at higher level instead (!) # TODO(maruel): Fix at higher level instead (!)
result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str)
result['URL'] = C(GetNamedNodeText(dom, 'url'), str) result['URL'] = C(GetNamedNodeText(dom, 'url'), str)
result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str)
result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', 'revision'), result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry',
int) 'revision'),
result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'), int)
str) result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'),
result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str) str)
result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str) # Differs across versions.
result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str) if result['Node Kind'] == 'dir':
result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str) result['Node Kind'] = 'directory'
return result result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str)
result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str)
result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str)
def CaptureSVNHeadRevision(url): result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str)
"""Get the head revision of a SVN repository. return result
Returns: @staticmethod
Int head revision def CaptureHeadRevision(url):
""" """Get the head revision of a SVN repository.
info = CaptureSVN(["info", "--xml", url], os.getcwd())
dom = xml.dom.minidom.parseString(info) Returns:
return dom.getElementsByTagName('entry')[0].getAttribute('revision') Int head revision
"""
info = SVN.Capture(["info", "--xml", url], os.getcwd())
def CaptureSVNStatus(files): dom = xml.dom.minidom.parseString(info)
"""Returns the svn 1.5 svn status emulated output. return dom.getElementsByTagName('entry')[0].getAttribute('revision')
@files can be a string (one file) or a list of files. @staticmethod
def CaptureStatus(files):
Returns an array of (status, file) tuples.""" """Returns the svn 1.5 svn status emulated output.
command = ["status", "--xml"]
if not files: @files can be a string (one file) or a list of files.
pass
elif isinstance(files, basestring): Returns an array of (status, file) tuples."""
command.append(files) command = ["status", "--xml"]
else: if not files:
command.extend(files) pass
elif isinstance(files, basestring):
status_letter = { command.append(files)
None: ' ', else:
'': ' ', command.extend(files)
'added': 'A',
'conflicted': 'C', status_letter = {
'deleted': 'D', None: ' ',
'external': 'X', '': ' ',
'ignored': 'I', 'added': 'A',
'incomplete': '!', 'conflicted': 'C',
'merged': 'G', 'deleted': 'D',
'missing': '!', 'external': 'X',
'modified': 'M', 'ignored': 'I',
'none': ' ', 'incomplete': '!',
'normal': ' ', 'merged': 'G',
'obstructed': '~', 'missing': '!',
'replaced': 'R', 'modified': 'M',
'unversioned': '?', 'none': ' ',
} 'normal': ' ',
dom = gclient_utils.ParseXML(CaptureSVN(command)) 'obstructed': '~',
results = [] 'replaced': 'R',
if dom: 'unversioned': '?',
# /status/target/entry/(wc-status|commit|author|date) }
for target in dom.getElementsByTagName('target'): dom = gclient_utils.ParseXML(SVN.Capture(command))
for entry in target.getElementsByTagName('entry'): results = []
file_path = entry.getAttribute('path') if dom:
wc_status = entry.getElementsByTagName('wc-status') # /status/target/entry/(wc-status|commit|author|date)
assert len(wc_status) == 1 for target in dom.getElementsByTagName('target'):
# Emulate svn 1.5 status ouput... #base_path = target.getAttribute('path')
statuses = [' '] * 7 for entry in target.getElementsByTagName('entry'):
# Col 0 file_path = entry.getAttribute('path')
xml_item_status = wc_status[0].getAttribute('item') wc_status = entry.getElementsByTagName('wc-status')
if xml_item_status in status_letter: assert len(wc_status) == 1
statuses[0] = status_letter[xml_item_status] # Emulate svn 1.5 status ouput...
else: statuses = [' '] * 7
raise Exception('Unknown item status "%s"; please implement me!' % # Col 0
xml_item_status) xml_item_status = wc_status[0].getAttribute('item')
# Col 1 if xml_item_status in status_letter:
xml_props_status = wc_status[0].getAttribute('props') statuses[0] = status_letter[xml_item_status]
if xml_props_status == 'modified': else:
statuses[1] = 'M' raise Exception('Unknown item status "%s"; please implement me!' %
elif xml_props_status == 'conflicted': xml_item_status)
statuses[1] = 'C' # Col 1
elif (not xml_props_status or xml_props_status == 'none' or xml_props_status = wc_status[0].getAttribute('props')
xml_props_status == 'normal'): if xml_props_status == 'modified':
pass statuses[1] = 'M'
else: elif xml_props_status == 'conflicted':
raise Exception('Unknown props status "%s"; please implement me!' % statuses[1] = 'C'
xml_props_status) elif (not xml_props_status or xml_props_status == 'none' or
# Col 2 xml_props_status == 'normal'):
if wc_status[0].getAttribute('wc-locked') == 'true': pass
statuses[2] = 'L' else:
# Col 3 raise Exception('Unknown props status "%s"; please implement me!' %
if wc_status[0].getAttribute('copied') == 'true': xml_props_status)
statuses[3] = '+' # Col 2
# Col 4 if wc_status[0].getAttribute('wc-locked') == 'true':
if wc_status[0].getAttribute('switched') == 'true': statuses[2] = 'L'
statuses[4] = 'S' # Col 3
# TODO(maruel): Col 5 and 6 if wc_status[0].getAttribute('copied') == 'true':
item = (''.join(statuses), file_path) statuses[3] = '+'
results.append(item) # Col 4
return results if wc_status[0].getAttribute('switched') == 'true':
statuses[4] = 'S'
# TODO(maruel): Col 5 and 6
item = (''.join(statuses), file_path)
results.append(item)
return results
@staticmethod
def IsMoved(filename):
"""Determine if a file has been added through svn mv"""
info = SVN.CaptureInfo(filename)
return (info.get('Copied From URL') and
info.get('Copied From Rev') and
info.get('Schedule') == 'add')
@staticmethod
def GetFileProperty(file, property_name):
"""Returns the value of an SVN property for the given file.
Args:
file: The file to check
property_name: The name of the SVN property, e.g. "svn:mime-type"
Returns:
The value of the property, which will be the empty string if the property
is not set on the file. If the file is not under version control, the
empty string is also returned.
"""
output = SVN.Capture(["propget", property_name, file])
if (output.startswith("svn: ") and
output.endswith("is not under version control")):
return ""
else:
return output
@staticmethod
def DiffItem(filename):
"""Diff a single file"""
# Use svn info output instead of os.path.isdir because the latter fails
# when the file is deleted.
if SVN.CaptureInfo(filename).get("Node Kind") == "directory":
return None
# If the user specified a custom diff command in their svn config file,
# then it'll be used when we do svn diff, which we don't want to happen
# since we want the unified diff. Using --diff-cmd=diff doesn't always
# work, since they can have another diff executable in their path that
# gives different line endings. So we use a bogus temp directory as the
# config directory, which gets around these problems.
if sys.platform.startswith("win"):
parent_dir = tempfile.gettempdir()
else:
parent_dir = sys.path[0] # tempdir is not secure.
bogus_dir = os.path.join(parent_dir, "temp_svn_config")
if not os.path.exists(bogus_dir):
os.mkdir(bogus_dir)
# Grabs the diff data.
data = SVN.Capture(["diff", "--config-dir", bogus_dir, filename], None)
# We know the diff will be incorrectly formatted. Fix it.
if SVN.IsMoved(filename):
# The file is "new" in the patch sense. Generate a homebrew diff.
# We can't use ReadFile() since it's not using binary mode.
file_handle = open(filename, 'rb')
file_content = file_handle.read()
file_handle.close()
# Prepend '+' to every lines.
file_content = ['+' + i for i in file_content.splitlines(True)]
nb_lines = len(file_content)
# We need to use / since patch on unix will fail otherwise.
filename = filename.replace('\\', '/')
data = "Index: %s\n" % filename
data += ("============================================================="
"======\n")
# Note: Should we use /dev/null instead?
data += "--- %s\n" % filename
data += "+++ %s\n" % filename
data += "@@ -0,0 +1,%d @@\n" % nb_lines
data += ''.join(file_content)
return data
...@@ -16,7 +16,7 @@ class GclTestsBase(SuperMoxTestBase): ...@@ -16,7 +16,7 @@ class GclTestsBase(SuperMoxTestBase):
SuperMoxTestBase.setUp(self) SuperMoxTestBase.setUp(self)
self.fake_root_dir = self.RootDir() self.fake_root_dir = self.RootDir()
self.mox.StubOutWithMock(gcl, 'RunShell') self.mox.StubOutWithMock(gcl, 'RunShell')
self.mox.StubOutWithMock(gcl.gclient_scm, 'CaptureSVNInfo') self.mox.StubOutWithMock(gcl.SVN, 'CaptureInfo')
self.mox.StubOutWithMock(gcl, 'tempfile') self.mox.StubOutWithMock(gcl, 'tempfile')
self.mox.StubOutWithMock(gcl.upload, 'RealMain') self.mox.StubOutWithMock(gcl.upload, 'RealMain')
# These are not tested. # These are not tested.
...@@ -29,25 +29,21 @@ class GclUnittest(GclTestsBase): ...@@ -29,25 +29,21 @@ class GclUnittest(GclTestsBase):
def testMembersChanged(self): def testMembersChanged(self):
self.mox.ReplayAll() self.mox.ReplayAll()
members = [ members = [
'CODEREVIEW_SETTINGS', 'CODEREVIEW_SETTINGS_FILE', 'CODEREVIEW_SETTINGS', 'CODEREVIEW_SETTINGS_FILE', 'Change',
'Change', 'ChangeInfo', 'Changes', 'Commit', 'ChangeInfo', 'Changes', 'Commit', 'DEFAULT_LINT_IGNORE_REGEX',
'DEFAULT_LINT_IGNORE_REGEX', 'DEFAULT_LINT_REGEX', 'DEFAULT_LINT_REGEX', 'DeleteEmptyChangeLists', 'DoPresubmitChecks',
'DeleteEmptyChangeLists', 'DoPresubmitChecks', 'ErrorExit', 'FILES_CACHE', 'FilterFlag', 'GenerateChangeName',
'ErrorExit', 'FILES_CACHE', 'FilterFlag', 'GenerateChangeName', 'GenerateDiff', 'GetCLs', 'GetCacheDir', 'GetCachedFile',
'GenerateDiff', 'GetChangelistInfoFile', 'GetChangesDir', 'GetCodeReviewSetting',
'GetCacheDir', 'GetCachedFile', 'GetChangesDir', 'GetCLs', 'GetEditor', 'GetFilesNotInCL', 'GetInfoDir', 'GetIssueDescription',
'GetChangelistInfoFile', 'GetCodeReviewSetting', 'GetEditor', 'GetModifiedFiles', 'GetRepositoryRoot', 'Help', 'Lint',
'GetFilesNotInCL', 'GetInfoDir', 'GetIssueDescription', 'LoadChangelistInfoForMultiple', 'MISSING_TEST_MSG', 'Opened',
'GetModifiedFiles', 'GetRepositoryRoot', 'OptionallyDoPresubmitChecks', 'PresubmitCL', 'REPOSITORY_ROOT',
'GetSVNFileProperty', 'Help', 'IsSVNMoved', 'ReadFile', 'RunShell', 'RunShellWithReturnCode', 'SVN',
'Lint', 'LoadChangelistInfoForMultiple', 'SendToRietveld', 'TryChange', 'UnknownFiles', 'UploadCL', 'Warn',
'MISSING_TEST_MSG', 'Opened', 'OptionallyDoPresubmitChecks', 'WriteFile', 'gclient_utils', 'getpass', 'main', 'os', 'random', 're',
'PresubmitCL', 'ReadFile', 'REPOSITORY_ROOT', 'RunShell', 'shutil', 'string', 'subprocess', 'sys', 'tempfile', 'upload',
'RunShellWithReturnCode', 'SendToRietveld', 'TryChange', 'urllib2',
'UnknownFiles', 'UploadCL', 'Warn', 'WriteFile',
'gclient_scm', 'gclient_utils', 'getpass', 'main', 'os', 'random', 're',
'shutil', 'string', 'subprocess', 'sys', 'tempfile',
'upload', 'urllib2',
] ]
# If this test fails, you should add the relevant test. # If this test fails, you should add the relevant test.
self.compareMembers(gcl, members) self.compareMembers(gcl, members)
...@@ -70,8 +66,7 @@ class GclUnittest(GclTestsBase): ...@@ -70,8 +66,7 @@ class GclUnittest(GclTestsBase):
result = { result = {
"Repository Root": "" "Repository Root": ""
} }
gcl.gclient_scm.CaptureSVNInfo("/bleh/prout", print_error=False).AndReturn( gcl.SVN.CaptureInfo("/bleh/prout", print_error=False).AndReturn(result)
result)
self.mox.ReplayAll() self.mox.ReplayAll()
self.assertRaises(Exception, gcl.GetRepositoryRoot) self.assertRaises(Exception, gcl.GetRepositoryRoot)
...@@ -80,13 +75,11 @@ class GclUnittest(GclTestsBase): ...@@ -80,13 +75,11 @@ class GclUnittest(GclTestsBase):
root_path = gcl.os.path.join('bleh', 'prout', 'pouet') root_path = gcl.os.path.join('bleh', 'prout', 'pouet')
gcl.os.getcwd().AndReturn(root_path) gcl.os.getcwd().AndReturn(root_path)
result1 = { "Repository Root": "Some root" } result1 = { "Repository Root": "Some root" }
gcl.gclient_scm.CaptureSVNInfo(root_path, gcl.SVN.CaptureInfo(root_path, print_error=False).AndReturn(result1)
print_error=False).AndReturn(result1)
gcl.os.getcwd().AndReturn(root_path) gcl.os.getcwd().AndReturn(root_path)
results2 = { "Repository Root": "A different root" } results2 = { "Repository Root": "A different root" }
gcl.gclient_scm.CaptureSVNInfo( gcl.SVN.CaptureInfo(gcl.os.path.dirname(root_path),
gcl.os.path.dirname(root_path), print_error=False).AndReturn(results2)
print_error=False).AndReturn(results2)
self.mox.ReplayAll() self.mox.ReplayAll()
self.assertEquals(gcl.GetRepositoryRoot(), root_path) self.assertEquals(gcl.GetRepositoryRoot(), root_path)
......
...@@ -33,12 +33,12 @@ class BaseTestCase(GCBaseTestCase): ...@@ -33,12 +33,12 @@ class BaseTestCase(GCBaseTestCase):
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite') self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'SubprocessCall') self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'SubprocessCall')
self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'RemoveDirectory') self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'RemoveDirectory')
self._CaptureSVNInfo = gclient_scm.CaptureSVNInfo self._CaptureSVNInfo = gclient_scm.scm.SVN.CaptureInfo
self.mox.StubOutWithMock(gclient_scm, 'CaptureSVN') self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Capture')
self.mox.StubOutWithMock(gclient_scm, 'CaptureSVNInfo') self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureInfo')
self.mox.StubOutWithMock(gclient_scm, 'CaptureSVNStatus') self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureStatus')
self.mox.StubOutWithMock(gclient_scm, 'RunSVN') self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Run')
self.mox.StubOutWithMock(gclient_scm, 'RunSVNAndGetFileList') self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'RunAndGetFileList')
self._scm_wrapper = gclient_scm.CreateSCM self._scm_wrapper = gclient_scm.CreateSCM
...@@ -64,9 +64,11 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -64,9 +64,11 @@ class SVNWrapperTestCase(BaseTestCase):
def testDir(self): def testDir(self):
members = [ members = [
'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export', 'COMMAND', 'Capture', 'CaptureHeadRevision', 'CaptureInfo',
'pack', 'relpath', 'revert', 'revinfo', 'runhooks', 'scm_name', 'status', 'CaptureStatus', 'DiffItem', 'FullUrlForRelativeUrl', 'GetFileProperty',
'update', 'url', 'IsMoved', 'Run', 'RunAndFilterOutput', 'RunAndGetFileList',
'RunCommand', 'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
'revinfo', 'runhooks', 'scm_name', 'status', 'update', 'url',
] ]
# If you add a member, be sure to add the relevant test! # If you add a member, be sure to add the relevant test!
...@@ -113,8 +115,9 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -113,8 +115,9 @@ class SVNWrapperTestCase(BaseTestCase):
# Checkout. # Checkout.
gclient_scm.os.path.exists(base_path).AndReturn(False) gclient_scm.os.path.exists(base_path).AndReturn(False)
files_list = self.mox.CreateMockAnything() files_list = self.mox.CreateMockAnything()
gclient_scm.RunSVNAndGetFileList(options, ['checkout', self.url, base_path], gclient_scm.scm.SVN.RunAndGetFileList(options,
self.root_dir, files_list) ['checkout', self.url, base_path],
self.root_dir, files_list)
self.mox.ReplayAll() self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
...@@ -125,9 +128,10 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -125,9 +128,10 @@ class SVNWrapperTestCase(BaseTestCase):
options = self.Options(verbose=True) options = self.Options(verbose=True)
base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
gclient_scm.os.path.isdir(base_path).AndReturn(True) gclient_scm.os.path.isdir(base_path).AndReturn(True)
gclient_scm.CaptureSVNStatus(base_path).AndReturn([]) gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn([])
gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'], gclient_scm.scm.SVN.RunAndGetFileList(options,
base_path, mox.IgnoreArg()) ['update', '--revision', 'BASE'],
base_path, mox.IgnoreArg())
self.mox.ReplayAll() self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
...@@ -145,15 +149,16 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -145,15 +149,16 @@ class SVNWrapperTestCase(BaseTestCase):
] ]
file_path1 = gclient_scm.os.path.join(base_path, 'a') file_path1 = gclient_scm.os.path.join(base_path, 'a')
file_path2 = gclient_scm.os.path.join(base_path, 'b') file_path2 = gclient_scm.os.path.join(base_path, 'b')
gclient_scm.CaptureSVNStatus(base_path).AndReturn(items) gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn(items)
gclient_scm.os.path.exists(file_path1).AndReturn(True) gclient_scm.os.path.exists(file_path1).AndReturn(True)
gclient_scm.os.path.isfile(file_path1).AndReturn(True) gclient_scm.os.path.isfile(file_path1).AndReturn(True)
gclient_scm.os.remove(file_path1) gclient_scm.os.remove(file_path1)
gclient_scm.os.path.exists(file_path2).AndReturn(True) gclient_scm.os.path.exists(file_path2).AndReturn(True)
gclient_scm.os.path.isfile(file_path2).AndReturn(True) gclient_scm.os.path.isfile(file_path2).AndReturn(True)
gclient_scm.os.remove(file_path2) gclient_scm.os.remove(file_path2)
gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'], gclient_scm.scm.SVN.RunAndGetFileList(options,
base_path, mox.IgnoreArg()) ['update', '--revision', 'BASE'],
base_path, mox.IgnoreArg())
print(gclient_scm.os.path.join(base_path, 'a')) print(gclient_scm.os.path.join(base_path, 'a'))
print(gclient_scm.os.path.join(base_path, 'b')) print(gclient_scm.os.path.join(base_path, 'b'))
...@@ -170,7 +175,7 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -170,7 +175,7 @@ class SVNWrapperTestCase(BaseTestCase):
items = [ items = [
('~ ', 'a'), ('~ ', 'a'),
] ]
gclient_scm.CaptureSVNStatus(base_path).AndReturn(items) gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn(items)
file_path = gclient_scm.os.path.join(base_path, 'a') file_path = gclient_scm.os.path.join(base_path, 'a')
print(file_path) print(file_path)
gclient_scm.os.path.exists(file_path).AndReturn(True) gclient_scm.os.path.exists(file_path).AndReturn(True)
...@@ -178,8 +183,9 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -178,8 +183,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.os.path.isdir(file_path).AndReturn(True) gclient_scm.os.path.isdir(file_path).AndReturn(True)
gclient_scm.gclient_utils.RemoveDirectory(file_path) gclient_scm.gclient_utils.RemoveDirectory(file_path)
file_list1 = [] file_list1 = []
gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'], gclient_scm.scm.SVN.RunAndGetFileList(options,
base_path, mox.IgnoreArg()) ['update', '--revision', 'BASE'],
base_path, mox.IgnoreArg())
self.mox.ReplayAll() self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
...@@ -191,8 +197,9 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -191,8 +197,9 @@ class SVNWrapperTestCase(BaseTestCase):
options = self.Options(verbose=True) options = self.Options(verbose=True)
base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
gclient_scm.os.path.isdir(base_path).AndReturn(True) gclient_scm.os.path.isdir(base_path).AndReturn(True)
gclient_scm.RunSVNAndGetFileList(options, ['status'] + self.args, gclient_scm.scm.SVN.RunAndGetFileList(options,
base_path, []).AndReturn(None) ['status'] + self.args,
base_path, []).AndReturn(None)
self.mox.ReplayAll() self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
...@@ -216,8 +223,9 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -216,8 +223,9 @@ class SVNWrapperTestCase(BaseTestCase):
# Checkout. # Checkout.
gclient_scm.os.path.exists(base_path).AndReturn(False) gclient_scm.os.path.exists(base_path).AndReturn(False)
files_list = self.mox.CreateMockAnything() files_list = self.mox.CreateMockAnything()
gclient_scm.RunSVNAndGetFileList(options, ['checkout', self.url, gclient_scm.scm.SVN.RunAndGetFileList(options,
base_path], self.root_dir, files_list) ['checkout', self.url, base_path],
self.root_dir, files_list)
self.mox.ReplayAll() self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath) relpath=self.relpath)
...@@ -238,17 +246,19 @@ class SVNWrapperTestCase(BaseTestCase): ...@@ -238,17 +246,19 @@ class SVNWrapperTestCase(BaseTestCase):
).AndReturn(False) ).AndReturn(False)
# Checkout or update. # Checkout or update.
gclient_scm.os.path.exists(base_path).AndReturn(True) gclient_scm.os.path.exists(base_path).AndReturn(True)
gclient_scm.CaptureSVNInfo(gclient_scm.os.path.join(base_path, "."), '.' gclient_scm.scm.SVN.CaptureInfo(
).AndReturn(file_info) gclient_scm.os.path.join(base_path, "."), '.'
).AndReturn(file_info)
# Cheat a bit here. # Cheat a bit here.
gclient_scm.CaptureSVNInfo(file_info['URL'], '.').AndReturn(file_info) gclient_scm.scm.SVN.CaptureInfo(file_info['URL'], '.').AndReturn(file_info)
additional_args = [] additional_args = []
if options.manually_grab_svn_rev: if options.manually_grab_svn_rev:
additional_args = ['--revision', str(file_info['Revision'])] additional_args = ['--revision', str(file_info['Revision'])]
files_list = [] files_list = []
gclient_scm.RunSVNAndGetFileList(options, gclient_scm.scm.SVN.RunAndGetFileList(
['update', base_path] + additional_args, options,
self.root_dir, files_list) ['update', base_path] + additional_args,
self.root_dir, files_list)
self.mox.ReplayAll() self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
...@@ -356,9 +366,9 @@ from :3 ...@@ -356,9 +366,9 @@ from :3
def testDir(self): def testDir(self):
members = [ members = [
'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export', 'COMMAND', 'Capture', 'CaptureStatus', 'FullUrlForRelativeUrl',
'relpath', 'revert', 'revinfo', 'runhooks', 'scm_name', 'status', 'RunCommand', 'cleanup', 'diff', 'export', 'relpath', 'revert',
'update', 'url', 'revinfo', 'runhooks', 'scm_name', 'status', 'update', 'url',
] ]
# If you add a member, be sure to add the relevant test! # If you add a member, be sure to add the relevant test!
......
...@@ -22,8 +22,6 @@ import __builtin__ ...@@ -22,8 +22,6 @@ import __builtin__
import StringIO import StringIO
import gclient import gclient
# Temporary due to the "from scm import *" in gclient_scm.
import scm
from super_mox import mox, IsOneOf, SuperMoxTestBase from super_mox import mox, IsOneOf, SuperMoxTestBase
...@@ -50,16 +48,11 @@ class GClientBaseTestCase(BaseTestCase): ...@@ -50,16 +48,11 @@ class GClientBaseTestCase(BaseTestCase):
self.mox.StubOutWithMock(gclient.gclient_utils, 'SubprocessCall') self.mox.StubOutWithMock(gclient.gclient_utils, 'SubprocessCall')
self.mox.StubOutWithMock(gclient.gclient_utils, 'RemoveDirectory') self.mox.StubOutWithMock(gclient.gclient_utils, 'RemoveDirectory')
# Mock them to be sure nothing bad happens. # Mock them to be sure nothing bad happens.
self.mox.StubOutWithMock(gclient.gclient_scm, 'CaptureSVN') self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'Capture')
self.mox.StubOutWithMock(gclient.gclient_scm, 'CaptureSVNInfo') self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'CaptureInfo')
self.mox.StubOutWithMock(gclient.gclient_scm, 'CaptureSVNStatus') self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'CaptureStatus')
self.mox.StubOutWithMock(gclient.gclient_scm, 'RunSVN') self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'Run')
self.mox.StubOutWithMock(gclient.gclient_scm, 'RunSVNAndGetFileList') self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'RunAndGetFileList')
self.mox.StubOutWithMock(scm, 'CaptureSVN')
self.mox.StubOutWithMock(scm, 'CaptureSVNInfo')
self.mox.StubOutWithMock(scm, 'CaptureSVNStatus')
self.mox.StubOutWithMock(scm, 'RunSVN')
self.mox.StubOutWithMock(scm, 'RunSVNAndGetFileList')
self._gclient_gclient = gclient.GClient self._gclient_gclient = gclient.GClient
gclient.GClient = self.mox.CreateMockAnything() gclient.GClient = self.mox.CreateMockAnything()
self._scm_wrapper = gclient.gclient_scm.CreateSCM self._scm_wrapper = gclient.gclient_scm.CreateSCM
......
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
import StringIO import StringIO
# Local imports
import presubmit_support as presubmit import presubmit_support as presubmit
import presubmit_canned_checks # Shortcut.
from presubmit_support import presubmit_canned_checks
from super_mox import mox, SuperMoxTestBase from super_mox import mox, SuperMoxTestBase
...@@ -47,9 +47,11 @@ def GetPreferredTrySlaves(): ...@@ -47,9 +47,11 @@ def GetPreferredTrySlaves():
presubmit.os.path.abspath = MockAbsPath presubmit.os.path.abspath = MockAbsPath
presubmit.os.getcwd = self.RootDir presubmit.os.getcwd = self.RootDir
presubmit.os.chdir = MockChdir presubmit.os.chdir = MockChdir
self.mox.StubOutWithMock(presubmit.gclient_scm, 'CaptureSVNInfo') self.mox.StubOutWithMock(presubmit.scm.SVN, 'CaptureInfo')
self.mox.StubOutWithMock(presubmit.gcl, 'GetSVNFileProperty') self.mox.StubOutWithMock(presubmit.scm.SVN, 'GetFileProperty')
# TODO(maruel): Err, small duplication of code here.
self.mox.StubOutWithMock(presubmit.gcl, 'ReadFile') self.mox.StubOutWithMock(presubmit.gcl, 'ReadFile')
self.mox.StubOutWithMock(presubmit.gclient_utils, 'FileRead')
class PresubmitUnittest(PresubmitTestsBase): class PresubmitUnittest(PresubmitTestsBase):
...@@ -63,9 +65,9 @@ class PresubmitUnittest(PresubmitTestsBase): ...@@ -63,9 +65,9 @@ class PresubmitUnittest(PresubmitTestsBase):
'NotImplementedException', 'OutputApi', 'ParseFiles', 'NotImplementedException', 'OutputApi', 'ParseFiles',
'PresubmitExecuter', 'PromptYesNo', 'ScanSubDirs', 'PresubmitExecuter', 'PromptYesNo', 'ScanSubDirs',
'SvnAffectedFile', 'SvnChange', 'cPickle', 'cStringIO', 'SvnAffectedFile', 'SvnChange', 'cPickle', 'cStringIO',
'exceptions', 'fnmatch', 'gcl', 'gclient_scm', 'glob', 'exceptions', 'fnmatch', 'gcl', 'gclient_utils', 'glob',
'logging', 'marshal', 'normpath', 'optparse', 'os', 'pickle', 'logging', 'marshal', 'normpath', 'optparse', 'os', 'pickle',
'presubmit_canned_checks', 'random', 're', 'subprocess', 'sys', 'presubmit_canned_checks', 'random', 're', 'scm', 'subprocess', 'sys',
'tempfile', 'time', 'traceback', 'types', 'unittest', 'urllib2', 'tempfile', 'time', 'traceback', 'types', 'unittest', 'urllib2',
'warnings', 'warnings',
] ]
...@@ -140,22 +142,22 @@ class PresubmitUnittest(PresubmitTestsBase): ...@@ -140,22 +142,22 @@ class PresubmitUnittest(PresubmitTestsBase):
presubmit.os.path.exists(notfound).AndReturn(True) presubmit.os.path.exists(notfound).AndReturn(True)
presubmit.os.path.isdir(notfound).AndReturn(False) presubmit.os.path.isdir(notfound).AndReturn(False)
presubmit.os.path.exists(flap).AndReturn(False) presubmit.os.path.exists(flap).AndReturn(False)
presubmit.gclient_scm.CaptureSVNInfo(flap presubmit.scm.SVN.CaptureInfo(flap
).AndReturn({'Node Kind': 'file'}) ).AndReturn({'Node Kind': 'file'})
presubmit.gcl.GetSVNFileProperty(blat, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None)
presubmit.gcl.GetSVNFileProperty( presubmit.scm.SVN.GetFileProperty(
binary, 'svn:mime-type').AndReturn('application/octet-stream') binary, 'svn:mime-type').AndReturn('application/octet-stream')
presubmit.gcl.GetSVNFileProperty( presubmit.scm.SVN.GetFileProperty(
notfound, 'svn:mime-type').AndReturn('') notfound, 'svn:mime-type').AndReturn('')
presubmit.gclient_scm.CaptureSVNInfo(blat).AndReturn( presubmit.scm.SVN.CaptureInfo(blat).AndReturn(
{'URL': 'svn:/foo/foo/blat.cc'}) {'URL': 'svn:/foo/foo/blat.cc'})
presubmit.gclient_scm.CaptureSVNInfo(binary).AndReturn( presubmit.scm.SVN.CaptureInfo(binary).AndReturn(
{'URL': 'svn:/foo/binary.dll'}) {'URL': 'svn:/foo/binary.dll'})
presubmit.gclient_scm.CaptureSVNInfo(notfound).AndReturn({}) presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({})
presubmit.gclient_scm.CaptureSVNInfo(flap).AndReturn( presubmit.scm.SVN.CaptureInfo(flap).AndReturn(
{'URL': 'svn:/foo/boo/flap.h'}) {'URL': 'svn:/foo/boo/flap.h'})
presubmit.gcl.ReadFile(blat).AndReturn('boo!\nahh?') presubmit.gclient_utils.FileRead(blat, 'rU').AndReturn('boo!\nahh?')
presubmit.gcl.ReadFile(notfound).AndReturn('look!\nthere?') presubmit.gclient_utils.FileRead(notfound, 'rU').AndReturn('look!\nthere?')
self.mox.ReplayAll() self.mox.ReplayAll()
change = presubmit.SvnChange('mychange', '\n'.join(description_lines), change = presubmit.SvnChange('mychange', '\n'.join(description_lines),
...@@ -285,10 +287,10 @@ class PresubmitUnittest(PresubmitTestsBase): ...@@ -285,10 +287,10 @@ class PresubmitUnittest(PresubmitTestsBase):
root_path = join(self.fake_root_dir, 'PRESUBMIT.py') root_path = join(self.fake_root_dir, 'PRESUBMIT.py')
presubmit.os.path.isfile(root_path).AndReturn(True) presubmit.os.path.isfile(root_path).AndReturn(True)
presubmit.os.path.isfile(haspresubmit_path).AndReturn(True) presubmit.os.path.isfile(haspresubmit_path).AndReturn(True)
presubmit.gcl.ReadFile(root_path, presubmit.gclient_utils.FileRead(root_path,
'rU').AndReturn(self.presubmit_text) 'rU').AndReturn(self.presubmit_text)
presubmit.gcl.ReadFile(haspresubmit_path, presubmit.gclient_utils.FileRead(haspresubmit_path,
'rU').AndReturn(self.presubmit_text) 'rU').AndReturn(self.presubmit_text)
presubmit.random.randint(0, 4).AndReturn(1) presubmit.random.randint(0, 4).AndReturn(1)
self.mox.ReplayAll() self.mox.ReplayAll()
...@@ -313,9 +315,9 @@ class PresubmitUnittest(PresubmitTestsBase): ...@@ -313,9 +315,9 @@ class PresubmitUnittest(PresubmitTestsBase):
for i in range(2): for i in range(2):
presubmit.os.path.isfile(presubmit_path).AndReturn(True) presubmit.os.path.isfile(presubmit_path).AndReturn(True)
presubmit.os.path.isfile(haspresubmit_path).AndReturn(True) presubmit.os.path.isfile(haspresubmit_path).AndReturn(True)
presubmit.gcl.ReadFile(presubmit_path, 'rU' presubmit.gclient_utils.FileRead(presubmit_path, 'rU'
).AndReturn(self.presubmit_text) ).AndReturn(self.presubmit_text)
presubmit.gcl.ReadFile(haspresubmit_path, 'rU' presubmit.gclient_utils.FileRead(haspresubmit_path, 'rU'
).AndReturn(self.presubmit_text) ).AndReturn(self.presubmit_text)
presubmit.random.randint(0, 4).AndReturn(1) presubmit.random.randint(0, 4).AndReturn(1)
presubmit.random.randint(0, 4).AndReturn(1) presubmit.random.randint(0, 4).AndReturn(1)
...@@ -349,8 +351,9 @@ class PresubmitUnittest(PresubmitTestsBase): ...@@ -349,8 +351,9 @@ class PresubmitUnittest(PresubmitTestsBase):
'PRESUBMIT.py') 'PRESUBMIT.py')
presubmit.os.path.isfile(presubmit_path).AndReturn(True) presubmit.os.path.isfile(presubmit_path).AndReturn(True)
presubmit.os.path.isfile(haspresubmit_path).AndReturn(True) presubmit.os.path.isfile(haspresubmit_path).AndReturn(True)
presubmit.gcl.ReadFile(presubmit_path, 'rU').AndReturn(self.presubmit_text) presubmit.gclient_utils.FileRead(presubmit_path, 'rU'
presubmit.gcl.ReadFile(haspresubmit_path, 'rU').AndReturn( ).AndReturn(self.presubmit_text)
presubmit.gclient_utils.FileRead(haspresubmit_path, 'rU').AndReturn(
self.presubmit_text) self.presubmit_text)
presubmit.random.randint(0, 4).AndReturn(1) presubmit.random.randint(0, 4).AndReturn(1)
self.mox.ReplayAll() self.mox.ReplayAll()
...@@ -502,14 +505,14 @@ def CheckChangeOnCommit(input_api, output_api): ...@@ -502,14 +505,14 @@ def CheckChangeOnCommit(input_api, output_api):
linux_presubmit = join(self.fake_root_dir, 'linux_only', 'PRESUBMIT.py') linux_presubmit = join(self.fake_root_dir, 'linux_only', 'PRESUBMIT.py')
presubmit.os.path.isfile(root_presubmit).AndReturn(True) presubmit.os.path.isfile(root_presubmit).AndReturn(True)
presubmit.gcl.ReadFile(root_presubmit, 'rU').AndReturn( presubmit.gclient_utils.FileRead(root_presubmit, 'rU').AndReturn(
self.presubmit_tryslave % '["win"]') self.presubmit_tryslave % '["win"]')
presubmit.os.path.isfile(root_presubmit).AndReturn(True) presubmit.os.path.isfile(root_presubmit).AndReturn(True)
presubmit.os.path.isfile(linux_presubmit).AndReturn(True) presubmit.os.path.isfile(linux_presubmit).AndReturn(True)
presubmit.gcl.ReadFile(root_presubmit, 'rU').AndReturn( presubmit.gclient_utils.FileRead(root_presubmit, 'rU').AndReturn(
self.presubmit_tryslave % '["win"]') self.presubmit_tryslave % '["win"]')
presubmit.gcl.ReadFile(linux_presubmit, 'rU').AndReturn( presubmit.gclient_utils.FileRead(linux_presubmit, 'rU').AndReturn(
self.presubmit_tryslave % '["linux"]') self.presubmit_tryslave % '["linux"]')
self.mox.ReplayAll() self.mox.ReplayAll()
...@@ -565,9 +568,9 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -565,9 +568,9 @@ class InputApiUnittest(PresubmitTestsBase):
self.compareMembers(presubmit.InputApi(None, './.', False), members) self.compareMembers(presubmit.InputApi(None, './.', False), members)
def testDepotToLocalPath(self): def testDepotToLocalPath(self):
presubmit.gclient_scm.CaptureSVNInfo('svn://foo/smurf').AndReturn( presubmit.scm.SVN.CaptureInfo('svn://foo/smurf').AndReturn(
{'Path': 'prout'}) {'Path': 'prout'})
presubmit.gclient_scm.CaptureSVNInfo('svn:/foo/notfound/burp').AndReturn({}) presubmit.scm.SVN.CaptureInfo('svn:/foo/notfound/burp').AndReturn({})
self.mox.ReplayAll() self.mox.ReplayAll()
path = presubmit.InputApi(None, './p', False).DepotToLocalPath( path = presubmit.InputApi(None, './p', False).DepotToLocalPath(
...@@ -578,9 +581,9 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -578,9 +581,9 @@ class InputApiUnittest(PresubmitTestsBase):
self.failUnless(path == None) self.failUnless(path == None)
def testLocalToDepotPath(self): def testLocalToDepotPath(self):
presubmit.gclient_scm.CaptureSVNInfo('smurf').AndReturn({'URL': presubmit.scm.SVN.CaptureInfo('smurf').AndReturn({'URL':
'svn://foo'}) 'svn://foo'})
presubmit.gclient_scm.CaptureSVNInfo('notfound-food').AndReturn({}) presubmit.scm.SVN.CaptureInfo('notfound-food').AndReturn({})
self.mox.ReplayAll() self.mox.ReplayAll()
path = presubmit.InputApi(None, './p', False).LocalToDepotPath('smurf') path = presubmit.InputApi(None, './p', False).LocalToDepotPath('smurf')
...@@ -631,18 +634,20 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -631,18 +634,20 @@ class InputApiUnittest(PresubmitTestsBase):
presubmit.os.path.exists(notfound).AndReturn(False) presubmit.os.path.exists(notfound).AndReturn(False)
presubmit.os.path.exists(flap).AndReturn(True) presubmit.os.path.exists(flap).AndReturn(True)
presubmit.os.path.isdir(flap).AndReturn(False) presubmit.os.path.isdir(flap).AndReturn(False)
presubmit.gclient_scm.CaptureSVNInfo(beingdeleted).AndReturn({}) presubmit.scm.SVN.CaptureInfo(beingdeleted).AndReturn({})
presubmit.gclient_scm.CaptureSVNInfo(notfound).AndReturn({}) presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({})
presubmit.gcl.GetSVNFileProperty(blat, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None)
presubmit.gcl.GetSVNFileProperty(readme, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(readme, 'svn:mime-type').AndReturn(None)
presubmit.gcl.GetSVNFileProperty(binary, 'svn:mime-type').AndReturn( presubmit.scm.SVN.GetFileProperty(binary, 'svn:mime-type').AndReturn(
'application/octet-stream') 'application/octet-stream')
presubmit.gcl.GetSVNFileProperty(weird, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(weird, 'svn:mime-type').AndReturn(None)
presubmit.gcl.GetSVNFileProperty(another, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(another, 'svn:mime-type').AndReturn(None)
presubmit.gcl.GetSVNFileProperty(third_party, 'svn:mime-type' presubmit.scm.SVN.GetFileProperty(third_party, 'svn:mime-type'
).AndReturn(None) ).AndReturn(None)
presubmit.gcl.ReadFile(blat).AndReturn('whatever\ncookie') presubmit.gclient_utils.FileRead(blat, 'rU'
presubmit.gcl.ReadFile(another).AndReturn('whatever\ncookie2') ).AndReturn('whatever\ncookie')
presubmit.gclient_utils.FileRead(another, 'rU'
).AndReturn('whatever\ncookie2')
self.mox.ReplayAll() self.mox.ReplayAll()
change = presubmit.SvnChange('mychange', '\n'.join(description_lines), change = presubmit.SvnChange('mychange', '\n'.join(description_lines),
...@@ -754,7 +759,7 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -754,7 +759,7 @@ class InputApiUnittest(PresubmitTestsBase):
item = presubmit.os.path.join(self.fake_root_dir, item) item = presubmit.os.path.join(self.fake_root_dir, item)
presubmit.os.path.exists(item).AndReturn(True) presubmit.os.path.exists(item).AndReturn(True)
presubmit.os.path.isdir(item).AndReturn(False) presubmit.os.path.isdir(item).AndReturn(False)
presubmit.gcl.GetSVNFileProperty(item, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(item, 'svn:mime-type').AndReturn(None)
self.mox.ReplayAll() self.mox.ReplayAll()
change = presubmit.SvnChange('mychange', '', self.fake_root_dir, files, 0, change = presubmit.SvnChange('mychange', '', self.fake_root_dir, files, 0,
...@@ -776,7 +781,7 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -776,7 +781,7 @@ class InputApiUnittest(PresubmitTestsBase):
item = presubmit.os.path.join(self.fake_root_dir, item) item = presubmit.os.path.join(self.fake_root_dir, item)
presubmit.os.path.exists(item).AndReturn(True) presubmit.os.path.exists(item).AndReturn(True)
presubmit.os.path.isdir(item).AndReturn(False) presubmit.os.path.isdir(item).AndReturn(False)
presubmit.gcl.GetSVNFileProperty(item, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(item, 'svn:mime-type').AndReturn(None)
self.mox.ReplayAll() self.mox.ReplayAll()
change = presubmit.SvnChange('mychange', '', self.fake_root_dir, files, 0, change = presubmit.SvnChange('mychange', '', self.fake_root_dir, files, 0,
...@@ -852,7 +857,7 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -852,7 +857,7 @@ class InputApiUnittest(PresubmitTestsBase):
def testReadFileStringAccepted(self): def testReadFileStringAccepted(self):
path = presubmit.os.path.join(self.fake_root_dir, 'AA/boo') path = presubmit.os.path.join(self.fake_root_dir, 'AA/boo')
presubmit.gcl.ReadFile(path, 'x').AndReturn(None) presubmit.gclient_utils.FileRead(path, 'x').AndReturn(None)
self.mox.ReplayAll() self.mox.ReplayAll()
change = presubmit.Change('foo', 'foo', self.fake_root_dir, [('M', 'AA')], change = presubmit.Change('foo', 'foo', self.fake_root_dir, [('M', 'AA')],
...@@ -873,7 +878,8 @@ class InputApiUnittest(PresubmitTestsBase): ...@@ -873,7 +878,8 @@ class InputApiUnittest(PresubmitTestsBase):
def testReadFileAffectedFileAccepted(self): def testReadFileAffectedFileAccepted(self):
file = presubmit.AffectedFile('AA/boo', 'M', self.fake_root_dir) file = presubmit.AffectedFile('AA/boo', 'M', self.fake_root_dir)
presubmit.gcl.ReadFile(file.AbsoluteLocalPath(), 'x').AndReturn(None) presubmit.gclient_utils.FileRead(file.AbsoluteLocalPath(), 'x'
).AndReturn(None)
self.mox.ReplayAll() self.mox.ReplayAll()
change = presubmit.Change('foo', 'foo', self.fake_root_dir, [('M', 'AA')], change = presubmit.Change('foo', 'foo', self.fake_root_dir, [('M', 'AA')],
...@@ -955,8 +961,8 @@ class AffectedFileUnittest(PresubmitTestsBase): ...@@ -955,8 +961,8 @@ class AffectedFileUnittest(PresubmitTestsBase):
path = presubmit.os.path.join('foo', 'blat.cc') path = presubmit.os.path.join('foo', 'blat.cc')
presubmit.os.path.exists(path).AndReturn(True) presubmit.os.path.exists(path).AndReturn(True)
presubmit.os.path.isdir(path).AndReturn(False) presubmit.os.path.isdir(path).AndReturn(False)
presubmit.gcl.ReadFile(path).AndReturn('whatever\ncookie') presubmit.gclient_utils.FileRead(path, 'rU').AndReturn('whatever\ncookie')
presubmit.gclient_scm.CaptureSVNInfo(path).AndReturn( presubmit.scm.SVN.CaptureInfo(path).AndReturn(
{'URL': 'svn:/foo/foo/blat.cc'}) {'URL': 'svn:/foo/foo/blat.cc'})
self.mox.ReplayAll() self.mox.ReplayAll()
af = presubmit.SvnAffectedFile('foo/blat.cc', 'M') af = presubmit.SvnAffectedFile('foo/blat.cc', 'M')
...@@ -968,7 +974,7 @@ class AffectedFileUnittest(PresubmitTestsBase): ...@@ -968,7 +974,7 @@ class AffectedFileUnittest(PresubmitTestsBase):
self.failUnless(af.ServerPath() == '') self.failUnless(af.ServerPath() == '')
def testProperty(self): def testProperty(self):
presubmit.gcl.GetSVNFileProperty('foo.cc', 'svn:secret-property' presubmit.scm.SVN.GetFileProperty('foo.cc', 'svn:secret-property'
).AndReturn('secret-property-value') ).AndReturn('secret-property-value')
self.mox.ReplayAll() self.mox.ReplayAll()
affected_file = presubmit.SvnAffectedFile('foo.cc', 'A') affected_file = presubmit.SvnAffectedFile('foo.cc', 'A')
...@@ -980,7 +986,7 @@ class AffectedFileUnittest(PresubmitTestsBase): ...@@ -980,7 +986,7 @@ class AffectedFileUnittest(PresubmitTestsBase):
def testIsDirectoryNotExists(self): def testIsDirectoryNotExists(self):
presubmit.os.path.exists('foo.cc').AndReturn(False) presubmit.os.path.exists('foo.cc').AndReturn(False)
presubmit.gclient_scm.CaptureSVNInfo('foo.cc').AndReturn({}) presubmit.scm.SVN.CaptureInfo('foo.cc').AndReturn({})
self.mox.ReplayAll() self.mox.ReplayAll()
affected_file = presubmit.SvnAffectedFile('foo.cc', 'A') affected_file = presubmit.SvnAffectedFile('foo.cc', 'A')
# Verify cache coherency. # Verify cache coherency.
...@@ -1006,8 +1012,8 @@ class AffectedFileUnittest(PresubmitTestsBase): ...@@ -1006,8 +1012,8 @@ class AffectedFileUnittest(PresubmitTestsBase):
presubmit.os.path.isdir(blat).AndReturn(False) presubmit.os.path.isdir(blat).AndReturn(False)
presubmit.os.path.exists(blob).AndReturn(True) presubmit.os.path.exists(blob).AndReturn(True)
presubmit.os.path.isdir(blob).AndReturn(False) presubmit.os.path.isdir(blob).AndReturn(False)
presubmit.gcl.GetSVNFileProperty(blat, 'svn:mime-type').AndReturn(None) presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None)
presubmit.gcl.GetSVNFileProperty(blob, 'svn:mime-type' presubmit.scm.SVN.GetFileProperty(blob, 'svn:mime-type'
).AndReturn('application/octet-stream') ).AndReturn('application/octet-stream')
self.mox.ReplayAll() self.mox.ReplayAll()
...@@ -1153,10 +1159,10 @@ class CannedChecksUnittest(PresubmitTestsBase): ...@@ -1153,10 +1159,10 @@ class CannedChecksUnittest(PresubmitTestsBase):
input_api1.AffectedSourceFiles(None).AndReturn(files1) input_api1.AffectedSourceFiles(None).AndReturn(files1)
else: else:
input_api1.AffectedFiles(include_deleted=False).AndReturn(files1) input_api1.AffectedFiles(include_deleted=False).AndReturn(files1)
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo/bar.cc'), presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo/bar.cc'),
property).AndReturn(value1) property).AndReturn(value1)
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo.cc'), presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo.cc'),
property).AndReturn(value1) property).AndReturn(value1)
change2 = presubmit.SvnChange('mychange', '', self.fake_root_dir, [], 0, 0) change2 = presubmit.SvnChange('mychange', '', self.fake_root_dir, [], 0, 0)
input_api2 = self.MockInputApi(change2, committing) input_api2 = self.MockInputApi(change2, committing)
files2 = [ files2 = [
...@@ -1168,10 +1174,10 @@ class CannedChecksUnittest(PresubmitTestsBase): ...@@ -1168,10 +1174,10 @@ class CannedChecksUnittest(PresubmitTestsBase):
else: else:
input_api2.AffectedFiles(include_deleted=False).AndReturn(files2) input_api2.AffectedFiles(include_deleted=False).AndReturn(files2)
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo/bar.cc'), presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo/bar.cc'),
property).AndReturn(value2) property).AndReturn(value2)
presubmit.gcl.GetSVNFileProperty(presubmit.normpath('foo.cc'), presubmit.scm.SVN.GetFileProperty(presubmit.normpath('foo.cc'),
property).AndReturn(value2) property).AndReturn(value2)
self.mox.ReplayAll() self.mox.ReplayAll()
results1 = check(input_api1, presubmit.OutputApi, None) results1 = check(input_api1, presubmit.OutputApi, None)
......
...@@ -61,6 +61,7 @@ class RevertMainUnittest(RevertTestsBase): ...@@ -61,6 +61,7 @@ class RevertMainUnittest(RevertTestsBase):
class RevertRevertUnittest(RevertTestsBase): class RevertRevertUnittest(RevertTestsBase):
def setUp(self): def setUp(self):
RevertTestsBase.setUp(self) RevertTestsBase.setUp(self)
self.mox.StubOutWithMock(revert.gclient_scm.scm.SVN, 'CaptureStatus')
def testRevert(self): def testRevert(self):
revert.gcl.GetRepositoryRoot().AndReturn('foo') revert.gcl.GetRepositoryRoot().AndReturn('foo')
...@@ -73,7 +74,7 @@ class RevertRevertUnittest(RevertTestsBase): ...@@ -73,7 +74,7 @@ class RevertRevertUnittest(RevertTestsBase):
}] }]
revert.CaptureSVNLog(['-r', '42', '-v']).AndReturn(entries) revert.CaptureSVNLog(['-r', '42', '-v']).AndReturn(entries)
revert.GetRepoBase().AndReturn('proto://fqdn/repo/') revert.GetRepoBase().AndReturn('proto://fqdn/repo/')
revert.gclient_scm.CaptureSVNStatus(['random_file']).AndReturn([]) revert.gclient_scm.scm.SVN.CaptureStatus(['random_file']).AndReturn([])
revert.gcl.RunShell(['svn', 'up', 'random_file']) revert.gcl.RunShell(['svn', 'up', 'random_file'])
revert.os.path.isdir('random_file').AndReturn(False) revert.os.path.isdir('random_file').AndReturn(False)
status = """--- Reverse-merging r42 into '.': status = """--- Reverse-merging r42 into '.':
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
"""Unit tests for scm.py.""" """Unit tests for scm.py."""
import shutil
import tempfile
from gclient_test import BaseTestCase from gclient_test import BaseTestCase
import scm import scm
from super_mox import mox from super_mox import mox, SuperMoxBaseTestBase
class BaseSCMTestCase(BaseTestCase): class BaseSCMTestCase(BaseTestCase):
...@@ -21,17 +24,14 @@ class RootTestCase(BaseSCMTestCase): ...@@ -21,17 +24,14 @@ class RootTestCase(BaseSCMTestCase):
def testMembersChanged(self): def testMembersChanged(self):
self.mox.ReplayAll() self.mox.ReplayAll()
members = [ members = [
'CaptureGit', 'CaptureGitStatus', 'GIT_COMMAND', 'GIT', 'SVN',
'CaptureSVN', 'CaptureSVNHeadRevision', 'CaptureSVNInfo', 'gclient_utils', 'os', 're', 'subprocess', 'sys', 'tempfile', 'xml',
'CaptureSVNStatus', 'RunSVN', 'RunSVNAndFilterOutput',
'RunSVNAndGetFileList', 'SVN_COMMAND',
'gclient_utils', 'os', 're', 'subprocess', 'sys', 'xml',
] ]
# If this test fails, you should add the relevant test. # If this test fails, you should add the relevant test.
self.compareMembers(scm, members) self.compareMembers(scm, members)
class GitWrapperTestCase(BaseSCMTestCase): class GitWrapperTestCase(SuperMoxBaseTestBase):
sample_git_import = """blob sample_git_import = """blob
mark :1 mark :1
data 6 data 6
...@@ -80,30 +80,44 @@ from :3 ...@@ -80,30 +80,44 @@ from :3
def CreateGitRepo(self, git_import, path): def CreateGitRepo(self, git_import, path):
try: try:
subprocess.Popen(['git', 'init'], stdout=subprocess.PIPE, scm.subprocess.Popen(['git', 'init'],
stderr=subprocess.STDOUT, cwd=path).communicate() stdout=scm.subprocess.PIPE,
except WindowsError: stderr=scm.subprocess.STDOUT,
cwd=path).communicate()
except OSError:
# git is not available, skip this test. # git is not available, skip this test.
return False return False
subprocess.Popen(['git', 'fast-import'], stdin=subprocess.PIPE, scm.subprocess.Popen(['git', 'fast-import'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=scm.subprocess.PIPE,
cwd=path).communicate(input=git_import) stdout=scm.subprocess.PIPE,
subprocess.Popen(['git', 'checkout'], stdout=subprocess.PIPE, stderr=scm.subprocess.STDOUT,
stderr=subprocess.STDOUT, cwd=path).communicate() cwd=path).communicate(input=git_import)
scm.subprocess.Popen(['git', 'checkout'],
stdout=scm.subprocess.PIPE,
stderr=scm.subprocess.STDOUT,
cwd=path).communicate()
return True return True
def setUp(self): def setUp(self):
BaseSCMTestCase.setUp(self) SuperMoxBaseTestBase.setUp(self)
self.args = self.Args() self.args = self.Args()
self.url = 'git://foo' self.url = 'git://foo'
self.root_dir = tempfile.mkdtemp() self.root_dir = tempfile.mkdtemp()
self.relpath = '.' self.relpath = '.'
self.base_path = os.path.join(self.root_dir, self.relpath) self.base_path = scm.os.path.join(self.root_dir, self.relpath)
self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path) self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
def tearDown(self): def tearDown(self):
shutil.rmtree(self.root_dir) shutil.rmtree(self.root_dir)
gclient_test.BaseTestCase.tearDown(self) SuperMoxBaseTestBase.tearDown(self)
def testMembersChanged(self):
self.mox.ReplayAll()
members = [
'COMMAND', 'Capture', 'CaptureStatus',
]
# If this test fails, you should add the relevant test.
self.compareMembers(scm.GIT, members)
class SVNTestCase(BaseSCMTestCase): class SVNTestCase(BaseSCMTestCase):
...@@ -114,7 +128,17 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -114,7 +128,17 @@ class SVNTestCase(BaseSCMTestCase):
self.url = self.Url() self.url = self.Url()
self.relpath = 'asf' self.relpath = 'asf'
def testGetSVNFileInfo(self): def testMembersChanged(self):
self.mox.ReplayAll()
members = [
'COMMAND', 'Capture', 'CaptureHeadRevision', 'CaptureInfo',
'CaptureStatus', 'DiffItem', 'GetFileProperty', 'IsMoved', 'Run',
'RunAndFilterOutput', 'RunAndGetFileList',
]
# If this test fails, you should add the relevant test.
self.compareMembers(scm.SVN, members)
def testGetFileInfo(self):
xml_text = r"""<?xml version="1.0"?> xml_text = r"""<?xml version="1.0"?>
<info> <info>
<entry kind="file" path="%s" revision="14628"> <entry kind="file" path="%s" revision="14628">
...@@ -130,8 +154,8 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -130,8 +154,8 @@ class SVNTestCase(BaseSCMTestCase):
</entry> </entry>
</info> </info>
""" % self.url """ % self.url
self.mox.StubOutWithMock(scm, 'CaptureSVN') self.mox.StubOutWithMock(scm.SVN, 'Capture')
scm.CaptureSVN(['info', '--xml', self.url], '.', True).AndReturn(xml_text) scm.SVN.Capture(['info', '--xml', self.url], '.', True).AndReturn(xml_text)
expected = { expected = {
'URL': 'http://src.chromium.org/svn/trunk/src/chrome/app/d', 'URL': 'http://src.chromium.org/svn/trunk/src/chrome/app/d',
'UUID': None, 'UUID': None,
...@@ -145,10 +169,10 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -145,10 +169,10 @@ class SVNTestCase(BaseSCMTestCase):
'Node Kind': 'file', 'Node Kind': 'file',
} }
self.mox.ReplayAll() self.mox.ReplayAll()
file_info = scm.CaptureSVNInfo(self.url, '.', True) file_info = scm.SVN.CaptureInfo(self.url, '.', True)
self.assertEquals(sorted(file_info.items()), sorted(expected.items())) self.assertEquals(sorted(file_info.items()), sorted(expected.items()))
def testCaptureSvnInfo(self): def testCaptureInfo(self):
xml_text = """<?xml version="1.0"?> xml_text = """<?xml version="1.0"?>
<info> <info>
<entry <entry
...@@ -172,10 +196,10 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -172,10 +196,10 @@ class SVNTestCase(BaseSCMTestCase):
</entry> </entry>
</info> </info>
""" % (self.url, self.root_dir) """ % (self.url, self.root_dir)
self.mox.StubOutWithMock(scm, 'CaptureSVN') self.mox.StubOutWithMock(scm.SVN, 'Capture')
scm.CaptureSVN(['info', '--xml', self.url], '.', True).AndReturn(xml_text) scm.SVN.Capture(['info', '--xml', self.url], '.', True).AndReturn(xml_text)
self.mox.ReplayAll() self.mox.ReplayAll()
file_info = scm.CaptureSVNInfo(self.url, '.', True) file_info = scm.SVN.CaptureInfo(self.url, '.', True)
expected = { expected = {
'URL': self.url, 'URL': self.url,
'UUID': '7b9385f5-0452-0410-af26-ad4892b7a1fb', 'UUID': '7b9385f5-0452-0410-af26-ad4892b7a1fb',
...@@ -185,11 +209,11 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -185,11 +209,11 @@ class SVNTestCase(BaseSCMTestCase):
'Copied From URL': None, 'Copied From URL': None,
'Copied From Rev': None, 'Copied From Rev': None,
'Path': '.', 'Path': '.',
'Node Kind': 'dir', 'Node Kind': 'directory',
} }
self.assertEqual(file_info, expected) self.assertEqual(file_info, expected)
def testCaptureSVNStatus(self): def testCaptureStatus(self):
text =r"""<?xml version="1.0"?> text =r"""<?xml version="1.0"?>
<status> <status>
<target path="."> <target path=".">
...@@ -236,7 +260,7 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -236,7 +260,7 @@ class SVNTestCase(BaseSCMTestCase):
proc.communicate().AndReturn((text, 0)) proc.communicate().AndReturn((text, 0))
self.mox.ReplayAll() self.mox.ReplayAll()
info = scm.CaptureSVNStatus('.') info = scm.SVN.CaptureStatus('.')
expected = [ expected = [
('? ', 'unversionned_file.txt'), ('? ', 'unversionned_file.txt'),
('M ', 'build\\internal\\essential.vsprops'), ('M ', 'build\\internal\\essential.vsprops'),
...@@ -246,14 +270,14 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -246,14 +270,14 @@ class SVNTestCase(BaseSCMTestCase):
] ]
self.assertEquals(sorted(info), sorted(expected)) self.assertEquals(sorted(info), sorted(expected))
def testRunSVN(self): def testRun(self):
param2 = 'bleh' param2 = 'bleh'
scm.gclient_utils.SubprocessCall(['svn', 'foo', 'bar'], scm.gclient_utils.SubprocessCall(['svn', 'foo', 'bar'],
param2).AndReturn(None) param2).AndReturn(None)
self.mox.ReplayAll() self.mox.ReplayAll()
scm.RunSVN(['foo', 'bar'], param2) scm.SVN.Run(['foo', 'bar'], param2)
def testCaptureSVNStatusEmpty(self): def testCaptureStatusEmpty(self):
text = r"""<?xml version="1.0"?> text = r"""<?xml version="1.0"?>
<status> <status>
<target <target
...@@ -268,7 +292,7 @@ class SVNTestCase(BaseSCMTestCase): ...@@ -268,7 +292,7 @@ class SVNTestCase(BaseSCMTestCase):
stdout=scm.subprocess.PIPE).AndReturn(proc) stdout=scm.subprocess.PIPE).AndReturn(proc)
proc.communicate().AndReturn((text, 0)) proc.communicate().AndReturn((text, 0))
self.mox.ReplayAll() self.mox.ReplayAll()
info = scm.CaptureSVNStatus(None) info = scm.SVN.CaptureStatus(None)
self.assertEquals(info, []) self.assertEquals(info, [])
......
...@@ -71,7 +71,7 @@ class SuperMoxBaseTestBase(mox.MoxTestBase): ...@@ -71,7 +71,7 @@ class SuperMoxBaseTestBase(mox.MoxTestBase):
if actual_members != expected_members: if actual_members != expected_members:
diff = ([i for i in actual_members if i not in expected_members] + diff = ([i for i in actual_members if i not in expected_members] +
[i for i in expected_members if i not in actual_members]) [i for i in expected_members if i not in actual_members])
print diff print>>sys.stderr, diff
self.assertEqual(actual_members, expected_members) self.assertEqual(actual_members, expected_members)
def UnMock(self, object, name): def UnMock(self, object, name):
......
...@@ -25,9 +25,9 @@ class TryChangeUnittest(TryChangeTestsBase): ...@@ -25,9 +25,9 @@ class TryChangeUnittest(TryChangeTestsBase):
'GetTryServerSettings', 'GuessVCS', 'GetTryServerSettings', 'GuessVCS',
'HELP_STRING', 'InvalidScript', 'NoTryServerAccess', 'PathDifference', 'HELP_STRING', 'InvalidScript', 'NoTryServerAccess', 'PathDifference',
'RunCommand', 'SCM', 'SVN', 'TryChange', 'USAGE', 'RunCommand', 'SCM', 'SVN', 'TryChange', 'USAGE',
'datetime', 'gcl', 'gclient_scm', 'getpass', 'logging', 'datetime', 'gcl', 'getpass', 'logging',
'optparse', 'os', 'presubmit_support', 'shutil', 'socket', 'subprocess', 'optparse', 'os', 'presubmit_support', 'scm', 'shutil', 'socket',
'sys', 'tempfile', 'upload', 'urllib', 'subprocess', 'sys', 'tempfile', 'upload', 'urllib',
] ]
# If this test fails, you should add the relevant test. # If this test fails, you should add the relevant test.
self.compareMembers(trychange, members) self.compareMembers(trychange, members)
......
...@@ -21,11 +21,11 @@ import tempfile ...@@ -21,11 +21,11 @@ import tempfile
import urllib import urllib
import gcl import gcl
import gclient_scm import scm
import presubmit_support import presubmit_support
import upload import upload
__version__ = '1.1.1' __version__ = '1.1.2'
# Constants # Constants
...@@ -150,50 +150,8 @@ class SVN(SCM): ...@@ -150,50 +150,8 @@ class SVN(SCM):
else: else:
os.chdir(root) os.chdir(root)
diff = [] # Directories will return None so filter them out.
for filename in files: diff = filter(None, [scm.SVN.DiffItem(f) for f in files])
# Use svn info output instead of os.path.isdir because the latter fails
# when the file is deleted.
if gclient_scm.CaptureSVNInfo(filename).get("Node Kind") in (
"dir", "directory"):
continue
# If the user specified a custom diff command in their svn config file,
# then it'll be used when we do svn diff, which we don't want to happen
# since we want the unified diff. Using --diff-cmd=diff doesn't always
# work, since they can have another diff executable in their path that
# gives different line endings. So we use a bogus temp directory as the
# config directory, which gets around these problems.
if sys.platform.startswith("win"):
parent_dir = tempfile.gettempdir()
else:
parent_dir = sys.path[0] # tempdir is not secure.
bogus_dir = os.path.join(parent_dir, "temp_svn_config")
if not os.path.exists(bogus_dir):
os.mkdir(bogus_dir)
# Grabs the diff data.
data = gcl.RunShell(["svn", "diff", "--config-dir", bogus_dir, filename])
# We know the diff will be incorrectly formatted. Fix it.
if gcl.IsSVNMoved(filename):
# The file is "new" in the patch sense. Generate a homebrew diff.
# We can't use ReadFile() since it's not using binary mode.
file_handle = open(filename, 'rb')
file_content = file_handle.read()
file_handle.close()
# Prepend '+' to every lines.
file_content = ['+' + i for i in file_content.splitlines(True)]
nb_lines = len(file_content)
# We need to use / since patch on unix will fail otherwise.
filename = filename.replace('\\', '/')
data = "Index: %s\n" % filename
data += ("============================================================="
"======\n")
# Note: Should we use /dev/null instead?
data += "--- %s\n" % filename
data += "+++ %s\n" % filename
data += "@@ -0,0 +1,%d @@\n" % nb_lines
data += ''.join(file_content)
diff.append(data)
os.chdir(previous_cwd) os.chdir(previous_cwd)
return "".join(diff) return "".join(diff)
...@@ -407,6 +365,7 @@ def GuessVCS(options): ...@@ -407,6 +365,7 @@ def GuessVCS(options):
Returns: Returns:
A SCM instance. Exits if the SCM can't be guessed. A SCM instance. Exits if the SCM can't be guessed.
""" """
__pychecker__ = 'no-returnvalues'
# Subversion has a .svn in all working directories. # Subversion has a .svn in all working directories.
if os.path.isdir('.svn'): if os.path.isdir('.svn'):
logging.info("Guessed VCS = Subversion") logging.info("Guessed VCS = Subversion")
......
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