def main():
    device, serialno = ViewClient.connectToDeviceOrExit(serialno=None)
    vc = ViewClient(device, serialno)
    views_dict = vc.getViewsById()

    print(
        "-------- Show the detail in the returned by the API ViewClient.getViewsById() --------"
    )
    for key, value in views_dict.items():
        print("{}:\n{}\n".format(key, unicode(value).encode("UTF-8")))

    views = filter(lambda v: len(v.getId()) > 0, views_dict.values())
    id_view_dict = {}
    for v in views:
        if v.getId() in id_view_dict.keys():
            id_view_dict[v.getId()].append(v)
        else:
            id_view_dict[v.getId()] = [v]

    print("\n")
    print("-------- Print the id-to-view pairs --------")
    for key, value in id_view_dict.items():
        for each in value:
            print("{}:\n{}\n".format(key, unicode(each).encode("UTF-8")))

    vc.traverse()

    pass
Ejemplo n.º 2
0
def run():
    AudioFunction.init()
    Logger.init(Logger.Mode.STDOUT)
    Adb.init()

    package = "com.htc.audiofunctionsdemo"
    activity = ".activities.MainActivity"
    component = package + "/" + activity

    device, serialno = ViewClient.connectToDeviceOrExit()
    vc = ViewClient(device, serialno, autodump=False)

    if not device.isScreenOn():
        device.wake()

    vc.dump()

    import StringIO as sio
    so = sio.StringIO()
    vc.traverse(stream=so)
    if "lockscreen" in so.getvalue():
        device.unlock()

    # keymap reference:
    #   https://github.com/dtmilano/AndroidViewClient/blob/master/src/com/dtmilano/android/adb/androidkeymap.py
    device.press("HOME")
    time.sleep(1)
    device.startActivity(component=component)
    time.sleep(1)

    playback_task_run(device)
    record_task_run(device, serialno)

    AudioFunction.finalize()
    Logger.finalize()
    def get_state(self):
        vc = ViewClient(self.device, self.serialno)
        so = sio.StringIO()
        vc.traverse(stream=so)
        states = [state for state in GoogleMusicApp.State.ALL_STATES if state["check"](so.getvalue())]
        if len(states) > 1:
            self.push_dump("get_state returns more than one states: [{}]".format( \
                    ", ".join(map(lambda state: "'{}'".format(state["name"]), states)) \
                ))
        if len(states) > 0:
            return states[0]["name"]

        return GoogleMusicApp.State.UNKNOWN
Ejemplo n.º 4
0
def wake_device(device, serialno):
    if device.isScreenOn():
        return

    device.wake()
    vc = ViewClient(device, serialno, autodump=False)
    try:
        vc.dump(sleep=0)
        so = sio.StringIO()
        vc.traverse(stream=so)

        if "lockscreen" in so.getvalue():
            device.unlock()
    except:
        pass
 def get_current_song(self):
     vc = ViewClient(self.handler.device, self.handler.serialno)
     so = sio.StringIO()
     vc.traverse(stream=so)
     line = [line for line in so.getvalue().splitlines() if GoogleMusicApp.CONTROL_PANEL_TRACKNAME_KEY in line]
     line = line[0] if len(line) > 0 else None
     if line:
         name = utf8str(line.split(GoogleMusicApp.CONTROL_PANEL_TRACKNAME_KEY)[-1].strip())
         for playcard_title, info in self.handler.cache["playcard"].items():
             if "songs" in info.keys() and name in info["songs"].keys():
                 song = dict(info["songs"][name])
                 song["name"] = name
                 song["playcard"] = playcard_title
                 return song
     return None
    def testViewClient_oneDevice_TwoViewClients(self):
        localPort1 = 9005
        remotePort1 = 9006
        print "Conencting to", remotePort1
        vc1 = ViewClient(device=ViewClientTest.device1, serialno=ViewClientTest.serialno1,
                         localport=localPort1, remoteport=remotePort1, autodump=True)
        self.assertTrue(vc1.getRoot() != None)
        vc1.traverse()

        localPort2 = 9007
        remotePort2 = 9008
        print "Conencting to", remotePort2
        vc2 = ViewClient(device=ViewClientTest.device2, serialno=ViewClientTest.serialno2,
                         localport=localPort2, remoteport=remotePort2, autodump=True)
        self.assertTrue(vc2.getRoot() != None)
        vc2.traverse()
Ejemplo n.º 7
0
    def testViewClient_oneDevice_TwoViewClients(self):
        localPort1 = 9005
        remotePort1 = 9006
        print "Conencting to", remotePort1
        vc1 = ViewClient(device=ViewClientTest.device1,
                         serialno=ViewClientTest.serialno1,
                         localport=localPort1,
                         remoteport=remotePort1,
                         autodump=True)
        self.assertTrue(vc1.getRoot() != None)
        vc1.traverse()

        localPort2 = 9007
        remotePort2 = 9008
        print "Conencting to", remotePort2
        vc2 = ViewClient(device=ViewClientTest.device2,
                         serialno=ViewClientTest.serialno2,
                         localport=localPort2,
                         remoteport=remotePort2,
                         autodump=True)
        self.assertTrue(vc2.getRoot() != None)
        vc2.traverse()
    pass
from com.dtmilano.android.viewclient import ViewClient, View

from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice

device, serialno = ViewClient.connectToDeviceOrExit()

DEBUG = True
FLAG_ACTIVITY_NEW_TASK = 0x10000000
# We are not using Settings as the bug describes because there's no WiFi dialog in emulator
componentName = 'com.android.settings/.Settings'
device.startActivity(component=componentName, flags=FLAG_ACTIVITY_NEW_TASK)
MonkeyRunner.sleep(3)

vc = ViewClient(device=device, serialno=serialno)
if DEBUG: vc.traverse(transform=ViewClient.TRAVERSE_CIT)
sound = vc.findViewWithText('Sound')
if sound:
    sound.touch()
    vc.dump()
    phoneRingtone = vc.findViewWithText('Phone ringtone')
    if phoneRingtone:
        phoneRingtone.touch()
        vc.dump()
        vespa = vc.findViewWithText('Vespa')
        if vespa:
            vespa.touch()
        MonkeyRunner.sleep(1)
        ok = vc.findViewById('id/button1')
        if ok:
            ok.touch()
#! /usr/bin/env python

'''
Copyright (C) 2014  Diego Torres Milano
Created on Apr 24, 2014

@author: diego
'''

from com.dtmilano.android.viewclient import ViewClient

kwargs1 = {'verbose': True, 'ignoresecuredevice': True}
kwargs2 = {'startviewserver': True, 'forceviewserveruse': True, 'autodump': False, 'ignoreuiautomatorkilled': True}
vc = ViewClient(*ViewClient.connectToDeviceOrExit(**kwargs1), **kwargs2)
windows = vc.list()
for wId in windows.keys():
    print ">>> window=", wId, windows[wId]
    vc.dump(window=wId)
    vc.traverse(transform=ViewClient.TRAVERSE_CIT, indent="    ")
Ejemplo n.º 10
0

import re
import sys
import os

# This must be imported before MonkeyRunner and MonkeyDevice,
# otherwise the import fails.
# PyDev sets PYTHONPATH, use it
try:
    for p in os.environ['PYTHONPATH'].split(':'):
        if not p in sys.path:
            sys.path.append(p)
except:
    pass
    
try:
    sys.path.append(os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass

from com.dtmilano.android.viewclient import ViewClient

from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
  
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device, serialno)
''' % date.today()

vc.traverse(transform=transform)
except:
    pass
from com.dtmilano.android.viewclient import ViewClient, View


device, serialno = ViewClient.connectToDeviceOrExit()

DEBUG = True
FLAG_ACTIVITY_NEW_TASK = 0x10000000
# We are not using Settings as the bug describes because there's no WiFi dialog in emulator
componentName = 'com.android.settings/.Settings'
device.startActivity(component=componentName, flags=FLAG_ACTIVITY_NEW_TASK)
ViewClient.sleep(3)

vc = ViewClient(device=device, serialno=serialno)
if DEBUG: vc.traverse(transform=ViewClient.TRAVERSE_CIT)
sound = vc.findViewWithText('Sound')
if sound:
    sound.touch()
    vc.dump()
    phoneRingtone = vc.findViewWithText('Phone ringtone')
    if phoneRingtone:
        phoneRingtone.touch()
        vc.dump()
        vespa = vc.findViewWithText('Vespa')
        if vespa:
            vespa.touch()
        ViewClient.sleep(1)
        ok = vc.findViewById('id/button1')
        if ok:
            ok.touch()
Ejemplo n.º 12
0
    def walk_through(self):
        if not self.to_top():
            Logger.log("GoogleMusicApp", "walk_through failed: unable to go to top activity")
            self.cache_init = False
            return False

        # Get the playcard titles
        vc = ViewClient(self.device, self.serialno)

        self.cache_init = True

        container_key = GoogleMusicApp.CONTAINER_KEY
        container = [v for v in vc.getViewsById().values() if v.getId() == container_key]
        container = container[0] if len(container) > 0 else None
        if container:
            self.cache["screen-info"] = container.getBounds()[1]
            self.push_dump("screen-info: {}".format(self.cache["screen-info"]))

        so = sio.StringIO()
        vc.traverse(stream=so)
        lines = so.getvalue().splitlines()
        play_card_key = GoogleMusicApp.PLAY_CARD_KEY
        playcards_idices = [idx for idx, line in enumerate(lines) if play_card_key in line]
        playcards_idices.append(len(lines))
        playcards_titles = []
        last_idx = playcards_idices[0]

        li_title_key = GoogleMusicApp.LI_TITLE_KEY
        for idx in playcards_idices[1:]:
            li_title_texts = [line for line in lines[last_idx:idx] if li_title_key in line]
            last_idx = idx

            if len(li_title_texts) != 1:
                self.push_dump("li_title_texts has length {}".format(len(li_title_texts)))

            playcards_titles.append(utf8str(li_title_texts[0].split(li_title_key)[-1].strip()))
            self.push_dump("playcards_titles.append('{}')".format(playcards_titles[-1]))

        # Get the track list of each playcard
        views = [v for v in vc.getViewsById().values() if v.getId() == li_title_key and utf8str(v.getText()) in playcards_titles]
        self.cache["playcard"] = dict( \
                map(lambda v: (utf8str(v.getText()), { "position": v.getCenter() }), views)
            )
        map(lambda v: self.push_dump("view: {}".format(utf8str(v))), views)
        map(lambda title: self.push_dump("playcard title: '{}'".format(title)), self.cache["playcard"].keys())

        if len(views) == 0:
            self.cache_init = False
            return False

        self.cache["shuffle_key"] = playcards_titles[0]
        self.push_dump("get the shuffle keyword '{}'".format(self.cache["shuffle_key"]))
        self.touch_playcard(self.cache["shuffle_key"])
        time.sleep(1)

        retry_count = 3
        while retry_count > 0:
            vc.dump()
            play_pause_header_key = GoogleMusicApp.PLAY_PAUSE_HEADER_KEY
            play_pause_btn_view = [v for v in vc.getViewsById().values() if v.getId() == play_pause_header_key]
            play_pause_btn_view = play_pause_btn_view[0] if len(play_pause_btn_view) > 0 else None
            if play_pause_btn_view:
                play_desc = utf8str(play_pause_btn_view.getContentDescription())
                self.check_play_status = lambda desc: desc == play_desc
                self.cache["play_pause_btn"] = { "position": play_pause_btn_view.getCenter(), "desc_feat": play_desc }

                art_pager_key = GoogleMusicApp.ART_PAGER_KEY
                art_pager_view = [v for v in vc.getViewsById().values() if v.getId() == art_pager_key]
                art_pager_view = art_pager_view[0] if len(art_pager_view) > 0 else None
                if not art_pager_view:
                    retry_count -= 1
                    continue
                self.cache["art_pager_view"] = { "position": art_pager_view.getCenter() }

                play_pause_btn_view.touch()
                break
            else:
                self.push_dump("cannot find the play/pause button, retry: {}".format(retry_count))
                retry_count -= 1

        if retry_count == 0:
            return False

        for li_title in self.cache["playcard"].keys():
            if li_title == self.cache["shuffle_key"]:
                continue
            self.push_dump("now fetching information in the playcard '{}'".format(li_title))
            if self.touch_playcard(li_title=li_title):
                time.sleep(1)
                self.cache["playcard"][li_title]["songs"] = self._fetch_songs()
                self.to_top()

        # Get the information of the control panel
        retry_count = 3
        while self.get_state() != GoogleMusicApp.State.CONTROL_PANEL and retry_count > 0:
            self.device.touch(*self.cache["art_pager_view"]["position"])
            retry_count -= 1

        if retry_count == 0 and self.get_state() != GoogleMusicApp.State.CONTROL_PANEL:
            self.to_top()
            time.sleep(5)
            self.touch_playcard(self.cache["shuffle_key"])
            time.sleep(2)
            self.device.touch(*self.cache["play_pause_btn"]["position"])
            time.sleep(2)
            self.device.touch(*self.cache["art_pager_view"]["position"])
            time.sleep(2)
            if self.get_state() != GoogleMusicApp.State.CONTROL_PANEL:
                self.push_dump("cannot get the information of the control panel")
                self.cache_init = False
                return False

        def find_view_position(vc, res_id):
            v = [v for v in vc.getViewsById().values() if v.getId() == res_id]
            if len(v) == 0:
                return ((-1, -1), (-1, -1)), (-1, -1)
            return v[0].getBounds(), v[0].getCenter()

        vc.dump()
        progress_bounds, progress_pos = find_view_position(vc, GoogleMusicApp.CONTROL_PANEL_PROGRESS_KEY)
        self.cache["control_panel"] = {
            "progress": { "position": progress_pos, "xbounds": [progress_bounds[0][0], progress_bounds[1][0]] },
            "prev": { "position": find_view_position(vc, GoogleMusicApp.CONTROL_PANEL_PREV_KEY)[1] },
            "next": { "position": find_view_position(vc, GoogleMusicApp.CONTROL_PANEL_NEXT_KEY)[1] },
            "play_pause": { "position": find_view_position(vc, GoogleMusicApp.CONTROL_PANEL_PLAY_PAUSE_KEY)[1] }
        }
        self.control_panel = GMControlPanel(self)
        self.push_dump("successfully walked through, now back to top")
        self.to_top()

        self.cache_init = True
        return True
Ejemplo n.º 13
0
def loadScreenshots():
    # Read all chats in list
    device, serialno = ViewClient.connectToDeviceOrExit()
    device.press('KEYCODE_HOME')
    device.startActivity(component=component)
    ViewClient.sleep(TOUCH_LONG_SLEEP)
    vc = ViewClient(device, serialno, autodump=True)
    putMainScreen(vc)
    toStart(vc)  ##coments if necessary continue a extraction
    vc.dump()
    new_chats = True
    while new_chats:  #check screen changes (in chat list) after dragging

        # track the chat list beginning.
        brute_chatList = vc.findViewsWithAttribute('class',
                                                   'android.view.ViewGroup')

        if brute_chatList[0] is None:
            logging.error('Cant go back to Facebook Home')
            quit()

        # capture new chats in screen list
        chatList = []
        new_chats = False

        for c in brute_chatList[0].children:
            if len(c.getText()) > 0 and c.getText(
            ) != 'Online' and not c.getContentDescription() in visitedChats:
                print c.getContentDescription().encode('utf-8')
                chatList.append(c)
                new_chats = True
        print "new Chats:(", len(chatList), ")", 'total ', len(visitedChats)
        # process new chats
        for chat in chatList:
            print '->' + chat.getContentDescription().encode(
                'utf-8'), chat.getTag(), chat.getUniqueId()
            path = ''
            if isinstance(chat.getContentDescription(), unicode):
                path = unicode(
                    extraction_path + '/' +
                    norm_unicode_filename(chat.getContentDescription()))
            if isinstance(chat.getContentDescription(), str):
                path = extraction_path + '/' + werkzeug.utils.secure_filename(
                    chat.getContentDescription().encode('utf-8'))

            os.mkdir(path, 0777)

            device, serialno = ViewClient.connectToDeviceOrExit()
            vc = ViewClient(device, serialno)

            chat.touch()
            #print 'touching...'
            if vc.isKeyboardShown():
                device.press('KEYCODE_BACK')
            root = vc.findViewsWithAttribute('class', 'android.view.ViewGroup')
            #print "Grupo:", len(root), root[0].getContentDescription(), root[0].getText()
            vc = ViewClient(device, serialno)

            # snapshot screen
            screenshot_count = 1
            before_dump = ''
            strScreen = StringIO.StringIO()
            vc.traverse(transform=ViewClient.TRAVERSE_CITPS, stream=strScreen)
            after_dump = strScreen.getvalue()
            while before_dump != after_dump:  #check screen changes (in msgs list) after dragging
                before_dump = after_dump
                print 'screenshot', screenshot_count
                device.takeSnapshot().save(
                    path + '/screenshot_' + str(screenshot_count) + ".png",
                    'PNG')
                device, serialno = ViewClient.connectToDeviceOrExit()
                vc = ViewClient(device, serialno)
                #print 'connected?',device.checkConnected()
                device.dragDip((169.0, 297.0), (173.0, 600.0), 1000, 20, 0)

                attemptCount = 0
                while attemptCount < 5:
                    try:
                        attemptCount = attemptCount + 1
                        vc = ViewClient(device, serialno)
                        break
                    except:
                        print 'Houston...we have a problem (BEEP) - small drag tilt'
                        device.dragDip((169.0, 297.0), (173.0, 310.0), 1000,
                                       20, 0)
                if attemptCount == 5:
                    print 'ERROR'
                    exit(1)
                strScreen = StringIO.StringIO()
                vc.traverse(transform=ViewClient.TRAVERSE_CITPS,
                            stream=strScreen)
                after_dump = strScreen.getvalue()
                screenshot_count = screenshot_count + 1
            visitedChats.append(chat.getContentDescription())
            putMainScreen(vc)
            #device.press('KEYCODE_BACK');
        # drag chat list
        device.dragDip((173.0, 560.0), (169.0, 150.0), 1000, 20, 0)
        vc = ViewClient(device, serialno)
        #Am i in FBM home?
        print 'put main screen'
        putMainScreen(vc)

    print 'Total chats:', len(visitedChats)
Ejemplo n.º 14
0
#! /usr/bin/env python

import sys
import os

try:
    sys.path.insert(
        0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass

from com.dtmilano.android.viewclient import ViewClient
from com.dtmilano.android.viewclient import View

vc = ViewClient(*ViewClient.connectToDeviceOrExit())

vc.traverse()
        options[SCREEN_GLARE] = True
    elif o in ['h', USE_UIAUTOMATOR_HELPER]:
        kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER] = True
    elif o in ['D', DO_NOT_DUMP_VIEWS]:
        options[DO_NOT_DUMP_VIEWS] = True
        transform = MAP[o]
    elif o in ['X', DEBUG]:
        kwargs2[ViewClientOptions.DEBUG] = debugArgsToDict(a)
    else:
        transform = MAP[o]

if options[DO_NOT_DUMP_VIEWS]:
    transform = MAP[DO_NOT_DUMP_VIEWS]

vc = ViewClient(*ViewClient.connectToDeviceOrExit(**kwargs1), **kwargs2)
if options[SAVE_SCREENSHOT]:
    vc.device.reconnect = True #(not options[DO_NOT_DUMP_VIEWS])
    ext = os.path.splitext(options[SAVE_SCREENSHOT])[1][1:].upper()
    _format = ext
    if ext == 'JPG':
        _format = 'JPEG'
    vc.writeImageToFile(options[SAVE_SCREENSHOT], _format=_format, deviceart=options[DEVICE_ART], dropshadow=options[DROP_SHADOW], screenglare=options[SCREEN_GLARE])
if not options[DO_NOT_DUMP_VIEWS] or options[SAVE_VIEW_SCREENSHOTS]:
    vc.dump(window=options[WINDOW])
    ViewClient.imageDirectory = options[SAVE_VIEW_SCREENSHOTS]
    vc.traverse(transform=transform)
if kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER]:
    try:
        vc.uiAutomatorHelper.quit()
    except:
        pass
Ejemplo n.º 16
0
class YkspTestCase(unittest.TestCase):

    package = None

    serial = None

    dirRoot = None

    logsFilename = None

    screenshotFolder = None

    screendumpFolder = None

    screenCount = 0

    def setUp(self):
        # Connnect to device
        self.device, self.serialno = ViewClient.connectToDeviceOrExit(serialno=YkspTestCase.serial)

        # Wake device
        self.device.wake()

        # Create ViewClient instance
        self.vc = ViewClient(self.device, self.serialno, autodump=False)

    def tearDown(self):
        # Force-stop the app
        self.device.shell('am force-stop %s' % YkspTestCase.package)

    def launchApp(self, package=None):
        '''
        Launches an app as if from the launcher.

        @type package: str
        @param package: An optional parameter to specify an application to launch by its package name. If not provided, the application package name provided in the application manifest is used.
        '''
        if package is None:
            package = YkspTestCase.package

        # Launch application only if the package is installed
        if self.device.shell('pm path %s' % package):
            self.device.shell('monkey -p %s -c android.intent.category.LAUNCHER 1' % package)
        else:
            self.fail('Failed to launch application. %s is not installed on this device' % package)

    def refreshScreen(self, sleep=1):
        '''
        Updates the view tree. This method or saveScreen() must be called after each screen transition to keep the view tree in sync with the device screen.

        @type sleep: float
        @param sleep: An optional parameter to indicate the time to sleep before refreshing the screen. Defaults to one second.
        '''
        self.vc.dump(window=-1, sleep=sleep)

    def saveScreen(self, tag=None, sleep=1):
        '''
        Updates the view tree and saves to disk the screenshot and screendump of the device screen. This method or refreshScreen() must be called after each screen transition to keep the view tree in sync with the device screen.

        @type tag: str
        @param tag: The tag for this screen. This is appended to the filename.
        @type sleep: float
        @param sleep: An optional parameter to indicate the time to sleep before saving the screen. Defaults to one second.
        '''
        if sleep > 0:
            self.vc.sleep(sleep)

        filename = YkspTestCase.screenCount
        if tag:
            filename = '%s-%s' % (filename, tag)

        # Take a screenshot and save
        self.device.takeSnapshot(reconnect=True).save('%s/%s/%s.png' % (YkspTestCase.dirRoot, YkspTestCase.screenshotFolder, filename), 'PNG')

        # Take a screendump and save
        screendump = self.vc.dump(window=-1, sleep=0)
        screendumpStream = open('%s/%s/%s.txt' % (YkspTestCase.dirRoot, YkspTestCase.screendumpFolder, filename), 'w')
        self.vc.traverse(transform=self.vc.TRAVERSE_CITPS, stream=screendumpStream)
        screendumpStream.close()

        YkspTestCase.screenCount += 1

    @staticmethod
    def parseArgs(argv):
        try:
            opts, args = getopt.getopt(argv[1:], 'hp:s:r:l:m:n:', ['help', 'package=', 'serial=', 'root=', 'logs=', 'screenshots=', 'screendumps='])
        except getopt.GetoptError:
            YkspTestCase.usage(2)

        for opt, arg in opts:
            if opt in ('-h', '--help'):
                YkspTestCase.usage(2)
            elif opt in ('-p', '--package'):
                YkspTestCase.package = arg
                argv.remove(opt)
                argv.remove(arg)
            elif opt in ('-s', '--serial'):
                YkspTestCase.serial = arg
                argv.remove(opt)
                argv.remove(arg)
            elif opt in ('-r', '--root'):
                YkspTestCase.dirRoot = arg
                argv.remove(opt)
                argv.remove(arg)
            elif opt in ('-l', '--logs'):
                YkspTestCase.logsFilename = arg
                argv.remove(opt)
                argv.remove(arg)
            elif opt in ('-m', '--screenshots'):
                YkspTestCase.screenshotFolder = arg
                argv.remove(opt)
                argv.remove(arg)
            elif opt in ('-n', '--screendumps'):
                YkspTestCase.screendumpFolder = arg
                argv.remove(opt)
                argv.remove(arg)

        if YkspTestCase.package is None:
            print '\nError:'
            print '--package must be specified'
            YkspTestCase.usage(2)
        if YkspTestCase.serial is None:
            print '\nError:'
            print '--serial must be specified'
            YkspTestCase.usage(2)
        if YkspTestCase.dirRoot is None:
            print '\nError:'
            print '--root must be specified'
            YkspTestCase.usage(2)
        if YkspTestCase.screenshotFolder is None:
            print '\nError:'
            print '--screenshots must be specified'
            YkspTestCase.usage(2)
        if YkspTestCase.screendumpFolder is None:
            print '\nError:'
            print '--screendumps must be specified'
            YkspTestCase.usage(2)
        if os.path.isdir(YkspTestCase.dirRoot) is False:
            print '\nError:'
            print '--root specifies an invalid directory'
            YkspTestCase.usage(2)

    @staticmethod
    def usage(exitVal=0):
        print '\nUsage:'
        print '-h, --help                    OPTIONAL    print this help and exit'
        print '-p, --package <package>       REQUIRED    specify the package name of the application'
        print '-s, --serial <serial>         REQUIRED    specify the serial number of the device to run this test case'
        print '-r, --root <dir>              REQUIRED    specify the root directory to save the results of this test case'
        print '-l, --logs <dir>              OPTIONAL    specify the filename to save the PyUnit logs'
        print '-m, --screenshots <folder>    REQUIRED    specify the folder name to save the screenshots'
        print '-n, --screendumps <folder>    REQUIRED    specify the folder name to save the screendumps'
        sys.exit(exitVal)

    @staticmethod
    def main(argv):
        YkspTestCase.parseArgs(argv)

        # Create subdirectories
        dirScreenshot = '%s/%s' % (YkspTestCase.dirRoot, YkspTestCase.screenshotFolder)
        if os.path.isdir(dirScreenshot) is False:
            os.mkdir(dirScreenshot)
        dirScreendump = '%s/%s' % (YkspTestCase.dirRoot, YkspTestCase.screendumpFolder)
        if os.path.isdir(dirScreendump) is False:
            os.mkdir(dirScreendump)

        # Configure logging and execute test case
        if YkspTestCase.logsFilename:
            logsStream = open('%s/%s' % (YkspTestCase.dirRoot, YkspTestCase.logsFilename), 'w')
            runner = unittest.TextTestRunner(stream=logsStream, verbosity=2)
            unittest.main(testRunner=runner)
        else:
            unittest.main()