GoogleGit

blob: 2a3808cdc2d2eec0f0b5c0a274398c50565b8999 [file] [log] [blame]
  1. # Copyright (c) 2013 The Chromium OS 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. import atexit
  5. import logging
  6. import os
  7. import urllib2
  8. try:
  9. from selenium import webdriver
  10. except ImportError:
  11. # Ignore import error, as this can happen when builder tries to call the
  12. # setup method of test that imports chromedriver.
  13. logging.error('selenium module failed to be imported.')
  14. pass
  15. from autotest_lib.client.bin import utils
  16. from autotest_lib.client.common_lib.cros import chrome
  17. CHROMEDRIVER_EXE_PATH = '/usr/local/chromedriver/chromedriver'
  18. X_SERVER_DISPLAY = ':0'
  19. X_AUTHORITY = '/home/chronos/.Xauthority'
  20. class chromedriver(object):
  21. """Wrapper class, a context manager type, for tests to use Chrome Driver."""
  22. def __init__(self, extra_chrome_flags=[], subtract_extra_chrome_flags=[],
  23. extension_paths=[], is_component=True, username=None,
  24. password=None, server_port=None, skip_cleanup=False, *args,
  25. **kwargs):
  26. """Initialize.
  27. @param extra_chrome_flags: Extra chrome flags to pass to chrome, if any.
  28. @param subtract_extra_chrome_flags: Remove default flags passed to
  29. chrome by chromedriver, if any.
  30. @param extension_paths: A list of paths to unzipped extensions. Note
  31. that paths to crx files won't work.
  32. @param is_component: True if the manifest.json has a key.
  33. @param username: Log in using this username instead of the default.
  34. @param password: Log in using this password instead of the default.
  35. @param server_port: Port number for the chromedriver server. If None,
  36. an available port is chosen at random.
  37. @param skip_cleanup: If True, leave the server and browser running
  38. so that remote tests can run after this script
  39. ends. Default is False.
  40. """
  41. self._cleanup = not skip_cleanup
  42. assert os.geteuid() == 0, 'Need superuser privileges'
  43. # Log in with telemetry
  44. self._chrome = chrome.Chrome(extension_paths=extension_paths,
  45. is_component=is_component,
  46. username=username,
  47. password=password,
  48. extra_browser_args=extra_chrome_flags)
  49. self._browser = self._chrome.browser
  50. # Close all tabs owned and opened by Telemetry, as these cannot be
  51. # transferred to ChromeDriver.
  52. self._browser.tabs[0].Close()
  53. # Start ChromeDriver server
  54. self._server = chromedriver_server(CHROMEDRIVER_EXE_PATH,
  55. port=server_port,
  56. skip_cleanup=skip_cleanup)
  57. # Open a new tab using Chrome remote debugging. ChromeDriver expects
  58. # a tab opened for remote to work. Tabs opened using Telemetry will be
  59. # owned by Telemetry, and will be inaccessible to ChromeDriver.
  60. urllib2.urlopen('http://localhost:%i/json/new' %
  61. utils.get_chrome_remote_debugging_port())
  62. chromeOptions = {'debuggerAddress':
  63. ('localhost:%d' %
  64. utils.get_chrome_remote_debugging_port())}
  65. capabilities = {'chromeOptions':chromeOptions}
  66. # Handle to chromedriver, for chrome automation.
  67. try:
  68. self.driver = webdriver.Remote(command_executor=self._server.url,
  69. desired_capabilities=capabilities)
  70. except NameError:
  71. logging.error('selenium module failed to be imported.')
  72. raise
  73. def __enter__(self):
  74. return self
  75. def __exit__(self, *args):
  76. """Clean up after running the test.
  77. """
  78. if hasattr(self, 'driver') and self.driver:
  79. self.driver.close()
  80. del self.driver
  81. if not hasattr(self, '_cleanup') or self._cleanup:
  82. if hasattr(self, '_server') and self._server:
  83. self._server.close()
  84. del self._server
  85. if hasattr(self, '_browser') and self._browser:
  86. self._browser.Close()
  87. del self._browser
  88. def get_extension(self, extension_path):
  89. """Gets an extension by proxying to the browser.
  90. @param extension_path: Path to the extension loaded in the browser.
  91. @return: A telemetry extension object representing the extension.
  92. """
  93. return self._chrome.get_extension(extension_path)
  94. class chromedriver_server(object):
  95. """A running ChromeDriver server.
  96. This code is migrated from chrome:
  97. src/chrome/test/chromedriver/server/server.py
  98. """
  99. def __init__(self, exe_path, port=None, skip_cleanup=False):
  100. """Starts the ChromeDriver server and waits for it to be ready.
  101. Args:
  102. exe_path: path to the ChromeDriver executable
  103. port: server port. If None, an available port is chosen at random.
  104. skip_cleanup: If True, leave the server running so that remote
  105. tests can run after this script ends. Default is
  106. False.
  107. Raises:
  108. RuntimeError if ChromeDriver fails to start
  109. """
  110. if not os.path.exists(exe_path):
  111. raise RuntimeError('ChromeDriver exe not found at: ' + exe_path)
  112. chromedriver_args = [exe_path]
  113. if port:
  114. # Allow remote connections if a port was specified
  115. chromedriver_args.append('--whitelisted-ips')
  116. else:
  117. port = utils.get_unused_port()
  118. chromedriver_args.append('--port=%d' % port)
  119. # Chromedriver will look for an X server running on the display
  120. # specified through the DISPLAY environment variable.
  121. utils.assert_has_X_server()
  122. os.environ['DISPLAY'] = X_SERVER_DISPLAY
  123. os.environ['XAUTHORITY'] = X_AUTHORITY
  124. self.bg_job = utils.BgJob(chromedriver_args, stderr_level=logging.DEBUG)
  125. self.url = 'http://localhost:%d' % port
  126. if self.bg_job is None:
  127. raise RuntimeError('ChromeDriver server cannot be started')
  128. try:
  129. timeout_msg = 'Timeout on waiting for ChromeDriver to start.'
  130. utils.poll_for_condition(self.is_running,
  131. exception=utils.TimeoutError(timeout_msg),
  132. timeout=10,
  133. sleep_interval=.1)
  134. except utils.TimeoutError:
  135. self.close_bgjob()
  136. raise RuntimeError('ChromeDriver server did not start')
  137. logging.debug('Chrome Driver server is up and listening at port %d.',
  138. port)
  139. if not skip_cleanup:
  140. atexit.register(self.close)
  141. def is_running(self):
  142. """Returns whether the server is up and running."""
  143. try:
  144. urllib2.urlopen(self.url + '/status')
  145. return True
  146. except urllib2.URLError as e:
  147. return False
  148. def close_bgjob(self):
  149. """Close background job and log stdout and stderr."""
  150. utils.nuke_subprocess(self.bg_job.sp)
  151. utils.join_bg_jobs([self.bg_job], timeout=1)
  152. result = self.bg_job.result
  153. if result.stdout or result.stderr:
  154. logging.info('stdout of Chrome Driver:\n%s', result.stdout)
  155. logging.error('stderr of Chrome Driver:\n%s', result.stderr)
  156. def close(self):
  157. """Kills the ChromeDriver server, if it is running."""
  158. if self.bg_job is None:
  159. return
  160. try:
  161. urllib2.urlopen(self.url + '/shutdown', timeout=10).close()
  162. except:
  163. pass
  164. self.close_bgjob()