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())
Beispiel #2
0
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
Beispiel #5
0
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())