| OLD | NEW |
| 1 # -*- coding: utf-8 -*- |
| 1 # Copyright 2013 Google Inc. All Rights Reserved. | 2 # Copyright 2013 Google Inc. All Rights Reserved. |
| 2 # | 3 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 4 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 5 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 6 # You may obtain a copy of the License at |
| 6 # | 7 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 8 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 9 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 10 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 11 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and | 13 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. | 14 # limitations under the License. |
| 14 | |
| 15 """Contains gsutil base unit test case class.""" | 15 """Contains gsutil base unit test case class.""" |
| 16 | 16 |
| 17 from __future__ import absolute_import |
| 18 |
| 19 import logging |
| 17 import os | 20 import os |
| 18 import sys | 21 import sys |
| 19 import tempfile | 22 import tempfile |
| 20 | 23 |
| 21 import boto | 24 import boto |
| 22 | |
| 23 from gslib import wildcard_iterator | 25 from gslib import wildcard_iterator |
| 26 from gslib.boto_translation import BotoTranslation |
| 27 from gslib.cloud_api_delegator import CloudApiDelegator |
| 24 from gslib.command_runner import CommandRunner | 28 from gslib.command_runner import CommandRunner |
| 25 from gslib.project_id import ProjectIdHandler | 29 from gslib.cs_api_map import ApiMapConstants |
| 30 from gslib.cs_api_map import ApiSelector |
| 31 from gslib.tests.mock_logging_handler import MockLoggingHandler |
| 32 from gslib.tests.testcase import base |
| 26 import gslib.tests.util as util | 33 import gslib.tests.util as util |
| 27 from gslib.tests.util import unittest | 34 from gslib.tests.util import unittest |
| 28 import base | |
| 29 | |
| 30 # The mock storage service comes from the Boto library, but it is not | |
| 31 # distributed with Boto when installed as a package. To get around this, we | |
| 32 # copy the file to gslib/tests/mock_storage_service.py when building the gsutil | |
| 33 # package. Try and import from several places here to find it. | |
| 34 try: | |
| 35 from gslib.tests import mock_storage_service | |
| 36 except ImportError: | |
| 37 try: | |
| 38 from boto.tests.integration.s3 import mock_storage_service | |
| 39 except ImportError: | |
| 40 try: | |
| 41 from tests.integration.s3 import mock_storage_service | |
| 42 except ImportError: | |
| 43 import mock_storage_service | |
| 44 | 35 |
| 45 | 36 |
| 46 class GSMockConnection(mock_storage_service.MockConnection): | 37 class GsutilApiUnitTestClassMapFactory(object): |
| 38 """Class map factory for use in unit tests. |
| 47 | 39 |
| 48 def __init__(self, *args, **kwargs): | 40 BotoTranslation is used for all cases so that GSMockBucketStorageUri can |
| 49 kwargs['provider'] = 'gs' | 41 be used to communicate with the mock XML service. |
| 50 super(GSMockConnection, self).__init__(*args, **kwargs) | 42 """ |
| 51 | 43 |
| 52 mock_connection = GSMockConnection() | 44 @classmethod |
| 45 def GetClassMap(cls): |
| 46 """Returns a class map for use in unit tests.""" |
| 47 gs_class_map = { |
| 48 ApiSelector.XML: BotoTranslation, |
| 49 ApiSelector.JSON: BotoTranslation |
| 50 } |
| 51 s3_class_map = { |
| 52 ApiSelector.XML: BotoTranslation |
| 53 } |
| 54 class_map = { |
| 55 'gs': gs_class_map, |
| 56 's3': s3_class_map |
| 57 } |
| 58 return class_map |
| 53 | 59 |
| 54 | 60 |
| 55 class GSMockBucketStorageUri(mock_storage_service.MockBucketStorageUri): | |
| 56 | |
| 57 def connect(self, access_key_id=None, secret_access_key=None): | |
| 58 return mock_connection | |
| 59 | |
| 60 def compose(self, components, headers=None): | |
| 61 """Dummy implementation to allow parallel uploads with tests.""" | |
| 62 return self.new_key() | |
| 63 | |
| 64 @unittest.skipUnless(util.RUN_UNIT_TESTS, | 61 @unittest.skipUnless(util.RUN_UNIT_TESTS, |
| 65 'Not running integration tests.') | 62 'Not running integration tests.') |
| 66 class GsUtilUnitTestCase(base.GsUtilTestCase): | 63 class GsUtilUnitTestCase(base.GsUtilTestCase): |
| 67 """Base class for gsutil unit tests.""" | 64 """Base class for gsutil unit tests.""" |
| 68 | 65 |
| 69 @classmethod | 66 @classmethod |
| 70 def setUpClass(cls): | 67 def setUpClass(cls): |
| 71 base.GsUtilTestCase.setUpClass() | 68 base.GsUtilTestCase.setUpClass() |
| 72 cls.mock_bucket_storage_uri = GSMockBucketStorageUri | 69 cls.mock_bucket_storage_uri = util.GSMockBucketStorageUri |
| 73 cls.proj_id_handler = ProjectIdHandler() | 70 cls.mock_gsutil_api_class_map_factory = GsutilApiUnitTestClassMapFactory |
| 74 config_file_list = boto.pyami.config.BotoConfigLocations | 71 cls.logger = logging.getLogger() |
| 75 # Use "gsutil_test_commands" as a fake UserAgent. This value will never be | 72 cls.command_runner = CommandRunner( |
| 76 # sent via HTTP because we're using MockStorageService here. | 73 bucket_storage_uri_class=cls.mock_bucket_storage_uri, |
| 77 cls.command_runner = CommandRunner(config_file_list, | 74 gsutil_api_class_map_factory=cls.mock_gsutil_api_class_map_factory) |
| 78 cls.mock_bucket_storage_uri) | |
| 79 | 75 |
| 80 def setUp(self): | 76 def setUp(self): |
| 81 super(GsUtilUnitTestCase, self).setUp() | 77 super(GsUtilUnitTestCase, self).setUp() |
| 82 self.bucket_uris = [] | 78 self.bucket_uris = [] |
| 79 self.stdout_save = sys.stdout |
| 80 self.stderr_save = sys.stderr |
| 81 fd, self.stdout_file = tempfile.mkstemp() |
| 82 sys.stdout = os.fdopen(fd, 'w+') |
| 83 fd, self.stderr_file = tempfile.mkstemp() |
| 84 sys.stderr = os.fdopen(fd, 'w+') |
| 85 self.accumulated_stdout = [] |
| 86 self.accumulated_stderr = [] |
| 87 |
| 88 self.root_logger = logging.getLogger() |
| 89 self.is_debugging = self.root_logger.isEnabledFor(logging.DEBUG) |
| 90 self.log_handlers_save = self.root_logger.handlers |
| 91 fd, self.log_handler_file = tempfile.mkstemp() |
| 92 self.log_handler_stream = os.fdopen(fd, 'w+') |
| 93 self.temp_log_handler = logging.StreamHandler(self.log_handler_stream) |
| 94 self.root_logger.handlers = [self.temp_log_handler] |
| 95 |
| 96 def tearDown(self): |
| 97 super(GsUtilUnitTestCase, self).tearDown() |
| 98 |
| 99 self.root_logger.handlers = self.log_handlers_save |
| 100 self.temp_log_handler.flush() |
| 101 self.log_handler_stream.seek(0) |
| 102 log_output = self.log_handler_stream.read() |
| 103 self.log_handler_stream.close() |
| 104 os.unlink(self.log_handler_file) |
| 105 |
| 106 sys.stdout.seek(0) |
| 107 sys.stderr.seek(0) |
| 108 stdout = sys.stdout.read() |
| 109 stderr = sys.stderr.read() |
| 110 stdout += ''.join(self.accumulated_stdout) |
| 111 stderr += ''.join(self.accumulated_stderr) |
| 112 sys.stdout.close() |
| 113 sys.stderr.close() |
| 114 sys.stdout = self.stdout_save |
| 115 sys.stderr = self.stderr_save |
| 116 os.unlink(self.stdout_file) |
| 117 os.unlink(self.stderr_file) |
| 118 |
| 119 if self.is_debugging and stdout: |
| 120 sys.stderr.write('==== stdout %s ====\n' % self.id()) |
| 121 sys.stderr.write(stdout) |
| 122 sys.stderr.write('==== end stdout ====\n') |
| 123 if self.is_debugging and stderr: |
| 124 sys.stderr.write('==== stderr %s ====\n' % self.id()) |
| 125 sys.stderr.write(stderr) |
| 126 sys.stderr.write('==== end stderr ====\n') |
| 127 if self.is_debugging and log_output: |
| 128 sys.stderr.write('==== log output %s ====\n' % self.id()) |
| 129 sys.stderr.write(log_output) |
| 130 sys.stderr.write('==== end log output ====\n') |
| 83 | 131 |
| 84 def RunCommand(self, command_name, args=None, headers=None, debug=0, | 132 def RunCommand(self, command_name, args=None, headers=None, debug=0, |
| 85 test_method=None, return_stdout=False, cwd=None): | 133 test_method=None, return_stdout=False, return_stderr=False, |
| 86 """ | 134 return_log_handler=False, cwd=None): |
| 87 Method for calling gslib.command_runner.CommandRunner, passing | 135 """Method for calling gslib.command_runner.CommandRunner. |
| 88 parallel_operations=False for all tests, optionally saving/returning stdout | 136 |
| 89 output. We run all tests multi-threaded, to exercise those more complicated | 137 Passes parallel_operations=False for all tests, optionally saving/returning |
| 90 code paths. | 138 stdout output. We run all tests multi-threaded, to exercise those more |
| 91 TODO: change to run with parallel_operations=True for all tests. At | 139 complicated code paths. |
| 140 TODO: Change to run with parallel_operations=True for all tests. At |
| 92 present when you do this it causes many test failures. | 141 present when you do this it causes many test failures. |
| 93 | 142 |
| 94 Args: | 143 Args: |
| 95 command_name: The name of the command being run. | 144 command_name: The name of the command being run. |
| 96 args: Command-line args (arg0 = actual arg, not command name ala bash). | 145 args: Command-line args (arg0 = actual arg, not command name ala bash). |
| 97 headers: Dictionary containing optional HTTP headers to pass to boto. | 146 headers: Dictionary containing optional HTTP headers to pass to boto. |
| 98 debug: Debug level to pass in to boto connection (range 0..3). | 147 debug: Debug level to pass in to boto connection (range 0..3). |
| 99 parallel_operations: Should command operations be executed in parallel? | |
| 100 test_method: Optional general purpose method for testing purposes. | 148 test_method: Optional general purpose method for testing purposes. |
| 101 Application and semantics of this method will vary by | 149 Application and semantics of this method will vary by |
| 102 command and test type. | 150 command and test type. |
| 151 return_stdout: If True, will save and return stdout produced by command. |
| 152 return_stderr: If True, will save and return stderr produced by command. |
| 153 return_log_handler: If True, will return a MockLoggingHandler instance |
| 154 that was attached to the command's logger while running. |
| 103 cwd: The working directory that should be switched to before running the | 155 cwd: The working directory that should be switched to before running the |
| 104 command. The working directory will be reset back to its original | 156 command. The working directory will be reset back to its original |
| 105 value after running the command. If not specified, the working | 157 value after running the command. If not specified, the working |
| 106 directory is left unchanged. | 158 directory is left unchanged. |
| 107 return_stdout: If true will save and return stdout produced by command. | 159 |
| 160 Returns: |
| 161 One or a tuple of requested return values, depending on whether |
| 162 return_stdout, return_stderr, and/or return_log_handler were specified. |
| 108 """ | 163 """ |
| 109 if util.VERBOSE_OUTPUT: | 164 args = args or [] |
| 110 sys.stderr.write('\nRunning test of %s %s\n' % | |
| 111 (command_name, ' '.join(args))) | |
| 112 if return_stdout: | |
| 113 # Redirect stdout temporarily, to save output to a file. | |
| 114 fh, outfile = tempfile.mkstemp() | |
| 115 os.close(fh) | |
| 116 elif not util.VERBOSE_OUTPUT: | |
| 117 outfile = os.devnull | |
| 118 else: | |
| 119 outfile = None | |
| 120 | 165 |
| 121 stdout_sav = sys.stdout | 166 command_line = ' '.join([command_name] + args) |
| 122 output = None | 167 if self.is_debugging: |
| 168 self.stderr_save.write('\nRunCommand of %s\n' % command_line) |
| 169 |
| 170 # Save and truncate stdout and stderr for the lifetime of RunCommand. This |
| 171 # way, we can return just the stdout and stderr that was output during the |
| 172 # RunNamedCommand call below. |
| 173 sys.stdout.seek(0) |
| 174 sys.stderr.seek(0) |
| 175 stdout = sys.stdout.read() |
| 176 stderr = sys.stderr.read() |
| 177 if stdout: |
| 178 self.accumulated_stdout.append(stdout) |
| 179 if stderr: |
| 180 self.accumulated_stderr.append(stderr) |
| 181 sys.stdout.seek(0) |
| 182 sys.stderr.seek(0) |
| 183 sys.stdout.truncate() |
| 184 sys.stderr.truncate() |
| 185 |
| 123 cwd_sav = None | 186 cwd_sav = None |
| 124 try: | 187 try: |
| 125 cwd_sav = os.getcwd() | 188 cwd_sav = os.getcwd() |
| 126 except OSError: | 189 except OSError: |
| 127 # This can happen if the current working directory no longer exists. | 190 # This can happen if the current working directory no longer exists. |
| 128 pass | 191 pass |
| 192 |
| 193 mock_log_handler = MockLoggingHandler() |
| 194 logging.getLogger(command_name).addHandler(mock_log_handler) |
| 195 |
| 129 try: | 196 try: |
| 130 if outfile: | |
| 131 fp = open(outfile, 'w') | |
| 132 sys.stdout = fp | |
| 133 if cwd: | 197 if cwd: |
| 134 os.chdir(cwd) | 198 os.chdir(cwd) |
| 135 self.command_runner.RunNamedCommand( | 199 self.command_runner.RunNamedCommand( |
| 136 command_name, args=args, headers=headers, debug=debug, | 200 command_name, args=args, headers=headers, debug=debug, |
| 137 parallel_operations=False, test_method=test_method) | 201 parallel_operations=False, test_method=test_method, do_shutdown=False) |
| 138 finally: | 202 finally: |
| 139 if cwd and cwd_sav: | 203 if cwd and cwd_sav: |
| 140 os.chdir(cwd_sav) | 204 os.chdir(cwd_sav) |
| 141 if outfile: | |
| 142 fp.close() | |
| 143 sys.stdout = stdout_sav | |
| 144 with open(outfile, 'r') as f: | |
| 145 output = f.read() | |
| 146 if return_stdout: | |
| 147 os.unlink(outfile) | |
| 148 | 205 |
| 149 if output is not None and return_stdout: | 206 sys.stdout.seek(0) |
| 150 return output | 207 stdout = sys.stdout.read() |
| 208 sys.stderr.seek(0) |
| 209 stderr = sys.stderr.read() |
| 210 logging.getLogger(command_name).removeHandler(mock_log_handler) |
| 211 |
| 212 log_output = '\n'.join( |
| 213 '%s:\n ' % level + '\n '.join(records) |
| 214 for level, records in mock_log_handler.messages.iteritems() |
| 215 if records) |
| 216 if self.is_debugging and log_output: |
| 217 self.stderr_save.write( |
| 218 '==== logging RunCommand %s %s ====\n' % (self.id(), command_line)) |
| 219 self.stderr_save.write(log_output) |
| 220 self.stderr_save.write('\n==== end logging ====\n') |
| 221 if self.is_debugging and stdout: |
| 222 self.stderr_save.write( |
| 223 '==== stdout RunCommand %s %s ====\n' % (self.id(), command_line)) |
| 224 self.stderr_save.write(stdout) |
| 225 self.stderr_save.write('==== end stdout ====\n') |
| 226 if self.is_debugging and stderr: |
| 227 self.stderr_save.write( |
| 228 '==== stderr RunCommand %s %s ====\n' % (self.id(), command_line)) |
| 229 self.stderr_save.write(stderr) |
| 230 self.stderr_save.write('==== end stderr ====\n') |
| 231 |
| 232 # Reset stdout and stderr files, so that we won't print them out again |
| 233 # in tearDown if debugging is enabled. |
| 234 sys.stdout.seek(0) |
| 235 sys.stderr.seek(0) |
| 236 sys.stdout.truncate() |
| 237 sys.stderr.truncate() |
| 238 |
| 239 to_return = [] |
| 240 if return_stdout: |
| 241 to_return.append(stdout) |
| 242 if return_stderr: |
| 243 to_return.append(stderr) |
| 244 if return_log_handler: |
| 245 to_return.append(mock_log_handler) |
| 246 if len(to_return) == 1: |
| 247 return to_return[0] |
| 248 return tuple(to_return) |
| 151 | 249 |
| 152 @classmethod | 250 @classmethod |
| 153 def _test_wildcard_iterator(cls, uri_or_str, debug=0): | 251 def _test_wildcard_iterator(cls, uri_or_str, debug=0): |
| 154 """ | 252 """Convenience method for instantiating a test instance of WildcardIterator. |
| 155 Convenience method for instantiating a testing instance of | 253 |
| 156 WildCardIterator, without having to specify all the params of that class | 254 This makes it unnecessary to specify all the params of that class |
| 157 (like bucket_storage_uri_class=mock_storage_service.MockBucketStorageUri). | 255 (like bucket_storage_uri_class=mock_storage_service.MockBucketStorageUri). |
| 158 Also naming the factory method this way makes it clearer in the test code | 256 Also, naming the factory method this way makes it clearer in the test code |
| 159 that WildcardIterator needs to be set up for testing. | 257 that WildcardIterator needs to be set up for testing. |
| 160 | 258 |
| 161 Args are same as for wildcard_iterator.wildcard_iterator(), except there's | 259 Args are same as for wildcard_iterator.wildcard_iterator(), except |
| 162 no bucket_storage_uri_class arg. | 260 there are no class args for bucket_storage_uri_class or gsutil_api_class. |
| 261 |
| 262 Args: |
| 263 uri_or_str: StorageUri or string representing the wildcard string. |
| 264 debug: debug level to pass to the underlying connection (0..3) |
| 163 | 265 |
| 164 Returns: | 266 Returns: |
| 165 WildcardIterator.IterUris(), over which caller can iterate. | 267 WildcardIterator, over which caller can iterate. |
| 166 """ | 268 """ |
| 167 return wildcard_iterator.wildcard_iterator( | 269 # TODO: Remove when tests no longer pass StorageUri arguments. |
| 168 uri_or_str, cls.proj_id_handler, cls.mock_bucket_storage_uri, | 270 uri_string = uri_or_str |
| 271 if hasattr(uri_or_str, 'uri'): |
| 272 uri_string = uri_or_str.uri |
| 273 |
| 274 cls.gsutil_api_map = { |
| 275 ApiMapConstants.API_MAP: ( |
| 276 cls.mock_gsutil_api_class_map_factory.GetClassMap()), |
| 277 ApiMapConstants.SUPPORT_MAP: { |
| 278 'gs': [ApiSelector.XML, ApiSelector.JSON], |
| 279 's3': [ApiSelector.XML] |
| 280 }, |
| 281 ApiMapConstants.DEFAULT_MAP: { |
| 282 'gs': ApiSelector.JSON, |
| 283 's3': ApiSelector.XML |
| 284 } |
| 285 } |
| 286 |
| 287 cls.gsutil_api = CloudApiDelegator( |
| 288 cls.mock_bucket_storage_uri, cls.gsutil_api_map, cls.logger, |
| 169 debug=debug) | 289 debug=debug) |
| 170 | 290 |
| 291 return wildcard_iterator.CreateWildcardIterator(uri_string, cls.gsutil_api) |
| 292 |
| 171 @staticmethod | 293 @staticmethod |
| 172 def _test_storage_uri(uri_str, default_scheme='file', debug=0, | 294 def _test_storage_uri(uri_str, default_scheme='file', debug=0, |
| 173 validate=True): | 295 validate=True): |
| 174 """ | 296 """Convenience method for instantiating a testing instance of StorageUri. |
| 175 Convenience method for instantiating a testing | 297 |
| 176 instance of StorageUri, without having to specify | 298 This makes it unnecessary to specify |
| 177 bucket_storage_uri_class=mock_storage_service.MockBucketStorageUri. | 299 bucket_storage_uri_class=mock_storage_service.MockBucketStorageUri. |
| 178 Also naming the factory method this way makes it clearer in the test | 300 Also naming the factory method this way makes it clearer in the test |
| 179 code that StorageUri needs to be set up for testing. | 301 code that StorageUri needs to be set up for testing. |
| 180 | 302 |
| 181 Args, Returns, and Raises are same as for boto.storage_uri(), except there's | 303 Args, Returns, and Raises are same as for boto.storage_uri(), except there's |
| 182 no bucket_storage_uri_class arg. | 304 no bucket_storage_uri_class arg. |
| 305 |
| 306 Args: |
| 307 uri_str: Uri string to create StorageUri for. |
| 308 default_scheme: Default scheme for the StorageUri |
| 309 debug: debug level to pass to the underlying connection (0..3) |
| 310 validate: If True, validate the resource that the StorageUri refers to. |
| 311 |
| 312 Returns: |
| 313 StorageUri based on the arguments. |
| 183 """ | 314 """ |
| 184 return boto.storage_uri(uri_str, default_scheme, debug, validate, | 315 return boto.storage_uri(uri_str, default_scheme, debug, validate, |
| 185 GSMockBucketStorageUri) | 316 util.GSMockBucketStorageUri) |
| 186 | 317 |
| 187 def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None): | 318 def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None, |
| 319 provider='gs'): |
| 188 """Creates a test bucket. | 320 """Creates a test bucket. |
| 189 | 321 |
| 190 The bucket and all of its contents will be deleted after the test. | 322 The bucket and all of its contents will be deleted after the test. |
| 191 | 323 |
| 192 Args: | 324 Args: |
| 193 bucket_name: Create the bucket with this name. If not provided, a | 325 bucket_name: Create the bucket with this name. If not provided, a |
| 194 temporary test bucket name is constructed. | 326 temporary test bucket name is constructed. |
| 195 test_objects: The number of objects that should be placed in the bucket or | 327 test_objects: The number of objects that should be placed in the bucket or |
| 196 a list of object names to place in the bucket. Defaults to | 328 a list of object names to place in the bucket. Defaults to |
| 197 0. | 329 0. |
| 198 storage_class: storage class to use. If not provided we us standard. | 330 storage_class: storage class to use. If not provided we us standard. |
| 331 provider: string provider to use, default gs. |
| 199 | 332 |
| 200 Returns: | 333 Returns: |
| 201 StorageUri for the created bucket. | 334 StorageUri for the created bucket. |
| 202 """ | 335 """ |
| 203 bucket_name = bucket_name or self.MakeTempName('bucket') | 336 bucket_name = bucket_name or self.MakeTempName('bucket') |
| 204 bucket_uri = boto.storage_uri( | 337 bucket_uri = boto.storage_uri( |
| 205 'gs://%s' % bucket_name.lower(), | 338 '%s://%s' % (provider, bucket_name.lower()), |
| 206 suppress_consec_slashes=False, | 339 suppress_consec_slashes=False, |
| 207 bucket_storage_uri_class=GSMockBucketStorageUri) | 340 bucket_storage_uri_class=util.GSMockBucketStorageUri) |
| 208 bucket_uri.create_bucket(storage_class=storage_class) | 341 bucket_uri.create_bucket(storage_class=storage_class) |
| 209 self.bucket_uris.append(bucket_uri) | 342 self.bucket_uris.append(bucket_uri) |
| 210 try: | 343 try: |
| 211 iter(test_objects) | 344 iter(test_objects) |
| 212 except TypeError: | 345 except TypeError: |
| 213 test_objects = [self.MakeTempName('obj') for _ in range(test_objects)] | 346 test_objects = [self.MakeTempName('obj') for _ in range(test_objects)] |
| 214 for i, name in enumerate(test_objects): | 347 for i, name in enumerate(test_objects): |
| 215 self.CreateObject(bucket_uri=bucket_uri, object_name=name, | 348 self.CreateObject(bucket_uri=bucket_uri, object_name=name, |
| 216 contents='test %d' % i) | 349 contents='test %d' % i) |
| 217 return bucket_uri | 350 return bucket_uri |
| 218 | 351 |
| 219 def CreateObject(self, bucket_uri=None, object_name=None, contents=None): | 352 def CreateObject(self, bucket_uri=None, object_name=None, contents=None): |
| 220 """Creates a test object. | 353 """Creates a test object. |
| 221 | 354 |
| 222 Args: | 355 Args: |
| 223 bucket: The URI of the bucket to place the object in. If not specified, a | 356 bucket_uri: The URI of the bucket to place the object in. If not |
| 224 new temporary bucket is created. | 357 specified, a new temporary bucket is created. |
| 225 object_name: The name to use for the object. If not specified, a temporary | 358 object_name: The name to use for the object. If not specified, a temporary |
| 226 test object name is constructed. | 359 test object name is constructed. |
| 227 contents: The contents to write to the object. If not specified, the key | 360 contents: The contents to write to the object. If not specified, the key |
| 228 is not written to, which means that it isn't actually created | 361 is not written to, which means that it isn't actually created |
| 229 yet on the server. | 362 yet on the server. |
| 230 | 363 |
| 231 Returns: | 364 Returns: |
| 232 A StorageUri for the created object. | 365 A StorageUri for the created object. |
| 233 """ | 366 """ |
| 234 bucket_uri = bucket_uri or self.CreateBucket() | 367 bucket_uri = bucket_uri or self.CreateBucket() |
| 235 object_name = object_name or self.MakeTempName('obj') | 368 object_name = object_name or self.MakeTempName('obj') |
| 236 key_uri = bucket_uri.clone_replace_name(object_name) | 369 key_uri = bucket_uri.clone_replace_name(object_name) |
| 237 if contents is not None: | 370 if contents is not None: |
| 238 key_uri.set_contents_from_string(contents) | 371 key_uri.set_contents_from_string(contents) |
| 239 return key_uri | 372 return key_uri |
| OLD | NEW |