Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(655)

Unified Diff: gslib/tests/testcase/unit_testcase.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gslib/tests/testcase/integration_testcase.py ('k') | gslib/tests/util.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gslib/tests/testcase/unit_testcase.py
===================================================================
--- gslib/tests/testcase/unit_testcase.py (revision 33376)
+++ gslib/tests/testcase/unit_testcase.py (working copy)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright 2013 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,56 +12,52 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
"""Contains gsutil base unit test case class."""
+from __future__ import absolute_import
+
+import logging
import os
import sys
import tempfile
import boto
-
from gslib import wildcard_iterator
+from gslib.boto_translation import BotoTranslation
+from gslib.cloud_api_delegator import CloudApiDelegator
from gslib.command_runner import CommandRunner
-from gslib.project_id import ProjectIdHandler
+from gslib.cs_api_map import ApiMapConstants
+from gslib.cs_api_map import ApiSelector
+from gslib.tests.mock_logging_handler import MockLoggingHandler
+from gslib.tests.testcase import base
import gslib.tests.util as util
from gslib.tests.util import unittest
-import base
-# The mock storage service comes from the Boto library, but it is not
-# distributed with Boto when installed as a package. To get around this, we
-# copy the file to gslib/tests/mock_storage_service.py when building the gsutil
-# package. Try and import from several places here to find it.
-try:
- from gslib.tests import mock_storage_service
-except ImportError:
- try:
- from boto.tests.integration.s3 import mock_storage_service
- except ImportError:
- try:
- from tests.integration.s3 import mock_storage_service
- except ImportError:
- import mock_storage_service
+class GsutilApiUnitTestClassMapFactory(object):
+ """Class map factory for use in unit tests.
-class GSMockConnection(mock_storage_service.MockConnection):
+ BotoTranslation is used for all cases so that GSMockBucketStorageUri can
+ be used to communicate with the mock XML service.
+ """
- def __init__(self, *args, **kwargs):
- kwargs['provider'] = 'gs'
- super(GSMockConnection, self).__init__(*args, **kwargs)
+ @classmethod
+ def GetClassMap(cls):
+ """Returns a class map for use in unit tests."""
+ gs_class_map = {
+ ApiSelector.XML: BotoTranslation,
+ ApiSelector.JSON: BotoTranslation
+ }
+ s3_class_map = {
+ ApiSelector.XML: BotoTranslation
+ }
+ class_map = {
+ 'gs': gs_class_map,
+ 's3': s3_class_map
+ }
+ return class_map
-mock_connection = GSMockConnection()
-
-class GSMockBucketStorageUri(mock_storage_service.MockBucketStorageUri):
-
- def connect(self, access_key_id=None, secret_access_key=None):
- return mock_connection
-
- def compose(self, components, headers=None):
- """Dummy implementation to allow parallel uploads with tests."""
- return self.new_key()
-
@unittest.skipUnless(util.RUN_UNIT_TESTS,
'Not running integration tests.')
class GsUtilUnitTestCase(base.GsUtilTestCase):
@@ -69,26 +66,78 @@
@classmethod
def setUpClass(cls):
base.GsUtilTestCase.setUpClass()
- cls.mock_bucket_storage_uri = GSMockBucketStorageUri
- cls.proj_id_handler = ProjectIdHandler()
- config_file_list = boto.pyami.config.BotoConfigLocations
- # Use "gsutil_test_commands" as a fake UserAgent. This value will never be
- # sent via HTTP because we're using MockStorageService here.
- cls.command_runner = CommandRunner(config_file_list,
- cls.mock_bucket_storage_uri)
+ cls.mock_bucket_storage_uri = util.GSMockBucketStorageUri
+ cls.mock_gsutil_api_class_map_factory = GsutilApiUnitTestClassMapFactory
+ cls.logger = logging.getLogger()
+ cls.command_runner = CommandRunner(
+ bucket_storage_uri_class=cls.mock_bucket_storage_uri,
+ gsutil_api_class_map_factory=cls.mock_gsutil_api_class_map_factory)
def setUp(self):
super(GsUtilUnitTestCase, self).setUp()
self.bucket_uris = []
+ self.stdout_save = sys.stdout
+ self.stderr_save = sys.stderr
+ fd, self.stdout_file = tempfile.mkstemp()
+ sys.stdout = os.fdopen(fd, 'w+')
+ fd, self.stderr_file = tempfile.mkstemp()
+ sys.stderr = os.fdopen(fd, 'w+')
+ self.accumulated_stdout = []
+ self.accumulated_stderr = []
+ self.root_logger = logging.getLogger()
+ self.is_debugging = self.root_logger.isEnabledFor(logging.DEBUG)
+ self.log_handlers_save = self.root_logger.handlers
+ fd, self.log_handler_file = tempfile.mkstemp()
+ self.log_handler_stream = os.fdopen(fd, 'w+')
+ self.temp_log_handler = logging.StreamHandler(self.log_handler_stream)
+ self.root_logger.handlers = [self.temp_log_handler]
+
+ def tearDown(self):
+ super(GsUtilUnitTestCase, self).tearDown()
+
+ self.root_logger.handlers = self.log_handlers_save
+ self.temp_log_handler.flush()
+ self.log_handler_stream.seek(0)
+ log_output = self.log_handler_stream.read()
+ self.log_handler_stream.close()
+ os.unlink(self.log_handler_file)
+
+ sys.stdout.seek(0)
+ sys.stderr.seek(0)
+ stdout = sys.stdout.read()
+ stderr = sys.stderr.read()
+ stdout += ''.join(self.accumulated_stdout)
+ stderr += ''.join(self.accumulated_stderr)
+ sys.stdout.close()
+ sys.stderr.close()
+ sys.stdout = self.stdout_save
+ sys.stderr = self.stderr_save
+ os.unlink(self.stdout_file)
+ os.unlink(self.stderr_file)
+
+ if self.is_debugging and stdout:
+ sys.stderr.write('==== stdout %s ====\n' % self.id())
+ sys.stderr.write(stdout)
+ sys.stderr.write('==== end stdout ====\n')
+ if self.is_debugging and stderr:
+ sys.stderr.write('==== stderr %s ====\n' % self.id())
+ sys.stderr.write(stderr)
+ sys.stderr.write('==== end stderr ====\n')
+ if self.is_debugging and log_output:
+ sys.stderr.write('==== log output %s ====\n' % self.id())
+ sys.stderr.write(log_output)
+ sys.stderr.write('==== end log output ====\n')
+
def RunCommand(self, command_name, args=None, headers=None, debug=0,
- test_method=None, return_stdout=False, cwd=None):
- """
- Method for calling gslib.command_runner.CommandRunner, passing
- parallel_operations=False for all tests, optionally saving/returning stdout
- output. We run all tests multi-threaded, to exercise those more complicated
- code paths.
- TODO: change to run with parallel_operations=True for all tests. At
+ test_method=None, return_stdout=False, return_stderr=False,
+ return_log_handler=False, cwd=None):
+ """Method for calling gslib.command_runner.CommandRunner.
+
+ Passes parallel_operations=False for all tests, optionally saving/returning
+ stdout output. We run all tests multi-threaded, to exercise those more
+ complicated code paths.
+ TODO: Change to run with parallel_operations=True for all tests. At
present when you do this it causes many test failures.
Args:
@@ -96,95 +145,178 @@
args: Command-line args (arg0 = actual arg, not command name ala bash).
headers: Dictionary containing optional HTTP headers to pass to boto.
debug: Debug level to pass in to boto connection (range 0..3).
- parallel_operations: Should command operations be executed in parallel?
test_method: Optional general purpose method for testing purposes.
Application and semantics of this method will vary by
command and test type.
+ return_stdout: If True, will save and return stdout produced by command.
+ return_stderr: If True, will save and return stderr produced by command.
+ return_log_handler: If True, will return a MockLoggingHandler instance
+ that was attached to the command's logger while running.
cwd: The working directory that should be switched to before running the
command. The working directory will be reset back to its original
value after running the command. If not specified, the working
directory is left unchanged.
- return_stdout: If true will save and return stdout produced by command.
+
+ Returns:
+ One or a tuple of requested return values, depending on whether
+ return_stdout, return_stderr, and/or return_log_handler were specified.
"""
- if util.VERBOSE_OUTPUT:
- sys.stderr.write('\nRunning test of %s %s\n' %
- (command_name, ' '.join(args)))
- if return_stdout:
- # Redirect stdout temporarily, to save output to a file.
- fh, outfile = tempfile.mkstemp()
- os.close(fh)
- elif not util.VERBOSE_OUTPUT:
- outfile = os.devnull
- else:
- outfile = None
+ args = args or []
- stdout_sav = sys.stdout
- output = None
+ command_line = ' '.join([command_name] + args)
+ if self.is_debugging:
+ self.stderr_save.write('\nRunCommand of %s\n' % command_line)
+
+ # Save and truncate stdout and stderr for the lifetime of RunCommand. This
+ # way, we can return just the stdout and stderr that was output during the
+ # RunNamedCommand call below.
+ sys.stdout.seek(0)
+ sys.stderr.seek(0)
+ stdout = sys.stdout.read()
+ stderr = sys.stderr.read()
+ if stdout:
+ self.accumulated_stdout.append(stdout)
+ if stderr:
+ self.accumulated_stderr.append(stderr)
+ sys.stdout.seek(0)
+ sys.stderr.seek(0)
+ sys.stdout.truncate()
+ sys.stderr.truncate()
+
cwd_sav = None
try:
cwd_sav = os.getcwd()
except OSError:
# This can happen if the current working directory no longer exists.
pass
+
+ mock_log_handler = MockLoggingHandler()
+ logging.getLogger(command_name).addHandler(mock_log_handler)
+
try:
- if outfile:
- fp = open(outfile, 'w')
- sys.stdout = fp
if cwd:
os.chdir(cwd)
self.command_runner.RunNamedCommand(
command_name, args=args, headers=headers, debug=debug,
- parallel_operations=False, test_method=test_method)
+ parallel_operations=False, test_method=test_method, do_shutdown=False)
finally:
if cwd and cwd_sav:
os.chdir(cwd_sav)
- if outfile:
- fp.close()
- sys.stdout = stdout_sav
- with open(outfile, 'r') as f:
- output = f.read()
- if return_stdout:
- os.unlink(outfile)
- if output is not None and return_stdout:
- return output
+ sys.stdout.seek(0)
+ stdout = sys.stdout.read()
+ sys.stderr.seek(0)
+ stderr = sys.stderr.read()
+ logging.getLogger(command_name).removeHandler(mock_log_handler)
+ log_output = '\n'.join(
+ '%s:\n ' % level + '\n '.join(records)
+ for level, records in mock_log_handler.messages.iteritems()
+ if records)
+ if self.is_debugging and log_output:
+ self.stderr_save.write(
+ '==== logging RunCommand %s %s ====\n' % (self.id(), command_line))
+ self.stderr_save.write(log_output)
+ self.stderr_save.write('\n==== end logging ====\n')
+ if self.is_debugging and stdout:
+ self.stderr_save.write(
+ '==== stdout RunCommand %s %s ====\n' % (self.id(), command_line))
+ self.stderr_save.write(stdout)
+ self.stderr_save.write('==== end stdout ====\n')
+ if self.is_debugging and stderr:
+ self.stderr_save.write(
+ '==== stderr RunCommand %s %s ====\n' % (self.id(), command_line))
+ self.stderr_save.write(stderr)
+ self.stderr_save.write('==== end stderr ====\n')
+
+ # Reset stdout and stderr files, so that we won't print them out again
+ # in tearDown if debugging is enabled.
+ sys.stdout.seek(0)
+ sys.stderr.seek(0)
+ sys.stdout.truncate()
+ sys.stderr.truncate()
+
+ to_return = []
+ if return_stdout:
+ to_return.append(stdout)
+ if return_stderr:
+ to_return.append(stderr)
+ if return_log_handler:
+ to_return.append(mock_log_handler)
+ if len(to_return) == 1:
+ return to_return[0]
+ return tuple(to_return)
+
@classmethod
def _test_wildcard_iterator(cls, uri_or_str, debug=0):
- """
- Convenience method for instantiating a testing instance of
- WildCardIterator, without having to specify all the params of that class
+ """Convenience method for instantiating a test instance of WildcardIterator.
+
+ This makes it unnecessary to specify all the params of that class
(like bucket_storage_uri_class=mock_storage_service.MockBucketStorageUri).
- Also naming the factory method this way makes it clearer in the test code
+ Also, naming the factory method this way makes it clearer in the test code
that WildcardIterator needs to be set up for testing.
- Args are same as for wildcard_iterator.wildcard_iterator(), except there's
- no bucket_storage_uri_class arg.
+ Args are same as for wildcard_iterator.wildcard_iterator(), except
+ there are no class args for bucket_storage_uri_class or gsutil_api_class.
+ Args:
+ uri_or_str: StorageUri or string representing the wildcard string.
+ debug: debug level to pass to the underlying connection (0..3)
+
Returns:
- WildcardIterator.IterUris(), over which caller can iterate.
+ WildcardIterator, over which caller can iterate.
"""
- return wildcard_iterator.wildcard_iterator(
- uri_or_str, cls.proj_id_handler, cls.mock_bucket_storage_uri,
+ # TODO: Remove when tests no longer pass StorageUri arguments.
+ uri_string = uri_or_str
+ if hasattr(uri_or_str, 'uri'):
+ uri_string = uri_or_str.uri
+
+ cls.gsutil_api_map = {
+ ApiMapConstants.API_MAP: (
+ cls.mock_gsutil_api_class_map_factory.GetClassMap()),
+ ApiMapConstants.SUPPORT_MAP: {
+ 'gs': [ApiSelector.XML, ApiSelector.JSON],
+ 's3': [ApiSelector.XML]
+ },
+ ApiMapConstants.DEFAULT_MAP: {
+ 'gs': ApiSelector.JSON,
+ 's3': ApiSelector.XML
+ }
+ }
+
+ cls.gsutil_api = CloudApiDelegator(
+ cls.mock_bucket_storage_uri, cls.gsutil_api_map, cls.logger,
debug=debug)
+ return wildcard_iterator.CreateWildcardIterator(uri_string, cls.gsutil_api)
+
@staticmethod
def _test_storage_uri(uri_str, default_scheme='file', debug=0,
validate=True):
- """
- Convenience method for instantiating a testing
- instance of StorageUri, without having to specify
+ """Convenience method for instantiating a testing instance of StorageUri.
+
+ This makes it unnecessary to specify
bucket_storage_uri_class=mock_storage_service.MockBucketStorageUri.
Also naming the factory method this way makes it clearer in the test
code that StorageUri needs to be set up for testing.
Args, Returns, and Raises are same as for boto.storage_uri(), except there's
no bucket_storage_uri_class arg.
+
+ Args:
+ uri_str: Uri string to create StorageUri for.
+ default_scheme: Default scheme for the StorageUri
+ debug: debug level to pass to the underlying connection (0..3)
+ validate: If True, validate the resource that the StorageUri refers to.
+
+ Returns:
+ StorageUri based on the arguments.
"""
return boto.storage_uri(uri_str, default_scheme, debug, validate,
- GSMockBucketStorageUri)
+ util.GSMockBucketStorageUri)
- def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None):
+ def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None,
+ provider='gs'):
"""Creates a test bucket.
The bucket and all of its contents will be deleted after the test.
@@ -196,15 +328,16 @@
a list of object names to place in the bucket. Defaults to
0.
storage_class: storage class to use. If not provided we us standard.
+ provider: string provider to use, default gs.
Returns:
StorageUri for the created bucket.
"""
bucket_name = bucket_name or self.MakeTempName('bucket')
bucket_uri = boto.storage_uri(
- 'gs://%s' % bucket_name.lower(),
+ '%s://%s' % (provider, bucket_name.lower()),
suppress_consec_slashes=False,
- bucket_storage_uri_class=GSMockBucketStorageUri)
+ bucket_storage_uri_class=util.GSMockBucketStorageUri)
bucket_uri.create_bucket(storage_class=storage_class)
self.bucket_uris.append(bucket_uri)
try:
@@ -220,8 +353,8 @@
"""Creates a test object.
Args:
- bucket: The URI of the bucket to place the object in. If not specified, a
- new temporary bucket is created.
+ bucket_uri: The URI of the bucket to place the object in. If not
+ specified, a new temporary bucket is created.
object_name: The name to use for the object. If not specified, a temporary
test object name is constructed.
contents: The contents to write to the object. If not specified, the key
« no previous file with comments | « gslib/tests/testcase/integration_testcase.py ('k') | gslib/tests/util.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698