• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Utility script to run tests on the Chromoting bot."""
6
7from __future__ import print_function
8
9import hashlib
10import os
11from os.path import expanduser
12import re
13import shutil
14import socket
15import subprocess
16
17import psutil
18
19PROD_DIR_ID = '#PROD_DIR#'
20CRD_ID = 'chrome-remote-desktop'  # Used in a few file/folder names
21HOST_READY_INDICATOR = 'Host ready to receive connections.'
22BROWSER_TEST_ID = 'browser_tests'
23HOST_HASH_VALUE = hashlib.md5(socket.gethostname()).hexdigest()
24NATIVE_MESSAGING_DIR = 'NativeMessagingHosts'
25# On a Swarming bot where these tests are executed, a temp folder is created
26# under which the files specified in an .isolate are copied. This temp folder
27# has a random name, which we'll store here for use later.
28# Note that the test-execution always starts from the testing/chromoting folder
29# under the temp folder.
30ISOLATE_CHROMOTING_HOST_PATH = 'remoting/host/linux/linux_me2me_host.py'
31ISOLATE_TEMP_FOLDER = os.path.abspath(os.path.join(os.getcwd(), '../..'))
32CHROMOTING_HOST_PATH = os.path.join(ISOLATE_TEMP_FOLDER,
33                                    ISOLATE_CHROMOTING_HOST_PATH)
34MAX_RETRIES = 1
35
36
37class HostOperationFailedException(Exception):
38  pass
39
40
41def RunCommandInSubProcess(command):
42  """Creates a subprocess with command-line that is passed in.
43
44  Args:
45    command: The text of command to be executed.
46  Returns:
47    results: stdout contents of executing the command.
48  """
49
50  cmd_line = [command]
51  try:
52    print('Going to run:\n%s' % command)
53    results = subprocess.check_output(cmd_line, stderr=subprocess.STDOUT,
54                                      shell=True)
55  except subprocess.CalledProcessError as e:
56    results = e.output
57  finally:
58    print(results)
59  return results
60
61
62def TestMachineCleanup(user_profile_dir, host_logs=None):
63  """Cleans up test machine so as not to impact other tests.
64
65  Args:
66    user_profile_dir: the user-profile folder used by Chromoting tests.
67    host_logs: List of me2me host logs; these will be deleted.
68  """
69
70  # Stop the host service.
71  RunCommandInSubProcess(CHROMOTING_HOST_PATH + ' --stop')
72
73  # Cleanup any host logs.
74  if host_logs:
75    for host_log in host_logs:
76      RunCommandInSubProcess('rm %s' % host_log)
77
78  # Remove the user-profile dir
79  if os.path.exists(user_profile_dir):
80    shutil.rmtree(user_profile_dir)
81
82
83def InitialiseTestMachineForLinux(cfg_file):
84  """Sets up a Linux machine for connect-to-host chromoting tests.
85
86  Copy over me2me host-config to expected locations.
87  By default, the Linux me2me host expects the host-config file to be under
88  $HOME/.config/chrome-remote-desktop
89  Its name is expected to have a hash that is specific to a machine.
90
91  Args:
92    cfg_file: location of test account's host-config file.
93
94  Raises:
95    Exception: if host did not start properly.
96  """
97
98  # First get home directory on current machine.
99  home_dir = expanduser('~')
100  default_config_file_location = os.path.join(home_dir, '.config', CRD_ID)
101  if os.path.exists(default_config_file_location):
102    shutil.rmtree(default_config_file_location)
103  os.makedirs(default_config_file_location)
104
105  # Copy over test host-config to expected location, with expected file-name.
106  # The file-name should contain a hash-value that is machine-specific.
107  default_config_file_name = 'host#%s.json' % HOST_HASH_VALUE
108  config_file_src = os.path.join(os.getcwd(), cfg_file)
109  shutil.copyfile(
110      config_file_src,
111      os.path.join(default_config_file_location, default_config_file_name))
112
113  # Make sure chromoting host is running.
114  RestartMe2MeHost()
115
116
117def RestartMe2MeHost():
118  """Stops and starts the Me2Me host on the test machine.
119
120  Launches the me2me start-host command, and parses the stdout of the execution
121  to obtain the host log-file name.
122
123  Returns:
124    log_file: Host log file.
125
126  Raises:
127    Exception: If host-log does not contain string indicating host is ready.
128  """
129
130  # To start the host, we want to be in the temp-folder for this test execution.
131  # Store the current folder to return back to it later.
132  previous_directory = os.getcwd()
133  os.chdir(ISOLATE_TEMP_FOLDER)
134
135  # Stop chromoting host.
136  RunCommandInSubProcess(CHROMOTING_HOST_PATH + ' --stop')
137  # Start chromoting host.
138  print('Starting chromoting host from %s' % CHROMOTING_HOST_PATH)
139  results = RunCommandInSubProcess(CHROMOTING_HOST_PATH + ' --start')
140
141  os.chdir(previous_directory)
142
143  # Get log file from results of above command printed to stdout. Example:
144  # Log file: /tmp/tmp0c3EcP/chrome_remote_desktop_20150929_101525_B0o89t
145  start_of_host_log = results.index('Log file: ') + len('Log file: ')
146  log_file = results[start_of_host_log:].rstrip()
147
148  # Confirm that the start process completed, and we got:
149  # "Host ready to receive connections." in the log.
150  if HOST_READY_INDICATOR not in results:
151    # Host start failed. Print out host-log. Don't run any tests.
152    with open(log_file, 'r') as f:
153      print(f.read())
154    raise HostOperationFailedException('Host restart failed.')
155
156  return log_file
157
158
159def CleanupUserProfileDir(args):
160  SetupUserProfileDir(args.me2me_manifest_file, args.it2me_manifest_file,
161                      args.user_profile_dir)
162
163
164def SetupUserProfileDir(me2me_manifest_file, it2me_manifest_file,
165                        user_profile_dir):
166  """Sets up the Google Chrome user profile directory.
167
168  Delete the previous user profile directory if exists and create a new one.
169  This invalidates any state changes by the previous test so each test can start
170  with the same environment.
171
172  When a user launches the remoting web-app, the native messaging host process
173  is started. For this to work, this function places the me2me and it2me native
174  messaging host manifest files in a specific folder under the user-profile dir.
175
176  Args:
177    me2me_manifest_file: location of me2me native messaging host manifest file.
178    it2me_manifest_file: location of it2me native messaging host manifest file.
179    user_profile_dir: Chrome user-profile-directory.
180  """
181  native_messaging_folder = os.path.join(user_profile_dir, NATIVE_MESSAGING_DIR)
182
183  if os.path.exists(user_profile_dir):
184    shutil.rmtree(user_profile_dir)
185  os.makedirs(native_messaging_folder)
186
187  manifest_files = [me2me_manifest_file, it2me_manifest_file]
188  for manifest_file in manifest_files:
189    manifest_file_src = os.path.join(os.getcwd(), manifest_file)
190    manifest_file_dest = (
191        os.path.join(native_messaging_folder, os.path.basename(manifest_file)))
192    shutil.copyfile(manifest_file_src, manifest_file_dest)
193
194
195def PrintRunningProcesses():
196  processes = psutil.get_process_list()
197  processes = sorted(processes, key=lambda process: process.name)
198
199  print('List of running processes:\n')
200  for process in processes:
201    print(process.name)
202
203
204def PrintHostLogContents(host_log_files=None):
205  if host_log_files:
206    host_log_contents = ''
207    for log_file in sorted(host_log_files):
208      with open(log_file, 'r') as log:
209        host_log_contents += '\nHOST LOG %s\n CONTENTS:\n%s' % (
210            log_file, log.read())
211    print(host_log_contents)
212
213
214def TestCaseSetup(args):
215  # Reset the user profile directory to start each test with a clean slate.
216  CleanupUserProfileDir(args)
217  # Stop+start me2me host process.
218  return RestartMe2MeHost()
219
220
221def GetJidListFromTestResults(results):
222  """Parse the output of a test execution to obtain the JID used by the test.
223
224  Args:
225    results: stdio contents of test execution.
226
227  Returns:
228    jids_used: List of JIDs used by test; empty list if not found.
229  """
230
231  # Reg-ex defining the JID information in the string being parsed.
232  jid_re = '(Connecting to )(.*.gserviceaccount.com/chromoting.*)(. Local.*)'
233  jids_used = []
234  for line in results.split('\n'):
235    match = re.search(jid_re, line)
236    if match:
237      jid_used = match.group(2)
238      if jid_used not in jids_used:
239        jids_used.append(jid_used)
240
241  return jids_used
242
243
244def GetJidFromHostLog(host_log_file):
245  """Parse the me2me host log to obtain the JID that the host registered.
246
247  Args:
248    host_log_file: path to host-log file that should be parsed for a JID.
249
250  Returns:
251    host_jid: host-JID if found in host-log, else None
252  """
253  host_jid = None
254  with open(host_log_file, 'r') as log_file:
255    for line in log_file:
256      # The host JID will be recorded in a line saying 'Signaling
257      # connected'.
258      if 'Signaling connected. ' in line:
259        components = line.split(':')
260        host_jid = components[-1].lstrip()
261        break
262
263  return host_jid
264