| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 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 | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 import json | |
| 7 import logging | |
| 8 import os | |
| 9 import StringIO | |
| 10 import sys | |
| 11 import threading | |
| 12 import unittest | |
| 13 import urllib2 | |
| 14 | |
| 15 import auto_stub | |
| 16 | |
| 17 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| 18 sys.path.insert(0, ROOT_DIR) | |
| 19 | |
| 20 import run_isolated | |
| 21 import swarm_get_results | |
| 22 | |
| 23 | |
| 24 TEST_CASE_SUCCESS = ( | |
| 25 '[----------] 2 tests from StaticCookiePolicyTest\n' | |
| 26 '[ RUN ] StaticCookiePolicyTest.AllowAllCookiesTest\n' | |
| 27 '[ OK ] StaticCookiePolicyTest.AllowAllCookiesTest (0 ms)\n' | |
| 28 '[ RUN ] StaticCookiePolicyTest.BlockAllCookiesTest\n' | |
| 29 '[ OK ] StaticCookiePolicyTest.BlockAllCookiesTest (0 ms)\n' | |
| 30 '[----------] 2 tests from StaticCookiePolicyTest (0 ms total)\n' | |
| 31 '\n' | |
| 32 '[----------] 1 test from TCPListenSocketTest\n' | |
| 33 '[ RUN ] TCPListenSocketTest.ServerSend\n' | |
| 34 '[ OK ] TCPListenSocketTest.ServerSend (1 ms)\n' | |
| 35 '[----------] 1 test from TCPListenSocketTest (1 ms total)\n') | |
| 36 | |
| 37 | |
| 38 TEST_CASE_FAILURE = ( | |
| 39 '[----------] 2 tests from StaticCookiePolicyTest\n' | |
| 40 '[ RUN ] StaticCookiePolicyTest.AllowAllCookiesTest\n' | |
| 41 '[ OK ] StaticCookiePolicyTest.AllowAllCookiesTest (0 ms)\n' | |
| 42 '[ RUN ] StaticCookiePolicyTest.BlockAllCookiesTest\n' | |
| 43 'C:\\win\\build\\src\\chrome\\test.cc: error: Value of: result()\n' | |
| 44 ' Actual: false\n' | |
| 45 'Expected: true\n' | |
| 46 '[ FAILED ] StaticCookiePolicyTest.BlockAllCookiesTest (0 ms)\n' | |
| 47 '[----------] 2 tests from StaticCookiePolicyTest (0 ms total)\n' | |
| 48 '\n' | |
| 49 '[----------] 1 test from TCPListenSocketTest\n' | |
| 50 '[ RUN ] TCPListenSocketTest.ServerSend\n' | |
| 51 '[ OK ] TCPListenSocketTest.ServerSend (1 ms)\n' | |
| 52 '[----------] 1 test from TCPListenSocketTest (1 ms total)\n') | |
| 53 | |
| 54 | |
| 55 SWARM_OUTPUT_SUCCESS = ( | |
| 56 '[ RUN ] unittests.Run Test\n' + | |
| 57 TEST_CASE_SUCCESS + | |
| 58 '[ OK ] unittests.Run Test (2549 ms)\n' | |
| 59 '[ RUN ] unittests.Clean Up\n' | |
| 60 'No output!\n' | |
| 61 '[ OK ] unittests.Clean Up (6 ms)\n' | |
| 62 '\n' | |
| 63 '[----------] unittests summary\n' | |
| 64 '[==========] 2 tests ran. (2556 ms total)\n') | |
| 65 | |
| 66 | |
| 67 SWARM_OUTPUT_FAILURE = ( | |
| 68 '[ RUN ] unittests.Run Test\n' + | |
| 69 TEST_CASE_FAILURE + | |
| 70 '[ OK ] unittests.Run Test (2549 ms)\n' | |
| 71 '[ RUN ] unittests.Clean Up\n' | |
| 72 'No output!\n' | |
| 73 '[ OK ] unittests.Clean Up (6 ms)\n' | |
| 74 '\n' | |
| 75 '[----------] unittests summary\n' | |
| 76 '[==========] 2 tests ran. (2556 ms total)\n') | |
| 77 | |
| 78 | |
| 79 SWARM_OUTPUT_WITH_NO_TEST_OUTPUT = ( | |
| 80 '\n' | |
| 81 'Unable to connection to swarm machine.\n') | |
| 82 | |
| 83 | |
| 84 TEST_SHARD_1 = 'Note: This is test shard 1 of 3.' | |
| 85 TEST_SHARD_2 = 'Note: This is test shard 2 of 3.' | |
| 86 TEST_SHARD_3 = 'Note: This is test shard 3 of 3.' | |
| 87 | |
| 88 | |
| 89 SWARM_SHARD_OUTPUT = ( | |
| 90 '[ RUN ] unittests.Run Test\n' | |
| 91 '%s\n' | |
| 92 '[ OK ] unittests.Run Test (2549 ms)\n' | |
| 93 '[ RUN ] unittests.Clean Up\n' | |
| 94 'No output!\n' | |
| 95 '[ OK ] unittests.Clean Up (6 ms)\n' | |
| 96 '\n' | |
| 97 '[----------] unittests summary\n' | |
| 98 '[==========] 2 tests ran. (2556 ms total)\n') | |
| 99 | |
| 100 | |
| 101 TEST_SHARD_OUTPUT_1 = SWARM_SHARD_OUTPUT % TEST_SHARD_1 | |
| 102 TEST_SHARD_OUTPUT_2 = SWARM_SHARD_OUTPUT % TEST_SHARD_2 | |
| 103 TEST_SHARD_OUTPUT_3 = SWARM_SHARD_OUTPUT % TEST_SHARD_3 | |
| 104 | |
| 105 | |
| 106 def gen_data(index, shard_output, exit_codes): | |
| 107 return { | |
| 108 u'config_instance_index': index, | |
| 109 u'exit_codes': unicode(exit_codes), | |
| 110 u'machine_id': u'host', | |
| 111 u'machine_tag': u'localhost', | |
| 112 u'output': unicode(shard_output), | |
| 113 } | |
| 114 | |
| 115 | |
| 116 def gen_yielded_data(index, shard_output, exit_codes): | |
| 117 """Returns an entry as it would be yielded by yield_results().""" | |
| 118 return index, gen_data(index, shard_output, exit_codes) | |
| 119 | |
| 120 | |
| 121 def generate_url_response(index, shard_output, exit_codes): | |
| 122 url_response = urllib2.addinfourl( | |
| 123 StringIO.StringIO(json.dumps(gen_data(index, shard_output, exit_codes))), | |
| 124 'mock message', | |
| 125 'host') | |
| 126 url_response.code = 200 | |
| 127 url_response.msg = 'OK' | |
| 128 return url_response | |
| 129 | |
| 130 | |
| 131 def get_swarm_results(keys): | |
| 132 """Simplifies the call to yield_results(). | |
| 133 | |
| 134 The timeout is hard-coded to 10 seconds. | |
| 135 """ | |
| 136 return list( | |
| 137 swarm_get_results.yield_results( | |
| 138 'http://host:9001', keys, 10., None)) | |
| 139 | |
| 140 | |
| 141 class TestCase(auto_stub.TestCase): | |
| 142 """Base class that defines the url_open mock.""" | |
| 143 def setUp(self): | |
| 144 super(TestCase, self).setUp() | |
| 145 self._lock = threading.Lock() | |
| 146 self.requests = [] | |
| 147 self.mock( | |
| 148 swarm_get_results.run_isolated, 'url_open', | |
| 149 self._url_open) | |
| 150 | |
| 151 def tearDown(self): | |
| 152 try: | |
| 153 if not self.has_failed(): | |
| 154 self.assertEqual([], self.requests) | |
| 155 finally: | |
| 156 super(TestCase, self).tearDown() | |
| 157 | |
| 158 def _url_open(self, url, **kwargs): | |
| 159 logging.info('url_open(%s)', url) | |
| 160 with self._lock: | |
| 161 # Since the client is multi-threaded, requests can be processed out of | |
| 162 # order. | |
| 163 for index, r in enumerate(self.requests): | |
| 164 if r[0] == url and r[1] == kwargs: | |
| 165 _, _, returned = self.requests.pop(index) | |
| 166 break | |
| 167 else: | |
| 168 self.fail('Failed to find url %s' % url) | |
| 169 return returned | |
| 170 | |
| 171 | |
| 172 class TestGetTestKeys(TestCase): | |
| 173 def test_no_keys(self): | |
| 174 self.mock(swarm_get_results.time, 'sleep', lambda x: x) | |
| 175 self.requests = [ | |
| 176 ( | |
| 177 'http://host:9001/get_matching_test_cases?name=my_test', | |
| 178 {'retry_404': True}, | |
| 179 StringIO.StringIO('No matching Test Cases'), | |
| 180 ) for _ in range(run_isolated.URL_OPEN_MAX_ATTEMPTS) | |
| 181 ] | |
| 182 try: | |
| 183 swarm_get_results.get_test_keys('http://host:9001', 'my_test') | |
| 184 self.fail() | |
| 185 except swarm_get_results.Failure as e: | |
| 186 msg = ( | |
| 187 'Error: Unable to find any tests with the name, my_test, on swarm ' | |
| 188 'server') | |
| 189 self.assertEqual(msg, e.args[0]) | |
| 190 | |
| 191 def test_no_keys_on_first_attempt(self): | |
| 192 self.mock(swarm_get_results.time, 'sleep', lambda x: x) | |
| 193 keys = ['key_1', 'key_2'] | |
| 194 self.requests = [ | |
| 195 ( | |
| 196 'http://host:9001/get_matching_test_cases?name=my_test', | |
| 197 {'retry_404': True}, | |
| 198 StringIO.StringIO('No matching Test Cases'), | |
| 199 ), | |
| 200 ( | |
| 201 'http://host:9001/get_matching_test_cases?name=my_test', | |
| 202 {'retry_404': True}, | |
| 203 StringIO.StringIO(json.dumps(keys)), | |
| 204 ), | |
| 205 ] | |
| 206 actual = swarm_get_results.get_test_keys('http://host:9001', 'my_test') | |
| 207 self.assertEqual(keys, actual) | |
| 208 | |
| 209 def test_find_keys(self): | |
| 210 keys = ['key_1', 'key_2'] | |
| 211 self.requests = [ | |
| 212 ( | |
| 213 'http://host:9001/get_matching_test_cases?name=my_test', | |
| 214 {'retry_404': True}, | |
| 215 StringIO.StringIO(json.dumps(keys)), | |
| 216 ), | |
| 217 ] | |
| 218 actual = swarm_get_results.get_test_keys('http://host:9001', 'my_test') | |
| 219 self.assertEqual(keys, actual) | |
| 220 | |
| 221 | |
| 222 class TestGetSwarmResults(TestCase): | |
| 223 def test_success(self): | |
| 224 self.requests = [ | |
| 225 ( | |
| 226 'http://host:9001/get_result?r=key1', | |
| 227 {'retry_404': False, 'retry_50x': False}, | |
| 228 generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'), | |
| 229 ), | |
| 230 ] | |
| 231 expected = [gen_yielded_data(0, SWARM_OUTPUT_SUCCESS, '0, 0')] | |
| 232 actual = get_swarm_results(['key1']) | |
| 233 self.assertEqual(expected, actual) | |
| 234 | |
| 235 def test_failure(self): | |
| 236 self.requests = [ | |
| 237 ( | |
| 238 'http://host:9001/get_result?r=key1', | |
| 239 {'retry_404': False, 'retry_50x': False}, | |
| 240 generate_url_response(0, SWARM_OUTPUT_FAILURE, '0, 1'), | |
| 241 ), | |
| 242 ] | |
| 243 expected = [gen_yielded_data(0, SWARM_OUTPUT_FAILURE, '0, 1')] | |
| 244 actual = get_swarm_results(['key1']) | |
| 245 self.assertEqual(expected, actual) | |
| 246 | |
| 247 def test_no_test_output(self): | |
| 248 self.requests = [ | |
| 249 ( | |
| 250 'http://host:9001/get_result?r=key1', | |
| 251 {'retry_404': False, 'retry_50x': False}, | |
| 252 generate_url_response(0, SWARM_OUTPUT_WITH_NO_TEST_OUTPUT, '0, 0'), | |
| 253 ), | |
| 254 ] | |
| 255 expected = [gen_yielded_data(0, SWARM_OUTPUT_WITH_NO_TEST_OUTPUT, '0, 0')] | |
| 256 actual = get_swarm_results(['key1']) | |
| 257 self.assertEqual(expected, actual) | |
| 258 | |
| 259 def test_no_keys(self): | |
| 260 actual = get_swarm_results([]) | |
| 261 self.assertEqual([], actual) | |
| 262 | |
| 263 def test_url_errors(self): | |
| 264 # NOTE: get_swarm_results() hardcodes timeout=10. range(12) is because of an | |
| 265 # additional time.time() call deep in run_isolated.url_open(). | |
| 266 now = {} | |
| 267 lock = threading.Lock() | |
| 268 def get_now(): | |
| 269 t = threading.current_thread() | |
| 270 with lock: | |
| 271 return now.setdefault(t, range(12)).pop(0) | |
| 272 self.mock( | |
| 273 swarm_get_results.run_isolated.HttpService, | |
| 274 'sleep_before_retry', | |
| 275 staticmethod(lambda _x, _y: None)) | |
| 276 self.mock(swarm_get_results, 'now', get_now) | |
| 277 # The actual number of requests here depends on 'now' progressing to 10 | |
| 278 # seconds. It's called twice per loop. | |
| 279 self.requests = [ | |
| 280 ( | |
| 281 'http://host:9001/get_result?r=key1', | |
| 282 {'retry_404': False, 'retry_50x': False}, | |
| 283 None, | |
| 284 ), | |
| 285 ( | |
| 286 'http://host:9001/get_result?r=key1', | |
| 287 {'retry_404': False, 'retry_50x': False}, | |
| 288 None, | |
| 289 ), | |
| 290 ( | |
| 291 'http://host:9001/get_result?r=key1', | |
| 292 {'retry_404': False, 'retry_50x': False}, | |
| 293 None, | |
| 294 ), | |
| 295 ( | |
| 296 'http://host:9001/get_result?r=key1', | |
| 297 {'retry_404': False, 'retry_50x': False}, | |
| 298 None, | |
| 299 ), | |
| 300 ( | |
| 301 'http://host:9001/get_result?r=key1', | |
| 302 {'retry_404': False, 'retry_50x': False}, | |
| 303 None, | |
| 304 ), | |
| 305 ] | |
| 306 actual = get_swarm_results(['key1']) | |
| 307 self.assertEqual([], actual) | |
| 308 self.assertTrue(all(not v for v in now.itervalues()), now) | |
| 309 | |
| 310 def test_shard_repeated(self): | |
| 311 self.requests = [ | |
| 312 ( | |
| 313 'http://host:9001/get_result?r=key1', | |
| 314 {'retry_404': False, 'retry_50x': False}, | |
| 315 generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'), | |
| 316 ), | |
| 317 ( | |
| 318 'http://host:9001/get_result?r=key1-repeat', | |
| 319 {'retry_404': False, 'retry_50x': False}, | |
| 320 generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'), | |
| 321 ), | |
| 322 ] | |
| 323 expected = [gen_yielded_data(0, SWARM_OUTPUT_SUCCESS, '0, 0')] | |
| 324 actual = get_swarm_results(['key1', 'key1-repeat']) | |
| 325 self.assertEqual(expected, actual) | |
| 326 | |
| 327 def test_one_shard_repeated(self): | |
| 328 """Have shard 1 repeated twice, then shard 2 and 3.""" | |
| 329 self.requests = [ | |
| 330 ( | |
| 331 'http://host:9001/get_result?r=key1', | |
| 332 {'retry_404': False, 'retry_50x': False}, | |
| 333 generate_url_response(0, TEST_SHARD_OUTPUT_1, '0, 0'), | |
| 334 ), | |
| 335 ( | |
| 336 'http://host:9001/get_result?r=key1-repeat', | |
| 337 {'retry_404': False, 'retry_50x': False}, | |
| 338 generate_url_response(0, TEST_SHARD_OUTPUT_1, '0, 0'), | |
| 339 ), | |
| 340 ( | |
| 341 'http://host:9001/get_result?r=key2', | |
| 342 {'retry_404': False, 'retry_50x': False}, | |
| 343 generate_url_response(1, TEST_SHARD_OUTPUT_2, '0, 0'), | |
| 344 ), | |
| 345 ( | |
| 346 'http://host:9001/get_result?r=key3', | |
| 347 {'retry_404': False, 'retry_50x': False}, | |
| 348 generate_url_response(2, TEST_SHARD_OUTPUT_3, '0, 0'), | |
| 349 ), | |
| 350 ] | |
| 351 expected = [ | |
| 352 gen_yielded_data(0, TEST_SHARD_OUTPUT_1, '0, 0'), | |
| 353 gen_yielded_data(1, TEST_SHARD_OUTPUT_2, '0, 0'), | |
| 354 gen_yielded_data(2, TEST_SHARD_OUTPUT_3, '0, 0'), | |
| 355 ] | |
| 356 actual = get_swarm_results(['key1', 'key1-repeat', 'key2', 'key3']) | |
| 357 self.assertEqual(expected, sorted(actual)) | |
| 358 | |
| 359 | |
| 360 if __name__ == '__main__': | |
| 361 logging.basicConfig( | |
| 362 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR) | |
| 363 if '-v' in sys.argv: | |
| 364 unittest.TestCase.maxDiff = None | |
| 365 unittest.main() | |
| OLD | NEW |