| 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 json | 5 import json |
| 6 import mock | 6 import mock |
| 7 import pickle | 7 import pickle |
| 8 import re | 8 import re |
| 9 | 9 |
| 10 import endpoints | |
| 11 from google.appengine.api import taskqueue | 10 from google.appengine.api import taskqueue |
| 12 import webtest | 11 import webtest |
| 13 | 12 |
| 14 from testing_utils import testing | 13 from testing_utils import testing |
| 15 | 14 |
| 16 from common.waterfall import failure_type | 15 from common.waterfall import failure_type |
| 17 import findit_api | 16 import findit_api |
| 17 from model import analysis_approach_type |
| 18 from model import analysis_status | 18 from model import analysis_status |
| 19 from model.flake.flake_analysis_request import FlakeAnalysisRequest | |
| 20 from model.wf_analysis import WfAnalysis | 19 from model.wf_analysis import WfAnalysis |
| 20 from model.wf_suspected_cl import WfSuspectedCL |
| 21 from model.wf_swarming_task import WfSwarmingTask | 21 from model.wf_swarming_task import WfSwarmingTask |
| 22 from model.wf_try_job import WfTryJob | 22 from model.wf_try_job import WfTryJob |
| 23 from waterfall import build_util |
| 24 from waterfall import suspected_cl_util |
| 23 from waterfall import waterfall_config | 25 from waterfall import waterfall_config |
| 24 | 26 |
| 25 | 27 |
| 26 class FinditApiTest(testing.EndpointsTestCase): | 28 class FinditApiTest(testing.EndpointsTestCase): |
| 27 api_service_cls = findit_api.FindItApi | 29 api_service_cls = findit_api.FindItApi |
| 28 | 30 |
| 29 def setUp(self): | 31 def setUp(self): |
| 30 super(FinditApiTest, self).setUp() | 32 super(FinditApiTest, self).setUp() |
| 31 self.taskqueue_requests = [] | 33 self.taskqueue_requests = [] |
| 32 def Mocked_taskqueue_add(**kwargs): | 34 def Mocked_taskqueue_add(**kwargs): |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 'builder_name': builder_name, | 215 'builder_name': builder_name, |
| 214 'build_number': build_number, | 216 'build_number': build_number, |
| 215 'step_name': 'a', | 217 'step_name': 'a', |
| 216 'is_sub_test': False, | 218 'is_sub_test': False, |
| 217 'first_known_failed_build_number': 23, | 219 'first_known_failed_build_number': 23, |
| 218 'suspected_cls': [ | 220 'suspected_cls': [ |
| 219 { | 221 { |
| 220 'repo_name': 'chromium', | 222 'repo_name': 'chromium', |
| 221 'revision': 'git_hash', | 223 'revision': 'git_hash', |
| 222 'commit_position': 123, | 224 'commit_position': 123, |
| 225 'analysis_approach': 'HEURISTIC' |
| 223 } | 226 } |
| 224 ], | 227 ], |
| 225 'analysis_approach': 'HEURISTIC', | 228 'analysis_approach': 'HEURISTIC', |
| 229 'try_job_status': 'FINISHED', |
| 230 'is_flaky_test': False |
| 226 }, | 231 }, |
| 227 ] | 232 ] |
| 228 | 233 |
| 229 analysis = WfAnalysis.Create(master_name, builder_name, build_number) | 234 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 230 analysis.status = analysis_status.RUNNING | 235 analysis.status = analysis_status.RUNNING |
| 231 analysis.result = analysis_result | 236 analysis.result = analysis_result |
| 232 analysis.put() | 237 analysis.put() |
| 233 | 238 |
| 234 response = self.call_api('AnalyzeBuildFailures', body=builds) | 239 response = self.call_api('AnalyzeBuildFailures', body=builds) |
| 235 self.assertEqual(200, response.status_int) | 240 self.assertEqual(200, response.status_int) |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 'builder_name': builder_name, | 337 'builder_name': builder_name, |
| 333 'build_number': build_number, | 338 'build_number': build_number, |
| 334 'step_name': 'test', | 339 'step_name': 'test', |
| 335 'is_sub_test': False, | 340 'is_sub_test': False, |
| 336 'first_known_failed_build_number': 3, | 341 'first_known_failed_build_number': 3, |
| 337 'suspected_cls': [ | 342 'suspected_cls': [ |
| 338 { | 343 { |
| 339 'repo_name': 'chromium', | 344 'repo_name': 'chromium', |
| 340 'revision': 'git_hash1', | 345 'revision': 'git_hash1', |
| 341 'commit_position': 234, | 346 'commit_position': 234, |
| 347 'analysis_approach': 'HEURISTIC' |
| 342 }, | 348 }, |
| 343 { | 349 { |
| 344 'repo_name': 'chromium', | 350 'repo_name': 'chromium', |
| 345 'revision': 'git_hash2', | 351 'revision': 'git_hash2', |
| 346 'commit_position': 288, | 352 'commit_position': 288, |
| 353 'analysis_approach': 'HEURISTIC' |
| 347 } | 354 } |
| 348 ], | 355 ], |
| 349 'analysis_approach': 'HEURISTIC', | 356 'analysis_approach': 'HEURISTIC', |
| 357 'is_flaky_test': False, |
| 358 'try_job_status': 'FINISHED' |
| 350 } | 359 } |
| 351 ] | 360 ] |
| 352 | 361 |
| 353 self._MockMasterIsSupported(supported=True) | 362 self._MockMasterIsSupported(supported=True) |
| 354 | 363 |
| 355 response = self.call_api('AnalyzeBuildFailures', body=builds) | 364 response = self.call_api('AnalyzeBuildFailures', body=builds) |
| 356 self.assertEqual(200, response.status_int) | 365 self.assertEqual(200, response.status_int) |
| 357 self.assertEqual(expected_results, response.json_body.get('results')) | 366 self.assertEqual(expected_results, response.json_body.get('results')) |
| 358 | 367 |
| 359 def testTryJobResultReturnedForCompileFailure(self): | 368 def testTryJobResultReturnedForCompileFailure(self): |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 'builder_name': builder_name, | 432 'builder_name': builder_name, |
| 424 'build_number': build_number, | 433 'build_number': build_number, |
| 425 'step_name': 'compile', | 434 'step_name': 'compile', |
| 426 'is_sub_test': False, | 435 'is_sub_test': False, |
| 427 'first_known_failed_build_number': 3, | 436 'first_known_failed_build_number': 3, |
| 428 'suspected_cls': [ | 437 'suspected_cls': [ |
| 429 { | 438 { |
| 430 'repo_name': 'chromium', | 439 'repo_name': 'chromium', |
| 431 'revision': 'r3', | 440 'revision': 'r3', |
| 432 'commit_position': 3, | 441 'commit_position': 3, |
| 442 'analysis_approach': 'TRY_JOB' |
| 433 }, | 443 }, |
| 434 ], | 444 ], |
| 435 'analysis_approach': 'TRY_JOB', | 445 'analysis_approach': 'TRY_JOB', |
| 446 'is_flaky_test': False, |
| 447 'try_job_status': 'FINISHED' |
| 436 } | 448 } |
| 437 ] | 449 ] |
| 438 | 450 |
| 439 self._MockMasterIsSupported(supported=True) | 451 self._MockMasterIsSupported(supported=True) |
| 440 | 452 |
| 441 response = self.call_api('AnalyzeBuildFailures', body=builds) | 453 response = self.call_api('AnalyzeBuildFailures', body=builds) |
| 442 self.assertEqual(200, response.status_int) | 454 self.assertEqual(200, response.status_int) |
| 443 self.assertEqual(expected_results, response.json_body.get('results')) | 455 self.assertEqual(expected_results, response.json_body.get('results')) |
| 444 | 456 |
| 445 def testTestLevelResultIsReturned(self): | 457 def testTryJobIsRunning(self): |
| 446 master_name = 'm' | 458 master_name = 'm' |
| 447 builder_name = 'b' | 459 builder_name = 'b' |
| 448 build_number = 5 | 460 build_number = 5 |
| 461 |
| 462 master_url = 'https://build.chromium.org/p/%s' % master_name |
| 463 builds = { |
| 464 'builds': [ |
| 465 { |
| 466 'master_url': master_url, |
| 467 'builder_name': builder_name, |
| 468 'build_number': build_number |
| 469 } |
| 470 ] |
| 471 } |
| 472 |
| 473 try_job = WfTryJob.Create(master_name, builder_name, 3) |
| 474 try_job.status = analysis_status.RUNNING |
| 475 try_job.put() |
| 476 |
| 477 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 478 analysis.status = analysis_status.COMPLETED |
| 479 analysis.build_failure_type = failure_type.COMPILE |
| 480 analysis.failure_result_map = { |
| 481 'compile': '/'.join([master_name, builder_name, '3']), |
| 482 } |
| 483 analysis.result = { |
| 484 'failures': [ |
| 485 { |
| 486 'step_name': 'compile', |
| 487 'first_failure': 3, |
| 488 'last_pass': 1, |
| 489 'suspected_cls': [ |
| 490 { |
| 491 'build_number': 3, |
| 492 'repo_name': 'chromium', |
| 493 'revision': 'git_hash2', |
| 494 'commit_position': 288, |
| 495 'score': 1, |
| 496 'hints': { |
| 497 'modify d/e/f.cc': 1, |
| 498 } |
| 499 } |
| 500 ] |
| 501 } |
| 502 ] |
| 503 } |
| 504 analysis.put() |
| 505 |
| 506 expected_results = [ |
| 507 { |
| 508 'master_url': master_url, |
| 509 'builder_name': builder_name, |
| 510 'build_number': build_number, |
| 511 'step_name': 'compile', |
| 512 'is_sub_test': False, |
| 513 'first_known_failed_build_number': 3, |
| 514 'suspected_cls': [ |
| 515 { |
| 516 'repo_name': 'chromium', |
| 517 'revision': 'git_hash2', |
| 518 'commit_position': 288, |
| 519 'analysis_approach': 'HEURISTIC' |
| 520 }, |
| 521 ], |
| 522 'analysis_approach': 'HEURISTIC', |
| 523 'is_flaky_test': False, |
| 524 'try_job_status': 'RUNNING' |
| 525 } |
| 526 ] |
| 527 |
| 528 self._MockMasterIsSupported(supported=True) |
| 529 |
| 530 response = self.call_api('AnalyzeBuildFailures', body=builds) |
| 531 self.assertEqual(200, response.status_int) |
| 532 self.assertEqual(expected_results, response.json_body.get('results')) |
| 533 |
| 534 def testTestIsFlaky(self): |
| 535 master_name = 'm' |
| 536 builder_name = 'b' |
| 537 build_number = 5 |
| 538 |
| 539 master_url = 'https://build.chromium.org/p/%s' % master_name |
| 540 builds = { |
| 541 'builds': [ |
| 542 { |
| 543 'master_url': master_url, |
| 544 'builder_name': builder_name, |
| 545 'build_number': build_number |
| 546 } |
| 547 ] |
| 548 } |
| 549 |
| 550 task = WfSwarmingTask.Create(master_name, builder_name, 3, 'b on platform') |
| 551 task.tests_statuses = { |
| 552 'Unittest3.Subtest1': { |
| 553 'total_run': 4, |
| 554 'SUCCESS': 2, |
| 555 'FAILURE': 2 |
| 556 } |
| 557 } |
| 558 task.put() |
| 559 |
| 560 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 561 analysis.status = analysis_status.COMPLETED |
| 562 analysis.failure_result_map = { |
| 563 'b on platform': { |
| 564 'Unittest3.Subtest1': '/'.join([master_name, builder_name, '3']), |
| 565 }, |
| 566 } |
| 567 analysis.result = { |
| 568 'failures': [ |
| 569 { |
| 570 'step_name': 'b on platform', |
| 571 'first_failure': 3, |
| 572 'last_pass': 2, |
| 573 'suspected_cls': [], |
| 574 'tests': [ |
| 575 { |
| 576 'test_name': 'Unittest3.Subtest1', |
| 577 'first_failure': 3, |
| 578 'last_pass': 2, |
| 579 'suspected_cls': [] |
| 580 } |
| 581 ] |
| 582 } |
| 583 ] |
| 584 } |
| 585 analysis.put() |
| 586 |
| 587 expected_results = [ |
| 588 { |
| 589 'master_url': master_url, |
| 590 'builder_name': builder_name, |
| 591 'build_number': build_number, |
| 592 'step_name': 'b on platform', |
| 593 'is_sub_test': True, |
| 594 'test_name': 'Unittest3.Subtest1', |
| 595 'first_known_failed_build_number': 3, |
| 596 'analysis_approach': 'HEURISTIC', |
| 597 'is_flaky_test': True, |
| 598 'try_job_status': 'FINISHED' |
| 599 } |
| 600 ] |
| 601 |
| 602 self._MockMasterIsSupported(supported=True) |
| 603 |
| 604 response = self.call_api('AnalyzeBuildFailures', body=builds) |
| 605 self.assertEqual(200, response.status_int) |
| 606 self.assertEqual(expected_results, response.json_body.get('results')) |
| 607 |
| 608 @mock.patch.object(suspected_cl_util, 'GetSuspectedCLConfidenceScore') |
| 609 def testTestLevelResultIsReturned(self, mock_fn): |
| 610 master_name = 'm' |
| 611 builder_name = 'b' |
| 612 build_number = 5 |
| 449 | 613 |
| 450 master_url = 'https://build.chromium.org/p/%s' % master_name | 614 master_url = 'https://build.chromium.org/p/%s' % master_name |
| 451 builds = { | 615 builds = { |
| 452 'builds': [ | 616 'builds': [ |
| 453 { | 617 { |
| 454 'master_url': master_url, | 618 'master_url': master_url, |
| 455 'builder_name': builder_name, | 619 'builder_name': builder_name, |
| 456 'build_number': build_number | 620 'build_number': build_number |
| 457 } | 621 } |
| 458 ] | 622 ] |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 587 'first_failure': 4, | 751 'first_failure': 4, |
| 588 'last_pass': 2, | 752 'last_pass': 2, |
| 589 'suspected_cls': [] | 753 'suspected_cls': [] |
| 590 } | 754 } |
| 591 ] | 755 ] |
| 592 } | 756 } |
| 593 ] | 757 ] |
| 594 } | 758 } |
| 595 analysis.put() | 759 analysis.put() |
| 596 | 760 |
| 761 suspected_cl_42 = WfSuspectedCL.Create('chromium', 'r4_2', 42) |
| 762 suspected_cl_42.builds = { |
| 763 build_util.CreateBuildId(master_name, builder_name, 4): { |
| 764 'approaches': [analysis_approach_type.TRY_JOB] |
| 765 } |
| 766 } |
| 767 suspected_cl_42.put() |
| 768 |
| 769 suspected_cl_21 = WfSuspectedCL.Create('chromium', 'r2_1', None) |
| 770 suspected_cl_21.builds = { |
| 771 build_util.CreateBuildId(master_name, builder_name, build_number): { |
| 772 'approaches': [analysis_approach_type.HEURISTIC], |
| 773 'top_score': 5 |
| 774 } |
| 775 } |
| 776 suspected_cl_21.put() |
| 777 |
| 778 def confidence_side_effect(_, build_info): |
| 779 if build_info.get('top_score'): |
| 780 return 90 |
| 781 return 98 |
| 782 |
| 783 mock_fn.side_effect = confidence_side_effect |
| 784 |
| 597 expected_results = [ | 785 expected_results = [ |
| 598 { | 786 { |
| 599 'master_url': master_url, | 787 'master_url': master_url, |
| 600 'builder_name': builder_name, | 788 'builder_name': builder_name, |
| 601 'build_number': build_number, | 789 'build_number': build_number, |
| 602 'step_name': 'a', | 790 'step_name': 'a', |
| 603 'is_sub_test': False, | 791 'is_sub_test': False, |
| 604 'first_known_failed_build_number': 4, | 792 'first_known_failed_build_number': 4, |
| 605 'suspected_cls': [ | 793 'suspected_cls': [ |
| 606 { | 794 { |
| 607 'repo_name': 'chromium', | 795 'repo_name': 'chromium', |
| 608 'revision': 'r4_2', | 796 'revision': 'r4_2', |
| 609 'commit_position': 42, | 797 'commit_position': 42, |
| 798 'confidence': 98, |
| 799 'analysis_approach': 'TRY_JOB' |
| 610 } | 800 } |
| 611 ], | 801 ], |
| 612 'analysis_approach': 'TRY_JOB', | 802 'analysis_approach': 'TRY_JOB', |
| 803 'is_flaky_test': False, |
| 804 'try_job_status': 'FINISHED' |
| 613 }, | 805 }, |
| 614 { | 806 { |
| 615 'master_url': master_url, | 807 'master_url': master_url, |
| 616 'builder_name': builder_name, | 808 'builder_name': builder_name, |
| 617 'build_number': build_number, | 809 'build_number': build_number, |
| 618 'step_name': 'b on platform', | 810 'step_name': 'b on platform', |
| 619 'is_sub_test': True, | 811 'is_sub_test': True, |
| 620 'test_name': 'Unittest1.Subtest1', | 812 'test_name': 'Unittest1.Subtest1', |
| 621 'first_known_failed_build_number': 3, | 813 'first_known_failed_build_number': 3, |
| 622 'suspected_cls': [ | 814 'suspected_cls': [ |
| 623 { | 815 { |
| 624 'repo_name': 'chromium', | 816 'repo_name': 'chromium', |
| 625 'revision': 'r2_1', | 817 'revision': 'r2_1', |
| 818 'confidence': 90, |
| 819 'analysis_approach': 'HEURISTIC' |
| 626 } | 820 } |
| 627 ], | 821 ], |
| 628 'analysis_approach': 'HEURISTIC', | 822 'analysis_approach': 'HEURISTIC', |
| 823 'is_flaky_test': False, |
| 824 'try_job_status': 'FINISHED' |
| 629 }, | 825 }, |
| 630 { | 826 { |
| 631 'master_url': master_url, | 827 'master_url': master_url, |
| 632 'builder_name': builder_name, | 828 'builder_name': builder_name, |
| 633 'build_number': build_number, | 829 'build_number': build_number, |
| 634 'step_name': 'b on platform', | 830 'step_name': 'b on platform', |
| 635 'is_sub_test': True, | 831 'is_sub_test': True, |
| 636 'test_name': 'Unittest2.Subtest1', | 832 'test_name': 'Unittest2.Subtest1', |
| 637 'first_known_failed_build_number': 4, | 833 'first_known_failed_build_number': 4, |
| 638 'suspected_cls': [ | 834 'suspected_cls': [ |
| 639 { | 835 { |
| 640 'repo_name': 'chromium', | 836 'repo_name': 'chromium', |
| 641 'revision': 'r2_1', | 837 'revision': 'r2_1', |
| 838 'confidence': 90, |
| 839 'analysis_approach': 'HEURISTIC' |
| 642 } | 840 } |
| 643 ], | 841 ], |
| 644 'analysis_approach': 'HEURISTIC', | 842 'analysis_approach': 'HEURISTIC', |
| 843 'is_flaky_test': False, |
| 844 'try_job_status': 'FINISHED' |
| 645 }, | 845 }, |
| 646 { | 846 { |
| 647 'master_url': master_url, | 847 'master_url': master_url, |
| 648 'builder_name': builder_name, | 848 'builder_name': builder_name, |
| 649 'build_number': build_number, | 849 'build_number': build_number, |
| 650 'step_name': 'b on platform', | 850 'step_name': 'b on platform', |
| 651 'is_sub_test': True, | 851 'is_sub_test': True, |
| 652 'test_name': 'Unittest3.Subtest1', | 852 'test_name': 'Unittest3.Subtest1', |
| 653 'first_known_failed_build_number': 4, | 853 'first_known_failed_build_number': 4, |
| 654 'suspected_cls': [ | 854 'suspected_cls': [ |
| 655 { | 855 { |
| 656 'repo_name': 'chromium', | 856 'repo_name': 'chromium', |
| 657 'revision': 'r4_10', | 857 'revision': 'r4_10', |
| 658 'commit_position': 410, | 858 'commit_position': 410, |
| 859 'analysis_approach': 'TRY_JOB' |
| 659 } | 860 } |
| 660 ], | 861 ], |
| 661 'analysis_approach': 'TRY_JOB', | 862 'analysis_approach': 'TRY_JOB', |
| 863 'is_flaky_test': False, |
| 864 'try_job_status': 'FINISHED' |
| 662 } | 865 } |
| 663 ] | 866 ] |
| 664 | 867 |
| 665 self._MockMasterIsSupported(supported=True) | 868 self._MockMasterIsSupported(supported=True) |
| 666 | 869 |
| 667 response = self.call_api('AnalyzeBuildFailures', body=builds) | 870 response = self.call_api('AnalyzeBuildFailures', body=builds) |
| 668 self.assertEqual(200, response.status_int) | 871 self.assertEqual(200, response.status_int) |
| 669 self.assertEqual(expected_results, response.json_body.get('results')) | 872 self.assertEqual(expected_results, response.json_body.get('results')) |
| 670 | 873 |
| 671 def testAnalysisRequestQueuedAsExpected(self): | 874 def testAnalysisRequestQueuedAsExpected(self): |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 784 self.assertFalse(request.is_step) | 987 self.assertFalse(request.is_step) |
| 785 self.assertEqual(123, request.bug_id) | 988 self.assertEqual(123, request.bug_id) |
| 786 self.assertEqual(1, len(request.build_steps)) | 989 self.assertEqual(1, len(request.build_steps)) |
| 787 self.assertEqual('m', request.build_steps[0].master_name) | 990 self.assertEqual('m', request.build_steps[0].master_name) |
| 788 self.assertEqual('b', request.build_steps[0].builder_name) | 991 self.assertEqual('b', request.build_steps[0].builder_name) |
| 789 self.assertEqual(456, request.build_steps[0].build_number) | 992 self.assertEqual(456, request.build_steps[0].build_number) |
| 790 self.assertEqual('name (with patch) on Windows-7-SP1', | 993 self.assertEqual('name (with patch) on Windows-7-SP1', |
| 791 request.build_steps[0].step_name) | 994 request.build_steps[0].step_name) |
| 792 self.assertEqual('test@chromium.org', user_email) | 995 self.assertEqual('test@chromium.org', user_email) |
| 793 self.assertTrue(is_admin) | 996 self.assertTrue(is_admin) |
| OLD | NEW |