blob: c9124a88a4300bd7e586e7fd030e235f8ec906e9 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (C) 2017 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
import argparse
import io
import logging
import zipfile
from webkitpy.common.host import Host
from webkitpy.common.net.bugzilla import bugzilla
from webkitpy.common.net.layouttestresults import LayoutTestResults
from webkitpy.common.webkit_finder import WebKitFinder
from webkitpy.layout_tests.controllers.test_result_writer import TestResultWriter
from webkitpy.layout_tests.models import test_expectations
_log = logging.getLogger(__name__)
def configure_logging():
class LogHandler(logging.StreamHandler):
def format(self, record):
if record.levelno > logging.INFO:
return "%s: %s" % (record.levelname, record.getMessage())
return record.getMessage()
logger = logging.getLogger()
logger.setLevel(logging.INFO)
handler = LogHandler()
handler.setLevel(logging.INFO)
logger.addHandler(handler)
return handler
def argument_parser():
description = """Update expected.txt files from patches submitted by EWS bots on bugzilla. Given id refers to a bug id by default."""
parser = argparse.ArgumentParser(prog='importupdate-test-expectations-from-bugzilla bugzilla_id', description=description, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-a', '--is-attachment-id', dest='is_bug_id', action='store_false', default=True, help='Whether the given id is a bugzilla attachment and not a bug id')
parser.add_argument('-s', '--is-platform-specific', dest='is_attachment_platform_specific', action='store_true', default=False, help='Whether generic expected files should be updated or not')
return parser
class TestExpectationUpdater(object):
def __init__(self, host, bugzilla_id, is_bug_id=True, is_attachment_platform_specific=False, attachment_fetcher=bugzilla.Bugzilla(), unzip=None):
self.host = host
self.filesystem = self.host.filesystem
self.unzip = unzip if unzip else lambda content: zipfile.ZipFile(io.BytesIO(content))
if is_bug_id:
self.platform_specific_attachments = {}
for attachment in attachment_fetcher.fetch_bug(bugzilla_id).attachments():
bot_type = self._bot_type(attachment)
if bot_type:
self.platform_specific_attachments[bot_type] = attachment
self.generic_attachment = self.platform_specific_attachments.pop("mac-wk2")
else:
attachment = attachment_fetcher.fetch_attachment(bugzilla_id)
self.platform_specific_attachments = {self._bot_type(attachment): attachment} if is_attachment_platform_specific else {}
self.generic_attachment = attachment if not is_attachment_platform_specific else None
webkit_finder = WebKitFinder(self.filesystem)
self.layout_test_repository = webkit_finder.path_from_webkit_base("LayoutTests")
def _bot_type(self, attachment):
name = attachment.name()
if "mac" in name and name.endswith("-wk2"):
return "mac-wk2"
if "mac" in name and not name.endswith("-wk2"):
return "mac-wk1"
if "simulator" in name:
return "ios-wk2"
return None
def _tests_to_update(self, attachment, bot_type=None):
_log.info("Processing attachment " + str(attachment.id()))
zip_file = self.unzip(attachment.contents())
results = LayoutTestResults.results_from_string(zip_file.read("full_results.json"))
results_to_update = [result.test_name for result in results.failing_test_results() if result.type == test_expectations.TEXT]
return {result: zip_file.read(TestResultWriter.actual_filename(result, self.filesystem)) for result in results_to_update}
def _file_content_if_exists(self, filename):
return self.filesystem.read_text_file(filename) if self.filesystem.exists(filename) else ""
# FIXME: Investigate the possibility to align what is done there with what single_test_runner is doing.
# In particular the fact of not overwriting the file if content is the same.
def _update_from_generic_attachment(self):
for test_name, expected_content in self._tests_to_update(self.generic_attachment).iteritems():
expected_filename = self.filesystem.join(self.layout_test_repository, TestResultWriter.expected_filename(test_name, self.filesystem))
if expected_content != self._file_content_if_exists(expected_filename):
_log.info("Updating " + test_name + " (" + expected_filename + ")")
self.filesystem.write_text_file(expected_filename, expected_content)
# FIXME: Investigate the possibility to align what is done there with what single_test_runner is doing.
# In particular the ability to remove a new specific expectation if it is the same as the generic one.
def _update_from_platform_specific_attachment(self, attachment, bot_type):
for test_name, expected_content in self._tests_to_update(attachment, bot_type).iteritems():
expected_filename = self.filesystem.join(self.layout_test_repository, TestResultWriter.expected_filename(test_name, self.filesystem, bot_type))
generic_expected_filename = self.filesystem.join(self.layout_test_repository, TestResultWriter.expected_filename(test_name, self.filesystem))
if expected_content != self._file_content_if_exists(generic_expected_filename):
_log.info("Updating " + test_name + " for " + bot_type + " (" + expected_filename + ")")
self.filesystem.maybe_make_directory(self.filesystem.dirname(expected_filename))
self.filesystem.write_text_file(expected_filename, expected_content)
elif self.filesystem.exists(expected_filename):
self.filesystem.remove(expected_filename)
def do_update(self):
if not self.generic_attachment or not self.platform_specific_attachments:
_log.info("No attachment to process")
return
self._update_from_generic_attachment()
for bot_type, attachment in self.platform_specific_attachments.iteritems():
self._update_from_platform_specific_attachment(attachment, bot_type)
def main(_argv, _stdout, _stderr):
parser = argument_parser()
options, args = parser.parse_known_args(_argv)
if not args:
raise Exception("Please provide a bug id or use -a option")
configure_logging()
bugzilla_id = args[0]
updater = TestExpectationUpdater(Host(), bugzilla_id, options.is_bug_id, options.is_attachment_platform_specific)
updater.do_update()