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 31import fnmatch 32import logging 33import re 34 35from optparse import make_option 36 37from webkitpy.common.system.crashlogs import CrashLogs 38from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand 39from webkitpy.layout_tests.models.test_expectations import TestExpectations 40from webkitpy.layout_tests.port import platform_options 41 42_log = logging.getLogger(__name__) 43 44 45class 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 50and 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 61class 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 153class 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