| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 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 """Rebase DumpAccessibilityTree Tests. | 6 """Rebase DumpAccessibilityTree Tests. |
| 7 | 7 |
| 8 This script is intended to be run when you make a change that could affect the | 8 This script is intended to be run when you make a change that could affect the |
| 9 expected results of tests in: | 9 expected results of tests in: |
| 10 | 10 |
| 11 content/test/data/accessibility | 11 content/test/data/accessibility |
| 12 | 12 |
| 13 It assumes that you've already uploaded a change and the try jobs have finished. | 13 It assumes that you've already uploaded a change and the try jobs have finished. |
| 14 It collects all of the results from try jobs on all platforms and updates the | 14 It collects all of the results from try jobs on all platforms and updates the |
| 15 expectation files locally. From there you can run 'git diff' to make sure all | 15 expectation files locally. From there you can run 'git diff' to make sure all |
| 16 of the changes look reasonable, then upload the change for code review. | 16 of the changes look reasonable, then upload the change for code review. |
| 17 """ | 17 """ |
| 18 | 18 |
| 19 import json |
| 19 import os | 20 import os |
| 20 import re | 21 import re |
| 21 import sys | 22 import sys |
| 22 import time | 23 import time |
| 23 import urllib | 24 import urllib |
| 24 | 25 |
| 25 # Load BeautifulSoup. It's checked into two places in the Chromium tree. | 26 # Load BeautifulSoup. It's checked into two places in the Chromium tree. |
| 26 sys.path.append( | 27 sys.path.append( |
| 27 'third_party/trace-viewer/third_party/tvcm/third_party/beautifulsoup') | 28 'third_party/trace-viewer/third_party/tvcm/third_party/beautifulsoup') |
| 28 from BeautifulSoup import BeautifulSoup | 29 from BeautifulSoup import BeautifulSoup |
| 29 | 30 |
| 30 # The location of the DumpAccessibilityTree html test files and expectations. | 31 # The location of the DumpAccessibilityTree html test files and expectations. |
| 31 TEST_DATA_PATH = os.path.join(os.getcwd(), 'content/test/data/accessibility') | 32 TEST_DATA_PATH = os.path.join(os.getcwd(), 'content/test/data/accessibility') |
| 32 | 33 |
| 33 # A global that keeps track of files we've already updated, so we don't | 34 # A global that keeps track of files we've already updated, so we don't |
| 34 # bother to update the same file twice. | 35 # bother to update the same file twice. |
| 35 completed_files = set() | 36 completed_files = set() |
| 36 | 37 |
| 37 def GitClIssue(): | 38 def GitClIssue(): |
| 38 '''Retrieve the current issue number as a string.''' | 39 '''Retrieve the current issue number as a string.''' |
| 39 result = os.popen('git cl issue').read() | 40 result = os.popen('git cl issue').read() |
| 40 # Returns string like: 'Issue number: 12345 (https://...)' | 41 # Returns string like: 'Issue number: 12345 (https://...)' |
| 41 return result.split()[2] | 42 return result.split()[2] |
| 42 | 43 |
| 43 def ParseFailure(name, url): | 44 def ParseFailure(name, url): |
| 44 '''Parse given the name of a failing trybot and the url of its build log.''' | 45 '''Parse given the name of a failing trybot and the url of its build log.''' |
| 46 print |
| 47 print "Checking trybot: %s" % name |
| 48 url = url.replace('/builders/', '/json/builders/') |
| 49 response = urllib.urlopen(url) |
| 50 if response.getcode() == 200: |
| 51 jsondata = response.read() |
| 45 | 52 |
| 46 # Figure out the platform. | 53 if not jsondata: |
| 47 if name.find('android') >= 0: | 54 print "Failed to fetch from: " + url |
| 48 platform_suffix = '-expected-android.txt' | |
| 49 elif name.find('mac') >= 0: | |
| 50 platform_suffix = '-expected-mac.txt' | |
| 51 elif name.find('win') >= 0: | |
| 52 platform_suffix = '-expected-win.txt' | |
| 53 else: | |
| 54 return | 55 return |
| 55 | 56 |
| 56 # Read the content_browsertests log file. | 57 try: |
| 57 data = None | 58 data = json.loads(jsondata) |
| 58 lines = None | 59 except: |
| 59 urls = [] | 60 print "Failed to parse JSON from: " + url |
| 60 for url_suffix in [ | |
| 61 '/steps/content_browsertests%20(with%20patch)/logs/stdio/text', | |
| 62 '/steps/content_browsertests/logs/stdio/text']: | |
| 63 urls.append(url + url_suffix) | |
| 64 for url in urls: | |
| 65 response = urllib.urlopen(url) | |
| 66 if response.getcode() == 200: | |
| 67 data = response.read() | |
| 68 lines = data.splitlines() | |
| 69 break | |
| 70 | |
| 71 if not data: | |
| 72 return | 61 return |
| 73 | 62 |
| 74 # Parse the log file for failing tests and overwrite the expected | 63 for step in data["steps"]: |
| 75 # result file locally with the actual results from the log. | 64 name = step["name"] |
| 76 test_name = None | 65 if name[:len("content_browsertests")] == "content_browsertests": |
| 66 if name.find("without") >= 0: |
| 67 continue |
| 68 if name.find("retry") >= 0: |
| 69 continue |
| 70 print "Found content_browsertests logs" |
| 71 for log in step["logs"]: |
| 72 (log_name, log_url) = log |
| 73 if log_name == "stdio": |
| 74 continue |
| 75 log_url += '/text' |
| 76 log_response = urllib.urlopen(log_url) |
| 77 if log_response.getcode() == 200: |
| 78 logdata = log_response.read() |
| 79 ParseLog(logdata) |
| 80 else: |
| 81 print "Failed to fetch test log data from: " + url |
| 82 |
| 83 def Fix(line): |
| 84 if line[:3] == '@@@': |
| 85 try: |
| 86 line = re.search('[^@]@([^@]*)@@@', line).group(1) |
| 87 except: |
| 88 pass |
| 89 return line |
| 90 |
| 91 def ParseLog(logdata): |
| 92 '''Parse the log file for failing tests and overwrite the expected |
| 93 result file locally with the actual results from the log.''' |
| 94 lines = logdata.splitlines() |
| 95 test_file = None |
| 96 expected_file = None |
| 77 start = None | 97 start = None |
| 78 filename = None | |
| 79 for i in range(len(lines)): | 98 for i in range(len(lines)): |
| 80 line = lines[i] | 99 line = Fix(lines[i]) |
| 81 if line[:12] == '[ RUN ]': | 100 if line.find('Testing:') >= 0: |
| 82 test_name = line[13:] | 101 test_file = re.search( |
| 83 if test_name and line[:8] == 'Testing:': | 102 'content.test.*accessibility.([^@]*)', line).group(1) |
| 84 filename = re.search('content.test.*accessibility.(.*)', line).group(1) | 103 expected_file = None |
| 85 if test_name and line == 'Actual': | 104 start = None |
| 105 if line.find('Expected output:') >= 0: |
| 106 expected_file = re.search( |
| 107 'content.test.*accessibility.([^@]*)', line).group(1) |
| 108 if line == 'Actual': |
| 86 start = i + 2 | 109 start = i + 2 |
| 87 if start and test_name and filename and line[:12] == '[ FAILED ]': | 110 if start and test_file and expected_file and line.find('End-of-file') >= 0: |
| 88 # Get the path to the html file. | 111 dst_fullpath = os.path.join(TEST_DATA_PATH, expected_file) |
| 89 dst_fullpath = os.path.join(TEST_DATA_PATH, filename) | |
| 90 # Strip off .html and replace it with the platform expected suffix. | |
| 91 dst_fullpath = dst_fullpath[:-5] + platform_suffix | |
| 92 if dst_fullpath in completed_files: | 112 if dst_fullpath in completed_files: |
| 93 continue | 113 continue |
| 94 | 114 |
| 95 actual = [line for line in lines[start : i - 1] if line] | 115 actual = [Fix(line) for line in lines[start : i] if line] |
| 96 fp = open(dst_fullpath, 'w') | 116 fp = open(dst_fullpath, 'w') |
| 97 fp.write('\n'.join(actual)) | 117 fp.write('\n'.join(actual)) |
| 98 fp.close() | 118 fp.close() |
| 99 print dst_fullpath | 119 print "* %s" % os.path.relpath(dst_fullpath) |
| 100 completed_files.add(dst_fullpath) | 120 completed_files.add(dst_fullpath) |
| 101 start = None | 121 start = None |
| 102 test_name = None | 122 test_file = None |
| 103 filename = None | 123 expected_file = None |
| 104 | 124 |
| 105 def ParseTrybots(data): | 125 def ParseTrybots(data): |
| 106 '''Parse the code review page to find links to try bots.''' | 126 '''Parse the code review page to find links to try bots.''' |
| 107 soup = BeautifulSoup(data) | 127 soup = BeautifulSoup(data) |
| 108 failures = soup.findAll( | 128 failures = soup.findAll( |
| 109 'a', | 129 'a', |
| 110 { "class" : "build-result build-status-color-failure" }) | 130 { "class" : "build-result build-status-color-failure" }) |
| 111 print 'Found %d trybots that failed' % len(failures) | 131 print 'Found %d trybots that failed' % len(failures) |
| 112 for f in failures: | 132 for f in failures: |
| 113 name = f.text.replace(' ', '') | 133 name = f.text.replace(' ', '') |
| 114 url = f['href'] | 134 url = f['href'] |
| 115 ParseFailure(name, url) | 135 ParseFailure(name, url) |
| 116 | 136 |
| 117 def Run(): | 137 def Run(): |
| 118 '''Main. Get the issue number and parse the code review page.''' | 138 '''Main. Get the issue number and parse the code review page.''' |
| 119 if len(sys.argv) == 2: | 139 if len(sys.argv) == 2: |
| 120 issue = sys.argv[1] | 140 issue = sys.argv[1] |
| 121 else: | 141 else: |
| 122 issue = GitClIssue() | 142 issue = GitClIssue() |
| 123 | 143 |
| 124 url = 'https://codereview.chromium.org/%s' % issue | 144 url = 'https://codereview.chromium.org/%s' % issue |
| 125 print 'Fetching issue from %s' % url | 145 print 'Fetching issue from %s' % url |
| 126 response = urllib.urlopen(url) | 146 response = urllib.urlopen(url) |
| 127 if response.getcode() != 200: | 147 if response.getcode() != 200: |
| 128 print 'Error code %d accessing url: %s' % (response.getcode(), url) | 148 print 'Error code %d accessing url: %s' % (response.getcode(), url) |
| 149 return |
| 129 data = response.read() | 150 data = response.read() |
| 130 ParseTrybots(data) | 151 ParseTrybots(data) |
| 131 | 152 |
| 153 print |
| 154 if len(completed_files) == 0: |
| 155 print "No output from DumpAccessibilityTree test results found." |
| 156 return |
| 157 else: |
| 158 print "Summary: modified the following files:" |
| 159 all_files = list(completed_files) |
| 160 all_files.sort() |
| 161 for f in all_files: |
| 162 print "* %s" % os.path.relpath(f) |
| 163 |
| 132 if __name__ == '__main__': | 164 if __name__ == '__main__': |
| 133 sys.exit(Run()) | 165 sys.exit(Run()) |
| OLD | NEW |