Index: third_party/gsutil/gslib/tests/testcase/integration_testcase.py |
diff --git a/third_party/gsutil/gslib/tests/testcase/integration_testcase.py b/third_party/gsutil/gslib/tests/testcase/integration_testcase.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1b10f8a90b978f139aff57fb7f72180c8cfa316f |
--- /dev/null |
+++ b/third_party/gsutil/gslib/tests/testcase/integration_testcase.py |
@@ -0,0 +1,197 @@ |
+# Copyright 2013 Google Inc. All Rights Reserved. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# 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 integration test case class.""" |
+ |
+import logging |
+import os.path |
+import subprocess |
+import sys |
+ |
+import boto |
+from boto.exception import GSResponseError |
+ |
+from gslib.project_id import ProjectIdHandler |
+import gslib.tests.util as util |
+from gslib.tests.util import unittest |
+from gslib.util import IS_WINDOWS |
+from gslib.util import Retry |
+import base |
+ |
+ |
+CURDIR = os.path.abspath(os.path.dirname(__file__)) |
+TESTS_DIR = os.path.split(CURDIR)[0] |
+GSLIB_DIR = os.path.split(TESTS_DIR)[0] |
+GSUTIL_DIR = os.path.split(GSLIB_DIR)[0] |
+GSUTIL_PATH = os.path.join(GSUTIL_DIR, 'gsutil') |
+LOGGER = logging.getLogger('integration-test') |
+ |
+ |
+@unittest.skipUnless(util.RUN_INTEGRATION_TESTS, |
+ 'Not running integration tests.') |
+class GsUtilIntegrationTestCase(base.GsUtilTestCase): |
+ """Base class for gsutil integration tests.""" |
+ GROUP_TEST_ADDRESS = 'gs-discussion@googlegroups.com' |
+ GROUP_TEST_ID = '00b4903a97d097895ab58ef505d535916a712215b79c3e54932c2eb502ad97f5' |
+ USER_TEST_ADDRESS = 'gs-team@google.com' |
+ USER_TEST_ID = '00b4903a9703325c6bfc98992d72e75600387a64b3b6bee9ef74613ef8842080' |
+ DOMAIN_TEST = 'google.com' |
+ |
+ def setUp(self): |
+ super(GsUtilIntegrationTestCase, self).setUp() |
+ self.bucket_uris = [] |
+ |
+ # Set up API version and project ID handler. |
+ self.api_version = boto.config.get_value( |
+ 'GSUtil', 'default_api_version', '1') |
+ self.proj_id_handler = ProjectIdHandler() |
+ |
+ # Retry with an exponential backoff if a server error is received. This |
+ # ensures that we try *really* hard to clean up after ourselves. |
+ @Retry(GSResponseError, logger=LOGGER) |
+ def tearDown(self): |
+ super(GsUtilIntegrationTestCase, self).tearDown() |
+ |
+ while self.bucket_uris: |
+ bucket_uri = self.bucket_uris[-1] |
+ bucket_list = list(bucket_uri.list_bucket(all_versions=True)) |
+ while bucket_list: |
+ for k in bucket_list: |
+ k.delete() |
+ bucket_list = list(bucket_uri.list_bucket(all_versions=True)) |
+ bucket_uri.delete_bucket() |
+ self.bucket_uris.pop() |
+ |
+ def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None): |
+ """Creates a test bucket. |
+ |
+ The bucket and all of its contents will be deleted after the test. |
+ |
+ Args: |
+ bucket_name: Create the bucket with this name. If not provided, a |
+ temporary test bucket name is constructed. |
+ test_objects: The number of objects that should be placed in the bucket. |
+ Defaults to 0. |
+ storage_class: storage class to use. If not provided we us standard. |
+ |
+ Returns: |
+ StorageUri for the created bucket. |
+ """ |
+ bucket_name = bucket_name or self.MakeTempName('bucket') |
+ |
+ bucket_uri = boto.storage_uri('gs://%s' % bucket_name.lower(), |
+ suppress_consec_slashes=False) |
+ |
+ # Apply API version and project ID headers if necessary. |
+ headers = {'x-goog-api-version': self.api_version} |
+ self.proj_id_handler.FillInProjectHeaderIfNeeded( |
+ 'test', bucket_uri, headers) |
+ |
+ bucket_uri.create_bucket(storage_class=storage_class, headers=headers) |
+ self.bucket_uris.append(bucket_uri) |
+ for i in range(test_objects): |
+ self.CreateObject(bucket_uri=bucket_uri, |
+ object_name=self.MakeTempName('obj'), |
+ contents='test %d' % i) |
+ return bucket_uri |
+ |
+ def CreateVersionedBucket(self, bucket_name=None, test_objects=0): |
+ """Creates a versioned test bucket. |
+ |
+ The bucket and all of its contents will be deleted after the test. |
+ |
+ Args: |
+ bucket_name: Create the bucket with this name. If not provided, a |
+ temporary test bucket name is constructed. |
+ test_objects: The number of objects that should be placed in the bucket. |
+ Defaults to 0. |
+ |
+ Returns: |
+ StorageUri for the created bucket with versioning enabled. |
+ """ |
+ bucket_uri = self.CreateBucket(bucket_name=bucket_name, |
+ test_objects=test_objects) |
+ bucket_uri.configure_versioning(True) |
+ return bucket_uri |
+ |
+ def CreateObject(self, bucket_uri=None, object_name=None, contents=None): |
+ """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. |
+ 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 |
+ is not written to, which means that it isn't actually created |
+ yet on the server. |
+ |
+ Returns: |
+ A StorageUri for the created object. |
+ """ |
+ bucket_uri = bucket_uri or self.CreateBucket() |
+ object_name = object_name or self.MakeTempName('obj') |
+ key_uri = bucket_uri.clone_replace_name(object_name) |
+ if contents is not None: |
+ key_uri.set_contents_from_string(contents) |
+ return key_uri |
+ |
+ def RunGsUtil(self, cmd, return_status=False, return_stdout=False, |
+ return_stderr=False, expected_status=0, stdin=None): |
+ """Runs the gsutil command. |
+ |
+ Args: |
+ cmd: The command to run, as a list, e.g. ['cp', 'foo', 'bar'] |
+ return_status: If True, the exit status code is returned. |
+ return_stdout: If True, the standard output of the command is returned. |
+ return_stderr: If True, the standard error of the command is returned. |
+ expected_status: The expected return code. If not specified, defaults to |
+ 0. If the return code is a different value, an exception |
+ is raised. |
+ stdin: A string of data to pipe to the process as standard input. |
+ |
+ Returns: |
+ A tuple containing the desired return values specified by the return_* |
+ arguments. |
+ """ |
+ cmd = [GSUTIL_PATH] + cmd |
+ if IS_WINDOWS: |
+ cmd = [sys.executable] + cmd |
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
+ stdin=subprocess.PIPE) |
+ (stdout, stderr) = p.communicate(stdin) |
+ status = p.returncode |
+ |
+ if expected_status is not None: |
+ self.assertEqual( |
+ status, expected_status, |
+ msg='Expected status %d, got %d.\nCommand:\n%s\n\nstderr:\n%s' % ( |
+ expected_status, status, ' '.join(cmd), stderr)) |
+ |
+ toreturn = [] |
+ if return_status: |
+ toreturn.append(status) |
+ if return_stdout: |
+ if IS_WINDOWS: |
+ stdout = stdout.replace('\r\n', '\n') |
+ toreturn.append(stdout) |
+ if return_stderr: |
+ if IS_WINDOWS: |
+ stderr = stderr.replace('\r\n', '\n') |
+ toreturn.append(stderr) |
+ |
+ if len(toreturn) == 1: |
+ return toreturn[0] |
+ elif toreturn: |
+ return tuple(toreturn) |