| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # pylint: disable=W0223 | 6 # pylint: disable=W0223 |
| 7 # pylint: disable=W0231 | 7 # pylint: disable=W0231 |
| 8 | 8 |
| 9 import binascii | |
| 10 import hashlib | 9 import hashlib |
| 11 import json | 10 import json |
| 12 import logging | 11 import logging |
| 13 import os | 12 import os |
| 14 import random | |
| 15 import shutil | 13 import shutil |
| 16 import StringIO | 14 import StringIO |
| 17 import sys | 15 import sys |
| 18 import tempfile | 16 import tempfile |
| 19 import threading | 17 import threading |
| 20 import unittest | 18 import unittest |
| 19 import urllib |
| 21 import zlib | 20 import zlib |
| 22 | 21 |
| 23 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) | 22 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) |
| 24 ROOT_DIR = os.path.dirname(BASE_PATH) | 23 ROOT_DIR = os.path.dirname(BASE_PATH) |
| 25 sys.path.insert(0, ROOT_DIR) | 24 sys.path.insert(0, ROOT_DIR) |
| 26 | 25 |
| 27 import auto_stub | 26 import auto_stub |
| 28 import isolateserver | 27 import isolateserver |
| 29 | 28 |
| 30 from utils import threading_utils | 29 from utils import threading_utils |
| (...skipping 26 matching lines...) Expand all Loading... |
| 57 for i, n in enumerate(self._requests): | 56 for i, n in enumerate(self._requests): |
| 58 if n[0] == url: | 57 if n[0] == url: |
| 59 _, expected_kwargs, result = self._requests.pop(i) | 58 _, expected_kwargs, result = self._requests.pop(i) |
| 60 self.assertEqual(expected_kwargs, kwargs) | 59 self.assertEqual(expected_kwargs, kwargs) |
| 61 if result is not None: | 60 if result is not None: |
| 62 return isolateserver.net.HttpResponse.get_fake_response(result, url) | 61 return isolateserver.net.HttpResponse.get_fake_response(result, url) |
| 63 return None | 62 return None |
| 64 self.fail('Unknown request %s' % url) | 63 self.fail('Unknown request %s' % url) |
| 65 | 64 |
| 66 | 65 |
| 66 class TestZipCompression(TestCase): |
| 67 """Test zip_compress and zip_decompress generators.""" |
| 68 |
| 69 def test_compress_and_decompress(self): |
| 70 """Test data === decompress(compress(data)).""" |
| 71 original = [str(x) for x in xrange(0, 1000)] |
| 72 processed = isolateserver.zip_decompress( |
| 73 isolateserver.zip_compress(original)) |
| 74 self.assertEqual(''.join(original), ''.join(processed)) |
| 75 |
| 76 def test_zip_bomb(self): |
| 77 """Verify zip_decompress always returns small chunks.""" |
| 78 original = '\x00' * 100000 |
| 79 bomb = ''.join(isolateserver.zip_compress(original)) |
| 80 decompressed = [] |
| 81 chunk_size = 1000 |
| 82 for chunk in isolateserver.zip_decompress([bomb], chunk_size): |
| 83 self.assertLessEqual(len(chunk), chunk_size) |
| 84 decompressed.append(chunk) |
| 85 self.assertEqual(original, ''.join(decompressed)) |
| 86 |
| 87 def test_bad_zip_file(self): |
| 88 """Verify decompressing broken file raises IOError.""" |
| 89 with self.assertRaises(IOError): |
| 90 ''.join(isolateserver.zip_decompress(['Im not a zip file'])) |
| 91 |
| 92 |
| 67 class StorageTest(TestCase): | 93 class StorageTest(TestCase): |
| 68 """Tests for Storage methods.""" | 94 """Tests for Storage methods.""" |
| 69 | 95 |
| 70 @staticmethod | 96 @staticmethod |
| 71 def mock_push(side_effect=None): | 97 def mock_push(side_effect=None): |
| 72 """Returns StorageApi subclass with mocked 'push' method.""" | 98 """Returns StorageApi subclass with mocked 'push' method.""" |
| 73 class MockedStorageApi(isolateserver.StorageApi): | 99 class MockedStorageApi(isolateserver.StorageApi): |
| 74 def __init__(self): | 100 def __init__(self): |
| 75 self.pushed = [] | 101 self.pushed = [] |
| 76 def push(self, item, expected_size, content_generator, push_urls=None): | 102 def push(self, item, expected_size, content_generator, push_urls=None): |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 if metadata['h'] == digest | 309 if metadata['h'] == digest |
| 284 ] | 310 ] |
| 285 self.assertEqual(1, len(filenames)) | 311 self.assertEqual(1, len(filenames)) |
| 286 filename = filenames[0] | 312 filename = filenames[0] |
| 287 data = files_data[filename] | 313 data = files_data[filename] |
| 288 self.assertEqual( | 314 self.assertEqual( |
| 289 (digest, len(data), data, push_urls[filename]), | 315 (digest, len(data), data, push_urls[filename]), |
| 290 push_call) | 316 push_call) |
| 291 | 317 |
| 292 | 318 |
| 293 class IsolateServerArchiveTest(TestCase): | 319 class IsolateServerStorageApiTest(TestCase): |
| 294 def setUp(self): | 320 @staticmethod |
| 295 super(IsolateServerArchiveTest, self).setUp() | 321 def mock_handshake_request(server, token='fake token', error=None): |
| 296 self.mock(isolateserver, 'randomness', lambda: 'not_really_random') | 322 handshake_request = { |
| 297 self.mock(sys, 'stdout', StringIO.StringIO()) | 323 'protocol_version': isolateserver.ISOLATE_PROTOCOL_VERSION, |
| 298 | 324 'client_app_version': isolateserver.__version__, |
| 299 def test_present(self): | 325 'fetcher': True, |
| 326 'pusher': True, |
| 327 } |
| 328 handshake_response = { |
| 329 'protocol_version': isolateserver.ISOLATE_PROTOCOL_VERSION, |
| 330 'server_app_version': 'mocked server T1000', |
| 331 'access_token': token, |
| 332 'error': error, |
| 333 } |
| 334 return ( |
| 335 server + '/content-gs/handshake', |
| 336 { |
| 337 'content_type': 'application/json', |
| 338 'method': 'POST', |
| 339 'data': json.dumps(handshake_request, separators=(',', ':')), |
| 340 }, |
| 341 json.dumps(handshake_response), |
| 342 ) |
| 343 |
| 344 @staticmethod |
| 345 def mock_fetch_request(server, namespace, item, data): |
| 346 return ( |
| 347 server + '/content-gs/retrieve/%s/%s' % (namespace, item), |
| 348 {'retry_404': True, 'read_timeout': 60}, |
| 349 data, |
| 350 ) |
| 351 |
| 352 @staticmethod |
| 353 def mock_contains_request(server, namespace, token, request, response): |
| 354 url = server + '/content-gs/pre-upload/%s?token=%s' % ( |
| 355 namespace, urllib.quote(token)) |
| 356 return ( |
| 357 url, |
| 358 { |
| 359 'data': json.dumps(request, separators=(',', ':')), |
| 360 'content_type': 'application/json', |
| 361 'method': 'POST', |
| 362 }, |
| 363 json.dumps(response), |
| 364 ) |
| 365 |
| 366 def test_server_capabilities_success(self): |
| 367 server = 'http://example.com' |
| 368 namespace = 'default' |
| 369 access_token = 'fake token' |
| 370 self._requests = [ |
| 371 self.mock_handshake_request(server, access_token), |
| 372 ] |
| 373 storage = isolateserver.IsolateServer(server, namespace) |
| 374 caps = storage.server_capabilities |
| 375 self.assertEqual(access_token, caps['access_token']) |
| 376 |
| 377 def test_server_capabilities_network_failure(self): |
| 378 self.mock(isolateserver.net, 'url_open', lambda *_args, **_kwargs: None) |
| 379 with self.assertRaises(isolateserver.MappingError): |
| 380 storage = isolateserver.IsolateServer('http://example.com', 'default') |
| 381 _ = storage.server_capabilities |
| 382 |
| 383 def test_server_capabilities_format_failure(self): |
| 384 server = 'http://example.com' |
| 385 namespace = 'default' |
| 386 handshake_req = self.mock_handshake_request(server) |
| 387 self._requests = [ |
| 388 (handshake_req[0], handshake_req[1], 'Im a bad response'), |
| 389 ] |
| 390 storage = isolateserver.IsolateServer(server, namespace) |
| 391 with self.assertRaises(isolateserver.MappingError): |
| 392 _ = storage.server_capabilities |
| 393 |
| 394 def test_server_capabilities_respects_error(self): |
| 395 server = 'http://example.com' |
| 396 namespace = 'default' |
| 397 error = 'Im sorry, Dave. Im afraid I cant do that.' |
| 398 self._requests = [ |
| 399 self.mock_handshake_request(server, error=error) |
| 400 ] |
| 401 storage = isolateserver.IsolateServer(server, namespace) |
| 402 with self.assertRaises(isolateserver.MappingError) as context: |
| 403 _ = storage.server_capabilities |
| 404 # Server error message should be reported to user. |
| 405 self.assertIn(error, str(context.exception)) |
| 406 |
| 407 def test_fetch_success_default(self): |
| 408 server = 'http://example.com' |
| 409 namespace = 'default' |
| 410 data = ''.join(str(x) for x in xrange(1000)) |
| 411 item = ALGO(data).hexdigest() |
| 412 self._requests = [ |
| 413 self.mock_fetch_request(server, namespace, item, data), |
| 414 ] |
| 415 storage = isolateserver.IsolateServer(server, namespace) |
| 416 fetched = ''.join(storage.fetch(item, len(data))) |
| 417 self.assertEqual(data, fetched) |
| 418 |
| 419 def test_fetch_success_default_gzip(self): |
| 420 server = 'http://example.com' |
| 421 namespace = 'default-gzip' |
| 422 data = ''.join(str(x) for x in xrange(1000)) |
| 423 item = ALGO(data).hexdigest() |
| 424 self._requests = [ |
| 425 self.mock_fetch_request(server, namespace, item, zlib.compress(data)), |
| 426 ] |
| 427 storage = isolateserver.IsolateServer(server, namespace) |
| 428 fetched = ''.join(storage.fetch(item, len(data))) |
| 429 self.assertEqual(data, fetched) |
| 430 |
| 431 def test_fetch_failure_missing(self): |
| 432 server = 'http://example.com' |
| 433 namespace = 'default' |
| 434 item = ALGO('something').hexdigest() |
| 435 self._requests = [ |
| 436 self.mock_fetch_request(server, namespace, item, None), |
| 437 ] |
| 438 storage = isolateserver.IsolateServer(server, namespace) |
| 439 with self.assertRaises(IOError): |
| 440 _ = ''.join(storage.fetch(item, isolateserver.UNKNOWN_FILE_SIZE)) |
| 441 |
| 442 def test_fetch_failure_bad_size(self): |
| 443 server = 'http://example.com' |
| 444 namespace = 'default' |
| 445 data = ''.join(str(x) for x in xrange(1000)) |
| 446 expected_size = len(data) |
| 447 item = ALGO(data).hexdigest() |
| 448 self._requests = [ |
| 449 self.mock_fetch_request(server, namespace, item, data[:100]), |
| 450 ] |
| 451 storage = isolateserver.IsolateServer(server, namespace) |
| 452 with self.assertRaises(IOError): |
| 453 _ = ''.join(storage.fetch(item, expected_size)) |
| 454 |
| 455 def test_fetch_failure_bad_zip(self): |
| 456 server = 'http://example.com' |
| 457 namespace = 'default-gzip' |
| 458 item = ALGO('something').hexdigest() |
| 459 self._requests = [ |
| 460 self.mock_fetch_request(server, namespace, item, 'Im not a zip'), |
| 461 ] |
| 462 storage = isolateserver.IsolateServer(server, namespace) |
| 463 with self.assertRaises(IOError): |
| 464 _ = ''.join(storage.fetch(item, isolateserver.UNKNOWN_FILE_SIZE)) |
| 465 |
| 466 def test_push_success(self): |
| 467 server = 'http://example.com' |
| 468 namespace = 'default' |
| 469 data = ''.join(str(x) for x in xrange(1000)) |
| 470 item = ALGO(data).hexdigest() |
| 471 push_urls = (server + '/push_here', server + '/call_this') |
| 472 self._requests = [ |
| 473 ( |
| 474 push_urls[0], |
| 475 { |
| 476 'data': data, |
| 477 'content_type': 'application/octet-stream', |
| 478 'method': 'PUT', |
| 479 }, |
| 480 '' |
| 481 ), |
| 482 ( |
| 483 push_urls[1], |
| 484 { |
| 485 'data': '', |
| 486 'content_type': 'application/json', |
| 487 'method': 'POST', |
| 488 }, |
| 489 '' |
| 490 ), |
| 491 ] |
| 492 storage = isolateserver.IsolateServer(server, namespace) |
| 493 storage.push(item, len(data), [data], push_urls) |
| 494 |
| 495 def test_push_failure_upload(self): |
| 496 server = 'http://example.com' |
| 497 namespace = 'default' |
| 498 data = ''.join(str(x) for x in xrange(1000)) |
| 499 item = ALGO(data).hexdigest() |
| 500 push_urls = (server + '/push_here', server + '/call_this') |
| 501 self._requests = [ |
| 502 ( |
| 503 push_urls[0], |
| 504 { |
| 505 'data': data, |
| 506 'content_type': 'application/octet-stream', |
| 507 'method': 'PUT', |
| 508 }, |
| 509 None |
| 510 ), |
| 511 ] |
| 512 storage = isolateserver.IsolateServer(server, namespace) |
| 513 with self.assertRaises(IOError): |
| 514 storage.push(item, len(data), [data], push_urls) |
| 515 |
| 516 def test_push_failure_finalize(self): |
| 517 server = 'http://example.com' |
| 518 namespace = 'default' |
| 519 data = ''.join(str(x) for x in xrange(1000)) |
| 520 item = ALGO(data).hexdigest() |
| 521 push_urls = (server + '/push_here', server + '/call_this') |
| 522 self._requests = [ |
| 523 ( |
| 524 push_urls[0], |
| 525 { |
| 526 'data': data, |
| 527 'content_type': 'application/octet-stream', |
| 528 'method': 'PUT', |
| 529 }, |
| 530 '' |
| 531 ), |
| 532 ( |
| 533 push_urls[1], |
| 534 { |
| 535 'data': '', |
| 536 'content_type': 'application/json', |
| 537 'method': 'POST', |
| 538 }, |
| 539 None |
| 540 ), |
| 541 ] |
| 542 storage = isolateserver.IsolateServer(server, namespace) |
| 543 with self.assertRaises(IOError): |
| 544 storage.push(item, len(data), [data], push_urls) |
| 545 |
| 546 def test_contains_success(self): |
| 547 server = 'http://example.com' |
| 548 namespace = 'default' |
| 549 token = 'fake token' |
| 300 files = [ | 550 files = [ |
| 301 os.path.join(BASE_PATH, 'isolateserver', f) | 551 ('file_a', {'h': 'hash1', 's': 100}), |
| 302 for f in ('small_file.txt', 'empty_file.txt') | 552 ('file_b', {'h': 'hash2', 's': 200, 'priority': '0'}), |
| 303 ] | 553 ('file_c', {'h': 'hash3', 's': 300, 'priority': '255'}), |
| 304 hash_encoded = ''.join( | 554 ] |
| 305 binascii.unhexlify(isolateserver.hash_file(f, ALGO)) for f in files) | 555 request = [ |
| 306 path = 'http://random/' | 556 {'h': 'hash1', 's': 100, 'i': 0}, |
| 307 self._requests = [ | 557 {'h': 'hash2', 's': 200, 'i': 1}, |
| 308 (path + 'content/get_token', {}, 'foo bar'), | 558 {'h': 'hash3', 's': 300, 'i': 0}, |
| 309 ( | 559 ] |
| 310 path + 'content/contains/default-gzip?token=foo%20bar', | 560 response = [ |
| 311 {'data': hash_encoded, 'content_type': 'application/octet-stream'}, | 561 None, |
| 312 '\1\1', | 562 ['http://example/upload_here_1', None], |
| 313 ), | 563 ['http://example/upload_here_2', 'http://example/call_this'], |
| 314 ] | 564 ] |
| 315 result = isolateserver.main(['archive', '--isolate-server', path] + files) | 565 missing = [ |
| 316 self.assertEqual(0, result) | 566 files[1] + (response[1],), |
| 317 | 567 files[2] + (response[2],) |
| 318 def test_missing(self): | 568 ] |
| 319 files = [ | 569 self._requests = [ |
| 320 os.path.join(BASE_PATH, 'isolateserver', f) | 570 self.mock_handshake_request(server, token), |
| 321 for f in ('small_file.txt', 'empty_file.txt') | 571 self.mock_contains_request(server, namespace, token, request, response), |
| 322 ] | 572 ] |
| 323 hashes = [isolateserver.hash_file(f, ALGO) for f in files] | 573 storage = isolateserver.IsolateServer(server, namespace) |
| 324 hash_encoded = ''.join(map(binascii.unhexlify, hashes)) | 574 result = storage.contains(files) |
| 325 compressed = [ | 575 self.assertEqual(missing, result) |
| 326 zlib.compress( | 576 |
| 327 open(f, 'rb').read(), | 577 def test_contains_network_failure(self): |
| 328 isolateserver.get_zip_compression_level(f)) | 578 server = 'http://example.com' |
| 329 for f in files | 579 namespace = 'default' |
| 330 ] | 580 token = 'fake token' |
| 331 path = 'http://random/' | 581 req = self.mock_contains_request(server, namespace, token, [], []) |
| 332 self._requests = [ | 582 self._requests = [ |
| 333 (path + 'content/get_token', {}, 'foo bar'), | 583 self.mock_handshake_request(server, token), |
| 334 ( | 584 (req[0], req[1], None), |
| 335 path + 'content/contains/default-gzip?token=foo%20bar', | 585 ] |
| 336 {'data': hash_encoded, 'content_type': 'application/octet-stream'}, | 586 storage = isolateserver.IsolateServer(server, namespace) |
| 337 '\0\0', | 587 with self.assertRaises(isolateserver.MappingError): |
| 338 ), | 588 storage.contains([]) |
| 339 ( | 589 |
| 340 path + 'content/store/default-gzip/%s?token=foo%%20bar' % hashes[0], | 590 def test_contains_format_failure(self): |
| 341 {'data': compressed[0], 'content_type': 'application/octet-stream'}, | 591 server = 'http://example.com' |
| 342 'ok', | 592 namespace = 'default' |
| 343 ), | 593 token = 'fake token' |
| 344 ( | 594 self._requests = [ |
| 345 path + 'content/store/default-gzip/%s?token=foo%%20bar' % hashes[1], | 595 self.mock_handshake_request(server, token), |
| 346 {'data': compressed[1], 'content_type': 'application/octet-stream'}, | 596 self.mock_contains_request(server, namespace, token, [], [1, 2, 3]) |
| 347 'ok', | 597 ] |
| 348 ), | 598 storage = isolateserver.IsolateServer(server, namespace) |
| 349 ] | 599 with self.assertRaises(isolateserver.MappingError): |
| 350 result = isolateserver.main(['archive', '--isolate-server', path] + files) | 600 storage.contains([]) |
| 351 self.assertEqual(0, result) | |
| 352 | |
| 353 def test_large(self): | |
| 354 content = '' | |
| 355 compressed = '' | |
| 356 while ( | |
| 357 len(compressed) <= isolateserver.MIN_SIZE_FOR_DIRECT_BLOBSTORE): | |
| 358 # The goal here is to generate a file, once compressed, is at least | |
| 359 # MIN_SIZE_FOR_DIRECT_BLOBSTORE. | |
| 360 content += ''.join(chr(random.randint(0, 255)) for _ in xrange(20*1024)) | |
| 361 compressed = zlib.compress( | |
| 362 content, isolateserver.get_zip_compression_level('foo.txt')) | |
| 363 | |
| 364 s = ALGO(content).hexdigest() | |
| 365 infiles = { | |
| 366 'foo.txt': { | |
| 367 's': len(content), | |
| 368 'h': s, | |
| 369 }, | |
| 370 } | |
| 371 path = 'http://random/' | |
| 372 hash_encoded = binascii.unhexlify(s) | |
| 373 content_type, body = isolateserver.encode_multipart_formdata( | |
| 374 [('token', 'foo bar')], [('content', s, compressed)]) | |
| 375 | |
| 376 self._requests = [ | |
| 377 (path + 'content/get_token', {}, 'foo bar'), | |
| 378 ( | |
| 379 path + 'content/contains/default-gzip?token=foo%20bar', | |
| 380 {'data': hash_encoded, 'content_type': 'application/octet-stream'}, | |
| 381 '\0', | |
| 382 ), | |
| 383 ( | |
| 384 path + 'content/generate_blobstore_url/default-gzip/%s' % s, | |
| 385 {'data': [('token', 'foo bar')]}, | |
| 386 'an_url/', | |
| 387 ), | |
| 388 ( | |
| 389 'an_url/', | |
| 390 {'data': body, 'content_type': content_type, 'retry_50x': False}, | |
| 391 'ok', | |
| 392 ), | |
| 393 ] | |
| 394 | |
| 395 # Setup mocks for zip_compress to return |compressed|. | |
| 396 self.mock(isolateserver, 'file_read', lambda *_: None) | |
| 397 self.mock(isolateserver, 'zip_compress', lambda *_: [compressed]) | |
| 398 result = isolateserver.upload_tree( | |
| 399 base_url=path, | |
| 400 indir=os.getcwd(), | |
| 401 infiles=infiles, | |
| 402 namespace='default-gzip') | |
| 403 | |
| 404 self.assertEqual(0, result) | |
| 405 | |
| 406 def test_upload_blobstore_simple(self): | |
| 407 # A tad over 20kb so it triggers uploading to the blob store. | |
| 408 content = '0123456789' * 21*1024 | |
| 409 s = ALGO(content).hexdigest() | |
| 410 path = 'http://example.com:80/' | |
| 411 data = [('token', 'a_token')] | |
| 412 content_type, body = isolateserver.encode_multipart_formdata( | |
| 413 data, [('content', s, content)]) | |
| 414 self._requests = [ | |
| 415 ( | |
| 416 path + 'content/get_token', | |
| 417 {}, | |
| 418 'a_token', | |
| 419 ), | |
| 420 ( | |
| 421 path + 'content/generate_blobstore_url/x/' + s, | |
| 422 {'data': data[:]}, | |
| 423 'http://example.com/an_url/', | |
| 424 ), | |
| 425 ( | |
| 426 'http://example.com/an_url/', | |
| 427 {'data': body, 'content_type': content_type, 'retry_50x': False}, | |
| 428 'ok42', | |
| 429 ), | |
| 430 ] | |
| 431 # |size| is currently ignored. | |
| 432 result = isolateserver.IsolateServer(path, 'x').push(s, -2, [content]) | |
| 433 self.assertEqual('ok42', result) | |
| 434 | |
| 435 def test_upload_blobstore_retry_500(self): | |
| 436 # A tad over 20kb so it triggers uploading to the blob store. | |
| 437 content = '0123456789' * 21*1024 | |
| 438 s = ALGO(content).hexdigest() | |
| 439 path = 'http://example.com:80/' | |
| 440 data = [('token', 'a_token')] | |
| 441 content_type, body = isolateserver.encode_multipart_formdata( | |
| 442 data, [('content', s, content)]) | |
| 443 self._requests = [ | |
| 444 ( | |
| 445 path + 'content/get_token', | |
| 446 {}, | |
| 447 'a_token', | |
| 448 ), | |
| 449 ( | |
| 450 path + 'content/generate_blobstore_url/x/' + s, | |
| 451 {'data': data[:]}, | |
| 452 'http://example.com/an_url/', | |
| 453 ), | |
| 454 ( | |
| 455 'http://example.com/an_url/', | |
| 456 {'data': body, 'content_type': content_type, 'retry_50x': False}, | |
| 457 # Let's say an HTTP 500 was returned. | |
| 458 None, | |
| 459 ), | |
| 460 # In that case, a new url must be generated since the last one may have | |
| 461 # been "consumed". | |
| 462 ( | |
| 463 path + 'content/generate_blobstore_url/x/' + s, | |
| 464 {'data': data[:]}, | |
| 465 'http://example.com/an_url_2/', | |
| 466 ), | |
| 467 ( | |
| 468 'http://example.com/an_url_2/', | |
| 469 {'data': body, 'content_type': content_type, 'retry_50x': False}, | |
| 470 'ok42', | |
| 471 ), | |
| 472 ] | |
| 473 # |size| is currently ignored. | |
| 474 result = isolateserver.IsolateServer(path, 'x').push(s, -2, [content]) | |
| 475 self.assertEqual('ok42', result) | |
| 476 | 601 |
| 477 | 602 |
| 478 class IsolateServerDownloadTest(TestCase): | 603 class IsolateServerDownloadTest(TestCase): |
| 479 tempdir = None | 604 tempdir = None |
| 480 | 605 |
| 481 def tearDown(self): | 606 def tearDown(self): |
| 482 try: | 607 try: |
| 483 if self.tempdir: | 608 if self.tempdir: |
| 484 shutil.rmtree(self.tempdir) | 609 shutil.rmtree(self.tempdir) |
| 485 finally: | 610 finally: |
| 486 super(IsolateServerDownloadTest, self).tearDown() | 611 super(IsolateServerDownloadTest, self).tearDown() |
| 487 | 612 |
| 488 def test_download_two_files(self): | 613 def test_download_two_files(self): |
| 489 # Test downloading two files. | 614 # Test downloading two files. |
| 490 actual = {} | 615 actual = {} |
| 491 def out(key, generator): | 616 def out(key, generator): |
| 492 actual[key] = ''.join(generator) | 617 actual[key] = ''.join(generator) |
| 493 self.mock(isolateserver, 'file_write', out) | 618 self.mock(isolateserver, 'file_write', out) |
| 494 server = 'http://example.com' | 619 server = 'http://example.com' |
| 495 self._requests = [ | 620 self._requests = [ |
| 496 ( | 621 ( |
| 497 server + '/content/retrieve/default-gzip/sha-1', | 622 server + '/content-gs/retrieve/default-gzip/sha-1', |
| 498 {'read_timeout': 60, 'retry_404': True}, | 623 {'read_timeout': 60, 'retry_404': True}, |
| 499 zlib.compress('Coucou'), | 624 zlib.compress('Coucou'), |
| 500 ), | 625 ), |
| 501 ( | 626 ( |
| 502 server + '/content/retrieve/default-gzip/sha-2', | 627 server + '/content-gs/retrieve/default-gzip/sha-2', |
| 503 {'read_timeout': 60, 'retry_404': True}, | 628 {'read_timeout': 60, 'retry_404': True}, |
| 504 zlib.compress('Bye Bye'), | 629 zlib.compress('Bye Bye'), |
| 505 ), | 630 ), |
| 506 ] | 631 ] |
| 507 cmd = [ | 632 cmd = [ |
| 508 'download', | 633 'download', |
| 509 '--isolate-server', server, | 634 '--isolate-server', server, |
| 510 '--target', ROOT_DIR, | 635 '--target', ROOT_DIR, |
| 511 '--file', 'sha-1', 'path/to/a', | 636 '--file', 'sha-1', 'path/to/a', |
| 512 '--file', 'sha-2', 'path/to/b', | 637 '--file', 'sha-2', 'path/to/b', |
| (...skipping 27 matching lines...) Expand all Loading... |
| 540 'files': dict( | 665 'files': dict( |
| 541 (k, {'h': ALGO(v).hexdigest(), 's': len(v)}) | 666 (k, {'h': ALGO(v).hexdigest(), 's': len(v)}) |
| 542 for k, v in files.iteritems()), | 667 for k, v in files.iteritems()), |
| 543 } | 668 } |
| 544 isolated_data = json.dumps(isolated, sort_keys=True, separators=(',',':')) | 669 isolated_data = json.dumps(isolated, sort_keys=True, separators=(',',':')) |
| 545 isolated_hash = ALGO(isolated_data).hexdigest() | 670 isolated_hash = ALGO(isolated_data).hexdigest() |
| 546 requests = [(v['h'], files[k]) for k, v in isolated['files'].iteritems()] | 671 requests = [(v['h'], files[k]) for k, v in isolated['files'].iteritems()] |
| 547 requests.append((isolated_hash, isolated_data)) | 672 requests.append((isolated_hash, isolated_data)) |
| 548 self._requests = [ | 673 self._requests = [ |
| 549 ( | 674 ( |
| 550 server + '/content/retrieve/default-gzip/' + h, | 675 server + '/content-gs/retrieve/default-gzip/' + h, |
| 551 { | 676 { |
| 552 'read_timeout': isolateserver.DOWNLOAD_READ_TIMEOUT, | 677 'read_timeout': isolateserver.DOWNLOAD_READ_TIMEOUT, |
| 553 'retry_404': True, | 678 'retry_404': True, |
| 554 }, | 679 }, |
| 555 zlib.compress(v), | 680 zlib.compress(v), |
| 556 ) for h, v in requests | 681 ) for h, v in requests |
| 557 ] | 682 ] |
| 558 cmd = [ | 683 cmd = [ |
| 559 'download', | 684 'download', |
| 560 '--isolate-server', server, | 685 '--isolate-server', server, |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 649 expected = gen_data(os.path.sep) | 774 expected = gen_data(os.path.sep) |
| 650 self.assertEqual(expected, actual) | 775 self.assertEqual(expected, actual) |
| 651 | 776 |
| 652 | 777 |
| 653 if __name__ == '__main__': | 778 if __name__ == '__main__': |
| 654 if '-v' in sys.argv: | 779 if '-v' in sys.argv: |
| 655 unittest.TestCase.maxDiff = None | 780 unittest.TestCase.maxDiff = None |
| 656 logging.basicConfig( | 781 logging.basicConfig( |
| 657 level=(logging.DEBUG if '-v' in sys.argv else logging.ERROR)) | 782 level=(logging.DEBUG if '-v' in sys.argv else logging.ERROR)) |
| 658 unittest.main() | 783 unittest.main() |
| OLD | NEW |