| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import collections | 5 import collections |
| 6 import json | 6 import json |
| 7 import mock |
| 7 import os | 8 import os |
| 8 import urllib | 9 import urllib |
| 9 import zlib | 10 import zlib |
| 10 | 11 |
| 12 from google.appengine.api.urlfetch_errors import DeadlineExceededError |
| 13 from google.appengine.api.urlfetch_errors import DownloadError |
| 14 from google.appengine.api.urlfetch_errors import ConnectionClosedError |
| 15 |
| 16 from common.http_client_appengine import HttpClientAppengine as HttpClient |
| 11 from common.retry_http_client import RetryHttpClient | 17 from common.retry_http_client import RetryHttpClient |
| 12 from model.wf_config import FinditConfig | 18 from model.wf_config import FinditConfig |
| 13 from model.wf_step import WfStep | 19 from model.wf_step import WfStep |
| 14 from waterfall import swarming_util | 20 from waterfall import swarming_util |
| 15 from waterfall import waterfall_config | 21 from waterfall import waterfall_config |
| 16 from waterfall.swarming_task_request import SwarmingTaskRequest | 22 from waterfall.swarming_task_request import SwarmingTaskRequest |
| 17 from waterfall.test import wf_testcase | 23 from waterfall.test import wf_testcase |
| 18 | 24 |
| 19 | 25 |
| 20 class SwarmingHttpClient(RetryHttpClient): | 26 class SwarmingHttpClient(RetryHttpClient): |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 url = ('https://chromium-swarm.appspot.com/' | 164 url = ('https://chromium-swarm.appspot.com/' |
| 159 '_ah/api/swarming/v1/task/%s/request' % task_id) | 165 '_ah/api/swarming/v1/task/%s/request' % task_id) |
| 160 self.logged_http_client.SetResponse( | 166 self.logged_http_client.SetResponse( |
| 161 'get', url, json.dumps(task_request_json), 200) | 167 'get', url, json.dumps(task_request_json), 200) |
| 162 | 168 |
| 163 task_request = swarming_util.GetSwarmingTaskRequest( | 169 task_request = swarming_util.GetSwarmingTaskRequest( |
| 164 task_id, self.logged_http_client) | 170 task_id, self.logged_http_client) |
| 165 | 171 |
| 166 self.assertEqual(task_request_json, task_request.Serialize()) | 172 self.assertEqual(task_request_json, task_request.Serialize()) |
| 167 | 173 |
| 174 @mock.patch.object(swarming_util, '_SendRequestToServer', |
| 175 return_value=(None, {'code': 1, 'message': 'error'})) |
| 176 def testGetSwarmingTaskRequestError(self, _): |
| 177 self.assertIsNone( |
| 178 swarming_util.GetSwarmingTaskRequest('task_id1', HttpClient())) |
| 179 |
| 168 def testTriggerSwarmingTask(self): | 180 def testTriggerSwarmingTask(self): |
| 169 request = SwarmingTaskRequest() | 181 request = SwarmingTaskRequest() |
| 170 request.expiration_secs = 2 | 182 request.expiration_secs = 2 |
| 171 request.name = 'name' | 183 request.name = 'name' |
| 172 request.parent_task_id = 'pti' | 184 request.parent_task_id = 'pti' |
| 173 request.priority = 1 | 185 request.priority = 1 |
| 174 request.tags = ['tag'] | 186 request.tags = ['tag'] |
| 175 request.user = 'user' | 187 request.user = 'user' |
| 176 request.command = 'cmd' | 188 request.command = 'cmd' |
| 177 request.dimensions = [{'key': 'd', 'value': 'dv'}] | 189 request.dimensions = [{'key': 'd', 'value': 'dv'}] |
| (...skipping 22 matching lines...) Expand all Loading... |
| 200 'extra_args': ['--flag'], | 212 'extra_args': ['--flag'], |
| 201 'grace_period_secs': 5, | 213 'grace_period_secs': 5, |
| 202 'idempotent': True, | 214 'idempotent': True, |
| 203 'inputs_ref': {'isolated': 'i'}, | 215 'inputs_ref': {'isolated': 'i'}, |
| 204 'io_timeout_secs': 3, | 216 'io_timeout_secs': 3, |
| 205 }, | 217 }, |
| 206 'tags': ['tag', 'findit:1', 'project:Chromium', 'purpose:post-commit'], | 218 'tags': ['tag', 'findit:1', 'project:Chromium', 'purpose:post-commit'], |
| 207 'user': 'user', | 219 'user': 'user', |
| 208 } | 220 } |
| 209 | 221 |
| 210 task_id = swarming_util.TriggerSwarmingTask( | 222 task_id, error = swarming_util.TriggerSwarmingTask( |
| 211 request, self.logged_http_client) | 223 request, self.logged_http_client) |
| 212 self.assertEqual('1', task_id) | 224 self.assertEqual('1', task_id) |
| 225 self.assertIsNone(error) |
| 213 | 226 |
| 214 method, data, _ = self.logged_http_client.GetRequest(url) | 227 method, data, _ = self.logged_http_client.GetRequest(url) |
| 215 self.assertEqual('post', method) | 228 self.assertEqual('post', method) |
| 216 self.assertEqual(expected_task_request_json, json.loads(data)) | 229 self.assertEqual(expected_task_request_json, json.loads(data)) |
| 217 | 230 |
| 231 @mock.patch.object(swarming_util, '_SendRequestToServer', |
| 232 return_value=(None, {'code': 1, 'message': 'error'})) |
| 233 def testTriggerSwarmingTaskError(self, _): |
| 234 request = SwarmingTaskRequest() |
| 235 task_id, error = swarming_util.TriggerSwarmingTask( |
| 236 request, HttpClient()) |
| 237 self.assertIsNone(task_id) |
| 238 self.assertIsNotNone(error) |
| 239 |
| 218 def testGetIsolatedDataForFailedBuild(self): | 240 def testGetIsolatedDataForFailedBuild(self): |
| 219 master_name = 'm' | 241 master_name = 'm' |
| 220 builder_name = 'b' | 242 builder_name = 'b' |
| 221 build_number = 223 | 243 build_number = 223 |
| 222 failed_steps = { | 244 failed_steps = { |
| 223 'a_tests': { | 245 'a_tests': { |
| 224 'current_failure': 2, | 246 'current_failure': 2, |
| 225 'first_failure': 0 | 247 'first_failure': 0 |
| 226 }, | 248 }, |
| 227 'unit_tests': { | 249 'unit_tests': { |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 390 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 369 'isolated_server') | 391 'isolated_server') |
| 370 } | 392 } |
| 371 isolated_storage_url = waterfall_config.GetSwarmingSettings().get( | 393 isolated_storage_url = waterfall_config.GetSwarmingSettings().get( |
| 372 'isolated_storage_url') | 394 'isolated_storage_url') |
| 373 self.http_client._SetResponseForPostRequest('shard1_isolated') | 395 self.http_client._SetResponseForPostRequest('shard1_isolated') |
| 374 self.http_client._SetResponseForPostRequest('shard1_url') | 396 self.http_client._SetResponseForPostRequest('shard1_url') |
| 375 self.http_client._SetResponseForGetRequestIsolated( | 397 self.http_client._SetResponseForGetRequestIsolated( |
| 376 'https://%s/default-gzip/shard1' % isolated_storage_url, 'shard1') | 398 'https://%s/default-gzip/shard1' % isolated_storage_url, 'shard1') |
| 377 | 399 |
| 378 result = swarming_util._DownloadTestResults( | 400 result, error = swarming_util._DownloadTestResults( |
| 379 isolated_data, self.http_client) | 401 isolated_data, self.http_client) |
| 380 | 402 |
| 381 expected_result = json.loads(zlib.decompress( | 403 expected_result = json.loads(zlib.decompress( |
| 382 self.http_client._GetData('isolated', 'shard1'))) | 404 self.http_client._GetData('isolated', 'shard1'))) |
| 383 self.assertEqual(expected_result, result) | 405 self.assertEqual(expected_result, result) |
| 406 self.assertIsNone(error) |
| 384 | 407 |
| 385 def testDownloadTestResultsFailedForSecondHash(self): | 408 def testDownloadTestResultsFailedForSecondHash(self): |
| 386 isolated_data = { | 409 isolated_data = { |
| 387 'digest': 'not found', | 410 'digest': 'not found', |
| 388 'namespace': 'default-gzip', | 411 'namespace': 'default-gzip', |
| 389 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 412 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 390 'isolated_server') | 413 'isolated_server') |
| 391 } | 414 } |
| 392 | 415 |
| 393 result = swarming_util._DownloadTestResults( | 416 result, error = swarming_util._DownloadTestResults( |
| 394 isolated_data, self.http_client) | 417 isolated_data, self.http_client) |
| 395 | 418 |
| 396 self.assertIsNone(result) | 419 self.assertIsNone(result) |
| 420 self.assertIsNotNone(error) |
| 397 | 421 |
| 398 def testDownloadTestResultsFailedForParsingSecondHash(self): | 422 def testDownloadTestResultsFailedForParsingSecondHash(self): |
| 399 isolated_data = { | 423 isolated_data = { |
| 400 'digest': 'not found', | 424 'digest': 'not found', |
| 401 'namespace': 'default-gzip', | 425 'namespace': 'default-gzip', |
| 402 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 426 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 403 'isolated_server') | 427 'isolated_server') |
| 404 } | 428 } |
| 405 | 429 |
| 406 self.http_client._SetResponseForPostRequest('not found') | 430 self.http_client._SetResponseForPostRequest('not found') |
| 407 result = swarming_util._DownloadTestResults( | 431 result, error = swarming_util._DownloadTestResults( |
| 408 isolated_data, self.http_client) | 432 isolated_data, self.http_client) |
| 409 | 433 |
| 410 self.assertIsNone(result) | 434 self.assertIsNone(result) |
| 435 self.assertIsNone(error) |
| 411 | 436 |
| 412 def testDownloadTestResultsFailedForFileUrl(self): | 437 def testDownloadTestResultsFailedForFileUrl(self): |
| 413 isolated_data = { | 438 isolated_data = { |
| 414 'digest': 'shard1_isolated', | 439 'digest': 'shard1_isolated', |
| 415 'namespace': 'default-gzip', | 440 'namespace': 'default-gzip', |
| 416 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 441 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 417 'isolated_server') | 442 'isolated_server') |
| 418 } | 443 } |
| 419 self.http_client._SetResponseForPostRequest('shard1_isolated') | 444 self.http_client._SetResponseForPostRequest('shard1_isolated') |
| 420 result = swarming_util._DownloadTestResults( | 445 result, error = swarming_util._DownloadTestResults( |
| 421 isolated_data, self.http_client) | 446 isolated_data, self.http_client) |
| 422 | 447 |
| 423 self.assertIsNone(result) | 448 self.assertIsNone(result) |
| 449 self.assertIsNotNone(error) |
| 424 | 450 |
| 425 def testDownloadTestResultsFailedForFile(self): | 451 def testDownloadTestResultsFailedForFile(self): |
| 426 isolated_data = { | 452 isolated_data = { |
| 427 'digest': 'shard1_isolated', | 453 'digest': 'shard1_isolated', |
| 428 'namespace': 'default-gzip', | 454 'namespace': 'default-gzip', |
| 429 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 455 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 430 'isolated_server') | 456 'isolated_server') |
| 431 } | 457 } |
| 432 self.http_client._SetResponseForPostRequest('shard1_isolated') | 458 self.http_client._SetResponseForPostRequest('shard1_isolated') |
| 433 self.http_client._SetResponseForPostRequest('shard1_url') | 459 self.http_client._SetResponseForPostRequest('shard1_url') |
| 434 result = swarming_util._DownloadTestResults( | 460 result, error = swarming_util._DownloadTestResults( |
| 435 isolated_data, self.http_client) | 461 isolated_data, self.http_client) |
| 436 | 462 |
| 437 self.assertIsNone(result) | 463 self.assertIsNone(result) |
| 464 self.assertIsNone(error) |
| 438 | 465 |
| 439 def testRetrieveShardedTestResultsFromIsolatedServer(self): | 466 def testRetrieveShardedTestResultsFromIsolatedServer(self): |
| 440 isolated_data = [ | 467 isolated_data = [ |
| 441 { | 468 { |
| 442 'digest': 'shard1_isolated', | 469 'digest': 'shard1_isolated', |
| 443 'namespace': 'default-gzip', | 470 'namespace': 'default-gzip', |
| 444 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 471 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 445 'isolated_server') | 472 'isolated_server') |
| 446 }, | 473 }, |
| 447 { | 474 { |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 result = swarming_util.RetrieveShardedTestResultsFromIsolatedServer( | 544 result = swarming_util.RetrieveShardedTestResultsFromIsolatedServer( |
| 518 isolated_data, self.http_client) | 545 isolated_data, self.http_client) |
| 519 | 546 |
| 520 self.assertIsNone(result) | 547 self.assertIsNone(result) |
| 521 | 548 |
| 522 def testGetSwarmingTaskResultById(self): | 549 def testGetSwarmingTaskResultById(self): |
| 523 task_id = '2944afa502297110' | 550 task_id = '2944afa502297110' |
| 524 | 551 |
| 525 self.http_client._SetResponseForGetRequestSwarmingResult(task_id) | 552 self.http_client._SetResponseForGetRequestSwarmingResult(task_id) |
| 526 | 553 |
| 527 data = swarming_util.GetSwarmingTaskResultById( | 554 data, error = swarming_util.GetSwarmingTaskResultById( |
| 528 task_id, self.http_client) | 555 task_id, self.http_client) |
| 529 | 556 |
| 530 expected_outputs_ref = { | 557 expected_outputs_ref = { |
| 531 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 558 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 532 'isolated_server'), | 559 'isolated_server'), |
| 533 'namespace': 'default-gzip', | 560 'namespace': 'default-gzip', |
| 534 'isolated': 'shard1_isolated' | 561 'isolated': 'shard1_isolated' |
| 535 } | 562 } |
| 536 | 563 |
| 537 self.assertEqual('COMPLETED', data['state']) | 564 self.assertEqual('COMPLETED', data['state']) |
| 538 self.assertEqual(expected_outputs_ref, data['outputs_ref']) | 565 self.assertEqual(expected_outputs_ref, data['outputs_ref']) |
| 566 self.assertIsNone(error) |
| 567 |
| 568 @mock.patch.object(swarming_util, '_SendRequestToServer', |
| 569 return_value=(None, {'code': 1, 'message': 'error'})) |
| 570 def testGetSwarmingTaskResultByIdError(self, _): |
| 571 data, error = swarming_util.GetSwarmingTaskResultById( |
| 572 'task_id', HttpClient()) |
| 573 self.assertEqual({}, data) |
| 574 self.assertIsNotNone(error) |
| 539 | 575 |
| 540 def testGetSwarmingTaskFailureLog(self): | 576 def testGetSwarmingTaskFailureLog(self): |
| 541 outputs_ref = { | 577 outputs_ref = { |
| 542 'isolatedserver': waterfall_config.GetSwarmingSettings().get( | 578 'isolatedserver': waterfall_config.GetSwarmingSettings().get( |
| 543 'isolated_server'), | 579 'isolated_server'), |
| 544 'namespace': 'default-gzip', | 580 'namespace': 'default-gzip', |
| 545 'isolated': 'shard1_isolated' | 581 'isolated': 'shard1_isolated' |
| 546 } | 582 } |
| 547 | 583 |
| 548 self.http_client._SetResponseForPostRequest('shard1_isolated') | 584 self.http_client._SetResponseForPostRequest('shard1_isolated') |
| 549 self.http_client._SetResponseForPostRequest('shard1_url') | 585 self.http_client._SetResponseForPostRequest('shard1_url') |
| 550 self.http_client._SetResponseForGetRequestIsolated( | 586 self.http_client._SetResponseForGetRequestIsolated( |
| 551 'https://%s/default-gzip/shard1' % ( | 587 'https://%s/default-gzip/shard1' % ( |
| 552 waterfall_config.GetSwarmingSettings().get('isolated_storage_url')), | 588 waterfall_config.GetSwarmingSettings().get('isolated_storage_url')), |
| 553 'shard1') | 589 'shard1') |
| 554 | 590 |
| 555 result = swarming_util.GetSwarmingTaskFailureLog( | 591 result, error = swarming_util.GetSwarmingTaskFailureLog( |
| 556 outputs_ref, self.http_client) | 592 outputs_ref, self.http_client) |
| 557 | 593 |
| 558 expected_result = json.loads(zlib.decompress( | 594 expected_result = json.loads(zlib.decompress( |
| 559 self.http_client._GetData('isolated', 'shard1'))) | 595 self.http_client._GetData('isolated', 'shard1'))) |
| 560 self.assertEqual(expected_result, result) | 596 self.assertEqual(expected_result, result) |
| 597 self.assertIsNone(error) |
| 561 | 598 |
| 562 def testRetrieveOutputJsonFileGetDirectly(self): | 599 def testRetrieveOutputJsonFileGetDirectly(self): |
| 563 output_json_content = ('{"content": "eJyrVkpLzMwpLUotVrKKVgpJLS4xV' | 600 output_json_content = ('{"content": "eJyrVkpLzMwpLUotVrKKVgpJLS4xV' |
| 564 'IrVUVAqS8zJTFGyUigpKk2tBQDr9wxZ"}') | 601 'IrVUVAqS8zJTFGyUigpKk2tBQDr9wxZ"}') |
| 565 | 602 |
| 566 failure_log = swarming_util._RetrieveOutputJsonFile( | 603 failure_log = swarming_util._RetrieveOutputJsonFile( |
| 567 output_json_content, self.http_client) | 604 output_json_content, self.http_client) |
| 568 | 605 |
| 569 expected_failure_log = { | 606 expected_failure_log = { |
| 570 'failures': ['Test1'], | 607 'failures': ['Test1'], |
| 571 'valid': True | 608 'valid': True |
| 572 } | 609 } |
| 573 | 610 |
| 574 self.assertEqual(expected_failure_log, failure_log) | 611 self.assertEqual(expected_failure_log, failure_log) |
| 575 | 612 |
| 576 def testGetTagValueInvalidTag(self): | 613 def testGetTagValueInvalidTag(self): |
| 577 tags = ['a:1', 'b:2'] | 614 tags = ['a:1', 'b:2'] |
| 578 self.assertIsNone(swarming_util.GetTagValue(tags, 'c')) | 615 self.assertIsNone(swarming_util.GetTagValue(tags, 'c')) |
| 579 | 616 |
| 580 def testGenerateIsolatedDataOutputsrefNone(self): | 617 def testGenerateIsolatedDataOutputsrefNone(self): |
| 581 self.assertEqual({}, swarming_util._GenerateIsolatedData(None)) | 618 self.assertEqual({}, swarming_util._GenerateIsolatedData(None)) |
| 582 | 619 |
| 583 def testFetchOutputJsonInfoFromIsolatedServerReturnNone(self): | 620 def testFetchOutputJsonInfoFromIsolatedServerReturnNone(self): |
| 584 self.assertIsNone(swarming_util._FetchOutputJsonInfoFromIsolatedServer( | 621 self.assertIsNone(swarming_util._FetchOutputJsonInfoFromIsolatedServer( |
| 585 None, self.http_client)) | 622 None, self.http_client)) |
| 623 |
| 624 @mock.patch.object( |
| 625 RetryHttpClient, 'Get', side_effect=ConnectionClosedError()) |
| 626 def testSendRequestToServerConnectionClosedError(self, _): |
| 627 content, error = swarming_util._SendRequestToServer('url', HttpClient()) |
| 628 self.assertIsNone(content) |
| 629 self.assertEqual( |
| 630 error['code'], swarming_util.URLFETCH_CONNECTION_CLOSED_ERROR) |
| 631 |
| 632 @mock.patch.object( |
| 633 RetryHttpClient, 'Get', side_effect=DeadlineExceededError()) |
| 634 def testSendRequestToServerDeadlineExceededError(self, _): |
| 635 content, error = swarming_util._SendRequestToServer('url', HttpClient()) |
| 636 self.assertIsNone(content) |
| 637 self.assertEqual( |
| 638 error['code'], swarming_util.URLFETCH_DEADLINE_EXCEEDED_ERROR) |
| 639 |
| 640 @mock.patch.object(RetryHttpClient, 'Get', side_effect=DownloadError()) |
| 641 def testSendRequestToServerDownloadError(self, _): |
| 642 content, error = swarming_util._SendRequestToServer('url', HttpClient()) |
| 643 self.assertIsNone(content) |
| 644 self.assertEqual(error['code'], swarming_util.URLFETCH_DOWNLOAD_ERROR) |
| OLD | NEW |