Home | History | Annotate | Download | only in layout_package
      1 # Copyright (C) 2010 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #     * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #     * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #     * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import logging
     30 
     31 from webkitpy.layout_tests.layout_package import json_results_generator
     32 from webkitpy.layout_tests.layout_package import test_expectations
     33 from webkitpy.layout_tests.layout_package import test_failures
     34 import webkitpy.thirdparty.simplejson as simplejson
     35 
     36 
     37 class JSONLayoutResultsGenerator(json_results_generator.JSONResultsGeneratorBase):
     38     """A JSON results generator for layout tests."""
     39 
     40     LAYOUT_TESTS_PATH = "LayoutTests"
     41 
     42     # Additional JSON fields.
     43     WONTFIX = "wontfixCounts"
     44 
     45     FAILURE_TO_CHAR = {test_expectations.PASS: json_results_generator.JSONResultsGeneratorBase.PASS_RESULT,
     46                        test_expectations.SKIP: json_results_generator.JSONResultsGeneratorBase.SKIP_RESULT,
     47                        test_expectations.FAIL: "Y",
     48                        test_expectations.CRASH: "C",
     49                        test_expectations.TIMEOUT: "T",
     50                        test_expectations.IMAGE: "I",
     51                        test_expectations.TEXT: "F",
     52                        test_expectations.MISSING: "O",
     53                        test_expectations.AUDIO: "A",
     54                        test_expectations.IMAGE_PLUS_TEXT: "Z"}
     55 
     56     def __init__(self, port, builder_name, build_name, build_number,
     57         results_file_base_path, builder_base_url,
     58         test_timings, expectations, result_summary, all_tests,
     59         test_results_server=None, test_type="", master_name=""):
     60         """Modifies the results.json file. Grabs it off the archive directory
     61         if it is not found locally.
     62 
     63         Args:
     64           result_summary: ResultsSummary object storing the summary of the test
     65               results.
     66         """
     67         super(JSONLayoutResultsGenerator, self).__init__(
     68             port, builder_name, build_name, build_number, results_file_base_path,
     69             builder_base_url, {}, port.test_repository_paths(),
     70             test_results_server, test_type, master_name)
     71 
     72         self._expectations = expectations
     73 
     74         # We want relative paths to LayoutTest root for JSON output.
     75         path_to_name = self._get_path_relative_to_layout_test_root
     76         self._result_summary = result_summary
     77         self._failures = dict(
     78             (path_to_name(test), test_failures.determine_result_type(failures))
     79             for (test, failures) in result_summary.failures.iteritems())
     80         self._all_tests = [path_to_name(test) for test in all_tests]
     81         self._test_timings = dict(
     82             (path_to_name(test_tuple.filename), test_tuple.test_run_time)
     83             for test_tuple in test_timings)
     84 
     85         self.generate_json_output()
     86 
     87     def _get_path_relative_to_layout_test_root(self, test):
     88         """Returns the path of the test relative to the layout test root.
     89         For example, for:
     90           src/third_party/WebKit/LayoutTests/fast/forms/foo.html
     91         We would return
     92           fast/forms/foo.html
     93         """
     94         index = test.find(self.LAYOUT_TESTS_PATH)
     95         if index is not -1:
     96             index += len(self.LAYOUT_TESTS_PATH)
     97 
     98         if index is -1:
     99             # Already a relative path.
    100             relativePath = test
    101         else:
    102             relativePath = test[index + 1:]
    103 
    104         # Make sure all paths are unix-style.
    105         return relativePath.replace('\\', '/')
    106 
    107     # override
    108     def _get_test_timing(self, test_name):
    109         if test_name in self._test_timings:
    110             # Floor for now to get time in seconds.
    111             return int(self._test_timings[test_name])
    112         return 0
    113 
    114     # override
    115     def _get_failed_test_names(self):
    116         return set(self._failures.keys())
    117 
    118     # override
    119     def _get_modifier_char(self, test_name):
    120         if test_name not in self._all_tests:
    121             return self.NO_DATA_RESULT
    122 
    123         if test_name in self._failures:
    124             return self.FAILURE_TO_CHAR[self._failures[test_name]]
    125 
    126         return self.PASS_RESULT
    127 
    128     # override
    129     def _get_result_char(self, test_name):
    130         return self._get_modifier_char(test_name)
    131 
    132     # override
    133     def _convert_json_to_current_version(self, results_json):
    134         archive_version = None
    135         if self.VERSION_KEY in results_json:
    136             archive_version = results_json[self.VERSION_KEY]
    137 
    138         super(JSONLayoutResultsGenerator,
    139               self)._convert_json_to_current_version(results_json)
    140 
    141         # version 2->3
    142         if archive_version == 2:
    143             for results_for_builder in results_json.itervalues():
    144                 try:
    145                     test_results = results_for_builder[self.TESTS]
    146                 except:
    147                     continue
    148 
    149             for test in test_results:
    150                 # Make sure all paths are relative
    151                 test_path = self._get_path_relative_to_layout_test_root(test)
    152                 if test_path != test:
    153                     test_results[test_path] = test_results[test]
    154                     del test_results[test]
    155 
    156     # override
    157     def _insert_failure_summaries(self, results_for_builder):
    158         summary = self._result_summary
    159 
    160         self._insert_item_into_raw_list(results_for_builder,
    161             len((set(summary.failures.keys()) |
    162                 summary.tests_by_expectation[test_expectations.SKIP]) &
    163                 summary.tests_by_timeline[test_expectations.NOW]),
    164             self.FIXABLE_COUNT)
    165         self._insert_item_into_raw_list(results_for_builder,
    166             self._get_failure_summary_entry(test_expectations.NOW),
    167             self.FIXABLE)
    168         self._insert_item_into_raw_list(results_for_builder,
    169             len(self._expectations.get_tests_with_timeline(
    170                 test_expectations.NOW)), self.ALL_FIXABLE_COUNT)
    171         self._insert_item_into_raw_list(results_for_builder,
    172             self._get_failure_summary_entry(test_expectations.WONTFIX),
    173             self.WONTFIX)
    174 
    175     # override
    176     def _normalize_results_json(self, test, test_name, tests):
    177         super(JSONLayoutResultsGenerator, self)._normalize_results_json(
    178             test, test_name, tests)
    179 
    180         # Remove tests that don't exist anymore.
    181         full_path = self._fs.join(self._port.layout_tests_dir(), test_name)
    182         full_path = self._fs.normpath(full_path)
    183         if not self._fs.exists(full_path):
    184             del tests[test_name]
    185 
    186     def _get_failure_summary_entry(self, timeline):
    187         """Creates a summary object to insert into the JSON.
    188 
    189         Args:
    190           summary   ResultSummary object with test results
    191           timeline  current test_expectations timeline to build entry for
    192                     (e.g., test_expectations.NOW, etc.)
    193         """
    194         entry = {}
    195         summary = self._result_summary
    196         timeline_tests = summary.tests_by_timeline[timeline]
    197         entry[self.SKIP_RESULT] = len(
    198             summary.tests_by_expectation[test_expectations.SKIP] &
    199             timeline_tests)
    200         entry[self.PASS_RESULT] = len(
    201             summary.tests_by_expectation[test_expectations.PASS] &
    202             timeline_tests)
    203         for failure_type in summary.tests_by_expectation.keys():
    204             if failure_type not in self.FAILURE_TO_CHAR:
    205                 continue
    206             count = len(summary.tests_by_expectation[failure_type] &
    207                         timeline_tests)
    208             entry[self.FAILURE_TO_CHAR[failure_type]] = count
    209         return entry
    210