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

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

Powered by Google App Engine
This is Rietveld 408576698