From 5434b4e20addb1fdfddc0b578ad8fd4b3b6de346 Mon Sep 17 00:00:00 2001 From: "maruel@chromium.org" Date: Wed, 26 May 2010 13:38:24 +0000 Subject: [PATCH] Reverting last change, broke update. git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@48272 0039d316-1c4b-4281-b951-d872f2087c98 --- PRESUBMIT.py | 1 + gclient.py | 329 ++++++---- tests/gclient_smoketest.py | 2 +- tests/gclient_test.py | 1273 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1485 insertions(+), 120 deletions(-) create mode 100755 tests/gclient_test.py diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 59b25bd45..07c4842ca 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -10,6 +10,7 @@ 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', diff --git a/gclient.py b/gclient.py index 8704be333..e7672a14b 100644 --- a/gclient.py +++ b/gclient.py @@ -55,7 +55,7 @@ Hooks ] """ -__version__ = "0.4" +__version__ = "0.3.7" import errno import logging @@ -73,6 +73,22 @@ import gclient_scm import gclient_utils from third_party.repo.progress import Progress +# default help text +DEFAULT_USAGE_TEXT = ( +"""%prog [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 + %prog help files +""") + def attr(attr, data): """Sets an attribute on a function.""" @@ -797,15 +813,19 @@ 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. -def CMDcleanup(parser, args): - """Cleans up all working copies. +Additional options and args may be passed to 'svn cleanup'. -Mostly svn-specific. Simply runs 'svn cleanup' for each module. +usage: cleanup [options] [--] [svn cleanup args/options] + +Valid options: + --verbose : output additional diagnostics """ - (options, args) = parser.parse_args(args) client = GClient.LoadCurrentConfig(options) if not client: raise gclient_utils.Error("client not configured; see 'gclient config'") @@ -816,23 +836,32 @@ Mostly svn-specific. Simply runs 'svn cleanup' for each module. return client.RunOnDeps('cleanup', args) -@attr('usage', '[url] [safesync url]') -def CMDconfig(parser, args): +def CMDconfig(parser, options, 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. +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":{}}]' """ - 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'") @@ -857,9 +886,8 @@ URL. return 0 -def CMDexport(parser, args): +def CMDexport(parser, options, 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) @@ -874,19 +902,30 @@ def CMDexport(parser, args): return client.RunOnDeps('export', args) -@attr('epilog', """Example: - gclient pack > patch.txt - generate simple patch for configured client and dependences -""") -def CMDpack(parser, args): +def CMDpack(parser, options, args): """Generate a patch which can be applied at the root of the tree. -Internally, runs 'svn diff'/'git diff' on each checked out module and +Internally, runs 'svn 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'. +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 """ - (options, args) = parser.parse_args(args) client = GClient.LoadCurrentConfig(options) if not client: raise gclient_utils.Error("client not configured; see 'gclient config'") @@ -897,9 +936,17 @@ checked out tree via 'patch -p0 < patchfile'. return client.RunOnDeps('pack', args) -def CMDstatus(parser, args): - """Show modification status for every dependencies.""" - (options, args) = parser.parse_args(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 +""" client = GClient.LoadCurrentConfig(options) if not client: raise gclient_utils.Error("client not configured; see 'gclient config'") @@ -910,7 +957,31 @@ def CMDstatus(parser, args): return client.RunOnDeps('status', args) -@attr('epilog', """Examples: +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: gclient sync update files from SCM according to current configuration, *for modules which have changed since last update or sync* @@ -919,33 +990,7 @@ def CMDstatus(parser, args): 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: @@ -978,13 +1023,31 @@ def CMDsync(parser, args): return client.RunOnDeps('update', args) -def CMDupdate(parser, args): +def CMDupdate(parser, options, args): """Alias for the sync command. Deprecated.""" - return CMDsync(parser, args) + return CMDsync(parser, options, args) + + +def CMDdiff(parser, options, args): + """Display the differences between two revisions of modules. -def CMDdiff(parser, args): - """Displays local diff for every dependencies.""" - (options, args) = parser.parse_args(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 +""" client = GClient.LoadCurrentConfig(options) if not client: raise gclient_utils.Error("client not configured; see 'gclient config'") @@ -995,24 +1058,24 @@ def CMDdiff(parser, args): return client.RunOnDeps('diff', args) -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 +def CMDrevert(parser, options, args): + """Revert every file in every managed directory in the client view.""" 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, 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) +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 +""" client = GClient.LoadCurrentConfig(options) if not client: raise gclient_utils.Error("client not configured; see 'gclient config'") @@ -1024,12 +1087,15 @@ def CMDrunhooks(parser, args): return client.RunOnDeps('runhooks', 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) +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' client = GClient.LoadCurrentConfig(options) if not client: raise gclient_utils.Error("client not configured; see 'gclient config'") @@ -1037,45 +1103,70 @@ def CMDrevinfo(parser, args): return 0 -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) +def CMDhelp(parser, options, args): + """Prints general help or command-specific documentation.""" if len(args) == 1: - return Main(args + ['--help']) + 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')])) parser.print_help() return 0 -def GenUsage(parser, command): - """Modify an OptParse object with the function's documentation.""" - obj = Command(command) - if command == 'help': - 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 Command(command): + return getattr(sys.modules[__name__], 'CMD' + command, CMDhelp) def Main(argv): - """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 = optparse.OptionParser(usage=DEFAULT_USAGE_TEXT, + 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): @@ -1085,22 +1176,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 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 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 "__main__" == __name__: diff --git a/tests/gclient_smoketest.py b/tests/gclient_smoketest.py index bc9907cde..7f3491f06 100755 --- a/tests/gclient_smoketest.py +++ b/tests/gclient_smoketest.py @@ -131,7 +131,7 @@ class GClientSmoke(GClientSmokeBase): def testCommands(self): """This test is to make sure no new command was added.""" result = self.gclient(['help']) - self.assertEquals(1197, len(result[0])) + self.assertEquals(3189, len(result[0])) self.assertEquals(0, len(result[1])) self.assertEquals(0, result[2]) diff --git a/tests/gclient_test.py b/tests/gclient_test.py new file mode 100755 index 000000000..c81a9de57 --- /dev/null +++ b/tests/gclient_test.py @@ -0,0 +1,1273 @@ +#!/usr/bin/python +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Unit tests for gclient.py.""" + +# Fixes include path. +from super_mox import mox, SuperMoxTestBase + +import gclient + + +class BaseTestCase(SuperMoxTestBase): + # Like unittest's assertRaises, but checks for Gclient.Error. + def assertRaisesError(self, msg, fn, *args, **kwargs): + try: + fn(*args, **kwargs) + except gclient.gclient_utils.Error, e: + self.assertEquals(e.args[0], msg) + else: + self.fail('%s not raised' % msg) + + +class GClientBaseTestCase(BaseTestCase): + def Options(self, *args, **kwargs): + return self.OptionsObject(self, *args, **kwargs) + + def setUp(self): + BaseTestCase.setUp(self) + # These are not tested. + self.mox.StubOutWithMock(gclient.gclient_utils, 'FileRead') + self.mox.StubOutWithMock(gclient.gclient_utils, 'FileWrite') + self.mox.StubOutWithMock(gclient.gclient_utils, 'SubprocessCall') + self.mox.StubOutWithMock(gclient.gclient_utils, 'RemoveDirectory') + # Mock them to be sure nothing bad happens. + self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'Capture') + self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'CaptureInfo') + self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'CaptureStatus') + self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'Run') + self.mox.StubOutWithMock(gclient.gclient_scm.scm.SVN, 'RunAndGetFileList') + self._gclient_gclient = gclient.GClient + gclient.GClient = self.mox.CreateMockAnything() + gclient.GClient.DEPS_FILE = self._gclient_gclient.DEPS_FILE + gclient.GClient.DEFAULT_CLIENT_FILE_TEXT = ( + self._gclient_gclient.DEFAULT_CLIENT_FILE_TEXT) + self._scm_wrapper = gclient.gclient_scm.CreateSCM + gclient.gclient_scm.CreateSCM = self.mox.CreateMockAnything() + + def tearDown(self): + gclient.GClient = self._gclient_gclient + gclient.gclient_scm.CreateSCM = self._scm_wrapper + BaseTestCase.tearDown(self) + + +class GclientTestCase(GClientBaseTestCase): + class OptionsObject(object): + def __init__(self, test_case, verbose=0, spec=None, + config_filename='a_file_name', + entries_filename='a_entry_file_name', + force=False, nohooks=False): + self.verbose = verbose + self.config_filename = config_filename + self.entries_filename = entries_filename + self.spec = spec + self.name = None + self.force = force + self.nohooks = nohooks + self.revisions = [] + self.manually_grab_svn_rev = True + self.deps_os = None + self.head = False + + def setUp(self): + GClientBaseTestCase.setUp(self) + self.args = self.Args() + self.root_dir = self.Dir() + self.url = self.Url() + + +class GClientCommandsTestCase(GClientBaseTestCase): + def testCommands(self): + known_commands = [gclient.CMDcleanup, gclient.CMDconfig, gclient.CMDdiff, + gclient.CMDexport, gclient.CMDhelp, gclient.CMDpack, + gclient.CMDrevert, gclient.CMDrevinfo, + gclient.CMDrunhooks, gclient.CMDstatus, gclient.CMDsync, + gclient.CMDupdate] + cmds = [getattr(gclient, x) for x in dir(gclient) if x.startswith('CMD')] + self.assertEquals(len(known_commands), len(cmds)) + for v in cmds: + # If it fails, you need to add a test case for the new command. + self.assert_(v in known_commands) + self.mox.ReplayAll() + +class TestCMDconfig(GclientTestCase): + def testMissingArgument(self): + exception_msg = "required argument missing; see 'gclient help config'" + + self.mox.ReplayAll() + self.assertRaisesError(exception_msg, gclient.CMDconfig, None, + self.Options(), ()) + + def testExistingClientFile(self): + options = self.Options() + exception_msg = ('%s file already exists in the current directory' % + options.config_filename) + gclient.os.path.exists(options.config_filename).AndReturn(True) + + self.mox.ReplayAll() + self.assertRaisesError(exception_msg, gclient.CMDconfig, None, options, + (1,)) + + def testFromText(self): + options = self.Options(spec='config_source_content') + gclient.os.path.exists(options.config_filename).AndReturn(False) + gclient.GClient('.', options).AndReturn(gclient.GClient) + gclient.GClient.SetConfig(options.spec) + gclient.GClient.SaveConfig() + + self.mox.ReplayAll() + gclient.CMDconfig(None, options, (1,),) + + def testCreateClientFile(self): + options = self.Options() + gclient.os.path.exists(options.config_filename).AndReturn(False) + gclient.GClient('.', options).AndReturn(gclient.GClient) + gclient.GClient.SetDefaultConfig('the_name', 'http://svn/url/the_name', + 'other') + gclient.GClient.SaveConfig() + + self.mox.ReplayAll() + gclient.CMDconfig(None, options, + ('http://svn/url/the_name', 'other', 'args', 'ignored')) + + +class GenericCommandTestCase(GclientTestCase): + def ReturnValue(self, command, return_value): + options = self.Options() + gclient.GClient.LoadCurrentConfig(options).AndReturn(gclient.GClient) + gclient.GClient.RunOnDeps(command, self.args).AndReturn(return_value) + + self.mox.ReplayAll() + function = getattr(gclient, 'CMD' + command) + result = function(None, options, self.args) + self.assertEquals(result, return_value) + + def BadClient(self, command): + options = self.Options() + gclient.GClient.LoadCurrentConfig(options).AndReturn(None) + + self.mox.ReplayAll() + function = getattr(gclient, 'CMD' + command) + self.assertRaisesError( + "client not configured; see 'gclient config'", + function, None, options, self.args) + + def Verbose(self, command): + options = self.Options(verbose=True) + gclient.GClient.LoadCurrentConfig(options).AndReturn(gclient.GClient) + text = "# Dummy content\nclient = 'my client'" + gclient.GClient.ConfigContent().AndReturn(text) + print(text) + gclient.GClient.RunOnDeps(command, self.args).AndReturn(0) + + self.mox.ReplayAll() + function = getattr(gclient, 'CMD' + command) + result = function(None, options, self.args) + self.assertEquals(result, 0) + + +class TestCMDcleanup(GenericCommandTestCase): + def testGoodClient(self): + self.ReturnValue('cleanup', 0) + def testError(self): + self.ReturnValue('cleanup', 42) + def testBadClient(self): + self.BadClient('cleanup') + + +class TestCMDstatus(GenericCommandTestCase): + def testGoodClient(self): + self.ReturnValue('status', 0) + def testError(self): + self.ReturnValue('status', 42) + def testBadClient(self): + self.BadClient('status') + + +class TestCMDrunhooks(GenericCommandTestCase): + def Options(self, verbose=False, *args, **kwargs): + return self.OptionsObject(self, verbose=verbose, *args, **kwargs) + + def testGoodClient(self): + self.ReturnValue('runhooks', 0) + def testError(self): + self.ReturnValue('runhooks', 42) + def testBadClient(self): + self.BadClient('runhooks') + + +class TestCMDupdate(GenericCommandTestCase): + def ReturnValue(self, command, return_value): + options = self.Options() + gclient.GClient.LoadCurrentConfig(options).AndReturn(gclient.GClient) + gclient.GClient.GetVar("solutions") + gclient.GClient.RunOnDeps(command, self.args).AndReturn(return_value) + + self.mox.ReplayAll() + function = getattr(gclient, 'CMD' + command) + result = function(None, options, self.args) + self.assertEquals(result, return_value) + + def Verbose(self, command): + options = self.Options(verbose=True) + gclient.GClient.LoadCurrentConfig(options).AndReturn(gclient.GClient) + gclient.GClient.GetVar("solutions") + text = "# Dummy content\nclient = 'my client'" + gclient.GClient.ConfigContent().AndReturn(text) + print(text) + gclient.GClient.RunOnDeps(command, self.args).AndReturn(0) + + self.mox.ReplayAll() + function = getattr(gclient, 'CMD' + command) + result = function(None, options, self.args) + self.assertEquals(result, 0) + + def Options(self, verbose=False, *args, **kwargs): + return self.OptionsObject(self, verbose=verbose, *args, **kwargs) + + def testBasic(self): + self.ReturnValue('update', 0) + def testError(self): + self.ReturnValue('update', 42) + def testBadClient(self): + self.BadClient('update') + def testVerbose(self): + self.Verbose('update') + + +class TestCMDdiff(GenericCommandTestCase): + def Options(self, *args, **kwargs): + return self.OptionsObject(self, *args, **kwargs) + + def testBasic(self): + self.ReturnValue('diff', 0) + def testError(self): + self.ReturnValue('diff', 42) + def testBadClient(self): + self.BadClient('diff') + def testVerbose(self): + self.Verbose('diff') + + +class TestCMDexport(GenericCommandTestCase): + def testBasic(self): + self.args = ['dir'] + self.ReturnValue('export', 0) + def testError(self): + self.args = ['dir'] + self.ReturnValue('export', 42) + def testBadClient(self): + self.args = ['dir'] + self.BadClient('export') + + +class TestCMDpack(GenericCommandTestCase): + def Options(self, *args, **kwargs): + return self.OptionsObject(self, *args, **kwargs) + + def testBasic(self): + self.ReturnValue('pack', 0) + def testError(self): + self.ReturnValue('pack', 42) + def testBadClient(self): + self.BadClient('pack') + + +class TestCMDrevert(GenericCommandTestCase): + def testBasic(self): + self.ReturnValue('revert', 0) + def testError(self): + self.ReturnValue('revert', 42) + def testBadClient(self): + self.BadClient('revert') + + +class GClientClassTestCase(GclientTestCase): + def testDir(self): + members = [ + 'ConfigContent', 'DEFAULT_CLIENT_FILE_TEXT', + 'DEFAULT_SNAPSHOT_FILE_TEXT', 'DEFAULT_SNAPSHOT_SOLUTION_TEXT', + 'DEPS_FILE', 'FileImpl', 'FromImpl', 'GetVar', 'LoadCurrentConfig', + 'PrintRevInfo', 'RunOnDeps', 'SaveConfig', 'SetConfig', + 'SetDefaultConfig', + 'deps_os_choices', 'supported_commands', + ] + + # If you add a member, be sure to add the relevant test! + self.compareMembers(self._gclient_gclient('root_dir', 'options'), members) + + def testSetConfig_ConfigContent_GetVar_SaveConfig_SetDefaultConfig(self): + options = self.Options() + text = "# Dummy content\nclient = 'my client'" + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.config_filename), + text) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(text) + self.assertEqual(client.ConfigContent(), text) + self.assertEqual(client.GetVar('client'), 'my client') + self.assertEqual(client.GetVar('foo'), None) + client.SaveConfig() + + solution_name = 'solution name' + solution_url = 'solution url' + safesync_url = 'safesync url' + default_text = gclient.GClient.DEFAULT_CLIENT_FILE_TEXT % { + 'solution_name' : solution_name, + 'solution_url' : solution_url, + 'safesync_url' : safesync_url + } + client.SetDefaultConfig(solution_name, solution_url, safesync_url) + self.assertEqual(client.ConfigContent(), default_text) + solutions = [{ + 'name': solution_name, + 'url': solution_url, + 'custom_deps': {}, + 'safesync_url': safesync_url + }] + self.assertEqual(client.GetVar('solutions'), solutions) + self.assertEqual(client.GetVar('foo'), None) + + def testLoadCurrentConfig(self): + options = self.Options() + gclient.os.path.realpath(self.root_dir).AndReturn(self.root_dir) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.config_filename) + ).AndReturn(True) + gclient.GClient(self.root_dir, options).AndReturn(gclient.GClient) + gclient.GClient._LoadConfig() + + self.mox.ReplayAll() + client = self._gclient_gclient.LoadCurrentConfig(options, self.root_dir) + + def testRunOnDepsNoDeps(self): + solution_name = 'testRunOnDepsNoDeps_solution_name' + gclient_config = ( + "solutions = [ {\n" + " 'name': '%s',\n" + " 'url': '%s',\n" + " 'custom_deps': {},\n" + "} ]\n" + ) % (solution_name, self.url) + + # pprint.pformat() is non-deterministic in this case!! + entries_content = ( + "entries = \\\n" + "{ '%s': '%s'}\n" + ) % (solution_name, self.url) + + options = self.Options() + + checkout_path = gclient.os.path.join(self.root_dir, solution_name) + gclient.os.path.exists(gclient.os.path.join(checkout_path, '.git') + ).AndReturn(False) + # Expect a check for the entries file and we say there is not one. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + + # An scm will be requested for the solution. + scm_wrapper_sol = self.mox.CreateMockAnything() + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, solution_name + ).AndReturn(scm_wrapper_sol) + # Then an update will be performed. + scm_wrapper_sol.RunCommand('update', options, self.args, []) + # Then an attempt will be made to read its DEPS file. + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, solution_name, + gclient.GClient.DEPS_FILE) + ).AndRaise(IOError(2, 'No DEPS file')) + + # After everything is done, an attempt is made to write an entries + # file. + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsRelativePaths(self): + solution_name = 'testRunOnDepsRelativePaths_solution_name' + gclient_config = ( + "solutions = [ {\n" + " 'name': '%s',\n" + " 'url': '%s',\n" + " 'custom_deps': {},\n" + "} ]\n" + ) % (solution_name, self.url) + + deps = ( + "use_relative_paths = True\n" + "deps = {\n" + " 'src/t': 'svn://scm.t/trunk',\n" + "}\n") + entry_path = gclient.os.path.join(solution_name, 'src', 't' + ).replace('\\', '\\\\') + entries_content = ( + "entries = \\\n" + "{ '%s': '%s',\n" + " '%s': 'svn://scm.t/trunk'}\n" + ) % (solution_name, self.url, entry_path) + + scm_wrapper_sol = self.mox.CreateMockAnything() + scm_wrapper_t = self.mox.CreateMockAnything() + + options = self.Options() + + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, solution_name, 'src', 't', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, solution_name, '.git') + ).AndReturn(False) + # Expect a check for the entries file and we say there is not one. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + + # An scm will be requested for the solution. + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, solution_name + ).AndReturn(scm_wrapper_sol) + # Then an update will be performed. + scm_wrapper_sol.RunCommand('update', options, self.args, []) + # Then an attempt will be made to read its DEPS file. + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, solution_name, + gclient.GClient.DEPS_FILE) + ).AndReturn(deps) + + # Next we expect an scm to be request for dep src/t but it should + # use the url specified in deps and the relative path should now + # be relative to the DEPS file. + gclient.gclient_scm.CreateSCM( + 'svn://scm.t/trunk', + self.root_dir, + gclient.os.path.join(solution_name, "src", "t") + ).AndReturn(scm_wrapper_t) + scm_wrapper_t.RunCommand('update', options, self.args, []) + + # After everything is done, an attempt is made to write an entries + # file. + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsCustomDeps(self): + solution_name = 'testRunOnDepsCustomDeps_solution_name' + gclient_config = ( + "solutions = [ {\n" + " 'name': '%s',\n" + " 'url': '%s',\n" + " 'custom_deps': {\n" + " 'src/b': None,\n" + " 'src/n': 'svn://custom.n/trunk',\n" + " 'src/t': 'svn://custom.t/trunk',\n" + " }\n} ]\n" + ) % (solution_name, self.url) + + deps = ( + "deps = {\n" + " 'src/b': 'svn://original.b/trunk',\n" + " 'src/t': 'svn://original.t/trunk',\n" + "}\n" + ) + + entries_content = ( + "entries = \\\n" + "{ 'src/n': 'svn://custom.n/trunk',\n" + " 'src/t': 'svn://custom.t/trunk',\n" + " '%s': '%s'}\n" + ) % (solution_name, self.url) + + scm_wrapper_sol = self.mox.CreateMockAnything() + scm_wrapper_t = self.mox.CreateMockAnything() + scm_wrapper_n = self.mox.CreateMockAnything() + + options = self.Options() + + checkout_path = gclient.os.path.join(self.root_dir, solution_name) + gclient.os.path.exists( + gclient.os.path.join(checkout_path, '.git')).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'src/n', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'src/t', '.git') + ).AndReturn(False) + + # Expect a check for the entries file and we say there is not one. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + + # An scm will be requested for the solution. + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, solution_name + ).AndReturn(scm_wrapper_sol) + # Then an update will be performed. + scm_wrapper_sol.RunCommand('update', options, self.args, []) + # Then an attempt will be made to read its DEPS file. + gclient.gclient_utils.FileRead( + gclient.os.path.join(checkout_path, gclient.GClient.DEPS_FILE) + ).AndReturn(deps) + + # Next we expect an scm to be request for dep src/n even though it does not + # exist in the DEPS file. + gclient.gclient_scm.CreateSCM('svn://custom.n/trunk', + self.root_dir, + "src/n").AndReturn(scm_wrapper_n) + + # Next we expect an scm to be request for dep src/t but it should + # use the url specified in custom_deps. + gclient.gclient_scm.CreateSCM('svn://custom.t/trunk', + self.root_dir, + "src/t").AndReturn(scm_wrapper_t) + + scm_wrapper_n.RunCommand('update', options, self.args, []) + scm_wrapper_t.RunCommand('update', options, self.args, []) + + # NOTE: the dep src/b should not create an scm at all. + + # After everything is done, an attempt is made to write an entries + # file. + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + # Regression test for Issue #11. + # http://code.google.com/p/gclient/issues/detail?id=11 + def testRunOnDepsSharedDependency(self): + name_a = 'testRunOnDepsSharedDependency_a' + name_b = 'testRunOnDepsSharedDependency_b' + + url_a = self.url + '/a' + url_b = self.url + '/b' + + # config declares two solutions and each has a dependency to place + # http://svn.t/trunk at src/t. + gclient_config = ( + "solutions = [ {\n" + " 'name': '%s',\n" + " 'url': '%s',\n" + " 'custom_deps': {},\n" + "}, {\n" + " 'name': '%s',\n" + " 'url': '%s',\n" + " 'custom_deps': {},\n" + "}\n]\n") % (name_a, url_a, name_b, url_b) + + deps_b = deps_a = ( + "deps = {\n" + " 'src/t' : 'http://svn.t/trunk',\n" + "}\n") + + entries_content = ( + "entries = \\\n" + "{ 'src/t': 'http://svn.t/trunk',\n" + " '%s': '%s',\n" + " '%s': '%s'}\n" + ) % (name_a, url_a, name_b, url_b) + + scm_wrapper_a = self.mox.CreateMockAnything() + scm_wrapper_b = self.mox.CreateMockAnything() + scm_wrapper_dep = self.mox.CreateMockAnything() + + options = self.Options() + + gclient.os.path.exists(gclient.os.path.join(self.root_dir, name_a, '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, name_b, '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'src/t', '.git') + ).AndReturn(False) + + # Expect a check for the entries file and we say there is not one. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + + # An scm will be requested for the first solution. + gclient.gclient_scm.CreateSCM(url_a, self.root_dir, name_a).AndReturn( + scm_wrapper_a) + # Then an attempt will be made to read it's DEPS file. + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name_a, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_a) + # Then an update will be performed. + scm_wrapper_a.RunCommand('update', options, self.args, []) + + # An scm will be requested for the second solution. + gclient.gclient_scm.CreateSCM(url_b, self.root_dir, name_b).AndReturn( + scm_wrapper_b) + # Then an attempt will be made to read its DEPS file. + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name_b, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_b) + # Then an update will be performed. + scm_wrapper_b.RunCommand('update', options, self.args, []) + + # Finally, an scm is requested for the shared dep. + gclient.gclient_scm.CreateSCM('http://svn.t/trunk', self.root_dir, 'src/t' + ).AndReturn(scm_wrapper_dep) + # And an update is run on it. + scm_wrapper_dep.RunCommand('update', options, self.args, []) + + # After everything is done, an attempt is made to write an entries file. + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsSuccess(self): + # Fake .gclient file. + name = 'testRunOnDepsSuccess_solution_name' + gclient_config = """solutions = [ { + 'name': '%s', + 'url': '%s', + 'custom_deps': {}, +}, ]""" % (name, self.url) + + # pprint.pformat() is non-deterministic in this case!! + entries_content = ( + "entries = \\\n" + "{ '%s': '%s'}\n" + ) % (name, self.url) + + options = self.Options() + gclient.os.path.exists(gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn("Boo = 'a'") + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsRevisions(self): + def OptIsRev(options, rev): + if not options.revision == str(rev): + print("options.revision = %s" % options.revision) + return options.revision == str(rev) + def OptIsRevNone(options): + if options.revision: + print("options.revision = %s" % options.revision) + return options.revision == None + def OptIsRev42(options): + return OptIsRev(options, 42) + def OptIsRev123(options): + return OptIsRev(options, 123) + def OptIsRev333(options): + return OptIsRev(options, 333) + + # Fake .gclient file. + gclient_config = """solutions = [ { + 'name': 'src', + 'url': '%s', + 'custom_deps': {}, +}, ]""" % self.url + # Fake DEPS file. + deps_content = """deps = { + 'src/breakpad/bar': 'http://google-breakpad.googlecode.com/svn/trunk/src@285', + 'foo/third_party/WebKit': '/trunk/deps/third_party/WebKit', + 'src/third_party/cygwin': '/trunk/deps/third_party/cygwin@3248', +} +deps_os = { + 'win': { + 'src/foosad/asdf': 'svn://random_server:123/asd/python_24@5580', + }, + 'mac': { + 'src/third_party/python_24': 'svn://random_server:123/trunk/python_24@5580', + }, +}""" + + cygwin_path = 'dummy path cygwin' + webkit_path = 'dummy path webkit' + + entries_content = ( + "entries = \\\n" + "{ 'foo/third_party/WebKit': '%s',\n" + " 'src': '%s',\n" + " 'src/breakpad/bar':" + " 'http://google-breakpad.googlecode.com/svn/trunk/src@285',\n" + " 'src/third_party/cygwin': '%s',\n" + " 'src/third_party/python_24':" + " 'svn://random_server:123/trunk/python_24@5580'}\n" + ) % (webkit_path, self.url, cygwin_path) + + scm_wrapper_bleh = self.mox.CreateMockAnything() + scm_wrapper_src = self.mox.CreateMockAnything() + scm_wrapper_src2 = self.mox.CreateMockAnything() + scm_wrapper_webkit = self.mox.CreateMockAnything() + scm_wrapper_breakpad = self.mox.CreateMockAnything() + scm_wrapper_cygwin = self.mox.CreateMockAnything() + scm_wrapper_python = self.mox.CreateMockAnything() + options = self.Options() + options.revisions = [ 'src@123', 'foo/third_party/WebKit@42', + 'src/third_party/cygwin@333' ] + options.deps_os = 'mac' + # Also, pymox doesn't verify the order of function calling w.r.t. different + # mock objects. Pretty lame. So reorder as we wish to make it clearer. + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, 'src', gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'src', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, 'foo/third_party/WebKit', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, 'src/third_party/cygwin', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, 'src/third_party/python_24', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, 'src/breakpad/bar', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, 'src').AndReturn( + scm_wrapper_src) + scm_wrapper_src.RunCommand('update', mox.Func(OptIsRev123), self.args, []) + + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, + None).AndReturn(scm_wrapper_src2) + scm_wrapper_src2.FullUrlForRelativeUrl('/trunk/deps/third_party/cygwin@3248' + ).AndReturn(cygwin_path) + + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, + None).AndReturn(scm_wrapper_src2) + scm_wrapper_src2.FullUrlForRelativeUrl('/trunk/deps/third_party/WebKit' + ).AndReturn(webkit_path) + + gclient.gclient_scm.CreateSCM( + webkit_path, self.root_dir, 'foo/third_party/WebKit' + ).AndReturn(scm_wrapper_webkit) + scm_wrapper_webkit.RunCommand('update', mox.Func(OptIsRev42), self.args, []) + + gclient.gclient_scm.CreateSCM( + 'http://google-breakpad.googlecode.com/svn/trunk/src@285', + self.root_dir, 'src/breakpad/bar').AndReturn(scm_wrapper_breakpad) + scm_wrapper_breakpad.RunCommand('update', mox.Func(OptIsRevNone), + self.args, []) + + gclient.gclient_scm.CreateSCM( + cygwin_path, self.root_dir, 'src/third_party/cygwin' + ).AndReturn(scm_wrapper_cygwin) + scm_wrapper_cygwin.RunCommand('update', mox.Func(OptIsRev333), self.args, + []) + + gclient.gclient_scm.CreateSCM( + 'svn://random_server:123/trunk/python_24@5580', + self.root_dir, + 'src/third_party/python_24' + ).AndReturn(scm_wrapper_python) + scm_wrapper_python.RunCommand('update', mox.Func(OptIsRevNone), self.args, + []) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsConflictingRevisions(self): + # Fake .gclient file. + name = 'testRunOnDepsConflictingRevisions_solution_name' + gclient_config = """solutions = [ { + 'name': '%s', + 'url': '%s', + 'custom_deps': {}, + 'custom_vars': {}, +}, ]""" % (name, self.url) + # Fake DEPS file. + deps_content = """deps = { + 'foo/third_party/WebKit': '/trunk/deps/third_party/WebKit', +}""" + + options = self.Options() + options.revisions = [ 'foo/third_party/WebKit@42', + 'foo/third_party/WebKit@43' ] + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + exception = "Conflicting revision numbers specified." + try: + client.RunOnDeps('update', self.args) + except gclient.gclient_utils.Error, e: + self.assertEquals(e.args[0], exception) + else: + self.fail('%s not raised' % exception) + + def testRunOnDepsSuccessVars(self): + # Fake .gclient file. + name = 'testRunOnDepsSuccessVars_solution_name' + gclient_config = """solutions = [ { + 'name': '%s', + 'url': '%s', + 'custom_deps': {}, + 'custom_vars': {}, +}, ]""" % (name, self.url) + # Fake DEPS file. + deps_content = """vars = { + 'webkit': '/trunk/bar/', +} +deps = { + 'foo/third_party/WebKit': Var('webkit') + 'WebKit', +}""" + + webkit_path = 'dummy path webkit' + + entries_content = ( + "entries = \\\n" + "{ 'foo/third_party/WebKit': '%s',\n" + " '%s': '%s'}\n" + ) % (webkit_path, name, self.url) + + scm_wrapper_webkit = self.mox.CreateMockAnything() + scm_wrapper_src = self.mox.CreateMockAnything() + + options = self.Options() + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, + gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, 'foo/third_party/WebKit', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, None + ).AndReturn(scm_wrapper_src) + scm_wrapper_src.FullUrlForRelativeUrl('/trunk/bar/WebKit' + ).AndReturn(webkit_path) + + gclient.gclient_scm.CreateSCM( + webkit_path, self.root_dir, 'foo/third_party/WebKit' + ).AndReturn(gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsSuccessCustomVars(self): + # Fake .gclient file. + name = 'testRunOnDepsSuccessCustomVars_solution_name' + gclient_config = """solutions = [ { + 'name': '%s', + 'url': '%s', + 'custom_deps': {}, + 'custom_vars': {'webkit': '/trunk/bar_custom/'}, +}, ]""" % (name, self.url) + # Fake DEPS file. + deps_content = """vars = { + 'webkit': '/trunk/bar/', +} +deps = { + 'foo/third_party/WebKit': Var('webkit') + 'WebKit', +}""" + + webkit_path = 'dummy path webkit' + + entries_content = ( + "entries = \\\n" + "{ 'foo/third_party/WebKit': '%s',\n" + " '%s': '%s'}\n" + ) % (webkit_path, name, self.url) + + scm_wrapper_webkit = self.mox.CreateMockAnything() + scm_wrapper_src = self.mox.CreateMockAnything() + + options = self.Options() + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + entries_content) + + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, 'foo/third_party/WebKit', '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, + None).AndReturn(scm_wrapper_src) + scm_wrapper_src.FullUrlForRelativeUrl('/trunk/bar_custom/WebKit' + ).AndReturn(webkit_path) + + gclient.gclient_scm.CreateSCM(webkit_path, self.root_dir, + 'foo/third_party/WebKit').AndReturn(gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testRunOnDepsFailureVars(self): + # Fake .gclient file. + name = 'testRunOnDepsFailureVars_solution_name' + gclient_config = """solutions = [ { + 'name': '%s', + 'url': '%s', + 'custom_deps': {}, + 'custom_vars': {}, +}, ]""" % (name, self.url) + # Fake DEPS file. + deps_content = """deps = { + 'foo/third_party/WebKit': Var('webkit') + 'WebKit', +}""" + + options = self.Options() + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + exception = "Var is not defined: webkit" + try: + client.RunOnDeps('update', self.args) + except gclient.gclient_utils.Error, e: + self.assertEquals(e.args[0], exception) + else: + self.fail('%s not raised' % exception) + + def testRunOnDepsFailureInvalidCommand(self): + options = self.Options() + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + exception = "'foo' is an unsupported command" + self.assertRaisesError(exception, self._gclient_gclient.RunOnDeps, client, + 'foo', self.args) + + def testRunOnDepsFailureEmpty(self): + options = self.Options() + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + exception = "No solution specified" + self.assertRaisesError(exception, self._gclient_gclient.RunOnDeps, client, + 'update', self.args) + + def testFromImplOne(self): + base_url = 'svn://base@123' + deps_content = ( + "deps = {\n" + " 'base': '%s',\n" + " 'main': From('base'),\n" + "}\n" % base_url + ) + main_url = 'svn://main@456' + base_deps_content = ( + "deps = {\n" + " 'main': '%s',\n" + "}\n" % main_url + ) + # Fake .gclient file. + name = 'testFromImplOne_solution_name' + gclient_config = ( + "solutions = [ {\n" + "'name': '%s',\n" + "'url': '%s',\n" + "'custom_deps': {},\n" + "}, ]" % (name, self.url)) + + options = self.Options() + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'main', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'base', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + + # base gets updated. + gclient.gclient_scm.CreateSCM(base_url, self.root_dir, 'base').AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, 'base', gclient.GClient.DEPS_FILE) + ).AndReturn(base_deps_content) + + # main gets updated. + gclient.gclient_scm.CreateSCM(main_url, self.root_dir, 'main').AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + # Process is done and will write an .gclient_entries. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + mox.IgnoreArg()) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testFromImplTwo(self): + base_url = 'svn://base@123' + deps_content = ( + "deps = {\n" + " 'base': '%s',\n" + " 'main': From('base', 'src/main'),\n" + "}\n" % base_url + ) + main_url = 'svn://main@456' + base_deps_content = ( + "deps = {\n" + " 'src/main': '%s',\n" + "}\n" % main_url + ) + # Fake .gclient file. + name = 'testFromImplTwo_solution_name' + gclient_config = ( + "solutions = [ {\n" + "'name': '%s',\n" + "'url': '%s',\n" + "'custom_deps': {},\n" + "}, ]" % (name, self.url)) + + options = self.Options() + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'main', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'base', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + + # base gets updated. + gclient.gclient_scm.CreateSCM(base_url, self.root_dir, 'base').AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, 'base', gclient.GClient.DEPS_FILE) + ).AndReturn(base_deps_content) + + # main gets updated. + gclient.gclient_scm.CreateSCM(main_url, self.root_dir, 'main').AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + # Process is done and will write an .gclient_entries. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + mox.IgnoreArg()) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testFromImplTwoRelatvie(self): + base_url = 'svn://base@123' + deps_content = ( + "deps = {\n" + " 'base': '%s',\n" + " 'main': From('base', 'src/main'),\n" + "}\n" % base_url + ) + main_url = '/relative@456' + base_deps_content = ( + "deps = {\n" + " 'src/main': '%s',\n" + "}\n" % main_url + ) + # Fake .gclient file. + name = 'testFromImplTwo_solution_name' + gclient_config = ( + "solutions = [ {\n" + "'name': '%s',\n" + "'url': '%s',\n" + "'custom_deps': {},\n" + "}, ]" % (name, self.url)) + + options = self.Options() + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'main', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, 'base', '.git') + ).AndReturn(False) + gclient.os.path.exists(gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + + # base gets updated. + gclient.gclient_scm.CreateSCM(base_url, self.root_dir, 'base').AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, 'base', gclient.GClient.DEPS_FILE) + ).AndReturn(base_deps_content) + + # main gets updated after resolving the relative url. + gclient.gclient_scm.CreateSCM(base_url, self.root_dir, None).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.FullUrlForRelativeUrl(main_url + ).AndReturn('svn://base' + main_url) + gclient.gclient_scm.CreateSCM('svn://base' + main_url, self.root_dir, + 'main').AndReturn(gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + + # Process is done and will write an .gclient_entries. + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + mox.IgnoreArg()) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def testFileImpl(self): + # Fake .gclient file. + name = "testFileImpl" + gclient_config = ( + "solutions = [ { 'name': '%s'," + "'url': '%s', } ]" % (name, self.url) + ) + # Fake DEPS file. + target = "chromium_deps" + deps_content = ( + "deps = {" + " '%s': File('%s/DEPS') }" % (target, self.url) + ) + + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, name).AndReturn( + gclient.gclient_scm.CreateSCM) + options = self.Options() + gclient.gclient_scm.CreateSCM.RunCommand('update', options, self.args, []) + gclient.gclient_utils.FileRead( + gclient.os.path.join(self.root_dir, name, gclient.GClient.DEPS_FILE) + ).AndReturn(deps_content) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, name, '.git') + ).AndReturn(False) + gclient.os.path.exists( + gclient.os.path.join(self.root_dir, options.entries_filename) + ).AndReturn(False) + + # This is where gclient tries to do the initial checkout. + gclient.gclient_scm.CreateSCM(self.url, self.root_dir, target).AndReturn( + gclient.gclient_scm.CreateSCM) + gclient.gclient_scm.CreateSCM.RunCommand('updatesingle', options, + self.args + ["DEPS"], []) + gclient.gclient_utils.FileWrite( + gclient.os.path.join(self.root_dir, options.entries_filename), + "entries = \\\n{ '%s': '%s'}\n" % (name, self.url)) + + self.mox.ReplayAll() + client = self._gclient_gclient(self.root_dir, options) + client.SetConfig(gclient_config) + client.RunOnDeps('update', self.args) + + def test_PrintRevInfo(self): + # TODO(aharper): no test yet for revinfo, lock it down once we've verified + # implementation for Pulse plugin + pass + + # No test for internal functions. + def test_GetAllDeps(self): + pass + def test_GetDefaultSolutionDeps(self): + pass + def test_LoadConfig(self): + pass + def test_ReadEntries(self): + pass + def test_SaveEntries(self): + pass + def test_VarImpl(self): + pass + + +if __name__ == '__main__': + import unittest + unittest.main() + +# vim: ts=2:sw=2:tw=80:et: