OLD | NEW |
---|---|
1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 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 logging | 6 import logging |
7 import os | 7 import os |
8 import traceback | 8 import traceback |
9 | 9 |
10 from google.appengine.api import taskqueue | 10 from google.appengine.api import taskqueue |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
107 return 'https://%s/job/%s' % (os.environ['HTTP_HOST'], self.job_id) | 107 return 'https://%s/job/%s' % (os.environ['HTTP_HOST'], self.job_id) |
108 | 108 |
109 def AddChange(self, change): | 109 def AddChange(self, change): |
110 self.state.AddChange(change) | 110 self.state.AddChange(change) |
111 | 111 |
112 def Start(self): | 112 def Start(self): |
113 self.Schedule() | 113 self.Schedule() |
114 self._PostBugComment('started') | 114 self._PostBugComment('started') |
115 | 115 |
116 def Complete(self): | 116 def Complete(self): |
117 self._PostBugComment('completed') | 117 self._PostBugComment('completed', include_differences=True) |
118 | 118 |
119 def Fail(self): | 119 def Fail(self): |
120 self.exception = traceback.format_exc() | 120 self.exception = traceback.format_exc() |
121 self._PostBugComment('stopped with an error') | 121 self._PostBugComment('stopped with an error') |
122 | 122 |
123 def Schedule(self): | 123 def Schedule(self): |
124 task = taskqueue.add(queue_name='job-queue', url='/api/run/' + self.job_id, | 124 task = taskqueue.add(queue_name='job-queue', url='/api/run/' + self.job_id, |
125 countdown=_TASK_INTERVAL) | 125 countdown=_TASK_INTERVAL) |
126 self.task = task.name | 126 self.task = task.name |
127 | 127 |
(...skipping 24 matching lines...) Expand all Loading... | |
152 | 152 |
153 'created': self.created.isoformat(), | 153 'created': self.created.isoformat(), |
154 'updated': self.updated.isoformat(), | 154 'updated': self.updated.isoformat(), |
155 'exception': self.exception, | 155 'exception': self.exception, |
156 'status': self.status, | 156 'status': self.status, |
157 } | 157 } |
158 if include_state: | 158 if include_state: |
159 d.update(self.state.AsDict()) | 159 d.update(self.state.AsDict()) |
160 return d | 160 return d |
161 | 161 |
162 def _PostBugComment(self, status): | 162 def _PostBugComment(self, status, include_differences=False): |
163 if not self.bug_id: | 163 if not self.bug_id: |
164 return | 164 return |
165 | 165 |
166 title = '%s Pinpoint job %s.' % (_ROUND_PUSHPIN, status) | 166 title = '%s Pinpoint job %s.' % (_ROUND_PUSHPIN, status) |
167 header = '\n'.join((title, self.url)) | 167 header = '\n'.join((title, self.url)) |
168 | 168 |
169 # Include list of Changes. | |
170 change_details = [] | 169 change_details = [] |
171 for _, change in self.state.Differences(): | 170 if include_differences: |
172 # TODO: Store the commit info in the Commit. | 171 # Include list of Changes. |
173 commit = change.last_commit | 172 differences = self.state.Differences() |
174 commit_info = gitiles_service.CommitInfo(commit.repository_url, | 173 if differences: |
175 commit.git_hash) | 174 for _, change in differences: |
176 subject = commit_info['message'].split('\n', 1)[0] | 175 change_details.append(_FormatChangeForBug(change)) |
177 author = commit_info['author']['email'] | 176 else: |
178 time = commit_info['committer']['time'] | 177 change_details.append('No change points found.') |
sullivan
2017/09/18 20:55:47
This might be a little confusing to users. "No sig
shatch
2017/09/18 21:00:14
Kinda wonder if people will ask what this means, m
perezju
2017/09/19 11:56:07
It would also help figure out the right wording if
| |
179 | |
180 byline = 'By %s %s %s' % (author, _MIDDLE_DOT, time) | |
181 git_link = commit.repository + '@' + commit.git_hash | |
182 change_details.append('\n'.join((subject, byline, git_link))) | |
183 | 178 |
184 comment = '\n\n'.join([header] + change_details) | 179 comment = '\n\n'.join([header] + change_details) |
185 | 180 |
186 issue_tracker = issue_tracker_service.IssueTrackerService( | 181 issue_tracker = issue_tracker_service.IssueTrackerService( |
187 utils.ServiceAccountHttp()) | 182 utils.ServiceAccountHttp()) |
188 issue_tracker.AddBugComment(self.bug_id, comment, send_email=False) | 183 issue_tracker.AddBugComment(self.bug_id, comment, send_email=False) |
189 | 184 |
190 | 185 |
191 class _JobState(object): | 186 class _JobState(object): |
192 """The internal state of a Job. | 187 """The internal state of a Job. |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
346 # Here, "the same" means that we fail to reject the null hypothesis. We can | 341 # Here, "the same" means that we fail to reject the null hypothesis. We can |
347 # never be completely sure that the two Changes have the same results, but | 342 # never be completely sure that the two Changes have the same results, but |
348 # we've run everything that we planned to, and didn't detect any difference. | 343 # we've run everything that we planned to, and didn't detect any difference. |
349 if (len(attempts_a) >= self._repeat_count and | 344 if (len(attempts_a) >= self._repeat_count and |
350 len(attempts_b) >= self._repeat_count): | 345 len(attempts_b) >= self._repeat_count): |
351 return _SAME | 346 return _SAME |
352 | 347 |
353 return _UNKNOWN | 348 return _UNKNOWN |
354 | 349 |
355 | 350 |
351 def _FormatChangeForBug(change): | |
352 # TODO: Store the commit info in the Commit. | |
353 commit = change.last_commit | |
354 commit_info = gitiles_service.CommitInfo(commit.repository_url, | |
355 commit.git_hash) | |
356 subject = commit_info['message'].split('\n', 1)[0] | |
357 author = commit_info['author']['email'] | |
358 time = commit_info['committer']['time'] | |
359 | |
360 byline = 'By %s %s %s' % (author, _MIDDLE_DOT, time) | |
361 git_link = commit.repository + '@' + commit.git_hash | |
362 return '\n'.join((subject, byline, git_link)) | |
363 | |
364 | |
356 def _CombineResultsPerQuest(attempts): | 365 def _CombineResultsPerQuest(attempts): |
357 aggregate_results = collections.defaultdict(list) | 366 aggregate_results = collections.defaultdict(list) |
358 for attempt in attempts: | 367 for attempt in attempts: |
359 if not attempt.completed: | 368 if not attempt.completed: |
360 continue | 369 continue |
361 | 370 |
362 for quest, results in attempt.result_values.iteritems(): | 371 for quest, results in attempt.result_values.iteritems(): |
363 aggregate_results[quest] += results | 372 aggregate_results[quest] += results |
364 | 373 |
365 return aggregate_results | 374 return aggregate_results |
366 | 375 |
367 | 376 |
368 def _CompareValues(values_a, values_b): | 377 def _CompareValues(values_a, values_b): |
369 if not (values_a and values_b): | 378 if not (values_a and values_b): |
370 return _UNKNOWN | 379 return _UNKNOWN |
371 | 380 |
372 try: | 381 try: |
373 p_value = mann_whitney_u.MannWhitneyU(values_a, values_b) | 382 p_value = mann_whitney_u.MannWhitneyU(values_a, values_b) |
374 except ValueError: | 383 except ValueError: |
375 return _UNKNOWN | 384 return _UNKNOWN |
376 | 385 |
377 if p_value < _SIGNIFICANCE_LEVEL: | 386 if p_value < _SIGNIFICANCE_LEVEL: |
378 return _DIFFERENT | 387 return _DIFFERENT |
379 else: | 388 else: |
380 return _UNKNOWN | 389 return _UNKNOWN |
OLD | NEW |