smart_monkey 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +17 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +77 -0
  6. data/Rakefile +57 -0
  7. data/Troubleshooting.md +61 -0
  8. data/VERSION +1 -0
  9. data/bin/smart_monkey +53 -0
  10. data/lib/bootstrap/css/bootstrap-responsive.css +1109 -0
  11. data/lib/bootstrap/css/bootstrap-responsive.min.css +9 -0
  12. data/lib/bootstrap/css/bootstrap.css +6167 -0
  13. data/lib/bootstrap/css/bootstrap.min.css +9 -0
  14. data/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
  15. data/lib/bootstrap/img/glyphicons-halflings.png +0 -0
  16. data/lib/bootstrap/js/bootstrap.js +2280 -0
  17. data/lib/bootstrap/js/bootstrap.min.js +6 -0
  18. data/lib/ios_device_log/deviceconsole +0 -0
  19. data/lib/smart_monkey.rb +2 -0
  20. data/lib/smart_monkey/command_helper.rb +71 -0
  21. data/lib/smart_monkey/monkey_runner.rb +549 -0
  22. data/lib/smart_monkey/templates/automation_result.xsl +61 -0
  23. data/lib/smart_monkey/templates/index.html.erb +77 -0
  24. data/lib/smart_monkey/templates/result.html.erb +110 -0
  25. data/lib/smart_monkey/templates/result_view.coffee +160 -0
  26. data/lib/smart_monkey/templates/result_view.js +250 -0
  27. data/lib/ui-auto-monkey/UIAutoMonkey.js +470 -0
  28. data/lib/ui-auto-monkey/custom.js +73 -0
  29. data/lib/ui-auto-monkey/handler/buttonHandler.js +111 -0
  30. data/lib/ui-auto-monkey/handler/wbScrollViewButtonHandler.js +114 -0
  31. data/lib/ui-auto-monkey/tuneup/LICENSE +20 -0
  32. data/lib/ui-auto-monkey/tuneup/assertions.js +402 -0
  33. data/lib/ui-auto-monkey/tuneup/image_asserter +26 -0
  34. data/lib/ui-auto-monkey/tuneup/image_assertion.js +65 -0
  35. data/lib/ui-auto-monkey/tuneup/image_assertion.rb +102 -0
  36. data/lib/ui-auto-monkey/tuneup/lang-ext.js +76 -0
  37. data/lib/ui-auto-monkey/tuneup/screen.js +11 -0
  38. data/lib/ui-auto-monkey/tuneup/test.js +71 -0
  39. data/lib/ui-auto-monkey/tuneup/test_runner/abbreviated_console_output.rb +38 -0
  40. data/lib/ui-auto-monkey/tuneup/test_runner/colored_console_output.rb +27 -0
  41. data/lib/ui-auto-monkey/tuneup/test_runner/console_output.rb +17 -0
  42. data/lib/ui-auto-monkey/tuneup/test_runner/preprocessor.rb +25 -0
  43. data/lib/ui-auto-monkey/tuneup/test_runner/run +343 -0
  44. data/lib/ui-auto-monkey/tuneup/test_runner/xunit_output.rb +114 -0
  45. data/lib/ui-auto-monkey/tuneup/tuneup.js +6 -0
  46. data/lib/ui-auto-monkey/tuneup/tuneup_js.podspec +52 -0
  47. data/lib/ui-auto-monkey/tuneup/uiautomation-ext.js +965 -0
  48. data/smart_monkey.gemspec +112 -0
  49. data/spec/spec_helper.rb +12 -0
  50. metadata +192 -0
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Bootstrap.js by @fat & @mdo
3
+ * Copyright 2012 Twitter, Inc.
4
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
5
+ */
6
+ !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('<div class="dropdown-backdrop"/>').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
Binary file
@@ -0,0 +1,2 @@
1
+ require 'smart_monkey/command_helper'
2
+ require 'smart_monkey/monkey_runner'
@@ -0,0 +1,71 @@
1
+ # coding: utf-8
2
+
3
+ module UIAutoMonkey
4
+ module CommandHelper
5
+ require 'open3'
6
+
7
+ def shell(cmds)
8
+ puts "Shell: #{cmds.inspect}"
9
+ Open3.popen3(*cmds) do |stdin, stdout, stderr|
10
+ stdin.close
11
+ return stdout.read
12
+ end
13
+ end
14
+
15
+ # def run_process(cmds)
16
+ # puts "Run: #{cmds.inspect}"
17
+ # Kernel.system(cmds[0], *cmds[1..-1])
18
+ # end
19
+
20
+ def relaunch_app(device,app)
21
+ `idevicedebug -u #{device} run #{app} >/dev/null 2>&1 &`
22
+ end
23
+
24
+ def run_process(cmds)
25
+ puts "Run: #{cmds.inspect}"
26
+ device = cmds[2]
27
+ app = cmds[-7]
28
+ Open3.popen3(*cmds) do |stdin, stdout, stderr, thread|
29
+ @tmpline = ""
30
+ stdin.close
31
+ app_hang_monitor_thread = Thread.start{
32
+ sleep 30
33
+ while true
34
+ current_line = @tmpline
35
+ sleep 30
36
+ after_sleep_line = @tmpline
37
+ if current_line == after_sleep_line
38
+ puts "WARN: Application go to background! Auto-Re-Launch app!"
39
+ relaunch_app(device, app)
40
+ end
41
+ end
42
+ }
43
+ instruments_stderr_thread = Thread.start{
44
+ stderr.each do |line|
45
+ puts line
46
+ end
47
+ }
48
+ stdout.each do |line|
49
+ @tmpline = line.strip
50
+ puts @tmpline
51
+ if @tmpline =~ /MonkeyTest finish/
52
+ app_hang_monitor_thread.kill
53
+ end
54
+ end
55
+ app_hang_monitor_thread.kill
56
+ instruments_stderr_thread.kill
57
+ end
58
+ end
59
+
60
+ def kill_all(process_name, signal=nil)
61
+ signal = signal ? "-#{signal}" : ''
62
+ # puts "killall #{signal} #{process_name}"
63
+ Kernel.system("killall #{signal} '#{process_name}' >/dev/null 2>&1")
64
+ end
65
+
66
+ def xcode_path
67
+ @xcode_path ||= shell(%w(xcode-select -print-path)).strip
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,549 @@
1
+ # coding: utf-8
2
+
3
+ module UIAutoMonkey
4
+ require 'fileutils'
5
+ require 'timeout'
6
+ require 'rexml/document'
7
+ require 'erubis'
8
+ require 'json'
9
+
10
+ class MonkeyRunner
11
+ TRACE_TEMPLATE='/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.xrplugin/Contents/Resources/Automation.tracetemplate'
12
+ RESULT_BASE_PATH = File.expand_path('smart_monkey_result')
13
+ INSTRUMENTS_TRACE_PATH = File.expand_path('*.trace')
14
+ TIME_STAP = Time.new.strftime("%Y%m%d%H%M%S")
15
+
16
+ include UIAutoMonkey::CommandHelper
17
+
18
+ def run(opts)
19
+ @options = opts
20
+ res_dir = @options[:result_base_dir] || RESULT_BASE_PATH
21
+
22
+ puts "INSTRUMENTS_TRACE_PATH : #{INSTRUMENTS_TRACE_PATH}"
23
+ puts "RESULT_BASE_PATH : #{res_dir}"
24
+
25
+ if @options[:show_config]
26
+ show_config
27
+ return true
28
+ elsif @options[:list_app]
29
+ list_app
30
+ return true
31
+ elsif @options[:list_devices]
32
+ list_devices
33
+ return true
34
+ elsif @options[:reset_iphone_simulator]
35
+ reset_iphone_simulator
36
+ return true
37
+ end
38
+ ###########
39
+ log @options.inspect
40
+ FileUtils.remove_dir(result_base_dir, true)
41
+ FileUtils.makedirs(result_base_dir)
42
+ generate_ui_auto_monkey
43
+ ###########
44
+ start_time = Time.new.strftime("%Y-%m-%d %H:%M:%S")
45
+ result_list = []
46
+ total_test_count.times do |times|
47
+ @times = times
48
+ setup_running
49
+ result = run_a_case
50
+ finish_running
51
+ result_list << result
52
+ end
53
+ create_index_html({
54
+ :start_time => start_time,
55
+ :end_time => Time.new.strftime("%Y-%m-%d %H:%M:%S"),
56
+ :result_list => result_list,
57
+ :ProductType => product_type(device),
58
+ :ProductVersion => product_version(device),
59
+ :UniqueDeviceID => device,
60
+ :DeviceName => device_name(device),
61
+ :Application => app_path
62
+ })
63
+ all_tests_ok?(result_list)
64
+ end
65
+
66
+ def setup_running
67
+ kill_all_need
68
+ # kill_all('iPhone Simulator')
69
+ FileUtils.remove_dir(result_dir, true)
70
+ ENV['UIARESULTSPATH'] = result_dir
71
+ @crashed = false
72
+ @no_run = false
73
+ @uia_trace = false
74
+ end
75
+
76
+ def run_a_case
77
+ log "=================================== Start Test (#{@times+1}/#{total_test_count}) ======================================="
78
+ FileUtils.makedirs(crash_save_dir(@times+1)) unless File.exists?(crash_save_dir(@times+1))
79
+ pull_crash_from_iphone(@times+1)
80
+ cr_list = crash_report_list(@times+1)
81
+ start_time = Time.now
82
+ watch_syslog do
83
+ begin
84
+ unless time_limit_sec.nil?
85
+ run_process(%W(instruments -w #{device} -l #{time_limit} -t #{TRACE_TEMPLATE} #{app_path} -e UIASCRIPT #{ui_custom_path} -e UIARESULTSPATH #{result_base_dir}))
86
+ else
87
+ run_process(%W(instruments -w #{device} -t #{TRACE_TEMPLATE} #{app_path} -e UIASCRIPT #{ui_custom_path} -e UIARESULTSPATH #{result_base_dir}))
88
+ end
89
+ rescue Timeout::Error
90
+ kill_all('instruments', '9')
91
+ end
92
+ end
93
+
94
+ pull_crash_from_iphone(@times+1)
95
+ new_cr_list = crash_report_list(@times+1)
96
+ # increase crash report?
97
+ diff_cr_list = new_cr_list - cr_list
98
+
99
+ if diff_cr_list.size > 0
100
+ @crashed = true
101
+ new_cr_name = File.basename(diff_cr_list[0]).gsub(/\.ips$/, '.crash')
102
+ new_cr_path = File.join(result_dir, new_cr_name)
103
+ log "Find new crash report: #{new_cr_path}"
104
+ if dsym_base_path != ''
105
+ puts "Symbolicating crash report..."
106
+ symbolicating_crash_report(diff_cr_list[0])
107
+ end
108
+ FileUtils.cp diff_cr_list[0], new_cr_path
109
+ end
110
+ # output result
111
+ create_result_html(parse_results)
112
+
113
+ {
114
+ :start_time => start_time,
115
+ :end_time => Time.now,
116
+ :times => @times,
117
+ :ok => !@crashed && !@no_run,
118
+ :crash => @crashed,
119
+ :result_dir => File.basename(result_history_dir(@times)),
120
+ :message => nil
121
+ }
122
+ end
123
+
124
+ def finish_running
125
+ kill_all_need
126
+ FileUtils.remove_dir(result_history_dir(@times), true)
127
+ FileUtils.remove_dir(crash_save_dir(@times+1), true)
128
+ FileUtils.move(result_dir, result_history_dir(@times))
129
+ if @options[:compress_rate]
130
+ compress_image(result_history_dir(@times))
131
+ end
132
+ rm_instruments_trace(INSTRUMENTS_TRACE_PATH)
133
+ kill_all('iPhone Simulator')
134
+ sleep 3
135
+ end
136
+
137
+ def create_index_html(result_hash)
138
+ er = Erubis::Eruby.new(File.read(template_path('index.html.erb')))
139
+ result_hash[:test_count] = result_hash[:result_list].size
140
+ result_hash[:ok_count] = result_hash[:result_list].select {|r| r[:ok]}.size
141
+ result_hash[:cr_count] = result_hash[:result_list].select {|r| r[:crash]}.size
142
+ result_hash[:nr_count] = result_hash[:test_count] - result_hash[:ok_count] - result_hash[:cr_count]
143
+ open("#{result_base_dir}/index.html", 'w') {|f| f.write er.result(result_hash)}
144
+ copy_html_resources
145
+ puts "Monkey Test Report:#{result_base_dir}/index.html"
146
+ end
147
+
148
+ def copy_html_resources
149
+ bootstrap_dir = File.expand_path('../../bootstrap', __FILE__)
150
+ FileUtils.copy("#{bootstrap_dir}/css/bootstrap.css", result_base_dir)
151
+ FileUtils.copy("#{bootstrap_dir}/js/bootstrap.js", result_base_dir)
152
+ end
153
+
154
+ def all_tests_ok?(result_list)
155
+ result_list.select {|r| !r[:ok]}.empty?
156
+ end
157
+
158
+ def show_config
159
+ puts File.read(config_custom_path)
160
+ end
161
+
162
+ def show_extend_javascript
163
+ if @options[:extend_javascript_path]
164
+ filename = @options[:extend_javascript_path]
165
+ return File.exist?(filename), filename
166
+ end
167
+ end
168
+
169
+ def list_app
170
+ puts "============For iPhone Simulator:"
171
+ puts find_apps('*.app').map{|n| File.basename n}.uniq.sort.join("\n")
172
+ puts "============For iPhone Device:"
173
+ if device
174
+ puts `ideviceinstaller -u #{device} -l`
175
+ end
176
+ end
177
+
178
+ def list_devices
179
+ puts devices.join("\n")
180
+ end
181
+
182
+ def log(msg)
183
+ puts msg
184
+ end
185
+
186
+ def reset_iphone_simulator
187
+ FileUtils.rm_rf("#{Dir.home}/Library/Application\ Support/iPhone\ Simulator/")
188
+ puts 'reset iPhone Simulator successful'
189
+ end
190
+
191
+ def total_test_count
192
+ (@options[:run_count] || 2)
193
+ end
194
+
195
+ def device
196
+ @options[:device] || (devices[0].strip.split("[")[1].delete "]")
197
+ end
198
+
199
+ def app_path
200
+ @app_path ||= find_app_path(@options)
201
+ end
202
+
203
+ def app_name
204
+ File.basename(app_path).gsub(/\.app$/, '')
205
+ end
206
+
207
+ def find_apps(app)
208
+ `"ls" -dt #{ENV['HOME']}/Library/Developer/Xcode/DerivedData/*/Build/Products/*/#{app}`.strip.split(/\n/)
209
+ end
210
+
211
+ def devices
212
+ `"instruments" -s devices`.strip.split(/\n/).drop(2)
213
+ end
214
+
215
+ def product_type(device)
216
+ product_hash={
217
+ "iPhone7,2"=>"iPhone 6",
218
+ "iPhone7,1"=>"iPhone 6 Plus",
219
+ "iPhone6,2"=>"iPhone 5S (CDMA)",
220
+ "iPhone6,1"=>"iPhone 5S (GSM)",
221
+ "iPhone5,4"=>"iPhone 5C (CDMA)",
222
+ "iPhone5,3"=>"iPhone 5C (GSM)",
223
+ "iPhone5,2"=>"iPhone 5",
224
+ "iPhone5,1"=>"iPhone 5",
225
+ "iPhone4,1"=>"iPhone 4S",
226
+ "iPhone3,2"=>"iPhone 4 - CDMA",
227
+ "iPhone3,1"=>"iPhone 4 - GSM",
228
+ "iPhone2,1"=>"iPhone 3GS",
229
+ "iPhone1,2"=>"iPhone 3G",
230
+ "iPhone1,1"=>"iPhone",
231
+ }
232
+ type=`ideviceinfo -u #{device} -k ProductType`.strip
233
+ product_hash[type]
234
+ end
235
+
236
+ def product_version(device)
237
+ `ideviceinfo -u #{device} -k ProductVersion`.strip
238
+ end
239
+
240
+ def device_name(device)
241
+ `ideviceinfo -u #{device} -k DeviceName`.strip
242
+ end
243
+
244
+ def compress_image(path)
245
+ puts 'Compress screenshot images...'
246
+ compress_rate = @options[:compress_rate]
247
+ # `find #{path} -name "*.png" -exec convert {} -resize 50% -sample 50% {} \\\;`
248
+ `mogrify -resize #{compress_rate} "#{path}/*.png"`
249
+ end
250
+
251
+ def kill_all_need
252
+ kill_all('instruments', '9')
253
+ kill_all('Instruments', '9')
254
+ kill_all('idevicedebug', '9')
255
+ end
256
+
257
+ def find_app_path(opts)
258
+ app_path = nil
259
+ if opts[:app_path].include?('/')
260
+ app_path = File.expand_path(opts[:app_path])
261
+ elsif opts[:app_path] =~ /\.app$/
262
+ apps = find_apps(opts[:app_path])
263
+ app_path = apps[0]
264
+ log "#{apps.size} apps are found, USE NEWEST APP: #{app_path}" if apps.size > 1
265
+ else
266
+ app_path = opts[:app_path]
267
+ log "BundleID was found: #{app_path}"
268
+ end
269
+
270
+ unless app_path
271
+ raise 'Invalid AppName'
272
+ end
273
+ app_path
274
+ end
275
+
276
+ def time_limit
277
+ time_limit_sec * 1000
278
+ end
279
+
280
+ def time_limit_sec
281
+ @options[:time_limit_sec]
282
+ end
283
+
284
+ def deviceconsole_original_path
285
+ File.expand_path('../../ios_device_log/deviceconsole', __FILE__)
286
+ end
287
+
288
+ def ui_auto_monkey_original_path
289
+ File.expand_path('../../ui-auto-monkey/UIAutoMonkey.js', __FILE__)
290
+ end
291
+
292
+ def ui_custom_original_path
293
+ File.expand_path('../../ui-auto-monkey/custom.js', __FILE__)
294
+ end
295
+
296
+ def ui_hole_handler_original_path
297
+ File.expand_path('../../ui-auto-monkey/handler', __FILE__)
298
+ end
299
+
300
+ def ui_tuneup_original_path
301
+ File.expand_path('../../ui-auto-monkey/tuneup', __FILE__)
302
+ end
303
+
304
+ def ui_auto_monkey_path
305
+ "#{result_base_dir}/UIAutoMonkey.js"
306
+ end
307
+
308
+ def ui_custom_path
309
+ "#{result_base_dir}/custom.js"
310
+ end
311
+
312
+ def dsym_base_path
313
+ @options[:dsym_file_path] || ""
314
+ end
315
+
316
+ def xcode_developer_path
317
+ `xcode-select --print-path`.strip
318
+ end
319
+
320
+ def xcode_path
321
+ `dirname #{xcode_developer_path}`.strip
322
+ end
323
+
324
+ def symbolicatecrash_base_path()
325
+ `find #{xcode_path} -name symbolicatecrash`.strip
326
+ end
327
+
328
+ def symbolicating_crash_report(crash_base_path)
329
+ `DEVELOPER_DIR=#{xcode_developer_path} #{symbolicatecrash_base_path} -o #{crash_base_path} #{crash_base_path} #{dsym_base_path};wait;`
330
+ end
331
+
332
+ def result_base_dir
333
+ File.join(@options[:result_base_dir] || RESULT_BASE_PATH, "report_#{TIME_STAP}")
334
+ end
335
+
336
+ def crash_save_dir(times)
337
+ "#{result_base_dir}/crash_#{times}"
338
+ end
339
+
340
+ def result_dir
341
+ "#{result_base_dir}/Run 1"
342
+ end
343
+
344
+ def result_history_dir(times)
345
+ "#{result_base_dir}/result_#{sprintf('%03d', times)}"
346
+ end
347
+
348
+ def crash_report_dir
349
+ "#{ENV['HOME']}/Library/Logs/DiagnosticReports"
350
+ end
351
+
352
+ def pull_crash_from_iphone(times)
353
+ `idevicecrashreport -u #{device} -e -k #{crash_save_dir(times)}`
354
+ end
355
+
356
+ def crash_report_list(times)
357
+ # ios version >7.0 => *.ips
358
+ `ls -t #{crash_save_dir(times)}/*.crash 2>&1;ls -t #{crash_save_dir(times)}/*.ips 2>&1;`.strip.split(/\n/)
359
+ # `ls -t #{crash_save_dir}/#{app_name}_*.crash`.strip.split(/\n/)
360
+ end
361
+
362
+ def rm_instruments_trace(traces)
363
+ `rm -rf #{traces}`
364
+ end
365
+
366
+ def grep_syslog
367
+ 'tail -n 0 -f /var/log/system.log'
368
+ end
369
+
370
+ def grep_ios_syslog
371
+ "#{deviceconsole_original_path} -u #{device}"
372
+ end
373
+
374
+ def console_log_path
375
+ "#{result_dir}/console.txt"
376
+ end
377
+
378
+ def uiautomation_xsl_path
379
+ File.expand_path("../templates/automation_result.xsl", __FILE__)
380
+ end
381
+
382
+ def template_path(name)
383
+ File.expand_path("../templates/#{name}", __FILE__)
384
+ end
385
+
386
+ def parse_uiautomation_plist
387
+ `xsltproc --output "#{result_dir}/uiautomation.html" #{uiautomation_xsl_path} "#{result_dir}/Automation Results.plist"`
388
+ end
389
+
390
+ def generate_ui_auto_monkey
391
+ # extend_javascript_flag, extend_javascript_path = show_extend_javascript
392
+ # orig = File.read(ui_custom_original_path)
393
+ # config = JSON.parse(File.read(config_json_path))
394
+ # replace_str = " this.config = #{JSON.pretty_generate(config, :indent => ' '*6)}; \n"
395
+ # js = replace_text(orig, replace_str, '__UIAutoMonkey Configuration Begin__', '__UIAutoMonkey Configuration End__')
396
+ # if extend_javascript_flag
397
+ # js = File.read(extend_javascript_path) + "\n" + js
398
+ # end
399
+ # File.open(ui_custom_path, 'w') {|f| f.write(js)}
400
+ FileUtils.copy(config_custom_path, result_base_dir)
401
+ FileUtils.copy(ui_auto_monkey_original_path, result_base_dir)
402
+ FileUtils.cp_r(ui_hole_handler_original_path, result_base_dir)
403
+ FileUtils.cp_r(ui_tuneup_original_path, result_base_dir)
404
+ # FileUtils.copy("#{bootstrap_dir}/js/bootstrap.js", result_base_dir)
405
+ end
406
+
407
+ def config_custom_path
408
+ @options[:custom_path] || ui_custom_original_path
409
+ end
410
+
411
+ def replace_text(orig, replace_str, marker_begin_line, marker_end_line)
412
+ results = []
413
+ status = 1
414
+ orig.each_line do |line|
415
+ if status == 1 && line =~ /#{marker_begin_line}/
416
+ status = 2
417
+ results << line
418
+ results << replace_str
419
+ elsif status == 2 && line =~/#{marker_end_line}/
420
+ status = 3
421
+ end
422
+ results << line unless status == 2
423
+ end
424
+ results.join('')
425
+ end
426
+
427
+ def parse_results
428
+ filename = "#{result_dir}/Automation Results.plist"
429
+ log_list = []
430
+ if File.exists?(filename)
431
+ doc = REXML::Document.new(open(filename))
432
+ doc.elements.each('plist/dict/array/dict') do |record|
433
+ ary = record.elements.to_a.map{|a| a.text}
434
+ log_list << Hash[*ary]
435
+ end
436
+ parse_uiautomation_plist
437
+ @uia_trace = true
438
+ end
439
+ @no_run = true if log_list.empty? || log_list[-1][MESSAGE] =~ /target application is not frontmost/
440
+ log_list
441
+ end
442
+
443
+ def create_result_html(log_list)
444
+ latest_list = LogDecoder.new(log_list).decode_latest(@options[:detail_event_count], @options[:drop_useless_img], result_dir)
445
+ hash = {}
446
+ hash[:log_list] = latest_list.reverse
447
+ hash[:log_list_json] = JSON.dump(hash[:log_list])
448
+ crash_report = Dir.glob("#{result_dir}/*.crash")[0]
449
+ hash[:crash_report] = crash_report ? File.basename(crash_report) : nil
450
+ hash[:uia_trace] = @uia_trace
451
+ hash[:crashed] = @crashed
452
+ hash[:no_run] = @no_run
453
+
454
+ er = Erubis::Eruby.new(File.read(template_path('result.html.erb')))
455
+ open("#{result_dir}/result.html", 'w') do |f|
456
+ f.write(er.result(hash))
457
+ end
458
+ FileUtils.copy(template_path('result_view.js'), "#{result_dir}/result_view.js")
459
+ end
460
+
461
+ def watch_syslog
462
+ STDOUT.sync = true
463
+ puts "Attempting iOS device system log capture via deviceconsole."
464
+ stdin, stdout, stderr = Open3.popen3(grep_ios_syslog)
465
+ log_filename = "#{result_base_dir}/console.txt"
466
+ thread = Thread.new do
467
+ File.open(log_filename, 'a') do |output|
468
+ begin
469
+ while true
470
+ line = stdout.readline
471
+ output.write(line)
472
+ # output.write(line) if line.include?(app_name)
473
+ end
474
+ rescue IOError
475
+ log 'Stop iOS system log capture.'
476
+ kill_all_need
477
+ end
478
+ end
479
+ end
480
+ yield
481
+ sleep 3
482
+ stdout.close; stderr.close; stdin.close
483
+ thread.join
484
+ FileUtils.makedirs(result_dir) unless File.exists?(result_dir)
485
+ if File.exists?(log_filename)
486
+ FileUtils.move(log_filename, console_log_path)
487
+ end
488
+ end
489
+ end
490
+
491
+ LOG_TYPE = 'LogType'
492
+ MESSAGE = 'Message'
493
+ TIMESTAMP = 'Timestamp'
494
+ SCREENSHOT = 'Screenshot'
495
+
496
+ class LogDecoder
497
+ def initialize(log_list)
498
+ @log_list = log_list
499
+ end
500
+
501
+ def rm_unused_imgs(used_imgs, dir)
502
+ used_strs = used_imgs.join("\\|")
503
+ `cd "#{dir}";find . -type 'f' -name '*.png' | grep -v "#{used_strs}" | xargs rm`
504
+ end
505
+
506
+ def decode_latest(num=10, drop_useless_img, drop_dir)
507
+ hash = {}
508
+ ret = []
509
+ used_imgs = []
510
+ @log_list.reverse.each do |log|
511
+ break if num == 0
512
+ if log[LOG_TYPE] == 'Screenshot'
513
+ if log[MESSAGE] =~ /^action/
514
+ hash[:action_image] = log[MESSAGE]
515
+ elsif log[MESSAGE] =~ /^monkey/
516
+ hash[:screen_image] = log[MESSAGE]
517
+ used_imgs << log[MESSAGE]
518
+ hash[:timestamp] = log[TIMESTAMP]
519
+
520
+ # emit and init
521
+ if block_given?
522
+ yield(hash)
523
+ else
524
+ ret << hash
525
+ end
526
+ hash = {}
527
+ num -= 1
528
+ end
529
+ elsif log[LOG_TYPE] == 'Debug' && log[MESSAGE] =~ /^target./
530
+ hash[:message] = log[MESSAGE] unless log[MESSAGE] =~ /^target.captureRectWithName/ && log[MESSAGE] =~ /switcherScrollView/
531
+ # elsif log[LOG_TYPE] == 'Default' && log[MESSAGE] =~ /^DeviceInfo/
532
+ # hash[:screen_size] = log[MESSAGE]
533
+ end
534
+ end
535
+ #drop unused imgs
536
+ if drop_useless_img
537
+ puts "Drop useless images..."
538
+ rm_unused_imgs(used_imgs, drop_dir)
539
+ end
540
+ if !@log_list.empty?
541
+ #add screen_size
542
+ hash = {}
543
+ hash[:screen_size] = @log_list[0][MESSAGE]
544
+ ret << hash
545
+ end
546
+ ret
547
+ end
548
+ end
549
+ end