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

Side by Side Diff: tests/swarming_test.py

Issue 22980008: Merge all swarm_*.py scripts into swarming.py. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: Rebase against r219402 Created 7 years, 3 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
« no previous file with comments | « tests/swarming_smoke_test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 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 import json 6 import json
7 import logging 7 import logging
8 import os 8 import os
9 import StringIO 9 import StringIO
10 import sys 10 import sys
11 import threading 11 import threading
12 import unittest 12 import unittest
13 import urllib2 13 import urllib2
14 14
15 import auto_stub 15 import auto_stub
16 16
17 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18 sys.path.insert(0, ROOT_DIR) 18 sys.path.insert(0, ROOT_DIR)
19 19
20 import run_isolated 20 import run_isolated
21 import swarm_get_results 21 import swarming
22
23
24 FILE_NAME = u'test.isolated'
25 FILE_HASH = u'1' * 40
26 TEST_NAME = u'unit_tests'
27 STDOUT_FOR_TRIGGER_LEN = 188
22 28
23 29
24 TEST_CASE_SUCCESS = ( 30 TEST_CASE_SUCCESS = (
25 '[----------] 2 tests from StaticCookiePolicyTest\n' 31 '[----------] 2 tests from StaticCookiePolicyTest\n'
26 '[ RUN ] StaticCookiePolicyTest.AllowAllCookiesTest\n' 32 '[ RUN ] StaticCookiePolicyTest.AllowAllCookiesTest\n'
27 '[ OK ] StaticCookiePolicyTest.AllowAllCookiesTest (0 ms)\n' 33 '[ OK ] StaticCookiePolicyTest.AllowAllCookiesTest (0 ms)\n'
28 '[ RUN ] StaticCookiePolicyTest.BlockAllCookiesTest\n' 34 '[ RUN ] StaticCookiePolicyTest.BlockAllCookiesTest\n'
29 '[ OK ] StaticCookiePolicyTest.BlockAllCookiesTest (0 ms)\n' 35 '[ OK ] StaticCookiePolicyTest.BlockAllCookiesTest (0 ms)\n'
30 '[----------] 2 tests from StaticCookiePolicyTest (0 ms total)\n' 36 '[----------] 2 tests from StaticCookiePolicyTest (0 ms total)\n'
31 '\n' 37 '\n'
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 url_response.code = 200 132 url_response.code = 200
127 url_response.msg = 'OK' 133 url_response.msg = 'OK'
128 return url_response 134 return url_response
129 135
130 136
131 def get_swarm_results(keys): 137 def get_swarm_results(keys):
132 """Simplifies the call to yield_results(). 138 """Simplifies the call to yield_results().
133 139
134 The timeout is hard-coded to 10 seconds. 140 The timeout is hard-coded to 10 seconds.
135 """ 141 """
136 return list( 142 return list(swarming.yield_results('http://host:9001', keys, 10., None))
137 swarm_get_results.yield_results(
138 'http://host:9001', keys, 10., None))
139 143
140 144
141 class TestCase(auto_stub.TestCase): 145 class TestCase(auto_stub.TestCase):
142 """Base class that defines the url_open mock.""" 146 """Base class that defines the url_open mock."""
143 def setUp(self): 147 def setUp(self):
144 super(TestCase, self).setUp() 148 super(TestCase, self).setUp()
145 self._lock = threading.Lock() 149 self._lock = threading.Lock()
146 self.requests = [] 150 self.requests = []
147 self.mock( 151 self.mock(swarming.run_isolated, 'url_open', self._url_open)
148 swarm_get_results.run_isolated, 'url_open',
149 self._url_open)
150 152
151 def tearDown(self): 153 def tearDown(self):
152 try: 154 try:
153 if not self.has_failed(): 155 if not self.has_failed():
154 self.assertEqual([], self.requests) 156 self.assertEqual([], self.requests)
155 finally: 157 finally:
156 super(TestCase, self).tearDown() 158 super(TestCase, self).tearDown()
157 159
158 def _url_open(self, url, **kwargs): 160 def _url_open(self, url, **kwargs):
159 logging.info('url_open(%s)', url) 161 logging.info('url_open(%s)', url)
160 with self._lock: 162 with self._lock:
161 # Since the client is multi-threaded, requests can be processed out of 163 # Since the client is multi-threaded, requests can be processed out of
162 # order. 164 # order.
163 for index, r in enumerate(self.requests): 165 for index, r in enumerate(self.requests):
164 if r[0] == url and r[1] == kwargs: 166 if r[0] == url and r[1] == kwargs:
165 _, _, returned = self.requests.pop(index) 167 _, _, returned = self.requests.pop(index)
166 break 168 break
167 else: 169 else:
168 self.fail('Failed to find url %s' % url) 170 self.fail('Failed to find url %s' % url)
169 return returned 171 return returned
170 172
171 173
172 class TestGetTestKeys(TestCase): 174 class TestGetTestKeys(TestCase):
173 def test_no_keys(self): 175 def test_no_keys(self):
174 self.mock(swarm_get_results.time, 'sleep', lambda x: x) 176 self.mock(swarming.time, 'sleep', lambda x: x)
175 self.requests = [ 177 self.requests = [
176 ( 178 (
177 'http://host:9001/get_matching_test_cases?name=my_test', 179 'http://host:9001/get_matching_test_cases?name=my_test',
178 {'retry_404': True}, 180 {'retry_404': True},
179 StringIO.StringIO('No matching Test Cases'), 181 StringIO.StringIO('No matching Test Cases'),
180 ) for _ in range(run_isolated.URL_OPEN_MAX_ATTEMPTS) 182 ) for _ in range(run_isolated.URL_OPEN_MAX_ATTEMPTS)
181 ] 183 ]
182 try: 184 try:
183 swarm_get_results.get_test_keys('http://host:9001', 'my_test') 185 swarming.get_test_keys('http://host:9001', 'my_test')
184 self.fail() 186 self.fail()
185 except swarm_get_results.Failure as e: 187 except swarming.Failure as e:
186 msg = ( 188 msg = (
187 'Error: Unable to find any tests with the name, my_test, on swarm ' 189 'Error: Unable to find any tests with the name, my_test, on swarm '
188 'server') 190 'server')
189 self.assertEqual(msg, e.args[0]) 191 self.assertEqual(msg, e.args[0])
190 192
191 def test_no_keys_on_first_attempt(self): 193 def test_no_keys_on_first_attempt(self):
192 self.mock(swarm_get_results.time, 'sleep', lambda x: x) 194 self.mock(swarming.time, 'sleep', lambda x: x)
193 keys = ['key_1', 'key_2'] 195 keys = ['key_1', 'key_2']
194 self.requests = [ 196 self.requests = [
195 ( 197 (
196 'http://host:9001/get_matching_test_cases?name=my_test', 198 'http://host:9001/get_matching_test_cases?name=my_test',
197 {'retry_404': True}, 199 {'retry_404': True},
198 StringIO.StringIO('No matching Test Cases'), 200 StringIO.StringIO('No matching Test Cases'),
199 ), 201 ),
200 ( 202 (
201 'http://host:9001/get_matching_test_cases?name=my_test', 203 'http://host:9001/get_matching_test_cases?name=my_test',
202 {'retry_404': True}, 204 {'retry_404': True},
203 StringIO.StringIO(json.dumps(keys)), 205 StringIO.StringIO(json.dumps(keys)),
204 ), 206 ),
205 ] 207 ]
206 actual = swarm_get_results.get_test_keys('http://host:9001', 'my_test') 208 actual = swarming.get_test_keys('http://host:9001', 'my_test')
207 self.assertEqual(keys, actual) 209 self.assertEqual(keys, actual)
208 210
209 def test_find_keys(self): 211 def test_find_keys(self):
210 keys = ['key_1', 'key_2'] 212 keys = ['key_1', 'key_2']
211 self.requests = [ 213 self.requests = [
212 ( 214 (
213 'http://host:9001/get_matching_test_cases?name=my_test', 215 'http://host:9001/get_matching_test_cases?name=my_test',
214 {'retry_404': True}, 216 {'retry_404': True},
215 StringIO.StringIO(json.dumps(keys)), 217 StringIO.StringIO(json.dumps(keys)),
216 ), 218 ),
217 ] 219 ]
218 actual = swarm_get_results.get_test_keys('http://host:9001', 'my_test') 220 actual = swarming.get_test_keys('http://host:9001', 'my_test')
219 self.assertEqual(keys, actual) 221 self.assertEqual(keys, actual)
220 222
221 223
222 class TestGetSwarmResults(TestCase): 224 class TestGetSwarmResults(TestCase):
223 def test_success(self): 225 def test_success(self):
224 self.requests = [ 226 self.requests = [
225 ( 227 (
226 'http://host:9001/get_result?r=key1', 228 'http://host:9001/get_result?r=key1',
227 {'retry_404': False, 'retry_50x': False}, 229 {'retry_404': False, 'retry_50x': False},
228 generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'), 230 generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'),
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 def test_url_errors(self): 265 def test_url_errors(self):
264 # NOTE: get_swarm_results() hardcodes timeout=10. range(12) is because of an 266 # NOTE: get_swarm_results() hardcodes timeout=10. range(12) is because of an
265 # additional time.time() call deep in run_isolated.url_open(). 267 # additional time.time() call deep in run_isolated.url_open().
266 now = {} 268 now = {}
267 lock = threading.Lock() 269 lock = threading.Lock()
268 def get_now(): 270 def get_now():
269 t = threading.current_thread() 271 t = threading.current_thread()
270 with lock: 272 with lock:
271 return now.setdefault(t, range(12)).pop(0) 273 return now.setdefault(t, range(12)).pop(0)
272 self.mock( 274 self.mock(
273 swarm_get_results.run_isolated.HttpService, 275 swarming.run_isolated.HttpService,
274 'sleep_before_retry', 276 'sleep_before_retry',
275 staticmethod(lambda _x, _y: None)) 277 staticmethod(lambda _x, _y: None))
276 self.mock(swarm_get_results, 'now', get_now) 278 self.mock(swarming, 'now', get_now)
277 # The actual number of requests here depends on 'now' progressing to 10 279 # The actual number of requests here depends on 'now' progressing to 10
278 # seconds. It's called twice per loop. 280 # seconds. It's called twice per loop.
279 self.requests = [ 281 self.requests = [
280 ( 282 (
281 'http://host:9001/get_result?r=key1', 283 'http://host:9001/get_result?r=key1',
282 {'retry_404': False, 'retry_50x': False}, 284 {'retry_404': False, 'retry_50x': False},
283 None, 285 None,
284 ), 286 ),
285 ( 287 (
286 'http://host:9001/get_result?r=key1', 288 'http://host:9001/get_result?r=key1',
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
350 ] 352 ]
351 expected = [ 353 expected = [
352 gen_yielded_data(0, TEST_SHARD_OUTPUT_1, '0, 0'), 354 gen_yielded_data(0, TEST_SHARD_OUTPUT_1, '0, 0'),
353 gen_yielded_data(1, TEST_SHARD_OUTPUT_2, '0, 0'), 355 gen_yielded_data(1, TEST_SHARD_OUTPUT_2, '0, 0'),
354 gen_yielded_data(2, TEST_SHARD_OUTPUT_3, '0, 0'), 356 gen_yielded_data(2, TEST_SHARD_OUTPUT_3, '0, 0'),
355 ] 357 ]
356 actual = get_swarm_results(['key1', 'key1-repeat', 'key2', 'key3']) 358 actual = get_swarm_results(['key1', 'key1-repeat', 'key2', 'key3'])
357 self.assertEqual(expected, sorted(actual)) 359 self.assertEqual(expected, sorted(actual))
358 360
359 361
362 def chromium_tasks(retrieval_url):
363 return [
364 {
365 u'action': [
366 u'python', u'run_isolated.py',
367 u'--hash', FILE_HASH,
368 u'--remote', retrieval_url + u'default-gzip/',
369 ],
370 u'decorate_output': False,
371 u'test_name': u'Run Test',
372 u'time_out': 600,
373 },
374 {
375 u'action' : [
376 u'python', u'swarm_cleanup.py',
377 ],
378 u'decorate_output': False,
379 u'test_name': u'Clean Up',
380 u'time_out': 600,
381 }
382 ]
383
384
385 def generate_expected_json(
386 shards,
387 slave_os,
388 working_dir,
389 isolate_server,
390 profile):
391 retrieval_url = isolate_server + '/content/retrieve/'
392 os_value = unicode(swarming.PLATFORM_MAPPING[slave_os])
393 expected = {
394 u'cleanup': u'root',
395 u'configurations': [
396 {
397 u'config_name': os_value,
398 u'dimensions': {
399 u'os': os_value,
400 },
401 u'min_instances': shards,
402 },
403 ],
404 u'data': [[retrieval_url + u'default/', u'swarm_data.zip']],
405 u'env_vars': {},
406 u'restart_on_failure': True,
407 u'test_case_name': TEST_NAME,
408 u'tests': chromium_tasks(retrieval_url),
409 u'working_dir': unicode(working_dir),
410 u'priority': 101,
411 }
412 if shards > 1:
413 expected[u'env_vars'][u'GTEST_SHARD_INDEX'] = u'%(instance_index)s'
414 expected[u'env_vars'][u'GTEST_TOTAL_SHARDS'] = u'%(num_instances)s'
415 if profile:
416 expected[u'tests'][0][u'action'].append(u'--verbose')
417 return expected
418
419
420 class MockZipFile(object):
421 def __init__(self, filename, mode):
422 pass
423
424 def write(self, source, dest=None):
425 pass
426
427 def close(self):
428 pass
429
430
431 def MockUrlOpen(url, _data, has_return_value):
432 if '/content/contains' in url:
433 return StringIO.StringIO(has_return_value)
434 return StringIO.StringIO('{}')
435
436
437 def MockUrlOpenHasZip(url, data=None, content_type=None):
438 assert content_type in (None, 'application/json', 'application/octet-stream')
439 return MockUrlOpen(url, data, has_return_value=chr(1))
440
441
442 def MockUrlOpenNoZip(url, data=None, content_type=None):
443 assert content_type in (None, 'application/json', 'application/octet-stream')
444 return MockUrlOpen(url, data, has_return_value=chr(0))
445
446
447 class ManifestTest(auto_stub.TestCase):
448 def setUp(self):
449 self.mock(swarming.time, 'sleep', lambda x: None)
450 self.mock(swarming.zipfile, 'ZipFile', MockZipFile)
451 self.mock(sys, 'stdout', StringIO.StringIO())
452 self.mock(sys, 'stderr', StringIO.StringIO())
453
454 def tearDown(self):
455 if not self.has_failed():
456 self._check_output('', '')
457 super(ManifestTest, self).tearDown()
458
459 def _check_output(self, out, err):
460 self.assertEqual(out, sys.stdout.getvalue())
461 self.assertEqual(err, sys.stderr.getvalue())
462
463 # Flush their content by mocking them again.
464 self.mock(sys, 'stdout', StringIO.StringIO())
465 self.mock(sys, 'stderr', StringIO.StringIO())
466
467 def test_basic_manifest(self):
468 manifest = swarming.Manifest(
469 manifest_hash=FILE_HASH,
470 test_name=TEST_NAME,
471 shards=2,
472 test_filter='*',
473 slave_os='win32',
474 working_dir='swarm_tests',
475 isolate_server='http://localhost:8081',
476 verbose=False,
477 profile=False,
478 priority=101)
479
480 swarming.chromium_setup(manifest)
481 manifest_json = json.loads(manifest.to_json())
482
483 expected = generate_expected_json(
484 shards=2,
485 slave_os='win32',
486 working_dir='swarm_tests',
487 isolate_server='http://localhost:8081',
488 profile=False)
489 self.assertEqual(expected, manifest_json)
490
491 def test_basic_linux(self):
492 """A basic linux manifest test to ensure that windows specific values
493 aren't used.
494 """
495 manifest = swarming.Manifest(
496 manifest_hash=FILE_HASH,
497 test_name=TEST_NAME,
498 shards=1,
499 test_filter='*',
500 slave_os='linux2',
501 working_dir='swarm_tests',
502 isolate_server='http://localhost:8081',
503 verbose=False,
504 profile=False,
505 priority=101)
506
507 swarming.chromium_setup(manifest)
508 manifest_json = json.loads(manifest.to_json())
509
510 expected = generate_expected_json(
511 shards=1,
512 slave_os='linux2',
513 working_dir='swarm_tests',
514 isolate_server='http://localhost:8081',
515 profile=False)
516 self.assertEqual(expected, manifest_json)
517
518 def test_basic_linux_profile(self):
519 manifest = swarming.Manifest(
520 manifest_hash=FILE_HASH,
521 test_name=TEST_NAME,
522 shards=1,
523 test_filter='*',
524 slave_os='linux2',
525 working_dir='swarm_tests',
526 isolate_server='http://localhost:8081',
527 verbose=False,
528 profile=True,
529 priority=101)
530
531 swarming.chromium_setup(manifest)
532 manifest_json = json.loads(manifest.to_json())
533
534 expected = generate_expected_json(
535 shards=1,
536 slave_os='linux2',
537 working_dir='swarm_tests',
538 isolate_server='http://localhost:8081',
539 profile=True)
540 self.assertEqual(expected, manifest_json)
541
542 def test_process_manifest_success(self):
543 self.mock(swarming.run_isolated, 'url_open', MockUrlOpenNoZip)
544
545 result = swarming.process_manifest(
546 file_sha1_or_isolated=FILE_HASH,
547 test_name=TEST_NAME,
548 shards=1,
549 test_filter='*',
550 slave_os='linux2',
551 working_dir='swarm_tests',
552 isolate_server='http://localhost:8081',
553 swarming='http://localhost:8082',
554 verbose=False,
555 profile=False,
556 priority=101)
557 self.assertEqual(0, result)
558
559 # Just assert it printed enough, since it contains variable output.
560 out = sys.stdout.getvalue()
561 self.assertTrue(
562 len(out) > STDOUT_FOR_TRIGGER_LEN,
563 (out, sys.stderr.getvalue()))
564 self.assertTrue('Zip file not on server, starting uploading.' in out)
565 self.mock(sys, 'stdout', StringIO.StringIO())
566
567 def test_process_manifest_success_zip_already_uploaded(self):
568 self.mock(swarming.run_isolated, 'url_open', MockUrlOpenHasZip)
569
570 result = swarming.process_manifest(
571 file_sha1_or_isolated=FILE_HASH,
572 test_name=TEST_NAME,
573 shards=1,
574 test_filter='*',
575 slave_os='linux2',
576 working_dir='swarm_tests',
577 isolate_server='http://localhost:8081',
578 swarming='http://localhost:8082',
579 verbose=False,
580 profile=False,
581 priority=101)
582 self.assertEqual(0, result)
583
584 # Just assert it printed enough, since it contains variable output.
585 out = sys.stdout.getvalue()
586 self.assertTrue(len(out) > STDOUT_FOR_TRIGGER_LEN)
587 self.assertTrue('Zip file already on server, no need to reupload.' in out)
588 self.mock(sys, 'stdout', StringIO.StringIO())
589
590 def test_no_request(self):
591 try:
592 swarming.main(['trigger'])
593 self.fail()
594 except SystemExit as e:
595 self.assertEqual(2, e.code)
596 self._check_output(
597 '',
598 'Usage: swarming.py trigger [options]\n\n'
599 'swarming.py: error: At least one --task is required.\n')
600
601
360 if __name__ == '__main__': 602 if __name__ == '__main__':
361 logging.basicConfig( 603 logging.basicConfig(
362 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR) 604 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
363 if '-v' in sys.argv: 605 if '-v' in sys.argv:
364 unittest.TestCase.maxDiff = None 606 unittest.TestCase.maxDiff = None
365 unittest.main() 607 unittest.main()
OLDNEW
« no previous file with comments | « tests/swarming_smoke_test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698