Home | History | Annotate | Download | only in commands
      1 # Copyright (c) 2009 Google Inc. All rights reserved.
      2 # Copyright (c) 2009 Apple Inc. All rights reserved.
      3 # Copyright (c) 2012 Intel Corporation. All rights reserved.
      4 #
      5 # Redistribution and use in source and binary forms, with or without
      6 # modification, are permitted provided that the following conditions are
      7 # met:
      8 #
      9 #     * Redistributions of source code must retain the above copyright
     10 # notice, this list of conditions and the following disclaimer.
     11 #     * Redistributions in binary form must reproduce the above
     12 # copyright notice, this list of conditions and the following disclaimer
     13 # in the documentation and/or other materials provided with the
     14 # distribution.
     15 #     * Neither the name of Google Inc. nor the names of its
     16 # contributors may be used to endorse or promote products derived from
     17 # this software without specific prior written permission.
     18 #
     19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 import fnmatch
     32 import logging
     33 import re
     34 
     35 from optparse import make_option
     36 
     37 from webkitpy.common.system.crashlogs import CrashLogs
     38 from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
     39 from webkitpy.layout_tests.models.test_expectations import TestExpectations
     40 from webkitpy.layout_tests.port import platform_options
     41 
     42 _log = logging.getLogger(__name__)
     43 
     44 
     45 class CrashLog(AbstractDeclarativeCommand):
     46     name = "crash-log"
     47     help_text = "Print the newest crash log for the given process"
     48     show_in_main_help = True
     49     long_help = """Finds the newest crash log matching the given process name
     50 and PID and prints it to stdout."""
     51     argument_names = "PROCESS_NAME [PID]"
     52 
     53     def execute(self, options, args, tool):
     54         crash_logs = CrashLogs(tool)
     55         pid = None
     56         if len(args) > 1:
     57             pid = int(args[1])
     58         print crash_logs.find_newest_log(args[0], pid)
     59 
     60 
     61 class PrintExpectations(AbstractDeclarativeCommand):
     62     name = 'print-expectations'
     63     help_text = 'Print the expected result for the given test(s) on the given port(s)'
     64     show_in_main_help = True
     65 
     66     def __init__(self):
     67         options = [
     68             make_option('--all', action='store_true', default=False,
     69                         help='display the expectations for *all* tests'),
     70             make_option('-x', '--exclude-keyword', action='append', default=[],
     71                         help='limit to tests not matching the given keyword (for example, "skip", "slow", or "crash". May specify multiple times'),
     72             make_option('-i', '--include-keyword', action='append', default=[],
     73                         help='limit to tests with the given keyword (for example, "skip", "slow", or "crash". May specify multiple times'),
     74             make_option('--csv', action='store_true', default=False,
     75                         help='Print a CSV-style report that includes the port name, bugs, specifiers, tests, and expectations'),
     76             make_option('-f', '--full', action='store_true', default=False,
     77                         help='Print a full TestExpectations-style line for every match'),
     78             make_option('--paths', action='store_true', default=False,
     79                         help='display the paths for all applicable expectation files'),
     80         ] + platform_options(use_globs=True)
     81 
     82         AbstractDeclarativeCommand.__init__(self, options=options)
     83         self._expectation_models = {}
     84 
     85     def execute(self, options, args, tool):
     86         if not options.paths and not args and not options.all:
     87             print "You must either specify one or more test paths or --all."
     88             return
     89 
     90         if options.platform:
     91             port_names = fnmatch.filter(tool.port_factory.all_port_names(), options.platform)
     92             if not port_names:
     93                 default_port = tool.port_factory.get(options.platform)
     94                 if default_port:
     95                     port_names = [default_port.name()]
     96                 else:
     97                     print "No port names match '%s'" % options.platform
     98                     return
     99             else:
    100                 default_port = tool.port_factory.get(port_names[0])
    101         else:
    102             default_port = tool.port_factory.get(options=options)
    103             port_names = [default_port.name()]
    104 
    105         if options.paths:
    106             files = default_port.expectations_files()
    107             layout_tests_dir = default_port.layout_tests_dir()
    108             for file in files:
    109                 if file.startswith(layout_tests_dir):
    110                     file = file.replace(layout_tests_dir, 'LayoutTests')
    111                 print file
    112             return
    113 
    114         tests = set(default_port.tests(args))
    115         for port_name in port_names:
    116             model = self._model(options, port_name, tests)
    117             tests_to_print = self._filter_tests(options, model, tests)
    118             lines = [model.get_expectation_line(test) for test in sorted(tests_to_print)]
    119             if port_name != port_names[0]:
    120                 print
    121             print '\n'.join(self._format_lines(options, port_name, lines))
    122 
    123     def _filter_tests(self, options, model, tests):
    124         filtered_tests = set()
    125         if options.include_keyword:
    126             for keyword in options.include_keyword:
    127                 filtered_tests.update(model.get_test_set_for_keyword(keyword))
    128         else:
    129             filtered_tests = tests
    130 
    131         for keyword in options.exclude_keyword:
    132             filtered_tests.difference_update(model.get_test_set_for_keyword(keyword))
    133         return filtered_tests
    134 
    135     def _format_lines(self, options, port_name, lines):
    136         output = []
    137         if options.csv:
    138             for line in lines:
    139                 output.append("%s,%s" % (port_name, line.to_csv()))
    140         elif lines:
    141             include_modifiers = options.full
    142             include_expectations = options.full or len(options.include_keyword) != 1 or len(options.exclude_keyword)
    143             output.append("// For %s" % port_name)
    144             for line in lines:
    145                 output.append("%s" % line.to_string(None, include_modifiers, include_expectations, include_comment=False))
    146         return output
    147 
    148     def _model(self, options, port_name, tests):
    149         port = self._tool.port_factory.get(port_name, options)
    150         return TestExpectations(port, tests).model()
    151 
    152 
    153 class PrintBaselines(AbstractDeclarativeCommand):
    154     name = 'print-baselines'
    155     help_text = 'Prints the baseline locations for given test(s) on the given port(s)'
    156     show_in_main_help = True
    157 
    158     def __init__(self):
    159         options = [
    160             make_option('--all', action='store_true', default=False,
    161                         help='display the baselines for *all* tests'),
    162             make_option('--csv', action='store_true', default=False,
    163                         help='Print a CSV-style report that includes the port name, test_name, test platform, baseline type, baseline location, and baseline platform'),
    164             make_option('--include-virtual-tests', action='store_true',
    165                         help='Include virtual tests'),
    166         ] + platform_options(use_globs=True)
    167         AbstractDeclarativeCommand.__init__(self, options=options)
    168         self._platform_regexp = re.compile('platform/([^\/]+)/(.+)')
    169 
    170     def execute(self, options, args, tool):
    171         if not args and not options.all:
    172             print "You must either specify one or more test paths or --all."
    173             return
    174 
    175         default_port = tool.port_factory.get()
    176         if options.platform:
    177             port_names = fnmatch.filter(tool.port_factory.all_port_names(), options.platform)
    178             if not port_names:
    179                 print "No port names match '%s'" % options.platform
    180         else:
    181             port_names = [default_port.name()]
    182 
    183         if options.include_virtual_tests:
    184             tests = sorted(default_port.tests(args))
    185         else:
    186             # FIXME: make real_tests() a public method.
    187             tests = sorted(default_port._real_tests(args))
    188 
    189         for port_name in port_names:
    190             if port_name != port_names[0]:
    191                 print
    192             if not options.csv:
    193                 print "// For %s" % port_name
    194             port = tool.port_factory.get(port_name)
    195             for test_name in tests:
    196                 self._print_baselines(options, port_name, test_name, port.expected_baselines_by_extension(test_name))
    197 
    198     def _print_baselines(self, options, port_name, test_name, baselines):
    199         for extension in sorted(baselines.keys()):
    200             baseline_location = baselines[extension]
    201             if baseline_location:
    202                 if options.csv:
    203                     print "%s,%s,%s,%s,%s,%s" % (port_name, test_name, self._platform_for_path(test_name),
    204                                                  extension[1:], baseline_location, self._platform_for_path(baseline_location))
    205                 else:
    206                     print baseline_location
    207 
    208     def _platform_for_path(self, relpath):
    209         platform_matchobj = self._platform_regexp.match(relpath)
    210         if platform_matchobj:
    211             return platform_matchobj.group(1)
    212         return None
    213