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

Side by Side Diff: telemetry/telemetry/internal/story_runner_unittest.py

Issue 2074183002: [telemetry] Try to print browser logs and capture a screenshot upon story failure (Closed) Base URL: git@github.com:catapult-project/catapult.git@master
Patch Set: Add DumpStateUponFailure to _FakeBrowser Created 4 years, 6 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
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 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 os 5 import os
6 import StringIO 6 import StringIO
7 import sys 7 import sys
8 import unittest 8 import unittest
9 9
10 from catapult_base import cloud_storage # pylint: disable=import-error 10 from catapult_base import cloud_storage # pylint: disable=import-error
11 11
12 from telemetry import benchmark 12 from telemetry import benchmark
13 from telemetry.core import exceptions 13 from telemetry.core import exceptions
14 from telemetry.core import util 14 from telemetry.core import util
15 from telemetry import decorators 15 from telemetry import decorators
16 from telemetry.internal.actions import page_action
17 from telemetry.internal.results import page_test_results
16 from telemetry.internal.results import results_options 18 from telemetry.internal.results import results_options
17 from telemetry.internal import story_runner 19 from telemetry.internal import story_runner
18 from telemetry.internal.util import exception_formatter as ex_formatter_module 20 from telemetry.internal.util import exception_formatter as ex_formatter_module
19 from telemetry.page import page as page_module 21 from telemetry.page import page as page_module
20 from telemetry.page import legacy_page_test 22 from telemetry.page import legacy_page_test
21 from telemetry import story as story_module 23 from telemetry import story as story_module
22 from telemetry.testing import options_for_unittests 24 from telemetry.testing import options_for_unittests
23 from telemetry.testing import system_stub 25 from telemetry.testing import system_stub
24 import mock 26 import mock
27 from telemetry.value import failure
25 from telemetry.value import improvement_direction 28 from telemetry.value import improvement_direction
26 from telemetry.value import list_of_scalar_values 29 from telemetry.value import list_of_scalar_values
27 from telemetry.value import scalar 30 from telemetry.value import scalar
31 from telemetry.value import skip
28 from telemetry.value import summary as summary_module 32 from telemetry.value import summary as summary_module
29 from telemetry.web_perf import story_test 33 from telemetry.web_perf import story_test
30 from telemetry.web_perf import timeline_based_measurement 34 from telemetry.web_perf import timeline_based_measurement
31 from telemetry.wpr import archive_info 35 from telemetry.wpr import archive_info
32 36
33 # This linter complains if we define classes nested inside functions. 37 # This linter complains if we define classes nested inside functions.
34 # pylint: disable=bad-super-call 38 # pylint: disable=bad-super-call
35 39
36 40
37 class FakePlatform(object): 41 class FakePlatform(object):
(...skipping 27 matching lines...) Expand all
65 69
66 def RunStory(self, results): 70 def RunStory(self, results):
67 raise NotImplementedError 71 raise NotImplementedError
68 72
69 def DidRunStory(self, results): 73 def DidRunStory(self, results):
70 pass 74 pass
71 75
72 def TearDownState(self): 76 def TearDownState(self):
73 pass 77 pass
74 78
79 def DumpStateUponFailure(self, story, results):
80 pass
81
75 82
76 class TestSharedPageState(TestSharedState): 83 class TestSharedPageState(TestSharedState):
77 def RunStory(self, results): 84 def RunStory(self, results):
78 self._test.RunPage(self._current_story, None, results) 85 self._test.RunPage(self._current_story, None, results)
79 86
80 87
81 class FooStoryState(TestSharedPageState): 88 class FooStoryState(TestSharedPageState):
82 pass 89 pass
83 90
84 91
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 151
145 152
146 def GetNumberOfSuccessfulPageRuns(results): 153 def GetNumberOfSuccessfulPageRuns(results):
147 return len([run for run in results.all_page_runs if run.ok or run.skipped]) 154 return len([run for run in results.all_page_runs if run.ok or run.skipped])
148 155
149 156
150 class TestOnlyException(Exception): 157 class TestOnlyException(Exception):
151 pass 158 pass
152 159
153 160
161 class FailureValueMatcher(object):
162
163 def __init__(self, expected_exception_message):
164 self._expected_exception_message = expected_exception_message
165
166 def __eq__(self, other):
167 return (isinstance(other, failure.FailureValue) and
168 other.exc_info[1].message == self._expected_exception_message)
169
170
171 class SkipValueMatcher(object):
172
173 def __eq__(self, other):
174 return isinstance(other, skip.SkipValue)
175
176
154 class StoryRunnerTest(unittest.TestCase): 177 class StoryRunnerTest(unittest.TestCase):
155 178
156 def setUp(self): 179 def setUp(self):
157 self.fake_stdout = StringIO.StringIO() 180 self.fake_stdout = StringIO.StringIO()
158 self.actual_stdout = sys.stdout 181 self.actual_stdout = sys.stdout
159 sys.stdout = self.fake_stdout 182 sys.stdout = self.fake_stdout
160 self.options = _GetOptionForUnittest() 183 self.options = _GetOptionForUnittest()
161 self.results = results_options.CreateResults( 184 self.results = results_options.CreateResults(
162 EmptyMetadataForTest(), self.options) 185 EmptyMetadataForTest(), self.options)
163 self._story_runner_logging_stub = None 186 self._story_runner_logging_stub = None
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 523
501 unit_test_events = [] # track what was called when 524 unit_test_events = [] # track what was called when
502 class DidRunTestError(Exception): 525 class DidRunTestError(Exception):
503 pass 526 pass
504 527
505 class TestTearDownSharedState(TestSharedPageState): 528 class TestTearDownSharedState(TestSharedPageState):
506 def TearDownState(self): 529 def TearDownState(self):
507 unit_test_events.append('tear-down-state') 530 unit_test_events.append('tear-down-state')
508 raise DidRunTestError 531 raise DidRunTestError
509 532
533 def DumpStateUponFailure(self, story, results):
534 unit_test_events.append('dump-state')
535
510 536
511 class Test(legacy_page_test.LegacyPageTest): 537 class Test(legacy_page_test.LegacyPageTest):
512 def __init__(self, *args): 538 def __init__(self, *args):
513 super(Test, self).__init__(*args) 539 super(Test, self).__init__(*args)
514 self.run_count = 0 540 self.run_count = 0
515 541
516 def RunPage(self, *_): 542 def RunPage(self, *_):
517 old_run_count = self.run_count 543 old_run_count = self.run_count
518 self.run_count += 1 544 self.run_count += 1
519 if old_run_count == 0: 545 if old_run_count == 0:
520 unit_test_events.append('app-crash') 546 unit_test_events.append('app-crash')
521 raise exceptions.AppCrashException 547 raise exceptions.AppCrashException
522 548
523 def ValidateAndMeasurePage(self, page, tab, results): 549 def ValidateAndMeasurePage(self, page, tab, results):
524 pass 550 pass
525 551
526 story_set.AddStory(DummyLocalStory(TestTearDownSharedState)) 552 story_set.AddStory(DummyLocalStory(TestTearDownSharedState))
527 story_set.AddStory(DummyLocalStory(TestTearDownSharedState)) 553 story_set.AddStory(DummyLocalStory(TestTearDownSharedState))
528 test = Test() 554 test = Test()
529 555
530 with self.assertRaises(DidRunTestError): 556 with self.assertRaises(DidRunTestError):
531 story_runner.Run( 557 story_runner.Run(
532 test, story_set, self.options, self.results) 558 test, story_set, self.options, self.results)
533 self.assertEqual(['app-crash', 'tear-down-state'], unit_test_events) 559 self.assertEqual(['app-crash', 'dump-state', 'tear-down-state'],
560 unit_test_events)
534 # The AppCrashException gets added as a failure. 561 # The AppCrashException gets added as a failure.
535 self.assertEquals(1, len(self.results.failures)) 562 self.assertEquals(1, len(self.results.failures))
536 563
537 def testPagesetRepeat(self): 564 def testPagesetRepeat(self):
538 story_set = story_module.StorySet() 565 story_set = story_module.StorySet()
539 566
540 # TODO(eakuefner): Factor this out after flattening page ref in Value 567 # TODO(eakuefner): Factor this out after flattening page ref in Value
541 blank_story = DummyLocalStory(TestSharedPageState, name='blank') 568 blank_story = DummyLocalStory(TestSharedPageState, name='blank')
542 green_story = DummyLocalStory(TestSharedPageState, name='green') 569 green_story = DummyLocalStory(TestSharedPageState, name='green')
543 story_set.AddStory(blank_story) 570 story_set.AddStory(blank_story)
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
671 698
672 def DidRunStory(self, results): 699 def DidRunStory(self, results):
673 pass 700 pass
674 701
675 def CanRunStory(self, story): 702 def CanRunStory(self, story):
676 return True 703 return True
677 704
678 def TearDownState(self): 705 def TearDownState(self):
679 pass 706 pass
680 707
708 def DumpStateUponFailure(self, story, results):
709 pass
710
681 class FailingStory(story_module.Story): 711 class FailingStory(story_module.Story):
682 def __init__(self): 712 def __init__(self):
683 super(FailingStory, self).__init__( 713 super(FailingStory, self).__init__(
684 shared_state_class=SimpleSharedState, 714 shared_state_class=SimpleSharedState,
685 is_local=True) 715 is_local=True)
686 self.was_run = False 716 self.was_run = False
687 717
688 def Run(self, shared_state): 718 def Run(self, shared_state):
689 self.was_run = True 719 self.was_run = True
690 raise legacy_page_test.Failure 720 raise legacy_page_test.Failure
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
723 num_failing_stories=5, runner_max_failures=3, 753 num_failing_stories=5, runner_max_failures=3,
724 options_max_failures=None, expected_num_failures=4) 754 options_max_failures=None, expected_num_failures=4)
725 755
726 def testMaxFailuresOption(self): 756 def testMaxFailuresOption(self):
727 # Runs up to max_failures+1 failing tests before stopping, since 757 # Runs up to max_failures+1 failing tests before stopping, since
728 # every tests after max_failures failures have been encountered 758 # every tests after max_failures failures have been encountered
729 # may all be passing. 759 # may all be passing.
730 self._testMaxFailuresOptionIsRespectedAndOverridable( 760 self._testMaxFailuresOptionIsRespectedAndOverridable(
731 num_failing_stories=5, runner_max_failures=3, 761 num_failing_stories=5, runner_max_failures=3,
732 options_max_failures=1, expected_num_failures=2) 762 options_max_failures=1, expected_num_failures=2)
763
764 def _CreateErrorProcessingMock(self, method_exceptions=None,
765 legacy_test=False):
766 if legacy_test:
767 test_class = legacy_page_test.LegacyPageTest
768 else:
769 test_class = story_test.StoryTest
770
771 root_mock = mock.NonCallableMock(
772 story=mock.NonCallableMagicMock(story_module.Story),
773 results=mock.NonCallableMagicMock(page_test_results.PageTestResults),
774 test=mock.NonCallableMagicMock(test_class),
775 state=mock.NonCallableMagicMock(
776 story_module.SharedState,
777 CanRunStory=mock.Mock(return_value=True)))
778
779 if method_exceptions:
780 root_mock.configure_mock(**{
781 path + '.side_effect': exception
782 for path, exception in method_exceptions.iteritems()})
783
784 return root_mock
785
786 def testRunStoryAndProcessErrorIfNeeded_success(self):
787 root_mock = self._CreateErrorProcessingMock()
788
789 story_runner._RunStoryAndProcessErrorIfNeeded(
790 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
791
792 self.assertEquals(root_mock.method_calls, [
793 mock.call.test.WillRunStory(root_mock.state.platform),
794 mock.call.state.WillRunStory(root_mock.story),
795 mock.call.state.CanRunStory(root_mock.story),
796 mock.call.state.RunStory(root_mock.results),
797 mock.call.test.Measure(root_mock.state.platform, root_mock.results),
798 mock.call.state.DidRunStory(root_mock.results),
799 mock.call.test.DidRunStory(root_mock.state.platform)
800 ])
801
802 def testRunStoryAndProcessErrorIfNeeded_successLegacy(self):
803 root_mock = self._CreateErrorProcessingMock(legacy_test=True)
804
805 story_runner._RunStoryAndProcessErrorIfNeeded(
806 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
807
808 self.assertEquals(root_mock.method_calls, [
809 mock.call.state.WillRunStory(root_mock.story),
810 mock.call.state.CanRunStory(root_mock.story),
811 mock.call.state.RunStory(root_mock.results),
812 mock.call.state.DidRunStory(root_mock.results),
813 mock.call.test.DidRunPage(root_mock.state.platform)
814 ])
815
816 def testRunStoryAndProcessErrorIfNeeded_tryTimeout(self):
817 root_mock = self._CreateErrorProcessingMock(method_exceptions={
818 'state.WillRunStory': exceptions.TimeoutException('foo')
819 })
820
821 story_runner._RunStoryAndProcessErrorIfNeeded(
822 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
823
824 self.assertEquals(root_mock.method_calls, [
825 mock.call.test.WillRunStory(root_mock.state.platform),
826 mock.call.state.WillRunStory(root_mock.story),
827 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
828 mock.call.results.AddValue(FailureValueMatcher('foo')),
829 mock.call.state.DidRunStory(root_mock.results),
830 mock.call.test.DidRunStory(root_mock.state.platform)
831 ])
832
833 def testRunStoryAndProcessErrorIfNeeded_tryError(self):
834 root_mock = self._CreateErrorProcessingMock(method_exceptions={
835 'state.CanRunStory': exceptions.Error('foo')
836 })
837
838 with self.assertRaisesRegexp(exceptions.Error, 'foo'):
839 story_runner._RunStoryAndProcessErrorIfNeeded(
840 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
841
842 self.assertEquals(root_mock.method_calls, [
843 mock.call.test.WillRunStory(root_mock.state.platform),
844 mock.call.state.WillRunStory(root_mock.story),
845 mock.call.state.CanRunStory(root_mock.story),
846 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
847 mock.call.results.AddValue(FailureValueMatcher('foo')),
848 mock.call.state.DidRunStory(root_mock.results),
849 mock.call.test.DidRunStory(root_mock.state.platform)
850 ])
851
852 def testRunStoryAndProcessErrorIfNeeded_tryUnsupportedAction(self):
853 root_mock = self._CreateErrorProcessingMock(method_exceptions={
854 'state.RunStory': page_action.PageActionNotSupported('foo')
855 })
856
857 story_runner._RunStoryAndProcessErrorIfNeeded(
858 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
859
860 self.assertEquals(root_mock.method_calls, [
861 mock.call.test.WillRunStory(root_mock.state.platform),
862 mock.call.state.WillRunStory(root_mock.story),
863 mock.call.state.CanRunStory(root_mock.story),
864 mock.call.state.RunStory(root_mock.results),
865 mock.call.results.AddValue(SkipValueMatcher()),
866 mock.call.state.DidRunStory(root_mock.results),
867 mock.call.test.DidRunStory(root_mock.state.platform)
868 ])
869
870 def testRunStoryAndProcessErrorIfNeeded_tryUnhandlable(self):
871 root_mock = self._CreateErrorProcessingMock(method_exceptions={
872 'test.WillRunStory': Exception('foo')
873 })
874
875 with self.assertRaisesRegexp(Exception, 'foo'):
876 story_runner._RunStoryAndProcessErrorIfNeeded(
877 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
878
879 self.assertEquals(root_mock.method_calls, [
880 mock.call.test.WillRunStory(root_mock.state.platform),
881 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
882 mock.call.results.AddValue(FailureValueMatcher('foo')),
883 mock.call.state.DidRunStory(root_mock.results),
884 mock.call.test.DidRunStory(root_mock.state.platform)
885 ])
886
887 def testRunStoryAndProcessErrorIfNeeded_finallyException(self):
888 root_mock = self._CreateErrorProcessingMock(method_exceptions={
889 'state.DidRunStory': Exception('bar')
890 })
891
892 with self.assertRaisesRegexp(Exception, 'bar'):
893 story_runner._RunStoryAndProcessErrorIfNeeded(
894 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
895
896 self.assertEquals(root_mock.method_calls, [
897 mock.call.test.WillRunStory(root_mock.state.platform),
898 mock.call.state.WillRunStory(root_mock.story),
899 mock.call.state.CanRunStory(root_mock.story),
900 mock.call.state.RunStory(root_mock.results),
901 mock.call.test.Measure(root_mock.state.platform, root_mock.results),
902 mock.call.state.DidRunStory(root_mock.results),
903 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results)
904 ])
905
906 def testRunStoryAndProcessErrorIfNeeded_tryTimeout_finallyException(self):
907 root_mock = self._CreateErrorProcessingMock(method_exceptions={
908 'state.RunStory': exceptions.TimeoutException('foo'),
909 'state.DidRunStory': Exception('bar')
910 })
911
912 story_runner._RunStoryAndProcessErrorIfNeeded(
913 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
914
915 self.assertEquals(root_mock.method_calls, [
916 mock.call.test.WillRunStory(root_mock.state.platform),
917 mock.call.state.WillRunStory(root_mock.story),
918 mock.call.state.CanRunStory(root_mock.story),
919 mock.call.state.RunStory(root_mock.results),
920 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
921 mock.call.results.AddValue(FailureValueMatcher('foo')),
922 mock.call.state.DidRunStory(root_mock.results)
923 ])
924
925 def testRunStoryAndProcessErrorIfNeeded_tryError_finallyException(self):
926 root_mock = self._CreateErrorProcessingMock(method_exceptions={
927 'state.WillRunStory': exceptions.Error('foo'),
928 'test.DidRunStory': Exception('bar')
929 })
930
931 with self.assertRaisesRegexp(exceptions.Error, 'foo'):
932 story_runner._RunStoryAndProcessErrorIfNeeded(
933 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
934
935 self.assertEquals(root_mock.method_calls, [
936 mock.call.test.WillRunStory(root_mock.state.platform),
937 mock.call.state.WillRunStory(root_mock.story),
938 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
939 mock.call.results.AddValue(FailureValueMatcher('foo')),
940 mock.call.state.DidRunStory(root_mock.results),
941 mock.call.test.DidRunStory(root_mock.state.platform)
942 ])
943
944 def testRunStoryAndProcessErrorIfNeeded_tryUnsupportedAction_finallyException(
945 self):
946 root_mock = self._CreateErrorProcessingMock(method_exceptions={
947 'test.WillRunStory': page_action.PageActionNotSupported('foo'),
948 'state.DidRunStory': Exception('bar')
949 })
950
951 story_runner._RunStoryAndProcessErrorIfNeeded(
952 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
953
954 self.assertEquals(root_mock.method_calls, [
955 mock.call.test.WillRunStory(root_mock.state.platform),
956 mock.call.results.AddValue(SkipValueMatcher()),
957 mock.call.state.DidRunStory(root_mock.results)
958 ])
959
960 def testRunStoryAndProcessErrorIfNeeded_tryUnhandlable_finallyException(self):
961 root_mock = self._CreateErrorProcessingMock(method_exceptions={
962 'test.Measure': Exception('foo'),
963 'test.DidRunStory': Exception('bar')
964 })
965
966 with self.assertRaisesRegexp(Exception, 'foo'):
967 story_runner._RunStoryAndProcessErrorIfNeeded(
968 root_mock.story, root_mock.results, root_mock.state, root_mock.test)
969
970 self.assertEquals(root_mock.method_calls, [
971 mock.call.test.WillRunStory(root_mock.state.platform),
972 mock.call.state.WillRunStory(root_mock.story),
973 mock.call.state.CanRunStory(root_mock.story),
974 mock.call.state.RunStory(root_mock.results),
975 mock.call.test.Measure(root_mock.state.platform, root_mock.results),
976 mock.call.state.DumpStateUponFailure(root_mock.story, root_mock.results),
977 mock.call.results.AddValue(FailureValueMatcher('foo')),
978 mock.call.state.DidRunStory(root_mock.results),
979 mock.call.test.DidRunStory(root_mock.state.platform)
980 ])
OLDNEW
« no previous file with comments | « telemetry/telemetry/internal/story_runner.py ('k') | telemetry/telemetry/page/shared_page_state.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698