Welcome to Poco (ポコ) documentation!

A cross-engine UI automation framework. Unity3D/cocos2dx-*/Android native APP/iOS native APP/(Other engines SDK)/…

Fork Star 99+

Getting Started

_images/logo-no-padding.png

Poco ポコ

Documentation Status chat on slack

A cross-engine UI automation framework. Unity3D/cocos2dx-*/Android native APP/iOS native APP/(Other engines SDK)/…

Example

First you should connect your Android phone, for example, via usb cable and enable the ADB DEBUG MODE.

_images/overview1.gif
# coding=utf-8

import time
from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

poco('btn_start').click()
time.sleep(1.5)

shell = poco('shell').focus('center')
for star in poco('star'):
    star.drag_to(shell)
time.sleep(1)

assert poco('scoreVal').get_text() == "100", "score correct."
poco('btn_back', type='Button').click()

More examples here.

Tools for writing test scripts

To retrieve the UI hierarchy of the game, please use our AirtestIDE (an IDE for writing test scripts) or standalone PocoHierarchyViewer (to view the hierarchy and attributes only but lightweight) !

_images/hunter-inspector.png

Installation

In order to use Poco, you must install Poco python library on your host and also install the poco-sdk in your game/app.

Poco can be installed straightforward with pip command. It is called pocoui.

pip install pocoui

SDK Integration

For poco-sdk integration please refer to Integration Guide

Features

  • supports mainstream game engines, including: Unity3D, cocos2dx-js, cocos2dx-lua, Android/iOS native apps
  • retrieves UI Elements Hierarchy in game’s runtime
  • is super fast and impact-free to the game
  • allows straightforward SDK integration to the game (within in 5 minutes)
  • provides powerful APIs that are engine independent
  • supports multi-touch e.g. fling/pinch/… (and more is coming soon)
  • support gps, accelerometer and gyro sensors, rotation (landscape/portrait) and other sensors as input (coming soon)
  • is extensible to other private engines by implementing poco-sdk .
  • is compatible with Python 2.7 and Python 3.3-3.6.

Documentation

Online docs.

Use poco on platforms/engines

This section guide you how to start to use poco to write your test cases on different platforms/engines.

Poco drivers

How to use Poco

Poco supports different types of engines by different drivers. For different engines please initialize poco instance by corresponding driver. Remember to connect an Android device to your PC/mac with a running game or launch and keep the game/app active on PC/mac.

Following example shows how to initialize poco instance for

  • Unity3D.
from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()
# for unity editor on windows
# poco = UnityPoco(('localhost', 5001), unity_editor=True)

ui = poco('...')
ui.click()
  • Android native APP
from poco.drivers.android.uiautomation import AndroidUiautomationPoco

poco = AndroidUiautomationPoco()
poco.device.wake()
poco(text='Clock').click()

Working with Poco Objects

Basic Selector

UI element objects can be selected by invoking poco(...) function instance. The function traverses through the render tree structure and selects all the corresponding UI elements matching the query expression.

The function takes one mandatory argument node name, the optional arguments can be substituted too and they refer to specific node properties. For more information, refer to API Reference selecting UI.

# select by node name
poco('bg_mission')

# select by name and other properties
poco('bg_mission', type='Button')
poco(textMatches='^据点.*$', type='Button', enable=True)
_images/hunter-poco-select-simple.png

Relative Selector

When there is any ambiguity in the selected objects by node names/node types or object unable to select, the relative selector tries to select the element object by hierarchy in following manner

# select by direct child/offspring
poco('main_node').child('list_item').offspring('item')
_images/hunter-poco-select-relative.png

Sequence Selector

Tree indexing and traversing is performed by default from up to down or from left to right. In case that the ‘not-yet-traversed’ nodes are removed from the screen, the exception is raised. The exception is not raised in case when the ‘already-traversed’ nodes are removed and in this case the traversing continues in previous order despite the fact that the nodes in views were rearranged during the travers process.

items = poco('main_node').child('list_item').offspring('item')
print(items[0].child('material_name').get_text())
print(items[1].child('material_name').get_text())
_images/hunter-poco-select-sequence.png

Iterate over a collection of objects

Following code snippet shows how to iterate over the collection of UI objects

# traverse through every item
items = poco('main_node').child('list_item').offspring('item')
for item in items:
    item.child('icn_item')
_images/hunter-poco-iteration.png

Get object properties

Following examples shows how to obtain various properties of an object

mission_btn = poco('bg_mission')
print(mission_btn.attr('type'))  # 'Button'
print(mission_btn.get_text())  # '据点支援'
print(mission_btn.attr('text'))  # '据点支援' equivalent to .get_text()
print(mission_btn.exists())  # True/False, exists in the screen or not

Global Operation

Poco framework also allows to perform the operations without any UI elements selected. These operations are called global operations.

click
poco.click([0.5, 0.5])  # click the center of screen
poco.long_click([0.5, 0.5], duration=3)
swipe
# swipe from A to B
point_a = [0.1, 0.1]
center = [0.5, 0.5]
poco.swipe(point_a, center)

# swipe from A by given direction
direction = [0.1, 0]
poco.swipe(point_a, direction=direction)
snapshot

Take a screenshot of the current screen in base64 encoded string. The image format depends on the sdk implementation. Take a look at ScreenInterface.getScreen to dive into sdk implementation details.

Note: snapshot is not supported in some engine implementation of poco.

from base64 import b64decode

b64img, fmt = poco.snapshot(width=720)
open('screen.{}'.format(fmt), 'wb').write(b64decode(b64img))

Exceptions

This sections describes the Poco framework errors and exceptions.

PocoTargetTimeout

from poco.exceptions import PocoTargetTimeout

try:
    poco('guide_panel', type='ImageView').wait_for_appearance()
except PocoTargetTimeout:
    # bugs here as the panel not shown
    raise

PocoNoSuchNodeException

from poco.exceptions import PocoNoSuchNodeException

img = poco('guide_panel', type='ImageView')
try:
    if not img.exists():
        img.click()
except PocoNoSuchNodeException:
    # If attempt to operate inexistent nodes, an exception will be thrown
    pass

Unit Test

Poco is an automation test framework. For unit testing, please refer to PocoUnit section. PocoUnit provides a full set of assertion methods and furthermore, it is also compatible with the unittest in Python standard library.

Tutorial of PocoUnit.

Some Concepts

This section describes some basic concepts of Poco. Basic terminology used in following section

  • Target device: test devices where the apps or games run on, it usually refers to mobile phone devices
  • UI proxy: proxy objects within Poco framework, they represent zero (none), one or multiple in-game UI elements
  • Node/UI element: UI element instances or nodes in app/game
  • query expression: a serializable internal data structure through which Poco interacts with target devices and selects the corresponding UI elements. It is not usually needed to pay much attention to this unless it is required to customize the Selector class.

Following images show the UI hierarchy represented in Poco

_images/hunter-inspector.png _images/hunter-inspector-text-attribute.png _images/hunter-inspector-hierarchy-relations.png

Definitions of coordinate system and metric space

Normalized Coordinate System

In normalized coordinate system, the origin (0, 0) lies on top left corner of the device display. The height and the width of the screen are chosen as 1 unit of length, refer to image below for more detailed information. In normalized coordinate system, the same UI elements on the devices with different resolution have always the same position and size. This is especially very handy when writing cross-device test cases.

The space of normalized coordinate system is uniformly distributed, i.e. the coordinates of the screen center are (0.5, 0.5) and the computing method of other scalars and vectors are all same in Euclidean space.

_images/hunter-poco-coordinate-system.png
Local Coordinate System (local positioning)

The aim of introducing local coordinate system is to express the coordinates with reference to a certain UI elements. The origin (0,0) of local coordinate system lies on the top left corner of UI bounding box, x-axis goes horizontally rightward direction and y-axis goes vertically downwards. The height and the width of UI element are chosen as 1 unit of length. Coordinates are expressed as signed distances from the origin. Other definitions are same as for normalized coordinate system.

Local coordinate system is more flexible in order to locate the position within or outside of UI element, e.g the coordinates at (0.5, 0.5) corresponds to the center of the UI element while coordinates larger than 1 or less than 0 correspond to the position out of the UI element.

Join to discuss!

chat on slack

Contributions

Any pull requests are welcomed! We will have the code checked carefully. Please make sure the codes are compatible with python 2/3 and have the same coding style.

Install Poco and PocoSDK

In order to use Poco, you must install Poco python library on your host and also install the poco-sdk in your game/app.

Poco can be installed straightforward with pip command

pip install pocoui

For poco-sdk integration please refer to Integration Guide

Using Poco as Python package

Simple demo

The following example shows a simple script on demo game using Unity3D. Check More examples section for more detailed info.

First you should connect your Android phone, for example, via usb cable and enable the ADB DEBUG MODE.

_images/overview.gif
# coding=utf-8

import time
from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

poco('btn_start').click()
time.sleep(1.5)

shell = poco('shell').focus('center')
for star in poco('star'):
    star.drag_to(shell)
time.sleep(1)

assert poco('scoreVal').get_text() == "100", "score correct."
poco('btn_back', type='Button').click()

Tools for writing test scripts

To retrieve the UI hierarchy of the game, please use our AirtestIDE (an IDE for writing test scripts) or standalone PocoHierarchyViewer (to viewer the hierarchy and attributes only but lightweight) !

_images/hunter-inspector.png

Use poco on platforms/engines

This section guide you how to start to use poco to write your test cases on different platforms/engines.

API reference

Poco API

You can find all functions/methods for writing test scripts under the following links.

poco.pocofw module

class Poco(agent, **options)[source]

Bases: poco.acceleration.PocoAccelerationMixin

Poco standard initializer.

Parameters:
  • agent (PocoAgent) – an agent object for Poco to communicate with the target device. See PocoAgent definition for more details.
  • options
    • action_interval: time interval to wait for the action (such as touch or swipe) completion performed on device and for the UI to become still (stable). Default value is 0.8s.
    • poll_interval: the minimum time needed between each poll events (such as waiting for UI element to appear on the screen). Polling is done periodically.
    • pre_action_wait_for_appearance: time interval to wait before the action (such as click or swipe) is performed. If the target UI element does not appear on the screen after this time interval, the PocoNoSuchNodeException is raised
    • touch_down_duration: Touch down step duration of the click operation last for. If this argument is provided, this value will set to self.agent.input module. Note that not all implementation of poco support this parameter. If not support, you may see a warning.
    • reevaluate_volatile_attributes: Re-select target UI proxy when retrieving volatile attributes. Poco drivers that using hrpc connections should default to be False as hrpc always reevaluate the attributes remotely. This option is useful for StdPoco driver and should be handled by StdPoco.
__call__(name=None, **kw)[source]

Call Poco instance to select the UI element by query expression. Query expression can contain specific name and/or other attributes. Invisible UI elements will be skipped even if “visible=False” argument is set.

Selection process is not executed instantly, the query expression is stored in the UI proxy and the selection is executed only then when the UI element(s) info is required (such get the point coordinates where to click, and/or retrieve the specific attribute value).

Examples

This example shows selecting a Button named ‘close’:

poco = Poco(...)
close_btn = poco('close', type='Button')
Parameters:

name (str) – name of the UI element to be selected

Keyword Arguments:
 
  • xx – arbitrary key value pair that stands for selecting the UI matching the value of UI.xx
  • xxMatches (str) – arbitrary key value pair that stands for selecting the UI matching the regular expression pattern UI.xx

In keyword args, you can only use xx or xxMatches at the same time. Using both with the same attribute does not make sense. Besides, xx should not start with _ (underscore) as attributes start with _ are private attributes that used by sdk implementation.

# select the UI element(s) which text attribute matches the pattern '^close.*$'
poco = Poco(...)
arb_close_btn = poco(textMatches='^close.*$')
Returns:UI proxy object representing the UI element matches the given query expression.
Return type:UIObjectProxy
add_post_action_callback(cb)[source]

Register a callback function to be invoked after each action (such as touch or swipe).

The arguments to be passed are identical to the callback function in add_pre_action_callback.

Parameters:cb – the callback function
add_pre_action_callback(cb)[source]

Register a callback function to be invoked before each action (such as touch or swipe).

The callback function arguments are defined as follows:

  • action (str): name or tag of the action
  • proxy (UIObjectProxy or NoneType): related UI proxy which is involved in the action itself
  • args (tuple): all required arguments of the specific action function
Parameters:cb – the callback function
agent

Readonly property to access poco agent instance. See poco.agent.PocoAgent for more details.

Returns:poco agent instance
Return type:poco.agent.PocoAgent
apply_motion_tracks(tracks, accuracy=0.004)[source]

Similar to click but press the screen for the given time interval and then release

Parameters:
  • tracks (list) – list of poco.utils.track.MotionTrack object
  • accuracy (float) – motion accuracy for each motion steps in normalized coordinate metrics.
click(pos)[source]

Perform click (touch, tap, etc.) action on target device at given coordinates.

The coordinates (x, y) are either a 2-list or 2-tuple. The coordinates values for x and y must be in the interval between 0 ~ 1 to represent the percentage of the screen. For example, the coordinates [0.5, 0.5] represent the center of the screen and the coordinates [0, 0] represent the top left corner.

See CoordinateSystem for more details about coordinate system.

Examples

Click the point of (100, 100) of screen which resolution is (1920, 1080):

poco.click([100.0 / 1920, 100.0 / 1080])
Parameters:pos (list(float, float) / tuple(float, float)) – coordinates (x, y) in range of 0 to 1
Raises:InvalidOperationException – when clicked outside of the screen
freeze()[source]

Snapshot current hierarchy and cache it into a new poco instance. This new poco instance is a copy from current poco instance (self). The hierarchy of the new poco instance is fixed and immutable. It will be super fast when calling dump function from frozen poco. See the example below.

Examples

poco = Poco(...)
frozen_poco = poco.freeze()
hierarchy_dict = frozen_poco.agent.hierarchy.dump()  # will return the already cached hierarchy data
Returns:new poco instance copy from current poco instance (self)
Return type:Poco
get_screen_size()[source]

Get the real physical resolution of the screen of target device.

Returns:float number indicating the screen physical resolution in pixels
Return type:tuple
long_click(pos, duration=2.0)[source]

Similar to click but press the screen for the given time interval and then release

Parameters:
  • pos (2-list/2-tuple) – coordinates (x, y) in range from 0 to 1
  • duration – duration of press the screen
pinch(direction=u'in', percent=0.6, duration=2.0, dead_zone=0.1)[source]

Squeezing or expanding 2 fingers on the entire screen.

Parameters:
  • direction (str) – pinching direction, only “in” or “out”. “in” for squeezing, “out” for expanding
  • percent (float) – squeezing range from or expanding range to of the entire screen
  • duration (float) – time interval in which the action is performed
  • dead_zone (float) – pinching inner circle radius. should not be greater than percent
scroll(direction=u'vertical', percent=0.6, duration=2.0)[source]

Scroll from the lower part to the upper part of the entire screen.

Parameters:
  • direction (str) – scrolling direction. “vertical” or “horizontal”
  • percent (float) – scrolling distance percentage of the entire screen height or width according to direction
  • duration (float) – time interval in which the action is performed
sleep_for_polling_interval()[source]

Sleep for fixed number of seconds after each poll event. There is no need to call this method manually. It’s automatically invoked when required.

snapshot(width=720)[source]

Take the screenshot from the target device. The supported output format (png, jpg, etc.) depends on the agent implementation.

Parameters:
  • width (int) – an expected width of the screenshot. The real size depends on the agent implementation
  • might not be possible to obtain the expected width of the screenshot (and) –
Returns:

  • screen_shot (str/bytes): base64 encoded screenshot data
  • format (str): output format ‘png’, ‘jpg’, etc.

Return type:

2-tuple

start_gesture(pos)[source]

Start a gesture action. This method will return a PendingGestureAction object which is able to generate decomposed gesture steps. You can invoke .to and .hold any times in a chain. See the following example.

Examples

poco = Poco(...)

# move from screen center to (0.6w, 0.6h) and hold for 1 second
# then return back to center
poco.start_gesture([0.5, 0.5]).to([0.6, 0.6]).hold(1).to([0.5, 0.5]).up()
Parameters:pos – starting coordinate of normalized coordinate system
Returns:an object for building serialized gesture action.
Return type:PendingGestureAction
swipe(p1, p2=None, direction=None, duration=2.0)[source]

Perform swipe action on target device from point to point given by start point and end point, or by the direction vector. At least one of the end point or direction must be provided.

The coordinates (x, y) definition for points is the same as for click event. The components of the direction vector (x, y) are also expressed in the range of the screen from 0 to 1.

See CoordinateSystem for more details about coordinate system.

Examples

Following example shows how to perform a swipe action from (100, 100) to (100, 200) on screen with resolution 1920x1080:

poco.swipe([100.0 / 1920, 100.0 / 1080], [100.0 / 1920, 200.0 / 1080])

Or given by the specific direction instead of end point:

poco.swipe([100.0 / 1920, 100.0 / 1080], direction=[0, 100.0 / 1080])
Parameters:
  • p1 (2-list/2-tuple) – start point
  • p2 – end point
  • direction – swipe direction
  • duration (float) – time interval in which the swipe action is performed
Raises:

InvalidOperationException – when the start point of the swipe action lies outside the screen

use_render_resolution(use=True, resolution=None)[source]

Whether to use render resolution

Parameters:
  • use – True or false
  • resolution – render resolution in portrait mode, offset_x, offset_y, offset_width, offset_height, (0, 10, 1080, 1820)
wait_for_all(objects, timeout=120)[source]

Wait until all of given UI proxies show up before timeout. All UI proxies will be polled periodically. See option poll_interval in Poco’s initialization for more details.

Parameters:
  • objects (Iterable<UIObjectProxy>) – iterable object of the given UI proxies
  • timeout (float) – timeout in seconds, default is 120s
Raises:

PocoTargetTimeout – when not all of UI proxies appeared before timeout

wait_for_any(objects, timeout=120)[source]

Wait until any of given UI proxies show up before timeout and return the first appeared UI proxy. All UI proxies will be polled periodically. See options poll_interval in Poco’s initialization for more details.

Parameters:
  • objects (Iterable<UIObjectProxy>) – iterable object of the given UI proxies
  • timeout (float) – timeout in seconds, default is 120s
Returns:

the first appeared UI proxy

Return type:

UIObjectProxy

Raises:

PocoTargetTimeout – when none of UI proxies appeared before timeout

wait_stable()[source]

Sleep for fixed number of seconds in order to wait for the UI to become still (stable). There is no need to call this method manually. It’s automatically invoked when required.

poco.proxy module

class UIObjectProxy(poco, name=None, **attrs)[source]

Bases: object

UI Proxy class that represents the UI element on target device.

Any action performing on this instance is handled by Poco. It is not necessary to initialize this object manually. See QueryCondition for more details about how to select the UI elements.

Parameters:
  • poco – the poco instance
  • name – query condition of “name” attribute, i.e. the UI element(s) with name name will be selected
  • attrs – other query expressions except for the name
__getitem__(item)[source]

Select the specific UI element by index. If this UI proxy represents a set of UI elements, then use this method to access the specific UI element. The new UI element will be wrapped by UIObjectProxy instance and therefore the returned value is also the UI proxy object.

The order of UI elements are determined by their position on the screen and not by the selection sequence. This rule is called “L2R U2D” (one by one from left to right, line by line from up to down), i.e. the most top left UI element is always the first one. See IterationOverUI for more details.

Warning

This method may cause some performance issues depending on implementation of PocoAgent.

Parameters:item (int) – the index.
Returns:a new UI proxy object representing the n-th of the current UI elements.
Return type:UIObjectProxy
__iter__()[source]

Similar method to .__getitem__() with the difference that this method iterates over all UI elements. The order rules of UI elements is same as for .__getitem__(). See IterationOverUI for more details.

Yields:UIObjectProxy – a generator yielding new UI proxy represents the specific UI element iterated over
Raises:PocoTargetRemovedException – when hierarchy structure has changed and it is attempted to access to the nonexistent UI element over the iteration
__len__()[source]

Return the number of selected UI elements.

Returns:returns 0 if none of the UI element matches the query expression otherwise returns the number of selected UI elements
Return type:int
attr(*args, **kwargs)[source]

Retrieve the attribute of UI element by given attribute name. Return None if attribute does not exist. If attribute type is str, it is encoded to utf-8 as str in Python2.7.

Parameters:name

attribute name, it can be one of the following or any other customized type implemented by SDK

  • visible: whether or not it is visible to user
  • text: string value of the UI element
  • type: the type name of UI element from remote runtime
  • pos: the position of the UI element
  • size: the percentage size [width, height] in range of 0~1 according to the screen
  • name: the name of UI element
  • …: other sdk implemented attributes
Returns:None if no such attribute or its value is None/null/nil/etc. Otherwise the attribute value is returned. The returned value type is json serializable. In both py2 and py3, if the attribute value in remote is a text-like object, the return value type will be str.
Raises:PocoNoSuchNodeException – when the UI element does not exists

Note

Exception NodeHasBeenRemovedException is caught automatically.

child(name=None, **attrs)[source]

Select the direct child(ren) from the UI element(s) given by the query expression, see QueryCondition for more details about the selectors.

Parameters:
  • name – query expression of attribute “name”, i.e. the UI elements with name name will be selected
  • attrs – other query expression except for the name
Returns:

a new UI proxy object representing the child(ren) of current UI element(s)

Return type:

UIObjectProxy

children()[source]

The same as .child() but it selects all children from the UI element(s).

Returns:a new UI proxy object
Return type:UIObjectProxy
click(*args, **kwargs)[source]

Perform the click action on the UI element(s) represented by the UI proxy. If this UI proxy represents a set of UI elements, the first one in the set is clicked and the anchor point of the UI element is used as the default one. It is also possible to click another point offset by providing focus argument.

See CoordinateSystem for more details.

Parameters:
  • focus (2-tuple/2-list/str) – an offset point (x, y) from the top left corner of the UI element(s), values must be in range of 0~1. This argument can be also specified by ‘anchor’ or ‘center’. ‘Center’ means to click the center of bounding box of UI element.
  • sleep_interval – number of seconds to wait after this action. Default is None which is the default sleep interval. This value can be configured by Poco initialization. See configuration at poco initialization for more details.
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

double_click(*args, **kwargs)[source]

Perform the double click action on the UI element(s) represented by the UI proxy. If this UI proxy represents a set of UI elements, the first one in the set is clicked and the anchor point of the UI element is used as the default one. It is also possible to click another point offset by providing focus argument.

See CoordinateSystem for more details.

Parameters:
  • focus (2-tuple/2-list/str) – an offset point (x, y) from the top left corner of the UI element(s), values must be in range of 0~1. This argument can be also specified by ‘anchor’ or ‘center’. ‘Center’ means to double click the center of bounding box of UI element.
  • sleep_interval – number of seconds to wait after this action. Default is None which is the default sleep interval. This value can be configured by Poco initialization. See configuration at poco initialization for more details.
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

drag_to(target, duration=2.0)[source]

Similar to swipe action, but the end point is provide by a UI proxy or by fixed coordinates.

Parameters:
  • target (UIObjectProxy) – a UI proxy or 2-list/2-tuple coordinates (x, y) in NormalizedCoordinate system
  • duration (float) – time interval in which the action is performed
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

exists(*args, **kwargs)[source]

Test whether the UI element is in the hierarchy. Similar to .attr('visible').

Returns:True if exists otherwise False
Return type:bool
focus(f)[source]

Get a new UI proxy copy with the given focus. Return a new UI proxy object as the UI proxy is immutable.

Parameters:f (2-tuple/2-list/str) – the focus point, it can be specified as 2-list/2-tuple coordinates (x, y) in NormalizedCoordinate system or as ‘center’ or ‘anchor’.
Returns:a new UI proxy object (copy)
Return type:UIObjectProxy
get_bounds(*args, **kwargs)[source]

Get the parameters of bounding box of the UI element.

Returns:4-list (top, right, bottom, left) coordinates related to the edge of screen in NormalizedCoordinate system
Return type:list <float>
get_name()[source]

Get the UI element name attribute

Returns:UI element name attribute
Return type:str
get_position(*args, **kwargs)[source]

Get the position of the UI elements.

Parameters:focus – focus point of UI proxy, see .focus() for more details
Returns:coordinates (x, y) in NormalizedCoordinate system
Return type:2-list/2-tuple
Raises:TypeError – raised when unsupported focus type is specified
get_size(*args, **kwargs)[source]

Get the UI element size in NormalizedCoordinate system.

Returns:size [width, height] in range of 0 ~ 1.
Return type:2-list
get_text()[source]

Get the text attribute of the UI element. Return None if no such attribute. Similar to .attr('text').

Returns:None if the UI element does not have the text element, otherwise the utf-8 encoded text value. In both py2 and py3, the return value type will be str.
Return type:str
invalidate()[source]

Clear the flag to indicate to re-query or re-select the UI element(s) from hierarchy.

alias is refresh()

Example

>>> a = poco(text="settings")
>>> print(a.exists())
>>> a.refresh()
>>> print(a.exists())
long_click(*args, **kwargs)[source]

Perform the long click action on the UI element(s) represented by the UI proxy. If this UI proxy represents a set of UI elements, the first one in the set is clicked and the anchor point of the UI element is used as the default one. Similar to click but press the screen for the given time interval and then release.

Parameters:duration (float) – whole action duration.
Returns:the same as poco.pocofw.Poco.long_click(), depending on poco agent implementation.
nodes

Readonly property accessing the UI element(s) in the remote runtime.

offspring(name=None, **attrs)[source]

Select the offsprings including the direct child(ren) from the UI element(s) given by the query expression, see QueryCondition for more details about selectors.

Parameters:
  • name – query expression of attribute “name”, i.e. the UI elements with name name will be selected
  • attrs – other query expression except for the name
Returns:

a new UI proxy object representing the child(ren) of current UI element(s)

Return type:

UIObjectProxy

parent()[source]

Select the direct child(ren) from the UI element(s) given by the query expression, see QueryCondition for more details about the selectors.

Warning

Experimental method, may not be available for all drivers.

Returns:a new UI proxy object representing the direct parent of the first UI element.
Return type:UIObjectProxy
pinch(direction=u'in', percent=0.6, duration=2.0, dead_zone=0.1)[source]

Squeezing or expanding 2 fingers on this UI with given motion range and duration.

Parameters:
  • direction (str) – pinching direction, only “in” or “out”. “in” for squeezing, “out” for expanding
  • percent (float) – squeezing range from or expanding range to of the bounds of the UI
  • duration (float) – time interval in which the action is performed
  • dead_zone (float) – pinching inner circle radius. should not be greater than percent
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

rclick(*args, **kwargs)[source]

Perform the right click action on the UI element(s) represented by the UI proxy. If this UI proxy represents a set of UI elements, the first one in the set is clicked and the anchor point of the UI element is used as the default one. It is also possible to click another point offset by providing focus argument.

See CoordinateSystem for more details.

Parameters:
  • focus (2-tuple/2-list/str) – an offset point (x, y) from the top left corner of the UI element(s), values must be in range of 0~1. This argument can be also specified by ‘anchor’ or ‘center’. ‘Center’ means to click the center of bounding box of UI element.
  • sleep_interval – number of seconds to wait after this action. Default is None which is the default sleep interval. This value can be configured by Poco initialization. See configuration at poco initialization for more details.
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

refresh()

Clear the flag to indicate to re-query or re-select the UI element(s) from hierarchy.

alias is refresh()

Example

>>> a = poco(text="settings")
>>> print(a.exists())
>>> a.refresh()
>>> print(a.exists())
scroll(direction=u'vertical', percent=0.6, duration=2.0)[source]

Simply touch down from point A and move to point B then release up finally. This action is performed within specific motion range and duration.

Parameters:
  • direction (str) – scrolling direction. “vertical” or “horizontal”
  • percent (float) – scrolling distance percentage of selected UI height or width according to direction
  • duration (float) – time interval in which the action is performed
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

set_text(text)[source]

Set the text attribute of the UI element. If the UI element does not support mutation, an exception is raised

Parameters:text – the text value to be set
Raises:InvalidOperationException – when unable to mutate text value of the UI element
setattr(*args, **kwargs)[source]

Change the attribute value of the UI element. Not all attributes can be casted to text. If changing the immutable attributes or attributes which do not exist, the InvalidOperationException exception is raised.

Parameters:
  • name – attribute name
  • val – new attribute value to cast
Raises:

InvalidOperationException – when it fails to set the attribute on UI element

sibling(name=None, **attrs)[source]

Select the sibling(s) from the UI element(s) given by the query expression, see QueryCondition for more details about the selectors.

Parameters:
  • name – query expression of attribute “name”, i.e. the UI elements with name name will be selected
  • attrs – other query expression except for the name
Returns:

a new UI proxy object representing the child(ren) of current UI element(s)

Return type:

UIObjectProxy

start_gesture()[source]

Start a gesture action. This method will return a PendingGestureAction object which is able to generate decomposed gesture steps. You can invoke .to and .hold any times in a chain. See the following example.

Examples

poco = Poco(...)
ui1 = poco('xxx')
ui2 = poco('yyy')

# touch down on ui1 and hold for 1s
# then drag to ui2 and hold for 1s
# finally release(touch up)
ui1.start_gesture().hold(1).to(ui2).hold(1).up()

Note

always starts touching down at the position of current UI object.

Returns:an object for building serialized gesture action.
Return type:PendingGestureAction
swipe(*args, **kwargs)[source]

Perform a swipe action given by the direction from this UI element. For notices and limitations see .click().

Parameters:
  • direction (2-tuple/2-list/str) – coordinates (x, y) in NormalizedCoordinate system, it can be also specified as ‘up’, ‘down’, ‘left’, ‘right’. Swipe ‘up’ is equivalent to [0, -0.1], swipe ‘down’ is equivalent to [0, 0.1], swipe ‘left’ is equivalent to [-0.1, 0] and swipe ‘right’ is equivalent to [0.1, 0]
  • focus (2-tuple/2-list/str) – see .click() for more details
  • duration (float) – time interval in which the action is performed
Raises:

PocoNoSuchNodeException – raised when the UI element does not exist

wait(timeout=3)[source]

Block and wait for max given time before the UI element appears.

Parameters:timeout – maximum waiting time in seconds
Returns:self
Return type:UIObjectProxy
wait_for_appearance(timeout=120)[source]

Block and wait until the UI element appears within the given timeout. When timeout, the PocoTargetTimeout is raised.

Parameters:timeout – maximum waiting time in seconds
Raises:PocoTargetTimeout – when timeout
wait_for_disappearance(timeout=120)[source]

Block and wait until the UI element disappears within the given timeout.

Parameters:timeout – maximum waiting time in seconds
Raises:PocoTargetTimeout – when timeout

poco.exceptions module

exception InvalidOperationException(message=None)[source]

Bases: poco.exceptions.PocoException

Raised when the operation performing on target device is foreseen, e.g. instruction to click outside the screen is definitely meaningless, then the InvalidOperationException is raised.

exception PocoException(message=None)[source]

Bases: exceptions.Exception

Base class for errors and exceptions of Poco. It is Python3 compatible.

exception PocoNoSuchNodeException(objproxy)[source]

Bases: poco.exceptions.PocoException

Raised when the UI element specified by query expression cannot be found.

exception PocoTargetRemovedException(action, objproxy)[source]

Bases: poco.exceptions.PocoException

Raised when the hierarchy structure changed over the selection or when accessing the UI element that is already recycled.

In most cases, there is no need to handle this exception manually. If this exception occurs, it usually means it is a bug in your code rather than application itself. Check your code first. The most of misuses comes from as follows.

Examples

button1 = poco('button1')
time.sleep(10)   # waiting for long enough before the UI hierarchy changing
button1.click()  # PocoTargetRemovedException will raise at this line. Because the 'button1' is not on the screen.
exception PocoTargetTimeout(action, poco_obj_proxy)[source]

Bases: poco.exceptions.PocoException

Raised when the timeout expired while waiting for some condition to be fulfilled, e.g. waiting for the specific UI element but it has not appeared on the screen.

Engine specific API

Poco drivers (engine specific poco implementation)

For different engines please initialize poco instance by corresponding driver. Here are API reference of different drivers.

For other engines and platforms, please refer to Integration guide for more details

For multiple devices cases, please refer to Multiple devices guide for more details.

Poco SDK API

poco.sdk package

Subpackages
poco.sdk.interfaces package

The brief introduction:

This package (poco.sdk.interfaces) defines the main standards for communication interfaces between poco and poco-sdk. If poco-sdk is integrated with an app running on another host or in different language, then poco-sdk is called remote runtime. The implementation of these interfaces can be done either remotely or locally depending on your own choice. If it is done locally, refer to poco.freezeui for more information.

Poco needs to communicate with the app runtime under the convention of interfaces described below and these interfaces must be properly implemented. Any object implementing the same interface is replaceable and the communication protocol or transport layer has no limitation. Furthermore, in many cases the communication can be customized that one part of interfaces can use HTTP protocol and other can use TCP.

Submodules
poco.sdk.interfaces.command module
class CommandInterface[source]

Bases: object

This is one of the main communication interfaces. This interface defines command-level behaviours providing abilities to control remote runtime by sending self-defined command. The provided command can be various type - from string type to specific structure of a dict.

command(cmd, type)[source]

Emit a command to remote runtime (target device).

Parameters:
  • cmd – any json serializable data.
  • type (str) – a string value indicated the command type (command tag).
Returns:

None (recommended).

poco.sdk.interfaces.hierarchy module
class HierarchyInterface[source]

Bases: object

This is one of the main communication interfaces. This interface defines hierarchy-related behaviour assembling from IDumper, Attributor and Selector. The hierarchy mentioned is the real hierarchy on target app runtime, e.g. a game UI hierarchy in its runtime.

dump()[source]

Get the UI hierarchy with its origin structure and attributes, then store the structure and attributes into a json serializable dictionary.

Returns:dict representing the hierarchy structure. Structure specification refers to IDumper.
Return type:dict
getAttr(nodes, name)[source]

Get attribute of UI element.

Parameters:
  • nodes – UI element or list of UI elements, if there is a list of UI elements provided, then only the first UI element will be used
  • name (str) – attribute name
select(query, multiple)[source]

Select UI element(s) matching the given query expression and return the list of selected UI element(s)

Parameters:
  • query (tuple) – query expression, for the structure specification refer to Selector.
  • multiple (bool) – whether or not to select multiple elements, if False, the method terminates immediately once the node is found, otherwise the method travers through all nodes and then terminates
Returns:

list of UI elements corresponding to the given query expression

Return type:

list

setAttr(nodes, name, value)[source]

Set attribute of UI element.

Parameters:
  • nodes – UI element or list of UI elements, if there is a list of UI elements provided, then only the first UI element will be used
  • name (str) – attribute name
  • value – new value to be set.
Raises:

UnableToSetAttributeException – raised when:

  • fails to set attributes on given UI element
  • the engine does not support mutating attributes
  • developer does not allow to change the attribute value by implementation
poco.sdk.interfaces.input module
class InputInterface[source]

Bases: object

This is one of the main communication interfaces. This interface ensures the ability for simulated input on target device. So far, the interface supports only some basic methods definitions. The motion event will be added in future to provide full support for mobile devices.

All coordinates are in NormalizedCoordinate system, see NormalizedCoordinate for more details.

applyMotionEvents(events)[source]

Apply a motion event on the device touch panel

Parameters:events (list) – list of events. each event is MotionEvent data (['u/d/m/s', (x, y), contact_id])
click(x, y)[source]

Perform click action as simulated input on target device. Coordinates arguments are all in range of 0~1.

Parameters:
  • y (float) – y-coordinate
  • x (float) – x-coordinate
getTouchDownDuration()[source]

Get touch down duration for each click operation. Each implementation should provide a default value.

Returns:the touch down duration in seconds
Return type:float
keyevent(keycode)[source]

Send a key event to target device.

Parameters:keycode (int or char) – Ascii key code
longClick(x, y, duration)[source]

Perform press action as simulated input on target device within given seconds. Coordinates arguments are all in range of 0~1.

Parameters:
  • x (float) – x-coordinate
  • y (float) – y-coordinate
  • duration (float) – time interval to perform the action
setTouchDownDuration(duration)[source]

Set touch down duration for each click operation.

Parameters:duration (float) – the duration in seconds
swipe(x1, y1, x2, y2, duration)[source]

Perform swipe action as simulated input on target device from point A to B within given time interval to perform the action. Coordinates arguments are all in range of 0~1.

Parameters:
  • x1 (float) – x-coordinate of the start point
  • y1 (float) – y-coordinate of the start point
  • x2 (float) – x-coordinate of the end point
  • y2 (float) – y-coordinate of the end point
  • duration (float) – time interval to perform the swipe action
poco.sdk.interfaces.screen module
class ScreenInterface[source]

Bases: object

This is one of the main communication interfaces. This interface ensures the ability for accessing the rendering the results presented on screen of target device. Note that rendering results are very often not used in automated testing directly. Following methods definitions can assist to obtain the information about the app.

getPortSize()[source]

Get the real resolution of the screen in pixels.

Returns:width and height in pixels
Return type:2-list (float, float)
getScreen(width)[source]

Take the screenshot of the target device screen or target app’s window

Parameters:width (int) – expected width of the screenshot in pixels
Returns:
  • b64img: base64 encoded screen data
  • format: screen data format (png/jpg/etc.)
Return type:2-list (str, str)
poco.sdk.std package
Subpackages
poco.sdk.std.rpc package
Submodules
poco.sdk.std.rpc.controller module
exception RpcRemoteException[source]

Bases: exceptions.Exception

poco.sdk.std.rpc.reactor module
exception NoSuchMethod(name, available_methods)[source]

Bases: exceptions.Exception

poco.sdk.std.transport package
Submodules
poco.sdk.std.protocol module
class SimpleProtocolFilter[source]

Bases: object

简单协议过滤器 协议按照 [有效数据字节数][有效数据] 这种协议包的格式进行打包和解包 [有效数据字节数]长度HEADER_SIZE字节 [有效数据]长度有效数据字节数字节 本类按照这种方式,顺序从数据流中取出数据进行拼接,一旦接收完一个完整的协议包,就会将协议包返回 [有效数据]字段接收到后会按照utf-8进行解码,因为在传输过程中是用utf-8进行编码的 所有编解码的操作在该类中完成

input(data)[source]

小数据片段拼接成完整数据包 如果内容足够则yield数据包

static pack(content)[source]

content should be str

static unpack(data)[source]

return length, content

Submodules
poco.sdk.AbstractDumper module
class IDumper[source]

Bases: object

This interface defines the standard dumper behavior. Dumper class is introduced to get the hierarchy information and convert it into serializable data.

dumpHierarchy(onlyVisibleNode)[source]

Return the json serializable dictionary holding the hierarchy data. Refer to sample of returned structure object below.

Structure of the dict:

{
    # name can be duplicated from the original name or just left the default one
    # if it cannot be determined, however providing some meaningful name is preferred
    'name': '<a recognizable string>'

    # All available attributes of this node are in form of key-value pairs
    'payload': {
        'name': '',
        'pos': [0, 0],
        'size': [1, 1],
        ...
    },

    # If there is no child, this part can be omitted
    'children': [
        {...},  # Same structure as this dict.
    ],
}
Returns:hierarchy data or None
Return type:dict or NoneType
getRoot()[source]

Return the root node of the UI Hierarchy. The node information is wrapped by AbstractNode. See definition of AbstractNode for more details.

Returns:instance that holds the hierarchy data
Return type:inherit from AbstractNode
class AbstractDumper[source]

Bases: poco.sdk.AbstractDumper.IDumper

This class partially implements IDumper using general traversal algorithm. In order to dump the hierarchy from the root node, this dumper first retrieves all available attributes of the root node and also the list all its children and then applies the same procedures as described on each child (i.e. treats each child as a root node) until the node that has no child(ren) is reached.

dumpHierarchy(onlyVisibleNode=True)[source]
Returns:json serializable dict holding the whole hierarchy data
Return type:dict
dumpHierarchyImpl(node, onlyVisibleNode=True)[source]

Crawl the hierarchy tree using the simple DFS algorithm. The dump procedure is the engine independent as the hierarchy structure is wrapped by AbstractNode and therefore the dump procedure can be algorithmized.

Following code demonstrates the simplest implementation. Feel free to implement your own algorithms to optimize the performance.

Note

Do not explicitly call this method as this is an internal function, call dumpHierarchy() function instead if you want to dump the hierarchy.

Parameters:
  • node (inherit from AbstractNode) – root node of the hierarchy to be dumped
  • onlyVisibleNode (bool) – dump only the visible nodes or all nodes, default to True
Returns:

json serializable dict holding the whole hierarchy data

Return type:

dict

poco.sdk.AbstractNode module
class AbstractNode[source]

Bases: object

AbstractNode is a wrapper class that provides UI hierarchy and node info in game engine.

This class uniformly specifies node-related methods such as retrieving the attributes or accessing the parent nodes or their children.

enumerateAttrs()[source]

Enumerate all available attributes and yield the 2-tuple (name, value).

Yields:Iterable<str, ValueType>
getAttr(attrName)[source]

Return the attributes of the node. The list below shows the most used basic attributes used during while writing test code. The implementation class should return the corresponding value as soon as it retrieves its value. If the value cannot be determined, the default value is obtained from super class invocation and returned. See the example below for more detailed information. More attributes can be added in order to enhance the selection and displaying in Inspector.

The most used basic attributes are listed as follows:

  • name: name of the node, use the unique and meaningful name for each node is recommended
  • type: type of the name of the node, it can be either any string, e.g. “android.widget.Button” or just simple as “Button”
  • visible: True or False whether the node is rendered on screen. In case the return value is False, all children nodes are ignored in Poco selector
  • pos: position of the node in screen, return value should be 2-list coordinates (x, y) representing the percentage of the screen. e.g. if the node lies in the center of the screen, the attribute will be [0.5f, 0.5f]. If the returned value for position is negative, it means the node lies out of the screen
  • size: size of the node bounding box, similar to pos, value is also a 2-list of the percentage of the screen size, e.g. the screen size is always [1.0f, 1.0f], if the node lies in left half side of the screen, its size will be [0.5f, 1.0f], the returned value of size should be always positive value
  • scale: scale factor applied to the node itself, leave it [1.0f, 1.0f] by default
  • anchorPoint: 2-list coordinates (x, y) of the anchor expressed in the percentage related to the bounding box of the node, leave it [0.5f, 0.5f] by default.
  • zOrders: rendering order of this node, its value is a dictionary such as {"global": 0, "local": 0}, global zOrder value is compared with all nodes in the hierarchy, local zOrder value is compared with its parent and siblings. The most top nodes have the largest values.

Examples

The following sample code demonstrates some ideas about the implementation of this method:

def getAttr(self, attrName):
    # basic attributes
    if attrName == 'name':
        return self.node.get_name() or '<no name>'

    elif attrName == 'pos':
        return self.node.get_position()

    # ...

    # extra engine-specific attributes
    elif attrName == 'rotation':
        return self.node.get_rotation()

    # call the super method by default
    else:
        return super().getAttr(attrName)
Parameters:attrName (str) – attribute name
Returns:JsonSerializable attribute value or None if no such attribute.
getAvailableAttributeNames()[source]

Enumerate all available attribute names of this node. This method in base class returns the basic attribute name by default. It is possible to add other customized or engine-specific attributes. See the example below.

Note

It is recommended to always call the super method and return should contain the part from super method.

Examples

This code demonstrates how to implement this method:

def getAvailableAttributeNames(self):
    return super().getAvailableAttributeNames() + (
        # add other engine-specific attribute names here if need.
        'rotation',
    )
Returns:Iterable<str>
getChildren()[source]

Return an iterator over all children nodes of this node. This method is invoked by Selector or Dumper to retrieve the UI hierarchy.

Returns:Iterable <AbstractNode>
getParent()[source]

Return the parent node of this node. Return None if there is no parent or parent is not accessible or this is the root node. This method is invoked by Selector or Dumper when traversing UI hierarchy.

Returns:parent node of this node or None.
Return type:AbstractNode or NoneType
setAttr(attrName, val)[source]

Apply changes of the attribute value to this node. Not all attributes can be modified. The most common attribute to be modified is the text. It is not recommended to modify attributes such as position, name, their modifications can lead to unexpected and false-positive errors.

Parameters:
  • attrName (str) – attribute name
  • val – attribute value
Returns:

True if success else False or raise.

See also

setAttr in poco.sdk.interfaces.hierarchy

poco.sdk.Attributor module
class Attributor[source]

Bases: object

This is a helper class to access the node attributes. In some cases it is not possible to explicitly invoke the node member functions thus the following two functions are introduced.

The instance of this class will be used in implementation of HierarchyInterface.

Note

Do not call these methods explicitly in the test codes.

poco.sdk.DefaultMatcher module
class DefaultMatcher[source]

Bases: poco.sdk.DefaultMatcher.IMatcher

Default matcher implementation for poco hierarchy traversing. Including logical query condition and predicate expression. When traversing through the hierarchy tree, matcher will apply the match method on each node of the tree.

The formal definition of query condition as follows:

expr := (op0, (expr0, expr1, ...))
expr := (op1, (arg1, arg2))
  • op0str is logical operator (‘or’ or ‘and’) which has the same semantics as in python, e.g. ‘or’ means this expression/condition matches if any of the exprN matches

  • op1str is comparator, can be one of as follows:

    op1 := 'attr='
    op1 := 'attr.*='
    op1 := ... (other customized)
    

    The op1 must be a string. The Matcher will help to map to Comparator object.

match(cond, node)[source]

See Also: IMatcher.match

class EqualizationComparator[source]

Bases: object

Compare two objects using the native equivalence (==) comparison operator

class RegexpComparator[source]

Bases: object

Compare two objects using regular expression. Available only when the original value is string type. It always returns False if the original value or given pattern are not str type.

compare(origin, pattern)[source]
Parameters:
  • origin (str) – original string
  • pattern (str) – Regexp pattern string
Returns:

True if matches otherwise False.

Return type:

bool

poco.sdk.Selector module
class ISelector[source]

Bases: object

This interface defines the standard selector behavior. Selector is used for selecting the specific UI element(s) by given query expression (formal definitions are in specific implementation classes).

select(cond, multiple=False)[source]
Parameters:
  • cond (tuple) – query expressiom
  • multiple (bool) – whether or not to select the multiple elements. If set to True, the method terminates immediately once the node is found, otherwise it traverses through all nodes and then exists
Returns:

list <inherited from AbstractNode>

Return type:

list

class Selector(dumper, matcher=None)[source]

Bases: poco.sdk.Selector.ISelector

This class implements the standard Selector interface that uses DFS algorithm to travers through tree-like hierarchy structure. It supports flexible query expressions such as parental relationship, attribute predicate, etc. Any combinations of expressions mentioned above are also allowed as the query conditions.

The query expression can be defined as follows:

expr := (op0, (expr0, expr1))
expr := ('index', (expr, :obj:`int`))
expr := <other query condition> See implementation of Matcher.
  • op0 can be one of the following (‘>’, ‘/’, ‘-‘), each operator stands for as follows:

    '>': offsprings, select all offsprings matched expr1 from all roots matched expr0.
    '/': children, select all children matched expr1 from all roots matched expr0.
    '-': siblings, select all siblings matched expr1 from all roots matched expr0.
    '^': parent, select the parent of 1st UI element matched expr0. expr1 is always None.
    
  • 'index': select specific n-th UI element from the previous results

  • others: passes the expression to matcher

Parameters:
  • dumper (any implementation of IDumper) – dumper for the selector
  • matcher (any implementation of IMatcher) – DefaultMatcher instance by default.
getRoot()[source]

Get a default root node.

Returns:default root node from the dumper.
select(cond, multiple=False)[source]

See Also: select method in ISelector.

selectImpl(cond, multiple, root, maxDepth, onlyVisibleNode, includeRoot)[source]

Selector internal implementation. TODO: add later.

Note

This doc shows only the outline of the algorithm. Do not call this method in your code as this is an internal method.

Parameters:
  • cond (tuple) – query expression
  • multiple (bool) – whether or not to select multiple nodes. If true, all nodes that matches the given condition will return, otherwise, only the first node matches will.
  • root (inherited from AbstractNode) – start traversing from the given root node
  • maxDepth (bool) – max traversing depth
  • onlyVisibleNode (bool) – If True, skip those node which visibility (the value of visible attribute) is False.
  • includeRoot (bool) – whether not not to include the root node if its child(ren) match(es) the node
Returns:

The same as

select.

Return type:

list <inherited from AbstractNode>

poco.sdk.exceptions module

This module provides several exceptions for poco-sdk. These exceptions are raised only in sdk corresponding runtime.

exception NoSuchTargetException[source]

Bases: exceptions.Exception

Raised when the index is out of range for selecting the UI element by given index.

exception NodeHasBeenRemovedException(attrName, node)[source]

Bases: exceptions.Exception

Raised when the node (UI element) is refreshed (updated, recycled or destroyed) while retrieving the attributes during traversing the hierarchy.

In some engines implementations, the UI hierarchy is refreshed in a stand-alone thread while poco is performing the traverse process, so the engine error might be triggered when poco is trying to retrieve the attribute of the UI element but the attribute is being updated at the same time. In this situation, poco sdk catches the engine error and raises this exception.

exception UnableToSetAttributeException(attrName, node)[source]

Bases: exceptions.Exception

Raised when settings the attribute of the given UI element failed. In most cases, the reason why it failed is that the UI element does not support mutation. From the point of view of SDK implementation, this exception can be raised proactively to indicate that the modification of the attribute is not allowed.

exception NoSuchComparatorException(matchingMethod, matcherName)[source]

Bases: exceptions.Exception

Raised when the matcher does not support the given comparison method.

exception NonuniqueSurfaceException(selector)[source]

Bases: exceptions.Exception

Raised when the device selector matches mutiple devices surface

exception InvalidSurfaceException(target, msg='None')[source]

Bases: exceptions.Exception

Raised when the device surface is invalid

Poco Examples and Tutorials

Here are several example poco test scripts. Each example is written by using Unity3D demo game. It is nothing different from other games.

Download the demo game

You can have a try most of the example scripts with the super simple demo game we provided.

More demo games for testing

The following files are standard apps with according poco-sdk integrated. You can download and install on your device to make sure it works for your environment.

PocoSDK Integration Guide

PocoSDK implementations for most popular game engines are already provided in poco-sdk repo. You can easily integrate PocoSDK in your game following the corresponding instruction.

Feel free to open an issue if you get stuck in integration.

Unity3D

PocoSDK supports Unity3D version 4 & 5, ngui & ugui, C# only for now. If your game is not supported yet, please refer to implementation guide.

  1. Clone source code from poco-sdk repo.
  2. Copy the Unity3D folder to your unity project script folder.
  3. If you are using ngui, just remove the sub folder Unity3D/ugui . If you are using ugui, just remove the sub folder Unity3D/ngui .
  4. Add Unity3D/PocoManager.cs as script component on any GameObject, generally on main camera.
Cocos2dx-lua

PocoSDK supports cocos2dx-lua version >= 3.1.

  1. Clone the sdk source code from poco-sdk repo.
  2. Copy the cocos2dx-lua/poco folder to your project script folder. You can rename the folder if you wish.
  3. Initialize poco-sdk by copying following code to your game initialization script.
local poco = require('poco.poco_manager')
poco:init_server(15004)  -- default port number is 15004, change to another if you like

Note

Your cocos2dx-lua project should at least enable socket or socket.core module, as poco requires this module to setup a tcp server at runtime.

Note

If your cocos2dx-lua project does not have mime module, it still works but a little slow.

  1. Check the adb logcat (for Android) whether successfully integrated. (It is highly recommended to use Android Studio.) If you can see the following “LUA-print”, you poco-sdk is successfully integrated.
_images/integration-guild-cocos2dxlua-sdk-started.png

After integration, you can start to play with poco. Or if you have any problems, feel free to open an issue here. Title format: [cocos2dx-lua sdk integration] xxx

Cocos2dx-js (beta)

PocoSDK supports 3.1 <= Cocos2dx version <= 3.13.1 (theoretically). To make sure compatibility across every javascript engines, the javascript sdk is written in ES5.

cocos2d-x 3.13.1 is tested and passed.

  1. Clone sdk source code from poco-sdk repo.
  2. Copy the cocos2dx-js/Poco folder to your cocos project javascript folder.
  3. As sdk requires WebSocketServer, we should build WebSocketServer module and js bindings in native code. The WebSocketServer is part of cocos2dx framework but not include by default thus we build it manually by following steps. See also WebSocketServer reference. You’d better backup your origin websockets folder before copying. The js binding for WebSocketServer requires libwebsockets version == 1.6. There may be some incompatibility issues in cocos2d-x itself if your cocos2d-x engine is not using libwebsockets version == 1.6.
cp -r cocos2dx-js/3rd/websockets <your-cocos-project>/frameworks/cocos2d-x/external/websockets
cp cocos2dx-js/3rd/src/* <your-cocos-project>/frameworks/runtime-src/Classes
  1. Edit <your-cocos-project>/frameworks/runtime-src/Classes/AppDelegate.cpp. Add following 2 lines at proper position respectively.
// include it at top
#include "jsb_websocketserver.h"

// register callbacks of websocketserver
sc->addRegisterCallback(register_jsb_websocketserver);
_images/integration-guild-cocos2dxjs-jsbwebsocketserver.png _images/integration-guild-cocos2dxjs-jsbwebsocketserver-2.png
  1. Edit <your-cocos-project>/frameworks/runtime-src/proj.android/jni/Android.mk. Add following boxed line at proper position in your own makefile. If you are using AndroidStudio project, edit proj.android-studio/jni/Android.mk and ensure the path is correct.
_images/integration-guild-cocos2dxjs-jsbwebsocketserver-3.png
...

$(call import-add-path, $(LOCAL_PATH)/../../../cocos2d-x/external)

LOCAL_SRC_FILES := hellojavascript/main.cpp \
                   ../../Classes/AppDelegate.cpp \
                   ../../Classes/WebSocketServer.cpp \
                   ../../Classes/jsb_websocketserver.cpp

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \
                    $(LOCAL_PATH)/../../../cocos2d-x/external/websockets/include/android

LOCAL_STATIC_LIBRARIES := cocos2d_js_static websockets_static

include $(BUILD_SHARED_LIBRARY)
$(call import-module, websockets/prebuilt/android)
$(call import-module, scripting/js-bindings/proj.android)

...
  1. Edit your project.json. Append sdk filenames to jsList in the following order.
{
    // ...
    jsList: [
        // ...
        "src/lib/Poco/sdk/AbstractDumper.js",
        "src/lib/Poco/sdk/AbstractNode.js",
        "src/lib/Poco/sdk/Attributor.js",
        "src/lib/Poco/sdk/DefaultMatcher.js",
        "src/lib/Poco/sdk/IScreen.js",
        "src/lib/Poco/sdk/Selector.js",
        "src/lib/Poco/Cocos2dxNode.js",
        "src/lib/Poco/Cocos2dxDumper.js",
        "src/lib/Poco/POCO_SDK_VERSION.js",
        "src/lib/Poco/Poco.js"
    ]
}
  1. Recompile your cocos project
  2. require('Poco') in your game’s first initialized script to start PocoSDK, and do not destroy it during game’s lifetime.
var PocoManager = window.PocoManager
var poco = new PocoManager()

// add poco on window object to persist
window.poco = poco

If anything goes wrong, please open an issue here. Title format: [cocos2dx-js sdk integration] xxx

Cocos-Creator

PocoSDK supports Cocos Creator 2.2.1 or higher.

Cocos Creator 2.2.1 is tested and passed.

  1. Clone sdk source code from poco-sdk repo.

  2. Copy the cocos-creator/Poco folder to your cocos project javascript folder.

  3. As sdk requires WebSocketServer, we should enable WebSocketServer module and js bindings in native code.

    The WebSocketServer is part of cocos2dx framework but not include by default thus we should include it manually by following steps.

    1. Go to the engine directory.
    2. Edit the ccConfig.h file. The path is like your/path/to/CocosCreator_2.2.1/resources/cocos2d-x/cocos/base/ccConfig.h
    3. Edit line 62. Change #define USE_WEBSOCKET_SERVER 0 to #define USE_WEBSOCKET_SERVER 1
  4. Recompile your cocos project

  5. require('Poco') in your game’s first initialized script to start PocoSDK, and do not destroy it during game’s lifetime.

cc.Class({
    extends: cc.Component,

    .....

    //remember to put code in onLoad function
    onLoad: function () {
       .....

        var poco = require("./Poco") // use your own relative path
        window.poco = new poco(); // not destroy
        cc.log(window.poco);

    },

   .....
});

Note

Currently only supports Android and Windows platform, and Poco can only be used after packaging. Not available in preview mode.

Unreal

(Coming soon.)

Android Native App

Nothing to do about integration. Just start writing tests and be happy. See poco for Android Native App section for more details.

Netease Internal Engines

Just config the module preload at Hunter. Please append following script to the end of hunter __init__ instruction. Require safaia version >= 1.2.0. Use print Safaia.__version__ to get current version. Any questions about hunter feel free to contact lxn3032@corp.netease.com.

  • for NeoX
# core (add only if not present)
Safaia().install(require('safaia.init.core'))

# poco uiautomation
PocoUiautomation = require('support.poco.neox.uiautomation')
Safaia().install(PocoUiautomation)

# inspector extension
screen_handler = require('support.poco.neox.screen')()
InspectorExt = require('support.poco.safaia.inspector')
InspectorExt.screen = screen_handler
InspectorExt.dumper = require('support.poco.neox.Dumper')()
Safaia().install(InspectorExt)
  • for Messiah
# core (add only if not present)
Safaia().install(require('safaia.init.core'))

# poco uiautomation
PocoUiautomation = require('support.poco.messiah.uiautomation')
Safaia().install(PocoUiautomation)

# inspector extension
screen_handler = require('support.poco.messiah.screen')()
InspectorExt = require('support.poco.safaia.inspector')
InspectorExt.screen = screen_handler
InspectorExt.dumper = require('support.poco.cocos2dx.Dumper')()
Safaia().install(InspectorExt)
  • for cocos2dx-* and others: please contact lxn3032@corp.netease.com.
Other Engines

See implementation guide. This guide helps you implement and integrate PocoSDK with your game step by step.

Dump UI hierarchy example

Poco defines an uniform format to serialize UI hierarchy for different game engines. This section shows how to dump UI hierarchy.

import json
from poco.drivers.unity3d import UnityPoco as Poco

poco = Poco()
ui = poco.agent.hierarchy.dump()
print(json.dumps(ui, indent=4))

The following is the snippet of UI hierarchy. All UI elements are organized in dict representing the tree structure. More detailed info about properties are described in .dumpHierarchy().

...
{
    "name": "OctopusArea",
    "payload": {
        "name": "OctopusArea",
        "type": "GameObject",
        "visible": true,
        "clickable": true,
        "zOrders": {
            "global": 0,
            "local": -10
        },
        "scale": [
            1,
            1
        ],
        "anchorPoint": [
            0.5,
            0.5
        ],
        "pos": [
            0.130729169,
            0.44907406
        ],
        "size": [
            0.0859375,
            0.125
        ]
    }
    "children": [
        {...},
        ...
    ],
}
...

Join to discuss!

join slack