Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env 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 import optparse | |
| 7 import os | |
| 8 import platform | |
| 9 import shutil | |
| 10 import sys | |
| 11 import tempfile | |
| 12 import unittest | |
| 13 import urllib | |
| 14 import zipfile | |
| 15 | |
| 16 import chrome_checkout | |
| 17 | |
|
kkania
2012/06/14 17:20:33
no newline here, i think; check the style guide
nkang
2012/06/28 19:02:39
It's gone.
| |
| 18 from chrome_installer import ChromeInstaller | |
| 19 | |
| 20 os.sys.path.append(os.path.join(os.path.pardir, 'pyautolib')) | |
|
kkania
2012/06/14 17:20:33
os.sys? I thought it was just sys
nkang
2012/06/28 19:02:39
They are the same, but in order to use just sys, y
| |
| 21 from fetch_prebuilt_pyauto import FetchPrebuilt | |
| 22 | |
| 23 # Global var. to hold pyautolib locations. A global is needed because pyautolib | |
| 24 # files are downloaded only once at the beginning of the test. We cannot assign | |
| 25 # these locations to a member var. because each unittest creates a new instance | |
| 26 # of InstallTest, which means that while the first instance will know about the | |
| 27 # locations, subsequent instances will not. So a global is used because it will | |
| 28 # not go out of scope until the process exits. | |
| 29 _DOWNLOAD_DIR = [] | |
| 30 # Flag that determines if its the first instance, and downloads pyautolib and | |
| 31 # other dependencies if it is. Its global for the same reason as above. | |
| 32 _B_FIRST_TIME = True | |
| 33 | |
| 34 class InstallTest(unittest.TestCase): | |
| 35 """Test fixture for tests involving installing/updating Chrome. | |
| 36 | |
| 37 Provides an interface to install or update chrome from within a testcase, and | |
| 38 allows users to run pyauto tests using the installed version. User and system | |
| 39 level installations are supported, and either one can be used for running the | |
| 40 pyauto tests. Pyautolib files are downloaded at runtime and a PyUITest object | |
| 41 is created when Chrome is installed or updated. Users can utilize that object | |
| 42 to run updater tests. All Updater tests should derive from this class. | |
| 43 """ | |
| 44 | |
| 45 def __init__(self, methodName='runTest'): | |
| 46 global _B_FIRST_TIME | |
| 47 unittest.TestCase.__init__(self, methodName) | |
| 48 self._pyauto = None | |
| 49 self._plat = self.GetPlatform() | |
| 50 self._source_dirs = ['functional', 'pyautolib', 'pyftpdlib', 'selenium', | |
| 51 'simplejson', 'testserver', 'tlslite'] | |
| 52 self._ParseArgs() | |
| 53 self._dir_prefix = '__CHRBLD__' | |
| 54 if self._builds: | |
| 55 if _B_FIRST_TIME: | |
| 56 for build in self._builds: | |
| 57 if not self._DownloadDeps(build): | |
| 58 print 'Couldn\'t download dependencies, aborting test...' | |
| 59 sys.exit(-1) | |
| 60 _B_FIRST_TIME = False | |
| 61 | |
| 62 def _ParseArgs(self): | |
|
kkania
2012/06/14 17:20:33
move the global initialization stuff out to a main
nkang
2012/06/28 19:02:39
Done. Although, I hope you don't mind - since you
| |
| 63 """Parses the command line arguments.""" | |
| 64 parser = optparse.OptionParser() | |
| 65 parser.add_option( | |
| 66 '-b', '--builds', type='string', default='', dest='builds', | |
| 67 help='Specifies the two (or more) builds needed for testing.') | |
| 68 parser.add_option( | |
| 69 '-u', '--url', type='string', default='', dest='url', | |
| 70 help='Specifies the chrome-master2 url, without the build number.') | |
| 71 parser.add_option( | |
| 72 '-d', '--dir', type='string', default=os.getcwd(), | |
| 73 help='Specifies directory where the installer will be downloaded.') | |
| 74 parser.add_option( | |
| 75 '-o', '--options', type='string', default='', | |
| 76 help='Specifies any additional Chrome options (i.e. --system-level).') | |
| 77 self.opts, self.args = parser.parse_args() | |
| 78 self.dir = (lambda d: os.path.isdir(d) and d or os.getcwd())(self.opts.dir) | |
| 79 self._builds = (lambda b: b.split(',') if b else [])(self.opts.builds) | |
| 80 self._builds.sort() | |
| 81 self._bld_counter = (lambda lst: 0 if len(lst) > 0 else None)(self._builds) | |
|
kkania
2012/06/14 17:20:33
you use a bit too many abbreviations ('opts', 'bld
nkang
2012/06/28 19:02:39
Got rid of abbreviations wherever possible.
| |
| 82 self._c_opts = ((lambda opts: opts.replace(',', ' ') if opts else '') | |
| 83 (self.opts.options)) | |
| 84 self._url = self.opts.url | |
| 85 if self._url and not self._url.endswith('/'): | |
| 86 self._url += '/' | |
| 87 if self._builds: | |
| 88 self._c_installer = ChromeInstaller(self._url, self._builds[0], | |
| 89 dest=self.dir, opts=self._c_opts, | |
| 90 clean=True) | |
| 91 else: | |
| 92 self._c_installer = None | |
| 93 if not self._builds or not self._url: | |
| 94 print 'Please specify a valid URL and at least two Chrome builds.' | |
| 95 sys.exit(-1) | |
| 96 | |
| 97 def setUp(self): | |
| 98 """Called before each unittest. It calls _Install, which installs the | |
| 99 | |
| 100 first Chrome build and creates a pyauto.PyUITest object. | |
| 101 """ | |
|
kkania
2012/06/14 17:20:33
i think we should uninstall first to make sure the
nkang
2012/06/28 19:02:39
Done, now if Chrome is already installed, we unins
| |
| 102 self.InstallBuild() | |
| 103 self.failIf(self._pyauto == None) | |
| 104 | |
| 105 def tearDown(self): | |
| 106 """Called at the end of each unittest. Clears the modules registry so | |
| 107 | |
| 108 pyautolib can be reloaded when the build is updated. | |
| 109 """ | |
| 110 self._Refresh() | |
| 111 self._DeleteBuild() | |
| 112 | |
| 113 def GetPlatform(self): | |
| 114 """Returns the platform name.""" | |
| 115 return ({'Windows': 'win', 'Darwin': 'mac', | |
| 116 'Linux': 'linux'}).get(platform.system()) | |
| 117 | |
| 118 def SetCurrentBuild(self, nVal): | |
|
kkania
2012/06/14 17:20:33
check these, i don't think they're used
nkang
2012/06/28 19:02:39
Removed both of them.
| |
| 119 """Sets current Chrome build.""" | |
| 120 self._bld_counter = (lambda n: n if(n > 0 and n <= 2) else 0)(nVal) | |
| 121 | |
| 122 def GetCurrentBuild(self): | |
| 123 """Returns the current chrome build.""" | |
| 124 return self._builds[self._bld_counter] | |
| 125 | |
| 126 def _Refresh(self): | |
| 127 """Deletes the PyUITest object and clears the modules registry.""" | |
| 128 try: | |
| 129 del(self._pyauto) | |
| 130 except NameError, err: | |
| 131 print 'CBaseUpdater._Refresh: ', err | |
| 132 pass | |
| 133 try: | |
| 134 os.sys.modules.pop('pyauto') | |
|
kkania
2012/06/14 17:20:33
i thought it was just sys.modules
nkang
2012/06/28 19:02:39
You can either call sys.modules or os.sys.modules,
| |
| 135 os.sys.modules.pop('pyautolib') | |
| 136 os.sys.modules.pop('_pyautolib') | |
| 137 except KeyError, err: | |
| 138 print 'CBaseUpdater._Refresh: ', err | |
|
kkania
2012/06/14 17:20:33
when could this occur? I think we should just let
nkang
2012/06/28 19:02:39
This scenario, though unlikely, could occur if the
| |
| 139 | |
| 140 def _Install(self): | |
| 141 """Installs chrome and creates a PyUITest object on completion.""" | |
| 142 self._pyauto = None | |
| 143 if isinstance(self._c_installer, ChromeInstaller): | |
| 144 ret = self._c_installer.InstallChrome() | |
| 145 if ret: | |
| 146 try: | |
| 147 import pyauto | |
| 148 self._pyauto = pyauto.PyUITest(methodName='runTest', | |
| 149 browser_path=os.path.dirname( | |
| 150 ret.GetChromeExePath())) | |
| 151 self._pyauto.suite_holder = pyauto.PyUITestSuite(['test.py']) | |
| 152 self._pyauto.setUp() | |
| 153 except ImportError, err: | |
| 154 print 'CBaseUpdater.InstallBuild: ', err | |
| 155 self._pyauto = None | |
| 156 | |
| 157 def InstallBuild(self): | |
| 158 """Installs the first of the Chrome builds specified as command args.""" | |
| 159 global _DOWNLOAD_DIR | |
| 160 if _DOWNLOAD_DIR[1] in os.sys.path: | |
|
kkania
2012/06/14 17:20:33
same here and many places in this file; i think al
nkang
2012/06/28 19:02:39
Done.
| |
| 161 os.sys.path.remove(_DOWNLOAD_DIR[1]) | |
| 162 if os.path.join(_DOWNLOAD_DIR[1], 'pyautolib') in os.sys.path: | |
| 163 os.sys.path.remove(os.path.join(_DOWNLOAD_DIR, 'pyautolib')) | |
| 164 os.sys.path.insert(0, _DOWNLOAD_DIR[0]) | |
| 165 os.sys.path.insert(1, os.path.join(_DOWNLOAD_DIR[0], 'pyautolib')) | |
| 166 self._bld_counter += 1 | |
| 167 self._Install() | |
| 168 | |
| 169 def _Update(self): | |
| 170 """Updates Chrome by installing the second(higher) version of Chrome.""" | |
| 171 global _DOWNLOAD_DIR | |
| 172 if _DOWNLOAD_DIR[0] in os.sys.path: | |
| 173 os.sys.path.remove(_DOWNLOAD_DIR[0]) | |
| 174 if os.path.join(_DOWNLOAD_DIR[0], 'pyautolib') in os.sys.path: | |
| 175 os.sys.path.remove(os.path.join(_DOWNLOAD_DIR[0], 'pyautolib')) | |
| 176 os.sys.path.insert(0, _DOWNLOAD_DIR[1]) | |
| 177 os.sys.path.insert(1, os.path.join(_DOWNLOAD_DIR[1], 'pyautolib')) | |
| 178 if self._bld_counter >= len(self._builds): | |
| 179 print 'No more builds left to install. The following builds have '\ | |
|
kkania
2012/06/14 17:20:33
raise, not print
nkang
2012/06/28 19:02:39
Done.
| |
| 180 'already been installed: %r' % self._builds | |
| 181 return None | |
| 182 if self._c_installer: | |
| 183 self._c_installer.SetBuild(self._builds[self._bld_counter]) | |
| 184 self._Install() | |
| 185 | |
| 186 def UpdateBuild(self): | |
| 187 if self._pyauto: | |
| 188 self._pyauto.TearDown() | |
| 189 self._Refresh() | |
| 190 self._Update() | |
| 191 | |
| 192 def _SrcFilesExist(self, root, items): | |
| 193 """Checks if specified files/folders exist at specified 'root' folder. | |
| 194 | |
| 195 Args: | |
| 196 root: Parent folder where all the source directories reside. | |
| 197 items: List of files/folders to be verified for existence in the root. | |
| 198 | |
| 199 Returns: | |
| 200 Boolean, True if all sub-folders exist in the root, otherwise False. | |
| 201 """ | |
| 202 return all(map(lambda p: os.path.exists(p) and True or False, | |
| 203 [os.path.join(root, path) for path in items])) | |
| 204 | |
| 205 def _CheckoutSourceFiles(self, build, location): | |
| 206 # Checkout folder doesn't exist or is missing some data. | |
| 207 if(not os.path.isdir(location) or not | |
| 208 self._SrcFilesExist(location, self._source_dirs)): | |
| 209 return chrome_checkout.CheckOut(build, location) | |
| 210 # Folder already exists, no need to do another checkout. | |
| 211 else: | |
| 212 return 0 | |
| 213 | |
| 214 def _DownloadDeps(self, build): | |
| 215 global _DOWNLOAD_DIR | |
| 216 ret = -1 | |
| 217 url = '%s%s/%s' % (self._url, build, self._plat) | |
| 218 download_dir = os.path.join('%s', '%s%s') % (tempfile.gettempdir(), | |
| 219 self._dir_prefix, build) | |
| 220 _DOWNLOAD_DIR.append(download_dir) | |
| 221 if not os.path.isdir(download_dir): | |
| 222 try: | |
| 223 os.mkdir(download_dir) | |
| 224 except(OSError, IOError), err: | |
| 225 print 'InstallTest._DownloadDeps: %s' % err | |
| 226 return False | |
| 227 if not self._SrcFilesExist(download_dir, ['pyautolib.py', | |
| 228 '_pyautolib.pyd']): | |
| 229 fpb = FetchPrebuilt(url, download_dir, self._plat) | |
| 230 if fpb.DoesUrlExist(url): | |
| 231 ret = fpb.Run() | |
| 232 else: | |
| 233 ret = 0 | |
| 234 # Check out source files. | |
| 235 if ret == 0: | |
| 236 self._CheckoutSourceFiles(build, download_dir) | |
| 237 return True | |
| 238 return False | |
| 239 | |
| 240 def _DeleteBuild(self): | |
| 241 """Uninstalls Chrome""" | |
| 242 if self._bld_counter == None or self._bld_counter < 0: | |
| 243 return -1 | |
| 244 cur_build = self._builds[self._bld_counter - 1] | |
| 245 if cur_build != self._c_installer.GetBuild(): | |
| 246 self._c_installer.SetBuild(cur_build) | |
| 247 ret = self._c_installer.UninstallChrome() | |
| 248 if ret: | |
| 249 self._bld_counter = 0 | |
| 250 return ret | |
| 251 | |
| 252 def _DeleteDepFiles(self): | |
| 253 """Deletes Chrome related files that were downloaded for testing.""" | |
| 254 global _DOWNLOAD_DIR | |
| 255 for path in _DOWNLOAD_DIR: | |
| 256 try: | |
| 257 shutil.rmtree(path) | |
| 258 except shutil.Error, err: | |
| 259 print 'CBaseUpdater._DeleteDepFiles: ', err | |
| 260 return -1 | |
| 261 return 0 | |
| OLD | NEW |