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

Side by Side Diff: install_test/chrome_installer.py

Issue 10384104: Chrome updater test framework (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/chrome/test/
Patch Set: Created 8 years, 5 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 | Annotate | Revision Log
« no previous file with comments | « install_test/chrome_checkout.py ('k') | install_test/install_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Provides an interface for installing Chrome."""
7
8 import _winreg
9 import ctypes
10 import httplib
11 import logging
12 import os
13 import platform
14 import shutil
15 import socket
16 import subprocess
17 import tempfile
18 import urllib
19
20
21 _CSIDL_COMMON_APPDATA = 0x1C
kkania 2012/07/23 16:46:23 how about move these 2 to class Installation?
nkang 2012/07/25 23:39:21 Moved both these variables to Installation or Chro
22 _CSIDL_PROGRAM_FILESX86 = 0x2A
23 _PLATFORM = platform.system().lower()
kkania 2012/07/23 16:46:23 I see there's a few checks in this file that check
nkang 2012/07/25 23:39:21 I added this check because I was told that ultimat
kkania 2012/07/25 23:59:38 There's a few other places you need to update that
nkang 2012/08/02 21:05:42 Got rid of the _PLATFORM variable and removed all
24
25 # Import only on Windows. On other platforms it will invoke a ValueError.
26 if _PLATFORM == 'windows':
27 from ctypes import wintypes, windll
28
29
30 class InstallationType:
31 """Defines the Chrome installation types."""
32 SYSTEM = 0
33 USER = 1
34
35
36 def _RemoveDuplicates(args):
37 """Removes duplicates options from a list.
38
39 Args:
40 args: A list containing the options.
41
42 Returns:
43 A string without any duplicate options.
44 """
45 return ((lambda arg: arg if arg else '')
46 (' '.join(map(lambda opt: opt, list(frozenset(args.split(',')))))))
47
48
49 def Install(installer_path, chrome_type, build, options='', clean=True):
50 """Installs the specified Chrome build."""
kkania 2012/07/23 16:46:23 Document the args and return
nkang 2012/07/25 23:39:21 Done.
51 def DoPreliminaryChecks(chrome_install):
52 """Validates the test parameters and Chrome version."""
53 assert(_PLATFORM == 'windows' and os.path.isfile(installer_path))
54 # Chrome installation type specified by the user.
55 install_type = ('system-level' in options and InstallationType.SYSTEM
56 or InstallationType.USER)
57 cur_build = chrome_install.GetChromeVersion()
58 # Chrome already installed, make sure new build can be installed over it.
59 if cur_build:
60 current_type = chrome_install.GetChromeInstallationType()
61 if(current_type == InstallationType.SYSTEM and install_type ==
62 InstallationType.USER):
63 raise RuntimeError('System level Chrome exists, aborting user level '
64 'installation.')
65 # Installing a build that's older than the currently installed build.
66 elif current_type == install_type:
67 if cur_build >= build:
68 raise RuntimeError('Please specify a newer version of Chrome.')
69
70 chrome_install = Installation(chrome_type)
71 DoPreliminaryChecks(chrome_install)
72 options += ' --install --do-not-launch-chrome'
73 options = _RemoveDuplicates(options)
kkania 2012/07/23 16:46:23 what's an example of options that might want to be
nkang 2012/07/25 23:39:21 Example of an option that needs to be passed in is
kkania 2012/07/25 23:59:38 Try running the installer/uninstaller manually and
nkang 2012/08/02 21:05:42 Checked with Prudhvi regarding duplicate options,
74 logging.log(logging.INFO, 'Launching Chrome installer...')
75 cmd = '%s %s' % (installer_path, options)
76 ret = subprocess.Popen(cmd, shell=True).wait()
77 if ret == 0:
78 logging.log(logging.INFO, 'Installation complete.')
79 return chrome_install
80 logging.log(logging.ERROR, 'Installation failed.')
kkania 2012/07/23 16:46:23 How about throw an error here instead, which inclu
nkang 2012/07/25 23:39:21 Got rid of the logging.log message and the return
81 return None
82
83
84 class Installation(object):
85 """Provides pertinent information about the installed Chrome version.
86
87 The type of Chrome version must be passed as an argument to the constructor,
88 (i.e. - user or system level).
89 """
90
91 HKEY_LOCAL = (r'SOFTWARE\Wow6432Node\Google\Update\ClientState'
kkania 2012/07/23 16:46:23 make these private?
nkang 2012/07/25 23:39:21 Made both of them private. I had initially held of
92 '\{8A69D345-D564-463C-AFF1-A69D9E530F96}')
93 HKEY_USER = HKEY_LOCAL.replace('\\Wow6432Node', '')
94
95 def __init__(self, install_type):
96 assert(install_type == InstallationType.SYSTEM or
97 install_type == InstallationType.USER)
98 self._type = install_type
99 self._key_type = self._GetKeyType(self._type)
100
101 def _OpenKey(self, key_type, key_name, key_access=_winreg.KEY_READ):
102 """Opens a registry key and returns the key handle.
103
104 Args:
105 key_type: The type of key HKLM or HKCU.
106 key_name: Name of the key.
107 key_access: Type of access required. Be default its read only.
108
109 Returns:
110 Key handle if successful, otherwise None.
111 """
112 try:
113 key = _winreg.OpenKey(key_type, key_name, 0, key_access)
114 return key
115 except _winreg.error:
116 return None
kkania 2012/07/23 16:46:23 when could this occur? Should we throw an exceptio
nkang 2012/07/25 23:39:21 This could occur for a number of reasons. It could
kkania 2012/07/25 23:59:38 I don't think it's too important that we try to fo
nkang 2012/08/02 21:05:42 The reason I wrote this method was because I wante
117
118 def _GetWinLocalFolder(self, ftype=_CSIDL_COMMON_APPDATA):
119 """Returns full path of the 'Local' folder on Windows.
120
121 Args:
122 ftype: Location to look up, which could vary based on installation type.
123
124 Returns:
125 A String representing the folder path if successful, otherwise an empty
126 string.
127 """
128 if _PLATFORM != 'windows':
129 return ''
130 SHGetFolderPathW = windll.shell32.SHGetFolderPathW
131 SHGetFolderPathW.argtypes = [wintypes.HWND,
132 ctypes.c_int,
133 wintypes.HANDLE,
134 wintypes.DWORD,
135 wintypes.LPCWSTR]
136 path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH)
137 result = SHGetFolderPathW(0, ftype, 0, 0, path_buf)
138 return str(path_buf.value)
139
140 def _GetKeyType(self, install_type):
141 """Determines the registry key to use based on installation type."""
142 if install_type == InstallationType.SYSTEM:
143 return _winreg.HKEY_LOCAL_MACHINE
144 elif install_type == InstallationType.USER:
145 return _winreg.HKEY_CURRENT_USER
146
147 def _GetKeyName(self, install_type, replace=True):
148 """Returns Chrome's registry key name based on installation type."""
kkania 2012/07/23 16:46:23 The replace arg here is a bit strange. Can you fin
nkang 2012/07/25 23:39:21 Changed the replace arg with a 'value' arg. If the
kkania 2012/07/25 23:59:38 I see. I think this function is still confusing. S
nkang 2012/08/02 21:05:42 Got rid of this method altogether. Per our convers
149 name = (install_type == InstallationType.SYSTEM and self.HKEY_LOCAL or
150 self.HKEY_USER)
151 return (replace and name.replace('ClientState', 'Clients') or name)
152
153 def _DeleteChromeRegEntries(self):
154 """Deletes chrome registry settings."""
155 if self._type == InstallationType.USER:
156 p_key = self.HKEY_USER[: self.HKEY_USER.rfind('\\')]
157 key_name = self.HKEY_USER[self.HKEY_USER.rfind('\\') + 1 :]
158 _type = _winreg.HKEY_CURRENT_USER
159 elif self._type == InstallationType.SYSTEM:
160 p_key = self.HKEY_LOCAL[: self.HKEY_LOCAL.rfind('\\')]
161 key_name = self.HKEY_LOCAL[self.HKEY_LOCAL.rfind('\\') + 1 :]
162 _type = _winreg.HKEY_LOCAL_MACHINE
163 try:
164 key = self._OpenKey(_type, p_key, _winreg.KEY_ALL_ACCESS)
165 _winreg.DeleteKey(key, key_name)
166 key.Close()
167 return 0
168 except _winreg.error, err:
169 logging.log(logging.ERROR, 'Could not delete registry entries: %s' % err)
170 return -1
171
172 def _GetValueFromRegistry(self, reg_type, key, subkey):
173 """Gets value of the specified subkey from the registry.
174
175 Args:
176 reg_type: Type of key to use HKLM or HKCU.
177 key: Name of the key.
178 subkey: Name of the subkey whose value will be returned.
179
180 Returns:
181 A string representing the subkey value if successful, otherwise None.
182 """
183 hkey = self._OpenKey(reg_type, key)
184 if hkey:
185 try:
186 value = str(_winreg.QueryValueEx(hkey, subkey)[0])
187 except _winreg.error:
188 value = None
189 _winreg.CloseKey(hkey)
190 return value
191 return None
192
193 def _GetUninstallString(self):
194 """Returns Chrome uninstall string from the registry."""
195 key_name = self._GetKeyName(self._type, False)
196 return self._GetValueFromRegistry(self._key_type, key_name,
197 'UninstallString')
198
199 def _GetUninstallArguments(self):
200 """Returns Chrome uninstall arguments from the registry."""
201 key_name = self._GetKeyName(self._type, False)
202 return self._GetValueFromRegistry(self._key_type, key_name,
203 'UninstallArguments')
204
205 def GetChromeVersion(self):
206 """Returns the installed version of Chrome."""
207 key_name = self._GetKeyName(self._type, True)
208 return self._GetValueFromRegistry(self._key_type, key_name, 'pv')
209
210 def _LaunchInstaller(self, installer_path, options):
211 """Launches the Chrome installer.
212
213 Args:
214 installer_path: Path where the installer is located.
215 options: Any additional options to be used for installation.
216 """
217 cmd = '%s %s' % (installer_path, options)
218 try:
219 subprocess.Popen(cmd, shell=True).wait()
220 except OSError, err:
221 raise err
222
223 def _Delete(self, _path):
kkania 2012/07/23 16:46:23 i don't think this is used
nkang 2012/07/25 23:39:21 Yes, you are right. This method is currently not b
224 """Deletes a file or folder."""
225 try:
226 (lambda p: shutil.rmtree(p) if os.path.isdir(p) else os.remove(p))(_path)
227 return 0
228 except(OSError, IOError, TypeError), err:
229 return -1
230
231 def GetChromeExePath(self):
232 """Returns Chrome binary location based on installation type."""
233 chrome_path = ''
234 if self._type == InstallationType.USER:
235 folder_id = _CSIDL_COMMON_APPDATA
236 elif self._type == InstallationType.SYSTEM:
237 folder_id = _CSIDL_PROGRAM_FILESX86
238 if _PLATFORM == 'windows':
239 chrome_path = os.path.join(self._GetWinLocalFolder(folder_id), 'Google',
240 'Chrome', 'Application', 'chrome.exe')
241 return chrome_path
242
243 def GetChromeInstallationType(self):
kkania 2012/07/23 16:46:23 a lot of functions in this class have Chrome in th
nkang 2012/07/25 23:39:21 Changed the class name from Installation to Chrome
244 """Determines Chrome installation type."""
245 reg_type = _winreg.HKEY_CURRENT_USER
246 key_name = self.HKEY_USER.replace('ClientState', 'Clients')
247 key = self._OpenKey(reg_type, key_name)
248 if not key:
249 reg_type = _winreg.HKEY_LOCAL_MACHINE
250 key_name = self.HKEY_LOCAL.replace('ClientState', 'Clients')
251 key = self._OpenKey(reg_type, key_name)
252 if not key:
253 return None
254 version = self._GetValueFromRegistry(reg_type, key_name, 'pv')
255 key.Close()
256 if version:
257 return (reg_type == _winreg.HKEY_CURRENT_USER and
258 InstallationType.USER or InstallationType.SYSTEM)
259 return None
260
261 def UninstallChrome(self):
262 """Uninstalls Chrome."""
263 if not self.GetChromeVersion():
264 raise RuntimeError('No Chrome version found on this system.')
265 chrome_path = self.GetChromeExePath()
266 install_type = self.GetChromeInstallationType()
267 reg_opts = self._GetUninstallArguments()
268 uninstall_str = self._GetUninstallString()
269 options = '%s --force-uninstall' % (reg_opts)
270 if self._type == InstallationType.SYSTEM:
271 options += ' --system-level'
272 if not os.path.exists(chrome_path):
273 logging.log(logging.ERROR, 'Could not find chrome, aborting uninstall.')
nkang 2012/07/25 23:39:21 Since we're now raising an exception instead of re
274 return False
275 logging.log(logging.INFO, 'Launching Chrome installer...')
276 ret = self._LaunchInstaller(uninstall_str, options)
kkania 2012/07/23 16:46:23 this function doesn't look like it returns anythin
nkang 2012/07/25 23:39:21 LaunchInstaller did at one point return a value, b
277 if not os.path.exists(chrome_path):
278 logging.log(logging.INFO, 'Chrome was uninstalled successfully...')
279 # TODO: add clean option like ChromeInstaller
kkania 2012/07/23 16:46:23 remove this TODO
nkang 2012/07/25 23:39:21 TODO: Remove the comment. Done!
280 logging.log(logging.INFO, 'Deleting registry entries...')
281 self._DeleteChromeRegEntries()
282 logging.log(logging.INFO, 'Uninstall complete.')
283 return True
284 else:
285 logging.log(logging.ERROR, 'Uninstall failed.')
kkania 2012/07/23 16:46:23 how about throw error instead
nkang 2012/07/25 23:39:21 This method now raises a RuntimeError exception. U
286 return False
OLDNEW
« no previous file with comments | « install_test/chrome_checkout.py ('k') | install_test/install_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698