OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2010 Google Inc. |
| 3 # |
| 4 # Permission is hereby granted, free of charge, to any person obtaining a |
| 5 # copy of this software and associated documentation files (the |
| 6 # "Software"), to deal in the Software without restriction, including |
| 7 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 8 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 9 # persons to whom the Software is furnished to do so, subject to the fol- |
| 10 # lowing conditions: |
| 11 # |
| 12 # The above copyright notice and this permission notice shall be included |
| 13 # in all copies or substantial portions of the Software. |
| 14 # |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 # IN THE SOFTWARE. |
| 22 |
| 23 """Tests for gsutil commands. |
| 24 The test code in this file runs against an in-memory storage service mock, |
| 25 so runs very quickly. This is valuable for testing changes that impact the |
| 26 naming rules, since those rules are complex and it's useful to be able to |
| 27 make small incremental changes and rerun the tests frequently. Additional |
| 28 end-to-end tests (which send traffic to the production Google Cloud Storage |
| 29 service) are available via the gsutil test command. |
| 30 """ |
| 31 |
| 32 import os |
| 33 import shutil |
| 34 import sys |
| 35 import tempfile |
| 36 import time |
| 37 import unittest |
| 38 import wildcard_iterator |
| 39 |
| 40 # Put local libs at front of path so tests will run latest lib code rather |
| 41 # than whatever code is found on user's PYTHONPATH. |
| 42 sys.path.insert(0, '.') |
| 43 sys.path.insert(0, 'boto') |
| 44 import boto |
| 45 |
| 46 from boto.exception import StorageResponseError |
| 47 from boto import storage_uri |
| 48 from gslib.command_runner import CommandRunner |
| 49 from gslib.commands import cp |
| 50 from gslib.exception import CommandException |
| 51 from gslib import test_util |
| 52 from tests.integration.s3.mock_storage_service import MockBucketStorageUri |
| 53 |
| 54 |
| 55 class GsutilCommandTests(unittest.TestCase): |
| 56 """gsutil command method test suite""" |
| 57 |
| 58 # We don't use the gsutil boto config discovery logic here, as it assumes |
| 59 # boto is a subdirectory of where the command is running, which is gslib |
| 60 # when running these tests. Instead we use a simplified setup: |
| 61 gsutil_bin_dir = '.' |
| 62 boto_lib_dir = os.path.join(gsutil_bin_dir, 'boto') |
| 63 config_file_list = boto.pyami.config.BotoConfigLocations |
| 64 # Use "gsutil_test_commands" as a fake UserAgent. This value will never be |
| 65 # sent via HTTP because we're using MockStorageService here. |
| 66 command_runner = CommandRunner(gsutil_bin_dir, boto_lib_dir, config_file_list, |
| 67 "gsutil_test_commands", MockBucketStorageUri) |
| 68 |
| 69 def GetSuiteDescription(self): |
| 70 return 'gsutil command method test suite' |
| 71 |
| 72 @classmethod |
| 73 def tearDown(cls): |
| 74 """Deletes any objects or files created by last test run""" |
| 75 try: |
| 76 for key_uri in test_util.test_wildcard_iterator( |
| 77 '%s**' % cls.dst_bucket_uri).IterUris(): |
| 78 key_uri.delete_key() |
| 79 # For some reason trying to catch except |
| 80 # wildcard_iterator.WildcardException doesn't work here. |
| 81 except Exception: |
| 82 # Ignore cleanup failures. |
| 83 pass |
| 84 # Recursively delete dst dir and then re-create it, so in effect we |
| 85 # remove all dirs and files under that directory. |
| 86 shutil.rmtree(cls.dst_dir_root) |
| 87 os.mkdir(cls.dst_dir_root) |
| 88 |
| 89 @classmethod |
| 90 def SrcFile(cls, fname): |
| 91 """Returns path for given test src file""" |
| 92 for path in cls.all_src_file_paths: |
| 93 if path.find(fname) != -1: |
| 94 return path |
| 95 raise Exception('SrcFile(%s): no match' % fname) |
| 96 |
| 97 @classmethod |
| 98 def CreateEmptyObject(cls, uri): |
| 99 """Creates an empty object for the given StorageUri""" |
| 100 key = uri.new_key() |
| 101 key.set_contents_from_string('') |
| 102 |
| 103 @classmethod |
| 104 def SetUpClass(cls): |
| 105 """Initializes test suite. |
| 106 |
| 107 Creates a source bucket containing 9 objects, 3 of which live under a |
| 108 subdir, 3 of which lives under a nested subdir; a source directory |
| 109 containing a subdirectory and file; and a destination bucket and directory. |
| 110 """ |
| 111 cls.uri_base_str = 'gs://gsutil_test_%s' % int(time.time()) |
| 112 # Use a designated tmpdir prefix to make it easy to find the end of |
| 113 # the tmp path. |
| 114 cls.tmpdir_prefix = 'tmp_gstest' |
| 115 |
| 116 # Create the test buckets. |
| 117 cls.src_bucket_uri = test_util.test_storage_uri('%s_src' % cls.uri_base_str) |
| 118 cls.dst_bucket_uri = test_util.test_storage_uri('%s_dst' % cls.uri_base_str) |
| 119 cls.src_bucket_uri.create_bucket() |
| 120 |
| 121 # Define the src and dest bucket subdir paths. Note that they exclude |
| 122 # a slash on the end so we can test handling of bucket subdirs specified |
| 123 # both with and without terminating slashes. |
| 124 cls.src_bucket_subdir_uri = test_util.test_storage_uri( |
| 125 '%s_src/src_subdir' % cls.uri_base_str) |
| 126 cls.src_bucket_subdir_uri_wildcard = test_util.test_storage_uri( |
| 127 '%s_src/src_sub*' % cls.uri_base_str) |
| 128 cls.dst_bucket_subdir_uri = test_util.test_storage_uri( |
| 129 '%s_dst/dst_subdir' % cls.uri_base_str) |
| 130 cls.dst_bucket_uri.create_bucket() |
| 131 |
| 132 # Create the test objects in the src bucket. |
| 133 cls.all_src_obj_uris = [] |
| 134 cls.all_src_top_level_obj_uris = [] |
| 135 cls.all_src_subdir_obj_uris = [] |
| 136 cls.all_src_subdir_and_below_obj_uris = [] |
| 137 for i in range(3): |
| 138 obj_uri = test_util.test_storage_uri('%sobj%d' % (cls.src_bucket_uri, i)) |
| 139 cls.CreateEmptyObject(obj_uri) |
| 140 cls.all_src_obj_uris.append(obj_uri) |
| 141 cls.all_src_top_level_obj_uris.append(obj_uri) |
| 142 # Subdir objects |
| 143 for i in range(4, 6): |
| 144 obj_uri = test_util.test_storage_uri( |
| 145 '%s/obj%d' % (cls.src_bucket_subdir_uri, i)) |
| 146 cls.CreateEmptyObject(obj_uri) |
| 147 cls.all_src_obj_uris.append(obj_uri) |
| 148 cls.all_src_subdir_obj_uris.append(obj_uri) |
| 149 cls.all_src_subdir_and_below_obj_uris.append(obj_uri) |
| 150 # Nested subdir objects |
| 151 for i in range(7, 9): |
| 152 obj_uri = test_util.test_storage_uri( |
| 153 '%s/nested/obj%d' % (cls.src_bucket_subdir_uri, i)) |
| 154 cls.CreateEmptyObject(obj_uri) |
| 155 cls.all_src_obj_uris.append(obj_uri) |
| 156 cls.all_src_subdir_and_below_obj_uris.append(obj_uri) |
| 157 |
| 158 # Create the test directories. |
| 159 cls.src_dir_root = '%s%s' % (tempfile.mkdtemp(prefix=cls.tmpdir_prefix), |
| 160 os.sep) |
| 161 nested_subdir = '%sdir0%sdir1' % (cls.src_dir_root, os.sep) |
| 162 os.makedirs(nested_subdir) |
| 163 cls.dst_dir_root = '%s%s' % (tempfile.mkdtemp(prefix=cls.tmpdir_prefix), |
| 164 os.sep) |
| 165 |
| 166 # Create the test files in the src directory. |
| 167 cls.all_src_file_paths = [] |
| 168 cls.nested_child_file_paths = ['f0', 'f1', 'f2.txt', 'dir0/dir1/nested'] |
| 169 cls.non_nested_file_names = ['f0', 'f1', 'f2.txt'] |
| 170 file_names = ['f0', 'f1', 'f2.txt', 'dir0%sdir1%snested' % (os.sep, os.sep)] |
| 171 file_paths = ['%s%s' % (cls.src_dir_root, f) for f in file_names] |
| 172 for file_path in file_paths: |
| 173 f = open(file_path, 'w') |
| 174 f.write('test data') |
| 175 f.close() |
| 176 cls.all_src_file_paths.append(file_path) |
| 177 cls.tmp_path = '%s%s' % (cls.src_dir_root, 'tmp0') |
| 178 |
| 179 cls.created_test_data = True |
| 180 |
| 181 @classmethod |
| 182 def TearDownClass(cls): |
| 183 """Cleans up buckets and directories created by SetUpClass""" |
| 184 |
| 185 if not hasattr(cls, 'created_test_data'): |
| 186 return |
| 187 # Call cls.tearDown() in case the tests got interrupted, to ensure |
| 188 # dst objects and files get deleted. |
| 189 cls.tearDown() |
| 190 # Now delete src objects and files, and all buckets and dirs. |
| 191 try: |
| 192 for key_uri in test_util.test_wildcard_iterator( |
| 193 '%s**' % cls.src_bucket_uri).IterUris(): |
| 194 key_uri.delete_key() |
| 195 except wildcard_iterator.WildcardException: |
| 196 # Ignore cleanup failures. |
| 197 pass |
| 198 try: |
| 199 for key_uri in test_util.test_wildcard_iterator( |
| 200 '%s**' % cls.src_dir_root).IterUris(): |
| 201 key_uri.delete_key() |
| 202 except wildcard_iterator.WildcardException: |
| 203 # Ignore cleanup failures. |
| 204 pass |
| 205 cls.src_bucket_uri.delete_bucket() |
| 206 cls.dst_bucket_uri.delete_bucket() |
| 207 shutil.rmtree(cls.src_dir_root) |
| 208 shutil.rmtree(cls.dst_dir_root) |
| 209 |
| 210 |
| 211 def RunCommand(self, command_name, args=None, headers=None, debug=0, |
| 212 test_method=None, return_stdout=False): |
| 213 """ |
| 214 Method for calling gslib.command_runner.CommandRunner, passing |
| 215 parallel_operations=False for all tests, optionally saving/returning stdout |
| 216 output. We run all tests multi-threaded, to exercise those more complicated |
| 217 code paths. |
| 218 TODO: change to run with parallel_operations=True for all tests. At |
| 219 present when you do this it causes many test failures. |
| 220 |
| 221 Args: |
| 222 command_name: The name of the command being run. |
| 223 args: Command-line args (arg0 = actual arg, not command name ala bash). |
| 224 headers: Dictionary containing optional HTTP headers to pass to boto. |
| 225 debug: Debug level to pass in to boto connection (range 0..3). |
| 226 parallel_operations: Should command operations be executed in parallel? |
| 227 test_method: Optional general purpose method for testing purposes. |
| 228 Application and semantics of this method will vary by |
| 229 command and test type. |
| 230 return_stdout: If true will save and return stdout produced by command. |
| 231 """ |
| 232 sys.stderr.write('\nRunning test of %s %s\n' % |
| 233 (command_name, ' '.join(args))) |
| 234 if return_stdout: |
| 235 # Redirect stdout temporarily, to save output to a file. |
| 236 tmpfile = tempfile.mkstemp()[1] |
| 237 stdout_sav = sys.stdout |
| 238 try: |
| 239 fp = open(tmpfile, 'w') |
| 240 sys.stdout = fp |
| 241 self.command_runner.RunNamedCommand( |
| 242 command_name, args=args, headers=headers, debug=debug, |
| 243 parallel_operations=False, test_method=test_method) |
| 244 finally: |
| 245 fp.close() |
| 246 sys.stdout = stdout_sav |
| 247 output = open(tmpfile, 'r').read() |
| 248 os.unlink(tmpfile) |
| 249 return output |
| 250 else: |
| 251 self.command_runner.RunNamedCommand( |
| 252 command_name, args=args, headers=headers, debug=debug, |
| 253 parallel_operations=False, test_method=test_method) |
| 254 |
| 255 def TestGetPathBeforeFinalDir(self): |
| 256 """Tests _GetPathBeforeFinalDir() (unit test)""" |
| 257 self.assertEqual('gs://', |
| 258 cp._GetPathBeforeFinalDir(storage_uri('gs://bucket/'))) |
| 259 self.assertEqual('gs://bucket', |
| 260 cp._GetPathBeforeFinalDir(storage_uri('gs://bucket/dir/'))) |
| 261 self.assertEqual('gs://bucket', |
| 262 cp._GetPathBeforeFinalDir(storage_uri('gs://bucket/dir'))) |
| 263 self.assertEqual('gs://bucket/dir', |
| 264 cp._GetPathBeforeFinalDir( |
| 265 storage_uri('gs://bucket/dir/obj'))) |
| 266 self.assertEqual('file://%s' % self.src_dir_root.rstrip('/'), |
| 267 cp._GetPathBeforeFinalDir(storage_uri( |
| 268 'file://%sdir0/' % self.src_dir_root))) |
| 269 |
| 270 def TestCopyingTopLevelFileToBucket(self): |
| 271 """Tests copying one top-level file to a bucket""" |
| 272 src_file = self.SrcFile('f0') |
| 273 self.RunCommand('cp', [src_file, self.dst_bucket_uri.uri]) |
| 274 actual = list(test_util.test_wildcard_iterator( |
| 275 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 276 self.assertEqual(1, len(actual)) |
| 277 self.assertEqual('f0', actual[0].object_name) |
| 278 |
| 279 def TestCopyingAbsolutePathDirToBucket(self): |
| 280 """Tests recursively copying absolute path directory to a bucket""" |
| 281 self.RunCommand('cp', ['-R', self.src_dir_root, self.dst_bucket_uri.uri]) |
| 282 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 283 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 284 expected = set() |
| 285 for file_path in self.all_src_file_paths: |
| 286 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 287 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 288 expected.add('%s%s' % (self.dst_bucket_uri.uri, |
| 289 file_path_sans_top_tmp_dir)) |
| 290 self.assertEqual(expected, actual) |
| 291 |
| 292 def TestCopyingRelativePathDirToBucket(self): |
| 293 """Tests recursively copying relative directory to a bucket""" |
| 294 orig_dir = os.getcwd() |
| 295 os.chdir(self.src_dir_root) |
| 296 self.RunCommand('cp', ['-R', 'dir0', self.dst_bucket_uri.uri]) |
| 297 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 298 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 299 expected = set() |
| 300 for file_path in self.all_src_file_paths: |
| 301 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 302 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 303 expected.add('%s%s' % (self.dst_bucket_uri.uri, 'dir0/dir1/nested')) |
| 304 self.assertEqual(expected, actual) |
| 305 os.chdir(orig_dir) |
| 306 |
| 307 def TestCopyingRelativePathSubDirToBucketSubdirSignifiedByDollarFolderObj(self
): |
| 308 """Tests recursively copying relative sub-directory to bucket subdir signifi
ed by a $folder$ object""" |
| 309 orig_dir = os.getcwd() |
| 310 os.chdir(self.src_dir_root) |
| 311 # Create a $folder$ object to simulate a folder created by GCS manager (or |
| 312 # various other tools), which gsutil understands to mean there is a folder i
nto |
| 313 # which the object is being copied. |
| 314 obj_name = '%sabc_$folder$' % self.dst_bucket_uri |
| 315 self.CreateEmptyObject(test_util.test_storage_uri(obj_name)) |
| 316 self.RunCommand('cp', ['-R', 'dir0/dir1', '%sabc' |
| 317 % self.dst_bucket_uri.uri]) |
| 318 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 319 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 320 expected = set([obj_name]) |
| 321 for file_path in self.all_src_file_paths: |
| 322 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 323 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 324 expected.add('%sabc/%s' % (self.dst_bucket_uri.uri, 'dir1/nested')) |
| 325 self.assertEqual(expected, actual) |
| 326 os.chdir(orig_dir) |
| 327 |
| 328 def TestCopyingRelativePathSubDirToBucketSubdirSignifiedBySlash(self): |
| 329 """Tests recursively copying relative sub-directory to bucket subdir signifi
ed by a / object""" |
| 330 orig_dir = os.getcwd() |
| 331 os.chdir(self.src_dir_root) |
| 332 self.RunCommand('cp', ['-R', 'dir0/dir1', '%sabc/' |
| 333 % self.dst_bucket_uri.uri]) |
| 334 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 335 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 336 expected = set() |
| 337 for file_path in self.all_src_file_paths: |
| 338 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 339 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 340 expected.add('%sabc/%s' % (self.dst_bucket_uri.uri, 'dir1/nested')) |
| 341 self.assertEqual(expected, actual) |
| 342 os.chdir(orig_dir) |
| 343 |
| 344 def TestCopyingRelativePathSubDirToBucket(self): |
| 345 """Tests recursively copying relative sub-directory to a bucket""" |
| 346 orig_dir = os.getcwd() |
| 347 os.chdir(self.src_dir_root) |
| 348 self.RunCommand('cp', ['-R', 'dir0/dir1', self.dst_bucket_uri.uri]) |
| 349 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 350 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 351 expected = set() |
| 352 for file_path in self.all_src_file_paths: |
| 353 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 354 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 355 expected.add('%s%s' % (self.dst_bucket_uri.uri, 'dir1/nested')) |
| 356 self.assertEqual(expected, actual) |
| 357 os.chdir(orig_dir) |
| 358 |
| 359 def TestCopyingDotSlashToBucket(self): |
| 360 """Tests copying ./ to a bucket produces expected naming""" |
| 361 # When running a command like gsutil cp -r . gs://dest we expect the dest |
| 362 # obj names to be of the form gs://dest/abc, not gs://dest/./abc. |
| 363 orig_dir = os.getcwd() |
| 364 for rel_src_dir in ['.', './']: |
| 365 os.chdir(self.src_dir_root) |
| 366 self.RunCommand('cp', ['-R', rel_src_dir, self.dst_bucket_uri.uri]) |
| 367 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 368 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 369 expected = set() |
| 370 for file_path in self.all_src_file_paths: |
| 371 start_tmp_pos = (file_path.find(self.src_dir_root) |
| 372 + len(self.src_dir_root)) |
| 373 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 374 expected.add('%s%s' % (self.dst_bucket_uri.uri, |
| 375 file_path_sans_top_tmp_dir)) |
| 376 self.assertEqual(expected, actual) |
| 377 # Clean up/re-set up for next variant iteration. |
| 378 self.TearDownClass() |
| 379 self.SetUpClass() |
| 380 os.chdir(orig_dir) |
| 381 |
| 382 def TestCopyingDirContainingOneFileToBucket(self): |
| 383 """Tests copying a directory containing 1 file to a bucket. |
| 384 We test this case to ensure that correct bucket handling isn't dependent |
| 385 on the copy being treated as a multi-source copy. |
| 386 """ |
| 387 self.RunCommand('cp', ['-R', '%sdir0%sdir1' % |
| 388 (self.src_dir_root, os.sep), |
| 389 self.dst_bucket_uri.uri]) |
| 390 actual = list((str(u) for u in test_util.test_wildcard_iterator( |
| 391 '%s**' % self.dst_bucket_uri.uri).IterUris())) |
| 392 self.assertEqual(1, len(actual)) |
| 393 self.assertEqual('%sdir1%snested' % (self.dst_bucket_uri.uri, os.sep), |
| 394 actual[0]) |
| 395 |
| 396 def TestCopyingBucketToDir(self): |
| 397 """Tests copying from a bucket to a directory""" |
| 398 self.RunCommand('cp', ['-R', self.src_bucket_uri.uri, self.dst_dir_root]) |
| 399 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 400 '%s**' % self.dst_dir_root).IterUris()) |
| 401 expected = set() |
| 402 for uri in self.all_src_obj_uris: |
| 403 expected.add('file://%s%s/%s' % (self.dst_dir_root, uri.bucket_name, |
| 404 uri.object_name)) |
| 405 self.assertEqual(expected, actual) |
| 406 |
| 407 def TestCopyingBucketToBucket(self): |
| 408 """Tests copying from a bucket-only URI to a bucket""" |
| 409 self.RunCommand('cp', ['-R', self.src_bucket_uri.uri, |
| 410 self.dst_bucket_uri.uri]) |
| 411 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 412 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 413 expected = set() |
| 414 for uri in self.all_src_obj_uris: |
| 415 expected.add('%s%s/%s' % (self.dst_bucket_uri.uri, uri.bucket_name, |
| 416 uri.object_name)) |
| 417 self.assertEqual(expected, actual) |
| 418 |
| 419 """Tests copying from a directory to a directory""" |
| 420 self.RunCommand('cp', ['-R', self.src_dir_root, self.dst_dir_root]) |
| 421 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 422 '%s**' % self.dst_dir_root).IterUris()) |
| 423 expected = set() |
| 424 for file_path in self.all_src_file_paths: |
| 425 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 426 file_path_sans_top_tmp_dir = file_path[start_tmp_pos:] |
| 427 expected.add('file://%s%s' % (self.dst_dir_root, |
| 428 file_path_sans_top_tmp_dir)) |
| 429 self.assertEqual(expected, actual) |
| 430 |
| 431 def TestCopyingFilesAndDirNonRecursive(self): |
| 432 """Tests copying containing files and a directory without -R""" |
| 433 self.RunCommand('cp', ['%s*' % self.src_dir_root, self.dst_dir_root]) |
| 434 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 435 '%s**' % self.dst_dir_root).IterUris()) |
| 436 expected = set(['file://%s%s' % (self.dst_dir_root, f) |
| 437 for f in self.non_nested_file_names]) |
| 438 self.assertEqual(expected, actual) |
| 439 |
| 440 def TestCopyingFileToDir(self): |
| 441 """Tests copying one file to a directory""" |
| 442 src_file = self.SrcFile('nested') |
| 443 self.RunCommand('cp', [src_file, self.dst_dir_root]) |
| 444 actual = list(test_util.test_wildcard_iterator( |
| 445 '%s*' % self.dst_dir_root).IterUris()) |
| 446 self.assertEqual(1, len(actual)) |
| 447 self.assertEqual('file://%s%s' % (self.dst_dir_root, 'nested'), |
| 448 actual[0].uri) |
| 449 |
| 450 def TestCopyingFileToObjectWithConsecutiveSlashes(self): |
| 451 """Tests copying a file to an object containing consecutive slashes""" |
| 452 src_file = self.SrcFile('f0') |
| 453 self.RunCommand('cp', [src_file, '%s/obj' % self.dst_bucket_uri.uri]) |
| 454 actual = list(test_util.test_wildcard_iterator( |
| 455 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 456 self.assertEqual(1, len(actual)) |
| 457 self.assertEqual('/obj', actual[0].object_name) |
| 458 |
| 459 def TestCopyingCompressedFileToBucket(self): |
| 460 """Tests copying one file with compression to a bucket""" |
| 461 src_file = self.SrcFile('f2.txt') |
| 462 self.RunCommand('cp', ['-z', 'txt', src_file, self.dst_bucket_uri.uri],) |
| 463 actual = list( |
| 464 str(u) for u in test_util.test_wildcard_iterator( |
| 465 '%s*' % self.dst_bucket_uri.uri).IterUris()) |
| 466 self.assertEqual(1, len(actual)) |
| 467 expected_dst_uri = self.dst_bucket_uri.clone_replace_name('f2.txt') |
| 468 self.assertEqual(expected_dst_uri.uri, actual[0]) |
| 469 dst_key = expected_dst_uri.get_key() |
| 470 dst_key.open_read() |
| 471 self.assertEqual('gzip', dst_key.content_encoding) |
| 472 |
| 473 def TestCopyingObjectToObject(self): |
| 474 """Tests copying an object to an object""" |
| 475 self.RunCommand('cp', ['%sobj1' % self.src_bucket_uri.uri, |
| 476 self.dst_bucket_uri.uri]) |
| 477 actual = list(test_util.test_wildcard_iterator( |
| 478 '%s*' % self.dst_bucket_uri.uri).IterUris()) |
| 479 self.assertEqual(1, len(actual)) |
| 480 self.assertEqual('obj1', actual[0].object_name) |
| 481 |
| 482 def TestCopyingObjsAndFilesToDir(self): |
| 483 """Tests copying objects and files to a directory""" |
| 484 self.RunCommand('cp', ['-R', '%s**' % self.src_bucket_uri.uri, |
| 485 '%s**' % self.src_dir_root, self.dst_dir_root]) |
| 486 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 487 '%s**' % self.dst_dir_root).IterUris()) |
| 488 expected = set() |
| 489 for uri in self.all_src_obj_uris: |
| 490 # Use FinalObjNameComponent here because we expect names to be flattened |
| 491 # when using wildcard copy semantics. |
| 492 expected.add('file://%s%s' % (self.dst_dir_root, |
| 493 self.FinalObjNameComponent(uri))) |
| 494 for file_path in self.nested_child_file_paths: |
| 495 # Use os.path.basename here because we expect names to be flattened when |
| 496 # using wildcard copy semantics. |
| 497 expected.add('file://%s%s' % (self.dst_dir_root, |
| 498 os.path.basename(file_path))) |
| 499 self.assertEqual(expected, actual) |
| 500 |
| 501 def TestCopyingObjToDot(self): |
| 502 """Tests that copying an object to . or ./ downloads to correct name""" |
| 503 for final_char in ('/', ''): |
| 504 prev_dir = os.getcwd() |
| 505 os.chdir(self.dst_dir_root) |
| 506 self.RunCommand('cp', |
| 507 ['%sobj1' % self.src_bucket_uri.uri, '.%s' % final_char]) |
| 508 actual = set() |
| 509 for dirname, dirnames, filenames in os.walk('.'): |
| 510 for subdirname in dirnames: |
| 511 actual.add(os.path.join(dirname, subdirname)) |
| 512 for filename in filenames: |
| 513 actual.add(os.path.join(dirname, filename)) |
| 514 expected = set(['./obj1']) |
| 515 self.assertEqual(expected, actual) |
| 516 os.chdir(prev_dir) |
| 517 # Clean up/re-set up for next variant iteration. |
| 518 self.TearDownClass() |
| 519 self.SetUpClass() |
| 520 |
| 521 def TestCopyingObjsAndFilesToBucket(self): |
| 522 """Tests copying objects and files to a bucket""" |
| 523 self.RunCommand('cp', ['-R', '%s**' % self.src_bucket_uri.uri, |
| 524 '%s**' % self.src_dir_root, self.dst_bucket_uri.uri]) |
| 525 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 526 '%s*' % self.dst_bucket_uri.uri).IterUris()) |
| 527 expected = set() |
| 528 for uri in self.all_src_obj_uris: |
| 529 # Use FinalObjNameComponent here because we expect names to be flattened |
| 530 # when using wildcard copy semantics. |
| 531 expected.add('%s%s' % (self.dst_bucket_uri.uri, |
| 532 self.FinalObjNameComponent(uri))) |
| 533 for file_path in self.nested_child_file_paths: |
| 534 # Use os.path.basename here because we expect names to be flattened when |
| 535 # using wildcard copy semantics. |
| 536 expected.add('%s%s' % (self.dst_bucket_uri.uri, |
| 537 os.path.basename(file_path))) |
| 538 self.assertEqual(expected, actual) |
| 539 |
| 540 def TestAttemptDirCopyWithoutRecursion(self): |
| 541 """Tests copying a directory without -R""" |
| 542 try: |
| 543 self.RunCommand('cp', [self.src_dir_root, |
| 544 self.dst_dir_root]) |
| 545 self.fail('Did not get expected CommandException') |
| 546 except CommandException, e: |
| 547 self.assertNotEqual(e.reason.find('No URIs matched'), -1) |
| 548 |
| 549 def TestAttemptCopyingProviderOnlySrc(self): |
| 550 """Attempts to copy a src specified as a provider-only URI""" |
| 551 try: |
| 552 self.RunCommand('cp', ['gs://', self.src_bucket_uri.uri]) |
| 553 self.fail('Did not get expected CommandException') |
| 554 except CommandException, e: |
| 555 self.assertNotEqual(e.reason.find('provider-only'), -1) |
| 556 |
| 557 def TestAttemptCopyingOverlappingSrcDstFile(self): |
| 558 """Attempts to an object atop itself""" |
| 559 obj_uri = test_util.test_storage_uri('%sobj' % self.dst_bucket_uri) |
| 560 self.CreateEmptyObject(obj_uri) |
| 561 try: |
| 562 self.RunCommand('cp', ['%s/f0' % self.src_dir_root, |
| 563 '%s/f0' % self.src_dir_root]) |
| 564 self.fail('Did not get expected CommandException') |
| 565 except CommandException, e: |
| 566 self.assertNotEqual(e.reason.find('are the same file - abort'), -1) |
| 567 |
| 568 def TestAttemptCopyingToMultiMatchWildcard(self): |
| 569 """Attempts to copy where dst wildcard matches >1 obj""" |
| 570 try: |
| 571 self.RunCommand('cp', ['%sobj0' % self.src_bucket_uri.uri, |
| 572 '%s*' % self.src_bucket_uri.uri]) |
| 573 self.fail('Did not get expected CommandException') |
| 574 except CommandException, e: |
| 575 self.assertNotEqual(e.reason.find('must match exactly 1 URI'), -1) |
| 576 |
| 577 def TestAttemptCopyingMultiObjsToFile(self): |
| 578 """Attempts to copy multiple objects to a file""" |
| 579 # Use src_dir_root so we can point to an existing file for this test. |
| 580 try: |
| 581 self.RunCommand('cp', ['-R', '%s*' % self.src_bucket_uri.uri, |
| 582 '%sf0' % self.src_dir_root]) |
| 583 self.fail('Did not get expected CommandException') |
| 584 except CommandException, e: |
| 585 self.assertNotEqual(e.reason.find('must name a cloud path or '), -2) |
| 586 |
| 587 def TestAttemptCopyingWithFileDirConflict(self): |
| 588 """Attempts to copy objects that cause a file/directory conflict""" |
| 589 # Create objects with name conflicts (a/b and a). Use 'dst' bucket because |
| 590 # it gets cleared after each test. |
| 591 self.CreateEmptyObject(test_util.test_storage_uri( |
| 592 '%sa/b' % self.dst_bucket_uri)) |
| 593 self.CreateEmptyObject(test_util.test_storage_uri( |
| 594 '%sa' % self.dst_bucket_uri)) |
| 595 try: |
| 596 self.RunCommand('cp', ['-R', self.dst_bucket_uri.uri, |
| 597 self.dst_dir_root]) |
| 598 self.fail('Did not get expected CommandException') |
| 599 except CommandException, e: |
| 600 self.assertNotEqual(e.reason.find( |
| 601 'exists where a directory needs to be created'), -1) |
| 602 |
| 603 def TestAttemptCopyingWithDirFileConflict(self): |
| 604 """Attempts to copy an object that causes a directory/file conflict""" |
| 605 # Create abc in dest dir. |
| 606 os.mkdir('%sabc' % self.dst_dir_root) |
| 607 # Create an object that conflicts with this dest subdir. Use 'dst' bucket |
| 608 # as source because it gets cleared after each test. |
| 609 obj_name = '%sabc' % self.dst_bucket_uri |
| 610 self.CreateEmptyObject(test_util.test_storage_uri(obj_name)) |
| 611 try: |
| 612 self.RunCommand('cp', [obj_name, self.dst_dir_root]) |
| 613 self.fail('Did not get expected CommandException') |
| 614 except CommandException, e: |
| 615 self.assertNotEqual(e.reason.find( |
| 616 'where the file needs to be created'), -1) |
| 617 |
| 618 def TestWildcardMoveWithinBucket(self): |
| 619 """Attempts to move using src wildcard that overlaps dest object. |
| 620 We want to ensure that this doesn't stomp the result data. See the |
| 621 comment starting with 'Expand wildcards before' in commands/mv.py |
| 622 for details. |
| 623 """ |
| 624 # Create a single object; use 'dst' bucket because it gets cleared after |
| 625 # each test. |
| 626 self.CreateEmptyObject( |
| 627 test_util.test_storage_uri('%sold' % self.dst_bucket_uri)) |
| 628 self.RunCommand('mv', ['%s*' % self.dst_bucket_uri.uri, |
| 629 '%snew' % self.dst_bucket_uri.uri]) |
| 630 actual = list( |
| 631 test_util.test_wildcard_iterator( |
| 632 '%s*' % self.dst_bucket_uri.uri).IterUris()) |
| 633 self.assertEqual(1, len(actual)) |
| 634 self.assertEqual('new', actual[0].object_name) |
| 635 |
| 636 def TestCatCommandRuns(self): |
| 637 """Test that the cat command basically runs""" |
| 638 self.RunCommand('cat', ['%sobj1' % self.src_bucket_uri.uri]) |
| 639 |
| 640 def TestGetAclCommandRuns(self): |
| 641 """Test that the getacl command basically runs""" |
| 642 self.RunCommand('getacl', [self.src_bucket_uri.uri]) |
| 643 |
| 644 def TestGetDefAclCommandRuns(self): |
| 645 """Test that the getdefacl command basically runs""" |
| 646 self.RunCommand('getacl', [self.src_bucket_uri.uri]) |
| 647 |
| 648 def TestGetLoggingCommandRuns(self): |
| 649 """Test that the getlogging command basically runs""" |
| 650 self.RunCommand('getlogging', [self.src_bucket_uri.uri]) |
| 651 |
| 652 def TestHelpCommandDoesntRaise(self): |
| 653 """Test that the help command doesn't raise (sanity checks all help)""" |
| 654 # Unset PAGER if defined, so help output paginating into $PAGER doesn't |
| 655 # cause test to pause. |
| 656 if 'PAGER' in os.environ: |
| 657 del os.environ['PAGER'] |
| 658 self.RunCommand('help', []) |
| 659 |
| 660 def TestLsNonExistentObjectWithPrefixName(self): |
| 661 """Test ls of non-existent obj that matches prefix of existing objs""" |
| 662 # Use an object name that matches a prefix of other names at that level, to |
| 663 # ensure the ls subdir handling logic doesn't pick up anything extra. |
| 664 try: |
| 665 output = self.RunCommand('ls', ['%sobj' % self.src_bucket_uri.uri], |
| 666 return_stdout=True) |
| 667 except CommandException, e: |
| 668 self.assertNotEqual(e.reason.find('No such object'), -1) |
| 669 |
| 670 def TestLsBucketNonRecursive(self): |
| 671 """Test that ls of a bucket returns expected results""" |
| 672 output = self.RunCommand('ls', ['%s*' % self.src_bucket_uri.uri], |
| 673 return_stdout=True) |
| 674 expected = set(x.uri for x in self.all_src_top_level_obj_uris) |
| 675 expected = expected.union(x.uri for x in self.all_src_subdir_obj_uris) |
| 676 expected.add('%ssrc_subdir/:' % self.src_bucket_uri.uri) |
| 677 expected.add('%ssrc_subdir/nested/' % self.src_bucket_uri.uri) |
| 678 expected.add('') # Blank line between subdir listings. |
| 679 actual = set(output.split('\n')) |
| 680 self.assertEqual(expected, actual) |
| 681 |
| 682 def TestLsBucketRecursive(self): |
| 683 """Test that ls -R of a bucket returns expected results""" |
| 684 output = self.RunCommand('ls', ['-R', '%s*' % self.src_bucket_uri.uri], |
| 685 return_stdout=True) |
| 686 expected = set(x.uri for x in self.all_src_obj_uris) |
| 687 expected = expected.union(x.uri for x in self.all_src_subdir_obj_uris) |
| 688 expected.add('%ssrc_subdir/:' % self.src_bucket_uri.uri) |
| 689 expected.add('%ssrc_subdir/nested/:' % self.src_bucket_uri.uri) |
| 690 expected.add('') # Blank line between subdir listings. |
| 691 actual = set(output.split('\n')) |
| 692 self.assertEqual(expected, actual) |
| 693 |
| 694 def TestLsBucketRecursiveWithLeadingSlashObjectName(self): |
| 695 """Test that ls -R of a bucket with an object that has leading slash""" |
| 696 src_file = self.SrcFile('f0') |
| 697 self.RunCommand('cp', [src_file, '%s/%s' % (self.dst_bucket_uri.uri, 'f0')]) |
| 698 output = self.RunCommand('ls', ['-R', '%s*' % self.dst_bucket_uri.uri], |
| 699 return_stdout=True) |
| 700 expected = set(['%s/%s' % (self.dst_bucket_uri.uri, 'f0')]) |
| 701 expected.add('') # Blank line between subdir listings. |
| 702 actual = set(output.split('\n')) |
| 703 self.assertEqual(expected, actual) |
| 704 |
| 705 def TestLsBucketSubdirNonRecursive(self): |
| 706 """Test that ls of a bucket subdir returns expected results""" |
| 707 output = self.RunCommand('ls', ['%ssrc_subdir' % self.src_bucket_uri.uri], |
| 708 return_stdout=True) |
| 709 expected = set(x.uri for x in self.all_src_subdir_obj_uris) |
| 710 expected = expected.union(x.uri for x in self.all_src_subdir_obj_uris) |
| 711 expected.add('%ssrc_subdir/nested/' % self.src_bucket_uri.uri) |
| 712 expected.add('') # Blank line between subdir listings. |
| 713 actual = set(output.split('\n')) |
| 714 self.assertEqual(expected, actual) |
| 715 |
| 716 def TestLsBucketSubdirRecursive(self): |
| 717 """Test that ls -R of a bucket subdir returns expected results""" |
| 718 for final_char in ('/', ''): |
| 719 output = self.RunCommand('ls', |
| 720 ['-R', '%ssrc_subdir%s' |
| 721 % (self.src_bucket_uri.uri, final_char)], |
| 722 return_stdout=True) |
| 723 expected = set(x.uri for x in self.all_src_subdir_and_below_obj_uris) |
| 724 expected = expected.union(x.uri for x in self.all_src_subdir_obj_uris) |
| 725 expected.add('%ssrc_subdir/:' % self.src_bucket_uri.uri) |
| 726 expected.add('%ssrc_subdir/nested/:' % self.src_bucket_uri.uri) |
| 727 expected.add('') # Blank line between subdir listings. |
| 728 actual = set(output.split('\n')) |
| 729 self.assertEqual(expected, actual) |
| 730 |
| 731 def TestMakeBucketsCommand(self): |
| 732 """Test mb on existing bucket""" |
| 733 try: |
| 734 self.RunCommand('mb', [self.dst_bucket_uri.uri]) |
| 735 self.fail('Did not get expected StorageCreateError') |
| 736 except boto.exception.StorageCreateError, e: |
| 737 self.assertEqual(e.status, 409) |
| 738 |
| 739 def TestRemoveBucketsCommand(self): |
| 740 """Test rb on non-existent bucket""" |
| 741 try: |
| 742 self.RunCommand('rb', ['gs://non_existent_%s' % |
| 743 self.dst_bucket_uri.bucket_name]) |
| 744 self.fail('Did not get expected StorageResponseError') |
| 745 except boto.exception.StorageResponseError, e: |
| 746 self.assertEqual(e.status, 404) |
| 747 |
| 748 def TestRemoveObjsCommand(self): |
| 749 """Test rm command on non-existent object""" |
| 750 try: |
| 751 self.RunCommand('rm', ['%snon_existent' % |
| 752 self.dst_bucket_uri.uri]) |
| 753 self.fail('Did not get expected WildcardException') |
| 754 except StorageResponseError, e: |
| 755 self.assertNotEqual(e.reason.find('Not Found'), -1) |
| 756 |
| 757 def TestSetAclOnBucketRuns(self): |
| 758 """Test that the setacl command basically runs""" |
| 759 # We don't test reading back the acl (via getacl command) because at present |
| 760 # MockStorageService doesn't translate canned ACLs into actual ACL XML. |
| 761 self.RunCommand('setacl', ['private', self.src_bucket_uri.uri]) |
| 762 |
| 763 def TestSetAclOnWildcardNamedBucketRuns(self): |
| 764 """Test that setacl basically runs against wildcard-named bucket""" |
| 765 # We don't test reading back the acl (via getacl command) because at present |
| 766 # MockStorageService doesn't translate canned ACLs into actual ACL XML. |
| 767 uri_str = '%s_s*c' % self.uri_base_str |
| 768 self.RunCommand('setacl', ['private', uri_str]) |
| 769 |
| 770 def TestSetAclOnObjectRuns(self): |
| 771 """Test that the setacl command basically runs""" |
| 772 self.RunCommand('setacl', ['private', '%s*' % self.src_bucket_uri.uri]) |
| 773 |
| 774 def TestSetDefAclOnBucketRuns(self): |
| 775 """Test that the setdefacl command basically runs""" |
| 776 self.RunCommand('setdefacl', ['private', self.src_bucket_uri.uri]) |
| 777 |
| 778 def TestSetDefAclOnObjectFails(self): |
| 779 """Test that the setdefacl command fails when run against an object""" |
| 780 try: |
| 781 self.RunCommand('setdefacl', ['private', '%s*' % self.src_bucket_uri.uri]) |
| 782 self.fail('Did not get expected CommandException') |
| 783 except CommandException, e: |
| 784 self.assertNotEqual(e.reason.find('URI must name a bucket'), -1) |
| 785 |
| 786 def TestDisableLoggingCommandRuns(self): |
| 787 """Test that the disablelogging command basically runs""" |
| 788 self.RunCommand('disablelogging', [self.src_bucket_uri.uri]) |
| 789 |
| 790 def TestEnableLoggingCommandRuns(self): |
| 791 """Test that the enablelogging command basically runs""" |
| 792 self.RunCommand('enablelogging', ['-b', 'gs://log_bucket', |
| 793 self.src_bucket_uri.uri]) |
| 794 |
| 795 # Now that gsutil ver computes a checksum it adds 1-3 seconds to test run |
| 796 # time (for in memory mocked tests that otherwise take ~ 0.1 seconds). Since |
| 797 # it provides very little test value, we're leaving this test commented out. |
| 798 #def TestVerCommmandRuns(self): |
| 799 # """Test that the Ver command basically runs""" |
| 800 # self.RunCommand('ver', []) |
| 801 |
| 802 def TestMinusDOptionWorks(self): |
| 803 """Tests using gsutil -D option""" |
| 804 src_file = self.SrcFile('f0') |
| 805 self.RunCommand('cp', [src_file, self.dst_bucket_uri.uri], debug=3) |
| 806 actual = list(test_util.test_wildcard_iterator( |
| 807 '%s*' % self.dst_bucket_uri.uri).IterUris()) |
| 808 self.assertEqual(1, len(actual)) |
| 809 self.assertEqual('f0', actual[0].object_name) |
| 810 |
| 811 def DownloadTestHelper(self, func): |
| 812 """ |
| 813 Test resumable download with custom test function to distort downloaded |
| 814 data. We expect an exception to be raised and the dest file to be removed. |
| 815 """ |
| 816 object_uri = self.all_src_obj_uris[0].uri |
| 817 try: |
| 818 self.RunCommand('cp', [object_uri, self.tmp_path], test_method=func) |
| 819 self.fail('Did not get expected CommandException') |
| 820 except CommandException: |
| 821 self.assertFalse(os.path.exists(self.tmp_path)) |
| 822 except: |
| 823 self.fail('Unexpected exception raised') |
| 824 |
| 825 def TestDownloadWithObjectSizeChange(self): |
| 826 """ |
| 827 Test resumable download on an object that changes size before the |
| 828 downloaded file's checksum is validated. |
| 829 """ |
| 830 def append(fp): |
| 831 """Append a byte at end of an open file and flush contents.""" |
| 832 fp.seek(0,2) |
| 833 fp.write('x') |
| 834 fp.flush() |
| 835 self.DownloadTestHelper(append) |
| 836 |
| 837 def TestDownloadWithFileContentChange(self): |
| 838 """ |
| 839 Tests resumable download on an object where the file content changes |
| 840 before the downloaded file's checksum is validated. |
| 841 """ |
| 842 def overwrite(fp): |
| 843 """Overwrite first byte in an open file and flush contents.""" |
| 844 fp.seek(0) |
| 845 fp.write('x') |
| 846 fp.flush() |
| 847 self.DownloadTestHelper(overwrite) |
| 848 |
| 849 def TestFlatCopyingObjsAndFilesToBucketSubDir(self): |
| 850 """Tests copying flatly listed objects and files to bucket subdir""" |
| 851 # Test with and without final slash on dest subdir. |
| 852 for final_char in ('/', ''): |
| 853 # Set up existing bucket subdir by creating an object in the subdir. |
| 854 self.RunCommand( |
| 855 'cp', ['%sf0' % self.src_dir_root, |
| 856 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 857 self.RunCommand( |
| 858 'cp', ['-R', '%s**' % self.src_bucket_uri.uri, |
| 859 '%s**' % self.src_dir_root, |
| 860 '%sdst_subdir%s' % (self.dst_bucket_uri.uri, final_char)]) |
| 861 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 862 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 863 expected = set(['%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 864 for uri in self.all_src_obj_uris: |
| 865 # Use FinalObjNameComponent here because we expect names to be flattened |
| 866 # when using wildcard copy semantics. |
| 867 expected.add('%sdst_subdir/%s' % (self.dst_bucket_uri.uri, |
| 868 self.FinalObjNameComponent(uri))) |
| 869 for file_path in self.nested_child_file_paths: |
| 870 # Use os.path.basename here because we expect names to be flattened when |
| 871 # using wildcard copy semantics. |
| 872 expected.add('%sdst_subdir/%s' % (self.dst_bucket_uri.uri, |
| 873 os.path.basename(file_path))) |
| 874 self.assertEqual(expected, actual) |
| 875 # Clean up/re-set up for next variant iteration. |
| 876 self.TearDownClass() |
| 877 self.SetUpClass() |
| 878 |
| 879 def TestRecursiveCopyObjsAndFilesToExistingBucketSubDir(self): |
| 880 """Tests recursive copy of objects and files to existing bucket subdir""" |
| 881 # Test with and without final slash on dest subdir. |
| 882 for final_char in ('/', ''): |
| 883 # Set up existing bucket subdir by creating an object in the subdir. |
| 884 self.RunCommand( |
| 885 'cp', ['%sf0' % self.src_dir_root, |
| 886 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 887 self.RunCommand( |
| 888 'cp', ['-R', '%s' % self.src_bucket_uri.uri, |
| 889 '%s' % self.src_dir_root, |
| 890 '%sdst_subdir%s' % (self.dst_bucket_uri.uri, final_char)]) |
| 891 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 892 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 893 expected = set(['%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 894 for uri in self.all_src_obj_uris: |
| 895 expected.add('%sdst_subdir/%s/%s' % |
| 896 (self.dst_bucket_uri.uri, uri.bucket_name, uri.object_name)) |
| 897 for file_path in self.all_src_file_paths: |
| 898 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 899 file_path_sans_base_dir = file_path[start_tmp_pos:] |
| 900 expected.add('%sdst_subdir/%s' % |
| 901 (self.dst_bucket_uri.uri, file_path_sans_base_dir)) |
| 902 self.assertEqual(expected, actual) |
| 903 # Clean up/re-set up for next variant iteration. |
| 904 self.TearDownClass() |
| 905 self.SetUpClass() |
| 906 |
| 907 def TestRecursiveCopyObjsAndFilesToNonExistentBucketSubDir(self): |
| 908 """Tests recursive copy of objs + files to non-existent bucket subdir""" |
| 909 # Test with and without final slash on dest subdir. |
| 910 self.RunCommand( |
| 911 'cp', ['-R', '%s' % self.src_bucket_uri.uri, |
| 912 '%s' % self.src_dir_root, |
| 913 '%sdst_subdir' % (self.dst_bucket_uri.uri)]) |
| 914 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 915 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 916 expected = set() |
| 917 for uri in self.all_src_obj_uris: |
| 918 expected.add('%sdst_subdir/%s' % |
| 919 (self.dst_bucket_uri.uri, uri.object_name)) |
| 920 for file_path in self.all_src_file_paths: |
| 921 start_tmp_pos = file_path.find(self.tmpdir_prefix) |
| 922 file_path_sans_base_dir = ( |
| 923 file_path[start_tmp_pos:].partition(os.sep)[-1]) |
| 924 expected.add('%sdst_subdir/%s' % |
| 925 (self.dst_bucket_uri.uri, file_path_sans_base_dir)) |
| 926 self.assertEqual(expected, actual) |
| 927 |
| 928 def TestCopyingBucketSubDirToDir(self): |
| 929 """Tests copying a bucket subdir to a directory""" |
| 930 # Test with and without final slash on dest subdir. |
| 931 for (final_src_char, final_dst_char) in ( |
| 932 ('', ''), ('', '/'), ('/', ''), ('/', '/') ): |
| 933 self.RunCommand( |
| 934 'cp', ['-R', '%s%s' % (self.src_bucket_subdir_uri, final_src_char), |
| 935 '%s%s' % (self.dst_dir_root, final_dst_char)]) |
| 936 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 937 '%s/**' % self.dst_dir_root).IterUris()) |
| 938 expected = set() |
| 939 for uri in self.all_src_subdir_and_below_obj_uris: |
| 940 expected.add('file://%s%s' % (self.dst_dir_root, uri.uri.partition( |
| 941 self.src_bucket_uri.uri)[-1])) |
| 942 self.assertEqual(expected, actual) |
| 943 # Clean up/re-set up for next variant iteration. |
| 944 self.TearDownClass() |
| 945 self.SetUpClass() |
| 946 |
| 947 def TestCopyingWildcardSpecifiedBucketSubDirToExistingDir(self): |
| 948 """Tests copying a wilcard-specified bucket subdir to a directory""" |
| 949 # Test with and without final slash on dest subdir. |
| 950 for (final_src_char, final_dst_char) in ( |
| 951 ('', ''), ('', '/'), ('/', ''), ('/', '/') ): |
| 952 self.RunCommand( |
| 953 'cp', ['-R', |
| 954 '%s%s' % (self.src_bucket_subdir_uri_wildcard, final_src_char), |
| 955 '%s%s' % (self.dst_dir_root, final_dst_char)]) |
| 956 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 957 '%s/**' % self.dst_dir_root).IterUris()) |
| 958 expected = set() |
| 959 for uri in self.all_src_subdir_and_below_obj_uris: |
| 960 expected.add('file://%s%s' % ( |
| 961 self.dst_dir_root, uri.uri.partition(self.src_bucket_uri.uri)[-1])) |
| 962 self.assertEqual(expected, actual) |
| 963 # Clean up/re-set up for next variant iteration. |
| 964 self.TearDownClass() |
| 965 self.SetUpClass() |
| 966 |
| 967 def TestCopyingBucketSubDirToDirFailsWithoutMinusR(self): |
| 968 """Tests for failure when attempting bucket subdir copy without -R""" |
| 969 try: |
| 970 self.RunCommand( |
| 971 'cp', ['%s' % self.src_bucket_subdir_uri, |
| 972 '%s' % self.dst_dir_root]) |
| 973 self.fail('Did not get expected CommandException') |
| 974 except CommandException, e: |
| 975 self.assertNotEqual(e.reason.find('does not exist'), -1) |
| 976 |
| 977 def TestCopyingBucketSubDirToBucketSubDir(self): |
| 978 """Tests copying a bucket subdir to another bucket subdir""" |
| 979 # Test with and without final slash on dest subdir. |
| 980 for (final_src_char, final_dst_char) in ( |
| 981 ('', ''), ('', '/'), ('/', ''), ('/', '/') ): |
| 982 # Set up existing bucket subdir by creating an object in the subdir. |
| 983 self.RunCommand( |
| 984 'cp', ['%sf0' % self.src_dir_root, |
| 985 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 986 self.RunCommand( |
| 987 'cp', ['-R', '%s%s' % (self.src_bucket_subdir_uri, final_src_char), |
| 988 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 989 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 990 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 991 expected = set(['%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 992 for uri in self.all_src_subdir_and_below_obj_uris: |
| 993 expected.add( |
| 994 '%s/%s' % (self.dst_bucket_subdir_uri.uri, uri.object_name)) |
| 995 self.assertEqual(expected, actual) |
| 996 # Clean up/re-set up for next variant iteration. |
| 997 self.TearDownClass() |
| 998 self.SetUpClass() |
| 999 |
| 1000 def TestMovingBucketSubDirToNonExistentBucketSubDir(self): |
| 1001 """Tests moving a bucket subdir to a non-existent bucket subdir""" |
| 1002 # Test with and without final slash on dest subdir. |
| 1003 for final_src_char in ('', '/'): |
| 1004 self.RunCommand( |
| 1005 'mv', ['%s%s' % (self.src_bucket_subdir_uri, final_src_char), |
| 1006 '%s' % (self.dst_bucket_subdir_uri.uri)]) |
| 1007 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1008 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1009 expected = set([]) |
| 1010 for uri in self.all_src_subdir_and_below_obj_uris: |
| 1011 # Unlike the case with copying, with mv we expect renaming to occur |
| 1012 # at the level of the src subdir, vs appending that subdir beneath the |
| 1013 # dst subdir like is done for copying. |
| 1014 expected_name = uri.object_name.replace('src_', 'dst_') |
| 1015 expected.add('%s%s' % (self.dst_bucket_uri.uri, expected_name)) |
| 1016 self.assertEqual(expected, actual) |
| 1017 # Clean up/re-set up for next variant iteration. |
| 1018 self.TearDownClass() |
| 1019 self.SetUpClass() |
| 1020 |
| 1021 def TestMovingBucketSubDirToExistingBucketSubDir(self): |
| 1022 """Tests moving a bucket subdir to a existing bucket subdir""" |
| 1023 # Test with and without final slash on dest subdir. |
| 1024 for (final_src_char, final_dst_char) in ( |
| 1025 ('', ''), ('', '/'), ('/', ''), ('/', '/') ): |
| 1026 # Set up existing bucket subdir by creating an object in the subdir. |
| 1027 self.RunCommand( |
| 1028 'cp', ['%sf0' % self.src_dir_root, |
| 1029 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1030 self.RunCommand( |
| 1031 'mv', ['%s%s' % (self.src_bucket_subdir_uri, final_src_char), |
| 1032 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 1033 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1034 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1035 expected = set(['%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1036 for uri in self.all_src_subdir_and_below_obj_uris: |
| 1037 expected.add( |
| 1038 '%s/%s' % (self.dst_bucket_subdir_uri.uri, uri.object_name)) |
| 1039 self.assertEqual(expected, actual) |
| 1040 # Clean up/re-set up for next variant iteration. |
| 1041 self.TearDownClass() |
| 1042 self.SetUpClass() |
| 1043 |
| 1044 def TestCopyingObjectToBucketSubDir(self): |
| 1045 """Tests copying an object to a bucket subdir""" |
| 1046 # Test with and without final slash on dest subdir. |
| 1047 for (final_dst_char) in ('', '/'): |
| 1048 # Set up existing bucket subdir by creating an object in the subdir. |
| 1049 self.RunCommand( |
| 1050 'cp', ['%sf0' % self.src_dir_root, |
| 1051 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1052 self.RunCommand( |
| 1053 'cp', ['%sobj0' % self.src_bucket_uri, |
| 1054 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 1055 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1056 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1057 expected = set([ |
| 1058 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri, |
| 1059 '%sdst_subdir/obj0' % self.dst_bucket_uri.uri]) |
| 1060 self.assertEqual(expected, actual) |
| 1061 # Clean up/re-set up for next variant iteration. |
| 1062 self.TearDownClass() |
| 1063 self.SetUpClass() |
| 1064 |
| 1065 def TestCopyingWildcardedFilesToBucketSubDir(self): |
| 1066 """Tests copying wildcarded files to a bucket subdir""" |
| 1067 # Test with and without final slash on dest subdir. |
| 1068 for (final_dst_char) in ('', '/'): |
| 1069 # Set up existing bucket subdir by creating an object in the subdir. |
| 1070 self.RunCommand( |
| 1071 'cp', ['%sf0' % self.src_dir_root, |
| 1072 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1073 self.RunCommand( |
| 1074 'cp', ['%sf?' % self.src_dir_root, |
| 1075 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 1076 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1077 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1078 expected = set([ |
| 1079 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri, |
| 1080 '%sdst_subdir/f0' % self.dst_bucket_uri.uri, |
| 1081 '%sdst_subdir/f1' % self.dst_bucket_uri.uri]) |
| 1082 self.assertEqual(expected, actual) |
| 1083 # Clean up/re-set up for next variant iteration. |
| 1084 self.TearDownClass() |
| 1085 self.SetUpClass() |
| 1086 |
| 1087 def TestCopyingOneNestedFileToBucketSubDir(self): |
| 1088 """Tests copying one nested file to a bucket subdir""" |
| 1089 # Test with and without final slash on dest subdir. |
| 1090 for (final_dst_char) in ('', '/'): |
| 1091 # Set up existing bucket subdir by creating an object in the subdir. |
| 1092 self.RunCommand( |
| 1093 'cp', ['%sf0' % self.src_dir_root, |
| 1094 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1095 self.RunCommand( |
| 1096 'cp', ['-r', '%sdir0' % self.src_dir_root, |
| 1097 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 1098 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1099 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1100 expected = set([ |
| 1101 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri, |
| 1102 '%sdst_subdir/dir0/dir1/nested' % self.dst_bucket_uri.uri]) |
| 1103 self.assertEqual(expected, actual) |
| 1104 # Clean up/re-set up for next variant iteration. |
| 1105 self.TearDownClass() |
| 1106 self.SetUpClass() |
| 1107 |
| 1108 def TestMovingWildcardedFilesToNonExistentBucketSubDir(self): |
| 1109 """Tests moving files to a non-existent bucket subdir""" |
| 1110 # This tests for how we allow users to do something like: |
| 1111 # gsutil cp *.txt gs://bucket/dir |
| 1112 # where *.txt matches more than 1 file and gs://bucket/dir |
| 1113 # doesn't exist as a subdir. |
| 1114 # |
| 1115 # Test with and without final slash on dest subdir. |
| 1116 for (final_dst_char) in ('', '/'): |
| 1117 # Set up existing bucket subdir by creating an object in the subdir. |
| 1118 self.RunCommand( |
| 1119 'cp', ['%sf0' % self.src_dir_root, |
| 1120 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1121 # Copy some files into place in dst bucket. |
| 1122 self.RunCommand( |
| 1123 'cp', ['%sf?' % self.src_dir_root, |
| 1124 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 1125 # Now do the move test. |
| 1126 self.RunCommand( |
| 1127 'mv', ['%s/*' % self.dst_bucket_subdir_uri.uri, |
| 1128 '%s/nonexistent/%s' % (self.dst_bucket_subdir_uri.uri, final_ds
t_char)]) |
| 1129 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1130 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1131 expected = set([ |
| 1132 '%sdst_subdir/nonexistent/existing_obj' % self.dst_bucket_uri.uri, |
| 1133 '%sdst_subdir/nonexistent/f0' % self.dst_bucket_uri.uri, |
| 1134 '%sdst_subdir/nonexistent/f1' % self.dst_bucket_uri.uri]) |
| 1135 self.assertEqual(expected, actual) |
| 1136 # Clean up/re-set up for next variant iteration. |
| 1137 self.TearDownClass() |
| 1138 self.SetUpClass() |
| 1139 |
| 1140 def TestMovingObjectToBucketSubDir(self): |
| 1141 """Tests moving an object to a bucket subdir""" |
| 1142 # Test with and without final slash on dest subdir. |
| 1143 for (final_dst_char) in ('', '/'): |
| 1144 # Set up existing bucket subdir by creating an object in the subdir. |
| 1145 self.RunCommand( |
| 1146 'cp', ['%sf0' % self.src_dir_root, |
| 1147 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri]) |
| 1148 self.RunCommand( |
| 1149 'mv', ['%sobj0' % self.src_bucket_uri, |
| 1150 '%s%s' % (self.dst_bucket_subdir_uri.uri, final_dst_char)]) |
| 1151 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1152 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1153 expected = set([ |
| 1154 '%sdst_subdir/existing_obj' % self.dst_bucket_uri.uri, |
| 1155 '%sdst_subdir/obj0' % self.dst_bucket_uri.uri]) |
| 1156 self.assertEqual(expected, actual) |
| 1157 # Clean up/re-set up for next variant iteration. |
| 1158 self.TearDownClass() |
| 1159 self.SetUpClass() |
| 1160 |
| 1161 def TestWildcardSrcSubDirMoveDisallowed(self): |
| 1162 """Tests moving a bucket subdir specified by wildcard is disallowed""" |
| 1163 try: |
| 1164 self.RunCommand( |
| 1165 'mv', ['%s*' % self.src_bucket_subdir_uri, |
| 1166 '%s' % self.dst_bucket_subdir_uri.uri]) |
| 1167 self.fail('Did not get expected CommandException') |
| 1168 except CommandException, e: |
| 1169 self.assertNotEqual(e.reason.find('mv command disallows naming'), -1) |
| 1170 |
| 1171 def TestMovingBucketNestedSubDirToBucketNestedSubDir(self): |
| 1172 """Tests moving a bucket nested subdir to another bucket nested subdir""" |
| 1173 # Test with and without final slash on dest subdir. |
| 1174 for final_src_char in ('', '/'): |
| 1175 self.RunCommand( |
| 1176 'mv', ['%s%s' % (self.src_bucket_subdir_uri, final_src_char), |
| 1177 '%s' % (self.dst_bucket_subdir_uri.uri)]) |
| 1178 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1179 '%s**' % self.dst_bucket_subdir_uri.uri).IterUris()) |
| 1180 expected = set([]) |
| 1181 for uri in self.all_src_subdir_and_below_obj_uris: |
| 1182 # Unlike the case with copying, with mv we expect renaming to occur |
| 1183 # at the level of the src subdir, vs appending that subdir beneath the |
| 1184 # dst subdir like is done for copying. |
| 1185 expected_name = uri.object_name.replace('src_', 'dst_') |
| 1186 expected.add('%s%s' % (self.dst_bucket_uri, expected_name)) |
| 1187 self.assertEqual(expected, actual) |
| 1188 # Clean up/re-set up for next variant iteration. |
| 1189 self.TearDownClass() |
| 1190 self.SetUpClass() |
| 1191 |
| 1192 def TestRemovingBucketSubDir(self): |
| 1193 """Tests removing a bucket subdir""" |
| 1194 # Test with and without final slash on dest subdir. |
| 1195 for final_src_char in ('', '/'): |
| 1196 # Setup: Copy a directory, including subdir, to bucket. |
| 1197 self.RunCommand('cp', ['-R', self.src_dir_root, self.dst_bucket_uri.uri]) |
| 1198 src_subdir = self.src_dir_root.split(os.path.sep)[-2] |
| 1199 # Test removing bucket subdir. |
| 1200 self.RunCommand( |
| 1201 'rm', ['-R', '%s%s/dir0%s' % |
| 1202 (self.dst_bucket_uri, src_subdir, final_src_char)]) |
| 1203 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1204 '%s**' % self.dst_bucket_uri.uri).IterUris()) |
| 1205 expected = set() |
| 1206 for fname in self.non_nested_file_names: |
| 1207 expected.add('%s%s/%s' % (self.dst_bucket_uri.uri, src_subdir, fname)) |
| 1208 self.assertEqual(expected, actual) |
| 1209 # Clean up/re-set up for next variant iteration. |
| 1210 self.TearDownClass() |
| 1211 self.SetUpClass() |
| 1212 |
| 1213 def TestRecursiveRemoveObjsInBucket(self): |
| 1214 """Tests removing all objects in bucket via rm -R gs://bucket""" |
| 1215 # Test with and without final slash on dest subdir. |
| 1216 for final_src_char in ('', '/'): |
| 1217 # Setup: Copy a directory, including subdir, to bucket. |
| 1218 self.RunCommand('cp', ['-R', self.src_dir_root, self.dst_bucket_uri.uri]) |
| 1219 # Test removing all objects via rm -R. |
| 1220 self.RunCommand('rm', ['-R', '%s%s' % (self.dst_bucket_uri, |
| 1221 final_src_char)]) |
| 1222 actual = set(str(u) for u in test_util.test_wildcard_iterator( |
| 1223 '%s*' % self.dst_bucket_uri.uri).IterUris()) |
| 1224 self.assertEqual(0, len(actual)) |
| 1225 # Clean up/re-set up for next variant iteration. |
| 1226 self.TearDownClass() |
| 1227 self.SetUpClass() |
| 1228 |
| 1229 def FinalObjNameComponent(self, uri): |
| 1230 """For gs://bucket/abc/def/ghi returns ghi.""" |
| 1231 return uri.uri.rpartition('/')[-1] |
| 1232 |
| 1233 if __name__ == '__main__': |
| 1234 if sys.version_info[:3] < (2, 5, 1): |
| 1235 sys.exit('These tests must be run on at least Python 2.5.1\n') |
| 1236 test_loader = unittest.TestLoader() |
| 1237 test_loader.testMethodPrefix = 'Test' |
| 1238 suite = test_loader.loadTestsFromTestCase(GsutilCommandTests) |
| 1239 # Seems like there should be a cleaner way to find the test_class. |
| 1240 test_class = suite.__getattribute__('_tests')[0] |
| 1241 # We call SetUpClass() and TearDownClass() ourselves because we |
| 1242 # don't assume the user has Python 2.7 (which supports classmethods |
| 1243 # that do it, with camelCase versions of these names). |
| 1244 try: |
| 1245 print 'Setting up %s...' % test_class.GetSuiteDescription() |
| 1246 test_class.SetUpClass() |
| 1247 print 'Running %s...' % test_class.GetSuiteDescription() |
| 1248 unittest.TextTestRunner(verbosity=2).run(suite) |
| 1249 finally: |
| 1250 print 'Cleaning up after %s...' % test_class.GetSuiteDescription() |
| 1251 test_class.TearDownClass() |
| 1252 print '' |
OLD | NEW |