Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(441)

Side by Side Diff: third_party/WebKit/LayoutTests/imported/wpt/check_stability.py

Issue 2434563008: Import wpt@26c8d4e87448d1c4e5ebf2ddb4917c0633c201db (Closed)
Patch Set: Mark one more test as potentially timing out Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 import argparse
2 import json
3 import logging
4 import os
5 import subprocess
6 import sys
7 import traceback
8 from collections import defaultdict
9
10 import requests
11
12 from wptrunner import wptrunner
13 from wptrunner import wptcommandline
14 from mozlog import reader
15
16 logger = logging.getLogger(os.path.splitext(__file__)[0])
17
18
19 def setup_logging():
20 handler = logging.StreamHandler(sys.stdout)
21 formatter = logging.Formatter(logging.BASIC_FORMAT, None)
22 handler.setFormatter(formatter)
23 logger.addHandler(handler)
24 logger.setLevel(logging.DEBUG)
25
26 setup_logging()
27
28
29 def setup_github_logging(args):
30 gh_handler = None
31 if args.comment_pr:
32 if args.gh_token:
33 try:
34 pr_number = int(args.comment_pr)
35 except ValueError:
36 pass
37 else:
38 gh_handler = GitHubCommentHandler(args.gh_token, pr_number)
39 logger.debug("Setting up GitHub logging")
40 logger.addHandler(gh_handler)
41 else:
42 logger.error("Must provide --comment-pr and --github-token together" )
43 return gh_handler
44
45
46 class GitHubCommentHandler(logging.Handler):
47 def __init__(self, token, pull_number):
48 logging.Handler.__init__(self)
49 self.token = token
50 self.pull_number = pull_number
51 self.log_data = []
52
53 def emit(self, record):
54 try:
55 msg = self.format(record)
56 self.log_data.append(msg)
57 except Exception:
58 self.handleError(record)
59
60 def send(self):
61 headers = {"Accept": "application/vnd.github.v3+json"}
62 auth = (self.token, "x-oauth-basic")
63 url = "https://api.github.com/repos/w3c/web-platform-tests/issues/%s/com ments" %(
64 self.pull_number,)
65 resp = requests.post(
66 url,
67 data=json.dumps({"body": "\n".join(self.log_data)}),
68 headers=headers,
69 auth=auth
70 )
71 resp.raise_for_status()
72 self.log_data = []
73
74
75 class Firefox(object):
76 product = "firefox"
77
78 def wptrunner_args(self, root):
79 return {
80 "product": "firefox",
81 "binary": "%s/firefox/firefox" % root,
82 "certutil_binary": "certutil",
83 "webdriver_binary": "%s/geckodriver" % root,
84 "prefs_root": "%s/profiles" % root,
85 }
86
87
88 class Chrome(object):
89 product = "chrome"
90
91 def wptrunner_args(self, root):
92 return {
93 "product": "chrome",
94 "binary": "%s/chrome-linux/chrome" % root,
95 "webdriver_binary": "%s/chromedriver" % root,
96 "test_types": ["testharness", "reftest"]
97 }
98
99
100 def get_git_cmd(repo_path):
101 def git(cmd, *args):
102 full_cmd = ["git", cmd] + list(args)
103 try:
104 return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subpr ocess.STDOUT)
105 except subprocess.CalledProcessError as e:
106 logger.error("Git command exited with status %i" % e.returncode)
107 logger.error(e.output)
108 sys.exit(1)
109 return git
110
111
112 def get_files_changed(root):
113 git = get_git_cmd("%s/w3c/web-platform-tests" % root)
114 branch_point = git("merge-base", "HEAD", "master").strip()
115 files = git("diff", "--name-only", "-z", "%s.." % branch_point)
116 if not files:
117 return []
118 assert files[-1] == "\0"
119 return ["%s/w3c/web-platform-tests/%s" % (root, item)
120 for item in files[:-1].split("\0")]
121
122
123 def wptrunner_args(root, files_changed, iterations, browser):
124 parser = wptcommandline.create_parser([browser.product])
125 args = vars(parser.parse_args([]))
126 wpt_root = os.path.join(root, "w3c", "web-platform-tests")
127 args.update(browser.wptrunner_args(root))
128 args.update({
129 "tests_root": wpt_root,
130 "metadata_root": wpt_root,
131 "repeat": iterations,
132 "config": "%s/w3c/wptrunner/wptrunner.default.ini" % root,
133 "test_list": files_changed,
134 "restart_on_unexpected": False,
135 "pause_after_test": False
136 })
137 wptcommandline.check_args(args)
138 return args
139
140
141 class LogHandler(reader.LogHandler):
142 def __init__(self):
143 self.results = defaultdict(lambda: defaultdict(lambda: defaultdict(int)) )
144
145 def test_status(self, data):
146 self.results[data["test"]][data.get("subtest")][data["status"]] += 1
147
148 def test_end(self, data):
149 self.results[data["test"]][None][data["status"]] += 1
150
151
152 def is_inconsistent(results_dict, iterations):
153 return len(results_dict) > 1 or sum(results_dict.values()) != iterations
154
155
156 def err_string(results_dict):
157 rv = []
158 for key, value in sorted(results_dict.items()):
159 rv.append("%s: %i" % (key, value))
160 rv = " ".join(rv)
161 if len(results_dict) > 1:
162 rv = "**%s**" % rv
163 return rv
164
165
166 def process_results(log, iterations):
167 inconsistent = []
168 handler = LogHandler()
169 reader.handle_log(reader.read(log), handler)
170 results = handler.results
171 for test, test_results in results.iteritems():
172 for subtest, result in test_results.iteritems():
173 if is_inconsistent(result, iterations):
174 inconsistent.append((test, subtest, result))
175 return results, inconsistent
176
177
178 def write_inconsistent(inconsistent):
179 logger.error("## Unstable results ##\n")
180 logger.error("| Test | Subtest | Results |")
181 logger.error("|------|---------|---------|")
182 for test, subtest, results in inconsistent:
183 logger.error("%s | %s | %s" % (test,
184 subtest if subtest else "(parent)",
185 err_string(results)))
186
187
188 def write_results(results, iterations):
189 logger.info("## All results ##\n")
190 logger.info("| Test | Subtest | Results |")
191 logger.info("|------|---------|---------|")
192 for test, test_results in results.iteritems():
193 parent = test_results.pop(None)
194 logger.info("| %s | | %s |" % (test, err_string(parent)))
195 for subtest, result in test_results.iteritems():
196 logger.info("| | %s | %s |" % (subtest, err_string(result)))
197
198
199 def get_parser():
200 parser = argparse.ArgumentParser()
201 parser.add_argument("--root",
202 action="store",
203 default=os.path.join(os.path.expanduser("~"), "build"),
204 help="Root path")
205 parser.add_argument("--iterations",
206 action="store",
207 default=10,
208 type=int,
209 help="Number of times to run tests")
210 parser.add_argument("--gh-token",
211 action="store",
212 help="OAuth token to use for accessing GitHub api")
213 parser.add_argument("--comment-pr",
214 action="store",
215 help="PR to comment on with stability results")
216 parser.add_argument("browser",
217 action="store",
218 help="Browser to run against")
219 return parser
220
221
222 def main():
223 retcode = 0
224 parser = get_parser()
225 args = parser.parse_args()
226
227 gh_handler = setup_github_logging(args)
228
229 logger.info("Testing in **%s**" % args.browser.title())
230
231 browser_cls = {"firefox": Firefox,
232 "chrome": Chrome}.get(args.browser)
233 if browser_cls is None:
234 logger.critical("Unrecognised browser %s" % args.browser)
235 return 2
236
237 # For now just pass the whole list of changed files to wptrunner and
238 # assume that it will run everything that's actually a test
239 files_changed = get_files_changed(args.root)
240
241 if not files_changed:
242 return 0
243
244 logger.info("Files changed:\n%s" % "".join(" * %s\n" % item for item in file s_changed))
245
246 browser = browser_cls()
247 kwargs = wptrunner_args(args.root,
248 files_changed,
249 args.iterations,
250 browser)
251 with open("raw.log", "wb") as log:
252 wptrunner.setup_logging(kwargs,
253 {"mach": sys.stdout,
254 "raw": log})
255 wptrunner.run_tests(**kwargs)
256
257 with open("raw.log", "rb") as log:
258 results, inconsistent = process_results(log, args.iterations)
259
260 if results:
261 if inconsistent:
262 write_inconsistent(inconsistent)
263 retcode = 1
264 else:
265 logger.info("All results were stable\n")
266 write_results(results, args.iterations)
267 else:
268 logger.info("No tests run.")
269
270 try:
271 if gh_handler:
272 gh_handler.send()
273 except Exception:
274 logger.error(traceback.format_exc())
275 return retcode
276
277
278 if __name__ == "__main__":
279 try:
280 retcode = main()
281 except:
282 raise
283 else:
284 sys.exit(retcode)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698