1edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# Copyright 2015 The Chromium Authors. All rights reserved.
2edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# Use of this source code is governed by a BSD-style license that can be
3edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# found in the LICENSE file.
4edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
5edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport sys
6edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport unittest
7edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
8edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport mock
9edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
10edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import find_anomalies
11edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import find_change_points
12edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import testing_common
13edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import utils
14edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard.models import anomaly
15edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard.models import graph_data
16edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard.models import sheriff
17edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
18edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# Sample time series.
19edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair_TEST_ROW_DATA = [
20cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241105, 2136.7), (241116, 2140.3), (241151, 2149.1),
21cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241154, 2147.2), (241156, 2130.6), (241160, 2136.2),
22cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241188, 2146.7), (241201, 2141.8), (241226, 2140.6),
23cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241247, 2128.1), (241249, 2134.2), (241254, 2130.0),
24cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241262, 2136.0), (241268, 2142.6), (241271, 2149.1),
25cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241282, 2156.6), (241294, 2125.3), (241298, 2155.5),
26cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241303, 2148.5), (241317, 2146.2), (241323, 2123.3),
27cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241330, 2121.5), (241342, 2141.2), (241355, 2145.2),
28cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241371, 2136.3), (241386, 2144.0), (241405, 2138.1),
29cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241420, 2147.6), (241432, 2140.7), (241441, 2132.2),
30cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241452, 2138.2), (241455, 2139.3), (241471, 2134.0),
31cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241488, 2137.2), (241503, 2152.5), (241520, 2136.3),
32cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241524, 2139.3), (241529, 2143.5), (241532, 2145.5),
33cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241535, 2147.0), (241537, 2184.1), (241546, 2180.8),
34cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241553, 2181.5), (241559, 2176.8), (241566, 2174.0),
35cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241577, 2182.8), (241579, 2184.8), (241582, 2190.5),
36cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241584, 2183.1), (241609, 2178.3), (241620, 2178.1),
37cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241645, 2190.8), (241653, 2177.7), (241666, 2185.3),
38cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241697, 2173.8), (241716, 2172.1), (241735, 2172.5),
39cef7893435aa41160dd1255c43cb8498279738ccChris Craik    (241757, 2174.7), (241766, 2196.7), (241782, 2184.1),
40edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair]
41edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
42edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
43edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairdef _MakeSampleChangePoint(x_value, median_before, median_after):
44edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  """Makes a sample find_change_points.ChangePoint for use in these tests."""
45edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  # The only thing that matters in these tests is the revision number
46edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  # and the values before and after.
47edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  return find_change_points.ChangePoint(
48edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      x_value=x_value,
49edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      median_before=median_before,
50edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      median_after=median_after,
51edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      window_start=1,
52edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      window_end=8,
53edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      size_before=None,
54edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      size_after=None,
55edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      relative_change=None,
56edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      std_dev_before=None,
57edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      t_statistic=None,
58edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      degrees_of_freedom=None,
59edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      p_value=None)
60edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
61edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
62edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairclass EndRevisionMatcher(object):
63edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  """Custom matcher to test if an anomaly matches a given end rev."""
64edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
65edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def __init__(self, end_revision):
66edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    """Initializes with the end time to check."""
67edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._end_revision = end_revision
68edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
69edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def __eq__(self, rhs):
70edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    """Checks to see if RHS has the same end time."""
71edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    return self._end_revision == rhs.end_revision
72edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
73edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def __repr__(self):
74edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    """Shows a readable revision which can be printed when assert fails."""
75edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    return '<IsEndRevision %d>' % self._end_revision
76edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
77edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
78edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairclass ModelMatcher(object):
79edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  """Custom matcher to check if two ndb entity names match."""
80edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
81edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def __init__(self, name):
82edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    """Initializes with the name of the entity."""
83edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._name = name
84edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
85edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def __eq__(self, rhs):
86edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    """Checks to see if RHS has the same name."""
87edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    return rhs.key.string_id() == self._name
88edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
89edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def __repr__(self):
90edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    """Shows a readable revision which can be printed when assert fails."""
91edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    return '<IsModel %s>' % self._name
92edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
93edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
94edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairclass ProcessAlertsTest(testing_common.TestCase):
95edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
96edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def setUp(self):
97edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    super(ProcessAlertsTest, self).setUp()
98edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.SetCurrentUser('foo@bar.com', is_admin=True)
99edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
100edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def _AddDataForTests(self):
101edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    testing_common.AddTests(
102edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        ['ChromiumGPU'],
103edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        ['linux-release'], {
104edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'scrolling_benchmark': {
105edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                'ref': {},
106edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            },
107edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        })
108edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref = utils.TestKey(
109edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
110edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    for i in range(9000, 10070, 5):
111edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      # Internal-only data should be found.
112edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      test_container_key = utils.GetTestContainerKey(ref.key)
113edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      graph_data.Row(
114edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          id=i + 1, value=float(i * 3),
115edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          parent=test_container_key, internal_only=True).put()
116edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
117edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(
118edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      find_anomalies.find_change_points, 'FindChangePoints',
119edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      mock.MagicMock(return_value=[
120edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10011, 50, 100),
121edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10041, 200, 100),
122edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10061, 0, 100),
123edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      ]))
124edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(find_anomalies.email_sheriff, 'EmailSheriff')
125edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest(self, mock_email_sheriff):
126edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._AddDataForTests()
127edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test_path = 'ChromiumGPU/linux-release/scrolling_benchmark/ref'
128edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test = utils.TestKey(test_path).get()
129edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
130edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[test_path]).put()
131edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.put()
132edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
133edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(test.key)
134edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
135edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    expected_calls = [
136edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        mock.call(ModelMatcher('sheriff'),
137edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  ModelMatcher('ref'),
138edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  EndRevisionMatcher(10011)),
139edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        mock.call(ModelMatcher('sheriff'),
140edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  ModelMatcher('ref'),
141edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  EndRevisionMatcher(10041)),
142edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        mock.call(ModelMatcher('sheriff'),
143edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  ModelMatcher('ref'),
144edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  EndRevisionMatcher(10061))]
145edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(expected_calls, mock_email_sheriff.call_args_list)
146edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
147edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    anomalies = anomaly.Anomaly.query().fetch()
148edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(len(anomalies), 3)
149edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
150edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    def AnomalyExists(
151edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        anomalies, test, percent_changed, direction,
152edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        start_revision, end_revision, sheriff_name, internal_only):
153edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      for a in anomalies:
154edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        if (a.test == test and
155edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            a.percent_changed == percent_changed and
156edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            a.direction == direction and
157edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            a.start_revision == start_revision and
158edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            a.end_revision == end_revision and
159edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            a.sheriff.string_id() == sheriff_name and
160edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            a.internal_only == internal_only):
161edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          return True
162edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      return False
163edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
164edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertTrue(
165edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        AnomalyExists(
166edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            anomalies, test.key, percent_changed=100, direction=anomaly.UP,
167edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            start_revision=10007, end_revision=10011, sheriff_name='sheriff',
168edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            internal_only=False))
169edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
170edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertTrue(
171edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        AnomalyExists(
172edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            anomalies, test.key, percent_changed=-50, direction=anomaly.DOWN,
173edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            start_revision=10037, end_revision=10041, sheriff_name='sheriff',
174edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            internal_only=False))
175edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
176edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertTrue(
177edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        AnomalyExists(
178edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            anomalies, test.key, percent_changed=sys.float_info.max,
179edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            direction=anomaly.UP, start_revision=10057, end_revision=10061,
180edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            sheriff_name='sheriff', internal_only=False))
181edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
182edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # This is here just to verify that AnomalyExists returns False sometimes.
183edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertFalse(
184edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        AnomalyExists(
185edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            anomalies, test.key, percent_changed=100, direction=anomaly.DOWN,
186edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            start_revision=10037, end_revision=10041, sheriff_name='sheriff',
187edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            internal_only=False))
188edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
189edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(
190edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      find_anomalies.find_change_points, 'FindChangePoints',
191edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      mock.MagicMock(return_value=[
192edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10011, 100, 50)
193edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      ]))
19446b43bff003ceda46cf9a5d40a47f7674996d2e0Zhen Wang  def testProcessTest_ImprovementMarkedAsImprovement(self):
195edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._AddDataForTests()
196edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test = utils.TestKey(
197edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
198edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
199edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[test.test_path]).put()
200edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.improvement_direction = anomaly.DOWN
201edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.put()
202edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(test.key)
203edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    anomalies = anomaly.Anomaly.query().fetch()
204edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(len(anomalies), 1)
205edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertTrue(anomalies[0].is_improvement)
206edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
207edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch('logging.error')
208edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest_NoSheriff_ErrorLogged(self, mock_logging_error):
209edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._AddDataForTests()
210edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref = utils.TestKey(
211edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
212edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(ref.key)
213edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    mock_logging_error.assert_called_with('No sheriff for %s', ref.key)
214edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
215edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(
216edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      find_anomalies.find_change_points, 'FindChangePoints',
217edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      mock.MagicMock(return_value=[
218edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10026, 55.2, 57.8),
219edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10041, 45.2, 37.8),
220edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      ]))
221edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(find_anomalies.email_sheriff, 'EmailSheriff')
222cef7893435aa41160dd1255c43cb8498279738ccChris Craik  def testProcessTest_FiltersOutImprovements(self, mock_email_sheriff):
223edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._AddDataForTests()
224edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test = utils.TestKey(
225edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
226edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
227edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[test.test_path]).put()
228edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.improvement_direction = anomaly.UP
229edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.put()
230edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(test.key)
231edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    mock_email_sheriff.assert_called_once_with(
232edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        ModelMatcher('sheriff'), ModelMatcher('ref'), EndRevisionMatcher(10041))
233edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
234edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(
235edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      find_anomalies.find_change_points, 'FindChangePoints',
236edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      mock.MagicMock(return_value=[
237edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair          _MakeSampleChangePoint(10011, 50, 100),
238edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      ]))
239edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch.object(find_anomalies.email_sheriff, 'EmailSheriff')
240edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest_InternalOnlyTest(self, mock_email_sheriff):
241edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._AddDataForTests()
242edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test = utils.TestKey(
243edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
244edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.internal_only = True
245edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
246edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[test.test_path]).put()
247edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.put()
248edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
249edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(test.key)
250edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    expected_calls = [
251edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        mock.call(ModelMatcher('sheriff'),
252edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  ModelMatcher('ref'),
253edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                  EndRevisionMatcher(10011))]
254edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(expected_calls, mock_email_sheriff.call_args_list)
255edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
256edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    anomalies = anomaly.Anomaly.query().fetch()
257edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(len(anomalies), 1)
258edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(test.key, anomalies[0].test)
259edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(100, anomalies[0].percent_changed)
260edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(anomaly.UP, anomalies[0].direction)
261edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(10007, anomalies[0].start_revision)
262edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(10011, anomalies[0].end_revision)
263edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertTrue(anomalies[0].internal_only)
264edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
265edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest_AnomaliesMatchRefSeries_NoAlertCreated(self):
266edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # Tests that a Anomaly entity is not created if both the test and its
267edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # corresponding ref build series have the same data.
268edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    testing_common.AddTests(
269edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        ['ChromiumGPU'], ['linux-release'], {
270edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'scrolling_benchmark': {'ref': {}},
271edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        })
272edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref = utils.TestKey(
273edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
274edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    non_ref = utils.TestKey(
275edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark').get()
276edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test_container_key = utils.GetTestContainerKey(ref.key)
277edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test_container_key_non_ref = utils.GetTestContainerKey(non_ref.key)
278edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    for row in _TEST_ROW_DATA:
279edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      graph_data.Row(id=row[0], value=row[1], parent=test_container_key).put()
280edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      graph_data.Row(id=row[0], value=row[1],
281edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                     parent=test_container_key_non_ref).put()
282edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
283edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[non_ref.test_path]).put()
284edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref.put()
285edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    non_ref.put()
286edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(non_ref.key)
287edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    new_anomalies = anomaly.Anomaly.query().fetch()
288edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(0, len(new_anomalies))
289edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
290edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest_AnomalyDoesNotMatchRefSeries_AlertCreated(self):
291edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # Tests that an Anomaly entity is created when non-ref series goes up, but
292edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # the ref series stays flat.
293edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    testing_common.AddTests(
294edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        ['ChromiumGPU'], ['linux-release'], {
295edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'scrolling_benchmark': {'ref': {}},
296edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        })
297edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref = utils.TestKey(
298edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
299edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    non_ref = utils.TestKey(
300edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark').get()
301edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test_container_key = utils.GetTestContainerKey(ref.key)
302edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test_container_key_non_ref = utils.GetTestContainerKey(non_ref.key)
303edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    for row in _TEST_ROW_DATA:
304edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      graph_data.Row(id=row[0], value=2125.375, parent=test_container_key).put()
305edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      graph_data.Row(id=row[0], value=row[1],
306edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair                     parent=test_container_key_non_ref).put()
307edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
308edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[ref.test_path]).put()
309edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
310edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[non_ref.test_path]).put()
311edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref.put()
312edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    non_ref.put()
313edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(non_ref.key)
314edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    new_anomalies = anomaly.Anomaly.query().fetch()
315edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(len(new_anomalies), 1)
316edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
317edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest_CreatesAnAnomaly(self):
318edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    testing_common.AddTests(
319edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        ['ChromiumGPU'], ['linux-release'], {
320edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'scrolling_benchmark': {'ref': {}},
321edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        })
322edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref = utils.TestKey(
323edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
324edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test_container_key = utils.GetTestContainerKey(ref.key)
325edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    for row in _TEST_ROW_DATA:
326edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      graph_data.Row(id=row[0], value=row[1], parent=test_container_key).put()
327edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    sheriff.Sheriff(
328edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        email='a@google.com', id='sheriff', patterns=[ref.test_path]).put()
329edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ref.put()
330edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(ref.key)
331edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    new_anomalies = anomaly.Anomaly.query().fetch()
332edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(1, len(new_anomalies))
333edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(anomaly.UP, new_anomalies[0].direction)
334edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(241536, new_anomalies[0].start_revision)
335edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertEqual(241537, new_anomalies[0].end_revision)
336edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
337edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  @mock.patch('logging.error')
338edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  def testProcessTest_LastAlertedRevisionTooHigh_PropertyReset(
339edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair      self, mock_logging_error):
340edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # If the last_alerted_revision property of the Test is too high,
341edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    # then the property should be reset and an error should be logged.
342edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self._AddDataForTests()
343edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test = utils.TestKey(
344edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        'ChromiumGPU/linux-release/scrolling_benchmark/ref').get()
345edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.last_alerted_revision = 1234567890
346edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    test.put()
347edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    find_anomalies.ProcessTest(test.key)
348edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    self.assertIsNone(test.key.get().last_alerted_revision)
349edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    calls = [
350edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        mock.call(
351edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'last_alerted_revision %d is higher than highest rev %d for test '
352edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            '%s; setting last_alerted_revision to None.',
353edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            1234567890,
354edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            10066,
355edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'ChromiumGPU/linux-release/scrolling_benchmark/ref'),
356edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair        mock.call(
357edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'No rows fetched for %s',
358edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair            'ChromiumGPU/linux-release/scrolling_benchmark/ref')
359edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    ]
360edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair    mock_logging_error.assert_has_calls(calls, any_order=True)
361edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
362edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair
363edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairif __name__ == '__main__':
364edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair  unittest.main()
365