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

Side by Side Diff: third_party/gsutil/boto/tests/integration/gs/test_resumable_downloads.py

Issue 12317103: Added gsutil to depot tools (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: added readme Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright 2010 Google Inc.
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish, dis-
9 # tribute, sublicense, and/or sell copies of the Software, and to permit
10 # persons to whom the Software is furnished to do so, subject to the fol-
11 # lowing conditions:
12 #
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 # IN THE SOFTWARE.
23
24 """
25 Tests of resumable downloads.
26 """
27
28 import errno
29 import os
30 import re
31
32 import boto
33 from boto.s3.resumable_download_handler import get_cur_file_size
34 from boto.s3.resumable_download_handler import ResumableDownloadHandler
35 from boto.exception import ResumableTransferDisposition
36 from boto.exception import ResumableDownloadException
37 from cb_test_harness import CallbackTestHarness
38 from tests.integration.gs.testcase import GSTestCase
39
40
41 SMALL_KEY_SIZE = 2 * 1024 # 2 KB.
42 LARGE_KEY_SIZE = 500 * 1024 # 500 KB.
43
44
45 class ResumableDownloadTests(GSTestCase):
46 """Resumable download test suite."""
47
48 def make_small_key(self):
49 small_src_key_as_string = os.urandom(SMALL_KEY_SIZE)
50 small_src_key = self._MakeKey(data=small_src_key_as_string)
51 return small_src_key_as_string, small_src_key
52
53 def make_tracker_file(self, tmpdir=None):
54 if not tmpdir:
55 tmpdir = self._MakeTempDir()
56 tracker_file = os.path.join(tmpdir, 'tracker')
57 return tracker_file
58
59 def make_dst_fp(self, tmpdir=None):
60 if not tmpdir:
61 tmpdir = self._MakeTempDir()
62 dst_file = os.path.join(tmpdir, 'dstfile')
63 return open(dst_file, 'w')
64
65 def test_non_resumable_download(self):
66 """
67 Tests that non-resumable downloads work
68 """
69 dst_fp = self.make_dst_fp()
70 small_src_key_as_string, small_src_key = self.make_small_key()
71 small_src_key.get_contents_to_file(dst_fp)
72 self.assertEqual(SMALL_KEY_SIZE,
73 get_cur_file_size(dst_fp))
74 self.assertEqual(small_src_key_as_string,
75 small_src_key.get_contents_as_string())
76
77 def test_download_without_persistent_tracker(self):
78 """
79 Tests a single resumable download, with no tracker persistence
80 """
81 res_download_handler = ResumableDownloadHandler()
82 dst_fp = self.make_dst_fp()
83 small_src_key_as_string, small_src_key = self.make_small_key()
84 small_src_key.get_contents_to_file(
85 dst_fp, res_download_handler=res_download_handler)
86 self.assertEqual(SMALL_KEY_SIZE,
87 get_cur_file_size(dst_fp))
88 self.assertEqual(small_src_key_as_string,
89 small_src_key.get_contents_as_string())
90
91 def test_failed_download_with_persistent_tracker(self):
92 """
93 Tests that failed resumable download leaves a correct tracker file
94 """
95 harness = CallbackTestHarness()
96 tmpdir = self._MakeTempDir()
97 tracker_file_name = self.make_tracker_file(tmpdir)
98 dst_fp = self.make_dst_fp(tmpdir)
99 res_download_handler = ResumableDownloadHandler(
100 tracker_file_name=tracker_file_name, num_retries=0)
101 small_src_key_as_string, small_src_key = self.make_small_key()
102 try:
103 small_src_key.get_contents_to_file(
104 dst_fp, cb=harness.call,
105 res_download_handler=res_download_handler)
106 self.fail('Did not get expected ResumableDownloadException')
107 except ResumableDownloadException, e:
108 # We'll get a ResumableDownloadException at this point because
109 # of CallbackTestHarness (above). Check that the tracker file was
110 # created correctly.
111 self.assertEqual(e.disposition,
112 ResumableTransferDisposition.ABORT_CUR_PROCESS)
113 self.assertTrue(os.path.exists(tracker_file_name))
114 f = open(tracker_file_name)
115 etag_line = f.readline()
116 m = re.search(ResumableDownloadHandler.ETAG_REGEX, etag_line)
117 f.close()
118 self.assertTrue(m)
119
120 def test_retryable_exception_recovery(self):
121 """
122 Tests handling of a retryable exception
123 """
124 # Test one of the RETRYABLE_EXCEPTIONS.
125 exception = ResumableDownloadHandler.RETRYABLE_EXCEPTIONS[0]
126 harness = CallbackTestHarness(exception=exception)
127 res_download_handler = ResumableDownloadHandler(num_retries=1)
128 dst_fp = self.make_dst_fp()
129 small_src_key_as_string, small_src_key = self.make_small_key()
130 small_src_key.get_contents_to_file(
131 dst_fp, cb=harness.call,
132 res_download_handler=res_download_handler)
133 # Ensure downloaded object has correct content.
134 self.assertEqual(SMALL_KEY_SIZE,
135 get_cur_file_size(dst_fp))
136 self.assertEqual(small_src_key_as_string,
137 small_src_key.get_contents_as_string())
138
139 def test_broken_pipe_recovery(self):
140 """
141 Tests handling of a Broken Pipe (which interacts with an httplib bug)
142 """
143 exception = IOError(errno.EPIPE, "Broken pipe")
144 harness = CallbackTestHarness(exception=exception)
145 res_download_handler = ResumableDownloadHandler(num_retries=1)
146 dst_fp = self.make_dst_fp()
147 small_src_key_as_string, small_src_key = self.make_small_key()
148 small_src_key.get_contents_to_file(
149 dst_fp, cb=harness.call,
150 res_download_handler=res_download_handler)
151 # Ensure downloaded object has correct content.
152 self.assertEqual(SMALL_KEY_SIZE,
153 get_cur_file_size(dst_fp))
154 self.assertEqual(small_src_key_as_string,
155 small_src_key.get_contents_as_string())
156
157 def test_non_retryable_exception_handling(self):
158 """
159 Tests resumable download that fails with a non-retryable exception
160 """
161 harness = CallbackTestHarness(
162 exception=OSError(errno.EACCES, 'Permission denied'))
163 res_download_handler = ResumableDownloadHandler(num_retries=1)
164 dst_fp = self.make_dst_fp()
165 small_src_key_as_string, small_src_key = self.make_small_key()
166 try:
167 small_src_key.get_contents_to_file(
168 dst_fp, cb=harness.call,
169 res_download_handler=res_download_handler)
170 self.fail('Did not get expected OSError')
171 except OSError, e:
172 # Ensure the error was re-raised.
173 self.assertEqual(e.errno, 13)
174
175 def test_failed_and_restarted_download_with_persistent_tracker(self):
176 """
177 Tests resumable download that fails once and then completes,
178 with tracker file
179 """
180 harness = CallbackTestHarness()
181 tmpdir = self._MakeTempDir()
182 tracker_file_name = self.make_tracker_file(tmpdir)
183 dst_fp = self.make_dst_fp(tmpdir)
184 small_src_key_as_string, small_src_key = self.make_small_key()
185 res_download_handler = ResumableDownloadHandler(
186 tracker_file_name=tracker_file_name, num_retries=1)
187 small_src_key.get_contents_to_file(
188 dst_fp, cb=harness.call,
189 res_download_handler=res_download_handler)
190 # Ensure downloaded object has correct content.
191 self.assertEqual(SMALL_KEY_SIZE,
192 get_cur_file_size(dst_fp))
193 self.assertEqual(small_src_key_as_string,
194 small_src_key.get_contents_as_string())
195 # Ensure tracker file deleted.
196 self.assertFalse(os.path.exists(tracker_file_name))
197
198 def test_multiple_in_process_failures_then_succeed(self):
199 """
200 Tests resumable download that fails twice in one process, then completes
201 """
202 res_download_handler = ResumableDownloadHandler(num_retries=3)
203 dst_fp = self.make_dst_fp()
204 small_src_key_as_string, small_src_key = self.make_small_key()
205 small_src_key.get_contents_to_file(
206 dst_fp, res_download_handler=res_download_handler)
207 # Ensure downloaded object has correct content.
208 self.assertEqual(SMALL_KEY_SIZE,
209 get_cur_file_size(dst_fp))
210 self.assertEqual(small_src_key_as_string,
211 small_src_key.get_contents_as_string())
212
213 def test_multiple_in_process_failures_then_succeed_with_tracker_file(self):
214 """
215 Tests resumable download that fails completely in one process,
216 then when restarted completes, using a tracker file
217 """
218 # Set up test harness that causes more failures than a single
219 # ResumableDownloadHandler instance will handle, writing enough data
220 # before the first failure that some of it survives that process run.
221 harness = CallbackTestHarness(
222 fail_after_n_bytes=LARGE_KEY_SIZE/2, num_times_to_fail=2)
223 larger_src_key_as_string = os.urandom(LARGE_KEY_SIZE)
224 larger_src_key = self._MakeKey(data=larger_src_key_as_string)
225 tmpdir = self._MakeTempDir()
226 tracker_file_name = self.make_tracker_file(tmpdir)
227 dst_fp = self.make_dst_fp(tmpdir)
228 res_download_handler = ResumableDownloadHandler(
229 tracker_file_name=tracker_file_name, num_retries=0)
230 try:
231 larger_src_key.get_contents_to_file(
232 dst_fp, cb=harness.call,
233 res_download_handler=res_download_handler)
234 self.fail('Did not get expected ResumableDownloadException')
235 except ResumableDownloadException, e:
236 self.assertEqual(e.disposition,
237 ResumableTransferDisposition.ABORT_CUR_PROCESS)
238 # Ensure a tracker file survived.
239 self.assertTrue(os.path.exists(tracker_file_name))
240 # Try it one more time; this time should succeed.
241 larger_src_key.get_contents_to_file(
242 dst_fp, cb=harness.call,
243 res_download_handler=res_download_handler)
244 self.assertEqual(LARGE_KEY_SIZE,
245 get_cur_file_size(dst_fp))
246 self.assertEqual(larger_src_key_as_string,
247 larger_src_key.get_contents_as_string())
248 self.assertFalse(os.path.exists(tracker_file_name))
249 # Ensure some of the file was downloaded both before and after failure.
250 self.assertTrue(
251 len(harness.transferred_seq_before_first_failure) > 1 and
252 len(harness.transferred_seq_after_first_failure) > 1)
253
254 def test_download_with_inital_partial_download_before_failure(self):
255 """
256 Tests resumable download that successfully downloads some content
257 before it fails, then restarts and completes
258 """
259 # Set up harness to fail download after several hundred KB so download
260 # server will have saved something before we retry.
261 harness = CallbackTestHarness(
262 fail_after_n_bytes=LARGE_KEY_SIZE/2)
263 larger_src_key_as_string = os.urandom(LARGE_KEY_SIZE)
264 larger_src_key = self._MakeKey(data=larger_src_key_as_string)
265 res_download_handler = ResumableDownloadHandler(num_retries=1)
266 dst_fp = self.make_dst_fp()
267 larger_src_key.get_contents_to_file(
268 dst_fp, cb=harness.call,
269 res_download_handler=res_download_handler)
270 # Ensure downloaded object has correct content.
271 self.assertEqual(LARGE_KEY_SIZE,
272 get_cur_file_size(dst_fp))
273 self.assertEqual(larger_src_key_as_string,
274 larger_src_key.get_contents_as_string())
275 # Ensure some of the file was downloaded both before and after failure.
276 self.assertTrue(
277 len(harness.transferred_seq_before_first_failure) > 1 and
278 len(harness.transferred_seq_after_first_failure) > 1)
279
280 def test_zero_length_object_download(self):
281 """
282 Tests downloading a zero-length object (exercises boundary conditions).
283 """
284 res_download_handler = ResumableDownloadHandler()
285 dst_fp = self.make_dst_fp()
286 k = self._MakeKey()
287 k.get_contents_to_file(dst_fp,
288 res_download_handler=res_download_handler)
289 self.assertEqual(0, get_cur_file_size(dst_fp))
290
291 def test_download_with_invalid_tracker_etag(self):
292 """
293 Tests resumable download with a tracker file containing an invalid etag
294 """
295 tmp_dir = self._MakeTempDir()
296 dst_fp = self.make_dst_fp(tmp_dir)
297 small_src_key_as_string, small_src_key = self.make_small_key()
298 invalid_etag_tracker_file_name = os.path.join(tmp_dir,
299 'invalid_etag_tracker')
300 f = open(invalid_etag_tracker_file_name, 'w')
301 f.write('3.14159\n')
302 f.close()
303 res_download_handler = ResumableDownloadHandler(
304 tracker_file_name=invalid_etag_tracker_file_name)
305 # An error should be printed about the invalid tracker, but then it
306 # should run the update successfully.
307 small_src_key.get_contents_to_file(
308 dst_fp, res_download_handler=res_download_handler)
309 self.assertEqual(SMALL_KEY_SIZE, get_cur_file_size(dst_fp))
310 self.assertEqual(small_src_key_as_string,
311 small_src_key.get_contents_as_string())
312
313 def test_download_with_inconsistent_etag_in_tracker(self):
314 """
315 Tests resumable download with an inconsistent etag in tracker file
316 """
317 tmp_dir = self._MakeTempDir()
318 dst_fp = self.make_dst_fp(tmp_dir)
319 small_src_key_as_string, small_src_key = self.make_small_key()
320 inconsistent_etag_tracker_file_name = os.path.join(tmp_dir,
321 'inconsistent_etag_tracker')
322 f = open(inconsistent_etag_tracker_file_name, 'w')
323 good_etag = small_src_key.etag.strip('"\'')
324 new_val_as_list = []
325 for c in reversed(good_etag):
326 new_val_as_list.append(c)
327 f.write('%s\n' % ''.join(new_val_as_list))
328 f.close()
329 res_download_handler = ResumableDownloadHandler(
330 tracker_file_name=inconsistent_etag_tracker_file_name)
331 # An error should be printed about the expired tracker, but then it
332 # should run the update successfully.
333 small_src_key.get_contents_to_file(
334 dst_fp, res_download_handler=res_download_handler)
335 self.assertEqual(SMALL_KEY_SIZE,
336 get_cur_file_size(dst_fp))
337 self.assertEqual(small_src_key_as_string,
338 small_src_key.get_contents_as_string())
339
340 def test_download_with_unwritable_tracker_file(self):
341 """
342 Tests resumable download with an unwritable tracker file
343 """
344 # Make dir where tracker_file lives temporarily unwritable.
345 tmp_dir = self._MakeTempDir()
346 tracker_file_name = os.path.join(tmp_dir, 'tracker')
347 save_mod = os.stat(tmp_dir).st_mode
348 try:
349 os.chmod(tmp_dir, 0)
350 res_download_handler = ResumableDownloadHandler(
351 tracker_file_name=tracker_file_name)
352 except ResumableDownloadException, e:
353 self.assertEqual(e.disposition, ResumableTransferDisposition.ABORT)
354 self.assertNotEqual(
355 e.message.find('Couldn\'t write URI tracker file'), -1)
356 finally:
357 # Restore original protection of dir where tracker_file lives.
358 os.chmod(tmp_dir, save_mod)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698