1# Copyright (c) 2010 Google Inc. All rights reserved. 2# Copyright (c) 2009 Apple Inc. All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: 7# 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above 11# copyright notice, this list of conditions and the following disclaimer 12# in the documentation and/or other materials provided with the 13# distribution. 14# * Neither the name of Google Inc. nor the names of its 15# contributors may be used to endorse or promote products derived from 16# this software without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30"""blink_tool.py is a tool with multiple sub-commands with different purposes. 31 32It has commands for printing expectations, fetching new test baselines, etc. 33These commands don't necessarily have anything to do with each other. 34""" 35 36import logging 37import optparse 38import sys 39 40from blinkpy.common.host import Host 41from blinkpy.tool.commands.analyze_baselines import AnalyzeBaselines 42from blinkpy.tool.commands.command import HelpPrintingOptionParser 43from blinkpy.tool.commands.copy_existing_baselines import CopyExistingBaselines 44from blinkpy.tool.commands.flaky_tests import FlakyTests 45from blinkpy.tool.commands.help_command import HelpCommand 46from blinkpy.tool.commands.optimize_baselines import OptimizeBaselines 47from blinkpy.tool.commands.pretty_diff import PrettyDiff 48from blinkpy.tool.commands.queries import CrashLog 49from blinkpy.tool.commands.queries import PrintBaselines 50from blinkpy.tool.commands.queries import PrintExpectations 51from blinkpy.tool.commands.rebaseline import Rebaseline 52from blinkpy.tool.commands.rebaseline_cl import RebaselineCL 53from blinkpy.tool.commands.rebaseline_test import RebaselineTest 54 55 56_log = logging.getLogger(__name__) 57 58 59class BlinkTool(Host): 60 # FIXME: It might make more sense if this class had a Host attribute 61 # instead of being a Host subclass. 62 63 global_options = [ 64 optparse.make_option( 65 '-v', '--verbose', action='store_true', dest='verbose', default=False, 66 help='enable all logging'), 67 optparse.make_option( 68 '-d', '--directory', action='append', default=[], 69 help='Directory to look at for changed files'), 70 ] 71 72 def __init__(self, path): 73 super(BlinkTool, self).__init__() 74 self._path = path 75 self.commands = [ 76 AnalyzeBaselines(), 77 CopyExistingBaselines(), 78 CrashLog(), 79 FlakyTests(), 80 OptimizeBaselines(), 81 PrettyDiff(), 82 PrintBaselines(), 83 PrintExpectations(), 84 Rebaseline(), 85 RebaselineCL(), 86 RebaselineTest(), 87 ] 88 self.help_command = HelpCommand(tool=self) 89 self.commands.append(self.help_command) 90 91 def main(self, argv=None): 92 argv = argv or sys.argv 93 (command_name, args) = self._split_command_name_from_args(argv[1:]) 94 95 option_parser = self._create_option_parser() 96 self._add_global_options(option_parser) 97 98 command = self.command_by_name(command_name) or self.help_command 99 if not command: 100 option_parser.error('%s is not a recognized command', command_name) 101 102 command.set_option_parser(option_parser) 103 (options, args) = command.parse_args(args) 104 105 result = command.check_arguments_and_execute(options, args, self) 106 return result 107 108 def path(self): 109 return self._path 110 111 @staticmethod 112 def _split_command_name_from_args(args): 113 # Assume the first argument which doesn't start with "-" is the command name. 114 command_index = 0 115 for arg in args: 116 if arg[0] != '-': 117 break 118 command_index += 1 119 else: 120 return (None, args[:]) 121 122 command = args[command_index] 123 return (command, args[:command_index] + args[command_index + 1:]) 124 125 def _create_option_parser(self): 126 usage = 'Usage: %prog [options] COMMAND [ARGS]' 127 name = optparse.OptionParser().get_prog_name() 128 return HelpPrintingOptionParser(epilog_method=self.help_command.help_epilog, prog=name, usage=usage) 129 130 def _add_global_options(self, option_parser): 131 global_options = self.global_options or [] 132 for option in global_options: 133 option_parser.add_option(option) 134 135 def name(self): 136 return optparse.OptionParser().get_prog_name() 137 138 def should_show_in_main_help(self, command): 139 return command.show_in_main_help 140 141 def command_by_name(self, command_name): 142 for command in self.commands: 143 if command_name == command.name: 144 return command 145 return None 146