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

Side by Side Diff: client/tests/swarming_test.py

Issue 1337633002: Reapply "Isolated task support in Endpoints API: client side (3/3)" and fixes" (Closed) Base URL: git@github.com:luci/luci-py.git@master
Patch Set: We did it, again Created 5 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
« client/swarming.py ('K') | « client/swarming.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 2013 The Swarming Authors. All rights reserved. 2 # Copyright 2013 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 that 3 # Use of this source code is governed under the Apache License, Version 2.0 that
4 # can be found in the LICENSE file. 4 # can be found in the LICENSE file.
5 5
6 import datetime 6 import datetime
7 import hashlib 7 import hashlib
8 import json 8 import json
9 import logging 9 import logging
10 import os 10 import os
11 import StringIO 11 import StringIO
12 import subprocess 12 import subprocess
13 import sys 13 import sys
14 import tempfile 14 import tempfile
15 import threading 15 import threading
16 import time 16 import time
17 import unittest 17 import unittest
18 18
19 # net_utils adjusts sys.path. 19 # net_utils adjusts sys.path.
20 import net_utils 20 import net_utils
21 21
22 from depot_tools import auto_stub 22 from depot_tools import auto_stub
23 23
24 import auth 24 import auth
25 import isolateserver 25 import isolateserver
26 import swarming 26 import swarming
27 import test_utils 27 import test_utils
28 28
29 from utils import file_path 29 from utils import file_path
30 from utils import logging_utils
30 from utils import tools 31 from utils import tools
31 32
32 import isolateserver_mock 33 import isolateserver_mock
33 34
34 35
35 FILE_HASH = u'1' * 40 36 FILE_HASH = u'1' * 40
36 TEST_NAME = u'unit_tests' 37 TEST_NAME = u'unit_tests'
37 38
38 39
39 OUTPUT = 'Ran stuff\n' 40 OUTPUT = 'Ran stuff\n'
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 74
74 def main(args): 75 def main(args):
75 """Bypasses swarming.main()'s exception handling. 76 """Bypasses swarming.main()'s exception handling.
76 77
77 It gets in the way when debugging test failures. 78 It gets in the way when debugging test failures.
78 """ 79 """
79 dispatcher = swarming.subcommand.CommandDispatcher('swarming') 80 dispatcher = swarming.subcommand.CommandDispatcher('swarming')
80 return dispatcher.execute(swarming.OptionParserSwarming(), args) 81 return dispatcher.execute(swarming.OptionParserSwarming(), args)
81 82
82 83
83 def gen_request_data(isolated_hash=FILE_HASH, properties=None, **kwargs): 84 def gen_request_data(properties=None, **kwargs):
84 out = { 85 out = {
85 'name': u'unit_tests', 86 'expiration_secs': 3600,
87 'name': 'unit_tests',
86 'parent_task_id': '', 88 'parent_task_id': '',
87 'priority': 101, 89 'priority': 101,
88 'properties': { 90 'properties': {
89 'commands': [ 91 'command': None,
90 [ 92 'dimensions': [
91 'python', 93 {'key': 'foo', 'value': 'bar'},
92 'run_isolated.zip', 94 {'key': 'os', 'value': 'Mac'},
93 '--isolated', isolated_hash,
94 '--isolate-server', 'https://localhost:2',
95 '--namespace', 'default-gzip',
96 '--',
97 '--some-arg',
98 '123',
99 ],
100 ], 95 ],
101 'data': [('https://localhost:1/fetch_url', 'swarm_data.zip')], 96 'env': [],
102 'dimensions': {
103 'foo': 'bar',
104 'os': 'Mac',
105 },
106 'env': {},
107 'execution_timeout_secs': 60, 97 'execution_timeout_secs': 60,
98 'extra_args': ['--some-arg', '123'],
99 'grace_period_secs': 30,
108 'idempotent': False, 100 'idempotent': False,
101 'inputs_ref': None,
109 'io_timeout_secs': 60, 102 'io_timeout_secs': 60,
110 }, 103 },
111 'scheduling_expiration_secs': 3600,
112 'tags': ['taga', 'tagb'], 104 'tags': ['taga', 'tagb'],
113 'user': 'joe@localhost', 105 'user': 'joe@localhost',
114 } 106 }
115 out.update(kwargs) 107 out.update(kwargs)
116 out['properties'].update(properties or {}) 108 out['properties'].update(properties or {})
117 return out 109 return out
118 110
119 111
120 def gen_request_response(request, **kwargs): 112 def gen_request_response(request, **kwargs):
121 # As seen in services/swarming/handlers_api.py. 113 # As seen in services/swarming/handlers_api.py.
122 out = { 114 out = {
123 'request': request.copy(), 115 'request': request.copy(),
124 'task_id': '12300', 116 'task_id': '12300',
125 } 117 }
126 out.update(kwargs) 118 out.update(kwargs)
127 return out 119 return out
128 120
129 121
130 def gen_result_response(**kwargs): 122 def gen_result_response(**kwargs):
131 out = { 123 out = {
132 "abandoned_ts": None, 124 u'bot_id': u'swarm6',
133 "bot_id": "swarm6", 125 u'completed_ts': u'2014-09-24T13:49:16.012345',
134 "completed_ts": "2014-09-24 13:49:16", 126 u'created_ts': u'2014-09-24T13:49:03.012345',
135 "created_ts": "2014-09-24 13:49:03", 127 u'duration': 0.9636809825897217,
136 "durations": [0.9636809825897217], 128 u'exit_code': 0,
137 "exit_codes": [0], 129 u'failure': False,
138 "failure": False, 130 u'internal_failure': False,
139 "id": "10100", 131 u'modified_ts': u'2014-09-24T13:49:17.012345',
140 "internal_failure": False, 132 u'name': u'heartbeat-canary-2014-09-24_13:49:01-os=Linux',
141 "modified_ts": "2014-09-24 13:49:17", 133 u'server_versions': [u'1'],
142 "name": "heartbeat-canary-2014-09-24_13:49:01-os=Linux", 134 u'started_ts': u'2014-09-24T13:49:09.012345',
143 "started_ts": "2014-09-24 13:49:09", 135 u'state': 'COMPLETED',
144 "state": 112, 136 u'tags': [u'cpu:x86', u'priority:100', u'user:joe@localhost'],
145 "try_number": 1, 137 u'task_id': u'10100',
146 "user": "unknown", 138 u'try_number': 1,
139 u'user': u'joe@localhost',
147 } 140 }
148 out.update(kwargs) 141 out.update(kwargs)
149 return out 142 return out
150 143
151 144
152 def gen_run_isolated_out_hack_log(isolate_server, namespace, isolated_hash):
153 data = {
154 'hash': isolated_hash,
155 'namespace': namespace,
156 'storage': isolate_server,
157 }
158 return (OUTPUT +
159 '[run_isolated_out_hack]%s[/run_isolated_out_hack]\n' % (
160 json.dumps(data, sort_keys=True, separators=(',',':'))))
161
162
163 # Silence pylint 'Access to a protected member _Event of a client class'. 145 # Silence pylint 'Access to a protected member _Event of a client class'.
164 class NonBlockingEvent(threading._Event): # pylint: disable=W0212 146 class NonBlockingEvent(threading._Event): # pylint: disable=W0212
165 """Just like threading.Event, but a class and ignores timeout in 'wait'. 147 """Just like threading.Event, but a class and ignores timeout in 'wait'.
166 148
167 Intended to be used as a mock for threading.Event in tests. 149 Intended to be used as a mock for threading.Event in tests.
168 """ 150 """
169 151
170 def wait(self, timeout=None): 152 def wait(self, timeout=None):
171 return super(NonBlockingEvent, self).wait(0) 153 return super(NonBlockingEvent, self).wait(0)
172 154
173 155
174 class NetTestCase(net_utils.TestCase): 156 class NetTestCase(net_utils.TestCase):
175 """Base class that defines the url_open mock.""" 157 """Base class that defines the url_open mock."""
176 def setUp(self): 158 def setUp(self):
177 super(NetTestCase, self).setUp() 159 super(NetTestCase, self).setUp()
178 self._tempdir = None 160 self._tempdir = None
179 self.mock(auth, 'ensure_logged_in', lambda _: None) 161 self.mock(auth, 'ensure_logged_in', lambda _: None)
180 self.mock(time, 'sleep', lambda _: None) 162 self.mock(time, 'sleep', lambda _: None)
181 self.mock(subprocess, 'call', lambda *_: self.fail()) 163 self.mock(subprocess, 'call', lambda *_: self.fail())
182 self.mock(threading, 'Event', NonBlockingEvent) 164 self.mock(threading, 'Event', NonBlockingEvent)
183 self.mock(sys, 'stdout', StringIO.StringIO()) 165 self.mock(sys, 'stdout', StringIO.StringIO())
184 self.mock(sys, 'stderr', StringIO.StringIO()) 166 self.mock(sys, 'stderr', StringIO.StringIO())
167 self.mock(logging_utils, 'prepare_logging', lambda *args: None)
168 self.mock(logging_utils, 'set_console_level', lambda *args: None)
185 169
186 def tearDown(self): 170 def tearDown(self):
187 try: 171 try:
188 if self._tempdir: 172 if self._tempdir:
189 file_path.rmtree(self._tempdir) 173 file_path.rmtree(self._tempdir)
190 if not self.has_failed(): 174 if not self.has_failed():
191 self._check_output('', '') 175 self._check_output('', '')
192 finally: 176 finally:
193 super(NetTestCase, self).tearDown() 177 super(NetTestCase, self).tearDown()
194 178
(...skipping 30 matching lines...) Expand all
225 finally: 209 finally:
226 super(TestIsolated, self).tearDown() 210 super(TestIsolated, self).tearDown()
227 211
228 @property 212 @property
229 def server(self): 213 def server(self):
230 """Creates the Isolate Server mock on first reference.""" 214 """Creates the Isolate Server mock on first reference."""
231 if not self._server: 215 if not self._server:
232 self._server = isolateserver_mock.MockIsolateServer() 216 self._server = isolateserver_mock.MockIsolateServer()
233 return self._server 217 return self._server
234 218
235 # Test isolated related code.
236 def test_isolated_get_data(self):
237 data = swarming.isolated_get_data(self.server.url)
238 self.assertEqual(['default'], self.server.contents.keys())
239 self.assertEqual(1, len(self.server.contents['default']))
240 h = self.server.contents['default'].popitem()[0]
241 expected = [
242 (
243 '%s/content-gs/retrieve/default/%s' % (self.server.url, h),
244 'swarm_data.zip',
245 ),
246 ]
247 self.assertEqual(expected, data)
248
249 def test_isolated_get_run_commands(self):
250 actual = swarming.isolated_get_run_commands(
251 'http://foo.invalid', 'default', '1'*40, ['fo', 'ba'], True)
252 expected = [
253 'python',
254 'run_isolated.zip',
255 '--isolated', '1111111111111111111111111111111111111111',
256 '--isolate-server', 'http://foo.invalid',
257 '--namespace', 'default',
258 '--verbose',
259 '--',
260 'fo',
261 'ba',
262 ]
263 self.assertEqual(expected, actual)
264
265 219
266 class TestSwarmingTrigger(NetTestCase): 220 class TestSwarmingTrigger(NetTestCase):
267 def test_trigger_task_shards_2_shards(self): 221 def test_trigger_task_shards_2_shards(self):
268 task_request = swarming.TaskRequest( 222 task_request = swarming.NewTaskRequest(
269 command=['a', 'b'], 223 expiration_secs=60*60,
270 data=[],
271 dimensions={'foo': 'bar', 'os': 'Mac'},
272 env={},
273 expiration=60*60,
274 hard_timeout=60,
275 idempotent=False,
276 io_timeout=60,
277 name=TEST_NAME, 224 name=TEST_NAME,
225 parent_task_id=None,
278 priority=101, 226 priority=101,
227 properties=swarming.TaskProperties(
228 command=['a', 'b'],
229 dimensions={'foo': 'bar', 'os': 'Mac'},
230 env={},
231 execution_timeout_secs=60,
232 extra_args=[],
233 grace_period_secs=30,
234 idempotent=False,
235 inputs_ref=None,
236 io_timeout_secs=60),
279 tags=['taga', 'tagb'], 237 tags=['taga', 'tagb'],
280 user='joe@localhost', 238 user='joe@localhost')
281 verbose=False)
282 239
283 request_1 = swarming.task_request_to_raw_request(task_request) 240 request_1 = swarming.task_request_to_raw_request(task_request)
284 request_1['name'] = u'unit_tests:0:2' 241 request_1['name'] = u'unit_tests:0:2'
285 request_1['properties']['env'] = { 242 request_1['properties']['env'] = [
286 'GTEST_SHARD_INDEX': '0', 'GTEST_TOTAL_SHARDS': '2', 243 {'key': 'GTEST_SHARD_INDEX', 'value': '0'},
287 } 244 {'key': 'GTEST_TOTAL_SHARDS', 'value': '2'},
245 ]
288 result_1 = gen_request_response(request_1) 246 result_1 = gen_request_response(request_1)
289 247
290 request_2 = swarming.task_request_to_raw_request(task_request) 248 request_2 = swarming.task_request_to_raw_request(task_request)
291 request_2['name'] = u'unit_tests:1:2' 249 request_2['name'] = u'unit_tests:1:2'
292 request_2['properties']['env'] = { 250 request_2['properties']['env'] = [
293 'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2', 251 {'key': 'GTEST_SHARD_INDEX', 'value': '1'},
294 } 252 {'key': 'GTEST_TOTAL_SHARDS', 'value': '2'},
253 ]
295 result_2 = gen_request_response(request_2, task_id='12400') 254 result_2 = gen_request_response(request_2, task_id='12400')
296 self.expected_requests( 255 self.expected_requests(
297 [ 256 [
298 ( 257 (
299 'https://localhost:1/swarming/api/v1/client/handshake', 258 'https://localhost:1/_ah/api/swarming/v1/tasks/new',
300 {'data': {}, 'headers': {'X-XSRF-Token-Request': '1'}}, 259 {'data': request_1},
301 {'server_version': 'v1', 'xsrf_token': 'Token'},
302 ),
303 (
304 'https://localhost:1/swarming/api/v1/client/request',
305 {'data': request_1, 'headers': {'X-XSRF-Token': 'Token'}},
306 result_1, 260 result_1,
307 ), 261 ),
308 ( 262 (
309 'https://localhost:1/swarming/api/v1/client/request', 263 'https://localhost:1/_ah/api/swarming/v1/tasks/new',
310 {'data': request_2, 'headers': {'X-XSRF-Token': 'Token'}}, 264 {'data': request_2},
311 result_2, 265 result_2,
312 ), 266 ),
313 ]) 267 ])
314 268
315 tasks = swarming.trigger_task_shards( 269 tasks = swarming.trigger_task_shards(
316 swarming='https://localhost:1', 270 swarming='https://localhost:1',
317 task_request=task_request, 271 task_request=task_request,
318 shards=2) 272 shards=2)
319 expected = { 273 expected = {
320 u'unit_tests:0:2': { 274 u'unit_tests:0:2': {
321 'shard_index': 0, 275 'shard_index': 0,
322 'task_id': '12300', 276 'task_id': '12300',
323 'view_url': 'https://localhost:1/user/task/12300', 277 'view_url': 'https://localhost:1/user/task/12300',
324 }, 278 },
325 u'unit_tests:1:2': { 279 u'unit_tests:1:2': {
326 'shard_index': 1, 280 'shard_index': 1,
327 'task_id': '12400', 281 'task_id': '12400',
328 'view_url': 'https://localhost:1/user/task/12400', 282 'view_url': 'https://localhost:1/user/task/12400',
329 }, 283 },
330 } 284 }
331 self.assertEqual(expected, tasks) 285 self.assertEqual(expected, tasks)
332 286
333 def test_trigger_task_shards_priority_override(self): 287 def test_trigger_task_shards_priority_override(self):
334 task_request = swarming.TaskRequest( 288 task_request = swarming.NewTaskRequest(
335 command=['a', 'b'], 289 expiration_secs=60*60,
336 data=[['https://foo.invalid/bar', 'bar.zip']],
337 dimensions={'foo': 'bar', 'os': 'Mac'},
338 env={},
339 expiration=60*60,
340 hard_timeout=60,
341 idempotent=False,
342 io_timeout=60,
343 name=TEST_NAME, 290 name=TEST_NAME,
291 parent_task_id='123',
344 priority=101, 292 priority=101,
293 properties=swarming.TaskProperties(
294 command=['a', 'b'],
295 dimensions={'foo': 'bar', 'os': 'Mac'},
296 env={},
297 execution_timeout_secs=60,
298 extra_args=[],
299 grace_period_secs=30,
300 idempotent=False,
301 inputs_ref=None,
302 io_timeout_secs=60),
345 tags=['taga', 'tagb'], 303 tags=['taga', 'tagb'],
346 user='joe@localhost', 304 user='joe@localhost')
347 verbose=False)
348 305
349 os.environ['SWARMING_TASK_ID'] = '123' 306 request = swarming.task_request_to_raw_request(task_request)
350 try:
351 request = swarming.task_request_to_raw_request(task_request)
352 finally:
353 os.environ.pop('SWARMING_TASK_ID')
354 self.assertEqual('123', request['parent_task_id']) 307 self.assertEqual('123', request['parent_task_id'])
355 308
356 result = gen_request_response(request) 309 result = gen_request_response(request)
357 result['request']['priority'] = 200 310 result['request']['priority'] = 200
358 self.expected_requests( 311 self.expected_requests(
359 [ 312 [
360 ( 313 (
361 'https://localhost:1/swarming/api/v1/client/handshake', 314 'https://localhost:1/_ah/api/swarming/v1/tasks/new',
362 {'data': {}, 'headers': {'X-XSRF-Token-Request': '1'}}, 315 {'data': request},
363 {'server_version': 'v1', 'xsrf_token': 'Token'},
364 ),
365 (
366 'https://localhost:1/swarming/api/v1/client/request',
367 {'data': request, 'headers': {'X-XSRF-Token': 'Token'}},
368 result, 316 result,
369 ), 317 ),
370 ]) 318 ])
371 319
372 os.environ['SWARMING_TASK_ID'] = '123' 320 os.environ['SWARMING_TASK_ID'] = '123'
373 try: 321 try:
374 tasks = swarming.trigger_task_shards( 322 tasks = swarming.trigger_task_shards(
375 swarming='https://localhost:1', 323 swarming='https://localhost:1',
376 shards=1, 324 shards=1,
377 task_request=task_request) 325 task_request=task_request)
378 finally: 326 finally:
379 os.environ.pop('SWARMING_TASK_ID') 327 os.environ.pop('SWARMING_TASK_ID')
380 expected = { 328 expected = {
381 u'unit_tests': { 329 u'unit_tests': {
382 'shard_index': 0, 330 'shard_index': 0,
383 'task_id': '12300', 331 'task_id': '12300',
384 'view_url': 'https://localhost:1/user/task/12300', 332 'view_url': 'https://localhost:1/user/task/12300',
385 } 333 }
386 } 334 }
387 self.assertEqual(expected, tasks) 335 self.assertEqual(expected, tasks)
388 self._check_output('', 'Priority was reset to 200\n') 336 self._check_output('', 'Priority was reset to 200\n')
389 337
390 def test_isolated_to_hash(self):
391 calls = []
392 self.mock(subprocess, 'call', lambda *c: calls.append(c))
393 content = '{}'
394 expected_hash = hashlib.sha1(content).hexdigest()
395 handle, isolated = tempfile.mkstemp(
396 prefix=u'swarming_test_', suffix=u'.isolated')
397 os.close(handle)
398 try:
399 with open(isolated, 'w') as f:
400 f.write(content)
401 hash_value, is_file = swarming.isolated_to_hash(
402 'https://localhost:2', 'default-gzip', isolated, hashlib.sha1, False)
403 finally:
404 os.remove(isolated)
405 self.assertEqual(expected_hash, hash_value)
406 self.assertEqual(True, is_file)
407 expected_calls = [
408 (
409 [
410 sys.executable,
411 os.path.join(swarming.ROOT_DIR, 'isolate.py'),
412 'archive',
413 '--isolate-server', 'https://localhost:2',
414 '--namespace', 'default-gzip',
415 '--isolated',
416 isolated,
417 ],
418 False,
419 ),
420 ]
421 self.assertEqual(expected_calls, calls)
422 self._check_output('Archiving: %s\n' % isolated, '')
423
424 338
425 class TestSwarmingCollection(NetTestCase): 339 class TestSwarmingCollection(NetTestCase):
426 def test_success(self): 340 def test_success(self):
427 self.expected_requests( 341 self.expected_requests(
428 [ 342 [
429 ( 343 (
430 'https://host:9001/swarming/api/v1/client/task/10100', 344 'https://host:9001/_ah/api/swarming/v1/task/10100/result',
431 {'retry_50x': False}, 345 {'retry_50x': False},
432 gen_result_response(), 346 gen_result_response(),
433 ), 347 ),
434 ( 348 (
435 'https://host:9001/swarming/api/v1/client/task/10100/output/all', 349 'https://host:9001/_ah/api/swarming/v1/task/10100/stdout',
436 {}, 350 {},
437 {'outputs': [OUTPUT]}, 351 {'output': OUTPUT},
438 ), 352 ),
439 ]) 353 ])
440 expected = [gen_yielded_data(0, outputs=[OUTPUT])] 354 expected = [gen_yielded_data(0, output=OUTPUT)]
441 actual = get_results(['10100']) 355 self.assertEqual(expected, get_results(['10100']))
442 self.assertEqual(expected, actual)
443 356
444 def test_failure(self): 357 def test_failure(self):
445 self.expected_requests( 358 self.expected_requests(
446 [ 359 [
447 ( 360 (
448 'https://host:9001/swarming/api/v1/client/task/10100', 361 'https://host:9001/_ah/api/swarming/v1/task/10100/result',
449 {'retry_50x': False}, 362 {'retry_50x': False},
450 gen_result_response(exit_codes=[0, 1]), 363 gen_result_response(exit_code=1),
451 ), 364 ),
452 ( 365 (
453 'https://host:9001/swarming/api/v1/client/task/10100/output/all', 366 'https://host:9001/_ah/api/swarming/v1/task/10100/stdout',
454 {}, 367 {},
455 {'outputs': [OUTPUT]}, 368 {'output': OUTPUT},
456 ), 369 ),
457 ]) 370 ])
458 expected = [gen_yielded_data(0, outputs=[OUTPUT], exit_codes=[0, 1])] 371 expected = [gen_yielded_data(0, output=OUTPUT, exit_code=1)]
459 actual = get_results(['10100']) 372 self.assertEqual(expected, get_results(['10100']))
460 self.assertEqual(expected, actual)
461 373
462 def test_no_ids(self): 374 def test_no_ids(self):
463 actual = get_results([]) 375 actual = get_results([])
464 self.assertEqual([], actual) 376 self.assertEqual([], actual)
465 377
466 def test_url_errors(self): 378 def test_url_errors(self):
467 self.mock(logging, 'error', lambda *_, **__: None) 379 self.mock(logging, 'error', lambda *_, **__: None)
468 # NOTE: get_results() hardcodes timeout=10. 380 # NOTE: get_results() hardcodes timeout=10.
469 now = {} 381 now = {}
470 lock = threading.Lock() 382 lock = threading.Lock()
471 def get_now(): 383 def get_now():
472 t = threading.current_thread() 384 t = threading.current_thread()
473 with lock: 385 with lock:
474 return now.setdefault(t, range(10)).pop(0) 386 return now.setdefault(t, range(10)).pop(0)
475 self.mock(swarming.net, 'sleep_before_retry', lambda _x, _y: None) 387 self.mock(swarming.net, 'sleep_before_retry', lambda _x, _y: None)
476 self.mock(swarming, 'now', get_now) 388 self.mock(swarming, 'now', get_now)
477 # The actual number of requests here depends on 'now' progressing to 10 389 # The actual number of requests here depends on 'now' progressing to 10
478 # seconds. It's called once per loop. Loop makes 9 iterations. 390 # seconds. It's called once per loop. Loop makes 9 iterations.
479 self.expected_requests( 391 self.expected_requests(
480 9 * [ 392 9 * [
481 ( 393 (
482 'https://host:9001/swarming/api/v1/client/task/10100', 394 'https://host:9001/_ah/api/swarming/v1/task/10100/result',
483 {'retry_50x': False}, 395 {'retry_50x': False},
484 None, 396 None,
485 ) 397 )
486 ]) 398 ])
487 actual = get_results(['10100']) 399 actual = get_results(['10100'])
488 self.assertEqual([], actual) 400 self.assertEqual([], actual)
489 self.assertTrue(all(not v for v in now.itervalues()), now) 401 self.assertTrue(all(not v for v in now.itervalues()), now)
490 402
491 def test_many_shards(self): 403 def test_many_shards(self):
492 self.expected_requests( 404 self.expected_requests(
493 [ 405 [
494 ( 406 (
495 'https://host:9001/swarming/api/v1/client/task/10100', 407 'https://host:9001/_ah/api/swarming/v1/task/10100/result',
496 {'retry_50x': False}, 408 {'retry_50x': False},
497 gen_result_response(), 409 gen_result_response(),
498 ), 410 ),
499 ( 411 (
500 'https://host:9001/swarming/api/v1/client/task/10100/output/all', 412 'https://host:9001/_ah/api/swarming/v1/task/10100/stdout',
501 {}, 413 {},
502 {'outputs': [SHARD_OUTPUT_1]}, 414 {'output': SHARD_OUTPUT_1},
503 ), 415 ),
504 ( 416 (
505 'https://host:9001/swarming/api/v1/client/task/10200', 417 'https://host:9001/_ah/api/swarming/v1/task/10200/result',
506 {'retry_50x': False}, 418 {'retry_50x': False},
507 gen_result_response(), 419 gen_result_response(),
508 ), 420 ),
509 ( 421 (
510 'https://host:9001/swarming/api/v1/client/task/10200/output/all', 422 'https://host:9001/_ah/api/swarming/v1/task/10200/stdout',
511 {}, 423 {},
512 {'outputs': [SHARD_OUTPUT_2]}, 424 {'output': SHARD_OUTPUT_2},
513 ), 425 ),
514 ( 426 (
515 'https://host:9001/swarming/api/v1/client/task/10300', 427 'https://host:9001/_ah/api/swarming/v1/task/10300/result',
516 {'retry_50x': False}, 428 {'retry_50x': False},
517 gen_result_response(), 429 gen_result_response(),
518 ), 430 ),
519 ( 431 (
520 'https://host:9001/swarming/api/v1/client/task/10300/output/all', 432 'https://host:9001/_ah/api/swarming/v1/task/10300/stdout',
521 {}, 433 {},
522 {'outputs': [SHARD_OUTPUT_3]}, 434 {'output': SHARD_OUTPUT_3},
523 ), 435 ),
524 ]) 436 ])
525 expected = [ 437 expected = [
526 gen_yielded_data(0, outputs=[SHARD_OUTPUT_1]), 438 gen_yielded_data(0, output=SHARD_OUTPUT_1),
527 gen_yielded_data(1, outputs=[SHARD_OUTPUT_2]), 439 gen_yielded_data(1, output=SHARD_OUTPUT_2),
528 gen_yielded_data(2, outputs=[SHARD_OUTPUT_3]), 440 gen_yielded_data(2, output=SHARD_OUTPUT_3),
529 ] 441 ]
530 actual = get_results(['10100', '10200', '10300']) 442 actual = get_results(['10100', '10200', '10300'])
531 self.assertEqual(expected, sorted(actual)) 443 self.assertEqual(expected, sorted(actual))
532 444
533 def test_output_collector_called(self): 445 def test_output_collector_called(self):
534 # Three shards, one failed. All results are passed to output collector. 446 # Three shards, one failed. All results are passed to output collector.
535 self.expected_requests( 447 self.expected_requests(
536 [ 448 [
537 ( 449 (
538 'https://host:9001/swarming/api/v1/client/task/10100', 450 'https://host:9001/_ah/api/swarming/v1/task/10100/result',
539 {'retry_50x': False}, 451 {'retry_50x': False},
540 gen_result_response(), 452 gen_result_response(),
541 ), 453 ),
542 ( 454 (
543 'https://host:9001/swarming/api/v1/client/task/10100/output/all', 455 'https://host:9001/_ah/api/swarming/v1/task/10100/stdout',
544 {}, 456 {},
545 {'outputs': [SHARD_OUTPUT_1]}, 457 {'output': SHARD_OUTPUT_1},
546 ), 458 ),
547 ( 459 (
548 'https://host:9001/swarming/api/v1/client/task/10200', 460 'https://host:9001/_ah/api/swarming/v1/task/10200/result',
549 {'retry_50x': False}, 461 {'retry_50x': False},
550 gen_result_response(), 462 gen_result_response(),
551 ), 463 ),
552 ( 464 (
553 'https://host:9001/swarming/api/v1/client/task/10200/output/all', 465 'https://host:9001/_ah/api/swarming/v1/task/10200/stdout',
554 {}, 466 {},
555 {'outputs': [SHARD_OUTPUT_2]}, 467 {'output': SHARD_OUTPUT_2},
556 ), 468 ),
557 ( 469 (
558 'https://host:9001/swarming/api/v1/client/task/10300', 470 'https://host:9001/_ah/api/swarming/v1/task/10300/result',
559 {'retry_50x': False}, 471 {'retry_50x': False},
560 gen_result_response(exit_codes=[0, 1]), 472 gen_result_response(exit_code=1),
561 ), 473 ),
562 ( 474 (
563 'https://host:9001/swarming/api/v1/client/task/10300/output/all', 475 'https://host:9001/_ah/api/swarming/v1/task/10300/stdout',
564 {}, 476 {},
565 {'outputs': [SHARD_OUTPUT_3]}, 477 {'output': SHARD_OUTPUT_3},
566 ), 478 ),
567 ]) 479 ])
568 480
569 class FakeOutputCollector(object): 481 class FakeOutputCollector(object):
570 def __init__(self): 482 def __init__(self):
571 self.results = [] 483 self.results = []
572 self._lock = threading.Lock() 484 self._lock = threading.Lock()
573 485
574 def process_shard_result(self, index, result): 486 def process_shard_result(self, index, result):
575 with self._lock: 487 with self._lock:
576 self.results.append((index, result)) 488 self.results.append((index, result))
577 489
578 output_collector = FakeOutputCollector() 490 output_collector = FakeOutputCollector()
579 get_results(['10100', '10200', '10300'], output_collector) 491 get_results(['10100', '10200', '10300'], output_collector)
580 492
581 expected = [ 493 expected = [
582 gen_yielded_data(0, outputs=[SHARD_OUTPUT_1]), 494 gen_yielded_data(0, output=SHARD_OUTPUT_1),
583 gen_yielded_data(1, outputs=[SHARD_OUTPUT_2]), 495 gen_yielded_data(1, output=SHARD_OUTPUT_2),
584 gen_yielded_data(2, outputs=[SHARD_OUTPUT_3], exit_codes=[0, 1]), 496 gen_yielded_data(2, output=SHARD_OUTPUT_3, exit_code=1),
585 ] 497 ]
586 self.assertEqual(sorted(expected), sorted(output_collector.results)) 498 self.assertEqual(sorted(expected), sorted(output_collector.results))
587 499
588 def test_collect_nothing(self): 500 def test_collect_nothing(self):
589 self.mock(swarming, 'yield_results', lambda *_: []) 501 self.mock(swarming, 'yield_results', lambda *_: [])
590 self.assertEqual( 502 self.assertEqual(
591 1, collect('https://localhost:1', 'name', ['10100', '10200'])) 503 1, collect('https://localhost:1', 'name', ['10100', '10200']))
592 self._check_output('', 'Results from some shards are missing: 0, 1\n') 504 self._check_output('', 'Results from some shards are missing: 0, 1\n')
593 505
594 def test_collect_success(self): 506 def test_collect_success(self):
595 data = gen_result_response(outputs=['Foo']) 507 data = gen_result_response(output='Foo')
596 self.mock(swarming, 'yield_results', lambda *_: [(0, data)]) 508 self.mock(swarming, 'yield_results', lambda *_: [(0, data)])
597 self.assertEqual(0, collect('https://localhost:1', 'name', ['10100'])) 509 self.assertEqual(0, collect('https://localhost:1', 'name', ['10100']))
598 expected = '\n'.join(( 510 expected = u'\n'.join((
599 '+---------------------------------------------------------------------+', 511 '+---------------------------------------------------------------------+',
600 '| Shard 0 https://localhost:1/user/task/10100 |', 512 '| Shard 0 https://localhost:1/user/task/10100 |',
601 '+---------------------------------------------------------------------+', 513 '+---------------------------------------------------------------------+',
602 'Foo', 514 'Foo',
603 '+---------------------------------------------------------------------+', 515 '+---------------------------------------------------------------------+',
604 '| End of shard 0 Pending: 6.0s Duration: 1.0s Bot: swarm6 Exit: 0 |', 516 '| End of shard 0 Pending: 6.0s Duration: 1.0s Bot: swarm6 Exit: 0 |',
605 '+---------------------------------------------------------------------+', 517 '+---------------------------------------------------------------------+',
606 'Total duration: 1.0s', 518 'Total duration: 1.0s',
607 '')) 519 ''))
608 self._check_output(expected, '') 520 self._check_output(expected, '')
609 521
610 def test_collect_fail(self): 522 def test_collect_fail(self):
611 data = gen_result_response(outputs=['Foo'], exit_codes=[-9]) 523 data = gen_result_response(output='Foo', exit_code=-9)
612 data['outputs'] = ['Foo'] 524 data['output'] = 'Foo'
613 self.mock(swarming, 'yield_results', lambda *_: [(0, data)]) 525 self.mock(swarming, 'yield_results', lambda *_: [(0, data)])
614 self.assertEqual(-9, collect('https://localhost:1', 'name', ['10100'])) 526 self.assertEqual(-9, collect('https://localhost:1', 'name', ['10100']))
615 expected = '\n'.join(( 527 expected = u'\n'.join((
616 '+----------------------------------------------------------------------' 528 '+----------------------------------------------------------------------'
617 '+', 529 '+',
618 '| Shard 0 https://localhost:1/user/task/10100 ' 530 '| Shard 0 https://localhost:1/user/task/10100 '
619 '|', 531 '|',
620 '+----------------------------------------------------------------------' 532 '+----------------------------------------------------------------------'
621 '+', 533 '+',
622 'Foo', 534 'Foo',
623 '+----------------------------------------------------------------------' 535 '+----------------------------------------------------------------------'
624 '+', 536 '+',
625 '| End of shard 0 Pending: 6.0s Duration: 1.0s Bot: swarm6 Exit: -9 ' 537 '| End of shard 0 Pending: 6.0s Duration: 1.0s Bot: swarm6 Exit: -9 '
626 '|', 538 '|',
627 '+----------------------------------------------------------------------' 539 '+----------------------------------------------------------------------'
628 '+', 540 '+',
629 'Total duration: 1.0s', 541 'Total duration: 1.0s',
630 '')) 542 ''))
631 self._check_output(expected, '') 543 self._check_output(expected, '')
632 544
633 def test_collect_one_missing(self): 545 def test_collect_one_missing(self):
634 data = gen_result_response(outputs=['Foo']) 546 data = gen_result_response(output='Foo')
635 data['outputs'] = ['Foo'] 547 data['output'] = 'Foo'
636 self.mock(swarming, 'yield_results', lambda *_: [(0, data)]) 548 self.mock(swarming, 'yield_results', lambda *_: [(0, data)])
637 self.assertEqual( 549 self.assertEqual(
638 1, collect('https://localhost:1', 'name', ['10100', '10200'])) 550 1, collect('https://localhost:1', 'name', ['10100', '10200']))
639 expected = '\n'.join(( 551 expected = u'\n'.join((
640 '+---------------------------------------------------------------------+', 552 '+---------------------------------------------------------------------+',
641 '| Shard 0 https://localhost:1/user/task/10100 |', 553 '| Shard 0 https://localhost:1/user/task/10100 |',
642 '+---------------------------------------------------------------------+', 554 '+---------------------------------------------------------------------+',
643 'Foo', 555 'Foo',
644 '+---------------------------------------------------------------------+', 556 '+---------------------------------------------------------------------+',
645 '| End of shard 0 Pending: 6.0s Duration: 1.0s Bot: swarm6 Exit: 0 |', 557 '| End of shard 0 Pending: 6.0s Duration: 1.0s Bot: swarm6 Exit: 0 |',
646 '+---------------------------------------------------------------------+', 558 '+---------------------------------------------------------------------+',
647 '', 559 '',
648 'Total duration: 1.0s', 560 'Total duration: 1.0s',
649 '')) 561 ''))
650 self._check_output(expected, 'Results from some shards are missing: 1\n') 562 self._check_output(expected, 'Results from some shards are missing: 1\n')
651 563
652 def test_collect_multi(self): 564 def test_collect_multi(self):
653 actual_calls = [] 565 actual_calls = []
654 self.mock( 566 def fetch_isolated(isolated_hash, storage, cache, outdir, require_command):
655 isolateserver, 'fetch_isolated', 567 self.assertIs(storage.__class__, isolateserver.Storage)
656 lambda *args: actual_calls.append(args)) 568 self.assertIs(cache.__class__, isolateserver.MemoryCache)
657 shards_output = [ 569 # Ensure storage is pointing to required location.
658 gen_run_isolated_out_hack_log('https://server', 'namespace', 'hash1'), 570 self.assertEqual('https://localhost:2', storage.location)
659 gen_run_isolated_out_hack_log('https://server', 'namespace', 'hash2'), 571 self.assertEqual('default', storage.namespace)
660 OUTPUT, 572 actual_calls.append((isolated_hash, outdir, require_command))
661 ] 573 self.mock(isolateserver, 'fetch_isolated', fetch_isolated)
662 574
663 collector = swarming.TaskOutputCollector( 575 collector = swarming.TaskOutputCollector(self.tempdir, 'name', 2)
664 self.tempdir, 'name', len(shards_output)) 576 for index in xrange(2):
665 for index, shard_output in enumerate(shards_output):
666 collector.process_shard_result( 577 collector.process_shard_result(
667 index, gen_result_response(outputs=[shard_output])) 578 index,
579 gen_result_response(
580 outputs_ref={
581 'isolated': str(index) * 40,
582 'isolatedserver': 'https://localhost:2',
583 'namespace': 'default',
584 }))
668 summary = collector.finalize() 585 summary = collector.finalize()
669 586
670 expected_calls = [ 587 expected_calls = [
671 ('hash1', None, None, os.path.join(self.tempdir, '0'), False), 588 ('0'*40, os.path.join(self.tempdir, '0'), False),
672 ('hash2', None, None, os.path.join(self.tempdir, '1'), False), 589 ('1'*40, os.path.join(self.tempdir, '1'), False),
673 ] 590 ]
674 self.assertEqual(len(expected_calls), len(actual_calls)) 591 self.assertEqual(expected_calls, actual_calls)
675 storage_instances = set()
676 for expected, used in zip(expected_calls, actual_calls):
677 isolated_hash, storage, cache, outdir, require_command = used
678 storage_instances.add(storage)
679 # Compare everything but |storage| and |cache| (use None in their place).
680 self.assertEqual(
681 expected, (isolated_hash, None, None, outdir, require_command))
682 # Ensure cache is set.
683 self.assertTrue(cache)
684
685 # Only one instance of Storage should be used.
686 self.assertEqual(1, len(storage_instances))
687
688 # Ensure storage is pointing to required location.
689 storage = storage_instances.pop()
690 self.assertEqual('https://server', storage.location)
691 self.assertEqual('namespace', storage.namespace)
692 592
693 # Ensure collected summary is correct. 593 # Ensure collected summary is correct.
694 isolated_outs = [ 594 outputs_refs = [
695 { 595 {
696 'hash': 'hash1', 596 'isolated': '0'*40,
697 'namespace': 'namespace', 597 'isolatedserver': 'https://localhost:2',
698 'server': 'https://server', 598 'namespace': 'default',
699 'view_url': 'https://server/browse?namespace=namespace&hash=hash1', 599 'view_url':
600 'https://localhost:2/browse?namespace=default&hash=' + '0'*40,
700 }, 601 },
701 { 602 {
702 'hash': 'hash2', 603 'isolated': '1'*40,
703 'namespace': 'namespace', 604 'isolatedserver': 'https://localhost:2',
704 'server': 'https://server', 605 'namespace': 'default',
705 'view_url': 'https://server/browse?namespace=namespace&hash=hash2', 606 'view_url':
607 'https://localhost:2/browse?namespace=default&hash=' + '1'*40,
706 }, 608 },
707 None,
708 ] 609 ]
709 expected = { 610 expected = {
710 'shards': [ 611 'shards': [gen_result_response(outputs_ref=o) for o in outputs_refs],
711 gen_result_response(isolated_out=isolated_out, outputs=[shard_output])
712 for index, (isolated_out, shard_output) in
713 enumerate(zip(isolated_outs, shards_output))
714 ],
715 } 612 }
716 self.assertEqual(expected, summary) 613 self.assertEqual(expected, summary)
717 614
718 # Ensure summary dumped to a file is correct as well. 615 # Ensure summary dumped to a file is correct as well.
719 with open(os.path.join(self.tempdir, 'summary.json'), 'r') as f: 616 with open(os.path.join(self.tempdir, 'summary.json'), 'r') as f:
720 summary_dump = json.load(f) 617 summary_dump = json.load(f)
721 self.assertEqual(expected, summary_dump) 618 self.assertEqual(expected, summary_dump)
722 619
723 def test_ensures_same_server(self): 620 def test_ensures_same_server(self):
724 self.mock(logging, 'error', lambda *_: None) 621 self.mock(logging, 'error', lambda *_: None)
725 # Two shard results, attempt to use different servers. 622 # Two shard results, attempt to use different servers.
726 actual_calls = [] 623 actual_calls = []
727 self.mock( 624 self.mock(
728 isolateserver, 'fetch_isolated', 625 isolateserver, 'fetch_isolated',
729 lambda *args: actual_calls.append(args)) 626 lambda *args: actual_calls.append(args))
730 data = [ 627 data = [
731 gen_result_response( 628 gen_result_response(
732 outputs=[ 629 outputs_ref={
733 gen_run_isolated_out_hack_log('https://server1', 'namespace', 'hash1') 630 'isolatedserver': 'https://server1',
734 ]), 631 'namespace': 'namespace',
632 'isolated':'hash1',
633 }),
735 gen_result_response( 634 gen_result_response(
736 outputs=[ 635 outputs_ref={
737 gen_run_isolated_out_hack_log('https://server2', 'namespace', 'hash2') 636 'isolatedserver': 'https://server2',
738 ]), 637 'namespace': 'namespace',
638 'isolated':'hash1',
639 }),
739 ] 640 ]
740 641
741 # Feed them to collector. 642 # Feed them to collector.
742 collector = swarming.TaskOutputCollector(self.tempdir, 'task/name', 2) 643 collector = swarming.TaskOutputCollector(self.tempdir, 'task/name', 2)
743 for index, result in enumerate(data): 644 for index, result in enumerate(data):
744 collector.process_shard_result(index, result) 645 collector.process_shard_result(index, result)
745 collector.finalize() 646 collector.finalize()
746 647
747 # Only first fetch is made, second one is ignored. 648 # Only first fetch is made, second one is ignored.
748 self.assertEqual(1, len(actual_calls)) 649 self.assertEqual(1, len(actual_calls))
749 isolated_hash, storage, _, outdir, _ = actual_calls[0] 650 isolated_hash, storage, _, outdir, _ = actual_calls[0]
750 self.assertEqual( 651 self.assertEqual(
751 ('hash1', os.path.join(self.tempdir, '0')), 652 ('hash1', os.path.join(self.tempdir, '0')),
752 (isolated_hash, outdir)) 653 (isolated_hash, outdir))
753 self.assertEqual('https://server1', storage.location) 654 self.assertEqual('https://server1', storage.location)
754 655
755 def test_extract_output_files_location_ok(self):
756 task_log = '\n'.join((
757 'some log',
758 'some more log',
759 gen_run_isolated_out_hack_log('https://fake', 'default', '12345'),
760 'more log',
761 ))
762 self.assertEqual(
763 {'hash': '12345',
764 'namespace': 'default',
765 'server': 'https://fake',
766 'view_url': 'https://fake/browse?namespace=default&hash=12345'},
767 swarming.extract_output_files_location(task_log))
768
769 def test_extract_output_files_location_empty(self):
770 task_log = '\n'.join((
771 'some log',
772 'some more log',
773 '[run_isolated_out_hack]',
774 '[/run_isolated_out_hack]',
775 ))
776 self.assertEqual(
777 None,
778 swarming.extract_output_files_location(task_log))
779
780 def test_extract_output_files_location_missing(self):
781 task_log = '\n'.join((
782 'some log',
783 'some more log',
784 'more log',
785 ))
786 self.assertEqual(
787 None,
788 swarming.extract_output_files_location(task_log))
789
790 def test_extract_output_files_location_corrupt(self):
791 task_log = '\n'.join((
792 'some log',
793 'some more log',
794 '[run_isolated_out_hack]',
795 '{"hash": "12345","namespace":}',
796 '[/run_isolated_out_hack]',
797 'more log',
798 ))
799 self.assertEqual(
800 None,
801 swarming.extract_output_files_location(task_log))
802
803 def test_extract_output_files_location_not_url(self):
804 task_log = '\n'.join((
805 'some log',
806 'some more log',
807 gen_run_isolated_out_hack_log('/local/path', 'default', '12345'),
808 'more log',
809 ))
810 self.assertEqual(
811 None,
812 swarming.extract_output_files_location(task_log))
813
814 656
815 class TestMain(NetTestCase): 657 class TestMain(NetTestCase):
816 # Tests calling main(). 658 # Tests calling main().
817 def test_bot_delete(self): 659 def test_bot_delete(self):
818 self.expected_requests( 660 self.expected_requests(
819 [ 661 [
820 ( 662 (
821 'https://localhost:1/swarming/api/v1/client/bot/foo', 663 'https://localhost:1/_ah/api/swarming/v1/bot/foo',
822 {'method': 'DELETE'}, 664 {'method': 'DELETE'},
823 {}, 665 {},
824 ), 666 ),
825 ]) 667 ])
826 ret = main( 668 ret = main(
827 ['bot_delete', '--swarming', 'https://localhost:1', 'foo', '--force']) 669 ['bot_delete', '--swarming', 'https://localhost:1', 'foo', '--force'])
828 self._check_output('', '') 670 self._check_output('', '')
829 self.assertEqual(0, ret) 671 self.assertEqual(0, ret)
830 672
831 def test_run_raw_cmd(self): 673 def test_run_raw_cmd(self):
832 # Minimalist use. 674 # Minimalist use.
833 request = { 675 request = {
834 'name': 'None/foo=bar', 676 'expiration_secs': 21600,
677 'name': u'None/foo=bar',
835 'parent_task_id': '', 678 'parent_task_id': '',
836 'priority': 100, 679 'priority': 100,
837 'properties': { 680 'properties': {
838 'commands': [['python', '-c', 'print(\'hi\')']], 681 'command': ['python', '-c', 'print(\'hi\')'],
839 'data': [], 682 'dimensions': [
840 'dimensions': {'foo': 'bar'}, 683 {'key': 'foo', 'value': 'bar'},
841 'env': {}, 684 ],
685 'env': [],
842 'execution_timeout_secs': 3600, 686 'execution_timeout_secs': 3600,
687 'extra_args': None,
688 'grace_period_secs': 30,
843 'idempotent': False, 689 'idempotent': False,
690 'inputs_ref': None,
844 'io_timeout_secs': 1200, 691 'io_timeout_secs': 1200,
845 }, 692 },
846 'scheduling_expiration_secs': 21600,
847 'tags': [], 693 'tags': [],
848 'user': None, 694 'user': None,
849 } 695 }
850 result = gen_request_response(request) 696 result = gen_request_response(request)
851 self.expected_requests( 697 self.expected_requests(
852 [ 698 [
853 ( 699 (
854 'https://localhost:1/swarming/api/v1/client/handshake', 700 'https://localhost:1/_ah/api/swarming/v1/tasks/new',
855 {'data': {}, 'headers': {'X-XSRF-Token-Request': '1'}}, 701 {'data': request},
856 {'server_version': 'v1', 'xsrf_token': 'Token'},
857 ),
858 (
859 'https://localhost:1/swarming/api/v1/client/request',
860 {'data': request, 'headers': {'X-XSRF-Token': 'Token'}},
861 result, 702 result,
862 ), 703 ),
863 ]) 704 ])
864 ret = main([ 705 ret = main([
865 'trigger', 706 'trigger',
866 '--swarming', 'https://localhost:1', 707 '--swarming', 'https://localhost:1',
867 '--dimension', 'foo', 'bar', 708 '--dimension', 'foo', 'bar',
868 '--raw-cmd', 709 '--raw-cmd',
869 '--', 710 '--',
870 'python', 711 'python',
871 '-c', 712 '-c',
872 'print(\'hi\')', 713 'print(\'hi\')',
873 ]) 714 ])
874 actual = sys.stdout.getvalue() 715 actual = sys.stdout.getvalue()
875 self.assertEqual(0, ret, (actual, sys.stderr.getvalue())) 716 self.assertEqual(0, ret, (actual, sys.stderr.getvalue()))
876 self._check_output( 717 self._check_output(
877 'Triggered task: None/foo=bar\n' 718 'Triggered task: None/foo=bar\n'
878 'To collect results, use:\n' 719 'To collect results, use:\n'
879 ' swarming.py collect -S https://localhost:1 12300\n' 720 ' swarming.py collect -S https://localhost:1 12300\n'
880 'Or visit:\n' 721 'Or visit:\n'
881 ' https://localhost:1/user/task/12300\n', 722 ' https://localhost:1/user/task/12300\n',
882 '') 723 '')
883 724
884 def test_run_isolated_hash(self): 725 def test_run_isolated_hash(self):
885 # pylint: disable=unused-argument 726 # pylint: disable=unused-argument
886 def isolated_upload_zip_bundle(isolate_server, bundle):
887 return 'https://localhost:1/fetch_url'
888 self.mock(
889 swarming, 'isolated_upload_zip_bundle', isolated_upload_zip_bundle)
890 self.mock(swarming, 'now', lambda: 123456) 727 self.mock(swarming, 'now', lambda: 123456)
891 728
892 request = gen_request_data() 729 request = gen_request_data(
730 properties={
731 'command': None,
732 'inputs_ref': {
733 'isolated': u'1111111111111111111111111111111111111111',
734 'isolatedserver': 'https://localhost:2',
735 'namespace': 'default-gzip',
736 },
737 })
893 result = gen_request_response(request) 738 result = gen_request_response(request)
894 self.expected_requests( 739 self.expected_requests(
895 [ 740 [
896 ( 741 (
897 'https://localhost:1/swarming/api/v1/client/handshake', 742 'https://localhost:1/_ah/api/swarming/v1/tasks/new',
898 {'data': {}, 'headers': {'X-XSRF-Token-Request': '1'}}, 743 {'data': request},
899 {'server_version': 'v1', 'xsrf_token': 'Token'},
900 ),
901 (
902 'https://localhost:1/swarming/api/v1/client/request',
903 {'data': request, 'headers': {'X-XSRF-Token': 'Token'}},
904 result, 744 result,
905 ), 745 ),
906 ]) 746 ])
907 ret = main([ 747 ret = main([
908 'trigger', 748 'trigger',
909 '--swarming', 'https://localhost:1', 749 '--swarming', 'https://localhost:1',
910 '--isolate-server', 'https://localhost:2', 750 '--isolate-server', 'https://localhost:2',
911 '--shards', '1', 751 '--shards', '1',
912 '--priority', '101', 752 '--priority', '101',
913 '--dimension', 'foo', 'bar', 753 '--dimension', 'foo', 'bar',
(...skipping 17 matching lines...) Expand all
931 'To collect results, use:\n' 771 'To collect results, use:\n'
932 ' swarming.py collect -S https://localhost:1 12300\n' 772 ' swarming.py collect -S https://localhost:1 12300\n'
933 'Or visit:\n' 773 'Or visit:\n'
934 ' https://localhost:1/user/task/12300\n', 774 ' https://localhost:1/user/task/12300\n',
935 '') 775 '')
936 776
937 def test_run_isolated_upload_and_json(self): 777 def test_run_isolated_upload_and_json(self):
938 # pylint: disable=unused-argument 778 # pylint: disable=unused-argument
939 write_json_calls = [] 779 write_json_calls = []
940 self.mock(tools, 'write_json', lambda *args: write_json_calls.append(args)) 780 self.mock(tools, 'write_json', lambda *args: write_json_calls.append(args))
941 def isolated_upload_zip_bundle(isolate_server, bundle):
942 return 'https://localhost:1/fetch_url'
943 self.mock(
944 swarming, 'isolated_upload_zip_bundle', isolated_upload_zip_bundle)
945 subprocess_calls = [] 781 subprocess_calls = []
946 self.mock(subprocess, 'call', lambda *c: subprocess_calls.append(c)) 782 self.mock(subprocess, 'call', lambda *c: subprocess_calls.append(c))
947 self.mock(swarming, 'now', lambda: 123456) 783 self.mock(swarming, 'now', lambda: 123456)
948 784
949 isolated = os.path.join(self.tempdir, 'zaz.isolated') 785 isolated = os.path.join(self.tempdir, 'zaz.isolated')
950 content = '{}' 786 content = '{}'
951 with open(isolated, 'wb') as f: 787 with open(isolated, 'wb') as f:
952 f.write(content) 788 f.write(content)
953 789
954 isolated_hash = isolateserver_mock.hash_content(content) 790 isolated_hash = isolateserver_mock.hash_content(content)
955 request = gen_request_data( 791 request = gen_request_data(
956 isolated_hash=isolated_hash, properties=dict(idempotent=True)) 792 properties={
793 'command': None,
794 'idempotent': True,
795 'inputs_ref': {
796 'isolated': isolated_hash,
797 'isolatedserver': 'https://localhost:2',
798 'namespace': 'default-gzip',
799 },
800 })
957 result = gen_request_response(request) 801 result = gen_request_response(request)
958 self.expected_requests( 802 self.expected_requests(
959 [ 803 [
960 ( 804 (
961 'https://localhost:1/swarming/api/v1/client/handshake', 805 'https://localhost:1/_ah/api/swarming/v1/tasks/new',
962 {'data': {}, 'headers': {'X-XSRF-Token-Request': '1'}}, 806 {'data': request},
963 {'server_version': 'v1', 'xsrf_token': 'Token'},
964 ),
965 (
966 'https://localhost:1/swarming/api/v1/client/request',
967 {'data': request, 'headers': {'X-XSRF-Token': 'Token'}},
968 result, 807 result,
969 ), 808 ),
970 ]) 809 ])
971 ret = main([ 810 ret = main([
972 'trigger', 811 'trigger',
973 '--swarming', 'https://localhost:1', 812 '--swarming', 'https://localhost:1',
974 '--isolate-server', 'https://localhost:2', 813 '--isolate-server', 'https://localhost:2',
975 '--shards', '1', 814 '--shards', '1',
976 '--priority', '101', 815 '--priority', '101',
977 '--dimension', 'foo', 'bar', 816 '--dimension', 'foo', 'bar',
978 '--dimension', 'os', 'Mac', 817 '--dimension', 'os', 'Mac',
979 '--expiration', '3600', 818 '--expiration', '3600',
980 '--user', 'joe@localhost', 819 '--user', 'joe@localhost',
981 '--tags', 'taga', 820 '--tags', 'taga',
982 '--tags', 'tagb', 821 '--tags', 'tagb',
983 '--hard-timeout', '60', 822 '--hard-timeout', '60',
984 '--io-timeout', '60', 823 '--io-timeout', '60',
985 '--idempotent', 824 '--idempotent',
986 '--task-name', 'unit_tests', 825 '--task-name', 'unit_tests',
987 '--dump-json', 'foo.json', 826 '--dump-json', 'foo.json',
988 isolated, 827 isolated,
989 '--', 828 '--',
990 '--some-arg', 829 '--some-arg',
991 '123', 830 '123',
992 ]) 831 ])
993 actual = sys.stdout.getvalue() 832 actual = sys.stdout.getvalue()
994 self.assertEqual(0, ret, (actual, sys.stderr.getvalue())) 833 self.assertEqual(0, ret, (actual, sys.stderr.getvalue()))
995 expected = [ 834 self.assertEqual([], subprocess_calls)
996 (
997 [
998 sys.executable,
999 os.path.join(swarming.ROOT_DIR, 'isolate.py'), 'archive',
1000 '--isolate-server', 'https://localhost:2',
1001 '--namespace' ,'default-gzip',
1002 '--isolated', isolated,
1003 ],
1004 0),
1005 ]
1006 self.assertEqual(expected, subprocess_calls)
1007 self._check_output( 835 self._check_output(
1008 'Archiving: %s\n'
1009 'Triggered task: unit_tests\n' 836 'Triggered task: unit_tests\n'
1010 'To collect results, use:\n' 837 'To collect results, use:\n'
1011 ' swarming.py collect -S https://localhost:1 --json foo.json\n' 838 ' swarming.py collect -S https://localhost:1 --json foo.json\n'
1012 'Or visit:\n' 839 'Or visit:\n'
1013 ' https://localhost:1/user/task/12300\n' % isolated, 840 ' https://localhost:1/user/task/12300\n',
1014 '') 841 '')
1015 expected = [ 842 expected = [
1016 ( 843 (
1017 'foo.json', 844 'foo.json',
1018 { 845 {
1019 'base_task_name': 'unit_tests', 846 'base_task_name': 'unit_tests',
1020 'tasks': { 847 'tasks': {
1021 'unit_tests': { 848 'unit_tests': {
1022 'shard_index': 0, 849 'shard_index': 0,
1023 'task_id': '12300', 850 'task_id': '12300',
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1089 '', 916 '',
1090 'Usage: swarming.py trigger [options] (hash|isolated) ' 917 'Usage: swarming.py trigger [options] (hash|isolated) '
1091 '[-- extra_args|raw command]' 918 '[-- extra_args|raw command]'
1092 '\n\n' 919 '\n\n'
1093 'swarming.py: error: Please at least specify one --dimension\n') 920 'swarming.py: error: Please at least specify one --dimension\n')
1094 921
1095 def test_query_base(self): 922 def test_query_base(self):
1096 self.expected_requests( 923 self.expected_requests(
1097 [ 924 [
1098 ( 925 (
1099 'https://localhost:1/swarming/api/v1/client/bots/botid/tasks?' 926 'https://localhost:1/_ah/api/swarming/v1/bot/botid/tasks?limit=200',
1100 'limit=200',
1101 {}, 927 {},
1102 {'yo': 'dawg'}, 928 {'yo': 'dawg'},
1103 ), 929 ),
1104 ]) 930 ])
1105 ret = main( 931 ret = main(
1106 [ 932 [
1107 'query', '--swarming', 'https://localhost:1', 'bots/botid/tasks', 933 'query', '--swarming', 'https://localhost:1', 'bot/botid/tasks',
1108 ]) 934 ])
1109 self._check_output('{\n "yo": "dawg"\n}\n', '') 935 self._check_output('{\n "yo": "dawg"\n}\n', '')
1110 self.assertEqual(0, ret) 936 self.assertEqual(0, ret)
1111 937
1112 def test_query_cursor(self): 938 def test_query_cursor(self):
1113 self.expected_requests( 939 self.expected_requests(
1114 [ 940 [
1115 ( 941 (
1116 'https://localhost:1/swarming/api/v1/client/bots/botid/tasks?' 942 'https://localhost:1/_ah/api/swarming/v1/bot/botid/tasks?'
1117 'limit=2', 943 'foo=bar&limit=2',
1118 {}, 944 {},
1119 { 945 {
1120 'cursor': '%', 946 'cursor': '%',
1121 'extra': False, 947 'extra': False,
1122 'items': ['A'], 948 'items': ['A'],
1123 }, 949 },
1124 ), 950 ),
1125 ( 951 (
1126 'https://localhost:1/swarming/api/v1/client/bots/botid/tasks?' 952 'https://localhost:1/_ah/api/swarming/v1/bot/botid/tasks?'
1127 'cursor=%25&limit=1', 953 'foo=bar&cursor=%25&limit=1',
1128 {}, 954 {},
1129 { 955 {
1130 'cursor': None, 956 'cursor': None,
1131 'items': ['B'], 957 'items': ['B'],
1132 'ignored': True, 958 'ignored': True,
1133 }, 959 },
1134 ), 960 ),
1135 ]) 961 ])
1136 ret = main( 962 ret = main(
1137 [ 963 [
1138 'query', '--swarming', 'https://localhost:1', 'bots/botid/tasks', 964 'query', '--swarming', 'https://localhost:1',
965 'bot/botid/tasks?foo=bar',
1139 '--limit', '2', 966 '--limit', '2',
1140 ]) 967 ])
1141 expected = ( 968 expected = (
1142 '{\n' 969 '{\n'
1143 ' "extra": false, \n' 970 ' "extra": false, \n'
1144 ' "items": [\n' 971 ' "items": [\n'
1145 ' "A", \n' 972 ' "A", \n'
1146 ' "B"\n' 973 ' "B"\n'
1147 ' ]\n' 974 ' ]\n'
1148 '}\n') 975 '}\n')
(...skipping 11 matching lines...) Expand all
1160 expected['aa'] = 'bb' 987 expected['aa'] = 'bb'
1161 self.assertEqual(expected, env) 988 self.assertEqual(expected, env)
1162 self.assertEqual('work', cwd) 989 self.assertEqual('work', cwd)
1163 return 0 990 return 0
1164 991
1165 self.mock(subprocess, 'call', call) 992 self.mock(subprocess, 'call', call)
1166 993
1167 self.expected_requests( 994 self.expected_requests(
1168 [ 995 [
1169 ( 996 (
1170 'https://localhost:1/swarming/api/v1/client/task/123/request', 997 'https://localhost:1/_ah/api/swarming/v1/task/123/request',
1171 {}, 998 {},
1172 { 999 {
1173 'properties': { 1000 'properties': {
1174 'commands': [['foo']], 1001 'command': ['foo'],
1175 'data': [], 1002 'env': [
1176 'env': {'aa': 'bb'}, 1003 {'key': 'aa', 'value': 'bb'},
1004 ],
1177 }, 1005 },
1178 }, 1006 },
1179 ), 1007 ),
1180 ]) 1008 ])
1181 ret = main( 1009 ret = main(
1182 [ 1010 [
1183 'reproduce', '--swarming', 'https://localhost:1', '123', 1011 'reproduce', '--swarming', 'https://localhost:1', '123',
1184 ]) 1012 ])
1185 self._check_output('', '') 1013 self._check_output('', '')
1186 self.assertEqual(0, ret) 1014 self.assertEqual(0, ret)
1187 finally: 1015 finally:
1188 os.chdir(old_cwd) 1016 os.chdir(old_cwd)
1189 1017
1190 1018
1191 class TestCommandBot(NetTestCase): 1019 class TestCommandBot(NetTestCase):
1192 # Specialized test fixture for command 'bot'. 1020 # Specialized test fixture for command 'bot'.
1193 def setUp(self): 1021 def setUp(self):
1194 super(TestCommandBot, self).setUp() 1022 super(TestCommandBot, self).setUp()
1195 # Expected requests are always the same, independent of the test case. 1023 # Expected requests are always the same, independent of the test case.
1196 self.expected_requests( 1024 self.expected_requests(
1197 [ 1025 [
1198 ( 1026 (
1199 'https://localhost:1/swarming/api/v1/client/bots?limit=250', 1027 'https://localhost:1/_ah/api/swarming/v1/bots/list?limit=250',
1200 {}, 1028 {},
1201 self.mock_swarming_api_v1_bots_page_1(), 1029 self.mock_swarming_api_v1_bots_page_1(),
1202 ), 1030 ),
1203 ( 1031 (
1204 'https://localhost:1/swarming/api/v1/client/bots?limit=250&' 1032 'https://localhost:1/_ah/api/swarming/v1/bots/list?limit=250&'
1205 'cursor=opaque_cursor', 1033 'cursor=opaque_cursor',
1206 {}, 1034 {},
1207 self.mock_swarming_api_v1_bots_page_2(), 1035 self.mock_swarming_api_v1_bots_page_2(),
1208 ), 1036 ),
1209 ]) 1037 ])
1210 1038
1211 @staticmethod 1039 @staticmethod
1212 def mock_swarming_api_v1_bots_page_1(): 1040 def mock_swarming_api_v1_bots_page_1():
1213 """Returns fake /swarming/api/v1/client/bots data.""" 1041 """Returns fake /_ah/api/swarming/v1/bots/list data."""
1214 # Sample data retrieved from actual server. 1042 # Sample data retrieved from actual server.
1215 now = unicode(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')) 1043 now = unicode(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
1216 return { 1044 return {
1217 u'items': [ 1045 u'items': [
1218 { 1046 {
1047 u'bot_id': u'swarm3',
1219 u'created_ts': now, 1048 u'created_ts': now,
1220 u'dimensions': { 1049 u'dimensions': [
1221 u'cores': u'4', 1050 {u'key': u'cores', u'value': [u'4']},
1222 u'cpu': [u'x86', u'x86-64'], 1051 {u'key': u'cpu', u'value': [u'x86', u'x86-64']},
1223 u'gpu': [u'15ad', u'15ad:0405'], 1052 {u'key': u'gpu', u'value': [u'15ad', u'15ad:0405']},
1224 u'hostname': u'swarm3.example.com', 1053 {u'key': u'id', u'value': [u'swarm3']},
1225 u'id': u'swarm3', 1054 {u'key': u'os', u'value': [u'Mac', u'Mac-10.9']},
1226 u'os': [u'Mac', u'Mac-10.9'], 1055 ],
1227 },
1228 u'external_ip': u'1.1.1.3', 1056 u'external_ip': u'1.1.1.3',
1229 u'hostname': u'swarm3.example.com', 1057 u'hostname': u'swarm3.example.com',
1230 u'id': u'swarm3',
1231 u'internal_ip': u'192.168.0.3', 1058 u'internal_ip': u'192.168.0.3',
1232 u'is_dead': False, 1059 u'is_dead': False,
1233 u'last_seen_ts': now, 1060 u'last_seen_ts': now,
1234 u'quarantined': False, 1061 u'quarantined': False,
1235 u'task_id': u'148569b73a89501', 1062 u'task_id': u'148569b73a89501',
1236 u'task_name': u'browser_tests', 1063 u'task_name': u'browser_tests',
1237 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905', 1064 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905',
1238 }, 1065 },
1239 { 1066 {
1067 u'bot_id': u'swarm1',
1240 u'created_ts': now, 1068 u'created_ts': now,
1241 u'dimensions': { 1069 u'dimensions': [
1242 u'cores': u'8', 1070 {u'key': u'cores', u'value': [u'8']},
1243 u'cpu': [u'x86', u'x86-64'], 1071 {u'key': u'cpu', u'value': [u'x86', u'x86-64']},
1244 u'gpu': [], 1072 {u'key': u'gpu', u'value': []},
1245 u'hostname': u'swarm1.example.com', 1073 {u'key': u'id', u'value': [u'swarm1']},
1246 u'id': u'swarm1', 1074 {u'key': u'os', u'value': [u'Linux', u'Linux-12.04']},
1247 u'os': [u'Linux', u'Linux-12.04'], 1075 ],
1248 },
1249 u'external_ip': u'1.1.1.1', 1076 u'external_ip': u'1.1.1.1',
1250 u'hostname': u'swarm1.example.com', 1077 u'hostname': u'swarm1.example.com',
1251 u'id': u'swarm1',
1252 u'internal_ip': u'192.168.0.1', 1078 u'internal_ip': u'192.168.0.1',
1253 u'is_dead': True, 1079 u'is_dead': True,
1254 u'last_seen_ts': 'A long time ago', 1080 u'last_seen_ts': 'A long time ago',
1255 u'quarantined': False, 1081 u'quarantined': False,
1256 u'task_id': u'', 1082 u'task_id': u'',
1257 u'task_name': None, 1083 u'task_name': None,
1258 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905', 1084 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905',
1259 }, 1085 },
1260 { 1086 {
1087 u'bot_id': u'swarm2',
1261 u'created_ts': now, 1088 u'created_ts': now,
1262 u'dimensions': { 1089 u'dimensions': [
1263 u'cores': u'8', 1090 {u'key': u'cores', u'value': [u'8']},
1264 u'cpu': [u'x86', u'x86-64'], 1091 {u'key': u'cpu', u'value': [u'x86', u'x86-64']},
1265 u'cygwin': u'0', 1092 {u'key': u'gpu', u'value': [
1266 u'gpu': [
1267 u'15ad', 1093 u'15ad',
1268 u'15ad:0405', 1094 u'15ad:0405',
1269 u'VMware Virtual SVGA 3D Graphics Adapter', 1095 u'VMware Virtual SVGA 3D Graphics Adapter',
1270 ], 1096 ]},
1271 u'hostname': u'swarm2.example.com', 1097 {u'key': u'id', u'value': [u'swarm2']},
1272 u'id': u'swarm2', 1098 {u'key': u'os', u'value': [u'Windows', u'Windows-6.1']},
1273 u'integrity': u'high', 1099 ],
1274 u'os': [u'Windows', u'Windows-6.1'],
1275 },
1276 u'external_ip': u'1.1.1.2', 1100 u'external_ip': u'1.1.1.2',
1277 u'hostname': u'swarm2.example.com', 1101 u'hostname': u'swarm2.example.com',
1278 u'id': u'swarm2',
1279 u'internal_ip': u'192.168.0.2', 1102 u'internal_ip': u'192.168.0.2',
1280 u'is_dead': False, 1103 u'is_dead': False,
1281 u'last_seen_ts': now, 1104 u'last_seen_ts': now,
1282 u'quarantined': False, 1105 u'quarantined': False,
1283 u'task_id': u'', 1106 u'task_id': u'',
1284 u'task_name': None, 1107 u'task_name': None,
1285 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905', 1108 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905',
1286 }, 1109 },
1287 ], 1110 ],
1288 u'cursor': u'opaque_cursor', 1111 u'cursor': u'opaque_cursor',
1289 u'death_timeout': 1800.0, 1112 u'death_timeout': 1800.0,
1290 u'limit': 4, 1113 u'limit': 4,
1291 u'now': unicode(now), 1114 u'now': unicode(now),
1292 } 1115 }
1293 1116
1294 @staticmethod 1117 @staticmethod
1295 def mock_swarming_api_v1_bots_page_2(): 1118 def mock_swarming_api_v1_bots_page_2():
1296 """Returns fake /swarming/api/v1/client/bots data.""" 1119 """Returns fake /_ah/api/swarming/v1/bots/list data."""
1297 # Sample data retrieved from actual server. 1120 # Sample data retrieved from actual server.
1298 now = unicode(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')) 1121 now = unicode(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
1299 return { 1122 return {
1300 u'items': [ 1123 u'items': [
1301 { 1124 {
1125 u'bot_id': u'swarm4',
1302 u'created_ts': now, 1126 u'created_ts': now,
1303 u'dimensions': { 1127 u'dimensions': [
1304 u'cores': u'8', 1128 {u'key': u'cores', u'value': [u'8']},
1305 u'cpu': [u'x86', u'x86-64'], 1129 {u'key': u'cpu', u'value': [u'x86', u'x86-64']},
1306 u'gpu': [], 1130 {u'key': u'gpu', u'value': []},
1307 u'hostname': u'swarm4.example.com', 1131 {u'key': u'id', u'value': [u'swarm4']},
1308 u'id': u'swarm4', 1132 {u'key': u'os', u'value': [u'Linux', u'Linux-12.04']},
1309 u'os': [u'Linux', u'Linux-12.04'], 1133 ],
1310 },
1311 u'external_ip': u'1.1.1.4', 1134 u'external_ip': u'1.1.1.4',
1312 u'hostname': u'swarm4.example.com', 1135 u'hostname': u'swarm4.example.com',
1313 u'id': u'swarm4',
1314 u'internal_ip': u'192.168.0.4', 1136 u'internal_ip': u'192.168.0.4',
1315 u'is_dead': False, 1137 u'is_dead': False,
1316 u'last_seen_ts': now, 1138 u'last_seen_ts': now,
1317 u'quarantined': False, 1139 u'quarantined': False,
1318 u'task_id': u'14856971a64c601', 1140 u'task_id': u'14856971a64c601',
1319 u'task_name': u'base_unittests', 1141 u'task_name': u'base_unittests',
1320 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905', 1142 u'version': u'56918a2ea28a6f51751ad14cc086f118b8727905',
1321 } 1143 }
1322 ], 1144 ],
1323 u'cursor': None, 1145 u'cursor': None,
1324 u'death_timeout': 1800.0, 1146 u'death_timeout': 1800.0,
1325 u'limit': 4, 1147 u'limit': 4,
1326 u'now': unicode(now), 1148 u'now': unicode(now),
1327 } 1149 }
1328 1150
1329 def test_bots(self): 1151 def test_bots(self):
1330 ret = main(['bots', '--swarming', 'https://localhost:1']) 1152 ret = main(['bots', '--swarming', 'https://localhost:1'])
1331 expected = ( 1153 expected = (
1332 u'swarm2\n' 1154 u'swarm2\n'
1333 u' {"cores": "8", "cpu": ["x86", "x86-64"], "cygwin": "0", "gpu": ' 1155 u' {"cores": ["8"], "cpu": ["x86", "x86-64"], "gpu": '
1334 '["15ad", "15ad:0405", "VMware Virtual SVGA 3D Graphics Adapter"], ' 1156 '["15ad", "15ad:0405", "VMware Virtual SVGA 3D Graphics Adapter"], '
1335 '"hostname": "swarm2.example.com", "id": "swarm2", "integrity": ' 1157 '"id": ["swarm2"], "os": ["Windows", "Windows-6.1"]}\n'
1336 '"high", "os": ["Windows", "Windows-6.1"]}\n'
1337 'swarm3\n' 1158 'swarm3\n'
1338 ' {"cores": "4", "cpu": ["x86", "x86-64"], "gpu": ["15ad", ' 1159 ' {"cores": ["4"], "cpu": ["x86", "x86-64"], "gpu": ["15ad", '
1339 '"15ad:0405"], "hostname": "swarm3.example.com", "id": "swarm3", ' 1160 '"15ad:0405"], "id": ["swarm3"], "os": ["Mac", "Mac-10.9"]}\n'
1340 '"os": ["Mac", "Mac-10.9"]}\n'
1341 u' task: 148569b73a89501\n' 1161 u' task: 148569b73a89501\n'
1342 u'swarm4\n' 1162 u'swarm4\n'
1343 u' {"cores": "8", "cpu": ["x86", "x86-64"], "gpu": [], "hostname": ' 1163 u' {"cores": ["8"], "cpu": ["x86", "x86-64"], "gpu": [], '
1344 '"swarm4.example.com", "id": "swarm4", "os": ["Linux", ' 1164 '"id": ["swarm4"], "os": ["Linux", "Linux-12.04"]}\n'
1345 '"Linux-12.04"]}\n'
1346 u' task: 14856971a64c601\n') 1165 u' task: 14856971a64c601\n')
1347 self._check_output(expected, '') 1166 self._check_output(expected, '')
1348 self.assertEqual(0, ret) 1167 self.assertEqual(0, ret)
1349 1168
1350 def test_bots_bare(self): 1169 def test_bots_bare(self):
1351 ret = main(['bots', '--swarming', 'https://localhost:1', '--bare']) 1170 ret = main(['bots', '--swarming', 'https://localhost:1', '--bare'])
1352 self._check_output("swarm2\nswarm3\nswarm4\n", '') 1171 self._check_output("swarm2\nswarm3\nswarm4\n", '')
1353 self.assertEqual(0, ret) 1172 self.assertEqual(0, ret)
1354 1173
1355 def test_bots_filter(self): 1174 def test_bots_filter(self):
1356 ret = main( 1175 ret = main(
1357 [ 1176 [
1358 'bots', '--swarming', 'https://localhost:1', 1177 'bots', '--swarming', 'https://localhost:1',
1359 '--dimension', 'os', 'Windows', 1178 '--dimension', 'os', 'Windows',
1360 ]) 1179 ])
1361 expected = ( 1180 expected = (
1362 u'swarm2\n {"cores": "8", "cpu": ["x86", "x86-64"], "cygwin": "0", ' 1181 u'swarm2\n {"cores": ["8"], "cpu": ["x86", "x86-64"], '
1363 '"gpu": ["15ad", "15ad:0405", "VMware Virtual SVGA 3D Graphics ' 1182 '"gpu": ["15ad", "15ad:0405", "VMware Virtual SVGA 3D Graphics '
1364 'Adapter"], "hostname": "swarm2.example.com", "id": "swarm2", ' 1183 'Adapter"], "id": ["swarm2"], '
1365 '"integrity": "high", "os": ["Windows", "Windows-6.1"]}\n') 1184 '"os": ["Windows", "Windows-6.1"]}\n')
1366 self._check_output(expected, '') 1185 self._check_output(expected, '')
1367 self.assertEqual(0, ret) 1186 self.assertEqual(0, ret)
1368 1187
1369 def test_bots_filter_keep_dead(self): 1188 def test_bots_filter_keep_dead(self):
1370 ret = main( 1189 ret = main(
1371 [ 1190 [
1372 'bots', '--swarming', 'https://localhost:1', 1191 'bots', '--swarming', 'https://localhost:1',
1373 '--dimension', 'os', 'Linux', '--keep-dead', 1192 '--dimension', 'os', 'Linux', '--keep-dead',
1374 ]) 1193 ])
1375 expected = ( 1194 expected = (
1376 u'swarm1\n {"cores": "8", "cpu": ["x86", "x86-64"], "gpu": [], ' 1195 u'swarm1\n {"cores": ["8"], "cpu": ["x86", "x86-64"], "gpu": [], '
1377 '"hostname": "swarm1.example.com", "id": "swarm1", "os": ["Linux", ' 1196 '"id": ["swarm1"], "os": ["Linux", "Linux-12.04"]}\n'
1378 '"Linux-12.04"]}\n'
1379 u'swarm4\n' 1197 u'swarm4\n'
1380 u' {"cores": "8", "cpu": ["x86", "x86-64"], "gpu": [], "hostname": ' 1198 u' {"cores": ["8"], "cpu": ["x86", "x86-64"], "gpu": [], '
1381 '"swarm4.example.com", "id": "swarm4", "os": ["Linux", ' 1199 '"id": ["swarm4"], "os": ["Linux", "Linux-12.04"]}\n'
1382 '"Linux-12.04"]}\n'
1383 u' task: 14856971a64c601\n') 1200 u' task: 14856971a64c601\n')
1384 self._check_output(expected, '') 1201 self._check_output(expected, '')
1385 self.assertEqual(0, ret) 1202 self.assertEqual(0, ret)
1386 1203
1387 def test_bots_filter_dead_only(self): 1204 def test_bots_filter_dead_only(self):
1388 ret = main( 1205 ret = main(
1389 [ 1206 [
1390 'bots', '--swarming', 'https://localhost:1', 1207 'bots', '--swarming', 'https://localhost:1',
1391 '--dimension', 'os', 'Linux', '--dead-only', 1208 '--dimension', 'os', 'Linux', '--dead-only',
1392 ]) 1209 ])
1393 expected = ( 1210 expected = (
1394 u'swarm1\n {"cores": "8", "cpu": ["x86", "x86-64"], "gpu": [], ' 1211 u'swarm1\n {"cores": ["8"], "cpu": ["x86", "x86-64"], "gpu": [], '
1395 '"hostname": "swarm1.example.com", "id": "swarm1", "os": ["Linux", ' 1212 '"id": ["swarm1"], "os": ["Linux", "Linux-12.04"]}\n')
1396 '"Linux-12.04"]}\n')
1397 self._check_output(expected, '') 1213 self._check_output(expected, '')
1398 self.assertEqual(0, ret) 1214 self.assertEqual(0, ret)
1399 1215
1400 1216
1401 if __name__ == '__main__': 1217 if __name__ == '__main__':
1402 logging.basicConfig( 1218 logging.basicConfig(
1403 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) 1219 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL)
1404 if '-v' in sys.argv: 1220 if '-v' in sys.argv:
1405 unittest.TestCase.maxDiff = None 1221 unittest.TestCase.maxDiff = None
1406 for e in ('ISOLATE_SERVER', 'SWARMING_TASK_ID', 'SWARMING_SERVER'): 1222 for e in ('ISOLATE_SERVER', 'SWARMING_TASK_ID', 'SWARMING_SERVER'):
1407 os.environ.pop(e, None) 1223 os.environ.pop(e, None)
1408 unittest.main() 1224 unittest.main()
OLDNEW
« client/swarming.py ('K') | « client/swarming.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698