Package tlib :: Package base :: Module AndroidHelper
[hide private]
[frames] | no frames]

Source Code for Module tlib.base.AndroidHelper

  1  # noinspection PyPackageRequirements 
  2  import requests 
  3  import time 
  4  # noinspection PyPackageRequirements 
  5  import pytest 
  6  import re 
  7  from _pytest import runner 
  8  from tlib.base import TestHelper 
  9  import logging 
 10   
 11  DEVICE_UNPLUGGED = "disconnected"    # Device is not connected 
 12  DEVICE_OFFLINE = "offline"           # Device is connected but offline 
 13  DEVICE_UNAUTHORIZED = "unauthorized" # Unauthorized 
 14  DEVICE_ONLINE = "online"             # Device is connected and accessible 
 15   
16 -def is_webdriver_running(tlib_logger, adb_logger, log=True):
17 """ 18 Function to validate if webdriver is running and a connection can be established 19 20 @param tlib_logger: TLib logger 21 @type tlib_logger: logging.logger 22 @param adb_logger: ADB logger 23 @type adb_logger: logging.logger 24 @param log: When true, log debugging information 25 @type log: bool 26 """ 27 try: 28 if log: tlib_logger.debug("Checking if Webdriver is running") 29 response = requests.get("http://localhost:8080/wd/hub/status", timeout=10) 30 except requests.ConnectionError as e: 31 if log: adb_logger.debug("Connection to Webdriver failed:\n%s" % e) 32 return False 33 34 return response.status_code == 200
35 36
37 -def get_device_status(tlib_logger, adb_logger, serial_id):
38 """ 39 Get status of a device using it's serial id\n 40 Serial id can be either a ID (for devices connected to USB) or an IP and port (For devices connected via IP) 41 42 @param tlib_logger: TLib logger 43 @type tlib_logger: logging.logger 44 @param adb_logger: ADB logger 45 @type adb_logger: logging.logger 46 @param serial_id: Device's serial number 47 @type serial_id: str 48 """ 49 50 out = TestHelper.run_command(adb_logger, ["adb", "devices"], fail_on_error=False) 51 if re.search(serial_id, out[0]) is None: 52 tlib_logger.debug("Device {serial_id} is not connected".format(serial_id=serial_id)) 53 return DEVICE_UNPLUGGED 54 elif re.search("{serial_id}\s+offline".format(serial_id=serial_id), out[0]) is not None: 55 tlib_logger.debug("Device {serial_id} is offline".format(serial_id=serial_id)) 56 return DEVICE_OFFLINE 57 elif re.search("{serial_id}\s+unauthorized".format(serial_id=serial_id), out[0]) is not None: 58 tlib_logger.debug("Device {serial_id} is offline".format(serial_id=serial_id)) 59 return DEVICE_UNAUTHORIZED 60 elif re.search("{serial_id}\s+device".format(serial_id=serial_id), out[0]) is not None: 61 tlib_logger.debug("Device {serial_id} is online".format(serial_id=serial_id)) 62 return DEVICE_ONLINE 63 else: 64 tlib_logger.error("Unknown device status\n%s" % out[0]) 65 pytest.fail("Unknown device status\n%s" % out[0])
66 67
68 -def setup_ip_connection(tlib_logger, adb_logger, serial_id):
69 """ 70 Connects to an android device via IP and waits for the connection to be established. 71 72 @param tlib_logger: TLib logger 73 @type tlib_logger: logging.logger 74 @param adb_logger: ADB logger 75 @type adb_logger: logging.logger 76 @param serial_id: Device's serial number 77 @type serial_id: str 78 """ 79 # Validate is device is already connected 80 tlib_logger.debug("Setting up IP connection to device {serial_id}".format(serial_id=serial_id)) 81 status = get_device_status(tlib_logger, adb_logger, serial_id) 82 83 if status == DEVICE_ONLINE: 84 tlib_logger.debug("Device is already online".format(serial_id=serial_id)) 85 return 86 87 # If device is offline or unauthorized, disconnect 88 if status == DEVICE_OFFLINE: 89 tlib_logger.warn("Device {serial_id} is offline, disconnecting and retrying".format(serial_id=serial_id)) 90 terminate_ip_connection(tlib_logger, adb_logger, serial_id) 91 elif status == DEVICE_UNAUTHORIZED: 92 terminate_ip_connection(tlib_logger, adb_logger, serial_id) 93 tlib_logger.error("Device {serial_id} is not authorized, aborting".format(serial_id=serial_id)) 94 pytest.fail("Device {serial_id} is not authorized, aborting".format(serial_id=serial_id)) 95 elif status == DEVICE_UNPLUGGED: 96 tlib_logger.warn("Device {serial_id} is disconecting, resetting connection".format(serial_id=serial_id)) 97 terminate_ip_connection(tlib_logger, adb_logger, serial_id) 98 99 timeout = 0.5 100 101 #We wait up to 3 seconds 102 for i in range(1, int(3 / timeout)): 103 out = TestHelper.run_command(adb_logger, ["adb", "connect", serial_id]) 104 log_adb_output(adb_logger, out) 105 106 if get_device_status(tlib_logger, adb_logger, serial_id) == DEVICE_ONLINE: 107 tlib_logger.debug("Connected to device") 108 return 109 else: 110 tlib_logger.debug("Not yet connected") 111 time.sleep(timeout) 112 113 #If we're here, it means connection failed 114 adb_logger.error(r"Timeout while connecting to device {serial_id}\nADB output:\n{out}".format(serial_id=serial_id, out=out)) 115 # noinspection PyUnresolvedReferences 116 pytest.fail(r"Timeout while connecting to device {serial_id}\nADB output:\n{out}".format(serial_id=serial_id, out=out))
117 118
119 -def terminate_ip_connection(tlib_logger, adb_logger, serial_id):
120 """ 121 Disconnects from device 122 123 @param tlib_logger: TLib logger 124 @type tlib_logger: logging.logger 125 @param adb_logger: ADB logger 126 @type adb_logger: logging.logger 127 @param serial_id: Device's serial number 128 @type serial_id: str 129 """ 130 tlib_logger.debug("Disconnecting from device {serial_id}".format(serial_id=serial_id)) 131 out = TestHelper.run_command(adb_logger, ["adb", "disconnect", serial_id]) 132 log_adb_output(adb_logger, out)
133 134
135 -def close_webdriver(tlib_logger, adb_logger, serial_id):
136 """ 137 Stops Webdriver app o the device 138 139 @param tlib_logger: TLib logger 140 @type tlib_logger: logging.logger 141 @param adb_logger: ADB logger 142 @type adb_logger: logging.logger 143 @param serial_id: Device's serial number 144 @type serial_id: str 145 """ 146 tlib_logger.debug("Closing Webdriver on {serial_id}".format(serial_id=serial_id)) 147 if serial_id is None or serial_id == "": 148 out = TestHelper.run_command(adb_logger, 149 "adb shell am force-stop org.openqa.selenium.android.app", shell=True) 150 else: 151 out = TestHelper.run_command(adb_logger, 152 "adb -s %s shell am force-stop org.openqa.selenium.android.app" % serial_id, shell=True) 153 log_adb_output(adb_logger, out)
154 155
156 -def setup_port_forwarding(tlib_logger, adb_logger, serial_id):
157 """ 158 Setup port forwarding between computer and device. 159 160 @param tlib_logger: TLib logger 161 @type tlib_logger: logging.logger 162 @param adb_logger: ADB logger 163 @type adb_logger: logging.logger 164 @param serial_id: Device's serial number 165 @type serial_id: str 166 """ 167 tlib_logger.info("Setting up port forwarding") 168 if serial_id is None or serial_id == "": 169 out = TestHelper.run_command(adb_logger, "adb forward tcp:8080 tcp:8080", shell=True) 170 else: 171 out = TestHelper.run_command(adb_logger, "adb -s %s forward tcp:8080 tcp:8080" % serial_id, shell=True) 172 log_adb_output(adb_logger, out)
173 174
175 -def teardown_port_forwarding(tlib_logger, adb_logger):
176 """ 177 Terminates all port forwarding connections 178 179 @param tlib_logger: TLib logger 180 @type tlib_logger: logging.logger 181 @param adb_logger: ADB logger 182 @type adb_logger: logging.logger 183 """ 184 tlib_logger.info("Tearing down port forwarding") 185 out = TestHelper.run_command(adb_logger, "adb forward --remove-all", shell=True) 186 log_adb_output(adb_logger, out)
187
188 -def start_adb_server(tlib_logger, adb_logger):
189 """ 190 Stops adb on the machine\n 191 This can be required by TeamCity so some folders are not locked 192 193 @param tlib_logger: TLib logger 194 @type tlib_logger: logging.logger 195 @param adb_logger: ADB logger 196 @type adb_logger: logging.logger 197 """ 198 tlib_logger.info("Starting ADB") 199 out = TestHelper.run_command(adb_logger, ["adb", "start-server"], fail_on_error=False) 200 log_adb_output(adb_logger, out)
201
202 -def stop_adb_server(tlib_logger, adb_logger):
203 """ 204 Stops adb on the machine\n 205 This can be required by TeamCity so some folders are not locked 206 207 @param tlib_logger: TLib logger 208 @type tlib_logger: logging.logger 209 @param adb_logger: ADB logger 210 @type adb_logger: logging.logger 211 """ 212 tlib_logger.info("Stopping ADB") 213 out = TestHelper.run_command(adb_logger, ["adb", "kill-server"], fail_on_error=False) 214 log_adb_output(adb_logger, out)
215 216
217 -def start_webdriver(tlib_logger, adb_logger, serial_id):
218 """ 219 Starts Webdriver app on the device 220 221 @param tlib_logger: TLib logger 222 @type tlib_logger: logging.logger 223 @param adb_logger: ADB logger 224 @type adb_logger: logging.logger 225 @param serial_id: Device's serial number 226 @type serial_id: str 227 """ 228 tlib_logger.info("Starting webdriver on the device") 229 if serial_id is None or serial_id == "": 230 out = TestHelper.run_command(adb_logger, 231 "adb shell am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity -e debug true", 232 shell=True) 233 else: 234 out = TestHelper.run_command(adb_logger, 235 "adb -s %s shell am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity -e debug true" % serial_id, 236 shell=True) 237 log_adb_output(adb_logger, out)
238 239
240 -def wait_for_connection_to_webdriver(tlib_logger, adb_logger):
241 """ 242 Waits up to 3 seconds for a connection to Webdriver 243 244 @param tlib_logger: TLib logger 245 @type tlib_logger: logging.logger 246 @param adb_logger: ADB logger 247 @type adb_logger: logging.logger 248 """ 249 #Check connection to webdriver can be established. Retry 3 times 250 tlib_logger.debug("Waiting for connection to Webdriver") 251 timeout = 0.5 252 for i in range(1, int(3 / timeout)): 253 if is_webdriver_running(tlib_logger, adb_logger, False): 254 tlib_logger.debug("Webdriver started successfully") 255 break 256 tlib_logger.debug("Can't connect to Webdriver, retrying in {timeout} seconds".format(timeout=timeout)) 257 time.sleep(timeout) 258 259 if not is_webdriver_running(tlib_logger, adb_logger, False): 260 tlib_logger.error("Couldn't start Webdriver. Make sure it's installed and running\n" 261 "See https://code.google.com/p/selenium/wiki/AndroidDriver#Setup_the_Emulator for more details") 262 # noinspection PyUnresolvedReferences 263 pytest.fail("Couldn't start Webdriver. Make sure it's installed and running\n" 264 "See https://code.google.com/p/selenium/wiki/AndroidDriver#Setup_the_Emulator for more details")
265 266 267 # noinspection PyUnresolvedReferences
268 -def setup_webdriver(tlib_logger, adb_logger, serial_id):
269 """ 270 Connects to a device and starts webdriver 271 272 @param tlib_logger: TLib logger 273 @type tlib_logger: logging.logger 274 @param adb_logger: ADB logger 275 @type adb_logger: logging.logger 276 @param serial_id: Device's serial number 277 @type serial_id: str 278 """ 279 #Try to connect to Webdriver and exit if success 280 if is_webdriver_running(tlib_logger, adb_logger, log=False): 281 tlib_logger.debug("Already connected to device") 282 return 283 284 tlib_logger.info("Connecting to Webdriver") 285 286 #Connect to device and setup port forwarding. 287 if TestHelper.is_valid_ip(serial_id): 288 setup_ip_connection(tlib_logger, adb_logger, serial_id) 289 290 setup_port_forwarding(tlib_logger, adb_logger, serial_id) 291 292 #Try again to connect to Webdriver and exit if success 293 if is_webdriver_running(tlib_logger, adb_logger): 294 tlib_logger.debug("Connected to Webdriver") 295 return 296 297 # Webdriver not running, start it 298 start_webdriver(tlib_logger, adb_logger, serial_id) 299 300 #Ensure we're connected 301 wait_for_connection_to_webdriver(tlib_logger, adb_logger) 302 303 tlib_logger.info("Connection to Webdriver established")
304 305 306 # noinspection PyUnresolvedReferences
307 -def teardown_webdriver(tlib_logger, adb_logger, serial_id):
308 """ 309 Closes webdriver and disconnects from device 310 311 @param tlib_logger: TLib logger 312 @type tlib_logger: logging.logger 313 @param adb_logger: ADB logger 314 @type adb_logger: logging.logger 315 @param serial_id: Device's serial number 316 @type serial_id: str 317 """ 318 #close selenium WebDriver 319 tlib_logger.info("Disconnecting from Webdriver") 320 close_webdriver(tlib_logger, adb_logger, serial_id) 321 322 # Terminates IP connection if an IP was given 323 if TestHelper.is_valid_ip(serial_id): 324 terminate_ip_connection(tlib_logger, adb_logger, serial_id) 325 326 #Stop adb service so it won't lock files required by TeamCity 327 try: 328 stop_adb_server(tlib_logger, adb_logger) 329 except runner.Failed: 330 #If ADB fails to stop, don't abort test 331 adb_logger.warn("Error stopping ADB server") 332 333 tlib_logger.info("Disconnected from Webdriver")
334 335 336 # noinspection PyUnresolvedReferences
337 -def start_selendroid_server(tlib_logger, adb_logger, app_path):
338 """ 339 Start selendroid server and point to apk file 340 341 @param tlib_logger: TLib logger 342 @type tlib_logger: logging.logger 343 @param adb_logger: Adb logger 344 @type adb_logger: logging.logger 345 @param app_path: Location of the apk file 346 @type app_path: str 347 """ 348 tlib_logger.info("Starting selendroid server") 349 stop_adb_server(tlib_logger, adb_logger) 350 start_adb_server(tlib_logger, adb_logger) 351 process = TestHelper.start_process(tlib_logger, "java -jar %s -aut %s" % (TestHelper.selendroid_server_jar(), app_path), shell=True) 352 trial_left = 20 353 while not is_selendroid_running(tlib_logger, adb_logger, False) and trial_left > 0: 354 time.sleep(0.5) 355 trial_left -= 1 356 if trial_left > 0: 357 return process 358 return None
359 360
361 -def is_selendroid_running(tlib_logger, adb_logger, log=True):
362 """ 363 Function to validate if webdriver is running and a connection can be established 364 365 @param tlib_logger: TLib logger 366 @type tlib_logger: logging.logger 367 @param adb_logger: ADB logger 368 @type adb_logger: logging.logger 369 @param log: When true, log debugging information 370 @type log: bool 371 """ 372 try: 373 if log: tlib_logger.debug("Checking if Selendroid server is running") 374 response = requests.get("http://localhost:4444/wd/hub/status", timeout=10) 375 except requests.ConnectionError as e: 376 if log: adb_logger.debug("Connection to Selendroid failed:\n%s" % e) 377 return False 378 379 return response.status_code == 200
380 381
382 -def get_android_app_id():
383 response = requests.get("http://localhost:4444/wd/hub/status") 384 if not response: 385 return None 386 from jsonpath_rw import jsonpath 387 apps = jsonpath(response.json(), '$.value.supportedApps[*].appId') 388 for app in apps: 389 if not app.startswith('io.selendroid.androiddriver'): 390 return app 391 return None
392
393 -def log_adb_output(logger, out):
394 """ 395 Logs ADB output 396 397 @param logger: ADB logger 398 @type logger: logging.logger 399 """ 400 if out[0] is not None and out[0] != '': 401 logger.debug("\n" + out[0]) 402 403 if out[1] is not None and out[1] != '': 404 logger.debug("\n" + out[1])
405