Reapply 48271 with fix.

"""
Enable automatic command and one-liner doc. Reformat pydoc accordingly.
Add parser as an argument and parse_args hook in preparation to move parse_args at the right place, inside the CMDxx functions.

R.I.P. gclient_test.py
"""
original code review at: http://codereview.chromium.org/2253002

In addition:
- Add more smoke tests: vars, hooks, runhooks
- Bugs filed about misbehavior with git checkout
- Fixed gclient runhooks

TEST=smoke test
BUG=23328

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@48289 0039d316-1c4b-4281-b951-d872f2087c98
experimental/szager/collated-output
maruel@chromium.org 15 years ago
parent 5434b4e20a
commit 3365c913c4

@ -10,7 +10,6 @@ details on the presubmit API built into gcl.
UNIT_TESTS = [
'tests.gcl_unittest',
'tests.gclient_test',
'tests.gclient_scm_test',
'tests.gclient_utils_test',
'tests.presubmit_unittest',

@ -55,7 +55,7 @@ Hooks
]
"""
__version__ = "0.3.7"
__version__ = "0.4"
import errno
import logging
@ -73,22 +73,6 @@ import gclient_scm
import gclient_utils
from third_party.repo.progress import Progress
# default help text
DEFAULT_USAGE_TEXT = (
"""%prog <subcommand> [options] [--] [SCM options/args...]
a wrapper for managing a set of svn client modules and/or git repositories.
Version """ + __version__ + """
Options and extra arguments can be passed to invoked SCM commands by
appending them to the command line. Note that if the first such
appended option starts with a dash (-) then the options must be
preceded by -- to distinguish them from gclient options.
For additional help on a subcommand or examples of usage, try
%prog help <subcommand>
%prog help files
""")
def attr(attr, data):
"""Sets an attribute on a function."""
@ -470,6 +454,8 @@ solutions = [
def _RunHookAction(self, hook_dict, matching_file_list):
"""Runs the action from a single hook.
"""
logging.info(hook_dict)
logging.info(matching_file_list)
command = hook_dict['action'][:]
if command[0] == 'python':
# If the hook specified "python" as the first item, the action is a
@ -813,19 +799,15 @@ solutions = [
print(snapclient._config_content)
## gclient commands.
#### gclient commands.
def CMDcleanup(parser, options, args):
"""Clean up all working copies, using 'svn cleanup' for each module.
Additional options and args may be passed to 'svn cleanup'.
def CMDcleanup(parser, args):
"""Cleans up all working copies.
usage: cleanup [options] [--] [svn cleanup args/options]
Valid options:
--verbose : output additional diagnostics
Mostly svn-specific. Simply runs 'svn cleanup' for each module.
"""
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
@ -836,32 +818,23 @@ Valid options:
return client.RunOnDeps('cleanup', args)
def CMDconfig(parser, options, args):
@attr('usage', '[url] [safesync url]')
def CMDconfig(parser, args):
"""Create a .gclient file in the current directory.
This specifies the configuration for further commands. After update/sync,
This specifies the configuration for further commands. After update/sync,
top-level DEPS files in each module are read to determine dependent
modules to operate on as well. If optional [url] parameter is
modules to operate on as well. If optional [url] parameter is
provided, then configuration is read from a specified Subversion server
URL. Otherwise, a --spec option must be provided. A --name option overrides
the default name for the solutions.
usage: config [option | url] [safesync url]
Valid options:
--name path : alternate relative path for the solution
--spec=GCLIENT_SPEC : contents of .gclient are read from string parameter.
*Note that due to Cygwin/Python brokenness, it
probably can't contain any newlines.*
Examples:
gclient config https://gclient.googlecode.com/svn/trunk/gclient
configure a new client to check out gclient.py tool sources
gclient config --name tools https://gclient.googlecode.com/svn/trunk/gclient
gclient config --spec='solutions=[{"name":"gclient",
'"url":"https://gclient.googlecode.com/svn/trunk/gclient",'
'"custom_deps":{}}]'
URL.
"""
parser.add_option("--spec",
help="create a gclient file containing the provided "
"string. Due to Cygwin/Python brokenness, it "
"probably can't contain any newlines.")
parser.add_option("--name",
help="overrides the default name for the solution")
(options, args) = parser.parse_args(args)
if len(args) < 1 and not options.spec:
raise gclient_utils.Error("required argument missing; see 'gclient help "
"config'")
@ -886,8 +859,9 @@ Examples:
return 0
def CMDexport(parser, options, args):
def CMDexport(parser, args):
"""Wrapper for svn export for all managed directories."""
(options, args) = parser.parse_args(args)
if len(args) != 1:
raise gclient_utils.Error("Need directory name")
client = GClient.LoadCurrentConfig(options)
@ -902,30 +876,19 @@ def CMDexport(parser, options, args):
return client.RunOnDeps('export', args)
def CMDpack(parser, options, args):
@attr('epilog', """Example:
gclient pack > patch.txt
generate simple patch for configured client and dependences
""")
def CMDpack(parser, args):
"""Generate a patch which can be applied at the root of the tree.
Internally, runs 'svn diff' on each checked out module and
Internally, runs 'svn diff'/'git diff' on each checked out module and
dependencies, and performs minimal postprocessing of the output. The
resulting patch is printed to stdout and can be applied to a freshly
checked out tree via 'patch -p0 < patchfile'. Additional args and
options to 'svn diff' can be passed after gclient options.
usage: pack [options] [--] [svn args/options]
Valid options:
--verbose : output additional diagnostics
Examples:
gclient pack > patch.txt
generate simple patch for configured client and dependences
gclient pack -- -x -b > patch.txt
generate patch using 'svn diff -x -b' to suppress
whitespace-only differences
gclient pack -- -r HEAD -x -b > patch.txt
generate patch, diffing each file versus the latest version of
each module
checked out tree via 'patch -p0 < patchfile'.
"""
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
@ -936,17 +899,9 @@ Examples:
return client.RunOnDeps('pack', args)
def CMDstatus(parser, options, args):
"""Show the modification status of for every dependencies.
Additional options and args may be passed to 'svn status'.
usage: status [options] [--] [svn diff args/options]
Valid options:
--verbose : output additional diagnostics
--nohooks : don't run the hooks after the update is complete
"""
def CMDstatus(parser, args):
"""Show modification status for every dependencies."""
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
@ -957,31 +912,7 @@ Valid options:
return client.RunOnDeps('status', args)
def CMDsync(parser, options, args):
"""Checkout/update the modules specified by the gclient configuration.
Unless --revision is specified, then the latest revision of the root solutions
is checked out, with dependent submodule versions updated according to DEPS
files. If --revision is specified, then the given revision is used in place
of the latest, either for a single solution or for all solutions.
Unless the --force option is provided, solutions and modules whose
local revision matches the one to update (i.e., they have not changed
in the repository) are *not* modified. Unless --nohooks is provided,
the hooks are run. See 'help config' for more information.
usage: gclient sync [options] [--] [SCM update options/args]
Valid options:
--force : force update even for unchanged modules
--nohooks : don't run the hooks after the update is complete
--revision SOLUTION@REV : update given solution to specified revision
--deps PLATFORM(S) : sync deps for the given platform(s), or 'all'
--verbose : output additional diagnostics
--head : update to latest revision, instead of last good
revision
--reset : resets any local changes before updating (git only)
Examples:
@attr('epilog', """Examples:
gclient sync
update files from SCM according to current configuration,
*for modules which have changed since last update or sync*
@ -990,7 +921,33 @@ Examples:
all modules (useful for recovering files deleted from local copy)
gclient sync --revision src@31000
update src directory to r31000
"""
""")
def CMDsync(parser, args):
"""Checkout/update all modules."""
parser.add_option("--force", action="store_true",
help="force update even for unchanged modules")
parser.add_option("--nohooks", action="store_true",
help="don't run hooks after the update is complete")
parser.add_option("-r", "--revision", action="append",
dest="revisions", metavar="REV", default=[],
help="update given solution to specified revision, "
"can be used multiple times for each solution, "
"e.g. -r src@123, -r internal@32")
parser.add_option("--head", action="store_true",
help="skips any safesync_urls specified in "
"configured solutions and sync to head instead")
parser.add_option("--delete_unversioned_trees", action="store_true",
help="delete any unexpected unversioned trees "
"that are in the checkout")
parser.add_option("--reset", action="store_true",
help="resets any local changes before updating (git only)")
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST",
help="sync deps for the specified (comma-separated) "
"platform(s); 'all' will sync all platforms")
parser.add_option("--manually_grab_svn_rev", action="store_true",
help="Skip svn up whenever possible by requesting "
"actual HEAD revision from the repository")
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
@ -1023,31 +980,13 @@ Examples:
return client.RunOnDeps('update', args)
def CMDupdate(parser, options, args):
def CMDupdate(parser, args):
"""Alias for the sync command. Deprecated."""
return CMDsync(parser, options, args)
def CMDdiff(parser, options, args):
"""Display the differences between two revisions of modules.
return CMDsync(parser, args)
(Does 'svn diff' for each checked out module and dependences.)
Additional args and options to 'svn diff' can be passed after
gclient options.
usage: diff [options] [--] [svn args/options]
Valid options:
--verbose : output additional diagnostics
Examples:
gclient diff
simple 'svn diff' for configured client and dependences
gclient diff -- -x -b
use 'svn diff -x -b' to suppress whitespace-only differences
gclient diff -- -r HEAD -x -b
diff versus the latest version of each module
"""
def CMDdiff(parser, args):
"""Displays local diff for every dependencies."""
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
@ -1058,24 +997,24 @@ Examples:
return client.RunOnDeps('diff', args)
def CMDrevert(parser, options, args):
"""Revert every file in every managed directory in the client view."""
def CMDrevert(parser, args):
"""Revert all modifications in every dependencies."""
parser.add_option("--nohooks", action="store_true",
help="don't run hooks after the revert is complete")
(options, args) = parser.parse_args(args)
# --force is implied.
options.force = True
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
return client.RunOnDeps('revert', args)
def CMDrunhooks(parser, options, args):
"""Runs hooks for files that have been modified in the local working copy.
Implies --force.
usage: runhooks [options]
Valid options:
--verbose : output additional diagnostics
"""
def CMDrunhooks(parser, args):
"""Runs hooks for files that have been modified in the local working copy."""
parser.add_option("--force", action="store_true", default=True,
help="Deprecated. No effect.")
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
@ -1084,18 +1023,16 @@ Valid options:
# client dict, but more legible, and it might contain helpful comments.
print(client.ConfigContent())
options.force = True
options.nohooks = False
return client.RunOnDeps('runhooks', args)
def CMDrevinfo(parser, options, args):
"""Outputs defails for every dependencies.
This includes source path, server URL and revision information for every
dependency in all solutions.
usage: revinfo [options]
"""
__pychecker__ = 'unusednames=args'
def CMDrevinfo(parser, args):
"""Outputs details for every dependencies."""
parser.add_option("--snapshot", action="store_true",
help="create a snapshot file of the current "
"version of all repositories")
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
@ -1103,70 +1040,45 @@ usage: revinfo [options]
return 0
def CMDhelp(parser, options, args):
"""Prints general help or command-specific documentation."""
def Command(name):
return getattr(sys.modules[__name__], 'CMD' + name, None)
def CMDhelp(parser, args):
"""Prints list of commands or help for a specific command."""
(options, args) = parser.parse_args(args)
if len(args) == 1:
command = Command(args[0])
if command:
print getattr(sys.modules[__name__], 'CMD' + args[0]).__doc__
return 0
parser.usage = (DEFAULT_USAGE_TEXT + '\nCommands are:\n' + '\n'.join([
' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip())
for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]))
return Main(args + ['--help'])
parser.print_help()
return 0
def Command(command):
return getattr(sys.modules[__name__], 'CMD' + command, CMDhelp)
def GenUsage(parser, command):
"""Modify an OptParse object with the function's documentation."""
obj = Command(command)
if command == 'help':
command = '<command>'
# OptParser.description prefer nicely non-formatted strings.
parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__)
usage = getattr(obj, 'usage', '')
parser.set_usage('%%prog %s [options] %s' % (command, usage))
parser.epilog = getattr(obj, 'epilog', None)
def Main(argv):
parser = optparse.OptionParser(usage=DEFAULT_USAGE_TEXT,
version='%prog ' + __version__)
"""Doesn't parse the arguments here, just find the right subcommand to
execute."""
# Do it late so all commands are listed.
CMDhelp.usage = ('\n\nCommands are:\n' + '\n'.join([
' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip())
for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]))
parser = optparse.OptionParser(version='%prog ' + __version__)
parser.add_option("-v", "--verbose", action="count", default=0,
help="Produces additional output for diagnostics. Can be "
"used up to three times for more logging info.")
parser.add_option("--gclientfile", metavar="FILENAME", dest="config_filename",
default=os.environ.get("GCLIENT_FILE", ".gclient"),
help="Specify an alternate .gclient file")
# The other options will be moved eventually.
parser.add_option("--force", action="store_true",
help="(update/sync only) force update even "
"for modules which haven't changed")
parser.add_option("--nohooks", action="store_true",
help="(update/sync/revert only) prevent the hooks "
"from running")
parser.add_option("--revision", action="append", dest="revisions",
metavar="REV", default=[],
help="(update/sync only) sync to a specific "
"revision, can be used multiple times for "
"each solution, e.g. --revision=src@123, "
"--revision=internal@32")
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST",
help="(update/sync only) sync deps for the "
"specified (comma-separated) platform(s); "
"'all' will sync all platforms")
parser.add_option("--reset", action="store_true",
help="(update/sync only) resets any local changes "
"before updating (git only)")
parser.add_option("--spec",
help="(config only) create a gclient file "
"containing the provided string")
parser.add_option("--manually_grab_svn_rev", action="store_true",
help="Skip svn up whenever possible by requesting "
"actual HEAD revision from the repository")
parser.add_option("--head", action="store_true",
help="skips any safesync_urls specified in "
"configured solutions")
parser.add_option("--delete_unversioned_trees", action="store_true",
help="on update, delete any unexpected "
"unversioned trees that are in the checkout")
parser.add_option("--snapshot", action="store_true",
help="(revinfo only), create a snapshot file "
"of the current version of all repositories")
parser.add_option("--name",
help="specify alternate relative solution path")
# Integrate standard options processing.
old_parser = parser.parse_args
def Parse(args):
@ -1176,22 +1088,22 @@ def Main(argv):
elif options.verbose > 2:
logging.basicConfig(level=logging.DEBUG)
options.entries_filename = options.config_filename + "_entries"
if not hasattr(options, 'revisions'):
# GClient.RunOnDeps expects it even if not applicable.
options.revisions = []
return (options, args)
parser.parse_args = Parse
# We don't want wordwrapping in epilog (usually examples)
parser.format_epilog = lambda _: parser.epilog or ''
if not len(argv):
argv = ['help']
# Add manual support for --version as first argument.
if argv[0] == '--version':
parser.print_version()
return 0
# Add manual support for --help as first argument.
if argv[0] == '--help':
argv[0] = 'help'
options, args = parser.parse_args(argv[1:])
return Command(argv[0])(parser, options, args)
if argv:
command = Command(argv[0])
if command:
# "fix" the usage and the description now that we know the subcommand.
GenUsage(parser, argv[0])
return command(parser, argv[1:])
# Not a known command. Default to help.
GenUsage(parser, 'help')
return CMDhelp(parser, argv)
if "__main__" == __name__:

@ -205,14 +205,19 @@ class FakeRepos(object):
# - versioned and unversioned reference
# - relative and full reference
# - deps_os
# TODO(maruel):
# - var
# - hooks
# TODO(maruel):
# - File
# - $matching_files
# - use_relative_paths
self._commit_svn(file_system(1, """
vars = {
'DummyVariable': 'third_party',
}
deps = {
'src/other': 'svn://%(host)s/svn/trunk/other',
'src/third_party/fpp': '/trunk/third_party/foo',
'src/third_party/fpp': '/trunk/' + Var('DummyVariable') + '/foo',
}
deps_os = {
'mac': {
@ -225,6 +230,21 @@ deps = {
'src/other': 'svn://%(host)s/svn/trunk/other',
'src/third_party/foo': '/trunk/third_party/foo@1',
}
# I think this is wrong to have the hooks run from the base of the gclient
# checkout. It's maybe a bit too late to change that behavior.
hooks = [
{
'pattern': '.',
'action': ['python', '-c',
'open(\\'src/hooked1\\', \\'w\\').write(\\'hooked1\\')'],
},
{
# Should not be run.
'pattern': 'nonexistent',
'action': ['python', '-c',
'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'],
},
]
""" % { 'host': '127.0.0.1' }))
def setUpGIT(self):
@ -241,15 +261,20 @@ deps = {
# - versioned and unversioned reference
# - relative and full reference
# - deps_os
# TODO(maruel):
# - var
# - hooks
# TODO(maruel):
# - File
# - $matching_files
# - use_relative_paths
self._commit_git('repo_1', {
'DEPS': """
vars = {
'DummyVariable': 'repo',
}
deps = {
'src/repo2': 'git://%(host)s/git/repo_2',
'src/repo2/repo3': '/repo_3',
'src/repo2/repo3': '/' + Var('DummyVariable') + '_3',
}
deps_os = {
'mac': {
@ -289,6 +314,21 @@ deps = {
'src/repo2': 'git://%(host)s/git/repo_2@%(hash)s',
'src/repo2/repo_renamed': '/repo_3',
}
# I think this is wrong to have the hooks run from the base of the gclient
# checkout. It's maybe a bit too late to change that behavior.
hooks = [
{
'pattern': '.',
'action': ['python', '-c',
'open(\\'src/hooked1\\', \\'w\\').write(\\'hooked1\\')'],
},
{
# Should not be run.
'pattern': 'nonexistent',
'action': ['python', '-c',
'open(\\'src/hooked2\\', \\'w\\').write(\\'hooked2\\')'],
},
]
""" % { 'host': '127.0.0.1', 'hash': self.git_hashes['repo_2'][0][0] },
'origin': "git/repo_1@2\n"
})

@ -13,6 +13,7 @@ This test assumes GClientSmokeBase.URL_BASE is valid.
import logging
import os
import pprint
import re
import shutil
import subprocess
import sys
@ -106,15 +107,20 @@ class GClientSmokeBase(unittest.TestCase):
(stdout, stderr) = process.communicate()
return (stdout, stderr, process.returncode)
def checkString(self, expected, result):
if expected != result:
# Strip the begining
while expected and result and expected[0] == result[0]:
expected = expected[1:]
result = result[1:]
# The exception trace makes it hard to read so dump it too.
if '\n' in result:
print result
self.assertEquals(expected, result)
def check(self, expected, results):
def checkString(expected, result):
if expected != result:
while expected and result and expected[0] == result[0]:
expected = expected[1:]
result = result[1:]
self.assertEquals(expected, result)
checkString(expected[0], results[0])
checkString(expected[1], results[1])
self.checkString(expected[0], results[0])
self.checkString(expected[1], results[1])
self.assertEquals(expected[2], results[2])
def assertTree(self, tree):
@ -131,7 +137,7 @@ class GClientSmoke(GClientSmokeBase):
def testCommands(self):
"""This test is to make sure no new command was added."""
result = self.gclient(['help'])
self.assertEquals(3189, len(result[0]))
self.assertEquals(1197, len(result[0]))
self.assertEquals(0, len(result[1]))
self.assertEquals(0, result[2])
@ -152,12 +158,13 @@ class GClientSmoke(GClientSmokeBase):
class GClientSmokeSVN(GClientSmokeBase):
"""sync is the most important command. Hence test it more."""
def testSync(self):
"""Test pure gclient svn checkout, example of Chromium checkout"""
self.gclient(['config', self.svn_base + 'trunk/src/'])
# Test unversioned checkout.
results = self.gclient(['sync', '--deps', 'mac'])
logging.debug(results[0])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
self.assertEquals(17, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_svn_tree(
(join('trunk', 'src'), 'src', FAKE.svn_revs[-1]),
@ -165,13 +172,19 @@ class GClientSmokeSVN(GClientSmokeBase):
FAKE.svn_revs[1]),
(join('trunk', 'other'), join('src', 'other'), FAKE.svn_revs[2]),
)
tree[join('src', 'hooked1')] = 'hooked1'
self.assertTree(tree)
# Manually remove hooked1 before synching to make sure it's not recreated.
os.remove(join(self.root_dir, 'src', 'hooked1'))
# Test incremental versioned sync: sync backward.
results = self.gclient(['sync', '--revision', 'src@1', '--deps', 'mac',
'--delete_unversioned_trees'])
logging.debug(results[0])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
self.assertEquals(19, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_svn_tree(
(join('trunk', 'src'), 'src', FAKE.svn_revs[1]),
@ -186,7 +199,9 @@ class GClientSmokeSVN(GClientSmokeBase):
# Test incremental sync: delete-unversioned_trees isn't there.
results = self.gclient(['sync', '--deps', 'mac'])
logging.debug(results[0])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
self.assertEquals(21, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_svn_tree(
(join('trunk', 'src'), 'src', FAKE.svn_revs[-1]),
@ -199,28 +214,35 @@ class GClientSmokeSVN(GClientSmokeBase):
join('src', 'third_party', 'prout'),
FAKE.svn_revs[2]),
)
tree[join('src', 'hooked1')] = 'hooked1'
self.assertTree(tree)
def testRevertAndStatus(self):
self.gclient(['config', self.svn_base + 'trunk/src/'])
results = self.gclient(['sync', '--deps', 'mac'])
# Tested in testSync.
self.gclient(['sync', '--deps', 'mac'])
write(join(self.root_dir, 'src', 'third_party', 'foo', 'hi'), 'Hey!')
results = self.gclient(['status'])
out = results[0].splitlines(False)
self.assertEquals(7, len(out))
self.assertEquals(out[0], '')
self.assertTrue(out[1].startswith('________ running \'svn status\' in \''))
self.assertEquals(out[2], '? other')
self.assertEquals(out[3], '? third_party/foo')
self.assertEquals(out[4], '')
self.assertTrue(out[5].startswith('________ running \'svn status\' in \''))
self.assertEquals(out[6], '? hi')
self.assertEquals(out[3], '? hooked1')
self.assertEquals(out[4], '? third_party/foo')
self.assertEquals(out[5], '')
self.assertTrue(out[6].startswith('________ running \'svn status\' in \''))
self.assertEquals(out[7], '? hi')
self.assertEquals(8, len(out))
self.assertEquals('', results[1])
self.assertEquals(0, results[2])
# Revert implies --force implies running hooks without looking at pattern
# matching.
results = self.gclient(['revert'])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
self.assertEquals(22, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_svn_tree(
(join('trunk', 'src'), 'src', FAKE.svn_revs[-1]),
@ -228,26 +250,50 @@ class GClientSmokeSVN(GClientSmokeBase):
FAKE.svn_revs[1]),
(join('trunk', 'other'), join('src', 'other'), FAKE.svn_revs[2]),
)
tree[join('src', 'hooked1')] = 'hooked1'
tree[join('src', 'hooked2')] = 'hooked2'
self.assertTree(tree)
results = self.gclient(['status'])
out = results[0].splitlines(False)
self.assertEquals(4, len(out))
self.assertEquals(out[0], '')
self.assertTrue(out[1].startswith('________ running \'svn status\' in \''))
self.assertEquals(out[2], '? other')
self.assertEquals(out[3], '? third_party/foo')
self.assertEquals('', results[1])
self.assertEquals(out[3], '? hooked1')
self.assertEquals(out[4], '? hooked2')
self.assertEquals(out[5], '? third_party/foo')
self.assertEquals(6, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
def testRunHooks(self):
self.gclient(['config', self.svn_base + 'trunk/src/'])
self.gclient(['sync', '--deps', 'mac'])
results = self.gclient(['runhooks'])
out = results[0].splitlines(False)
self.assertEquals(4, len(out))
self.assertEquals(out[0], '')
self.assertTrue(re.match(r'^________ running \'.*?python -c '
r'open\(\'src/hooked1\', \'w\'\)\.write\(\'hooked1\'\)\' in \'.*',
out[1]))
self.assertEquals(out[2], '')
# runhooks runs all hooks even if not matching by design.
self.assertTrue(re.match(r'^________ running \'.*?python -c '
r'open\(\'src/hooked2\', \'w\'\)\.write\(\'hooked2\'\)\' in \'.*',
out[3]))
self.checkString('', results[1])
self.assertEquals(0, results[2])
class GClientSmokeGIT(GClientSmokeBase):
def testSyncGit(self):
"""Test pure gclient git checkout, example of Chromium OS checkout"""
def testSync(self):
self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
# Test unversioned checkout.
results = self.gclient(['sync', '--deps', 'mac'])
logging.debug(results[0])
out = results[0].splitlines(False)
# TODO(maruel): http://crosbug.com/3582 hooks run even if not matching, must
# add sync parsing to get the list of updated files.
self.assertEquals(13, len(out))
self.assertTrue(results[1].startswith('Switched to a new branch \''))
self.assertEquals(0, results[2])
tree = mangle_git_tree(
@ -255,14 +301,21 @@ class GClientSmokeGIT(GClientSmokeBase):
(join('src', 'repo2'), FAKE.git_hashes['repo_2'][0][1]),
(join('src', 'repo2', 'repo_renamed'), FAKE.git_hashes['repo_3'][1][1]),
)
tree[join('src', 'hooked1')] = 'hooked1'
tree[join('src', 'hooked2')] = 'hooked2'
self.assertTree(tree)
# Manually remove hooked1 before synching to make sure it's not recreated.
os.remove(join(self.root_dir, 'src', 'hooked1'))
# Test incremental versioned sync: sync backward.
results = self.gclient(['sync', '--revision',
'src@' + FAKE.git_hashes['repo_1'][0][0],
'--deps', 'mac', '--delete_unversioned_trees'])
logging.debug(results[0])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
self.assertEquals(20, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_git_tree(
('src', FAKE.git_hashes['repo_1'][0][1]),
@ -270,11 +323,14 @@ class GClientSmokeGIT(GClientSmokeBase):
(join('src', 'repo2', 'repo3'), FAKE.git_hashes['repo_3'][1][1]),
(join('src', 'repo4'), FAKE.git_hashes['repo_4'][1][1]),
)
tree[join('src', 'hooked2')] = 'hooked2'
self.assertTree(tree)
# Test incremental sync: delete-unversioned_trees isn't there.
results = self.gclient(['sync', '--deps', 'mac'])
logging.debug(results[0])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
self.assertEquals(25, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_git_tree(
('src', FAKE.git_hashes['repo_1'][1][1]),
@ -283,42 +339,66 @@ class GClientSmokeGIT(GClientSmokeBase):
(join('src', 'repo2', 'repo_renamed'), FAKE.git_hashes['repo_3'][1][1]),
(join('src', 'repo4'), FAKE.git_hashes['repo_4'][1][1]),
)
tree[join('src', 'hooked1')] = 'hooked1'
tree[join('src', 'hooked2')] = 'hooked2'
self.assertTree(tree)
def testRevertAndStatus(self):
"""TODO(maruel): Remove this line once this test is fixed."""
self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
results = self.gclient(['sync', '--deps', 'mac'])
# Tested in testSync.
self.gclient(['sync', '--deps', 'mac'])
write(join(self.root_dir, 'src', 'repo2', 'hi'), 'Hey!')
results = self.gclient(['status'])
out = results[0].splitlines(False)
# TODO(maruel): THIS IS WRONG.
# TODO(maruel): http://crosbug.com/3584 It should output the unversioned
# files.
self.assertEquals(0, len(out))
# Revert implies --force implies running hooks without looking at pattern
# matching.
results = self.gclient(['revert'])
self.assertEquals('', results[1])
out = results[0].splitlines(False)
# TODO(maruel): http://crosbug.com/3583 It just runs the hooks right now.
self.assertEquals(7, len(out))
self.checkString('', results[1])
self.assertEquals(0, results[2])
tree = mangle_git_tree(
('src', FAKE.git_hashes['repo_1'][1][1]),
(join('src', 'repo2'), FAKE.git_hashes['repo_2'][0][1]),
(join('src', 'repo2', 'repo_renamed'), FAKE.git_hashes['repo_3'][1][1]),
)
# TODO(maruel): THIS IS WRONG.
# TODO(maruel): http://crosbug.com/3583 This file should have been removed.
tree[join('src', 'repo2', 'hi')] = 'Hey!'
tree[join('src', 'hooked1')] = 'hooked1'
tree[join('src', 'hooked2')] = 'hooked2'
self.assertTree(tree)
results = self.gclient(['status'])
out = results[0].splitlines(False)
# TODO(maruel): THIS IS WRONG.
# TODO(maruel): http://crosbug.com/3584 It should output the unversioned
# files.
self.assertEquals(0, len(out))
class GClientSmokeRevInfo(GClientSmokeBase):
"""revert is the second most important command. Hence test it more."""
def setUp(self):
GClientSmokeBase.setUp(self)
self.gclient(['config', self.URL_BASE])
def testRunHooks(self):
self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
self.gclient(['sync', '--deps', 'mac'])
results = self.gclient(['runhooks'])
logging.debug(results[0])
out = results[0].splitlines(False)
self.assertEquals(4, len(out))
self.assertEquals(out[0], '')
self.assertTrue(re.match(r'^________ running \'.*?python -c '
r'open\(\'src/hooked1\', \'w\'\)\.write\(\'hooked1\'\)\' in \'.*',
out[1]))
self.assertEquals(out[2], '')
# runhooks runs all hooks even if not matching by design.
self.assertTrue(re.match(r'^________ running \'.*?python -c '
r'open\(\'src/hooked2\', \'w\'\)\.write\(\'hooked2\'\)\' in \'.*',
out[3]))
self.checkString('', results[1])
self.assertEquals(0, results[2])
if __name__ == '__main__':

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save