1#!/usr/bin/env python
2# Copyright 2015 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
6import logging
7import os
8import shutil
9import stat
10import subprocess
11import sys
12import tempfile
13import time
14import urllib2
15import zipfile
16
17# URL on omahaproxy.appspot.com which lists cloud storage buckets.
18OMAHA_URL = 'https://omahaproxy.appspot.com/all?os=%s&channel=stable'
19
20# URL in cloud storage to download Chrome zip from.
21CLOUDSTORAGE_URL = ('https://commondatastorage.googleapis.com/chrome-unsigned'
22                    '/desktop-W15K3Y/%s/%s/chrome-%s.zip')
23
24# Mapping of sys.platform -> platform-specific names and paths.
25PLATFORM_MAPPING = {
26    'linux2': {
27        'omaha': 'linux',
28        'cs_dir': 'precise64',
29        'cs_filename': 'precise64',
30        'chromepath': 'chrome-precise64/chrome',
31        'use_xfvb': True,
32    },
33    'win32': {
34        'omaha': 'win',
35        'cs_dir': 'win',
36        'cs_filename': 'win',
37        'chromepath': 'Chrome-bin\\chrome.exe',
38        'installer_url': ('https://commondatastorage.googleapis.com/'
39                          'chrome-signed/desktop-W15K3Y/%VERSION%/win/'
40                          '%VERSION%_chrome_installer.exe'),
41    },
42    'darwin': {
43        'omaha': 'mac',
44        'cs_dir': 'mac64',
45        'cs_filename': 'mac',
46        'chromepath': ('chrome-mac/Google Chrome.app/'
47                       'Contents/MacOS/Google Chrome'),
48        'additional_paths': [
49            ('chrome-mac/Google Chrome.app/Contents/Versions/%VERSION%/'
50             'Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper'),
51        ],
52    },
53}
54
55
56def StartXvfb():
57  display = ':99'
58  xvfb_command = [
59    'Xvfb',
60    display,
61    '-screen',
62    '0',
63    '1024x769x24',
64    '-ac'
65  ]
66  xvfb_process = subprocess.Popen(
67      xvfb_command, stdout=open(os.devnull), stderr=open(os.devnull))
68  time.sleep(0.2)
69  returncode = xvfb_process.poll()
70  if returncode is None:
71    os.environ['DISPLAY'] = display
72  else:
73    logging.error('Xvfb did not start, returncode: %s', returncode)
74
75
76def IsDepotToolsPath(path):
77  return os.path.isfile(os.path.join(path, 'gclient'))
78
79
80def FindDepotTools():
81  # Check if depot_tools is already in PYTHONPATH
82  for path in sys.path:
83    if path.rstrip(os.sep).endswith('depot_tools') and IsDepotToolsPath(path):
84      return path
85
86  # Check if depot_tools is in the path
87  for path in os.environ['PATH'].split(os.pathsep):
88    if IsDepotToolsPath(path):
89        return path.rstrip(os.sep)
90
91  return None
92
93
94def DownloadSignedWinChromeStable(url, version):
95  """On Windows, use signed Chrome since it may be more stable."""
96  url = url.replace('%VERSION%', version)
97  tmpdir = tempfile.mkdtemp()
98  installer_path = os.path.join(tmpdir, url[url.rindex('/') + 1:])
99  with open(installer_path, 'wb') as local_file:
100    local_file.write(urllib2.urlopen(url).read())
101  depot_tools_path = FindDepotTools()
102  path_7z = os.path.join(depot_tools_path, 'win_toolchain', '7z', '7z.exe')
103  command_7z = [path_7z, 'x', '-o' + tmpdir, installer_path]
104  process_7z = subprocess.Popen(
105    command_7z, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
106  out_7z, err_7z = process_7z.communicate()
107  command_7z = [path_7z, 'x', '-o' + tmpdir, os.path.join(tmpdir, 'chrome.7z')]
108  process_7z = subprocess.Popen(
109    command_7z, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
110  out_7z, err_7z = process_7z.communicate()
111  return tmpdir, version
112
113
114def DownloadChromeStable():
115  platform_data = PLATFORM_MAPPING[sys.platform]
116  omaha_platform = platform_data['omaha']
117  omaha_url = OMAHA_URL % omaha_platform
118  response = urllib2.urlopen(omaha_url)
119  version = response.readlines()[1].split(',')[2]
120  if 'installer_url' in platform_data:
121    return DownloadSignedWinChromeStable(
122        platform_data['installer_url'], version)
123  cs_url = CLOUDSTORAGE_URL % (
124      version,
125      platform_data['cs_dir'],
126      platform_data['cs_filename'])
127  tmpdir = tempfile.mkdtemp()
128  zip_path = os.path.join(tmpdir, 'chrome.zip')
129  with open(zip_path, 'wb') as local_file:
130    local_file.write(urllib2.urlopen(cs_url).read())
131  zf = zipfile.ZipFile(zip_path)
132  zf.extractall(path=tmpdir)
133  return tmpdir, version
134
135
136def main():
137  try:
138    platform_data = PLATFORM_MAPPING[sys.platform]
139    if platform_data.get('use_xfvb'):
140      StartXvfb()
141    user_data_dir = tempfile.mkdtemp()
142    tmpdir, version = DownloadChromeStable()
143    server_path = os.path.join(os.path.dirname(
144        os.path.abspath(__file__)), os.pardir, 'run_dev_server')
145    server_command = [server_path, '--no-install-hooks']
146    if sys.platform.startswith('win'):
147        server_command = ['python.exe'] + server_command
148    server_process = subprocess.Popen(
149        server_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
150    time.sleep(5)
151
152    chrome_path = os.path.join(
153        tmpdir, platform_data['chromepath'])
154    os.chmod(chrome_path, os.stat(chrome_path).st_mode | stat.S_IEXEC)
155    if platform_data.get('additional_paths'):
156      for path in platform_data.get('additional_paths'):
157        path = path.replace('%VERSION%', version)
158        path = os.path.join(tmpdir, path)
159        os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
160    chrome_command = [
161        chrome_path,
162        '--user-data-dir=%s' % user_data_dir,
163        '--no-sandbox',
164        '--no-experiments',
165        '--no-first-run',
166        '--noerrdialogs',
167        'http://localhost:8003/base/tests.html?headless=true&testTypeToRun=all',
168    ]
169    chrome_process = subprocess.Popen(
170        chrome_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
171    server_out, server_err = server_process.communicate()
172    chrome_process.kill()
173    if server_process.returncode != 0:
174      logging.error('Tests failed!')
175      logging.error('Server stderr:')
176      logging.error(server_err)
177      logging.error('Server stdout:')
178      logging.error(server_out)
179    else:
180      print server_out
181  finally:
182    # Wait for Chrome to be killed before deleting temp Chrome dir.
183    time.sleep(5)
184    shutil.rmtree(tmpdir)
185    shutil.rmtree(user_data_dir)
186
187  sys.exit(server_process.returncode)
188
189
190
191if __name__ == "__main__":
192    main()
193