| 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 datetime | 5 import datetime |
| 6 import json | 6 import json |
| 7 import mock | 7 import mock |
| 8 import urllib2 | 8 import urllib2 |
| 9 | 9 |
| 10 from google.appengine.datastore import datastore_stub_util | 10 from google.appengine.datastore import datastore_stub_util |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 | 73 |
| 74 # Ignore steps that are duplicating error in another step. | 74 # Ignore steps that are duplicating error in another step. |
| 75 {'results': [2], 'name': 'steps', 'text': ['bar10']}, | 75 {'results': [2], 'name': 'steps', 'text': ['bar10']}, |
| 76 {'results': [2], 'name': '[swarming] foo7', 'text': ['bar11']}, | 76 {'results': [2], 'name': '[swarming] foo7', 'text': ['bar11']}, |
| 77 {'results': [2], 'name': 'presubmit', 'text': ['bar12']}, | 77 {'results': [2], 'name': 'presubmit', 'text': ['bar12']}, |
| 78 {'results': [2], 'name': 'recipe failure reason', 'text': ['bar12a']}, | 78 {'results': [2], 'name': 'recipe failure reason', 'text': ['bar12a']}, |
| 79 {'results': [2], 'name': 'test results', 'text': ['bar12b']}, | 79 {'results': [2], 'name': 'test results', 'text': ['bar12b']}, |
| 80 {'results': [2], 'name': 'Uncaught Exception', 'text': ['bar12c']}, | 80 {'results': [2], 'name': 'Uncaught Exception', 'text': ['bar12c']}, |
| 81 {'results': [2], 'name': 'bot_update', 'text': ['bot_update PATCH FAILED']}, | 81 {'results': [2], 'name': 'bot_update', 'text': ['bot_update PATCH FAILED']}, |
| 82 | 82 |
| 83 # Detect infra-failure for 'Patch failure', but igore normal error. |
| 84 {'results': [4], 'name': 'Patch failure', 'text': ['Patch failure']}, |
| 85 {'results': [2], 'name': 'Patch failure', 'text': ['Patch failure']}, |
| 86 |
| 83 # Only count first step (with patch) and ignore summary step. | 87 # Only count first step (with patch) and ignore summary step. |
| 84 {'results': [2], 'name': 'foo8 xx (with patch)', 'text': ['bar13']}, | 88 {'results': [2], 'name': 'foo8 xx (with patch)', 'text': ['bar13']}, |
| 85 {'results': [0], 'name': 'foo8 xx (without patch)', 'text': ['bar14']}, | 89 {'results': [0], 'name': 'foo8 xx (without patch)', 'text': ['bar14']}, |
| 86 {'results': [2], 'name': 'foo8 xx (retry summary)', 'text': ['bar15']}, | 90 {'results': [2], 'name': 'foo8 xx (retry summary)', 'text': ['bar15']}, |
| 87 ] | 91 ] |
| 88 }) | 92 }) |
| 89 | 93 |
| 90 | 94 |
| 91 class MockComment(object): | 95 class MockComment(object): |
| 92 def __init__(self, created, author, comment=None): | 96 def __init__(self, created, author, comment=None): |
| (...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 672 | 676 |
| 673 def test_get_flaky_run_reason(self): | 677 def test_get_flaky_run_reason(self): |
| 674 now = datetime.datetime.utcnow() | 678 now = datetime.datetime.utcnow() |
| 675 br_f, br_s = self._create_build_runs(now - datetime.timedelta(hours=1), now) | 679 br_f, br_s = self._create_build_runs(now - datetime.timedelta(hours=1), now) |
| 676 | 680 |
| 677 urlfetch_mock = mock.Mock(side_effect = [ | 681 urlfetch_mock = mock.Mock(side_effect = [ |
| 678 # JSON results for the build. | 682 # JSON results for the build. |
| 679 mock.Mock(status_code=200, content=TEST_BUILDBOT_JSON_REPLY), | 683 mock.Mock(status_code=200, content=TEST_BUILDBOT_JSON_REPLY), |
| 680 # JSON results for step "foo1". | 684 # JSON results for step "foo1". |
| 681 mock.Mock(status_code=200, content=TEST_TEST_RESULTS_REPLY), | 685 mock.Mock(status_code=200, content=TEST_TEST_RESULTS_REPLY), |
| 686 # JSON results for step "Patch failure". |
| 687 mock.Mock(status_code=404), |
| 682 # For step "foo8 xx (with patch)", something failed while parsing JSON, | 688 # For step "foo8 xx (with patch)", something failed while parsing JSON, |
| 683 # step text ("bar13") should be reported as flake. | 689 # step text ("bar13") should be reported as flake. |
| 684 Exception(), | 690 Exception(), |
| 685 ]) | 691 ]) |
| 686 | 692 |
| 687 # We also create one Flake to test that it is correctly updated. Other Flake | 693 # We also create one Flake to test that it is correctly updated. Other Flake |
| 688 # entities will be created automatically. | 694 # entities will be created automatically. |
| 689 Flake(id='foo2', name='foo2', occurrences=[], | 695 Flake(id='foo2', name='foo2', occurrences=[], |
| 690 last_time_seen=datetime.datetime.min).put() | 696 last_time_seen=datetime.datetime.min).put() |
| 691 | 697 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 708 mock.call( | 714 mock.call( |
| 709 'http://build.chromium.org/p/test.master/json/builders/test-builder/' | 715 'http://build.chromium.org/p/test.master/json/builders/test-builder/' |
| 710 'builds/100'), | 716 'builds/100'), |
| 711 # Verify that we've used correct URLs to retrieve test-results GTest JSON. | 717 # Verify that we've used correct URLs to retrieve test-results GTest JSON. |
| 712 mock.call( | 718 mock.call( |
| 713 'http://test-results.appspot.com/testfile?builder=test-builder&' | 719 'http://test-results.appspot.com/testfile?builder=test-builder&' |
| 714 'name=full_results.json&master=test.master&testtype=foo1&' | 720 'name=full_results.json&master=test.master&testtype=foo1&' |
| 715 'buildnumber=100'), | 721 'buildnumber=100'), |
| 716 mock.call( | 722 mock.call( |
| 717 'http://test-results.appspot.com/testfile?builder=test-builder&' | 723 'http://test-results.appspot.com/testfile?builder=test-builder&' |
| 724 'name=full_results.json&master=test.master&testtype=Patch&' |
| 725 'buildnumber=100'), |
| 726 mock.call( |
| 727 'http://test-results.appspot.com/testfile?builder=test-builder&' |
| 718 'name=full_results.json&master=test.master&' | 728 'name=full_results.json&master=test.master&' |
| 719 'testtype=foo8%20%28with%20patch%29&buildnumber=100')]) | 729 'testtype=foo8%20%28with%20patch%29&buildnumber=100')]) |
| 720 | 730 |
| 721 # Expected flakes to be found: list of (step_name, test_name). | 731 # Expected flakes to be found: list of (step_name, test_name). |
| 722 expected_flakes = [ | 732 # We compare sets below, because order of entities returned by datastore |
| 733 # doesn't have to be same as steps above. |
| 734 expected_flakes = set([ |
| 723 ('foo1', 'test2.a'), | 735 ('foo1', 'test2.a'), |
| 724 ('foo1', 'test2.d'), | 736 ('foo1', 'test2.d'), |
| 725 ('foo2', 'foo2'), | 737 ('foo2', 'foo2'), |
| 726 ('foo8 xx (with patch)', 'foo8 (with patch)'), | 738 ('foo8 xx (with patch)', 'foo8 (with patch)'), |
| 727 ] | 739 ('Patch failure', 'Patch'), |
| 740 ]) |
| 728 | 741 |
| 729 flake_occurrences = flaky_run.flakes | 742 flake_occurrences = flaky_run.flakes |
| 730 self.assertEqual(len(flake_occurrences), len(expected_flakes)) | 743 self.assertEqual(len(flake_occurrences), len(expected_flakes)) |
| 731 actual_flake_occurrences = [ | 744 actual_flake_occurrences = set([ |
| 732 (fo.name, fo.failure) for fo in flake_occurrences] | 745 (fo.name, fo.failure) for fo in flake_occurrences]) |
| 733 self.assertEqual(expected_flakes, actual_flake_occurrences) | 746 self.assertEqual(expected_flakes, actual_flake_occurrences) |
| 734 | 747 |
| 735 # We compare sets below, because order of flakes returned by datastore | |
| 736 # doesn't have to be same as steps above. | |
| 737 flakes = Flake.query().fetch() | 748 flakes = Flake.query().fetch() |
| 738 self.assertEqual(len(flakes), len(expected_flakes)) | 749 self.assertEqual(len(flakes), len(expected_flakes)) |
| 739 expected_flake_names = set([ef[1] for ef in expected_flakes]) | 750 expected_flake_names = set([ef[1] for ef in expected_flakes]) |
| 740 actual_flake_names = set([f.name for f in flakes]) | 751 actual_flake_names = set([f.name for f in flakes]) |
| 741 self.assertEqual(expected_flake_names, actual_flake_names) | 752 self.assertEqual(expected_flake_names, actual_flake_names) |
| 742 | 753 |
| 743 for flake in flakes: | 754 for flake in flakes: |
| 744 self.assertEqual(flake.occurrences, [flaky_run.key]) | 755 self.assertEqual(flake.occurrences, [flaky_run.key]) |
| 745 | 756 |
| 746 def test_flattens_tests_correctly(self): | 757 def test_flattens_tests_correctly(self): |
| 747 passed, failed, skipped = CreateFlakyRun._flatten_tests( | 758 passed, failed, skipped = CreateFlakyRun._flatten_tests( |
| 748 json.loads(TEST_TEST_RESULTS_REPLY)['tests'], '/') | 759 json.loads(TEST_TEST_RESULTS_REPLY)['tests'], '/') |
| 749 self.assertEqual(set(passed), set(['test1'])) | 760 self.assertEqual(set(passed), set(['test1'])) |
| 750 self.assertEqual(set(failed), set(['test2/a', 'test2/d'])) | 761 self.assertEqual(set(failed), set(['test2/a', 'test2/d'])) |
| 751 self.assertEqual(set(skipped), set(['test2/b'])) | 762 self.assertEqual(set(skipped), set(['test2/b'])) |
| OLD | NEW |