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

Side by Side Diff: install_test/install_test.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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/env python
Nirnimesh 2012/07/04 00:39:19 Why is the dir called 'install_test' if the intent
nkang 2012/07/13 23:50:50 I asked Ken about this on Tuesday. His said that i
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 httplib
7 import logging
8 import optparse
9 import os
10 import platform
11 import shutil
12 import sys
13 import tempfile
14 import unittest
15 import urllib
16 import urlparse
17
18 import chrome_checkout
19 import chrome_installer
20 from py_unittest_util import GTestTextTestRunner
21
22 sys.path.append(os.path.join(os.path.pardir, 'pyautolib'))
23
24 # This import should go after sys.path is set appropriately.
25 from fetch_prebuilt_pyauto import FetchPrebuilt
26
27 _DOWNLOAD_DIR = []
Nirnimesh 2012/07/04 00:39:19 Do not use globals please
nkang 2012/07/13 23:50:50 The naming scheme being used previously for downlo
28 _INSTALLERS = []
29 _B_FIRST_TIME = True
30 _OPTIONS = None
31
32
33 class InstallTest(unittest.TestCase):
34 """Test fixture for tests involving installing/updating Chrome.
35
36 Provides an interface to install or update chrome from within a testcase, and
37 allows users to run pyauto tests using the installed version. User and system
38 level installations are supported, and either one can be used for running the
39 pyauto tests. Pyautolib files are downloaded at runtime and a PyUITest object
40 is created when Chrome is installed or updated. Users can utilize that object
41 to run updater tests. All Updater tests should derive from this class.
42 """
43
44 _build_iterator = None
45 _current_build = ''
46 _current_location = None
47 _dir_prefix = '__CHRBLD__'
48 _dir_iterator = None
49 _installer = None
50 _pyauto = None
51
52 def __init__(self, methodName='runTest'):
53 global _B_FIRST_TIME
54 global _DOWNLOAD_DIR
55 unittest.TestCase.__init__(self, methodName)
56 self._platform = self._GetPlatform()
57 self._SetUp()
58 self._InitSourceFolderNames()
59 if _B_FIRST_TIME:
60 if self._installation.GetChromeInstallationType():
61 self._DeleteBuild()
62 for build in self._builds:
63 if not self._DownloadDeps(build):
64 raise RuntimeError('Could not download dependencies.')
65 _B_FIRST_TIME = False
66 self._installer_iter = iter(_INSTALLERS)
67 self._dir_iterator = iter(_DOWNLOAD_DIR)
68 self._current_location = next(self._dir_iterator, None)
69
70 def _SetUp(self):
Nirnimesh 2012/07/04 00:39:19 Choose a different name, not to confuse with setUp
nkang 2012/07/13 23:50:50 I chose the name _SetUp because PyUITest also has
71 """Sets test parameters."""
72 global _OPTIONS
73 self._url = _OPTIONS.url
74 self._builds = _OPTIONS.builds and _OPTIONS.builds.split(',') or []
75 if not self._url or not self._builds:
76 raise RuntimeError('Please specify a valid URL and two Chrome builds.')
77 self._builds.sort()
78 self._build_iterator = iter(self._builds)
79 self._current_build = next(self._build_iterator, None)
80 self._url = self._url.endswith('/') and self._url or self._url + '/'
81 self._dir = os.path.isdir(_OPTIONS.dir) and _OPTIONS.dir or os.getcwd()
82 self._options = (_OPTIONS.options and _OPTIONS.options.replace(',', ' ')
83 or '')
84 self._install_type = ('system-level' in self._options and
85 chrome_installer.InstallationType.SYSTEM or
86 chrome_installer.InstallationType.USER)
87 self._installation = chrome_installer.Installation(self._install_type)
88
89 def _InitSourceFolderNames(self):
90 """Sets Chrome source folder names.
91
92 The folder names are used after checkout to confirm their existence.
93 """
94 self._source_dirs = [os.path.join('src', 'chrome', 'test', 'functional'),
Nirnimesh 2012/07/04 00:39:19 This is a maintenance burden. Please do not do thi
nkang 2012/07/13 23:50:50 Got rid of this method. Its main purpose was to ma
95 os.path.join('src', 'chrome', 'test', 'pyautolib'),
96 os.path.join('src', 'third_party', 'pyftpdlib'),
97 os.path.join('src', 'third_party', 'simplejson'),
98 os.path.join('src', 'third_party', 'tlslite'),
99 os.path.join('src', 'net', 'tools', 'testserver'),
100 os.path.join('src', 'third_party', 'webdriver',
101 'pylib', 'selenium')]
102
103 def setUp(self):
104 """Called before each unittest to prepare the test fixture."""
105 self.InstallBuild()
106 self.failIf(self._pyauto == None)
107
108 def tearDown(self):
109 """Called at the end of each unittest to do any test related cleanup."""
110 self._Refresh()
111 self._DeleteBuild()
112
113 def _GetPlatform(self):
114 """Returns the platform name."""
115 return ({'Windows': 'win',
116 'Darwin': 'mac',
117 'Linux': 'linux'}).get(platform.system())
118
119 def _Refresh(self):
120 """Deletes the PyUITest object and clears the modules registry."""
121 if self._pyauto:
122 del(self._pyauto)
123 for module in ['pyauto', 'pyautolib', '_pyautolib']:
124 if module in sys.modules:
125 sys.modules.pop(module)
126
127 def _RemovePaths(self):
128 """Restores the sys.path variable to its original state."""
129 sys.path = list(frozenset(sys.path))
130 if self._current_location in sys.path:
131 sys.path.remove(self._current_location)
132 if os.path.join(self._current_location, 'pyautolib') in sys.path:
133 sys.path.remove(os.path.join(self._current_location, 'pyautolib'))
134
135 def _Install(self):
136 """Helper method that installs Chrome and creates a PyUITest object."""
137 self._pyauto = None
138 installer_path = next(self._installer_iter, None)
139 if not installer_path:
140 raise RuntimeError('No more builds left to install.')
141 ret = chrome_installer.Install(installer_path, self._install_type,
142 self._current_build, self._options)
143 if not ret:
144 raise RuntimeError('Chrome installation for build %s failed.' %
145 self._current_build)
146 try:
147 import pyauto
148 self._pyauto = pyauto.PyUITest(methodName='runTest',
149 browser_path=os.path.dirname(
150 self._installation.GetChromeExePath()))
151 self._pyauto.suite_holder = pyauto.PyUITestSuite(['test.py'])
152 self._pyauto.setUp()
153 except ImportError, err:
154 logging.log(logging.ERROR, err)
155 self._pyauto = None
156
157 def InstallBuild(self):
158 self._RemovePaths()
159 sys.path.insert(0, self._current_location)
160 sys.path.insert(1, os.path.join(self._current_location, 'pyautolib'))
161 self._Install()
162
163 def _Update(self):
164 """Helper method for updating Chrome."""
165 self._RemovePaths()
166 self._current_location = next(self._dir_iterator, None)
167 assert (self._current_location)
168 sys.path.insert(0, self._current_location)
169 sys.path.insert(1, os.path.join(self._current_location, 'pyautolib'))
170 build = next(self._build_iterator, None)
171 if not build:
172 raise RuntimeError('No more builds left to install. Following builds '
173 'have already been installed: %r' % self._builds)
174 self._current_build = build
175 self._Install()
176
177 def UpdateBuild(self):
178 """Installs the second Chrome build specified in the command args."""
179 if self._pyauto:
180 self._pyauto.TearDown()
181 self._Refresh()
182 self._Update()
183
184 def _SrcFilesExist(self, root, items):
185 """Checks if specified files/folders exist at specified 'root' folder.
186
187 Args:
188 root: Parent folder where all the source directories reside.
189 items: List of files/folders to be verified for existence in the root.
190
191 Returns:
192 True, if all sub-folders exist in the root, otherwise False.
193 """
194 return all(map(lambda p: os.path.exists(p) and True or False,
195 [os.path.join(root, path) for path in items]))
196
197 def _CheckoutSourceFiles(self, build, location):
198 """Checks out source files associated with the current build.
199
200 Args:
201 build: Chrome release version number.
202 location: Destination where source files will be saved.
203 Returns:
204 Zero if successful, otherwise a negative value.
205 """
206 if(not os.path.isdir(location) or not
207 self._SrcFilesExist(location, self._source_dirs)):
208 chrome_checkout.CheckOut(build, location)
209 return self._SrcFilesExist(location, self._source_dirs)
210 else:
211 return True
212
213 def _DownloadDeps(self, build):
214 """Downloads Chrome build, source files, and chrome installer.
215
216 Args:
217 build: Chrome build number.
218 """
219 global _DOWNLOAD_DIR
220 ret = -1
221 url = '%s%s/%s' % (self._url, build, self._platform)
222 download_dir = os.path.join('%s', '%s%s') % (tempfile.gettempdir(),
223 self._dir_prefix, build)
224 _DOWNLOAD_DIR.append(download_dir)
225 if not os.path.isdir(download_dir):
226 try:
227 os.mkdir(download_dir)
228 except(OSError, IOError), err:
229 logging.log(logging.ERROR, err)
230 return False
231 if not self._SrcFilesExist(download_dir, ['pyautolib.py',
232 '_pyautolib.pyd']):
233 fpb = FetchPrebuilt(url, download_dir, self._platform)
234 if fpb.DoesUrlExist(url):
235 ret = fpb.Run()
236 else:
237 ret = 0
238 if ret == 0:
239 if self._Download('mini_installer.exe', url, download_dir):
240 _INSTALLERS.append(os.path.join(download_dir, 'mini_installer.exe'))
241 else:
242 return False
243 return self._CheckoutSourceFiles(build, download_dir)
244 return False
245
246 def _DeleteBuild(self):
247 """Uninstalls Chrome."""
248 global _DOWNLOAD_DIR
249 global _B_FIRST_TIME
250 ret = self._installation.UninstallChrome()
251 if not _B_FIRST_TIME:
252 if ret:
253 # Reposition iterators to the beginning of their respective lists.
254 self._build_iterator = iter(self._builds)
255 self._dir_iterator = iter(_DOWNLOAD_DIR)
256 self._current_build = next(self._build_iterator, None)
257 self._current_location = next(self._dir_iterator, None)
258 self._RemovePaths()
259 return ret
260
261 def _DeleteDepFiles(self):
262 """Deletes Chrome related files that were downloaded for testing."""
263 global _DOWNLOAD_DIR
264 for path in _DOWNLOAD_DIR:
265 try:
266 shutil.rmtree(path)
267 except shutil.Error, err:
268 logging.log(logging.ERROR, err)
269 return -1
270 return 0
271
272 def _Download(self, dfile, url, dest=None):
273 """Downloads a file from the specified URL.
274
275 Args:
276 dfile: Name of the file to download.
277 url: URL where the file is located.
278 dest: Location where file will be downloaded. Default is CWD.
279
280 Returns:
281 Zero if successful, otherwise a non-zero value.
282 """
283 filename = ((dest and os.path.exists(dest)) and os.path.join(dest, dfile)
284 or os.path.join(tempfile.gettempdir(), dfile))
285 file_url = '%s/%s' % (url, dfile)
286 if not self._DoesUrlExist(file_url):
287 raise RuntimeError('Either the URL or the file name is invalid.')
288 try:
289 d = urllib.urlretrieve(file_url, filename)
290 except IOError, err:
291 raise err
292 return os.path.isfile(d[0])
293
294 def _DoesUrlExist(self, url):
295 """Checks if a URL exists.
296
297 Args:
298 url: URL to be verified.
299
300 Returns:
301 True if the URL exists, otherwise False.
302 """
303 parse = urlparse.urlparse(url)
304 if parse[0] == '' or parse[1] == '':
305 return False
306 try:
307 connection = httplib.HTTPConnection(parse.netloc)
308 connection.request('HEAD', parse.path)
309 response = connection.getresponse()
310 except (socket.error, socket.gaierror):
311 return False
312 finally:
313 connection.close()
314 if response.status == 302 or response.status == 301:
315 return self._DoesUrlExist(response.getheader('location'))
316 return response.status == 200
317
318
319 class Main(object):
320 """Main program for running Updater tests."""
321
322 _tests_file = 'PYAUTO_TESTS'
323 _mod_path = sys.argv[0]
324 _pyauto_doc_url = 'http://dev.chromium.org/developers/testing/pyauto'
325
326 def __init__(self):
327 self._ParseArgs()
328 self._Run()
329
330 def _GetUnitTests(self):
331 """Returns a list of unittests from the calling script."""
332 mod_name = [os.path.splitext(os.path.basename(self._mod_path))[0]]
333 if os.path.dirname(self._mod_path) not in sys.path:
334 sys.path.append(os.path.dirname(self._mod_path))
335 return unittest.defaultTestLoader.loadTestsFromNames(mod_name)
336
337 def _Run(self):
338 """Runs the unit tests."""
339 tests = self._GetUnitTests()
340 result = GTestTextTestRunner(verbosity=1).run(tests)
341 del(tests)
342 if not result.wasSuccessful():
343 print >>sys.stderr, ('Tests can be disabled by editing %s. Ref: %s'
344 % (self._tests_file, self._pyauto_doc_url))
345 sys.exit(1)
346 else:
347 sys.exit(0)
348
349 def _ParseArgs(self):
350 """Parses command line arguments."""
351 global _OPTIONS
352 parser = optparse.OptionParser()
353 parser.add_option(
354 '-b', '--builds', type='string', default='', dest='builds',
355 help='Specifies the two (or more) builds needed for testing.')
356 parser.add_option(
357 '-u', '--url', type='string', default='', dest='url',
358 help='Specifies the chrome-master2 url, without the build number.')
359 parser.add_option(
360 '-d', '--dir', type='string', default=os.getcwd(),
361 help='Specifies directory where the installer will be downloaded.')
362 parser.add_option(
363 '-o', '--options', type='string', default='',
364 help='Specifies any additional Chrome options (i.e. --system-level).')
365 opts, args = parser.parse_args()
366 _OPTIONS = opts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698