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

Side by Side Diff: tests/isolateserver_test.py

Issue 24578004: Client side implementation of new /content-gs isolate protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: decompress by chunks, test for zip/unzip Created 7 years, 2 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 | Annotate | Revision Log
OLDNEW
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
21 import zlib 19 import zlib
22 20
23 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) 21 BASE_PATH = os.path.dirname(os.path.abspath(__file__))
24 ROOT_DIR = os.path.dirname(BASE_PATH) 22 ROOT_DIR = os.path.dirname(BASE_PATH)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 for i, n in enumerate(self._requests): 55 for i, n in enumerate(self._requests):
58 if n[0] == url: 56 if n[0] == url:
59 _, expected_kwargs, result = self._requests.pop(i) 57 _, expected_kwargs, result = self._requests.pop(i)
60 self.assertEqual(expected_kwargs, kwargs) 58 self.assertEqual(expected_kwargs, kwargs)
61 if result is not None: 59 if result is not None:
62 return isolateserver.net.HttpResponse.get_fake_response(result, url) 60 return isolateserver.net.HttpResponse.get_fake_response(result, url)
63 return None 61 return None
64 self.fail('Unknown request %s' % url) 62 self.fail('Unknown request %s' % url)
65 63
66 64
65 class TestZipCompression(TestCase):
66 """Test zip_compress and zip_decompress generators."""
67
68 def test_compress_and_decompress(self):
69 """Test data === decompress(compress(data))."""
70 original = [str(x) for x in xrange(0, 1000)]
71 processed = isolateserver.zip_decompress(
72 isolateserver.zip_compress(original))
73 self.assertEqual(''.join(original), ''.join(processed))
74
75 def test_zip_bomb(self):
76 """Verify zip_decompress alwasy returns small chunks."""
77 chunk_size = 1000
78 bomb = ''.join(isolateserver.zip_compress('\x00' * 100000))
79 for chunk in isolateserver.zip_decompress([bomb], chunk_size):
80 self.assertLessEqual(len(chunk), chunk_size)
81
82
67 class StorageTest(TestCase): 83 class StorageTest(TestCase):
68 """Tests for Storage methods.""" 84 """Tests for Storage methods."""
69 85
70 @staticmethod 86 @staticmethod
71 def mock_push(side_effect=None): 87 def mock_push(side_effect=None):
72 """Returns StorageApi subclass with mocked 'push' method.""" 88 """Returns StorageApi subclass with mocked 'push' method."""
73 class MockedStorageApi(isolateserver.StorageApi): 89 class MockedStorageApi(isolateserver.StorageApi):
74 def __init__(self): 90 def __init__(self):
75 self.pushed = [] 91 self.pushed = []
76 def push(self, item, expected_size, content_generator, push_urls=None): 92 def push(self, item, expected_size, content_generator, push_urls=None):
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 if metadata['h'] == digest 299 if metadata['h'] == digest
284 ] 300 ]
285 self.assertEqual(1, len(filenames)) 301 self.assertEqual(1, len(filenames))
286 filename = filenames[0] 302 filename = filenames[0]
287 data = files_data[filename] 303 data = files_data[filename]
288 self.assertEqual( 304 self.assertEqual(
289 (digest, len(data), data, push_urls[filename]), 305 (digest, len(data), data, push_urls[filename]),
290 push_call) 306 push_call)
291 307
292 308
293 class IsolateServerArchiveTest(TestCase):
294 def setUp(self):
295 super(IsolateServerArchiveTest, self).setUp()
296 self.mock(isolateserver, 'randomness', lambda: 'not_really_random')
297 self.mock(sys, 'stdout', StringIO.StringIO())
298
299 def test_present(self):
300 files = [
301 os.path.join(BASE_PATH, 'isolateserver', f)
302 for f in ('small_file.txt', 'empty_file.txt')
303 ]
304 hash_encoded = ''.join(
305 binascii.unhexlify(isolateserver.hash_file(f, ALGO)) for f in files)
306 path = 'http://random/'
307 self._requests = [
308 (path + 'content/get_token', {}, 'foo bar'),
309 (
310 path + 'content/contains/default-gzip?token=foo%20bar',
311 {'data': hash_encoded, 'content_type': 'application/octet-stream'},
312 '\1\1',
313 ),
314 ]
315 result = isolateserver.main(['archive', '--isolate-server', path] + files)
316 self.assertEqual(0, result)
317
318 def test_missing(self):
319 files = [
320 os.path.join(BASE_PATH, 'isolateserver', f)
321 for f in ('small_file.txt', 'empty_file.txt')
322 ]
323 hashes = [isolateserver.hash_file(f, ALGO) for f in files]
324 hash_encoded = ''.join(map(binascii.unhexlify, hashes))
325 compressed = [
326 zlib.compress(
327 open(f, 'rb').read(),
328 isolateserver.get_zip_compression_level(f))
329 for f in files
330 ]
331 path = 'http://random/'
332 self._requests = [
333 (path + 'content/get_token', {}, 'foo bar'),
334 (
335 path + 'content/contains/default-gzip?token=foo%20bar',
336 {'data': hash_encoded, 'content_type': 'application/octet-stream'},
337 '\0\0',
338 ),
339 (
340 path + 'content/store/default-gzip/%s?token=foo%%20bar' % hashes[0],
341 {'data': compressed[0], 'content_type': 'application/octet-stream'},
342 'ok',
343 ),
344 (
345 path + 'content/store/default-gzip/%s?token=foo%%20bar' % hashes[1],
346 {'data': compressed[1], 'content_type': 'application/octet-stream'},
347 'ok',
348 ),
349 ]
350 result = isolateserver.main(['archive', '--isolate-server', path] + files)
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
477
478 class IsolateServerDownloadTest(TestCase): 309 class IsolateServerDownloadTest(TestCase):
479 tempdir = None 310 tempdir = None
480 311
481 def tearDown(self): 312 def tearDown(self):
482 try: 313 try:
483 if self.tempdir: 314 if self.tempdir:
484 shutil.rmtree(self.tempdir) 315 shutil.rmtree(self.tempdir)
485 finally: 316 finally:
486 super(IsolateServerDownloadTest, self).tearDown() 317 super(IsolateServerDownloadTest, self).tearDown()
487 318
488 def test_download_two_files(self): 319 def test_download_two_files(self):
489 # Test downloading two files. 320 # Test downloading two files.
490 actual = {} 321 actual = {}
491 def out(key, generator): 322 def out(key, generator):
492 actual[key] = ''.join(generator) 323 actual[key] = ''.join(generator)
493 self.mock(isolateserver, 'file_write', out) 324 self.mock(isolateserver, 'file_write', out)
494 server = 'http://example.com' 325 server = 'http://example.com'
495 self._requests = [ 326 self._requests = [
496 ( 327 (
497 server + '/content/retrieve/default-gzip/sha-1', 328 server + '/content-gs/retrieve/default-gzip/sha-1',
498 {'read_timeout': 60, 'retry_404': True}, 329 {'read_timeout': 60, 'retry_404': True},
499 zlib.compress('Coucou'), 330 zlib.compress('Coucou'),
500 ), 331 ),
501 ( 332 (
502 server + '/content/retrieve/default-gzip/sha-2', 333 server + '/content-gs/retrieve/default-gzip/sha-2',
503 {'read_timeout': 60, 'retry_404': True}, 334 {'read_timeout': 60, 'retry_404': True},
504 zlib.compress('Bye Bye'), 335 zlib.compress('Bye Bye'),
505 ), 336 ),
506 ] 337 ]
507 cmd = [ 338 cmd = [
508 'download', 339 'download',
509 '--isolate-server', server, 340 '--isolate-server', server,
510 '--target', ROOT_DIR, 341 '--target', ROOT_DIR,
511 '--file', 'sha-1', 'path/to/a', 342 '--file', 'sha-1', 'path/to/a',
512 '--file', 'sha-2', 'path/to/b', 343 '--file', 'sha-2', 'path/to/b',
(...skipping 27 matching lines...) Expand all
540 'files': dict( 371 'files': dict(
541 (k, {'h': ALGO(v).hexdigest(), 's': len(v)}) 372 (k, {'h': ALGO(v).hexdigest(), 's': len(v)})
542 for k, v in files.iteritems()), 373 for k, v in files.iteritems()),
543 } 374 }
544 isolated_data = json.dumps(isolated, sort_keys=True, separators=(',',':')) 375 isolated_data = json.dumps(isolated, sort_keys=True, separators=(',',':'))
545 isolated_hash = ALGO(isolated_data).hexdigest() 376 isolated_hash = ALGO(isolated_data).hexdigest()
546 requests = [(v['h'], files[k]) for k, v in isolated['files'].iteritems()] 377 requests = [(v['h'], files[k]) for k, v in isolated['files'].iteritems()]
547 requests.append((isolated_hash, isolated_data)) 378 requests.append((isolated_hash, isolated_data))
548 self._requests = [ 379 self._requests = [
549 ( 380 (
550 server + '/content/retrieve/default-gzip/' + h, 381 server + '/content-gs/retrieve/default-gzip/' + h,
551 { 382 {
552 'read_timeout': isolateserver.DOWNLOAD_READ_TIMEOUT, 383 'read_timeout': isolateserver.DOWNLOAD_READ_TIMEOUT,
553 'retry_404': True, 384 'retry_404': True,
554 }, 385 },
555 zlib.compress(v), 386 zlib.compress(v),
556 ) for h, v in requests 387 ) for h, v in requests
557 ] 388 ]
558 cmd = [ 389 cmd = [
559 'download', 390 'download',
560 '--isolate-server', server, 391 '--isolate-server', server,
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 expected = gen_data(os.path.sep) 480 expected = gen_data(os.path.sep)
650 self.assertEqual(expected, actual) 481 self.assertEqual(expected, actual)
651 482
652 483
653 if __name__ == '__main__': 484 if __name__ == '__main__':
654 if '-v' in sys.argv: 485 if '-v' in sys.argv:
655 unittest.TestCase.maxDiff = None 486 unittest.TestCase.maxDiff = None
656 logging.basicConfig( 487 logging.basicConfig(
657 level=(logging.DEBUG if '-v' in sys.argv else logging.ERROR)) 488 level=(logging.DEBUG if '-v' in sys.argv else logging.ERROR))
658 unittest.main() 489 unittest.main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698