OLD | NEW |
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 """Helper functions for the layout test analyzer.""" | 5 """Helper functions for the layout test analyzer.""" |
6 | 6 |
7 from datetime import datetime | 7 from datetime import datetime |
8 from email.mime.multipart import MIMEMultipart | 8 from email.mime.multipart import MIMEMultipart |
9 from email.mime.text import MIMEText | 9 from email.mime.text import MIMEText |
10 import fileinput | 10 import fileinput |
11 import os | 11 import os |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 @staticmethod | 67 @staticmethod |
68 def GetDiffString(diff_map_element, type_str): | 68 def GetDiffString(diff_map_element, type_str): |
69 """Get difference string out of diff map element. | 69 """Get difference string out of diff map element. |
70 | 70 |
71 The difference string shows difference between two analyzer results | 71 The difference string shows difference between two analyzer results |
72 (for example, a result for now and a result for sometime in the past) | 72 (for example, a result for now and a result for sometime in the past) |
73 in HTML format (with colors). This is used for generating email messages. | 73 in HTML format (with colors). This is used for generating email messages. |
74 | 74 |
75 Args: | 75 Args: |
76 diff_map_element: An element of the compared map generated by | 76 diff_map_element: An element of the compared map generated by |
77 |CompareResultMaps()|. The element has two lists of test cases. One | 77 |CompareResultMaps()|. The element has two lists of test cases. One |
78 is for test names that are in the current result but NOT in the | 78 is for test names that are in the current result but NOT in the |
79 previous result. The other is for test names that are in the previous | 79 previous result. The other is for test names that are in the previous |
80 results but NOT in the current result. Please refer to comments in | 80 results but NOT in the current result. Please refer to comments in |
81 |CompareResultMaps()| for details. | 81 |CompareResultMaps()| for details. |
82 type_str: a string indicating the test group to which |diff_map_element| | 82 type_str: a string indicating the test group to which |diff_map_element| |
83 belongs; used for color determination. Must be 'whole', 'skip', or | 83 belongs; used for color determination. Must be 'whole', 'skip', or |
84 'nonskip'. | 84 'nonskip'. |
85 | 85 |
86 Returns: | 86 Returns: |
87 a string in HTML format (with colors) to show difference between two | 87 a string in HTML format (with colors) to show difference between two |
88 analyzer results. | 88 analyzer results. |
89 """ | 89 """ |
90 diff = len(diff_map_element[0]) - len(diff_map_element[1]) | 90 diff = len(diff_map_element[0]) - len(diff_map_element[1]) |
91 if diff == 0: | 91 if diff == 0: |
92 return 'No Change' | 92 return 'No Change' |
93 color = '' | 93 color = '' |
94 if diff > 0 and type_str != 'whole': | 94 if diff > 0 and type_str != 'whole': |
95 color = 'red' | 95 color = 'red' |
96 else: | 96 else: |
97 color = 'green' | 97 color = 'green' |
98 diff_sign = '' | 98 diff_sign = '' |
99 if diff > 0: | 99 if diff > 0: |
100 diff_sign = '+' | 100 diff_sign = '+' |
101 whole_str = '<font color="%s">%s%d</font>' % (color, diff_sign, diff) | 101 whole_str = '<font color="%s">%s%d</font>' % (color, diff_sign, diff) |
| 102 colors = ['red', 'green'] |
| 103 if type_str == 'whole': |
| 104 # Bug 107773 - when we increase the number of tests, |
| 105 # the name of the tests are in red, it should be green |
| 106 # since it is good thing. |
| 107 colors = ['green', 'red'] |
102 str1 = '' | 108 str1 = '' |
103 for (name, _) in diff_map_element[0]: | 109 for (name, _) in diff_map_element[0]: |
104 str1 += '<font color="red">%s,</font> ' % name | 110 str1 += '<font color="%s">%s,</font>' % (colors[0], name) |
105 str1 = str1[:-1] | |
106 str2 = '' | 111 str2 = '' |
107 for (name, _) in diff_map_element[1]: | 112 for (name, _) in diff_map_element[1]: |
108 str2 += '<font color="green">%s,</font> ' % name | 113 str2 += '<font color="%s">%s,</font>' % (colors[1], name) |
109 str2 = str2[:-1] | |
110 if str1 or str2: | 114 if str1 or str2: |
111 whole_str += ':' | 115 whole_str += ':' |
112 if str1: | 116 if str1: |
113 whole_str += str1 | 117 whole_str += str1 |
114 if str2: | 118 if str2: |
115 whole_str += str2 | 119 whole_str += str2 |
| 120 # Remove the last occurrence of ','. |
| 121 whole_str = ''.join(whole_str.rsplit(',', 1)) |
116 return whole_str | 122 return whole_str |
117 | 123 |
118 def GetPassingRate(self): | 124 def GetPassingRate(self): |
119 """Get passing rate. | 125 """Get passing rate. |
120 | 126 |
121 Returns: | 127 Returns: |
122 layout test passing rate of this result in percent. | 128 layout test passing rate of this result in percent. |
123 | 129 |
124 Raises: | 130 Raises: |
125 ValueEror when the number of tests in test group "whole" is equal or less | 131 ValueEror when the number of tests in test group "whole" is equal |
126 than that of "skip". | 132 or less than that of "skip". |
127 """ | 133 """ |
128 delta = len(self.result_map['whole'].keys()) - ( | 134 delta = len(self.result_map['whole'].keys()) - ( |
129 len(self.result_map['skip'].keys())) | 135 len(self.result_map['skip'].keys())) |
130 if delta <= 0: | 136 if delta <= 0: |
131 raise ValueError('The number of tests in test group "whole" is equal or ' | 137 raise ValueError('The number of tests in test group "whole" is equal or ' |
132 'less than that of "skip"') | 138 'less than that of "skip"') |
133 return 100 - len(self.result_map['nonskip'].keys()) * 100 / delta | 139 return 100 - len(self.result_map['nonskip'].keys()) * 100 / delta |
134 | 140 |
135 def ConvertToCSVText(self, current_time): | 141 def ConvertToCSVText(self, current_time): |
136 """Convert |self.result_map| into stats and issues text in CSV format. | 142 """Convert |self.result_map| into stats and issues text in CSV format. |
137 | 143 |
138 Both are used as inputs for Google spreadsheet. | 144 Both are used as inputs for Google spreadsheet. |
139 | 145 |
140 Args: | 146 Args: |
141 current_time: a string depicting a time in year-month-day-hour | 147 current_time: a string depicting a time in year-month-day-hour |
142 format (e.g., 2011-11-08-16). | 148 format (e.g., 2011-11-08-16). |
143 | 149 |
144 Returns: | 150 Returns: |
145 a tuple of stats and issues_txt | 151 a tuple of stats and issues_txt |
146 stats: analyzer result in CSV format that shows: | 152 stats: analyzer result in CSV format that shows: |
147 (current_time, the number of tests, the number of skipped tests, | 153 (current_time, the number of tests, the number of skipped tests, |
148 the number of failing tests, passing rate) | 154 the number of failing tests, passing rate) |
149 For example, | 155 For example, |
150 "2011-11-10-15,204,22,12,94" | 156 "2011-11-10-15,204,22,12,94" |
151 issues_txt: issues listed in CSV format that shows: | 157 issues_txt: issues listed in CSV format that shows: |
152 (BUGWK or BUGCR, bug number, the test expectation entry, | 158 (BUGWK or BUGCR, bug number, the test expectation entry, |
153 the name of the test) | 159 the name of the test) |
154 For example, | 160 For example, |
155 "BUGWK,71543,TIMEOUT PASS,media/media-element-play-after-eos.html, | 161 "BUGWK,71543,TIMEOUT PASS,media/media-element-play-after-eos.html, |
156 BUGCR,97657,IMAGE CPU MAC TIMEOUT PASS,media/audio-repaint.html," | 162 BUGCR,97657,IMAGE CPU MAC TIMEOUT PASS,media/audio-repaint.html," |
157 """ | 163 """ |
158 stats = ','.join([current_time, str(len(self.result_map['whole'].keys())), | 164 stats = ','.join([current_time, str(len(self.result_map['whole'].keys())), |
159 str(len(self.result_map['skip'].keys())), | 165 str(len(self.result_map['skip'].keys())), |
160 str(len(self.result_map['nonskip'].keys())), | 166 str(len(self.result_map['nonskip'].keys())), |
161 str(self.GetPassingRate())]) | 167 str(self.GetPassingRate())]) |
162 issues_txt = '' | 168 issues_txt = '' |
163 for bug_txt, test_info_list in ( | 169 for bug_txt, test_info_list in ( |
164 self.GetListOfBugsForNonSkippedTests().iteritems()): | 170 self.GetListOfBugsForNonSkippedTests().iteritems()): |
165 matches = re.match(r'(BUG(CR|WK))(\d+)', bug_txt) | 171 matches = re.match(r'(BUG(CR|WK))(\d+)', bug_txt) |
166 bug_suffix = '' | 172 bug_suffix = '' |
167 bug_no = '' | 173 bug_no = '' |
168 if matches: | 174 if matches: |
169 bug_suffix = matches.group(1) | 175 bug_suffix = matches.group(1) |
170 bug_no = matches.group(3) | 176 bug_no = matches.group(3) |
171 issues_txt += bug_suffix + ',' + bug_no + ',' | 177 issues_txt += bug_suffix + ',' + bug_no + ',' |
172 for test_info in test_info_list: | 178 for test_info in test_info_list: |
173 test_name, te_info = test_info | 179 test_name, te_info = test_info |
174 issues_txt += ' '.join(te_info.keys()) + ',' + test_name + ',' | 180 issues_txt += ' '.join(te_info.keys()) + ',' + test_name + ',' |
175 issues_txt += '\n' | 181 issues_txt += '\n' |
176 return stats, issues_txt | 182 return stats, issues_txt |
177 | 183 |
178 def ConvertToString(self, prev_time, diff_map, bug_anno_map): | 184 def ConvertToString(self, prev_time, diff_map, bug_anno_map, |
| 185 issue_detail_mode): |
179 """Convert this result to HTML display for email. | 186 """Convert this result to HTML display for email. |
180 | 187 |
181 Args: | 188 Args: |
182 prev_time: the previous time string that are compared against. | 189 prev_time: the previous time string that are compared against. |
183 diff_map: the compared map generated by |CompareResultMaps()|. | 190 diff_map: the compared map generated by |CompareResultMaps()|. |
184 bug_anno_map: a annotation map where keys are bug names and values are | 191 bug_anno_map: a annotation map where keys are bug names and values are |
185 annotations for the bug. | 192 annotations for the bug. |
| 193 issue_detail_mode: includes the issue details in the output string if |
| 194 this is True. |
186 | 195 |
187 Returns: | 196 Returns: |
188 a analyzer result string in HTML format. | 197 a analyzer result string in HTML format. |
189 """ | 198 """ |
190 return_str = '' | 199 return_str = '' |
191 if diff_map: | 200 if diff_map: |
192 return_str += ('<b>Statistics (Diff Compared to %s):</b><ul>' | 201 return_str += ( |
193 '<li>The number of tests: %d (%s)</li>' | 202 '<b>Statistics (Diff Compared to %s):</b><ul>' |
194 '<li>The number of failing skipped tests: %d (%s)</li>' | 203 '<li>The number of tests: %d (%s)</li>' |
195 '<li>The number of failing non-skipped tests: %d (%s)</li>' | 204 '<li>The number of failing skipped tests: %d (%s)</li>' |
196 '<li>Passing rate: %d %%</li></ul>') % ( | 205 '<li>The number of failing non-skipped tests: %d (%s)</li>' |
197 prev_time, len(self.result_map['whole'].keys()), | 206 '<li>Passing rate: %d %%</li></ul>') % ( |
198 AnalyzerResultMap.GetDiffString(diff_map['whole'], 'whole'), | 207 prev_time, len(self.result_map['whole'].keys()), |
199 len(self.result_map['skip'].keys()), | 208 AnalyzerResultMap.GetDiffString(diff_map['whole'], 'whole'), |
200 AnalyzerResultMap.GetDiffString(diff_map['skip'], 'skip'), | 209 len(self.result_map['skip'].keys()), |
201 len(self.result_map['nonskip'].keys()), | 210 AnalyzerResultMap.GetDiffString(diff_map['skip'], 'skip'), |
202 AnalyzerResultMap.GetDiffString(diff_map['nonskip'], | 211 len(self.result_map['nonskip'].keys()), |
203 'nonskip'), | 212 AnalyzerResultMap.GetDiffString(diff_map['nonskip'], 'nonskip'), |
204 self.GetPassingRate()) | 213 self.GetPassingRate()) |
205 return_str += '<b>Current issues about failing non-skipped tests:</b>' | 214 if issue_detail_mode: |
206 for (bug_txt, test_info_list) in ( | 215 return_str += '<b>Current issues about failing non-skipped tests:</b>' |
207 self.GetListOfBugsForNonSkippedTests().iteritems()): | 216 for (bug_txt, test_info_list) in ( |
208 if not bug_txt in bug_anno_map: | 217 self.GetListOfBugsForNonSkippedTests().iteritems()): |
209 bug_anno_map[bug_txt] = '<font color="red">Needs investigation!</font>' | 218 if not bug_txt in bug_anno_map: |
210 return_str += '<ul>%s (%s)' % (Bug(bug_txt), bug_anno_map[bug_txt]) | 219 bug_anno_map[bug_txt] = '' |
211 for test_info in test_info_list: | 220 else: |
212 (test_name, te_info) = test_info | 221 bug_anno_map[bug_txt] = '(' + bug_anno_map[bug_txt] + ')' |
213 gpu_link = '' | 222 return_str += '<ul>%s %s' % (Bug(bug_txt), bug_anno_map[bug_txt]) |
214 if 'GPU' in te_info: | 223 for test_info in test_info_list: |
215 gpu_link = 'group=%40ToT%20GPU%20Mesa%20-%20chromium.org&' | 224 (test_name, te_info) = test_info |
216 dashboard_link = ('http://test-results.appspot.com/dashboards/' | 225 gpu_link = '' |
217 'flakiness_dashboard.html#%stests=%s') % ( | 226 if 'GPU' in te_info: |
218 gpu_link, test_name) | 227 gpu_link = 'group=%40ToT%20GPU%20Mesa%20-%20chromium.org&' |
219 return_str += '<li><a href="%s">%s</a> (%s) </li>' % ( | 228 dashboard_link = ('http://test-results.appspot.com/dashboards/' |
220 dashboard_link, test_name, ' '.join(te_info.keys())) | 229 'flakiness_dashboard.html#%stests=%s') % ( |
221 return_str += '</ul>\n' | 230 gpu_link, test_name) |
| 231 return_str += '<li><a href="%s">%s</a> (%s) </li>' % ( |
| 232 dashboard_link, test_name, ' '.join(te_info.keys())) |
| 233 return_str += '</ul>\n' |
222 return return_str | 234 return return_str |
223 | 235 |
224 def CompareToOtherResultMap(self, other_result_map): | 236 def CompareToOtherResultMap(self, other_result_map): |
225 """Compare this result map with the other to see if there are any diff. | 237 """Compare this result map with the other to see if there are any diff. |
226 | 238 |
227 The comparison is done for layouttests which belong to 'whole', 'skip', | 239 The comparison is done for layouttests which belong to 'whole', 'skip', |
228 or 'nonskip'. | 240 or 'nonskip'. |
229 | 241 |
230 Args: | 242 Args: |
231 other_result_map: another result map to be compared against the result | 243 other_result_map: another result map to be compared against the result |
232 map of the current object. | 244 map of the current object. |
233 | 245 |
234 Returns: | 246 Returns: |
235 a map that has 'whole', 'skip' and 'nonskip' as keys. | 247 a map that has 'whole', 'skip' and 'nonskip' as keys. |
236 Please refer to |diff_map| in |SendStatusEmail()|. | 248 Please refer to |diff_map| in |SendStatusEmail()|. |
237 """ | 249 """ |
238 comp_result_map = {} | 250 comp_result_map = {} |
239 for name in ['whole', 'skip', 'nonskip']: | 251 for name in ['whole', 'skip', 'nonskip']: |
240 if name == 'nonskip': | 252 if name == 'nonskip': |
241 # Look into expectation to get diff only for non-skipped tests. | 253 # Look into expectation to get diff only for non-skipped tests. |
242 lookIntoTestExpectationInfo = True | 254 lookIntoTestExpectationInfo = True |
243 else: | 255 else: |
244 # Otherwise, only test names are compared to get diff. | 256 # Otherwise, only test names are compared to get diff. |
245 lookIntoTestExpectationInfo = False | 257 lookIntoTestExpectationInfo = False |
246 comp_result_map[name] = GetDiffBetweenMaps( | 258 comp_result_map[name] = GetDiffBetweenMaps( |
247 self.result_map[name], other_result_map.result_map[name], | 259 self.result_map[name], other_result_map.result_map[name], |
248 lookIntoTestExpectationInfo) | 260 lookIntoTestExpectationInfo) |
249 return comp_result_map | 261 return comp_result_map |
250 | 262 |
251 @staticmethod | 263 @staticmethod |
252 def Load(file_path): | 264 def Load(file_path): |
253 """Load the object from |file_path| using pickle library. | 265 """Load the object from |file_path| using pickle library. |
254 | 266 |
255 Args: | 267 Args: |
256 file_path: the string path to the file from which to read the result. | 268 file_path: the string path to the file from which to read the result. |
257 | 269 |
258 Returns: | 270 Returns: |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 bug_anno_map: bug annotation map where bug name and annotations are | 336 bug_anno_map: bug annotation map where bug name and annotations are |
325 stored. | 337 stored. |
326 receiver_email_address: receiver's email address. | 338 receiver_email_address: receiver's email address. |
327 test_group_name: string representing the test group name (e.g., 'media'). | 339 test_group_name: string representing the test group name (e.g., 'media'). |
328 appended_text_to_email: a text which is appended at the end of the status | 340 appended_text_to_email: a text which is appended at the end of the status |
329 email. | 341 email. |
330 email_content: an email content string that will be shown on the dashboard. | 342 email_content: an email content string that will be shown on the dashboard. |
331 rev_str: a revision string that contains revision information that is sent | 343 rev_str: a revision string that contains revision information that is sent |
332 out in the status email. It is obtained by calling | 344 out in the status email. It is obtained by calling |
333 |GetRevisionString()|. | 345 |GetRevisionString()|. |
334 email_only_change_mode: please refer to |options|. | 346 email_only_change_mode: send email only when there is a change if this is |
| 347 True. Otherwise, always send email after each run. |
335 """ | 348 """ |
336 if rev_str: | 349 if rev_str: |
337 email_content += '<br><b>Revision Information:</b>' | 350 email_content += '<br><b>Revision Information:</b>' |
338 email_content += rev_str | 351 email_content += rev_str |
339 localtime = time.asctime(time.localtime(time.time())) | 352 localtime = time.asctime(time.localtime(time.time())) |
340 change_str = '' | 353 change_str = '' |
341 if email_only_change_mode: | 354 if email_only_change_mode: |
342 change_str = 'Status Change ' | 355 change_str = 'Status Change ' |
343 subject = 'Layout Test Analyzer Result %s(%s): %s' % (change_str, | 356 subject = 'Layout Test Analyzer Result %s(%s): %s' % (change_str, |
344 test_group_name, | 357 test_group_name, |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 link = urllib.unquote('http://trac.webkit.org/changeset?new=%d%40trunk' | 405 link = urllib.unquote('http://trac.webkit.org/changeset?new=%d%40trunk' |
393 '%2FLayoutTests%2Fplatform%2Fchromium%2F' | 406 '%2FLayoutTests%2Fplatform%2Fchromium%2F' |
394 'test_expectations.txt&old=%d%40trunk%2F' | 407 'test_expectations.txt&old=%d%40trunk%2F' |
395 'LayoutTests%2Fplatform%2Fchromium%2F' | 408 'LayoutTests%2Fplatform%2Fchromium%2F' |
396 'test_expectations.txt') % (new_rev, old_rev) | 409 'test_expectations.txt') % (new_rev, old_rev) |
397 rev_str += '<ul><a href="%s">%s->%s</a>\n' % (link, old_rev, new_rev) | 410 rev_str += '<ul><a href="%s">%s->%s</a>\n' % (link, old_rev, new_rev) |
398 simple_rev_str = '<a href="%s">%s->%s</a>,' % (link, old_rev, new_rev) | 411 simple_rev_str = '<a href="%s">%s->%s</a>,' % (link, old_rev, new_rev) |
399 rev_str += '<li>%s</li>\n' % author | 412 rev_str += '<li>%s</li>\n' % author |
400 rev_str += '<li>%s</li>\n<ul>' % date | 413 rev_str += '<li>%s</li>\n<ul>' % date |
401 for line in target_lines: | 414 for line in target_lines: |
402 rev_str += '<li>%s</li>\n' % line | 415 # Find *.html pattern (test name) and replace it with the link to |
| 416 # flakiness dashboard. |
| 417 test_name_pattern = r'(\S+.html)' |
| 418 match = re.search(test_name_pattern, line) |
| 419 if match: |
| 420 test_name = match.group(1) |
| 421 gpu_link = '' |
| 422 if 'GPU' in line: |
| 423 gpu_link = 'group=%40ToT%20GPU%20Mesa%20-%20chromium.org&' |
| 424 dashboard_link = ('http://test-results.appspot.com/dashboards/' |
| 425 'flakiness_dashboard.html#%stests=%s') % ( |
| 426 gpu_link, test_name) |
| 427 line = line.replace(test_name, '<a href="%s">%s</a>' % ( |
| 428 dashboard_link, test_name)) |
| 429 # Find bug text and replace it with the link to the bug. |
| 430 bug = Bug(line) |
| 431 if bug.bug_txt: |
| 432 line = '<li>%s</li>\n' % line.replace(bug.bug_txt, str(bug)) |
| 433 rev_str += line |
403 rev_str += '</ul></ul>' | 434 rev_str += '</ul></ul>' |
404 return (rev_str, simple_rev_str, rev, rev_date) | 435 return (rev_str, simple_rev_str, rev, rev_date) |
405 | 436 |
406 | 437 |
407 def SendEmail(sender_email_address, receivers_email_addresses, subject, | 438 def SendEmail(sender_email_address, receivers_email_addresses, subject, |
408 message): | 439 message): |
409 """Send email using localhost's mail server. | 440 """Send email using localhost's mail server. |
410 | 441 |
411 Args: | 442 Args: |
412 sender_email_address: sender's email address. | 443 sender_email_address: sender's email address. |
413 receivers_email_addresses: receiver's email addresses. | 444 receivers_email_addresses: receiver's email addresses. |
414 subject: subject string. | 445 subject: subject string. |
415 message: email message. | 446 message: email message. |
| 447 """ |
| 448 try: |
| 449 html_top = """ |
| 450 <html> |
| 451 <head></head> |
| 452 <body> |
416 """ | 453 """ |
417 try: | 454 html_bot = """ |
418 html_top = """ | 455 </body> |
419 <html> | 456 </html> |
420 <head></head> | 457 """ |
421 <body> | 458 html = html_top + message + html_bot |
422 """ | 459 msg = MIMEMultipart('alternative') |
423 html_bot = """ | 460 msg['Subject'] = subject |
424 </body> | 461 msg['From'] = sender_email_address |
425 </html> | 462 msg['To'] = receivers_email_addresses[0] |
426 """ | 463 part1 = MIMEText(html, 'html') |
427 html = html_top + message + html_bot | 464 smtp_obj = smtplib.SMTP('localhost') |
428 msg = MIMEMultipart('alternative') | 465 msg.attach(part1) |
429 msg['Subject'] = subject | 466 smtp_obj.sendmail(sender_email_address, receivers_email_addresses, |
430 msg['From'] = sender_email_address | 467 msg.as_string()) |
431 msg['To'] = receivers_email_addresses[0] | 468 print 'Successfully sent email' |
432 part1 = MIMEText(html, 'html') | 469 except smtplib.SMTPException, ex: |
433 smtp_obj = smtplib.SMTP('localhost') | 470 print 'Authentication failed:', ex |
434 msg.attach(part1) | 471 print 'Error: unable to send email' |
435 smtp_obj.sendmail(sender_email_address, receivers_email_addresses, | 472 except (socket.gaierror, socket.error, socket.herror), ex: |
436 msg.as_string()) | 473 print ex |
437 print 'Successfully sent email' | 474 print 'Error: unable to send email' |
438 except smtplib.SMTPException, ex: | |
439 print 'Authentication failed:', ex | |
440 print 'Error: unable to send email' | |
441 except (socket.gaierror, socket.error, socket.herror), ex: | |
442 print ex | |
443 print 'Error: unable to send email' | |
444 | 475 |
445 | 476 |
446 def FindLatestTime(time_list): | 477 def FindLatestTime(time_list): |
447 """Find latest time from |time_list|. | 478 """Find latest time from |time_list|. |
448 | 479 |
449 The current status is compared to the status of the latest file in | 480 The current status is compared to the status of the latest file in |
450 |RESULT_DIR|. | 481 |RESULT_DIR|. |
451 | 482 |
452 Args: | 483 Args: |
453 time_list: a list of time string in the form of 'Year-Month-Day-Hour' | 484 time_list: a list of time string in the form of 'Year-Month-Day-Hour' |
454 (e.g., 2011-10-23-23). Strings not in this format are ignored. | 485 (e.g., 2011-10-23-23). Strings not in this format are ignored. |
455 | 486 |
456 Returns: | 487 Returns: |
457 a string representing latest time among the time_list or None if | 488 a string representing latest time among the time_list or None if |
458 |time_list| is empty or no valid date string in |time_list|. | 489 |time_list| is empty or no valid date string in |time_list|. |
459 """ | 490 """ |
460 if not time_list: | 491 if not time_list: |
461 return None | 492 return None |
462 latest_date = None | 493 latest_date = None |
463 for time_element in time_list: | 494 for time_element in time_list: |
464 try: | 495 try: |
465 item_date = datetime.strptime(time_element, '%Y-%m-%d-%H') | 496 item_date = datetime.strptime(time_element, '%Y-%m-%d-%H') |
466 if latest_date == None or latest_date < item_date: | 497 if latest_date is None or latest_date < item_date: |
467 latest_date = item_date | 498 latest_date = item_date |
468 except ValueError: | 499 except ValueError: |
469 # Do nothing. | 500 # Do nothing. |
470 pass | 501 pass |
471 if latest_date: | 502 if latest_date: |
472 return latest_date.strftime('%Y-%m-%d-%H') | 503 return latest_date.strftime('%Y-%m-%d-%H') |
473 else: | 504 else: |
474 return None | 505 return None |
475 | 506 |
476 | 507 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 a list of tuples (name, te_info) that are in |map1| but not in |map2|. | 570 a list of tuples (name, te_info) that are in |map1| but not in |map2|. |
540 """ | 571 """ |
541 name_list = [] | 572 name_list = [] |
542 for (name, value1) in map1.iteritems(): | 573 for (name, value1) in map1.iteritems(): |
543 if name in map2: | 574 if name in map2: |
544 if lookIntoTestExpectationInfo and 'te_info' in value1: | 575 if lookIntoTestExpectationInfo and 'te_info' in value1: |
545 list1 = value1['te_info'] | 576 list1 = value1['te_info'] |
546 list2 = map2[name]['te_info'] | 577 list2 = map2[name]['te_info'] |
547 te_diff = [item for item in list1 if not item in list2] | 578 te_diff = [item for item in list1 if not item in list2] |
548 if te_diff: | 579 if te_diff: |
549 name_list.append((name, te_diff)) | 580 name_list.append((name, te_diff)) |
550 else: | 581 else: |
551 name_list.append((name, value1)) | 582 name_list.append((name, value1)) |
552 return name_list | 583 return name_list |
553 | 584 |
554 return (GetDiffBetweenMapsHelper(map1, map2, lookIntoTestExpectationInfo), | 585 return (GetDiffBetweenMapsHelper(map1, map2, lookIntoTestExpectationInfo), |
555 GetDiffBetweenMapsHelper(map2, map1, lookIntoTestExpectationInfo)) | 586 GetDiffBetweenMapsHelper(map2, map1, lookIntoTestExpectationInfo)) |
OLD | NEW |