| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright (C) 2013 Google Inc. All rights reserved. | 3 # Copyright (C) 2013 Google Inc. All rights reserved. |
| 4 # | 4 # |
| 5 # Redistribution and use in source and binary forms, with or without | 5 # Redistribution and use in source and binary forms, with or without |
| 6 # modification, are permitted provided that the following conditions are | 6 # modification, are permitted provided that the following conditions are |
| 7 # met: | 7 # met: |
| 8 # | 8 # |
| 9 # * Redistributions of source code must retain the above copyright | 9 # * Redistributions of source code must retain the above copyright |
| 10 # notice, this list of conditions and the following disclaimer. | 10 # notice, this list of conditions and the following disclaimer. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | 30 |
| 31 """Prints lists of bug numbers / tests whose bugs haven't been modified recently
.""" | 31 """Prints a list of test expectations for tests whose bugs haven't been modified
recently.""" |
| 32 | 32 |
| 33 import datetime | 33 import datetime |
| 34 import json | 34 import json |
| 35 import optparse | 35 import optparse |
| 36 import re | |
| 37 import sys | 36 import sys |
| 38 import time | |
| 39 import urllib2 | 37 import urllib2 |
| 40 | 38 |
| 41 from webkitpy.common.host import Host | 39 from webkitpy.common.host import Host |
| 42 from webkitpy.common.system.filesystem import FileSystem | |
| 43 from webkitpy.common.webkit_finder import WebKitFinder | |
| 44 from webkitpy.layout_tests.models.test_expectations import TestExpectationParser | 40 from webkitpy.layout_tests.models.test_expectations import TestExpectationParser |
| 45 | 41 |
| 46 google_code_url = 'https://www.googleapis.com/projecthosting/v2/projects/chromiu
m/issues/%s?key=AIzaSyDgCqT1Dt5AZWLHo4QJjyMHaCjhnFacGF0' | 42 # FIXME: Make this a direct request to Monorail. |
| 47 crbug_prefix = 'crbug.com/' | 43 GOOGLE_CODE_URL = 'https://www.googleapis.com/projecthosting/v2/projects/chromiu
m/issues/%s?key=AIzaSyDgCqT1Dt5AZWLHo4QJjyMHaCjhnFacGF0' |
| 44 CRBUG_PREFIX = 'crbug.com/' |
| 48 | 45 |
| 49 | 46 |
| 50 class StaleTestPrinter(object): | 47 class StaleTestPrinter(object): |
| 51 | 48 |
| 52 def __init__(self, options): | 49 def __init__(self, options): |
| 53 self.days = options.days | 50 self.days = options.days |
| 54 self.create_csv = options.create_csv | 51 self.csv_filename = options.create_csv |
| 52 self.host = Host() |
| 55 self.is_stale_results = {} | 53 self.is_stale_results = {} |
| 56 | 54 |
| 55 def print_stale_tests(self): |
| 56 port = self.host.port_factory.get() |
| 57 expectations = port.expectations_dict() |
| 58 parser = TestExpectationParser(port, all_tests=(), is_lint_mode=False) |
| 59 expectations_file, expectations_contents = expectations.items()[0] |
| 60 expectation_lines = parser.parse(expectations_file, expectations_content
s) |
| 61 csv_rows = [] |
| 62 for line in expectation_lines: |
| 63 row = self.check_expectations_line(line) |
| 64 if row: |
| 65 csv_rows.append(row) |
| 66 if self.csv_filename: |
| 67 self.write_csv(csv_rows) |
| 68 |
| 69 def write_csv(self, rows): |
| 70 row_strings = [', '.join(r) for r in rows] |
| 71 contents = '\n'.join(row_strings) + '\n' |
| 72 self.host.filesystem.write_text_file(self.csv_filename, contents) |
| 73 |
| 74 def check_expectations_line(self, line): |
| 75 """Checks the bugs in one test expectations line to see if they're stale
. |
| 76 |
| 77 Args: |
| 78 line: A TestExpectationsLine instance. |
| 79 |
| 80 Returns: |
| 81 A CSV row (a list of strings), or None if there are no stale bugs. |
| 82 """ |
| 83 bug_links, test_name = line.bugs, line.name |
| 84 try: |
| 85 if bug_links and all(self.is_stale(bug_link) for bug_link in bug_lin
ks): |
| 86 print line.original_string.strip() |
| 87 return [bug_links[0], test_name] |
| 88 except urllib2.HTTPError as error: |
| 89 if error.code == 404: |
| 90 message = 'got 404, bug does not exist.' |
| 91 elif error.code == 403: |
| 92 message = 'got 403, not accessible. Not able to tell if it\'s st
ale.' |
| 93 else: |
| 94 message = str(error) |
| 95 print >> sys.stderr, 'Error when checking %s: %s' % (','.join(bug_li
nks), message) |
| 96 return None |
| 97 |
| 57 def is_stale(self, bug_link): | 98 def is_stale(self, bug_link): |
| 58 if bug_link in self.is_stale_results: | 99 if bug_link in self.is_stale_results: |
| 59 return self.is_stale_results[bug_link] | 100 return self.is_stale_results[bug_link] |
| 60 # In case there's an error in the request, don't make the same request a
gain. | 101 # In case there's an error in the request, don't make the same request a
gain. |
| 61 self.is_stale_results[bug_link] = False | 102 self.is_stale_results[bug_link] = False |
| 62 bug_number = bug_link.strip(crbug_prefix) | 103 bug_number = bug_link.strip(CRBUG_PREFIX) |
| 63 url = google_code_url % bug_number | 104 url = GOOGLE_CODE_URL % bug_number |
| 64 response = urllib2.urlopen(url) | 105 response = urllib2.urlopen(url) |
| 65 parsed = json.loads(response.read()) | 106 parsed = json.loads(response.read()) |
| 66 last_updated = parsed['updated'] | 107 last_updated = parsed['updated'] |
| 67 parsed_time = datetime.datetime.strptime(last_updated.split(".")[0] + "U
TC", "%Y-%m-%dT%H:%M:%S%Z") | 108 parsed_time = datetime.datetime.strptime(last_updated.split(".")[0] + "U
TC", "%Y-%m-%dT%H:%M:%S%Z") |
| 68 time_delta = datetime.datetime.now() - parsed_time | 109 time_delta = datetime.datetime.now() - parsed_time |
| 69 self.is_stale_results[bug_link] = time_delta.days > self.days | 110 self.is_stale_results[bug_link] = time_delta.days > self.days |
| 70 return self.is_stale_results[bug_link] | 111 return self.is_stale_results[bug_link] |
| 71 | 112 |
| 72 def print_stale_tests(self): | |
| 73 host = Host() | |
| 74 port = host.port_factory.get() | |
| 75 exps = port.expectations_dict() | |
| 76 csv_contents = '' | |
| 77 parser = TestExpectationParser(port, all_tests=(), is_lint_mode=False) | |
| 78 for line in parser.parse(*exps.items()[0]): | |
| 79 bugs, name = line.bugs, line.name | |
| 80 try: | |
| 81 if bugs and all(self.is_stale(bug) for bug in bugs): | |
| 82 print line.original_string.strip() | |
| 83 csv_contents += "%s, %s\n" % (bugs[0], name) | |
| 84 except urllib2.HTTPError as error: | |
| 85 if error.code == 404: | |
| 86 print 'Does not exist.' | |
| 87 elif error.code == 403: | |
| 88 print 'Is not accessible. Not able to tell if it\'s stale.' | |
| 89 is_bug_stale = False | |
| 90 else: | |
| 91 print error | |
| 92 if self.create_csv: | |
| 93 host.filesystem.write_text_file(self.create_csv, csv_contents) | |
| 94 | |
| 95 | 113 |
| 96 def main(argv): | 114 def main(argv): |
| 97 option_parser = optparse.OptionParser() | 115 option_parser = optparse.OptionParser() |
| 98 option_parser.add_option('--days', type='int', default=90, help='Number of d
ays to consider a bug stale.'), | |
| 99 option_parser.add_option( | 116 option_parser.add_option( |
| 100 '--create-csv', | 117 '--days', type='int', default=90, |
| 101 type='string', | 118 help='Number of days to consider a bug stale.') |
| 102 default=0, | 119 option_parser.add_option( |
| 103 help='Generate a CSV of the stale entries as well. Followed by the filen
ame.'), | 120 '--create-csv', type='string', default='', |
| 104 options, args = option_parser.parse_args(argv) | 121 help='Filename of CSV file to write stale entries to. No file will be wr
itten if no name specified.') |
| 105 | 122 options, _ = option_parser.parse_args(argv) |
| 106 printer = StaleTestPrinter(options) | 123 printer = StaleTestPrinter(options) |
| 107 printer.print_stale_tests() | 124 printer.print_stale_tests() |
| 108 return 0 | 125 return 0 |
| 109 | 126 |
| 127 |
| 110 if __name__ == '__main__': | 128 if __name__ == '__main__': |
| 111 sys.exit(main(sys.argv[1:])) | 129 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |