crash_monkey 0.1.5
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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +68 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/crash_monkey +33 -0
- data/crash_monkey.gemspec +89 -0
- data/lib/bootstrap/css/bootstrap-responsive.css +1109 -0
- data/lib/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/lib/bootstrap/css/bootstrap.css +6167 -0
- data/lib/bootstrap/css/bootstrap.min.css +9 -0
- data/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/lib/bootstrap/img/glyphicons-halflings.png +0 -0
- data/lib/bootstrap/js/bootstrap.js +2280 -0
- data/lib/bootstrap/js/bootstrap.min.js +6 -0
- data/lib/crash_monkey.rb +2 -0
- data/lib/crash_monkey/command_helper.rb +28 -0
- data/lib/crash_monkey/monkey_runner.rb +322 -0
- data/lib/crash_monkey/templates/config.json +25 -0
- data/lib/crash_monkey/templates/index.html.erb +51 -0
- data/lib/crash_monkey/templates/result.html.erb +73 -0
- data/lib/crash_monkey/templates/result_view.coffee +160 -0
- data/lib/crash_monkey/templates/result_view.js +246 -0
- data/lib/ui-auto-monkey/LICENSE +19 -0
- data/lib/ui-auto-monkey/README.md +0 -0
- data/lib/ui-auto-monkey/UIAutoMonkey.js +333 -0
- data/spec/spec_helper.rb +12 -0
- metadata +205 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
MonkeyResult = ->
|
2
|
+
that = {}
|
3
|
+
options = null
|
4
|
+
|
5
|
+
that.configure = (opts) ->
|
6
|
+
options = opts
|
7
|
+
options.image_size_rate = opts.image_size_rate || 1
|
8
|
+
that
|
9
|
+
|
10
|
+
that.draw = ->
|
11
|
+
ir = options.image_size_rate
|
12
|
+
for log, i in options.log_list
|
13
|
+
do (log, i) ->
|
14
|
+
text_div = $('#'+options.text_prefix+'-'+i)
|
15
|
+
text_div.html(log.message + "<hr>"+log.timestamp)
|
16
|
+
img = new Image()
|
17
|
+
img.addEventListener "load", ->
|
18
|
+
element = $('#'+options.view_prefix+'-'+i)
|
19
|
+
canvas = element[0]
|
20
|
+
canvas.width = img.width * ir
|
21
|
+
canvas.height = img.height * ir
|
22
|
+
context = canvas.getContext('2d')
|
23
|
+
context.scale(1,1)
|
24
|
+
context.clearRect(0,0,canvas.width, canvas.height)
|
25
|
+
context.drawImage(img, 0, 0, canvas.width, canvas.height)
|
26
|
+
target = DrawTarget(canvas: canvas, context: context, image_size_rate: ir, index: i)
|
27
|
+
eval(log.message)
|
28
|
+
img.src = log.screen_image + '.png'
|
29
|
+
|
30
|
+
return that
|
31
|
+
|
32
|
+
DrawTarget = (opts) ->
|
33
|
+
that = {}
|
34
|
+
options = opts
|
35
|
+
canvas = options.canvas
|
36
|
+
context = options.context
|
37
|
+
ir = options.image_size_rate
|
38
|
+
context.strokeStyle = "#f00"
|
39
|
+
context.lineWidth = 2
|
40
|
+
arc_radius = 20
|
41
|
+
|
42
|
+
that.tapWithOptions = (p1, info) ->
|
43
|
+
console.log(opts.index, p1, info)
|
44
|
+
pos_to_number(p1)
|
45
|
+
|
46
|
+
tc = 1*info.touchCount
|
47
|
+
for num in [0..(tc-1)]
|
48
|
+
o = x: (context.lineWidth + 2)*(num - (tc-1)/2), y: 0
|
49
|
+
draw_arc({x: (p1.x*ir + o.x)/ir, y: (p1.y*ir + o.y)/ir}, r: 0.1)
|
50
|
+
|
51
|
+
for num in [0..(1*info.tapCount-1)]
|
52
|
+
draw_arc(p1, r: (arc_radius*ir + (1+context.lineWidth)*num)/ir)
|
53
|
+
|
54
|
+
that.pinchCloseFromToForDuration = (p1, p2, info) ->
|
55
|
+
pos_to_number(p) for p in [p1, p2]
|
56
|
+
draw_arc(p1)
|
57
|
+
draw_arc(p2)
|
58
|
+
#
|
59
|
+
center = x: (p1.x+p2.x)/2, y: (p1.y+p2.y)/2
|
60
|
+
draw_arrow(p1, center)
|
61
|
+
draw_arrow(p2, center)
|
62
|
+
|
63
|
+
that.pinchOpenFromToForDuration = (p1, p2, info) ->
|
64
|
+
pos_to_number(p) for p in [p1, p2]
|
65
|
+
draw_arc(p1)
|
66
|
+
draw_arc(p2)
|
67
|
+
#
|
68
|
+
center = x: (p1.x+p2.x)/2, y: (p1.y+p2.y)/2
|
69
|
+
draw_arrow(center, p1)
|
70
|
+
draw_arrow(center, p2)
|
71
|
+
|
72
|
+
that.dragFromToForDuration = (p1, p2, info) ->
|
73
|
+
pos_to_number(p) for p in [p1, p2]
|
74
|
+
console.log(opts.index, p1, p2, info)
|
75
|
+
draw_arc(p1)
|
76
|
+
draw_arc(p2)
|
77
|
+
draw_arrow(p1, p2)
|
78
|
+
|
79
|
+
that.flickFromTo = (p1, p2, info) ->
|
80
|
+
pos_to_number(p) for p in [p1, p2]
|
81
|
+
console.log(opts.index, p1, p2, info)
|
82
|
+
draw_arc(p1)
|
83
|
+
draw_arrow(p1, p2)
|
84
|
+
|
85
|
+
that.lockForDuration = (duration) ->
|
86
|
+
draw_text("Lock Screen #{Math.floor(duration * 100)/100} Secs.")
|
87
|
+
|
88
|
+
that.deactivateAppForDuration = (duration) ->
|
89
|
+
draw_text("Deactivate #{Math.floor(duration * 100)/100} Secs.")
|
90
|
+
|
91
|
+
that.setDeviceOrientation = (orientation) ->
|
92
|
+
draw_text("Orientation to #{orientation_name(orientation)}")
|
93
|
+
|
94
|
+
that.shake = ->
|
95
|
+
draw_text("Shake!")
|
96
|
+
|
97
|
+
that.clickVolumeUp = ->
|
98
|
+
draw_text("clickVolumeUp!")
|
99
|
+
|
100
|
+
that.clickVolumeDown = ->
|
101
|
+
draw_text("clickVolumeDown!")
|
102
|
+
|
103
|
+
draw_text = (text, opts={}) ->
|
104
|
+
context.font = "10px 'MS Pゴシック'"
|
105
|
+
context.lineWidth = 1
|
106
|
+
context.strokeStyle = "green"
|
107
|
+
context.beginPath()
|
108
|
+
context.fillRect(canvas.width*0.1, 20, canvas.width*0.8, 40)
|
109
|
+
context.stroke()
|
110
|
+
context.strokeText(text, canvas.width*0.13, 42)
|
111
|
+
|
112
|
+
|
113
|
+
orientation_name = (orientation) ->
|
114
|
+
# http://www.testmachine.ch/javadoc/constant-values.html#ch.sukha.testmachine.client.IosDebuggingInterface.UIA_DEVICE_ORIENTATION_FACEDOWN
|
115
|
+
switch 1 * orientation
|
116
|
+
when 0 then "UNKNOWN"
|
117
|
+
when 1 then "PORTRAIT"
|
118
|
+
when 2 then "PORTRAIT_UPSIDEDOWN"
|
119
|
+
when 3 then "LANDSCAPELEFT"
|
120
|
+
when 4 then "LANDSCAPERIGHT"
|
121
|
+
when 5 then "FACEUP"
|
122
|
+
when 6 then "FACEDOWN"
|
123
|
+
else "UNDEF"
|
124
|
+
|
125
|
+
pos_to_number = (p) ->
|
126
|
+
p.x = 1 * p.x
|
127
|
+
p.y = 1 * p.y
|
128
|
+
p
|
129
|
+
|
130
|
+
draw_arc = (p, opts={}) ->
|
131
|
+
radius = opts.r || arc_radius
|
132
|
+
context.beginPath()
|
133
|
+
context.arc(p.x * ir, p.y * ir, radius * ir, 0, Math.PI*2)
|
134
|
+
context.stroke()
|
135
|
+
|
136
|
+
draw_arrow = (p1, p2, opts={}) ->
|
137
|
+
vx = p2.x - p1.x
|
138
|
+
vy = p2.y - p1.y
|
139
|
+
v = Math.sqrt(vx*vx + vy*vy)
|
140
|
+
ux = vx/v
|
141
|
+
uy = vy/v
|
142
|
+
opts.w ||= 8
|
143
|
+
opts.h ||= 12
|
144
|
+
opts.h2 ||= 5
|
145
|
+
lp = {x: p2.x - uy * opts.w - ux * opts.h, y: p2.y + ux * opts.w - uy * opts.h}
|
146
|
+
rp = {x: p2.x + uy * opts.w - ux * opts.h, y: p2.y - ux * opts.w - uy * opts.h}
|
147
|
+
mp = {x: p2.x - ux * opts.h2, y: p2.y - uy * opts.h2}
|
148
|
+
context.beginPath()
|
149
|
+
context.moveTo(p1.x * ir, p1.y * ir)
|
150
|
+
context.lineTo(mp.x * ir, mp.y * ir)
|
151
|
+
context.lineTo(lp.x * ir, lp.y * ir)
|
152
|
+
context.lineTo(p2.x * ir, p2.y * ir)
|
153
|
+
context.lineTo(rp.x * ir, rp.y * ir)
|
154
|
+
context.lineTo(mp.x * ir, mp.y * ir)
|
155
|
+
context.stroke()
|
156
|
+
|
157
|
+
return that
|
158
|
+
|
159
|
+
window.MonkeyResult = MonkeyResult
|
160
|
+
|
@@ -0,0 +1,246 @@
|
|
1
|
+
// Generated by CoffeeScript 1.6.3
|
2
|
+
(function() {
|
3
|
+
var DrawTarget, MonkeyResult;
|
4
|
+
|
5
|
+
MonkeyResult = function() {
|
6
|
+
var options, that;
|
7
|
+
that = {};
|
8
|
+
options = null;
|
9
|
+
that.configure = function(opts) {
|
10
|
+
options = opts;
|
11
|
+
options.image_size_rate = opts.image_size_rate || 1;
|
12
|
+
return that;
|
13
|
+
};
|
14
|
+
that.draw = function() {
|
15
|
+
var i, ir, log, _i, _len, _ref, _results;
|
16
|
+
ir = options.image_size_rate;
|
17
|
+
_ref = options.log_list;
|
18
|
+
_results = [];
|
19
|
+
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
20
|
+
log = _ref[i];
|
21
|
+
_results.push((function(log, i) {
|
22
|
+
var img, text_div;
|
23
|
+
text_div = $('#' + options.text_prefix + '-' + i);
|
24
|
+
text_div.html(log.message + "<hr>" + log.timestamp);
|
25
|
+
img = new Image();
|
26
|
+
img.addEventListener("load", function() {
|
27
|
+
var canvas, context, element, target;
|
28
|
+
element = $('#' + options.view_prefix + '-' + i);
|
29
|
+
canvas = element[0];
|
30
|
+
canvas.width = img.width * ir;
|
31
|
+
canvas.height = img.height * ir;
|
32
|
+
context = canvas.getContext('2d');
|
33
|
+
context.scale(1, 1);
|
34
|
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
35
|
+
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
36
|
+
target = DrawTarget({
|
37
|
+
canvas: canvas,
|
38
|
+
context: context,
|
39
|
+
image_size_rate: ir,
|
40
|
+
index: i
|
41
|
+
});
|
42
|
+
return eval(log.message);
|
43
|
+
});
|
44
|
+
return img.src = log.screen_image + '.png';
|
45
|
+
})(log, i));
|
46
|
+
}
|
47
|
+
return _results;
|
48
|
+
};
|
49
|
+
return that;
|
50
|
+
};
|
51
|
+
|
52
|
+
DrawTarget = function(opts) {
|
53
|
+
var arc_radius, canvas, context, draw_arc, draw_arrow, draw_text, ir, options, orientation_name, pos_to_number, that;
|
54
|
+
that = {};
|
55
|
+
options = opts;
|
56
|
+
canvas = options.canvas;
|
57
|
+
context = options.context;
|
58
|
+
ir = options.image_size_rate;
|
59
|
+
context.strokeStyle = "#f00";
|
60
|
+
context.lineWidth = 2;
|
61
|
+
arc_radius = 20;
|
62
|
+
that.tapWithOptions = function(p1, info) {
|
63
|
+
var num, o, tc, _i, _j, _ref, _ref1, _results;
|
64
|
+
console.log(opts.index, p1, info);
|
65
|
+
pos_to_number(p1);
|
66
|
+
tc = 1 * info.touchCount;
|
67
|
+
for (num = _i = 0, _ref = tc - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; num = 0 <= _ref ? ++_i : --_i) {
|
68
|
+
o = {
|
69
|
+
x: (context.lineWidth + 2) * (num - (tc - 1) / 2),
|
70
|
+
y: 0
|
71
|
+
};
|
72
|
+
draw_arc({
|
73
|
+
x: (p1.x * ir + o.x) / ir,
|
74
|
+
y: (p1.y * ir + o.y) / ir
|
75
|
+
}, {
|
76
|
+
r: 0.1
|
77
|
+
});
|
78
|
+
}
|
79
|
+
_results = [];
|
80
|
+
for (num = _j = 0, _ref1 = 1 * info.tapCount - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; num = 0 <= _ref1 ? ++_j : --_j) {
|
81
|
+
_results.push(draw_arc(p1, {
|
82
|
+
r: (arc_radius * ir + (1 + context.lineWidth) * num) / ir
|
83
|
+
}));
|
84
|
+
}
|
85
|
+
return _results;
|
86
|
+
};
|
87
|
+
that.pinchCloseFromToForDuration = function(p1, p2, info) {
|
88
|
+
var center, p, _i, _len, _ref;
|
89
|
+
_ref = [p1, p2];
|
90
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
91
|
+
p = _ref[_i];
|
92
|
+
pos_to_number(p);
|
93
|
+
}
|
94
|
+
draw_arc(p1);
|
95
|
+
draw_arc(p2);
|
96
|
+
center = {
|
97
|
+
x: (p1.x + p2.x) / 2,
|
98
|
+
y: (p1.y + p2.y) / 2
|
99
|
+
};
|
100
|
+
draw_arrow(p1, center);
|
101
|
+
return draw_arrow(p2, center);
|
102
|
+
};
|
103
|
+
that.pinchOpenFromToForDuration = function(p1, p2, info) {
|
104
|
+
var center, p, _i, _len, _ref;
|
105
|
+
_ref = [p1, p2];
|
106
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
107
|
+
p = _ref[_i];
|
108
|
+
pos_to_number(p);
|
109
|
+
}
|
110
|
+
draw_arc(p1);
|
111
|
+
draw_arc(p2);
|
112
|
+
center = {
|
113
|
+
x: (p1.x + p2.x) / 2,
|
114
|
+
y: (p1.y + p2.y) / 2
|
115
|
+
};
|
116
|
+
draw_arrow(center, p1);
|
117
|
+
return draw_arrow(center, p2);
|
118
|
+
};
|
119
|
+
that.dragFromToForDuration = function(p1, p2, info) {
|
120
|
+
var p, _i, _len, _ref;
|
121
|
+
_ref = [p1, p2];
|
122
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
123
|
+
p = _ref[_i];
|
124
|
+
pos_to_number(p);
|
125
|
+
}
|
126
|
+
console.log(opts.index, p1, p2, info);
|
127
|
+
draw_arc(p1);
|
128
|
+
draw_arc(p2);
|
129
|
+
return draw_arrow(p1, p2);
|
130
|
+
};
|
131
|
+
that.flickFromTo = function(p1, p2, info) {
|
132
|
+
var p, _i, _len, _ref;
|
133
|
+
_ref = [p1, p2];
|
134
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
135
|
+
p = _ref[_i];
|
136
|
+
pos_to_number(p);
|
137
|
+
}
|
138
|
+
console.log(opts.index, p1, p2, info);
|
139
|
+
draw_arc(p1);
|
140
|
+
return draw_arrow(p1, p2);
|
141
|
+
};
|
142
|
+
that.lockForDuration = function(duration) {
|
143
|
+
return draw_text("Lock Screen " + (Math.floor(duration * 100) / 100) + " Secs.");
|
144
|
+
};
|
145
|
+
that.deactivateAppForDuration = function(duration) {
|
146
|
+
return draw_text("Deactivate " + (Math.floor(duration * 100) / 100) + " Secs.");
|
147
|
+
};
|
148
|
+
that.setDeviceOrientation = function(orientation) {
|
149
|
+
return draw_text("Orientation to " + (orientation_name(orientation)));
|
150
|
+
};
|
151
|
+
that.shake = function() {
|
152
|
+
return draw_text("Shake!");
|
153
|
+
};
|
154
|
+
that.clickVolumeUp = function() {
|
155
|
+
return draw_text("clickVolumeUp!");
|
156
|
+
};
|
157
|
+
that.clickVolumeDown = function() {
|
158
|
+
return draw_text("clickVolumeDown!");
|
159
|
+
};
|
160
|
+
draw_text = function(text, opts) {
|
161
|
+
if (opts == null) {
|
162
|
+
opts = {};
|
163
|
+
}
|
164
|
+
context.font = "10px 'MS Pゴシック'";
|
165
|
+
context.lineWidth = 1;
|
166
|
+
context.strokeStyle = "green";
|
167
|
+
context.beginPath();
|
168
|
+
context.fillRect(canvas.width * 0.1, 20, canvas.width * 0.8, 40);
|
169
|
+
context.stroke();
|
170
|
+
return context.strokeText(text, canvas.width * 0.13, 42);
|
171
|
+
};
|
172
|
+
orientation_name = function(orientation) {
|
173
|
+
switch (1 * orientation) {
|
174
|
+
case 0:
|
175
|
+
return "UNKNOWN";
|
176
|
+
case 1:
|
177
|
+
return "PORTRAIT";
|
178
|
+
case 2:
|
179
|
+
return "PORTRAIT_UPSIDEDOWN";
|
180
|
+
case 3:
|
181
|
+
return "LANDSCAPELEFT";
|
182
|
+
case 4:
|
183
|
+
return "LANDSCAPERIGHT";
|
184
|
+
case 5:
|
185
|
+
return "FACEUP";
|
186
|
+
case 6:
|
187
|
+
return "FACEDOWN";
|
188
|
+
default:
|
189
|
+
return "UNDEF";
|
190
|
+
}
|
191
|
+
};
|
192
|
+
pos_to_number = function(p) {
|
193
|
+
p.x = 1 * p.x;
|
194
|
+
p.y = 1 * p.y;
|
195
|
+
return p;
|
196
|
+
};
|
197
|
+
draw_arc = function(p, opts) {
|
198
|
+
var radius;
|
199
|
+
if (opts == null) {
|
200
|
+
opts = {};
|
201
|
+
}
|
202
|
+
radius = opts.r || arc_radius;
|
203
|
+
context.beginPath();
|
204
|
+
context.arc(p.x * ir, p.y * ir, radius * ir, 0, Math.PI * 2);
|
205
|
+
return context.stroke();
|
206
|
+
};
|
207
|
+
draw_arrow = function(p1, p2, opts) {
|
208
|
+
var lp, mp, rp, ux, uy, v, vx, vy;
|
209
|
+
if (opts == null) {
|
210
|
+
opts = {};
|
211
|
+
}
|
212
|
+
vx = p2.x - p1.x;
|
213
|
+
vy = p2.y - p1.y;
|
214
|
+
v = Math.sqrt(vx * vx + vy * vy);
|
215
|
+
ux = vx / v;
|
216
|
+
uy = vy / v;
|
217
|
+
opts.w || (opts.w = 8);
|
218
|
+
opts.h || (opts.h = 12);
|
219
|
+
opts.h2 || (opts.h2 = 5);
|
220
|
+
lp = {
|
221
|
+
x: p2.x - uy * opts.w - ux * opts.h,
|
222
|
+
y: p2.y + ux * opts.w - uy * opts.h
|
223
|
+
};
|
224
|
+
rp = {
|
225
|
+
x: p2.x + uy * opts.w - ux * opts.h,
|
226
|
+
y: p2.y - ux * opts.w - uy * opts.h
|
227
|
+
};
|
228
|
+
mp = {
|
229
|
+
x: p2.x - ux * opts.h2,
|
230
|
+
y: p2.y - uy * opts.h2
|
231
|
+
};
|
232
|
+
context.beginPath();
|
233
|
+
context.moveTo(p1.x * ir, p1.y * ir);
|
234
|
+
context.lineTo(mp.x * ir, mp.y * ir);
|
235
|
+
context.lineTo(lp.x * ir, lp.y * ir);
|
236
|
+
context.lineTo(p2.x * ir, p2.y * ir);
|
237
|
+
context.lineTo(rp.x * ir, rp.y * ir);
|
238
|
+
context.lineTo(mp.x * ir, mp.y * ir);
|
239
|
+
return context.stroke();
|
240
|
+
};
|
241
|
+
return that;
|
242
|
+
};
|
243
|
+
|
244
|
+
window.MonkeyResult = MonkeyResult;
|
245
|
+
|
246
|
+
}).call(this);
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2013 Jonathan Penn (http://cocoamanifest.net/)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
File without changes
|
@@ -0,0 +1,333 @@
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////
|
2
|
+
// ORIGINAL Copyright
|
3
|
+
/////////////////////////////////////////////////////////////////////////////////////
|
4
|
+
|
5
|
+
// Copyright (c) 2013 Jonathan Penn (http://cocoamanifest.net/)
|
6
|
+
|
7
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
// of this software and associated documentation files (the "Software"), to deal
|
9
|
+
// in the Software without restriction, including without limitation the rights
|
10
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
// copies of the Software, and to permit persons to whom the Software is
|
12
|
+
// furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
// The above copyright notice and this permission notice shall be included in
|
15
|
+
// all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
// THE SOFTWARE.
|
24
|
+
|
25
|
+
/////////////////////////////////////////////////////////////////////////////////////
|
26
|
+
//
|
27
|
+
/////////////////////////////////////////////////////////////////////////////////////
|
28
|
+
|
29
|
+
|
30
|
+
"use strict";
|
31
|
+
|
32
|
+
var UIAutoMonkey = {
|
33
|
+
///////////////////////// __UIAutoMonkey Configuration Begin__ ///////////////////////////////
|
34
|
+
config: {
|
35
|
+
numberOfEvents: 1000,
|
36
|
+
delayBetweenEvents: 0.05, // In seconds
|
37
|
+
result_detail_event_num: 20,
|
38
|
+
|
39
|
+
// Events are triggered based on the relative weights here. The event
|
40
|
+
// with this highest number gets triggered the most.
|
41
|
+
//
|
42
|
+
// If you want to add your own "events", check out the event method
|
43
|
+
// definitions below.
|
44
|
+
eventWeights: {
|
45
|
+
tap: 500,
|
46
|
+
drag: 100,
|
47
|
+
flick: 100,
|
48
|
+
orientation: 1,
|
49
|
+
clickVolumeUp: 1,
|
50
|
+
clickVolumeDown: 1,
|
51
|
+
lock: 3,
|
52
|
+
pinchClose: 50,
|
53
|
+
pinchOpen: 50,
|
54
|
+
shake: 1,
|
55
|
+
deactivate: 3
|
56
|
+
},
|
57
|
+
|
58
|
+
// Probability that touch events will have these different properties
|
59
|
+
touchProbability: {
|
60
|
+
multipleTaps: 0.05,
|
61
|
+
multipleTouches: 0.05,
|
62
|
+
longPress: 0.05
|
63
|
+
},
|
64
|
+
|
65
|
+
// Uncomment the following to restrict events to a rectangluar area of
|
66
|
+
// the screen
|
67
|
+
/*
|
68
|
+
frame: {
|
69
|
+
origin: {x: 0, y: 0},
|
70
|
+
size: {width: 100, height: 50}
|
71
|
+
}
|
72
|
+
*/
|
73
|
+
},
|
74
|
+
///////////////////////// __UIAutoMonkey Configuration End__ ///////////////////////////////
|
75
|
+
|
76
|
+
// --- --- --- ---
|
77
|
+
// Event Methods
|
78
|
+
//
|
79
|
+
// Any event probability in the hash above corresponds to a related event
|
80
|
+
// method below. So, a "tap" probability will trigger a "tapEvent" method.
|
81
|
+
//
|
82
|
+
// If you want to add your own events, just add a probability to the hash
|
83
|
+
// above and then add a corresponding method below. Boom!
|
84
|
+
//
|
85
|
+
// Each event method can call any other method on this UIAutoMonkey object.
|
86
|
+
// All the methods at the end are helpers at your disposal and feel free to
|
87
|
+
// add your own.
|
88
|
+
|
89
|
+
tapEvent: function() {
|
90
|
+
var p1 = { x: this.randomX(), y: this.randomY() };
|
91
|
+
this.target().tapWithOptions(p1,
|
92
|
+
{
|
93
|
+
tapCount: this.randomTapCount(),
|
94
|
+
touchCount: this.randomTouchCount(),
|
95
|
+
duration: this.randomTapDuration()
|
96
|
+
}
|
97
|
+
);
|
98
|
+
},
|
99
|
+
|
100
|
+
dragEvent: function() {
|
101
|
+
var p1 = { x: this.randomX(), y: this.randomY() };
|
102
|
+
var p2 = { x: this.randomX(), y: this.randomY() };
|
103
|
+
this.target().dragFromToForDuration(p1, p2, 0.5);
|
104
|
+
},
|
105
|
+
|
106
|
+
flickEvent: function() {
|
107
|
+
var p1 = { x: this.randomX(), y: this.randomY() };
|
108
|
+
var p2 = { x: this.randomX(), y: this.randomY() };
|
109
|
+
this.target().flickFromTo(p1, p2);
|
110
|
+
},
|
111
|
+
|
112
|
+
orientationEvent: function() {
|
113
|
+
var orientations = [
|
114
|
+
UIA_DEVICE_ORIENTATION_PORTRAIT,
|
115
|
+
UIA_DEVICE_ORIENTATION_PORTRAIT_UPSIDEDOWN,
|
116
|
+
UIA_DEVICE_ORIENTATION_LANDSCAPELEFT,
|
117
|
+
UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT
|
118
|
+
];
|
119
|
+
|
120
|
+
var i = Math.floor(Math.random() * 10) % orientations.length;
|
121
|
+
var newOrientation = orientations[i];
|
122
|
+
this.target().setDeviceOrientation(newOrientation);
|
123
|
+
this.delay(0.9);
|
124
|
+
},
|
125
|
+
|
126
|
+
clickVolumeUpEvent: function() {
|
127
|
+
this.target().clickVolumeUp();
|
128
|
+
},
|
129
|
+
|
130
|
+
clickVolumeDownEvent: function() {
|
131
|
+
this.target().clickVolumeDown();
|
132
|
+
},
|
133
|
+
|
134
|
+
lockEvent: function() {
|
135
|
+
this.target().lockForDuration(Math.random() * 3);
|
136
|
+
},
|
137
|
+
|
138
|
+
pinchCloseEvent: function () {
|
139
|
+
var p1 = { x: this.randomX(), y: this.randomY() };
|
140
|
+
var p2 = { x: this.randomX(), y: this.randomY() };
|
141
|
+
this.target().pinchCloseFromToForDuration(p1, p2, 0.5);
|
142
|
+
},
|
143
|
+
|
144
|
+
pinchOpenEvent: function () {
|
145
|
+
var p1 = { x: this.randomX(), y: this.randomY() };
|
146
|
+
var p2 = { x: this.randomX(), y: this.randomY() };
|
147
|
+
this.target().pinchOpenFromToForDuration(p1, p2, 0.5);
|
148
|
+
},
|
149
|
+
|
150
|
+
shakeEvent: function() {
|
151
|
+
this.target().shake();
|
152
|
+
},
|
153
|
+
|
154
|
+
deactivateEvent: function() {
|
155
|
+
this.target().deactivateAppForDuration(5 + Math.random() * 10);
|
156
|
+
},
|
157
|
+
|
158
|
+
// --- --- --- ---
|
159
|
+
// Helper methods
|
160
|
+
//
|
161
|
+
|
162
|
+
results_path: function() {
|
163
|
+
var host = this.target().host();
|
164
|
+
var results = host.performTaskWithPathArgumentsTimeout("/bin/bash", ["-c", "echo -n $UIARESULTSPATH"], 5);
|
165
|
+
var ret = results.stdout;
|
166
|
+
UIALogger.logDebug("results_path: '" + ret+"'");
|
167
|
+
return ret;
|
168
|
+
},
|
169
|
+
|
170
|
+
deleteImage: function(dir, from, to) {
|
171
|
+
var host = this.target().host();
|
172
|
+
var files = [];
|
173
|
+
for (var i=from; i < to; i++) {
|
174
|
+
files.push(dir + "/screen-"+i + ".png");
|
175
|
+
// files.push(dir + "/action-"+i + ".png");
|
176
|
+
}
|
177
|
+
host.performTaskWithPathArgumentsTimeout("/bin/rm", files, 5);
|
178
|
+
},
|
179
|
+
|
180
|
+
RELEASE_THE_MONKEY: function() {
|
181
|
+
// Called at the bottom of this ui-auto-monkey to, you know...
|
182
|
+
//
|
183
|
+
// RELEASE THE MONKEY!
|
184
|
+
var target = this.target();
|
185
|
+
var initBundleID = target.frontMostApp().bundleID();
|
186
|
+
var results_path = this.results_path();
|
187
|
+
var result_num = this.config.result_detail_event_num;
|
188
|
+
|
189
|
+
for(var i = 0; i < this.config.numberOfEvents; i++) {
|
190
|
+
// Delete old images
|
191
|
+
if (i % 10 == 0 && i > result_num) {
|
192
|
+
this.deleteImage(results_path, i-result_num-10, i-result_num);
|
193
|
+
}
|
194
|
+
// Capture Screen Image
|
195
|
+
target.captureScreenWithName("screen-" + i);
|
196
|
+
// if another application become frontMost, exit.
|
197
|
+
var bundleID = target.frontMostApp().bundleID();
|
198
|
+
if (bundleID != initBundleID) {
|
199
|
+
UIALogger.logDebug("************************ Different Application **************: " + bundleID);
|
200
|
+
break;
|
201
|
+
}
|
202
|
+
|
203
|
+
// Send Random Event
|
204
|
+
this.triggerRandomEvent();
|
205
|
+
|
206
|
+
// Wait
|
207
|
+
this.delay();
|
208
|
+
}
|
209
|
+
},
|
210
|
+
|
211
|
+
triggerRandomEvent: function() {
|
212
|
+
var name = this.chooseEventName();
|
213
|
+
// Find the event method based on the name of the event
|
214
|
+
var event = this[name + "Event"];
|
215
|
+
return event.apply(this);
|
216
|
+
},
|
217
|
+
|
218
|
+
target: function() {
|
219
|
+
// Return the local target.
|
220
|
+
return UIATarget.localTarget();
|
221
|
+
},
|
222
|
+
|
223
|
+
delay: function(seconds) {
|
224
|
+
// Delay the target by `seconds` (can be a fraction)
|
225
|
+
// Defaults to setting in configuration
|
226
|
+
seconds = seconds || this.config.delayBetweenEvents;
|
227
|
+
this.target().delay(seconds);
|
228
|
+
},
|
229
|
+
|
230
|
+
chooseEventName: function() {
|
231
|
+
// Randomly chooses an event name from the `eventsWeight` dictionary
|
232
|
+
// based on the given weights.
|
233
|
+
var calculatedEventWeights = [];
|
234
|
+
var totalWeight = 0;
|
235
|
+
var events = this.config.eventWeights;
|
236
|
+
for (var event in events) {
|
237
|
+
if (events.hasOwnProperty(event)) {
|
238
|
+
calculatedEventWeights.push({
|
239
|
+
weight: events[event]+totalWeight,
|
240
|
+
event: event
|
241
|
+
});
|
242
|
+
totalWeight += events[event];
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
var chosenWeight = Math.random() * 1000 % totalWeight;
|
247
|
+
|
248
|
+
for (var i = 0; i < calculatedEventWeights.length; i++) {
|
249
|
+
if (chosenWeight < calculatedEventWeights[i].weight) {
|
250
|
+
return calculatedEventWeights[i].event;
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
throw "No even was chosen!";
|
255
|
+
},
|
256
|
+
|
257
|
+
screenWidth: function() {
|
258
|
+
// Need to adjust by one to stay within rectangle
|
259
|
+
return this.target().rect().size.width - 1;
|
260
|
+
},
|
261
|
+
|
262
|
+
screenHeight: function() {
|
263
|
+
// Need to adjust by one to stay within rectangle
|
264
|
+
return this.target().rect().size.height - 1;
|
265
|
+
},
|
266
|
+
|
267
|
+
randomX: function() {
|
268
|
+
var min, max;
|
269
|
+
|
270
|
+
if (this.config.frame){
|
271
|
+
// Limits coordinates to given frame if set in config
|
272
|
+
min = this.config.frame.origin.x;
|
273
|
+
max = this.config.frame.size.width + min;
|
274
|
+
} else {
|
275
|
+
// Returns a random X coordinate within the screen rectangle
|
276
|
+
min = 0;
|
277
|
+
max = this.screenWidth();
|
278
|
+
}
|
279
|
+
|
280
|
+
return Math.floor(Math.random() * (max - min) + min) + 1;
|
281
|
+
},
|
282
|
+
|
283
|
+
randomY: function() {
|
284
|
+
var min, max;
|
285
|
+
|
286
|
+
if (this.config.frame){
|
287
|
+
// Limits coordinates to given frame if set in config
|
288
|
+
min = this.config.frame.origin.y;
|
289
|
+
max = this.config.frame.size.height + min;
|
290
|
+
} else {
|
291
|
+
// Returns a random Y coordinate within the screen rectangle
|
292
|
+
min = 0;
|
293
|
+
max = this.screenHeight();
|
294
|
+
}
|
295
|
+
|
296
|
+
return Math.floor(Math.random() * (max - min) + min) + 1;
|
297
|
+
},
|
298
|
+
|
299
|
+
randomTapCount: function() {
|
300
|
+
// Calculates a tap count for tap events based on touch probabilities
|
301
|
+
if (this.config.touchProbability.multipleTaps > Math.random()) {
|
302
|
+
return Math.floor(Math.random() * 10) % 3 + 1;
|
303
|
+
}
|
304
|
+
else return 1;
|
305
|
+
},
|
306
|
+
|
307
|
+
randomTouchCount: function() {
|
308
|
+
// Calculates a touch count for tap events based on touch probabilities
|
309
|
+
if (this.config.touchProbability.multipleTouches > Math.random()) {
|
310
|
+
return Math.floor(Math.random() * 10) % 3 + 1;
|
311
|
+
}
|
312
|
+
else return 1;
|
313
|
+
},
|
314
|
+
|
315
|
+
randomTapDuration: function() {
|
316
|
+
// Calculates whether or not a tap should be a long press based on
|
317
|
+
// touch probabilities
|
318
|
+
if (this.config.touchProbability.longPress > Math.random()) {
|
319
|
+
return 0.5;
|
320
|
+
}
|
321
|
+
else return 0;
|
322
|
+
},
|
323
|
+
|
324
|
+
randomRadians: function() {
|
325
|
+
// Returns a random radian value
|
326
|
+
return Math.random() * 10 % (3.14159 * 2);
|
327
|
+
}
|
328
|
+
};
|
329
|
+
|
330
|
+
UIAutoMonkey.RELEASE_THE_MONKEY();
|
331
|
+
|
332
|
+
/* Instruments uses 4-wide tab stops. */
|
333
|
+
/* vim: set tabstop=4 shiftwidth=4 softtabstop=4 copyindent noexpandtab: */
|