--- /dev/null
+# AURUM(Tizen UI Automator)
+- Aurum is a UI automation framework without UI Toolkit dependency.
+
+ Provides Commands to interact with the device¡¯s UI by simulation user actions and introspection of the screen content.
+
+ It relies on the platform accessibility APIs to introspect the screen.
+
+- User can use the IDL defined in Proto to create an automation app or script in a variety of languages without a language dependency.
+
+ [List of Supported Command With Python Example](https://code.sec.samsung.net/confluence/pages/viewpage.action?pageId=212993496)
+
+
+### Running on TV
+- Pre-condition
+
+ The TV device and Host PC should be already connected through SDB
+
+- Gets Aurum latest version and checkout to tizen branch
+
+ [Aurum Github](https://github.sec.samsung.net/tizen/aurum)
+
+- Set up a python virtual environment and run UI Automation (working directory: aurum/)
+
+ (host) cd ui_automation/python/tv
+
+ #### Create virtual env
+ (host) python3 -m venv v
+
+ #### Activate a virtual env
+ Linux
+ (host) source v/bin/activate
+ Window
+ (host) v/Scripts/activate.bat
+
+ #### Install required pkg (only once)
+ (python_virtual) pip3 install -r ../../../protocol/resources/python/requirements.txt
+
+ #### Generate aurum.proto file for python (only once)
+ (python_virtual) python3 -m grpc_tools.protoc --python_out=./ --grpc_python_out=./ -I ./../../../protocol/ ../../../protocol/aurum.proto
+
+ #### Target setup such as sdb forward, bootstrap execution
+ (python_virtual) python3 ../../../protocol/resources/python/tv/tvSetup.py
+
+ #### Then Run your test script (Please refer sample scripts in 'aurum/protocol/resources/python/tv')
+ (python_virtual) python3 myTest.py
+
+ #### Deactivate a virtual env
+ (python_virtual) deactivate
+
+### Reference and Tip
+- [How to run aurum on TM1](https://code.sec.samsung.net/confluence/display/GFX/04.+NUITizenGallery+Test+Script+Guide)
+
+* test python script must be in same location as aurum_pb2.py
+
```sh
python3 -m venv v
--- /dev/null
+from __future__ import print_function
+import os
+import sys
+sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(os.path.dirname(__file__)))))
+from aurum_pb2 import *
+from aurum_pb2_grpc import BootstrapStub
+import logging
+import grpc
+import time
+
+# Check the object in the screen(TM1) or not
+def inScreen(size):
+ if size.x < 0: return False
+ if size.y < 0: return False
+ if size.x >= 720: return False
+ if size.y >= 1280: return False
+ return True
+
+# 1. Find TextField(entry)
+# 2. Set Text as Picker
+# 3. Click Run button
+# 4. Find PickerTest1 item on the result list
+# 5. Find 'Black' textlabel on the layout
+def PickerExecuteTestWithText(stub):
+ # 1
+ response = stub.findElement(ReqFindElement(widgetType='TextField'))
+ if len(response.elements) <= 0: return False
+ # 2
+ targetObj = response.elements[0].elementId
+ testString = 'Picker'
+ stub.setValue(ReqSetValue(elementId=targetObj, stringValue=testString))
+ # 3
+ response = stub.findElement(ReqFindElement(textField='Run'))
+ if len(response.elements) <= 0: return False
+ targetObj = response.elements[0].elementId
+ stub.click(ReqClick(type='ELEMENTID', elementId=targetObj))
+ # 4
+ response = stub.findElement(ReqFindElement(textField='PickerTest1'))
+ if len(response.elements) <= 0: return False
+ targetObj = response.elements[0].elementId
+ stub.click(ReqClick(type='ELEMENTID', elementId=targetObj))
+ # 5
+ response = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) <= 0: return False
+
+ return True
+
+# 1. Find PickerTest1 item on the list
+# 2. Click PickerTest1 item
+# 3. Find 'Black' textlabel on the layout
+def PickerExecuteTest(stub):
+ for tryCnt in range(10):
+ # 1
+ stub.flick(ReqFlick(startPoint=Point(x=300, y=750), endPoint=Point(x=300, y=200), durationMs=150))
+ response = stub.findElement(ReqFindElement(textField='PickerTest1'))
+ if len(response.elements) <= 0: continue
+ targetObj = response.elements[0].elementId
+ response = stub.getSize(ReqGetSize(elementId=targetObj))
+ if inScreen(response.size):
+ # 2
+ stub.click(ReqClick(type='ELEMENTID', elementId=targetObj))
+ break
+
+ # 3
+ response = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) <= 0: return False
+ return True
+
+# 1. Find PickerScroller(Picker's internal scroller)
+# 2. Find 'Black' textlabel on the layout
+# 3. Get PickerScroller geometry value for flick event
+# 4. Check the loop works well while changing the picker item by flick event
+def PickerScrollTest(stub):
+ # 1
+ response = stub.findElement(ReqFindElement(widgetType='PickerScroller'))
+ if len(response.elements) <= 0: return False
+ # 2
+ responseText = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) <= 0: return False
+ # 3
+ pickerCenterX = response.elements[0].geometry.x + (response.elements[0].geometry.width / 2)
+ pickerCenterY = response.elements[0].geometry.y + (response.elements[0].geometry.height / 2)
+
+ for tryCnt in range(30):
+ # 4
+ stub.flick(ReqFlick(startPoint=Point(x=int(pickerCenterX), y=int(pickerCenterY)), endPoint=Point(x=int(pickerCenterX), y=int(pickerCenterY-70)), durationMs=100))
+ response = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) > 0:
+ if response.elements[0].elementId == responseText.elements[0].elementId:
+ return True
+
+ return False
+
+# Launch application. it returns application running state
+def launchAppTest(stub):
+ stub.launchApp(ReqLaunchApp(packageName='org.tizen.example.NUITizenGallery'))
+ return stub.getAppInfo(ReqGetAppInfo(packageName='org.tizen.example.NUITizenGallery')).isRunning
+
+# Close application. it returns application running state
+def closeAppTest(stub):
+ stub.closeApp(ReqCloseApp(packageName='org.tizen.example.NUITizenGallery'))
+ return stub.getAppInfo(ReqGetAppInfo(packageName='org.tizen.example.NUITizenGallery')).isRunning != True
+
+def defaultSetup(stub):
+ if stub.getAppInfo(ReqGetAppInfo(packageName='org.tizen.example.NUITizenGallery')).isRunning:
+ stub.closeApp(ReqCloseApp(packageName='org.tizen.example.NUITizenGallery'))
+
+ stub.launchApp(ReqLaunchApp(packageName='org.tizen.example.NUITizenGallery'))
+
+def defaultTearDown(stub):
+ stub.closeApp(ReqCloseApp(packageName='org.tizen.example.NUITizenGallery'))
+
+def runTest(stub, testFunc, setup=defaultSetup, tearDown=defaultTearDown, alwaySucceed=False):
+ print("Testing started :", testFunc)
+
+ setup(stub)
+ result = testFunc(stub)
+ tearDown(stub)
+
+ print("Testing result :", result)
+ if alwaySucceed: return True
+
+def runTestWithoutSetupAndTearDown(stub, testFunc, setup=defaultSetup, tearDown=defaultTearDown):
+ def Empty(stub):
+ pass
+
+ runTest(stub, testFunc, Empty, Empty)
+
+def run():
+ with grpc.insecure_channel('127.0.0.1:50051') as channel:
+ stub = BootstrapStub(channel)
+ # Picker Test
+ runTestWithoutSetupAndTearDown(stub, launchAppTest)
+ runTestWithoutSetupAndTearDown(stub, PickerExecuteTestWithText)
+ runTestWithoutSetupAndTearDown(stub, PickerScrollTest)
+ runTestWithoutSetupAndTearDown(stub, closeAppTest)
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ run()
from __future__ import print_function
+import os
+import sys
+sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(os.path.dirname(__file__)))))
+from __future__ import print_function
from aurum_pb2 import *
from aurum_pb2_grpc import BootstrapStub
import logging
--- /dev/null
+import os
+import subprocess
+import re
+import sys
+import time
+
+def run_command(command):
+ stream = os.popen(command)
+ output = stream.read()
+
+# Start scrip here
+run_command("sdb root on")
+run_command("sdb forward tcp:50051 tcp:50051")
+run_command("sdb shell app_launcher -s org.tizen.aurum-bootstrap")
+# Wait 1 sec till bootstrap launched
+time.sleep(1)
--- /dev/null
+from __future__ import print_function
+import os
+import sys
+sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(os.path.dirname(__file__)))))
+from aurum_pb2 import *
+from aurum_pb2_grpc import BootstrapStub
+import logging
+import grpc
+import time
+
+# Check the object in the screen(TV) or not
+def inScreen(size):
+ if size.x < 0: return False
+ if size.y < 0: return False
+ if size.x >= 1920: return False
+ if size.y >= 1080: return False
+ return True
+
+# 1. Find TextField(entry)
+# 2. Set Text as Picker
+# 3. Click Run button
+# 4. Find PickerTest1 item on the result list
+# 5. Find 'Black' textlabel on the layout
+def PickerExecuteTestWithText(stub):
+ # 1
+ response = stub.findElement(ReqFindElement(widgetType='TextField'))
+ if len(response.elements) <= 0: return False
+ # 2
+ targetObj = response.elements[0].elementId
+ testString = 'Picker'
+ stub.setValue(ReqSetValue(elementId=targetObj, stringValue=testString))
+ # 3
+ response = stub.findElement(ReqFindElement(textField='Run'))
+ if len(response.elements) <= 0: return False
+ targetObj = response.elements[0].elementId
+ stub.click(ReqClick(type='ELEMENTID', elementId=targetObj))
+ # 4
+ response = stub.findElement(ReqFindElement(textField='PickerTest1'))
+ if len(response.elements) <= 0: return False
+ targetObj = response.elements[0].elementId
+ stub.click(ReqClick(type='ELEMENTID', elementId=targetObj))
+ # 5
+ response = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) <= 0: return False
+
+ return True
+
+# 1. Find PickerTest1 item on the list
+# 2. Click PickerTest1 item
+# 3. Find 'Black' textlabel on the layout
+def PickerExecuteTest(stub):
+ for tryCnt in range(10):
+ # 1
+ stub.flick(ReqFlick(startPoint=Point(x=300, y=750), endPoint=Point(x=300, y=200), durationMs=150))
+ response = stub.findElement(ReqFindElement(textField='PickerTest1'))
+ if len(response.elements) <= 0: continue
+ targetObj = response.elements[0].elementId
+ response = stub.getSize(ReqGetSize(elementId=targetObj))
+ if inScreen(response.size):
+ # 2
+ stub.click(ReqClick(type='ELEMENTID', elementId=targetObj))
+ break
+
+ # 3
+ response = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) <= 0: return False
+ return True
+
+# 1. Find PickerScroller(Picker's internal scroller)
+# 2. Find 'Black' textlabel on the layout
+# 3. Get PickerScroller geometry value for flick event
+# 4. Check the loop works well while changing the picker item by flick event
+def PickerScrollTest(stub):
+ # 1
+ response = stub.findElement(ReqFindElement(widgetType='PickerScroller'))
+ if len(response.elements) <= 0: return False
+ # 2
+ responseText = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) <= 0: return False
+ # 3
+ pickerCenterX = response.elements[0].geometry.x + (response.elements[0].geometry.width / 2)
+ pickerCenterY = response.elements[0].geometry.y + (response.elements[0].geometry.height / 2)
+
+ for tryCnt in range(30):
+ # 4
+ stub.flick(ReqFlick(startPoint=Point(x=int(pickerCenterX), y=int(pickerCenterY)), endPoint=Point(x=int(pickerCenterX), y=int(pickerCenterY-70)), durationMs=100))
+ # Wait until view update
+ time.sleep(0.3)
+ response = stub.findElement(ReqFindElement(textField='Black'))
+ if len(response.elements) > 0:
+ if response.elements[0].elementId == responseText.elements[0].elementId:
+ return True
+
+ return False
+
+# Launch application. it returns application running state
+def launchAppTest(stub):
+ stub.launchApp(ReqLaunchApp(packageName='org.tizen.example.NUITizenGallery'))
+ return stub.getAppInfo(ReqGetAppInfo(packageName='org.tizen.example.NUITizenGallery')).isRunning
+
+# Close application. it returns application running state
+def closeAppTest(stub):
+ stub.closeApp(ReqCloseApp(packageName='org.tizen.example.NUITizenGallery'))
+ return stub.getAppInfo(ReqGetAppInfo(packageName='org.tizen.example.NUITizenGallery')).isRunning != True
+
+def defaultSetup(stub):
+ if stub.getAppInfo(ReqGetAppInfo(packageName='org.tizen.example.NUITizenGallery')).isRunning:
+ stub.closeApp(ReqCloseApp(packageName='org.tizen.example.NUITizenGallery'))
+
+ stub.launchApp(ReqLaunchApp(packageName='org.tizen.example.NUITizenGallery'))
+
+def defaultTearDown(stub):
+ stub.closeApp(ReqCloseApp(packageName='org.tizen.example.NUITizenGallery'))
+
+def runTest(stub, testFunc, setup=defaultSetup, tearDown=defaultTearDown, alwaySucceed=False):
+ print("Testing started :", testFunc)
+
+ setup(stub)
+ result = testFunc(stub)
+ tearDown(stub)
+
+ print("Testing result :", result)
+ if alwaySucceed: return True
+
+def runTestWithoutSetupAndTearDown(stub, testFunc, setup=defaultSetup, tearDown=defaultTearDown):
+ def Empty(stub):
+ pass
+
+ runTest(stub, testFunc, Empty, Empty)
+
+def run():
+ with grpc.insecure_channel('127.0.0.1:50051') as channel:
+ stub = BootstrapStub(channel)
+ # Picker Test
+ runTestWithoutSetupAndTearDown(stub, launchAppTest)
+ runTestWithoutSetupAndTearDown(stub, PickerExecuteTestWithText)
+ runTestWithoutSetupAndTearDown(stub, PickerScrollTest)
+ runTestWithoutSetupAndTearDown(stub, closeAppTest)
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ run()
--- /dev/null
+from __future__ import print_function
+import os
+import sys
+sys.path.append(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
+from aurum_pb2 import *
+from aurum_pb2_grpc import BootstrapStub
+import logging
+import grpc
+import time
+
+# Second view size change and check
+# Please refer key codes below page
+# https://code.sec.samsung.net/confluence/display/GFX/VD+Key+Code+Table
+def MultiViewSizeTest(stub):
+ response = stub.findElement(ReqFindElement(textField='VSComponent2'))
+ if len(response.elements) <= 0: return False
+
+ responseGuide = stub.findElement(ReqFindElement(textField='Guide TextBox'))
+ if len(response.elements) <= 0:
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Return'))
+
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Right'))
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Up'))
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Return'))
+ # Wait until render finished
+ time.sleep(1)
+
+ responseAfter = stub.findElement(ReqFindElement(textField='VSComponent2'))
+ if len(responseAfter.elements) <= 0: return False
+
+ if response.elements[0].geometry.width < responseAfter.elements[0].geometry.width:
+ return True
+
+ return False
+
+# Launch 3rd-party app and long press back key test
+def MultiViewContentsTest(stub):
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Return'))
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Left'))
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Up'))
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='Return'))
+ # Wait until render finished
+ time.sleep(10)
+
+ # It fails if there is a View
+ response= stub.findElement(ReqFindElement(textField='VSComponent2'))
+ if len(response.elements) > 0: return False
+
+ stub.sendKey(ReqKey(type='XF86', actionType='LONG_STROKE', XF86keyCode='XF86Back'))
+
+ return True
+
+# Launch application. it returns application running state
+def launchAppTest(stub):
+ stub.launchApp(ReqLaunchApp(packageName='com.samsung.tv.multiscreen'))
+ time.sleep(5)
+ return stub.getAppInfo(ReqGetAppInfo(packageName='com.samsung.tv.multiscreen')).isRunning
+
+# Close application. it returns application running state
+def closeAppTest(stub):
+ stub.closeApp(ReqCloseApp(packageName='com.samsung.tv.multiscreen'))
+ return stub.getAppInfo(ReqGetAppInfo(packageName='com.samsung.tv.multiscreen')).isRunning != True
+
+def runTest(stub, testFunc):
+ print("Testing started :", testFunc)
+
+ result = testFunc(stub)
+
+ print("Testing result :", result)
+ return True
+
+def run():
+ with grpc.insecure_channel('127.0.0.1:50051') as channel:
+ stub = BootstrapStub(channel)
+ runTest(stub, launchAppTest)
+ runTest(stub, MultiViewSizeTest)
+ runTest(stub, MultiViewContentsTest)
+ runTest(stub, closeAppTest)
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ run()
--- /dev/null
+from __future__ import print_function
+import os
+import sys
+sys.path.append(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
+from aurum_pb2 import *
+from aurum_pb2_grpc import BootstrapStub
+import logging
+import grpc
+import time
+
+# Find TextField and input "Movie" text
+def SearchTestWithText(stub):
+ response = stub.findElement(ReqFindElement(widgetType='TextField'))
+ if len(response.elements) <= 0: return False
+ targetObj = response.elements[0].elementId
+ testString = 'Movie'
+ stub.setValue(ReqSetValue(elementId=targetObj, stringValue=testString))
+
+ # Wait until result upload
+ time.sleep(2)
+
+ return True
+
+# Find Foused item and move focus to right then compare focused item with previous one
+def SearchFocusedObject(stub):
+ response = stub.findElement(ReqFindElement(isFocused=True))
+ if len(response.elements) <= 0: return False
+
+ prevObj = response.elements[0].elementId
+ stub.sendKey(ReqKey(type='XF86', actionType='LONG_STROKE', XF86keyCode='Right'))
+ time.sleep(1)
+
+ response = stub.findElement(ReqFindElement(isFocused=True))
+ if len(response.elements) <= 0: return False
+
+ if prevObj != response.elements[0].elementId:
+ return True
+
+ return False
+
+
+# Launch application. it returns application running state
+def launchAppTest(stub):
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='XF86Search'))
+ # Wait until app launch
+ time.sleep(5)
+ return stub.getAppInfo(ReqGetAppInfo(packageName='com.samsung.tv.searchall')).isRunning
+
+# Close application. it returns application running state
+def closeAppTest(stub):
+ stub.sendKey(ReqKey(type='XF86', actionType='STROKE', XF86keyCode='XF86Exit'))
+ time.sleep(2)
+ return stub.getAppInfo(ReqGetAppInfo(packageName='com.samsung.tv.searchall')).isRunning != True
+
+def runTest(stub, testFunc):
+ print("Testing started :", testFunc)
+
+ result = testFunc(stub)
+
+ print("Testing result :", result)
+ return True
+
+def run():
+ with grpc.insecure_channel('127.0.0.1:50051') as channel:
+ stub = BootstrapStub(channel)
+ runTest(stub, launchAppTest)
+ runTest(stub, SearchTestWithText)
+ runTest(stub, SearchFocusedObject)
+ runTest(stub, closeAppTest)
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ run()
--- /dev/null
+import os
+import subprocess
+import re
+import sys
+import time
+
+def run_command(command):
+ stream = os.popen(command)
+ output = stream.read()
+
+# Start scrip here
+run_command("sdb root on")
+# To run bootstrap as command
+run_command("sdb shell tpk-backend -y org.tizen.aurum-bootstrap --preload")
+# TV need to enable touch
+run_command("sdb shell vconftool set -f -t bool memory/window_system/input/force_enable_touch 1")
+# Dut to input generator issue, temporarily do below command
+run_command("sdb shell winfo -init_device --type=touch")
+run_command("sdb forward tcp:50051 tcp:50051")
+run_command("sdb shell app_launcher -s org.tizen.aurum-bootstrap")
+# Wait 1 sec till bootstrap launched
+time.sleep(1)
--- /dev/null
+python3 -m venv v
+
+source v/bin/activate
+
+pip3 install -r ../../../protocol/resources/python/requirements.txt
+
+python3 -m grpc_tools.protoc --python_out=./ --grpc_python_out=./ -I ./../../../protocol/ ../../../protocol/aurum.proto
+
+python3 ../../../protocol/resources/python/mobile/mobileSetup.py
--- /dev/null
+python3 -m venv v
+
+source v/bin/activate
+
+pip3 install -r ../../../protocol/resources/python/requirements.txt
+
+python3 -m grpc_tools.protoc --python_out=./ --grpc_python_out=./ -I ./../../../protocol/ ../../../protocol/aurum.proto
+
+python3 ../../../protocol/resources/python/tv/tvSetup.py