1# Copyright (c) 2012 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import constants 6import traceback 7import warnings 8 9 10# Location where chrome reads command line flags from 11CHROME_COMMAND_FILE = constants.TEST_EXECUTABLE_DIR + '/chrome-command-line' 12 13class FlagChanger(object): 14 """Changes the flags Chrome runs with. 15 16 There are two different use cases for this file: 17 * Flags are permanently set by calling Set(). 18 * Flags can be temporarily set for a particular set of unit tests. These 19 tests should call Restore() to revert the flags to their original state 20 once the tests have completed. 21 """ 22 23 def __init__(self, android_cmd): 24 self._android_cmd = android_cmd 25 26 # Save the original flags. 27 self._orig_line = self._android_cmd.GetFileContents(CHROME_COMMAND_FILE) 28 if self._orig_line: 29 self._orig_line = self._orig_line[0].strip() 30 31 # Parse out the flags into a list to facilitate adding and removing flags. 32 self._current_flags = self._TokenizeFlags(self._orig_line) 33 34 def Get(self): 35 """Returns list of current flags.""" 36 return self._current_flags 37 38 def Set(self, flags): 39 """Replaces all flags on the current command line with the flags given. 40 41 Args: 42 flags: A list of flags to set, eg. ['--single-process']. 43 """ 44 if flags: 45 assert flags[0] != 'chrome' 46 47 self._current_flags = flags 48 self._UpdateCommandLineFile() 49 50 def AddFlags(self, flags): 51 """Appends flags to the command line if they aren't already there. 52 53 Args: 54 flags: A list of flags to add on, eg. ['--single-process']. 55 """ 56 if flags: 57 assert flags[0] != 'chrome' 58 59 # Avoid appending flags that are already present. 60 for flag in flags: 61 if flag not in self._current_flags: 62 self._current_flags.append(flag) 63 self._UpdateCommandLineFile() 64 65 def RemoveFlags(self, flags): 66 """Removes flags from the command line, if they exist. 67 68 Args: 69 flags: A list of flags to remove, eg. ['--single-process']. Note that we 70 expect a complete match when removing flags; if you want to remove 71 a switch with a value, you must use the exact string used to add 72 it in the first place. 73 """ 74 if flags: 75 assert flags[0] != 'chrome' 76 77 for flag in flags: 78 if flag in self._current_flags: 79 self._current_flags.remove(flag) 80 self._UpdateCommandLineFile() 81 82 def Restore(self): 83 """Restores the flags to their original state.""" 84 self._current_flags = self._TokenizeFlags(self._orig_line) 85 self._UpdateCommandLineFile() 86 87 def _UpdateCommandLineFile(self): 88 """Writes out the command line to the file, or removes it if empty.""" 89 print "Current flags: ", self._current_flags 90 91 if self._current_flags: 92 self._android_cmd.SetFileContents(CHROME_COMMAND_FILE, 93 'chrome ' + 94 ' '.join(self._current_flags)) 95 else: 96 self._android_cmd.RunShellCommand('rm ' + CHROME_COMMAND_FILE) 97 98 def _TokenizeFlags(self, line): 99 """Changes the string containing the command line into a list of flags. 100 101 Follows similar logic to CommandLine.java::tokenizeQuotedArguments: 102 * Flags are split using whitespace, unless the whitespace is within a 103 pair of quotation marks. 104 * Unlike the Java version, we keep the quotation marks around switch 105 values since we need them to re-create the file when new flags are 106 appended. 107 108 Args: 109 line: A string containing the entire command line. The first token is 110 assumed to be the program name. 111 """ 112 if not line: 113 return [] 114 115 tokenized_flags = [] 116 current_flag = "" 117 within_quotations = False 118 119 # Move through the string character by character and build up each flag 120 # along the way. 121 for c in line.strip(): 122 if c is '"': 123 if len(current_flag) > 0 and current_flag[-1] == '\\': 124 # Last char was a backslash; pop it, and treat this " as a literal. 125 current_flag = current_flag[0:-1] + '"' 126 else: 127 within_quotations = not within_quotations 128 current_flag += c 129 elif not within_quotations and (c is ' ' or c is '\t'): 130 if current_flag is not "": 131 tokenized_flags.append(current_flag) 132 current_flag = "" 133 else: 134 current_flag += c 135 136 # Tack on the last flag. 137 if not current_flag: 138 if within_quotations: 139 warnings.warn("Unterminated quoted string: " + current_flag) 140 else: 141 tokenized_flags.append(current_flag) 142 143 # Return everything but the program name. 144 return tokenized_flags[1:] 145