Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Side by Side Diff: chrome/test/mini_installer/test_installer.py

Issue 23523045: Clean the machine before running commands in the mini_installer test framework. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Address gab's comments. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """This script tests the installer with test cases specified in the config file. 5 """This script tests the installer with test cases specified in the config file.
6 6
7 For each test case, it checks that the machine states after the execution of 7 For each test case, it checks that the machine states after the execution of
8 each command match the expected machine states. For more details, take a look at 8 each command match the expected machine states. For more details, take a look at
9 the design documentation at http://goo.gl/Q0rGM6 9 the design documentation at http://goo.gl/Q0rGM6
10 """ 10 """
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 """ 62 """
63 return 'Test: %s' % (' -> '.join(self._test)) 63 return 'Test: %s' % (' -> '.join(self._test))
64 64
65 def runTest(self): 65 def runTest(self):
66 """Run the test case.""" 66 """Run the test case."""
67 # |test| is an array of alternating state names and action names, starting 67 # |test| is an array of alternating state names and action names, starting
68 # and ending with state names. Therefore, its length must be odd. 68 # and ending with state names. Therefore, its length must be odd.
69 self.assertEqual(1, len(self._test) % 2, 69 self.assertEqual(1, len(self._test) % 2,
70 'The length of test array must be odd') 70 'The length of test array must be odd')
71 71
72 # TODO(sukolsak): run a reset command that puts the machine in clean state.
73
74 state = self._test[0] 72 state = self._test[0]
75 self._VerifyState(state) 73 self._VerifyState(state)
76 74
77 # Starting at index 1, we loop through pairs of (action, state). 75 # Starting at index 1, we loop through pairs of (action, state).
78 for i in range(1, len(self._test), 2): 76 for i in range(1, len(self._test), 2):
79 action = self._test[i] 77 action = self._test[i]
80 self._RunCommand(self._config.actions[action]) 78 command = self._config.actions[action]
79 resolved_command = self._path_resolver.ResolvePath(command)
80 RunCommand(resolved_command)
81 81
82 state = self._test[i + 1] 82 state = self._test[i + 1]
83 self._VerifyState(state) 83 self._VerifyState(state)
84 84
85 def tearDown(self):
86 """Cleans up the machine after running the test case."""
87 RunCleanCommand(True)
gab 2013/09/16 13:59:58 We should only need to do this if the test case fa
sukolsak 2013/09/16 18:19:22 Done.
88
85 def shortDescription(self): 89 def shortDescription(self):
86 """Overridden from unittest.TestCase. 90 """Overridden from unittest.TestCase.
87 91
88 We return None as the short description to suppress its printing. 92 We return None as the short description to suppress its printing.
89 The default implementation of this method returns the docstring of the 93 The default implementation of this method returns the docstring of the
90 runTest method, which is not useful since it's the same for every test case. 94 runTest method, which is not useful since it's the same for every test case.
91 The description from the __str__ method is informative enough. 95 The description from the __str__ method is informative enough.
92 """ 96 """
93 return None 97 return None
94 98
95 def _VerifyState(self, state): 99 def _VerifyState(self, state):
96 """Verifies that the current machine state matches a given state. 100 """Verifies that the current machine state matches a given state.
97 101
98 Args: 102 Args:
99 state: A state name. 103 state: A state name.
100 """ 104 """
101 try: 105 try:
102 verifier.Verify(self._config.states[state], self._path_resolver) 106 verifier.Verify(self._config.states[state], self._path_resolver)
103 except AssertionError as e: 107 except AssertionError as e:
104 # If an AssertionError occurs, we intercept it and add the state name 108 # If an AssertionError occurs, we intercept it and add the state name
105 # to the error message so that we know where the test fails. 109 # to the error message so that we know where the test fails.
106 raise AssertionError("In state '%s', %s" % (state, e)) 110 raise AssertionError("In state '%s', %s" % (state, e))
107 111
108 def _RunCommand(self, command):
109 """Runs the given command from the current file's directory.
110 112
111 Args: 113 def RunCommand(command):
112 command: A command to run. It is expanded using ResolvePath. 114 """Runs the given command from the current file's directory.
113 """ 115
114 resolved_command = self._path_resolver.ResolvePath(command) 116 This function throws an Exception if the command returns with non-zero exit
115 script_dir = os.path.dirname(os.path.abspath(__file__)) 117 status.
116 exit_status = subprocess.call(resolved_command, shell=True, cwd=script_dir) 118
117 if exit_status != 0: 119 Args:
118 self.fail('Command %s returned non-zero exit status %s' % ( 120 command: A command to run.
119 resolved_command, exit_status)) 121 """
122 script_dir = os.path.dirname(os.path.abspath(__file__))
123 exit_status = subprocess.call(command, shell=True, cwd=script_dir)
124 if exit_status != 0:
125 raise Exception('Command %s returned non-zero exit status %s' % (
126 command, exit_status))
127
128
129 def RunCleanCommand(force_clean):
130 """Puts the machine in the clean state (i.e. Chrome not installed).
131
132 Args:
133 force_clean: A boolean indicating whether to force cleaning existing
134 installations.
135 """
136 # TODO(sukolsak): Read the clean state from the config file and clean
137 # the machine according to it.
138 # TODO(sukolsak): Handle Chrome SxS installs.
139 commands = []
140 interactive_option = '--interactive' if not force_clean else ''
141 for chrome_long_name in ['Google Chrome', 'Chromium']:
gab 2013/09/16 13:59:58 I don't like having to delete both brands; imo we
sukolsak 2013/09/16 18:19:22 Done.
142 for level_option in ['', '--system-level']:
143 commands.append('python uninstall_chrome.py --chrome-long-name="%s" '
144 '--no-error-if-absent %s %s' %
145 (chrome_long_name, level_option, interactive_option))
146 RunCommand(' && '.join(commands))
120 147
121 148
122 def MergePropertyDictionaries(current_property, new_property): 149 def MergePropertyDictionaries(current_property, new_property):
123 """Merges the new property dictionary into the current property dictionary. 150 """Merges the new property dictionary into the current property dictionary.
124 151
125 This is different from general dictionary merging in that, in case there are 152 This is different from general dictionary merging in that, in case there are
126 keys with the same name, we merge values together in the first level, and we 153 keys with the same name, we merge values together in the first level, and we
127 override earlier values in the second level. For more details, take a look at 154 override earlier values in the second level. For more details, take a look at
128 http://goo.gl/uE0RoR 155 http://goo.gl/uE0RoR
129 156
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 config = Config() 205 config = Config()
179 config.tests = config_data['tests'] 206 config.tests = config_data['tests']
180 for state_name, state_property_filenames in config_data['states']: 207 for state_name, state_property_filenames in config_data['states']:
181 config.states[state_name] = ParsePropertyFiles(directory, 208 config.states[state_name] = ParsePropertyFiles(directory,
182 state_property_filenames) 209 state_property_filenames)
183 for action_name, action_command in config_data['actions']: 210 for action_name, action_command in config_data['actions']:
184 config.actions[action_name] = action_command 211 config.actions[action_name] = action_command
185 return config 212 return config
186 213
187 214
188 def RunTests(mini_installer_path, config): 215 def RunTests(mini_installer_path, config, force_clean):
189 """Tests the installer using the given Config object. 216 """Tests the installer using the given Config object.
190 217
191 Args: 218 Args:
192 mini_installer_path: The path to mini_installer.exe. 219 mini_installer_path: The path to mini_installer.exe.
193 config: A Config object. 220 config: A Config object.
221 force_clean: A boolean indicating whether to force cleaning existing
222 installations.
194 223
195 Returns: 224 Returns:
196 True if all the tests passed, or False otherwise. 225 True if all the tests passed, or False otherwise.
197 """ 226 """
227 RunCleanCommand(force_clean)
198 suite = unittest.TestSuite() 228 suite = unittest.TestSuite()
199 path_resolver = PathResolver(mini_installer_path) 229 path_resolver = PathResolver(mini_installer_path)
200 for test in config.tests: 230 for test in config.tests:
201 suite.addTest(InstallerTest(test, config, path_resolver)) 231 suite.addTest(InstallerTest(test, config, path_resolver))
202 result = unittest.TextTestRunner(verbosity=2).run(suite) 232 result = unittest.TextTestRunner(verbosity=2).run(suite)
203 return result.wasSuccessful() 233 return result.wasSuccessful()
204 234
205 235
206 def main(): 236 def main():
207 usage = 'usage: %prog [options] config_filename' 237 usage = 'usage: %prog [options] config_filename'
208 parser = optparse.OptionParser(usage, description='Test the installer.') 238 parser = optparse.OptionParser(usage, description='Test the installer.')
209 parser.add_option('--build-dir', default='out', 239 parser.add_option('--build-dir', default='out',
210 help='Path to main build directory (the parent of the ' 240 help='Path to main build directory (the parent of the '
211 'Release or Debug directory)') 241 'Release or Debug directory)')
212 parser.add_option('--target', default='Release', 242 parser.add_option('--target', default='Release',
213 help='Build target (Release or Debug)') 243 help='Build target (Release or Debug)')
244 parser.add_option('--force-clean', action='store_true', dest='force_clean',
245 default=False, help='Force cleaning existing installations')
214 options, args = parser.parse_args() 246 options, args = parser.parse_args()
215 if len(args) != 1: 247 if len(args) != 1:
216 parser.error('Incorrect number of arguments.') 248 parser.error('Incorrect number of arguments.')
217 config_filename = args[0] 249 config_filename = args[0]
218 250
219 mini_installer_path = os.path.join(options.build_dir, options.target, 251 mini_installer_path = os.path.join(options.build_dir, options.target,
220 'mini_installer.exe') 252 'mini_installer.exe')
221 assert os.path.exists(mini_installer_path), ('Could not find file %s' % 253 assert os.path.exists(mini_installer_path), ('Could not find file %s' %
222 mini_installer_path) 254 mini_installer_path)
223 config = ParseConfigFile(config_filename) 255 config = ParseConfigFile(config_filename)
224 if not RunTests(mini_installer_path, config): 256 if not RunTests(mini_installer_path, config, options.force_clean):
225 return 1 257 return 1
226 return 0 258 return 0
227 259
228 260
229 if __name__ == '__main__': 261 if __name__ == '__main__':
230 sys.exit(main()) 262 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | chrome/test/mini_installer/uninstall_chrome.py » ('j') | chrome/test/mini_installer/uninstall_chrome.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698