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