Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / telemetry / third_party / gsutilz / gslib / tests / mock_cloud_api.py
blobd1cee12cfc6370a956cdfc59c736026e54d77aa7
1 # -*- coding: utf-8 -*-
2 # Copyright 2014 Google Inc. All Rights Reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Implements a simple mock gsutil Cloud API for unit testing.
17 gsutil 4 was primarily unit-tested using boto/gsutil 3's mock storage_uri class,
18 since it was possible that changing out the underlying mocks would have had
19 subtly different behavior and increased the risk of breaking back-compat.
21 Most unit and integration tests in gsutil 4 still set up the test objects with
22 storage_uris and boto, and the unit tests interact with test objects via
23 storage uris and boto.
25 This testing approach ties our tests heavily to boto; extending the
26 boto mocks is difficult because it requires checking into boto. This also
27 makes the unit test coverage boto-specific in several cases.
29 MockCloudApi was initially written to cover some parallel composite upload
30 cases that the boto mocks couldn't handle. It is not yet a full implementation.
31 Eventually, we can move to full a mock Cloud API implementation. However, we
32 need to ensure we don't lose boto coverage from mock storage_uri.
33 """
36 from gslib.cloud_api import ServiceException
37 from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages
38 from gslib.translation_helper import CreateBucketNotFoundException
39 from gslib.translation_helper import CreateObjectNotFoundException
42 class MockObject(object):
43 """Defines a mock cloud storage provider object."""
45 def __init__(self, root_object, contents=''):
46 self.root_object = root_object
47 self.contents = contents
49 def __str__(self):
50 return '%s/%s#%s' % (self.root_object.bucket,
51 self.root_object.name,
52 self.root_object.generation)
54 def __repr__(self):
55 return str(self)
58 class MockBucket(object):
59 """Defines a mock cloud storage provider bucket."""
61 def __init__(self, bucket_name, versioned=False):
62 self.root_object = apitools_messages.Bucket(
63 name=bucket_name,
64 versioning=apitools_messages.Bucket.VersioningValue(enabled=versioned))
65 # Dict of object_name: (dict of 'live': MockObject
66 # 'versioned': ordered list of MockObject).
67 self.objects = {}
69 def CreateObject(self, object_name, contents=''):
70 return self.CreateObjectWithMetadata(MockObject(
71 apitools_messages.Object(name=object_name, contents=contents)))
73 def CreateObjectWithMetadata(self, apitools_object, contents=''):
74 """Creates an object in the bucket according to the input metadata.
76 This will create a new object version (ignoring the generation specified
77 in the input object).
79 Args:
80 apitools_object: apitools Object.
81 contents: optional object contents.
83 Returns:
84 apitools Object representing created object.
85 """
86 # This modifies the apitools_object with a generation number.
87 object_name = apitools_object.name
88 if (self.root_object.versioning and self.root_object.versioning.enabled and
89 apitools_object.name in self.objects):
90 if 'live' in self.objects[object_name]:
91 # Versioning enabled and object exists, create an object with a
92 # generation 1 higher.
93 apitools_object.generation = (
94 self.objects[object_name]['live'].root_object.generation + 1)
95 # Move the live object to versioned.
96 if 'versioned' not in self.objects[object_name]:
97 self.objects[object_name]['versioned'] = []
98 self.objects[object_name]['versioned'].append(
99 self.objects[object_name]['live'])
100 elif ('versioned' in self.objects[object_name] and
101 self.objects[object_name]['versioned']):
102 # Versioning enabled but only archived objects exist, pick a generation
103 # higher than the highest versioned object (which will be at the end).
104 apitools_object.generation = (
105 self.objects[object_name]['versioned'][-1].root_object.generation
106 + 1)
107 else:
108 # Versioning disabled or no objects exist yet with this name.
109 apitools_object.generation = 1
110 self.objects[object_name] = {}
111 new_object = MockObject(apitools_object, contents=contents)
112 self.objects[object_name]['live'] = new_object
113 return new_object
116 class MockCloudApi(object):
117 """Simple mock service for buckets/objects that implements Cloud API.
119 Also includes some setup functions for tests.
122 def __init__(self, provider='gs'):
123 self.buckets = {}
124 self.provider = provider
126 def MockCreateBucket(self, bucket_name):
127 """Creates a simple bucket without exercising the API directly."""
128 if bucket_name in self.buckets:
129 raise ServiceException('Bucket %s already exists.' % bucket_name,
130 status=409)
131 self.buckets[bucket_name] = MockBucket(bucket_name)
133 def MockCreateVersionedBucket(self, bucket_name):
134 """Creates a simple bucket without exercising the API directly."""
135 if bucket_name in self.buckets:
136 raise ServiceException('Bucket %s already exists.' % bucket_name,
137 status=409)
138 self.buckets[bucket_name] = MockBucket(bucket_name, versioned=True)
140 def MockCreateObject(self, bucket_name, object_name, contents=''):
141 """Creates an object without exercising the API directly."""
142 if bucket_name not in self.buckets:
143 self.MockCreateBucket(bucket_name)
144 self.buckets[bucket_name].CreateObject(object_name, contents=contents)
146 def MockCreateObjectWithMetadata(self, apitools_object, contents=''):
147 """Creates an object without exercising the API directly."""
148 assert apitools_object.bucket, 'No bucket specified for mock object'
149 assert apitools_object.name, 'No object name specified for mock object'
150 if apitools_object.bucket not in self.buckets:
151 self.MockCreateBucket(apitools_object.bucket)
152 return self.buckets[apitools_object.bucket].CreateObjectWithMetadata(
153 apitools_object, contents=contents).root_object
155 # pylint: disable=unused-argument
156 def GetObjectMetadata(self, bucket_name, object_name, generation=None,
157 provider=None, fields=None):
158 """See CloudApi class for function doc strings."""
159 if generation:
160 generation = long(generation)
161 if bucket_name in self.buckets:
162 bucket = self.buckets[bucket_name]
163 if object_name in bucket.objects and bucket.objects[object_name]:
164 if generation:
165 if 'versioned' in bucket.objects[object_name]:
166 for obj in bucket.objects[object_name]['versioned']:
167 if obj.root_object.generation == generation:
168 return obj.root_object
169 if 'live' in bucket.objects[object_name]:
170 if (bucket.objects[object_name]['live'].root_object.generation ==
171 generation):
172 return bucket.objects[object_name]['live'].root_object
173 else:
174 # Return live object.
175 if 'live' in bucket.objects[object_name]:
176 return bucket.objects[object_name]['live'].root_object
177 raise CreateObjectNotFoundException(404, self.provider, bucket_name,
178 object_name)
179 raise CreateBucketNotFoundException(404, self.provider, bucket_name)