def run_cases_on_image(apk_path, replay_file_list, image_name): gui_port = find_free_port() appium_port = find_free_port() emu_port = find_free_port() adb_port = find_free_port() print( f"[*] port config: gui={gui_port}, appium={appium_port}, emu={emu_port} ,adb={adb_port}" ) print(f"{image_name.split('-')[-1]} [url] http://10.20.38.233:{gui_port}/") # adb related config adb_connection_str = "localhost:" + str( adb_port) # this is generated at runtime remote_addr = "http://localhost:" + str(appium_port) + "/wd/hub" # initialize client = docker.from_env() apkname = Path(apk_path).name replace_img_name = image_name.replace("/", "_") write_name = f"{apkname}_{replace_img_name}" now = datetime.datetime.now() # print("[about to init docker]") container = docker_init(client=client, image_name=image_name, gui_port=gui_port, appium_port=appium_port, emu_port=emu_port, adb_port=adb_port) try: adb_connect_install(adb_exe_path=adb_exe_path, adb_connection_str=adb_connection_str, apk_path=apk_path) except Exception as e: try: container.remove(force=True) print("{*} container removed.") except Exception as e: print(case.stem, e) return write_name log_to_file(f"replay/replay_logs/replay.log_{write_name}", "adbinstall:" + str(e), now, write_name) print("{*} remote addr: ", remote_addr) for idx, case in enumerate(replay_file_list): case = Path(case) print("{*} running case: " + str(case) + " with image: " + image_name + " apk:" + str(apk_path)) with open(case, "r", encoding="utf8") as f: code = "\n".join(f.readlines())
def run_single_apk_task(apk_path): print(f"[!] current processing {apk_path}") apk_path = str(apk_path) container = None apk_name = Path(apk_path).stem success_finish = True try: # analyse apk # apk_path="/home/luzhirui/jerrylu/mineapk/de.danoeh.antennapod.apk" print(f"[!] analyse...") aapt_path="/home/luzhirui/jerrylu/android9/android-9/aapt" package = "UNKNOWN" SDKversion, package, main_activity, minSdk = analyse_apk(apk_path, aapt_path=aapt_path) if package == "UNKNOWN": return "analyze_apk_failed..." print(SDKversion, package, main_activity, minSdk) SDKversion = str(SDKversion) # docker related config APIlevel_androidversion = { '21': '5.0.1', '22': '5.1.1', '23': '6.0', '24': '7.0', '25': '7.1.1', '26': '8.0', '27': '8.1', '28': '9.0', '29': '10.0' } Android_version = APIlevel_androidversion[SDKversion] image_name="budtmo/docker-android-x86-{}".format(Android_version) # image_name="budtmo/docker-android-x86-{}".format("9.0") gui_port=find_free_port() appium_port=find_free_port() emu_port=find_free_port() adb_port=find_free_port() print(f"[*] {apk_name}: port config: gui={gui_port}, appium={appium_port}, emu={emu_port} ,adb={adb_port}") with open("port.log", "a") as port_log: port_log.write(f"[*] {apk_name}: port config: gui={gui_port}, appium={appium_port}, emu={emu_port} ,adb={adb_port}") port_log.write("\n") # adb related config adb_exe_path="/home/luzhirui/jerrylu/adb/platform-tools/adb" adb_connection_str="localhost:" + str(adb_port) # this is generated at runtime # appium desired caps desired_caps = {} desired_caps['platformName'] = 'Android' desired_caps['platformVersion'] = Android_version desired_caps['deviceName'] = 'emulator-5554' # This should be fine... They are all called `emulator-5554` internally inside container desired_caps['appPackage'] = package desired_caps['appActivity'] = main_activity desired_caps['eventTimings'] = True desired_caps['automationName'] = 'UIAutomator2' desired_caps['autoGrantPermissions'] = True # test related data containers test_num = 0 # record target APIs triggered trigger_target_APIs = set() # record activities triggered activities = set() # record widgets triggered widgets = set() # record widgets in page_source widgets_page_source = set() # initialize print(f"[!] setting up docker...") client = docker.from_env() container = docker_init(client=client, image_name=image_name, gui_port=gui_port, appium_port=appium_port, emu_port=emu_port, adb_port=adb_port) print(f"[!] starting install apk...") adb_connect_install(adb_exe_path=adb_exe_path, adb_connection_str=adb_connection_str,apk_path=apk_path) remote_addr = "http://localhost:"+str(appium_port)+"/wd/hub" # main test loop print(f"[!] entering main test loop...") while test_num < 3: # try: print("\n{} test:\n".format(test_num)) appium_command = appium_driver_ui(desired_caps, 100, activities, widgets, widgets_page_source, test_num, remote_addr=remote_addr, adb_exe_path=adb_exe_path, apk_name=apk_name, adb_port=adb_port) print("\n"+"appium_command:") print(appium_command) generate_test(appium_command, test_num, trigger_target_APIs, apk_name=apk_name) # except Exception as e: # print(e) # print("error: {}".format(test_num) + "test") # exc_type, exc_value, exc_traceback_obj = sys.exc_info() # traceback.print_tb(exc_traceback_obj) test_num += 1 sleep(5) # output & save results print("triggered activities:") print(activities) print("triggered executable elements:") print(widgets) print("triggered target APIs:") print(trigger_target_APIs) print("all achievable executable elements") print(widgets_page_source) print("widget coverage:") coverage = len(widgets)/len(widgets_page_source) print(str(coverage)) print(f"[!] saving to report...") with open(f'report/{apk_name}_report.txt', "w") as f: f.write("triggered activities:"+"\n") f.write(str(activities)+"\n") f.write("triggered executable elements:"+"\n") f.write(str(widgets)+"\n") f.write("triggered target APIs:"+"\n") f.write(str(trigger_target_APIs)+"\n") f.write("all achievable executable elements"+"\n") f.write(str(widgets_page_source)+"\n") f.write("widget coverage:"+"\n") f.write(str(coverage)) print("-----------DONE---------------") except FunctionTimedOut as e: success_finish = False with open(f"generate_logs/{apk_name}.log", "a") as f: f.write(str(e)) print(f"[!] error {e}") print("-----------ERROR LAST---------------") except Exception as e: success_finish = False with open(f"generate_logs/{apk_name}.log", "a") as f: f.write(str(e)) print(f"[!] error {e}") print("-----------ERROR LAST---------------") finally: # tear down: remove container try: if container is not None: container.remove(force=True) except Exception as e2: with open(f"generate_logs/{apk_name}.log", "a") as f: f.write(str(e2)) print(f"[!] error {e2}") with open(f"generate_logs/{apk_name}.log", "a") as f: f.write("\nsuccess finish:"+str(success_finish)) print("{*} container removed.") return apk_path
def run_test_case(adb_port: int, appium_port: int, replay_request: ReplayRequest, channel, current_status_line, adb_exe_path, local_apk_root, replay_output_full_path) -> bool: # NOTE: replay_output_full_path ends with / # subfolder: replay_adb, replay_page_sources, replay_screenshots logging.info(f"received request: {replay_request.apkName}") # step0. prepare param apk_path = Path(local_apk_root) / replay_request.apkName adb_connection_str = "localhost:" + str(adb_port) remote_addr = "http://*****:*****@ {current_status_line}" ) if not install_success: logging.error( f"apk install failed: {replay_request.apkName} @ {current_status_line}" ) return False # step2. run test cases # for each test case # run -> read result -> package ReplayResponse -> send to queue for idx, testcase in enumerate(replay_request.replayCaseList): write_name = f"{replay_request.apkName}_t{testcase.testNumber}c{testcase.ctestNumber}_{replay_request.androidVersion}" # set up the response replay_response = ReplayResponse( success=True, errorMessage="not inited", pageSource="", imgBase64="", adbLog="", testcaseSummary=ReplayTestCaseSummary( apkName=replay_request.apkName, packageName=replay_request.packageName, androidVersion=replay_request.androidVersion, testNumber=testcase.testNumber, ctestNumber=testcase.ctestNumber, writeName=write_name)) # read test case code & load logging.info( f"running case {idx} (t{testcase.testNumber}c{testcase.ctestNumber}) of apk {replay_request.apkName}" ) code = testcase.fileContent logging.info(f"[*] read code len={len(code)}, executing...") code = code.replace("def test_function(", f"def test_function_{idx}(") code += """\n with open(f"replay/replay_screenshots/{write_name}.png", "wb") as f: f.write(base64.b64decode(driver.get_screenshot_as_base64()))""" # replace the replay output path! code = code.replace('open(f"replay/', f'open(f"{replay_output_full_path}') exec(code) logging.info(f"[*] test case load done, start replay...") logging.info( f"[DBG] remote_addr={remote_addr}, write_name={write_name}") # try to run the test case try: locals()[f"test_function_{str(idx)}"]( remote_addr=remote_addr, desired_caps=replay_request.desiredCaps, write_name=write_name) except Exception as e: logging.exception(f"run case exception @ {write_name}") replay_response.success = False replay_response.errorMessage = str(e) # read the results if replay_response.success: output_root = Path(replay_output_full_path) with open(output_root / "replay_screenshots" / f"{write_name}.png", "rb") as screenshot_file: replay_response.imgBase64 = base64.b64encode( screenshot_file.read()) with open( output_root / "replay_page_sources" / f"page_source_{write_name}.xml", "r") as xml_file: replay_response.pageSource = xml_file.read() with open(output_root / "replay_adb" / f"adblog_{write_name}.json", "r") as json_file: replay_response.adbLog = json_file.read() # send the result logging.info(f"[*] test case {idx} done, sending reply") routing_key = f"{replay_request.apkName}.{replay_request.androidVersion}.{testcase.testNumber}.{testcase.ctestNumber}" channel.basic_publish(exchange="replay_response", routing_key=routing_key, body=jsonpickle.encode(replay_response)) # step3. uninstall apk uninstall_success = False try: uninstall_success = adb_connect_uninstall_pkg( adb_exe_path=adb_exe_path, adb_connection_str=adb_connection_str, pkg_name=replay_request.packageName) except: logging.exception( f"apk uninstall error: {replay_request.apkName} @ {current_status_line}" ) # if uninstall failed, not big problem, but still need to be noted if not uninstall_success: logging.warning( f"apk uninstall failed: {replay_request.apkName} @ {current_status_line}" ) return True
print(case.stem, e) exc_type, exc_value, exc_traceback_obj = sys.exc_info() traceback.print_tb(exc_traceback_obj) log_to_file(f"replay/replay_logs/replay.log_{write_name}", "ERR:" + str(e), now, write_name) if "socket hang up" in str(e): print("hangup retry1") container.remove(force=True) container = docker_init(client=client, image_name=image_name, gui_port=gui_port, appium_port=appium_port, emu_port=emu_port, adb_port=adb_port) adb_connect_install(adb_exe_path=adb_exe_path, adb_connection_str=adb_connection_str, apk_path=apk_path) try: test_function(remote_addr=remote_addr, desired_caps=desired_caps, write_name=case.stem) log_to_file(f"replay/replay_logs/replay.log_{write_name}", "SUCCESS! with retry", now, write_name) except Exception as e: print(case.stem, e) log_to_file(f"replay/replay_logs/replay.log_{write_name}", "ERR(retry):" + str(e), now, write_name) # finally: try: print("load done, press to clean") # NOTE: 结束之后需要随便输入点东西来触发 container.remove
def run_cases_on_image(apk_path, replay_file_list, image_name): """ This runs multiple test case at the same time on a specific image container. """ # find free ports gui_port = find_free_port() appium_port = find_free_port() emu_port = find_free_port() adb_port = find_free_port() print( f"[*] port config: gui={gui_port}, appium={appium_port}, emu={emu_port} ,adb={adb_port}" ) # adb related config adb_connection_str = "localhost:" + str( adb_port) # this is generated at runtime remote_addr = "http://localhost:" + str(appium_port) + "/wd/hub" # initialize client = docker.from_env() apkname = Path(apk_path).name replace_img_name = image_name.replace("/", "_") write_name = f"{apkname}_{replace_img_name}" now = datetime.datetime.now() # prepare container # WARNING: THESE ARE NOT LIGHTWEIGHT CONTAINERS! THEY ARE QUITE HEAVY! container = docker_init(client=client, image_name=image_name, gui_port=gui_port, appium_port=appium_port, emu_port=emu_port, adb_port=adb_port, name=write_name) try: # try to install # this might lead to timeout, but should not hang up here... adb_connect_install(adb_exe_path=adb_exe_path, adb_connection_str=adb_connection_str, apk_path=apk_path) except Exception as e: # if adb install fail, try to remove the docker??? print("adb install failed...") try: log_to_file(f"replay/replay_logs/replay.log_{write_name}", "adbinstall:" + str(e), now, write_name) container.remove(force=True) container = None logging.exception("adb exception happened") print("{*} container removed.") except FunctionTimedOut: logging.exception("adb install timed out") except Exception as e: logging.exception("adb exception handle failed") print(case.stem, e) finally: return write_name # by now, container should be ready, and the apk has been installed print("{*} remote addr: ", remote_addr) # run all the case one by one for idx, case in enumerate(replay_file_list): case = Path(case) print("{*} running case: " + str(case) + " with image: " + image_name + " apk:" + str(apk_path)) with open(case, "r", encoding="utf8") as f: code = "\n".join(f.readlines())