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

Source Code for Module tlib.base.AndroidHelper

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