| 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 |