Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Helper functions for the layout test analyzer.""" | 6 """Helper functions for the layout test analyzer.""" |
| 7 | 7 |
| 8 from datetime import datetime | 8 from datetime import datetime |
| 9 from email.mime.multipart import MIMEMultipart | 9 from email.mime.multipart import MIMEMultipart |
| 10 from email.mime.text import MIMEText | 10 from email.mime.text import MIMEText |
| 11 import fileinput | 11 import fileinput |
| 12 import os | 12 import os |
| 13 import pickle | 13 import pickle |
| 14 import re | |
| 14 import smtplib | 15 import smtplib |
| 15 import socket | 16 import socket |
| 16 import sys | 17 import sys |
| 17 import time | 18 import time |
| 18 import urllib | 19 import urllib |
| 19 | 20 |
| 20 from bug import Bug | 21 from bug import Bug |
| 21 from test_expectations_history import TestExpectationsHistory | 22 from test_expectations_history import TestExpectationsHistory |
| 22 | 23 |
| 23 | 24 |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 ValueEror when the number of tests in test group "whole" is equal or less | 126 ValueEror when the number of tests in test group "whole" is equal or less |
| 126 than that of "skip". | 127 than that of "skip". |
| 127 """ | 128 """ |
| 128 delta = len(self.result_map['whole'].keys()) - ( | 129 delta = len(self.result_map['whole'].keys()) - ( |
| 129 len(self.result_map['skip'].keys())) | 130 len(self.result_map['skip'].keys())) |
| 130 if delta <= 0: | 131 if delta <= 0: |
| 131 raise ValueError('The number of tests in test group "whole" is equal or ' | 132 raise ValueError('The number of tests in test group "whole" is equal or ' |
| 132 'less than that of "skip"') | 133 'less than that of "skip"') |
| 133 return 100 - len(self.result_map['nonskip'].keys()) * 100 / delta | 134 return 100 - len(self.result_map['nonskip'].keys()) * 100 / delta |
| 134 | 135 |
| 136 def ConvertToCSVText(self, current_time): | |
| 137 """Convert result_map into stats and issues text in CSV format. | |
|
dennis_jeffrey
2011/11/11 00:01:36
'result_map' --> |self.result_map|
imasaki1
2011/11/11 00:25:52
Done.
| |
| 138 | |
| 139 Both are used for inputs for Google spreadsheet. | |
|
dennis_jeffrey
2011/11/11 00:01:36
nit: 'for inputs' --> 'as inputs'
imasaki1
2011/11/11 00:25:52
Done.
| |
| 140 | |
| 141 Args: | |
| 142 current_time: a string shows current time (e.g, 2011-11-08-16) | |
|
dennis_jeffrey
2011/11/11 00:01:36
current_time: a string depicting a time in year-mo
imasaki1
2011/11/11 00:25:52
Done.
| |
| 143 | |
| 144 Returns: | |
| 145 a tuple of stats and issues_txt | |
| 146 stats: analyzer result in CSV format that shows: | |
| 147 (current_time, the number of tests, the number of skipped tests, | |
| 148 the number of failing tests) | |
| 149 For example, | |
| 150 "2011-11-10-15,204,22,12" | |
| 151 issues_txt: issues listed in CSV format that shows: | |
|
dennis_jeffrey
2011/11/11 00:01:36
Could this include information for multiple issues
imasaki1
2011/11/11 00:25:52
Done.
| |
| 152 (BUGWK or BUGCR, bug number, the test expectation entry, | |
| 153 the name of the test) | |
| 154 For example, | |
| 155 "71543,TIMEOUT PASS,media/media-element-play-after-eos.html," | |
|
dennis_jeffrey
2011/11/11 00:01:36
This example seems to be missing the "BUGWK"/"BUGC
imasaki1
2011/11/11 00:25:52
Done.
| |
| 156 """ | |
| 157 stats = ','.join([current_time, str(len(self.result_map['whole'].keys())), | |
| 158 str(len(self.result_map['skip'].keys())), | |
|
dennis_jeffrey
2011/11/11 00:01:36
nit: indent this by 1 more space.
imasaki1
2011/11/11 00:25:52
Done.
| |
| 159 str(len(self.result_map['nonskip'].keys()))]) | |
|
dennis_jeffrey
2011/11/11 00:01:36
nit: indent this by 2 more spaces
imasaki1
2011/11/11 00:25:52
Done.
| |
| 160 issues_txt = '' | |
| 161 for (bug_txt, test_info_list) in ( | |
|
dennis_jeffrey
2011/11/11 00:01:36
I believe the parens are unnecessary in '(bug_txt,
imasaki1
2011/11/11 00:25:52
Done.
| |
| 162 self.GetListOfBugsForNonSkippedTests().iteritems()): | |
|
dennis_jeffrey
2011/11/11 00:01:36
nit: indent this by 2 more spaces
imasaki1
2011/11/11 00:25:52
Done.
| |
| 163 matches = re.match(r'(BUG(CR|WK))(\d+)', bug_txt) | |
| 164 bug_prefix = '' | |
|
dennis_jeffrey
2011/11/11 00:01:36
nit: 'bug_prefix' --> 'bug_suffix', since it's the
imasaki1
2011/11/11 00:25:52
Done.
| |
| 165 bug_no = '' | |
| 166 if matches: | |
| 167 bug_prefix = matches.group(1) | |
| 168 bug_no = matches.group(3) | |
| 169 issues_txt += bug_prefix + ',' + bug_no + ',' | |
| 170 for test_info in test_info_list: | |
| 171 (test_name, te_info) = test_info | |
|
dennis_jeffrey
2011/11/11 00:01:36
no need for parentheses here
imasaki1
2011/11/11 00:25:52
Done.
| |
| 172 issues_txt += ' '.join(te_info.keys()) + ',' + test_name + ',' | |
| 173 issues_txt += '\n' | |
| 174 return (stats, issues_txt) | |
|
dennis_jeffrey
2011/11/11 00:01:36
no need for parentheses here
imasaki1
2011/11/11 00:25:52
Done.
| |
| 175 | |
| 135 def ConvertToString(self, prev_time, diff_map, bug_anno_map): | 176 def ConvertToString(self, prev_time, diff_map, bug_anno_map): |
| 136 """Convert this result to HTML display for email. | 177 """Convert this result to HTML display for email. |
| 137 | 178 |
| 138 Args: | 179 Args: |
| 139 prev_time: the previous time string that are compared against. | 180 prev_time: the previous time string that are compared against. |
| 140 diff_map: the compared map generated by |CompareResultMaps()|. | 181 diff_map: the compared map generated by |CompareResultMaps()|. |
| 141 bug_anno_map: a annotation map where keys are bug names and values are | 182 bug_anno_map: a annotation map where keys are bug names and values are |
| 142 annotations for the bug. | 183 annotations for the bug. |
| 143 | 184 |
| 144 Returns: | 185 Returns: |
| 145 a analyzer result string in HTML format. | 186 a analyzer result string in HTML format. |
| 146 """ | 187 """ |
| 147 return_str = ('<b>Statistics (Diff Compared to %s):</b><ul>' | 188 return_str = '' |
| 148 '<li>The number of tests: %d (%s)</li>' | 189 if diff_map: |
| 149 '<li>The number of failing skipped tests: %d (%s)</li>' | 190 return_str += ('<b>Statistics (Diff Compared to %s):</b><ul>' |
| 150 '<li>The number of failing non-skipped tests: %d (%s)</li>' | 191 '<li>The number of tests: %d (%s)</li>' |
| 151 '<li>Passing rate: %d %%</li></ul>') % ( | 192 '<li>The number of failing skipped tests: %d (%s)</li>' |
| 152 prev_time, len(self.result_map['whole'].keys()), | 193 '<li>The number of failing non-skipped tests: %d (%s)</li>' |
| 153 AnalyzerResultMap.GetDiffString(diff_map['whole'], 'whole'), | 194 '<li>Passing rate: %d %%</li></ul>') % ( |
| 154 len(self.result_map['skip'].keys()), | 195 prev_time, len(self.result_map['whole'].keys()), |
| 155 AnalyzerResultMap.GetDiffString(diff_map['skip'], 'skip'), | 196 AnalyzerResultMap.GetDiffString(diff_map['whole'], 'whole'), |
| 156 len(self.result_map['nonskip'].keys()), | 197 len(self.result_map['skip'].keys()), |
| 157 AnalyzerResultMap.GetDiffString(diff_map['nonskip'], | 198 AnalyzerResultMap.GetDiffString(diff_map['skip'], 'skip'), |
| 158 'nonskip'), | 199 len(self.result_map['nonskip'].keys()), |
| 159 self.GetPassingRate()) | 200 AnalyzerResultMap.GetDiffString(diff_map['nonskip'], |
| 201 'nonskip'), | |
| 202 self.GetPassingRate()) | |
| 160 return_str += '<b>Current issues about failing non-skipped tests:</b>' | 203 return_str += '<b>Current issues about failing non-skipped tests:</b>' |
| 161 for (bug_txt, test_info_list) in ( | 204 for (bug_txt, test_info_list) in ( |
| 162 self.GetListOfBugsForNonSkippedTests().iteritems()): | 205 self.GetListOfBugsForNonSkippedTests().iteritems()): |
| 163 if not bug_txt in bug_anno_map: | 206 if not bug_txt in bug_anno_map: |
| 164 bug_anno_map[bug_txt] = '<font color="red">Needs investigation!</font>' | 207 bug_anno_map[bug_txt] = '<font color="red">Needs investigation!</font>' |
| 165 return_str += '<ul>%s (%s)' % (Bug(bug_txt), bug_anno_map[bug_txt]) | 208 return_str += '<ul>%s (%s)' % (Bug(bug_txt), bug_anno_map[bug_txt]) |
| 166 for test_info in test_info_list: | 209 for test_info in test_info_list: |
| 167 (test_name, te_info) = test_info | 210 (test_name, te_info) = test_info |
| 168 gpu_link = '' | 211 gpu_link = '' |
| 169 if 'GPU' in te_info: | 212 if 'GPU' in te_info: |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 if 'Bugs' in te_info: | 291 if 'Bugs' in te_info: |
| 249 for bug in te_info['Bugs']: | 292 for bug in te_info['Bugs']: |
| 250 if bug not in bug_map: | 293 if bug not in bug_map: |
| 251 bug_map[bug] = [] | 294 bug_map[bug] = [] |
| 252 bug_map[bug].append((name, main_te_info)) | 295 bug_map[bug].append((name, main_te_info)) |
| 253 return bug_map | 296 return bug_map |
| 254 | 297 |
| 255 | 298 |
| 256 def SendStatusEmail(prev_time, analyzer_result_map, diff_map, | 299 def SendStatusEmail(prev_time, analyzer_result_map, diff_map, |
| 257 bug_anno_map, receiver_email_address, test_group_name, | 300 bug_anno_map, receiver_email_address, test_group_name, |
| 258 appended_text_to_email, email_content, rev_str): | 301 appended_text_to_email, email_content, rev_str, |
| 302 email_only_change_mode): | |
| 259 """Send status email. | 303 """Send status email. |
| 260 | 304 |
| 261 Args: | 305 Args: |
| 262 prev_time: the date string such as '2011-10-09-11'. This format has been | 306 prev_time: the date string such as '2011-10-09-11'. This format has been |
| 263 used in this analyzer. | 307 used in this analyzer. |
| 264 analyzer_result_map: current analyzer result. | 308 analyzer_result_map: current analyzer result. |
| 265 diff_map: a map that has 'whole', 'skip' and 'nonskip' as keys. | 309 diff_map: a map that has 'whole', 'skip' and 'nonskip' as keys. |
| 266 The values of the map are the result of |GetDiffBetweenMaps()|. | 310 The values of the map are the result of |GetDiffBetweenMaps()|. |
| 267 The element has two lists of test cases. One (with index 0) is for | 311 The element has two lists of test cases. One (with index 0) is for |
| 268 test names that are in the current result but NOT in the previous | 312 test names that are in the current result but NOT in the previous |
| 269 result. The other (with index 1) is for test names that are in the | 313 result. The other (with index 1) is for test names that are in the |
| 270 previous results but NOT in the current result. | 314 previous results but NOT in the current result. |
| 271 For example (test expectation information is omitted for | 315 For example (test expectation information is omitted for |
| 272 simplicity), | 316 simplicity), |
| 273 comp_result_map['whole'][0] = ['foo1.html'] | 317 comp_result_map['whole'][0] = ['foo1.html'] |
| 274 comp_result_map['whole'][1] = ['foo2.html'] | 318 comp_result_map['whole'][1] = ['foo2.html'] |
| 275 This means that current result has 'foo1.html' but it is NOT in the | 319 This means that current result has 'foo1.html' but it is NOT in the |
| 276 previous result. This also means the previous result has 'foo2.html' | 320 previous result. This also means the previous result has 'foo2.html' |
| 277 but it is NOT in the current result. | 321 but it is NOT in the current result. |
| 278 bug_anno_map: bug annotation map where bug name and annotations are | 322 bug_anno_map: bug annotation map where bug name and annotations are |
| 279 stored. | 323 stored. |
| 280 receiver_email_address: receiver's email address. | 324 receiver_email_address: receiver's email address. |
| 281 test_group_name: string representing the test group name (e.g., 'media'). | 325 test_group_name: string representing the test group name (e.g., 'media'). |
| 282 appended_text_to_email: a text which is appended at the end of the status | 326 appended_text_to_email: a text which is appended at the end of the status |
| 283 email. | 327 email. |
| 284 email_content: an email content string that will be shown on the dashboard. | 328 email_content: an email content string that will be shown on the dashboard. |
| 285 rev_str: a revision string that contains revision information that is sent | 329 rev_str: a revision string that contains revision information that is sent |
| 286 out in the status email. It is obtained by calling | 330 out in the status email. It is obtained by calling |
| 287 |GetRevisionString()|. | 331 |GetRevisionString()|. |
| 332 email_only_change_mode: please refer to |options|. | |
| 288 """ | 333 """ |
| 289 if rev_str: | 334 if rev_str: |
| 290 email_content += '<br><b>Revision Information:</b>' | 335 email_content += '<br><b>Revision Information:</b>' |
| 291 email_content += rev_str | 336 email_content += rev_str |
| 292 localtime = time.asctime(time.localtime(time.time())) | 337 localtime = time.asctime(time.localtime(time.time())) |
| 338 change_str = '' | |
| 339 if email_only_change_mode: | |
| 340 change_str = 'Status Change' | |
|
dennis_jeffrey
2011/11/11 00:01:36
add a space right after the 'e' at the end of this
imasaki1
2011/11/11 00:25:52
Done.
| |
| 341 subject = 'Layout Test Analyzer Result %s (%s): %s' % (change_str, | |
|
dennis_jeffrey
2011/11/11 00:01:36
remove the space right after the first '%s'
imasaki1
2011/11/11 00:25:52
Done.
| |
| 342 test_group_name, | |
| 343 localtime) | |
| 293 # TODO(imasaki): remove my name from here. | 344 # TODO(imasaki): remove my name from here. |
| 294 subject = 'Layout Test Analyzer Result (%s): %s' % (test_group_name, | |
| 295 localtime) | |
| 296 SendEmail('imasaki@chromium.org', [receiver_email_address], | 345 SendEmail('imasaki@chromium.org', [receiver_email_address], |
| 297 subject, email_content + appended_text_to_email) | 346 subject, email_content + appended_text_to_email) |
| 298 | 347 |
| 299 | 348 |
| 300 def GetRevisionString(prev_time, current_time, diff_map): | 349 def GetRevisionString(prev_time, current_time, diff_map): |
| 301 """Get a string for revision information during the specified time period. | 350 """Get a string for revision information during the specified time period. |
| 302 | 351 |
| 303 Args: | 352 Args: |
| 304 prev_time: the previous time as a floating point number expressed | 353 prev_time: the previous time as a floating point number expressed |
| 305 in seconds since the epoch, in UTC. | 354 in seconds since the epoch, in UTC. |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 list2 = map2[name]['te_info'] | 544 list2 = map2[name]['te_info'] |
| 496 te_diff = [item for item in list1 if not item in list2] | 545 te_diff = [item for item in list1 if not item in list2] |
| 497 if te_diff: | 546 if te_diff: |
| 498 name_list.append((name, te_diff)) | 547 name_list.append((name, te_diff)) |
| 499 else: | 548 else: |
| 500 name_list.append((name, value1)) | 549 name_list.append((name, value1)) |
| 501 return name_list | 550 return name_list |
| 502 | 551 |
| 503 return (GetDiffBetweenMapsHelper(map1, map2, lookIntoTestExpectationInfo), | 552 return (GetDiffBetweenMapsHelper(map1, map2, lookIntoTestExpectationInfo), |
| 504 GetDiffBetweenMapsHelper(map2, map1, lookIntoTestExpectationInfo)) | 553 GetDiffBetweenMapsHelper(map2, map1, lookIntoTestExpectationInfo)) |
| OLD | NEW |