Ejemplo n.º 1
0
    def register_game_callback(self, name, func):
        if not name or not func:
            raise WeTestInvaildArg("Name or Function can't be None")
        if not self._callback_socket or not self._callback_functions:
            self._callback_socket = SocketClient(self.address, self.port)
            event = threading.Event()
            self._callback_thread = RPCReceiveThread(self._callback_socket, self._callback_functions, event)
            self._callback_thread.start()

        self._callback_functions[name] = func
        self._callback_socket.send_package(Commands.PRC_SET_METHOD, name)
        return self._callback_thread.get_result()
Ejemplo n.º 2
0
class UnityEngine(GameEngine):
    def __init__(self, address, port, uiauto_interface):
        super(UnityEngine, self).__init__(address, port, uiauto_interface)
        self._callback_socket = None
        self._callback_functions = {}
        self._callback_thread = None

    def _parse_path(self, path):
        if path is None:
            return None

        nodeInfos = []
        nodes = path.split("/")

        if len(nodes) == 0:
            return None
        elif nodes[0] == '':
            pathNode = {
                "name": "/",
                "index": -1,
                "txt": "",
                "img": "",
                "regex": ""
            }
            nodeInfos.append(pathNode)
            nodes = nodes[1:]
        name_re = re.compile(r"(?P<name>[^[{]+)")
        txt_re = re.compile(r"(txt\s*=\s*(?P<txt>[^,\}]*))")
        img_re = re.compile(r"(img\s*=\s*(?P<img>[^,\}]*))")
        index_re = re.compile(r"\[(?P<index>\d+)\]")
        regex_re = re.compile(r"\{\{(?P<regex>.+)\}\}")
        for node in nodes:
            result = regex_re.search(node)
            if result:
                regex = result.groupdict().get("regex", "")
                name = ""
            else:
                regex = ""
                result = name_re.search(node)
                if result:
                    name = result.groupdict().get("name", "")
                else:
                    name = ""

            result = txt_re.search(node)
            if result:
                txt = result.groupdict().get("txt", "")
            else:
                txt = ""

            result = img_re.search(node)
            if result:
                img = result.groupdict().get("img", "")
            else:
                img = ""
            result = index_re.search(node)
            if result:
                index = result.groupdict().get("index", "-1")
                index = int(index)
            else:
                index = -1
            pathNode = {
                "name": name,
                "index": index,
                "txt": txt,
                "img": img,
                "regex": regex
            }
            nodeInfos.append(pathNode)
        return nodeInfos

    def find_elements_path(self, path):
        """
        表达式匹配获取符合的所有节点
        第一个节点"/"代表从根节点,如果不是代表可以从任意位置开始


        index:代表节点的位置。
             parent
           /      \
         index[0]  index[1]

         name:代表节点名称,"*"代表任意名称
         text:代表节点的文字内容(当前GameObject的挂载的Compenent)
         img:图片名称,sprite或者texture名称


         /Canvas/Panel/Button
         Canvas[0]{txt="hello"}/Button
         /Canvas/Panel/*[1]/Button
        :param path:
            需要查找的路径,/Canvas/Button[0]{txt=Button,img=img}
        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>button=engine.find_elements_path('/Canvas/Panel/Button')
            >>>button2=engine.find_elements_path('/Canvas/Panel/*[1]/Button')
            >>>button3=engine.find_elements_path('Canvas/Button{txt=hello}')
        :return:包含instance的Elements对象列表
            example:
            [{"objectName":"/Canvas/Panel/Button",
            "Instance":4257741},{"objectName":"/Canvas/Panel/Button",
            "Instance":4257742}]
        """
        nodeInfos = self._parse_path(path)
        if nodeInfos is None:
            raise WeTestInvaildArg("Error path")

        ret = self.socket.send_command(Commands.FIND_ELEMENT_PATH, nodeInfos)
        elements = []
        for e in ret:
            element = Element(e["name"], e["instance"])
            elements.append(element)

        return elements

    def find_elements_by_component(self, name):
        """
            通过GameObject.FindObjectsOfType(Type.GetType(name))查找对应的GameObject.

            Type.GetType获取不到的话,默认会尝试name,Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            及name, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
        :param name:
            查找的类型的,AssemblyQualifiedName,如WetestManager,U3DAutomation, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
        :return:
            当前界面中所有挂载改界面的
        """
        ret = self.socket.send_command(Commands.FIND_ELEMENTS_COMPONENT,
                                       [name])
        elements = []
        for e in ret:
            element = Element(e["name"], e["instance"])
            elements.append(element)
        return elements

    def get_element_text(self, element):
        """
            获取GameObject文字信息
            NGUI控件则获取UILable、UIInput、GUIText组件上的文字信息
            UGUI控件则获取Text、GUIText组件上的文字信息
        :param element: 查找到的GameObject

        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>element=engine.find_element('/Canvas/Panel/Button')
            >>>text=engine.get_element_text(element)
        :return:文字内容
        :raises WeTestInvaildArg,WeTestRuntimeError
        """
        if element is None:
            raise WeTestInvaildArg("Invalid Instance")
        ret = self.socket.send_command(Commands.GET_ELEMENT_TEXT,
                                       element.instance)
        return ret

    def get_element_image(self, element):
        """
            获取GameObject图片名称
            NGUI控件则获取UITexture、UISprite、SpriteRenderer组件上的图片名称
            UGUI控件则获取Image、RawImage、SpriteRenderer组件上的图片名称
        :param element: 查找到的GameObject

        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>element=engine.find_element('/Canvas/Panel/Button')
            >>>image=engine.get_element_image(element)
        :return:图片名称
        :rtype: str
        :raises WeTestInvaildArg,WeTestRuntimeError
        """
        if element is None:
            raise WeTestInvaildArg("Invalid Instance")
        ret = self.socket.send_command(Commands.GET_ELEMENT_IMAGE,
                                       element.instance)
        return ret

    def get_registered_handlers(self):
        """
            获取用户当前注册的自定义的函数集合
        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>function_names=engine.get_registered_handlers()
        :return: []注册的自定义函数名称序列
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        ret = self.socket.send_command(Commands.GET_REGISTERED_HANDLERS)
        return ret

    def call_registered_handler(self, name, args):
        """
            调用指定的注册的函数,并返回返回值
        :param name:已经注册的函数名称
        :param args:传入函数中的参数,一个不超过1024个字符的参数
        :param timeout:超时时间(超时后直接返回,SDK中对应的函数可能还是会执行完,不会中断)
        :return:
            自定义注册函数的返回值
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        ret = self.socket.send_command(Commands.CALL_REGISTER_HANDLER, {
            "name": name,
            "args": args
        })
        return ret

    def get_element_bound(self, element):
        """
        获取GameObject在屏幕上的位置和长宽高
        :param element: 查找到的GameObject

        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>element=engine.find_element('/Canvas/Panel/Button')
            >>>bound=engine.get_element_bound(element)
        :return:屏幕中的位置(x,y),左上角为原点,及长宽
            examples:
            {"x":400.0,
            "y":500.0,
            "width":200.0
            "height":300.0}
        :rtype: ElementBound
        :raises WeTestInvaildArg,WeTestRuntimeError
        """
        if element is None:
            raise WeTestInvaildArg("Invaild Instance,element is None")

        ret = self._get_elements_bound([element])
        if ret:
            result = ret[0]
            if not result["existed"]:
                return None
            else:
                return ElementBound(result["x"], result["y"], result["width"],
                                    result["height"], result["visible"])
        return None

    def _get_elements_bound(self, elements):
        send_params = [e.instance for e in elements]
        ret = self.socket.send_command(Commands.GET_ELEMENTS_BOUND,
                                       send_params)
        return ret

    def get_touchable_elements(self, params=None):
        """
            获取当前界面的可点击节点的列表(过期)
        :return:
            [(element1,point1),(element2,point2)]
            [({"object_name":"/Canvas/Panel/Button","Instance":4257741},{"x":250,"y":300}),.....]
        :raise WeTestRuntimeError
        """
        ret = self.socket.send_command(Commands.GET_UI_INTERACT_STATUS, params)
        elements = []
        for i in range(0, len(ret["elements"])):
            node = ret["elements"][i]
            element = Element(node["name"], node["instanceid"])
            elements.append((element, {
                "x":
                node["bound"]["x"] + node["bound"]["fWidth"] / 2,
                "y":
                node["bound"]["y"] + node["bound"]["fHeight"] / 2
            }))
        return elements

    def get_touchable_elements_bound(self, params=None):
        """
            获取当前界面的可点击节点的列表
        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>buttons=engine.get_touchable_elements_bound()
        :return:
            [(element1,point1),(element2,point2)]
            [({"object_name":"/Canvas/Panel/Button","Instance":4257741},{"x":250,"y":300}),.....]
        :raise WeTestRuntimeError
        """
        ret = self.socket.send_command(Commands.GET_UI_INTERACT_STATUS, params)
        elements = []
        for i in range(0, len(ret["elements"])):
            node = ret["elements"][i]
            element = Element(node["name"], node["instanceid"])
            bound = ElementBound(node["bound"]["x"], node["bound"]["y"],
                                 node["bound"]["fWidth"],
                                 node["bound"]["fHeight"])
            elements.append((element, bound))
        return ret["scenename"], elements

    def _inject_touch_actions(self, actions, timeout=20):
        """
            发送touch序列号,touch事件结束之后,才会返回。同步函数
        :param actions:
        :return:
        """
        ret = self.socket.send_command(Commands.HANDLE_TOUCH_EVENTS, actions,
                                       timeout)
        return ret

    # def click_position(self, x, y):
    #     """
    #         在屏幕的某个位置进行点击
    #     :param x: 150 在屏幕x=150的位置进行点击
    #     :param y: 200 在屏幕y=200的位置进行点击
    #
    #     :Usage:
    #         >>>import wpyscripts.manager as manager
    #         >>>engine=manager.get_engine()
    #         >>>engine.click_position(100,300)
    #
    #     :raise WeTestRuntimeError
    #     """
    #     x = int(x)
    #     y = int(y)
    #     actions = [{"x": x, "y": y, "sleeptime": 50, "type": TouchEvent.ACTION_DOWN},
    #                {"x": x, "y": y, "sleeptime": 0, "type": TouchEvent.ACTION_UP}]
    #     logger.debug("click {0}".format(actions))
    #     self._inject_touch_actions(actions)
    #     return True
    #
    # def swipe_position(self, start_x, start_y, end_x, end_y, steps, duration=1000):
    #     """
    #
    #     :param start_x,start_y,end_x,end_y: 一个包含起点位置和终点位置
    #         example:从start_x=200,start_y=300的位置滑动到end_x=500,end_y=600
    #     :param steps:步数,通过控制步数能够控制滑动时长和滑动平滑度,move的数量
    #     :param duration:持续时间,以毫秒来进行计算
    #
    #     :Usage:
    #         >>>import wpyscripts.manager as manager
    #         >>>engine=manager.get_engine()
    #         >>>engine.swipe_position(200,300,500,600,duration=1000)
    #     :raises: WeTestRuntimeError
    #     """
    #
    #     if steps <= 0:
    #         reason = "steps = {0} is invaild, steps must more than 0".format(steps)
    #         raise WeTestInvaildArg(reason)
    #     start_x = int(start_x)
    #     start_y = int(start_y)
    #     end_x = int(end_x)
    #     end_y = int(end_y)
    #     actions = [{"x": start_x, "y": start_y, "sleeptime": 0, "type": TouchEvent.ACTION_DOWN}]
    #
    #     x_distance = (end_x - start_x) * 1.0 / steps
    #     y_distance = (end_y - start_y) * 1.0 / steps
    #     move_x, move_y = start_x, start_y
    #
    #     step_sleep = 0 if duration <= 0 else int(duration / steps)
    #
    #     for i in range(steps):
    #         move_x += x_distance
    #         move_y += y_distance
    #         actions.append(
    #             {"x": int(move_x), "y": int(move_y), "sleeptime": step_sleep, "type": TouchEvent.ACTION_MOVE})
    #
    #     actions.append({"x": int(end_x), "y": int(end_y), "sleeptime": 0, "type": TouchEvent.ACTION_UP})
    #
    #     self._inject_touch_actions(actions)
    #     return True
    #
    # def swipe(self, start_element, end_element, steps=20, duration=1000):
    #     """
    #
    #     :param start_element:
    #     :param end_element:
    #     :param steps:步数,通过控制步数能够控制滑动时长和滑动平滑度,每一步走的耗时在step_sleep为5ms左右。100步时长>500毫。无法做到精确控制,调试确认
    #     :param step_sleep:每个步骤的间隔时长,单位毫秒
    #
    #     :Usage:
    #         >>>import wpyscripts.manager as manager
    #         >>>engine=manager.get_engine()
    #         >>>start_element=engine.find_element('/Canvas/Panel/Button')
    #         >>>end_element=engine.find_element('/Canvas/Panel/Button2')
    #         >>>engine.swipe(start_element,end_element,5000)
    #     :return: WeTestRuntimeError,WeTestInvaildArg
    #     """
    #     start_x = 0
    #     start_y = 0
    #     end_x = 0
    #     end_y = 0
    #     if start_element and isinstance(start_element, Element) and end_element and isinstance(end_element, Element):
    #         ret = self._get_elements_bound([start_element,end_element])
    #         if ret and len(ret) == 2:
    #             result = ret[0]
    #             if not result["existed"] or not result["visible"]:
    #                 reason = "StartElement = {0} not existed or unvisible".format(start_element)
    #                 raise WeTestInvaildArg(reason)
    #             else:
    #                 start_x = result["x"] + result["width"] / 2
    #                 start_y = result["y"] + result["height"] / 2
    #
    #             result = ret[1]
    #             if not result["existed"] or not result["visible"]:
    #                 reason = "EndElement = {0} not existed or unvisible".format(start_element)
    #                 raise WeTestInvaildArg(reason)
    #             else:
    #                 end_x = result["x"] + result["width"] / 2
    #                 end_y = result["y"] + result["height"] / 2
    #     elif start_element and isinstance(start_element, ElementBound) and end_element and isinstance(end_element,
    #                                                                                                   ElementBound):
    #         start_x = start_element.x + start_element.width / 2
    #         start_y = start_element.y + start_element.height / 2
    #         end_x = end_element.x + end_element.width / 2
    #         end_y = end_element.y + end_element.height / 2
    #     else:
    #         reason = "Input start_element = {0},end_element = {1},vaild argument is Element or ElementBound".format(
    #             start_element, end_element)
    #         raise WeTestInvaildArg(reason)
    #
    #     return self.swipe_position(start_x, start_y, end_x, end_y, steps, duration=duration)
    #
    # def swipe_and_press(self, start_x, start_y, end_x, end_y, steps, duration, step_sleep=5):
    #     """
    #     滑动并在结束的地方一直按压
    #     :param start_x: 起始位置x,绝对坐标
    #     :param start_y: 起始位置y,绝对坐标
    #     :param end_x: 结束位置x,绝对坐标
    #     :param end_y:结束位置y,绝对坐标
    #     :param steps: 滑动中间的步骤数,每一步的间隔为5ms,可以用于控制滑动速度和平滑度
    #     :param step_sleep:每个步骤的间隔时长,单位毫秒
    #     :param duration: 结束位置按压时间,单位是毫秒ms
    #
    #     :Usage:
    #         >>>import wpyscripts.manager as manager
    #         >>>engine=manager.get_engine()
    #         >>>engine.swipe_and_press(start_x,start_y,end_x,end_y,50,10000,step_sleep=200)
    #     :raise WeTestInvaildArg,WeTestRuntimeError
    #     """
    #     if steps <= 0:
    #         reason = "steps = {0} is invaild, steps must more than 0".format(steps)
    #         raise WeTestInvaildArg(reason)
    #     start_x = int(start_x)
    #     start_y = int(start_y)
    #     end_x = int(end_x)
    #     end_y = int(end_y)
    #     actions = [{"x": start_x, "y": start_y, "sleeptime": 0, "type": TouchEvent.ACTION_DOWN}]
    #
    #     x_distance = (end_x - start_x) * 1.0 / steps
    #     y_distance = (end_y - start_y) * 1.0 / steps
    #     move_x, move_y = start_x, start_y
    #
    #     for i in range(steps - 1):
    #         move_x += x_distance
    #         move_y += y_distance
    #         actions.append(
    #             {"x": int(move_x), "y": int(move_y), "sleeptime": step_sleep, "type": TouchEvent.ACTION_MOVE})
    #
    #     move_x += x_distance
    #     move_y += y_distance
    #     actions.append(
    #         {"x": int(move_x), "y": int(move_y), "sleeptime": duration, "type": TouchEvent.ACTION_MOVE})
    #
    #     actions.append({"x": int(end_x), "y": int(end_y), "sleeptime": 0, "type": TouchEvent.ACTION_UP})
    #
    #     self._inject_touch_actions(actions, timeout=60)
    #     return True

    def input(self, locator, text):
        """

        :param locator: is an element locator
        :param text: input的内容

        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>element=engine.find_element("/Canvas/Panel/input")
            >>>old_txt=engine.input(element,"new text")
        :return: "hello world"input控件上原有的内容
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        if locator and isinstance(locator, Element):
            result = self.socket.send_command(Commands.SET_INPUT_TEXT, {
                "instance": locator.instance,
                "content": text
            })
            return result
        else:
            reason = "Input locator = {0},text = {1},vaild argument is Element or ElementBound".format(
                locator, text)
            raise WeTestInvaildArg(reason)

    def get_element_world_bound(self, elements):
        """
            查找节点对应的世界坐标。世界坐标包含,节点的中心位置的x,y,z坐标,及物体离中心点在在x,y,z轴上的大小。
            具体详见:http://docs.unity3d.com/ScriptReference/Bounds.html
        :param elements: 一个Element或者Element[],节点应该至少包含Renderer、MeshFilter或Colider组件
        :return: WorldBound[]

        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>hero1=engine.find_element("hero1")
            >>>hero2=engine.find_element("hero2")
            >>>bounds=engine.get_element_world_bound([hero1,hero2])
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        if elements is None:
            raise WeTestInvaildArg("Invaild Instance")
        if isinstance(elements, Element):
            elements = [elements]

        if len(elements) == 0:
            raise WeTestInvaildArg("Invaild Instance,search node is error")

        req = [e.instance for e in elements]
        ret = self.socket.send_command(Commands.GET_ELEMENT_WORLD_BOUND, req)

        world_bounds = []
        for res in ret:
            world_bound = WorldBound(res["id"], res["existed"])
            if world_bound.existed:
                world_bound.center_x = res["centerX"]
                world_bound.center_y = res["centerY"]
                world_bound.center_z = res["centerZ"]
                world_bound.extents_x = res["extentsX"]
                world_bound.extents_y = res["extentsY"]
                world_bound.extents_z = res["extentsZ"]
                world_bounds.append(world_bound)

        return world_bounds

    def get_component_field(self, element, component, attribute):
        """
            通过反射的方式获取到GameObject上面组件的属性值。反射查看是否存在Property或者Field名称为attribute
        :param element:已经找到的GameObject对象
        :param component:组件名称
        :param attribute:字段或者属性名称
        :return: 字段或者属性名称toString()之后的string值

        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>elements = self.engine.find_elements_path("Sample/Text")
            >>>res = self.engine.get_component_field(elements[0], "Text", "text")
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        if element is None or component is None or attribute is None:
            raise WeTestInvaildArg("Invaild Instance")

        ret = self.socket.send_command(
            Commands.GET_OBJECT_FIELD, {
                "instance": element.instance,
                "comopentName": component,
                "attributeName": attribute
            })

        return ret

    def set_camera(self, camera):
        ret = self.socket.send_command(Commands.SET_CAMERA_NAME, [camera])
        return ret

    def get_component_methods(self, element, component):
        """
        通过反射获取组件的方法
        :param element:已经找到的GameObject对象
        :param component:组件名称
        :return:方法的描述,包括方法名称,返回值类型,参数类型
        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>element = engine.find_element("Sample")
            >>>methods = engine.get_component_methods(element, "ReflectionTest")
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        if element is None or component is None:
            raise WeTestInvaildArg("Invaild Instance")

        ret = self.socket.send_command(Commands.GET_COMPONENT_METHODS, {
            "instance": element.instance,
            "comopentName": component
        })
        return ret

    def call_component_method(self, element, component, method, params):
        """
        通过反射调用组件上的方法
        :param element:已经找到的GameObject对象
        :param component:组件名称
        :param method:方法名称
        :param params:方法参数数组
        :return:方法的返回
        :Usage:
            >>>import wpyscripts.manager as manager
            >>>engine=manager.get_engine()
            >>>element = engine.find_element("Sample")
            >>>params = [5, "Hello World"]
            >>>result = engine.call_component_method(element, "ReflectionTest", "TestReflection", params)
        :raise WeTestInvaildArg,WeTestRuntimeError
        """
        if element is None or component is None or method is None:
            raise WeTestInvaildArg("Invaild Instance")

        ret = self.socket.send_command(
            Commands.CALL_COMPONENT_MOTHOD, {
                "instance": element.instance,
                "comopentName": component,
                "methodName": method,
                "parameters": params
            })
        return ret

    def game_script_init(self, path):
        """
            将gametestlib.dll push到手机上的/data/local/tmp目录下
            然后调用gametestlib.dll中的GameTest.Test.init()方法
        :param path: gametestlib.dll, inject c# script
        :return:
        """
        logger.debug("push c# test script : {0}".format(path))
        result = AdbTool().cmd_wait("push", path,
                                    "/data/local/tmp/gametestlib.dll")
        logger.debug("push result : {0}".format(result))

        ret = self.socket.send_command(Commands.LOAD_TEST_LIB)
        return ret

    def register_game_callback(self, name, func):
        if not name or not func:
            raise WeTestInvaildArg("Name or Function can't be None")
        if not self._callback_socket or not self._callback_functions:
            self._callback_socket = SocketClient(self.address, self.port)
            event = threading.Event()
            self._callback_thread = RPCReceiveThread(self._callback_socket,
                                                     self._callback_functions,
                                                     event)
            self._callback_thread.start()

        self._callback_functions[name] = func
        self._callback_socket.send_package(Commands.PRC_SET_METHOD, name)
        return self._callback_thread.get_result()