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

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 comment. 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
« no previous file with comments | « no previous file | chrome/test/mini_installer/uninstall_chrome.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 Args: 45 Args:
46 test: An array of alternating state names and action names, starting and 46 test: An array of alternating state names and action names, starting and
47 ending with state names. 47 ending with state names.
48 config: The Config object. 48 config: The Config object.
49 path_resolver: A PathResolver object. 49 path_resolver: A PathResolver object.
50 """ 50 """
51 super(InstallerTest, self).__init__() 51 super(InstallerTest, self).__init__()
52 self._test = test 52 self._test = test
53 self._config = config 53 self._config = config
54 self._path_resolver = path_resolver 54 self._path_resolver = path_resolver
55 self._clean_on_teardown = True
55 56
56 def __str__(self): 57 def __str__(self):
57 """Returns a string representing the test case. 58 """Returns a string representing the test case.
58 59
59 Returns: 60 Returns:
60 A string created by joining state names and action names together with 61 A string created by joining state names and action names together with
61 ' -> ', for example, 'Test: clean -> install chrome -> chrome_installed'. 62 ' -> ', for example, 'Test: clean -> install chrome -> chrome_installed'.
62 """ 63 """
63 return 'Test: %s' % (' -> '.join(self._test)) 64 return 'Test: %s' % (' -> '.join(self._test))
64 65
65 def runTest(self): 66 def runTest(self):
66 """Run the test case.""" 67 """Run the test case."""
67 # |test| is an array of alternating state names and action names, starting 68 # |test| is an array of alternating state names and action names, starting
68 # and ending with state names. Therefore, its length must be odd. 69 # and ending with state names. Therefore, its length must be odd.
69 self.assertEqual(1, len(self._test) % 2, 70 self.assertEqual(1, len(self._test) % 2,
70 'The length of test array must be odd') 71 'The length of test array must be odd')
71 72
72 # TODO(sukolsak): run a reset command that puts the machine in clean state.
73
74 state = self._test[0] 73 state = self._test[0]
75 self._VerifyState(state) 74 self._VerifyState(state)
76 75
77 # Starting at index 1, we loop through pairs of (action, state). 76 # Starting at index 1, we loop through pairs of (action, state).
78 for i in range(1, len(self._test), 2): 77 for i in range(1, len(self._test), 2):
79 action = self._test[i] 78 action = self._test[i]
80 self._RunCommand(self._config.actions[action]) 79 RunCommand(self._config.actions[action], self._path_resolver)
81 80
82 state = self._test[i + 1] 81 state = self._test[i + 1]
83 self._VerifyState(state) 82 self._VerifyState(state)
84 83
84 # If the test makes it here, it means it was successful, because RunCommand
85 # and _VerifyState throw an exception on failure.
86 self._clean_on_teardown = False
87
88 def tearDown(self):
89 """Cleans up the machine if the test case fails."""
90 if self._clean_on_teardown:
91 RunCleanCommand(True, self._path_resolver)
92
85 def shortDescription(self): 93 def shortDescription(self):
86 """Overridden from unittest.TestCase. 94 """Overridden from unittest.TestCase.
87 95
88 We return None as the short description to suppress its printing. 96 We return None as the short description to suppress its printing.
89 The default implementation of this method returns the docstring of the 97 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. 98 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. 99 The description from the __str__ method is informative enough.
92 """ 100 """
93 return None 101 return None
94 102
95 def _VerifyState(self, state): 103 def _VerifyState(self, state):
96 """Verifies that the current machine state matches a given state. 104 """Verifies that the current machine state matches a given state.
97 105
98 Args: 106 Args:
99 state: A state name. 107 state: A state name.
100 """ 108 """
101 try: 109 try:
102 verifier.Verify(self._config.states[state], self._path_resolver) 110 verifier.Verify(self._config.states[state], self._path_resolver)
103 except AssertionError as e: 111 except AssertionError as e:
104 # If an AssertionError occurs, we intercept it and add the state name 112 # 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. 113 # to the error message so that we know where the test fails.
106 raise AssertionError("In state '%s', %s" % (state, e)) 114 raise AssertionError("In state '%s', %s" % (state, e))
107 115
108 def _RunCommand(self, command):
109 """Runs the given command from the current file's directory.
110 116
111 Args: 117 def RunCommand(command, path_resolver):
112 command: A command to run. It is expanded using ResolvePath. 118 """Runs the given command from the current file's directory.
113 """ 119
114 resolved_command = self._path_resolver.ResolvePath(command) 120 This function throws an Exception if the command returns with non-zero exit
115 script_dir = os.path.dirname(os.path.abspath(__file__)) 121 status.
116 exit_status = subprocess.call(resolved_command, shell=True, cwd=script_dir) 122
117 if exit_status != 0: 123 Args:
118 self.fail('Command %s returned non-zero exit status %s' % ( 124 command: A command to run. It is expanded using ResolvePath.
119 resolved_command, exit_status)) 125 path_resolver: A PathResolver object.
126 """
127 resolved_command = path_resolver.ResolvePath(command)
128 script_dir = os.path.dirname(os.path.abspath(__file__))
129 exit_status = subprocess.call(resolved_command, shell=True, cwd=script_dir)
130 if exit_status != 0:
131 raise Exception('Command %s returned non-zero exit status %s' % (
132 command, exit_status))
133
134
135 def RunCleanCommand(force_clean, path_resolver):
136 """Puts the machine in the clean state (i.e. Chrome not installed).
137
138 Args:
139 force_clean: A boolean indicating whether to force cleaning existing
140 installations.
141 path_resolver: A PathResolver object.
142 """
143 # TODO(sukolsak): Read the clean state from the config file and clean
144 # the machine according to it.
145 # TODO(sukolsak): Handle Chrome SxS installs.
146 commands = []
147 interactive_option = '--interactive' if not force_clean else ''
148 for level_option in ['', '--system-level']:
Mathieu 2013/09/16 22:32:12 very optional nit: tuples are more memory efficien
sukolsak 2013/09/16 22:38:54 Done.
149 commands.append('python uninstall_chrome.py '
150 '--chrome-long-name="$CHROME_LONG_NAME" '
151 '--no-error-if-absent %s %s' %
152 (level_option, interactive_option))
153 RunCommand(' && '.join(commands), path_resolver)
120 154
121 155
122 def MergePropertyDictionaries(current_property, new_property): 156 def MergePropertyDictionaries(current_property, new_property):
123 """Merges the new property dictionary into the current property dictionary. 157 """Merges the new property dictionary into the current property dictionary.
124 158
125 This is different from general dictionary merging in that, in case there are 159 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 160 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 161 override earlier values in the second level. For more details, take a look at
128 http://goo.gl/uE0RoR 162 http://goo.gl/uE0RoR
129 163
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 config = Config() 212 config = Config()
179 config.tests = config_data['tests'] 213 config.tests = config_data['tests']
180 for state_name, state_property_filenames in config_data['states']: 214 for state_name, state_property_filenames in config_data['states']:
181 config.states[state_name] = ParsePropertyFiles(directory, 215 config.states[state_name] = ParsePropertyFiles(directory,
182 state_property_filenames) 216 state_property_filenames)
183 for action_name, action_command in config_data['actions']: 217 for action_name, action_command in config_data['actions']:
184 config.actions[action_name] = action_command 218 config.actions[action_name] = action_command
185 return config 219 return config
186 220
187 221
188 def RunTests(mini_installer_path, config): 222 def RunTests(mini_installer_path, config, force_clean):
189 """Tests the installer using the given Config object. 223 """Tests the installer using the given Config object.
190 224
191 Args: 225 Args:
192 mini_installer_path: The path to mini_installer.exe. 226 mini_installer_path: The path to mini_installer.exe.
193 config: A Config object. 227 config: A Config object.
228 force_clean: A boolean indicating whether to force cleaning existing
229 installations.
194 230
195 Returns: 231 Returns:
196 True if all the tests passed, or False otherwise. 232 True if all the tests passed, or False otherwise.
197 """ 233 """
198 suite = unittest.TestSuite() 234 suite = unittest.TestSuite()
199 path_resolver = PathResolver(mini_installer_path) 235 path_resolver = PathResolver(mini_installer_path)
236 RunCleanCommand(force_clean, path_resolver)
200 for test in config.tests: 237 for test in config.tests:
201 suite.addTest(InstallerTest(test, config, path_resolver)) 238 suite.addTest(InstallerTest(test, config, path_resolver))
202 result = unittest.TextTestRunner(verbosity=2).run(suite) 239 result = unittest.TextTestRunner(verbosity=2).run(suite)
203 return result.wasSuccessful() 240 return result.wasSuccessful()
204 241
205 242
206 def main(): 243 def main():
207 usage = 'usage: %prog [options] config_filename' 244 usage = 'usage: %prog [options] config_filename'
208 parser = optparse.OptionParser(usage, description='Test the installer.') 245 parser = optparse.OptionParser(usage, description='Test the installer.')
209 parser.add_option('--build-dir', default='out', 246 parser.add_option('--build-dir', default='out',
210 help='Path to main build directory (the parent of the ' 247 help='Path to main build directory (the parent of the '
211 'Release or Debug directory)') 248 'Release or Debug directory)')
212 parser.add_option('--target', default='Release', 249 parser.add_option('--target', default='Release',
213 help='Build target (Release or Debug)') 250 help='Build target (Release or Debug)')
251 parser.add_option('--force-clean', action='store_true', dest='force_clean',
252 default=False, help='Force cleaning existing installations')
214 options, args = parser.parse_args() 253 options, args = parser.parse_args()
215 if len(args) != 1: 254 if len(args) != 1:
216 parser.error('Incorrect number of arguments.') 255 parser.error('Incorrect number of arguments.')
217 config_filename = args[0] 256 config_filename = args[0]
218 257
219 mini_installer_path = os.path.join(options.build_dir, options.target, 258 mini_installer_path = os.path.join(options.build_dir, options.target,
220 'mini_installer.exe') 259 'mini_installer.exe')
221 assert os.path.exists(mini_installer_path), ('Could not find file %s' % 260 assert os.path.exists(mini_installer_path), ('Could not find file %s' %
222 mini_installer_path) 261 mini_installer_path)
223 config = ParseConfigFile(config_filename) 262 config = ParseConfigFile(config_filename)
224 if not RunTests(mini_installer_path, config): 263 if not RunTests(mini_installer_path, config, options.force_clean):
225 return 1 264 return 1
226 return 0 265 return 0
227 266
228 267
229 if __name__ == '__main__': 268 if __name__ == '__main__':
230 sys.exit(main()) 269 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | chrome/test/mini_installer/uninstall_chrome.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698