| OLD | NEW |
| (Empty) | |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """Test fixture for tests involving installing/updating Chrome. |
| 6 |
| 7 Provides an interface to install or update chrome from within a testcase, and |
| 8 allows users to run tests using installed version of Chrome. User and system |
| 9 level installations are supported, and either one can be used for running the |
| 10 tests. Currently the only platform it supports is Windows. |
| 11 """ |
| 12 |
| 13 import atexit |
| 14 import logging |
| 15 import optparse |
| 16 import os |
| 17 import platform |
| 18 import re |
| 19 import shutil |
| 20 import stat |
| 21 import sys |
| 22 import tempfile |
| 23 import unittest |
| 24 import urllib |
| 25 |
| 26 import chrome_installer_win |
| 27 |
| 28 _DIRECTORY = os.path.dirname(os.path.abspath(__file__)) |
| 29 sys.path.append(os.path.join(os.path.dirname(_DIRECTORY), 'pyautolib')) |
| 30 sys.path.append(os.path.join(_DIRECTORY, os.path.pardir, os.path.pardir, |
| 31 os.path.pardir, 'third_party', 'webdriver', |
| 32 'pylib')) |
| 33 |
| 34 # This import should go after sys.path is set appropriately. |
| 35 import pyauto_utils |
| 36 import selenium.webdriver.chrome.service as service |
| 37 from selenium import webdriver |
| 38 |
| 39 |
| 40 def MakeTempDir(parent_dir=None): |
| 41 """Creates a temporary directory and returns an absolute path to it. |
| 42 |
| 43 The temporary directory is automatically deleted when the python interpreter |
| 44 exits normally. |
| 45 |
| 46 Args: |
| 47 parent_dir: the directory to create the temp dir in. If None, the system |
| 48 temp dir is used. |
| 49 |
| 50 Returns: |
| 51 The absolute path to the temporary directory. |
| 52 """ |
| 53 path = tempfile.mkdtemp(dir=parent_dir) |
| 54 def DeleteDir(): |
| 55 # Don't use shutil.rmtree because it can't delete read-only files on Win. |
| 56 for root, dirs, files in os.walk(path, topdown=False): |
| 57 for name in files: |
| 58 filename = os.path.join(root, name) |
| 59 os.chmod(filename, stat.S_IWRITE) |
| 60 os.remove(filename) |
| 61 for name in dirs: |
| 62 os.rmdir(os.path.join(root, name)) |
| 63 atexit.register(DeleteDir) |
| 64 return path |
| 65 |
| 66 |
| 67 class InstallTest(unittest.TestCase): |
| 68 """Base updater test class. |
| 69 |
| 70 All dependencies, like Chrome installers and ChromeDriver, are downloaded at |
| 71 the beginning of the test. Dependencies are downloaded in the temp directory. |
| 72 This download occurs only once, before the first test is executed. Each test |
| 73 case starts an instance of ChromeDriver and terminates it upon completion. |
| 74 All updater tests should derive from this class. |
| 75 |
| 76 Example: |
| 77 |
| 78 class SampleUpdater(InstallTest): |
| 79 |
| 80 def testCanOpenGoogle(self): |
| 81 self._driver.get('http://www.google.com/') |
| 82 self.UpdateBuild() |
| 83 self._driver.get('http://www.msn.com/') |
| 84 |
| 85 Include the following in your updater test script to make it run standalone. |
| 86 |
| 87 from install_test import Main |
| 88 |
| 89 if __name__ == '__main__': |
| 90 Main() |
| 91 |
| 92 To fire off an updater test, use the command below. |
| 93 python test_script.py --url=<URL> --builds=22.0.1230.0,22.0.1231.0 |
| 94 """ |
| 95 |
| 96 _DIR_PREFIX = '__CHRBLD__' |
| 97 _INSTALLER_NAME = 'mini_installer.exe' |
| 98 _installer_paths = [] |
| 99 _chrome_driver = '' |
| 100 _installer_options = [] |
| 101 |
| 102 def __init__(self, methodName='runTest'): |
| 103 unittest.TestCase.__init__(self, methodName) |
| 104 self._counter = 0 |
| 105 current_version = chrome_installer_win.ChromeInstallation.GetCurrent() |
| 106 if current_version: |
| 107 current_version.Uninstall() |
| 108 self._install_type = ('system-level' in self._installer_options and |
| 109 chrome_installer_win.InstallationType.SYSTEM or |
| 110 chrome_installer_win.InstallationType.USER) |
| 111 |
| 112 def setUp(self): |
| 113 """Called before each unittest to prepare the test fixture.""" |
| 114 self._InstallNext() |
| 115 self._StartChromeDriver() |
| 116 |
| 117 def tearDown(self): |
| 118 """Called at the end of each unittest to do any test related cleanup.""" |
| 119 self._driver.quit() |
| 120 self._service.stop() |
| 121 self._installation.Uninstall() |
| 122 |
| 123 def _StartChromeDriver(self): |
| 124 """Starts ChromeDriver.""" |
| 125 self._service = service.Service(InstallTest._chrome_driver) |
| 126 self._service.start() |
| 127 self._driver = webdriver.Remote( |
| 128 self._service.service_url, |
| 129 {'chrome.binary' : self._installation.GetExePath()} |
| 130 |
| 131 def _InstallNext(self): |
| 132 """Helper method that installs Chrome.""" |
| 133 self._installation = chrome_installer_win.Install( |
| 134 self._installer_paths[self._counter], |
| 135 self._install_type, |
| 136 self._builds[self._counter], |
| 137 self._installer_options) |
| 138 self._counter += 1 |
| 139 |
| 140 def UpdateBuild(self): |
| 141 """Updates Chrome by installing a newer version.""" |
| 142 self._driver.quit() |
| 143 self._InstallNext() |
| 144 self._driver = webdriver.Remote(self._service.service_url, |
| 145 self._capabilities) |
| 146 |
| 147 @staticmethod |
| 148 def _Download(url, path): |
| 149 """Downloads a file from the specified URL. |
| 150 |
| 151 Args: |
| 152 url: URL where the file is located. |
| 153 path: Location where file will be downloaded. |
| 154 """ |
| 155 if not pyauto_utils.DoesUrlExist(url): |
| 156 raise RuntimeError('Either the URL or the file name is invalid.') |
| 157 urllib.urlretrieve(url, path) |
| 158 |
| 159 @staticmethod |
| 160 def InitTestFixture(builds, base_url, options): |
| 161 """Static method for passing command options to InstallTest. |
| 162 |
| 163 We do not instantiate InstallTest. Therefore, command arguments cannot |
| 164 be passed to its constructor. Since InstallTest needs to use these options |
| 165 and using globals is not an option, this method can be used by the Main |
| 166 class to pass the arguments it parses onto InstallTest. |
| 167 """ |
| 168 builds = builds.split(',') if builds else [] |
| 169 system = ({'Windows': 'win', |
| 170 'Darwin': 'mac', |
| 171 'Linux': 'linux'}).get(platform.system()) |
| 172 InstallTest._installer_options = options.split(',') if options else [] |
| 173 InstallTest._builds = builds |
| 174 tempdir = MakeTempDir() |
| 175 for build in builds: |
| 176 url = '%s%s/%s/mini_installer.exe' % (base_url, build, system) |
| 177 installer_path = os.path.join(tempdir, 'mini_installer_%s.exe' % build) |
| 178 InstallTest._installer_paths.append(installer_path) |
| 179 InstallTest._Download(url, installer_path) |
| 180 InstallTest._chrome_driver = os.path.join(tempdir, 'chromedriver.exe') |
| 181 url = '%s%s/%s/%s/chromedriver.exe' % (base_url, build, system, |
| 182 'chrome-win32.test') |
| 183 InstallTest._Download(url, InstallTest._chrome_driver) |
| 184 |
| 185 |
| 186 class Main(object): |
| 187 """Main program for running Updater tests.""" |
| 188 |
| 189 def __init__(self): |
| 190 self._SetLoggingConfiguration() |
| 191 self._ParseArgs() |
| 192 self._Run() |
| 193 |
| 194 def _ParseArgs(self): |
| 195 """Parses command line arguments.""" |
| 196 parser = optparse.OptionParser() |
| 197 parser.add_option( |
| 198 '-b', '--builds', type='string', default='', dest='builds', |
| 199 help='Specifies the two builds needed for testing.') |
| 200 parser.add_option( |
| 201 '-u', '--url', type='string', default='', dest='url', |
| 202 help='Specifies the build url, without the build number.') |
| 203 parser.add_option( |
| 204 '-o', '--options', type='string', default='', |
| 205 help='Specifies any additional Chrome options (i.e. --system-level).') |
| 206 opts, args = parser.parse_args() |
| 207 self._ValidateArgs(opts) |
| 208 InstallTest.InitTestFixture(opts.builds, opts.url, opts.options) |
| 209 |
| 210 def _ValidateArgs(self, opts): |
| 211 """Verifies the sanity of the command arguments. |
| 212 |
| 213 Confirms that all specified builds have a valid version number, and the |
| 214 build urls are valid. |
| 215 |
| 216 Args: |
| 217 opts: An object containing values for all command args. |
| 218 """ |
| 219 builds = opts.builds.split(',') |
| 220 for build in builds: |
| 221 if not re.match('\d+\.\d+\.\d+\.\d+', build): |
| 222 raise RuntimeError('Invalid build number: %s' % build) |
| 223 if not pyauto_utils.DoesUrlExist('%s/%s/' % (opts.url, build)): |
| 224 raise RuntimeError('Could not locate build no. %s' % build) |
| 225 |
| 226 def _SetLoggingConfiguration(self): |
| 227 """Sets the basic logging configuration.""" |
| 228 log_format = '%(asctime)s %(levelname)-8s %(message)s' |
| 229 logging.basicConfig(level=logging.INFO, format=log_format) |
| 230 |
| 231 def _GetTests(self): |
| 232 """Returns a list of unittests from the calling script.""" |
| 233 mod_name = [os.path.splitext(os.path.basename(sys.argv[0]))[0]] |
| 234 if os.path.dirname(sys.argv[0]) not in sys.path: |
| 235 sys.path.append(os.path.dirname(sys.argv[0])) |
| 236 return unittest.defaultTestLoader.loadTestsFromNames(mod_name) |
| 237 |
| 238 def _Run(self): |
| 239 """Runs the unit tests.""" |
| 240 tests = self._GetTests() |
| 241 result = pyauto_utils.GTestTextTestRunner(verbosity=1).run(tests) |
| 242 del(tests) |
| 243 if not result.wasSuccessful(): |
| 244 print >>sys.stderr, ('Not all tests were successful.') |
| 245 sys.exit(1) |
| 246 sys.exit(0) |
| OLD | NEW |