OLD | NEW |
1 import argparse | 1 import argparse |
2 import json | 2 import json |
3 import logging | 3 import logging |
4 import os | 4 import os |
5 import re | 5 import re |
6 import stat | 6 import stat |
7 import subprocess | 7 import subprocess |
8 import sys | 8 import sys |
9 import tarfile | 9 import tarfile |
10 import traceback | 10 import traceback |
11 import zipfile | 11 import zipfile |
12 from cStringIO import StringIO | 12 from cStringIO import StringIO |
13 from collections import defaultdict | 13 from collections import defaultdict |
14 from urlparse import urljoin | 14 from urlparse import urljoin |
15 | 15 |
16 import requests | 16 import requests |
17 | 17 |
| 18 BaseHandler = None |
| 19 LogActionFilter = None |
| 20 LogHandler = None |
| 21 LogLevelFilter = None |
| 22 StreamHandler = None |
| 23 TbplFormatter = None |
| 24 reader = None |
| 25 wptcommandline = None |
18 wptrunner = None | 26 wptrunner = None |
19 wptcommandline = None | 27 |
20 reader = None | |
21 LogHandler = None | |
22 | 28 |
23 logger = logging.getLogger(os.path.splitext(__file__)[0]) | 29 logger = logging.getLogger(os.path.splitext(__file__)[0]) |
24 | 30 |
25 | 31 |
26 def do_delayed_imports(): | 32 def do_delayed_imports(): |
27 global wptrunner, wptcommandline, reader | 33 global BaseHandler |
28 from wptrunner import wptrunner | 34 global LogLevelFilter |
29 from wptrunner import wptcommandline | 35 global StreamHandler |
| 36 global TbplFormatter |
| 37 global reader |
| 38 global wptcommandline |
| 39 global wptrunner |
30 from mozlog import reader | 40 from mozlog import reader |
| 41 from mozlog.formatters import TbplFormatter |
| 42 from mozlog.handlers import BaseHandler, LogLevelFilter, StreamHandler |
| 43 from wptrunner import wptcommandline, wptrunner |
31 setup_log_handler() | 44 setup_log_handler() |
| 45 setup_action_filter() |
32 | 46 |
33 | 47 |
34 def setup_logging(): | 48 def setup_logging(): |
35 handler = logging.StreamHandler(sys.stdout) | 49 handler = logging.StreamHandler(sys.stdout) |
36 formatter = logging.Formatter(logging.BASIC_FORMAT, None) | 50 formatter = logging.Formatter(logging.BASIC_FORMAT, None) |
37 handler.setFormatter(formatter) | 51 handler.setFormatter(formatter) |
38 logger.addHandler(handler) | 52 logger.addHandler(handler) |
39 logger.setLevel(logging.DEBUG) | 53 logger.setLevel(logging.DEBUG) |
40 | 54 |
41 setup_logging() | 55 setup_logging() |
42 | 56 |
43 | 57 |
| 58 def setup_action_filter(): |
| 59 global LogActionFilter |
| 60 |
| 61 class LogActionFilter(BaseHandler): |
| 62 """Handler that filters out messages with action of log and a level |
| 63 lower than some specified level. |
| 64 |
| 65 :param inner: Handler to use for messages that pass this filter |
| 66 :param level: Minimum log level to process |
| 67 """ |
| 68 def __init__(self, inner, actions): |
| 69 BaseHandler.__init__(self, inner) |
| 70 self.inner = inner |
| 71 self.actions = actions |
| 72 |
| 73 def __call__(self, item): |
| 74 if item["action"] in self.actions: |
| 75 return self.inner(item) |
| 76 |
| 77 |
44 class GitHub(object): | 78 class GitHub(object): |
45 def __init__(self, org, repo, token): | 79 def __init__(self, org, repo, token): |
46 self.token = token | 80 self.token = token |
47 self.headers = {"Accept": "application/vnd.github.v3+json"} | 81 self.headers = {"Accept": "application/vnd.github.v3+json"} |
48 self.auth = (self.token, "x-oauth-basic") | 82 self.auth = (self.token, "x-oauth-basic") |
49 self.org = org | 83 self.org = org |
50 self.repo = repo | 84 self.repo = repo |
51 self.base_url = "https://api.github.com/repos/%s/%s/" % (org, repo) | 85 self.base_url = "https://api.github.com/repos/%s/%s/" % (org, repo) |
52 | 86 |
53 def _headers(self, headers): | 87 def _headers(self, headers): |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 os.mkdir("profiles") | 161 os.mkdir("profiles") |
128 with open(os.path.join("profiles", "prefs_general.js"), "wb") as f: | 162 with open(os.path.join("profiles", "prefs_general.js"), "wb") as f: |
129 resp = get("https://hg.mozilla.org/mozilla-central/raw-file/tip/test
ing/profiles/prefs_general.js") | 163 resp = get("https://hg.mozilla.org/mozilla-central/raw-file/tip/test
ing/profiles/prefs_general.js") |
130 f.write(resp.content) | 164 f.write(resp.content) |
131 call("pip", "install", "-r", os.path.join("w3c", "wptrunner", "requireme
nts_firefox.txt")) | 165 call("pip", "install", "-r", os.path.join("w3c", "wptrunner", "requireme
nts_firefox.txt")) |
132 | 166 |
133 def _latest_geckodriver_version(self): | 167 def _latest_geckodriver_version(self): |
134 # This is used rather than an API call to avoid rate limits | 168 # This is used rather than an API call to avoid rate limits |
135 tags = call("git", "ls-remote", "--tags", "--refs", | 169 tags = call("git", "ls-remote", "--tags", "--refs", |
136 "https://github.com/mozilla/geckodriver.git") | 170 "https://github.com/mozilla/geckodriver.git") |
137 logger.debug("Found tags:\n%s" % tags) | |
138 release_re = re.compile(".*refs/tags/v(\d+)\.(\d+)\.(\d+)") | 171 release_re = re.compile(".*refs/tags/v(\d+)\.(\d+)\.(\d+)") |
139 latest_release = 0 | 172 latest_release = 0 |
140 for item in tags.split("\n"): | 173 for item in tags.split("\n"): |
141 m = release_re.match(item) | 174 m = release_re.match(item) |
142 if m: | 175 if m: |
143 version = [int(item) for item in m.groups()] | 176 version = [int(item) for item in m.groups()] |
144 if version > latest_release: | 177 if version > latest_release: |
145 latest_release = version | 178 latest_release = version |
146 assert latest_release != 0 | 179 assert latest_release != 0 |
147 return "v%s.%s.%s" % tuple(str(item) for item in latest_release) | 180 return "v%s.%s.%s" % tuple(str(item) for item in latest_release) |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 max_widths = reduce(lambda prev, cur: [(len(cur[i]) + 2) | 408 max_widths = reduce(lambda prev, cur: [(len(cur[i]) + 2) |
376 if (len(cur[i]) + 2) > prev[i] | 409 if (len(cur[i]) + 2) > prev[i] |
377 else prev[i] | 410 else prev[i] |
378 for i in cols], | 411 for i in cols], |
379 data, | 412 data, |
380 [len(item) + 2 for item in headings]) | 413 [len(item) + 2 for item in headings]) |
381 log("|%s|" % "|".join(item.center(max_widths[i]) for i, item in enumerate(he
adings))) | 414 log("|%s|" % "|".join(item.center(max_widths[i]) for i, item in enumerate(he
adings))) |
382 log("|%s|" % "|".join("-" * max_widths[i] for i in cols)) | 415 log("|%s|" % "|".join("-" * max_widths[i] for i in cols)) |
383 for row in data: | 416 for row in data: |
384 log("|%s|" % "|".join(" %s" % row[i].ljust(max_widths[i] - 1) for i in c
ols)) | 417 log("|%s|" % "|".join(" %s" % row[i].ljust(max_widths[i] - 1) for i in c
ols)) |
| 418 log("") |
385 | 419 |
386 | 420 |
387 def write_inconsistent(inconsistent, iterations): | 421 def write_inconsistent(inconsistent, iterations): |
388 logger.error("## Unstable results ##\n") | 422 logger.error("## Unstable results ##\n") |
389 strings = [(test, subtest if subtest else "", err_string(results, iterations
)) | 423 strings = [(test, subtest if subtest else "", err_string(results, iterations
)) |
390 for test, subtest, results in inconsistent] | 424 for test, subtest, results in inconsistent] |
391 table(["Test", "Subtest", "Results"], strings, logger.error) | 425 table(["Test", "Subtest", "Results"], strings, logger.error) |
392 | 426 |
393 | 427 |
394 def write_results(results, iterations): | 428 def write_results(results, iterations, comment_pr): |
395 logger.info("## All results ##\n") | 429 logger.info("## All results ##\n") |
396 for test, test_results in results.iteritems(): | 430 for test, test_results in results.iteritems(): |
397 logger.info("### %s ###" % test) | 431 baseurl = "http://w3c-test.org/submissions" |
| 432 if "https" in os.path.splitext(test)[0].split(".")[1:]: |
| 433 baseurl = "https://w3c-test.org/submissions" |
| 434 pr_number = None |
| 435 if comment_pr: |
| 436 try: |
| 437 pr_number = int(comment_pr) |
| 438 except ValueError: |
| 439 pass |
| 440 if pr_number: |
| 441 logger.info("### [%s](%s/%s%s) ###" % (test, baseurl, pr_number, tes
t)) |
| 442 else: |
| 443 logger.info("### %s ###" % test) |
398 parent = test_results.pop(None) | 444 parent = test_results.pop(None) |
399 strings = [("", err_string(parent, iterations))] | 445 strings = [("", err_string(parent, iterations))] |
400 strings.extend(((subtest if subtest else "", err_string(results, iterati
ons)) | 446 strings.extend(((subtest if subtest else "", err_string(results, iterati
ons)) |
401 for subtest, results in test_results.iteritems())) | 447 for subtest, results in test_results.iteritems())) |
402 table(["Subtest", "Results"], strings, logger.info) | 448 table(["Subtest", "Results"], strings, logger.info) |
403 | 449 |
404 | 450 |
405 def get_parser(): | 451 def get_parser(): |
406 parser = argparse.ArgumentParser() | 452 parser = argparse.ArgumentParser() |
407 parser.add_argument("--root", | 453 parser.add_argument("--root", |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 browser.install() | 523 browser.install() |
478 browser.install_webdriver() | 524 browser.install_webdriver() |
479 | 525 |
480 kwargs = wptrunner_args(args.root, | 526 kwargs = wptrunner_args(args.root, |
481 files_changed, | 527 files_changed, |
482 args.iterations, | 528 args.iterations, |
483 browser) | 529 browser) |
484 | 530 |
485 print >> sys.stderr, "travis_fold:end:browser_setup" | 531 print >> sys.stderr, "travis_fold:end:browser_setup" |
486 print >> sys.stderr, "travis_fold:start:running_tests" | 532 print >> sys.stderr, "travis_fold:start:running_tests" |
| 533 logger.info("Starting %i test iterations" % args.iterations) |
487 with open("raw.log", "wb") as log: | 534 with open("raw.log", "wb") as log: |
488 wptrunner.setup_logging(kwargs, | 535 wptrunner.setup_logging(kwargs, |
489 {"tbpl": sys.stdout, | 536 {"raw": log}) |
490 "raw": log}) | 537 # Setup logging for wptrunner that keeps process output and |
| 538 # warning+ level logs only |
| 539 wptrunner.logger.add_handler( |
| 540 LogActionFilter( |
| 541 LogLevelFilter( |
| 542 StreamHandler( |
| 543 sys.stdout, |
| 544 TbplFormatter() |
| 545 ), |
| 546 "WARNING"), |
| 547 ["log", "process_output"])) |
| 548 |
491 wptrunner.run_tests(**kwargs) | 549 wptrunner.run_tests(**kwargs) |
492 | 550 |
493 with open("raw.log", "rb") as log: | 551 with open("raw.log", "rb") as log: |
494 results, inconsistent = process_results(log, args.iterations) | 552 results, inconsistent = process_results(log, args.iterations) |
495 | 553 |
496 print >> sys.stderr, "travis_fold:end:running_tests" | 554 print >> sys.stderr, "travis_fold:end:running_tests" |
497 | 555 |
498 if results: | 556 if results: |
499 if inconsistent: | 557 if inconsistent: |
500 write_inconsistent(inconsistent, args.iterations) | 558 write_inconsistent(inconsistent, args.iterations) |
501 retcode = 2 | 559 retcode = 2 |
502 else: | 560 else: |
503 logger.info("All results were stable\n") | 561 logger.info("All results were stable\n") |
504 print >> sys.stderr, "travis_fold:start:full_results" | 562 print >> sys.stderr, "travis_fold:start:full_results" |
505 write_results(results, args.iterations) | 563 write_results(results, args.iterations, args.comment_pr) |
506 print >> sys.stderr, "travis_fold:end:full_results" | 564 print >> sys.stderr, "travis_fold:end:full_results" |
507 else: | 565 else: |
508 logger.info("No tests run.") | 566 logger.info("No tests run.") |
509 | 567 |
510 try: | 568 try: |
511 if gh_handler: | 569 if gh_handler: |
512 gh_handler.send() | 570 gh_handler.send() |
513 except Exception: | 571 except Exception: |
514 logger.error(traceback.format_exc()) | 572 logger.error(traceback.format_exc()) |
515 return retcode | 573 return retcode |
516 | 574 |
517 | 575 |
518 if __name__ == "__main__": | 576 if __name__ == "__main__": |
519 try: | 577 try: |
520 retcode = main() | 578 retcode = main() |
521 except: | 579 except: |
522 raise | 580 raise |
523 else: | 581 else: |
524 sys.exit(retcode) | 582 sys.exit(retcode) |
OLD | NEW |