zoukankan      html  css  js  c++  java
  • ui automonkey

    来源:https://github.com/jonathanpenn/ui-auto-monkey

     

    // Copyright (c) 2013 Jonathan Penn (http://cocoamanifest.net/)

     

    // Permission is hereby granted, free of charge, to any person obtaining a copy

    // of this software and associated documentation files (the "Software"), to deal

    // in the Software without restriction, including without limitation the rights

    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

    // copies of the Software, and to permit persons to whom the Software is

    // furnished to do so, subject to the following conditions:

     

    // The above copyright notice and this permission notice shall be included in

    // all copies or substantial portions of the Software.

     

    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

    // THE SOFTWARE.

    var target = UIATarget.localTarget();

    var app = target.frontMostApp();

    var window = app.mainWindow();

    target.logElementTree();

     

    "use strict";

     

    var target = UIATarget.localTarget();

     

    function UIAutoMonkey() {

        

        this.config = {

            //run either by minutesToRun or numberOfEvents. Only one of these can set. (To use minutes you can use config.numberOfEvents = 0)

            //minutesToRun = 60 * 8; //sample to run for 8 hours.

            //checkTimeEvery = 60; //how often to check (in events) if minutesToRun has is used. 

        numberOfEvents: 1000,

        delayBetweenEvents: 0.03,    // In seconds

            

            /**

             * Sometimes the monkey can fall into UI Holes from which it it is hard to escape. The monkey may then spend an inordinate

             * amount of time in such holes, neglecting other parts of the application.

             *

             * For example, if a parent Window P has a large image

             * and clicking on the image opens a child window C from which one exits by tapping a small X on the top right, then until that small X is

             * tapped we will remain in C. conditionHandlers offer the developer the option to periodically recognize that we are in C and press the X.

             *

             * See buttonHandler.js for a specialized conditionHandler useful when a top level button can be used to escape from a UI hole.

             *

             * conditionHandlers are objects that respond to the following methods:

             *  isTrue(target, eventNumber): returns True if the condition is true given target and event number eventNumber.

             *  checkEvery(): How many events should pass before we check.

             *  handle(target, mainWindow) handle the condition.

             *  isExclusive() if true then if this condition's handler is invoked then processing subsequent conditions is skipped for this particular event. This

             * is usually set to true as it allows the condition to exit a UI hole and at that point there may be no point executing other conditions

             *  logStats() log statics using UIALogger;

             * condition handlers must have the following property

             *  statsHandleInvokedCount - the count of the number of times we were invoked

             */

            

        conditionHandlers: [],

            

            /**

             * Unfortunately if the application is not responsive "ANR", the monkey may not notice and continue to fire events not realizing that

             * the application is stuck. When run via continuous integration users may not notice that "successful" monkey runs in fact were in an 

             * ANR state.

             *

             * To deal with this the monkey supports ANR detection. Using an anrFingerprint function it periodically takes a fingerprint and if these

             * are identical for a specified interval then an ANR exception is thrown.

             *

             *

             */

        anrSettings: {

            //fingerprintFunction defaults to false which will disable ANR fingerprinting. Otherwise set to a function that will return

            //a string. One useful idiom using tuneup.js is

            //#import tuneup.js

            //config.anrSettings.fingerprintFunction = function() {return logVisibleElementTreeJSON(false)};

        fingerprintFunction: false,

        eventsBeforeANRDeclared: 1500, //throw exception if the fingerprint hasn't changed within this number of events

        eventsBetweenSnapshots: 150, //how often (in events) to take a snapshot using the fingerprintFunction 

        debug: false //if true extra logging is made

        },

            

            // If the following line is uncommented, then screenshots are taken

            // every "n" seconds.

            //screenshotInterval: 5,

            

            // Events are triggered based on the relative weights here. The event

            // with this highest number gets triggered the most.

            //

            // If you want to add your own "events", check out the event method

            // definitions below.

        eventWeights: {

        tap: 500,

        drag: 50,

        flick: 1,

        orientation: 1,

        clickVolumeUp: 1,

        clickVolumeDown: 1,

        lock: 1,

        pinchClose: 10,

        pinchOpen: 10,

        shake: 1

        },

            

            // Probability that touch events will have these different properties

        touchProbability: {

        multipleTaps: 0.05,

        multipleTouches: 0.05,

        longPress: 0.05

        },

            

            

            // Uncomment the following to restrict events to a rectangular area of

            // the screen

        frame: {

        origin: {x: 0,y: 25},

        size: { 200,height: 400}

        }      

            

        };

        

        // Dismiss alerts 

        UIATarget.onAlert = function onAlert(alert) {

            var title = alert.name();

            UIALogger.logMessage('On Alert: ' + title);

            return true;

        }

    }

     

    // --- --- --- ---

    // Event Methods

    //

    // Any event probability in the hash above corresponds to a related event

    // method below. So, a "tap" probability will trigger a "tap" method.

    //

    // If you want to add your own events, just add a probability to the hash

    // above and then add a corresponding method below. Boom!

    //

    // Each event method can call any other method on this UIAutoMonkey object.

    // All the methods at the end are helpers at your disposal and feel free to

    // add your own.

     

    UIAutoMonkey.prototype.allEvents = {

    tap: function() {

        this.target().tapWithOptions(

                                     { x: this.randomX(), y: this.randomY() },

                                     {

                                     tapCount: this.randomTapCount(),

                                     touchCount: this.randomTouchCount(),

                                     duration: this.randomTapDuration()

                                     }

                                     );

    },

        

    drag: function() {

        this.target().dragFromToForDuration(

                                            { x: this.randomX(), y: this.randomY() },

                                            { x: this.randomX(), y: this.randomY() },

                                            0.5

                                            );

    },

        

    flick: function() {

        this.target().flickFromTo(

                                  { x: this.randomX(), y: this.randomY() },

                                  { x: this.randomX(), y: this.randomY() }

                                  );

    },

        

    orientation: function() {

        var orientations = [

                            UIA_DEVICE_ORIENTATION_PORTRAIT,

                            UIA_DEVICE_ORIENTATION_PORTRAIT_UPSIDEDOWN,

                            UIA_DEVICE_ORIENTATION_LANDSCAPELEFT,

                            UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT

                            ];

        

        var i = Math.floor(Math.random() * 10) % orientations.length;

        var newOrientation = orientations[i];

        this.target().setDeviceOrientation(newOrientation);

        this.delay(0.9);

    },

        

    clickVolumeUp: function() {

        this.target().clickVolumeUp();

    },

        

    clickVolumeDown: function() {

        this.target().clickVolumeDown();

    },

        

        //lock: function() {

        //    this.target().lockForDuration(Math.random() * 3);

        //},

        

    pinchClose: function () {

        this.target().pinchCloseFromToForDuration(

                                                  { x: this.randomX(), y: this.randomY() },

                                                  { x: this.randomX(), y: this.randomY() },

                                                  0.5

                                                  );

    },

        

    pinchOpen: function () {

        this.target().pinchOpenFromToForDuration(

                                                 { x: this.randomX(), y: this.randomY() },

                                                 { x: this.randomX(), y: this.randomY() },

                                                 0.5

                                                 );

    },

        

    shake: function() {

        this.target().shake();

    }

    };

     

    // --- --- --- ---

    // Helper methods

    //

    UIAutoMonkey.prototype.RELEASE_THE_MONKEY = function() {

        // Called at the bottom of this script to, you know...

        //

        // RELEASE THE MONKEY!

        if (this.config.minutesToRun && this.config.numberOfEvents) {

            throw "invalid configuration. You cannot define both minutesToRun and numberOfEvents"

        }

        var conditionHandlers = this.config.conditionHandlers || []; //For legacy configs, if not present default to empty.

        var useConditionHandlers = conditionHandlers.length > 0;

        var checkTime = false;

        var localNumberOfEvents = this.config.numberOfEvents; //we may modify so we want to leave config untouched

        if (this.config.minutesToRun) {

            checkTime = true;

            localNumberOfEvents = 2000000000;

            var startTime = new Date().getTime();

            var checkTimeEvery = this.config.checkTimeEvery || 60; //number of events to pass before we check the time

        }

        //setup anr parameters as needed

        var anrFingerprintFunction = this.config.anrSettings ? this.config.anrSettings.fingerprintFunction : false; //handle legacy settings missing this

        if (anrFingerprintFunction) {

            this.anrSnapshot = "Initial snapshot-nothing should match this!!";

            this.anrSnapshotTakenAtIndex = -1;

            var anrEventsBetweenSnapshots = this.config.anrSettings.eventsBetweenSnapshots || 300;

            var anrDebug = this.config.anrSettings.debug;

            this.anrMaxElapsedCount = -1;

        } 

        

        var targetBundleId = this.target().frontMostApp().bundleID();

        for (var i = 0; i < localNumberOfEvents; i++) {

            if (checkTime && (i % checkTimeEvery == 0)) { //check the time if needed

                var currTime = new Date().getTime();

                var elapsedMinutes = (currTime-startTime) / 60000;

                if (elapsedMinutes >= this.config.minutesToRun) {

                    UIALogger.logDebug("Ending monkey after " + elapsedMinutes + " minutes run time.");

                    break;

                } else {

                    UIALogger.logDebug(this.config.minutesToRun - elapsedMinutes + " minutes left to run.")

                }

            }

            

            var currentBundleId = this.target().frontMostApp().bundleID();

            if (currentBundleId !== targetBundleId) {

                UIALogger.logDebug("Ending monkey because it went outside of the tested app ('" + currentBundleId + "')");

                break;

            }

            

            this.triggerRandomEvent();

            if (anrFingerprintFunction && (i % anrEventsBetweenSnapshots == 0)) this.anrCheck(i, anrFingerprintFunction, anrDebug);

            if (this.config.screenshotInterval) this.takeScreenShotIfItIsTime();

            if (useConditionHandlers) this.processConditionHandlers(conditionHandlers, i+1, this.target());

            this.delay();

        }

        // publish stats if warranted

        if (anrFingerprintFunction) {

            UIALogger.logDebug("ANR Statistics");

            UIALogger.logDebug("ANR max event count for identical fingerprint snapshots :: events before ANR declared: " + this.anrMaxElapsedCount + " :: " + this.config.anrSettings.eventsBeforeANRDeclared);

        }

        if (useConditionHandlers) {

            UIALogger.logDebug("ConditionHandler Statistics")

            conditionHandlers.forEach(function(aHandler) {aHandler.logStats()});

            conditionHandlers.sort(function(aHandler, bHandler) {return aHandler.statsHandleInvokedCount - bHandler.statsHandleInvokedCount});

            UIALogger.logDebug("sorted by HandleInvokedCount");

            conditionHandlers.forEach(function(aHandler) {UIALogger.logDebug(aHandler + ": " + aHandler.statsHandleInvokedCount)});

        }

    };

     

     

    UIAutoMonkey.prototype.anrCheck = function(i, fingerprintFunction, debugFlag){

        

        var newSnapshot = fingerprintFunction();

        if (newSnapshot != this.anrSnapshot) {

            //all is good, we're moving along

            if (debugFlag) UIALogger.logDebug("UIAutoMonkey:anrCheck(): snapshot != for event " + i);

            this.anrSnapshot = newSnapshot;

            this.anrSnapshotTakenAtIndex = i;

        } 

        else {

            //have a match

            //for how many counts?

            var elapsedCount = i - this.anrSnapshotTakenAtIndex;

            this.anrMaxElapsedCount = Math.max(this.anrMaxElapsedCount, elapsedCount);

            UIALogger.logDebug("UIAutoMonkey:anrCheck(): snapshot == with elapsed count=" + elapsedCount);

            if (elapsedCount > this.config.anrSettings.eventsBeforeANRDeclared) {

                UIALogger.logDebug("duplicate snapshot detected" + this.anrSnapshot);

                throw "anr exception-identical after " + elapsedCount + " events";

            };

        };

    };

     

     

    UIAutoMonkey.prototype.processConditionHandlers = function(conditionHandlers, eventNumberPlus1, target) {

        var mainWindow = target.frontMostApp().mainWindow(); //optimization to let handlers do less work. Assumes isTrue() doesn't alter the mainWindow.

        for (var i = 0; i < conditionHandlers.length; i++) {

            var aCondition = conditionHandlers[i];

            if ((eventNumberPlus1 % aCondition.checkEvery()) != 0) {

                continue; //not yet time to process aCondition.

            }

            try {

                UIATarget.localTarget().pushTimeout(0);

                var isConditionTrue = aCondition.isTrue(target, eventNumberPlus1, mainWindow);

            }

            finally {

                UIATarget.localTarget().popTimeout();

            }

            if (isConditionTrue) {

                aCondition.handle(target, mainWindow);

                if (aCondition.isExclusive()) {

                    break;

                } else {

                    mainWindow = target.frontMostApp().mainWindow(); //could be stale

                }

            };

        };

        

    };

     

    UIAutoMonkey.prototype.triggerRandomEvent = function() {

        var name = this.chooseEventName();

        console.log('outputeventname',name);

        // Find the event method based on the name of the event

        var event = this.allEvents[name];

        console.log('outputevent',event);

        if(typeof(event)!="undefined"){

         event.apply(this)   

        };

    };

     

    UIAutoMonkey.prototype.target = function() {

        // Return the local target.

        return UIATarget.localTarget();

    };

     

    UIAutoMonkey.prototype.delay = function(seconds) {

        // Delay the target by `seconds` (can be a fraction)

        // Defaults to setting in configuration

        seconds = seconds || this.config.delayBetweenEvents;

        this.target().delay(seconds);

    };

     

    UIAutoMonkey.prototype.chooseEventName = function() {

        // Randomly chooses an event name from the `eventsWeight` dictionary

        // based on the given weights.

        var calculatedEventWeights = [];

        var totalWeight = 0;

        var events = this.config.eventWeights;

        for (var event in events) {

            if (events.hasOwnProperty(event)) {

                calculatedEventWeights.push({

                                            weight: events[event]+totalWeight,

                                            event: event

                                            });

                totalWeight += events[event];

            }

        }

        

        var chosenWeight = Math.random() * 1000 % totalWeight;

        

        for (var i = 0; i < calculatedEventWeights.length; i++) {

            if (chosenWeight < calculatedEventWeights[i].weight) {

                return calculatedEventWeights[i].event;

            }

        }

        

        throw "No even was chosen!";

    };

     

    UIAutoMonkey.prototype.screenWidth = function() {

        // Need to adjust by one to stay within rectangle

        return this.target().rect().size.width - 1;

    };

     

    UIAutoMonkey.prototype.screenHeight = function() {

        // Need to adjust by one to stay within rectangle

        return this.target().rect().size.height - 1;

    };

     

    UIAutoMonkey.prototype.randomX = function() {

        var min, max;

        

        if (this.config.frame){

            // Limits coordinates to given frame if set in config

            min = this.config.frame.origin.x;

            max = this.config.frame.size.width + min;

        } else {

            // Returns a random X coordinate within the screen rectangle

            min = 0;

            max = this.screenWidth();

        }

        

        return Math.floor(Math.random() * (max - min) + min) + 1;

    };

     

    UIAutoMonkey.prototype.randomY = function() {

        var min, max;

        

        if (this.config.frame){

            // Limits coordinates to given frame if set in config

            min = this.config.frame.origin.y;

            max = this.config.frame.size.height + min;

        } else {

            // Returns a random Y coordinate within the screen rectangle

            min = 0;

            max = this.screenHeight();

        }

        

        return Math.floor(Math.random() * (max - min) + min) + 1;

    };

     

    UIAutoMonkey.prototype.randomTapCount = function() {

        // Calculates a tap count for tap events based on touch probabilities

        if (this.config.touchProbability.multipleTaps > Math.random()) {

            return Math.floor(Math.random() * 10) % 3 + 1;

        }

        else return 1;

    };

     

    UIAutoMonkey.prototype.randomTouchCount = function() {

        // Calculates a touch count for tap events based on touch probabilities

        if (this.config.touchProbability.multipleTouches > Math.random()) {

            return Math.floor(Math.random() * 10) % 3 + 1;

        }

        else return 1;

    };

     

    UIAutoMonkey.prototype.randomTapDuration = function() {

        // Calculates whether or not a tap should be a long press based on

        // touch probabilities

        if (this.config.touchProbability.longPress > Math.random()) {

            return 0.5;

        }

        else return 0;

    };

     

    UIAutoMonkey.prototype.randomRadians = function() {

        // Returns a random radian value

        return Math.random() * 10 % (3.14159 * 2);

    };

     

    UIAutoMonkey.prototype.takeScreenShotIfItIsTime = function() {

        var now = (new Date()).valueOf();

        if (!this._lastScreenshotTime) this._lastScreenshotTime = 0;

        

        if (now - this._lastScreenshotTime > this.config.screenshotInterval * 1000) {

            var filename = "monkey-" + (new Date()).toISOString().replace(/[:.]+/g, "-");

            this.target().captureScreenWithName(filename);

            this._lastScreenshotTime = now;

        }

    };

     

    // If you want to control when the monkey is released please follow the pattern in the SampleCustomization folder. In brief you want to set a global

    // as set in SetGlobals.js, but due to Apple's javascript implementation you cannot simply set it before you import UIAutoMonkey.js.

    //

    if (typeof UIAutoMonkeyClientWillReleaseTheMonkey == 'undefined' || !UIAutoMonkeyClientWillReleaseTheMonkey) {

        // the variable is not defined or it's defined and false

        UIALogger.logDebug("Releasing the monkey directly from UIAutoMonkey"); //explain why it was released to aid in problem resolution.

        (new UIAutoMonkey()).RELEASE_THE_MONKEY();

    }

  • 相关阅读:
    MapReduce学习总结之简介
    Hive Cli相关操作
    使用Hive UDF和GeoIP库为Hive加入IP识别功能
    Google Maps-IP地址的可视化查询
    hive多表联合查询(GroupLens->Users,Movies,Ratings表)
    云计算平台管理的三大利器Nagios、Ganglia和Splunk
    机器大数据也离不开Hadoop
    hive与hbase的整合
    hive优化之------控制hive任务中的map数和reduce数
    Hadoop管理员的十个最佳实践(转)
  • 原文地址:https://www.cnblogs.com/huangxiaoying/p/5543574.html
Copyright © 2011-2022 走看看