test-prof 0.4.9 → 0.5.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -20
  3. data/README.md +10 -57
  4. data/assets/tagprof.demo.html +447 -0
  5. data/assets/tagprof.template.html +447 -0
  6. data/lib/minitest/event_prof_formatter.rb +18 -16
  7. data/lib/test_prof/any_fixture/dsl.rb +18 -0
  8. data/lib/test_prof/any_fixture.rb +92 -8
  9. data/lib/test_prof/event_prof/custom_events/factory_create.rb +1 -1
  10. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +1 -1
  11. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +1 -1
  12. data/lib/test_prof/event_prof/custom_events.rb +30 -0
  13. data/lib/test_prof/event_prof/minitest.rb +6 -0
  14. data/lib/test_prof/event_prof/profiler.rb +129 -0
  15. data/lib/test_prof/event_prof/rspec.rb +20 -11
  16. data/lib/test_prof/event_prof.rb +10 -108
  17. data/lib/test_prof/ext/active_record_refind.rb +20 -0
  18. data/lib/test_prof/factory_all_stub/factory_bot_patch.rb +13 -0
  19. data/lib/test_prof/factory_all_stub.rb +32 -0
  20. data/lib/test_prof/factory_doctor/rspec.rb +3 -2
  21. data/lib/test_prof/factory_prof/printers/flamegraph.rb +9 -13
  22. data/lib/test_prof/recipes/active_record_one_love.rb +4 -0
  23. data/lib/test_prof/recipes/active_record_shared_connection.rb +55 -0
  24. data/lib/test_prof/recipes/logging.rb +37 -0
  25. data/lib/test_prof/recipes/rspec/any_fixture.rb +4 -1
  26. data/lib/test_prof/recipes/rspec/factory_all_stub.rb +10 -0
  27. data/lib/test_prof/rspec_dissect/rspec.rb +4 -2
  28. data/lib/test_prof/rspec_stamp/rspec.rb +3 -2
  29. data/lib/test_prof/tag_prof/printers/html.rb +24 -0
  30. data/lib/test_prof/tag_prof/printers/simple.rb +82 -0
  31. data/lib/test_prof/tag_prof/result.rb +38 -0
  32. data/lib/test_prof/tag_prof/rspec.rb +43 -40
  33. data/lib/test_prof/tag_prof.rb +4 -0
  34. data/lib/test_prof/utils/html_builder.rb +21 -0
  35. data/lib/test_prof/version.rb +1 -1
  36. data/lib/test_prof.rb +2 -1
  37. metadata +20 -22
  38. data/assets/logo.svg +0 -1
  39. data/assets/testprof.png +0 -0
  40. data/guides/.rubocop.yml +0 -1
  41. data/guides/any_fixture.md +0 -114
  42. data/guides/before_all.md +0 -98
  43. data/guides/event_prof.md +0 -177
  44. data/guides/factory_default.md +0 -111
  45. data/guides/factory_doctor.md +0 -119
  46. data/guides/factory_prof.md +0 -86
  47. data/guides/let_it_be.md +0 -97
  48. data/guides/rspec_dissect.md +0 -60
  49. data/guides/rspec_stamp.md +0 -52
  50. data/guides/rubocop.md +0 -48
  51. data/guides/ruby_prof.md +0 -63
  52. data/guides/stack_prof.md +0 -47
  53. data/guides/tag_prof.md +0 -51
  54. data/guides/tests_sampling.md +0 -24
@@ -0,0 +1,447 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>TestProf: TagProf report</title>
5
+ <meta name="viewport" content="width=device-width">
6
+ <meta charset="UTF-8">
7
+ <style>
8
+ html, body {
9
+ height: 100%;
10
+ width: 100%;
11
+ min-width: 300px;
12
+ }
13
+
14
+ body {
15
+ background: #f9f9f9;
16
+ color: #363636;
17
+ font: 18px/30px "Arial",sans-serif;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+
22
+ a {
23
+ text-decoration: none;
24
+ color: #ff5e5e
25
+ }
26
+
27
+ a:visited, a:active{
28
+ color: #ff5e5e;
29
+ }
30
+
31
+ a:hover{
32
+ opacity: 0.8;
33
+ }
34
+
35
+ h1 {
36
+ font-size: 30px;
37
+ line-height: 32px;
38
+ letter-spacing: 2px;
39
+ font-weight: bold;
40
+ padding: 30px 0 30px 0;
41
+ margin: 0;
42
+ }
43
+
44
+ .main {
45
+ height: 100%;
46
+ width: 100%;
47
+ margin: 0;
48
+ padding: 0;
49
+ }
50
+
51
+ header {
52
+ position: relative;
53
+ text-align: center;
54
+ background: #fff;
55
+ overflow: hidden;
56
+ display: flex;
57
+ align-items: center;
58
+ flex-direction: column;
59
+ }
60
+
61
+ .chart {
62
+ display: flex;
63
+ justify-content: center;
64
+ position: relative;
65
+ box-sizing: border-box;
66
+ flex-direction: row;
67
+ padding: 20px;
68
+ }
69
+
70
+ .tooltip {
71
+ background: #eee;
72
+ box-shadow: 0 0 5px #999999;
73
+ color: #333;
74
+ display: none;
75
+ font-size: 14px;
76
+ left: 130px;
77
+ padding: 10px;
78
+ position: absolute;
79
+ text-align: left;
80
+ top: 95px;
81
+ z-index: 10;
82
+ }
83
+
84
+ .tooltip .label.active {
85
+ color: #ff5e5e;
86
+ }
87
+
88
+ .tooltip .value-label {
89
+ font-weight: bold;
90
+ margin-right: 10px;
91
+ }
92
+
93
+ .legend, .totals {
94
+ font-size: 14px;
95
+ }
96
+
97
+ #selector {
98
+ font-size: 14px;
99
+ display: flex;
100
+ align-items: flex-start;
101
+ position: relative;
102
+ box-sizing: border-box;
103
+ flex-direction: column;
104
+ padding: 20px;
105
+ }
106
+
107
+ rect {
108
+ stroke-width: 2;
109
+ }
110
+
111
+ rect.disabled { /* NEW */
112
+ fill: transparent !important; /* NEW */
113
+ }
114
+
115
+ .d3-donut-path {
116
+ transition: opacity 200ms;
117
+ opacity: 1;
118
+ cursor: pointer;
119
+ }
120
+
121
+ .d3-donut-path:hover {
122
+ opacity: 0.8;
123
+ }
124
+
125
+ .humanoids__link {
126
+ width: 130px;
127
+ }
128
+
129
+ .humanoids{
130
+ position: absolute;
131
+ bottom: -23px;
132
+ width: 150px;
133
+ height: 60px;
134
+ margin: 0 auto 0 -10px;
135
+ font-size: 0;
136
+ transform: scale(0.6);
137
+ }
138
+ .humanoids circle{
139
+ transform: scaleY(1);
140
+ transform-origin: 50%;
141
+ animation-duration: 8s;
142
+ animation-name: humanoids-blink;
143
+ animation-iteration-count: infinite;
144
+ }
145
+ .humanoids svg{
146
+ height: 60px;
147
+ }
148
+ .humanoids__human, .humanoids__martian{
149
+ position: absolute;
150
+ width: 80px;
151
+ height: 90px;
152
+ transition: transform 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
153
+ transform: translateY(0);
154
+ }
155
+ .humanoids__human:hover, .humanoids__martian:hover{
156
+ transform: translateY(-12px);
157
+ }
158
+ .humanoids__martian{
159
+ left: 0;
160
+ }
161
+ .humanoids__human{
162
+ right: 0;
163
+ }
164
+ .humanoids__human circle{
165
+ animation-delay: 0.5s;
166
+ }
167
+
168
+ [name="root"] .label {
169
+ text-align: center;
170
+ }
171
+ </style>
172
+ </head>
173
+ <body>
174
+ <div class="main">
175
+ <header>
176
+ <h1>TagProf Report</h1>
177
+ <a class="humanoids__link" href="https://evilmartians.com">
178
+ <div class="humanoids">
179
+ <div class="humanoids__martian"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#f64242" d="M26 88L8 78l18-10"/><path fill="#f64242" d="M94 92v-6H44c-5.5 0-10-4.5-10-10s4.5-10 10-10h50V32c0-14-7.9-22-22-22H48c-14.1 0-22 8-22 22v60h68z"/><circle fill="#FFF" cx="48" cy="50" r="8"/><circle fill="#FFF" cx="72" cy="50" r="8"/><circle fill="#BF6C35" cx="48" cy="50" r="4"/><circle fill="#BF6C35" cx="72" cy="50" r="4"/><g fill="#663F4C"><path d="M48 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM82 50c0 5.5-4.5 10-10 10s-10-4.5-10-10 4.5-10 10-10 10 4.5 10 10zm-16 0c0 3.3 2.7 6 6 6s6-2.7 6-6-2.7-6-6-6-6 2.7-6 6z"/><path d="M102 8c-3.8 0-6-2.2-6-6 0-1.1-.9-2-2-2s-2 .9-2 2c0 2.2.5 4.1 1.5 5.7L88.2 13c-4-3.3-9.5-5-16.2-5H48c-6.7 0-12.2 1.7-16.2 4.9l-5.3-5.3C27.5 6.1 28 4.2 28 2c0-1.1-.9-2-2-2s-2 .9-2 2c0 3.8-2.2 6-6 6-1.1 0-2 .9-2 2s.9 2 2 2c2.2 0 4.1-.5 5.7-1.5l5.3 5.3c-3.2 4-4.9 9.5-4.9 16.2v34.8L3.9 78 24 89v3h4V32c0-12.9 7.1-20 20-20h24c12.9 0 20 7.1 20 20v32H44c-6.6 0-12 5.4-12 12s5.4 12 12 12h48v4h4v-8h-2l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-3.1 6.2C37.1 80.7 36 78.5 36 76c0-4.4 3.6-8 8-8l4 8 4-8h4l4 8 4-8h4l4 8 4-8h4l4 8 4-8h8V32c0-6.7-1.7-12.2-4.9-16.2l5.3-5.3c1.6 1 3.5 1.5 5.7 1.5 1.1 0 2-.9 2-2s-1-2-2.1-2zM24 84.4L12.1 78 24 71.4v13z"/></g></svg></div>
180
+ <div class="humanoids__human"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#FFFFFF" d="M14 62v30h68v-6H32c-5.5 0-10-4.5-10-10s4.5-10 10-10h50v-4c4 0 8-3.6 8-8s-4-8-8-8V32c0-14-7.9-22-22-22H36c-14.1 0-22 8-22 22v14c-4 0-8 3.6-8 8s4 8 8 8z"/><circle fill="#FFF" cx="36" cy="50" r="8"/><circle fill="#FFF" cx="60" cy="50" r="8"/><circle fill="#f64242" cx="36" cy="50" r="4"/><circle fill="#f64242" cx="60" cy="50" r="4"/><path fill="#f64242" d="M60 10H36c-14.1 0-22 8-22 22v2l4-4 6 6 6-6 6 6 6-6 6 6 6-6 6 6 6-6 6 6 6-6 4 4v-2c0-14-7.9-22-22-22z"/><g fill="#663F4C"><path d="M36 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM60 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6z"/><path d="M12 63.8V92h4V32c0-12.9 7.1-20 20-20h24c12.9 0 20 7.1 20 20v32H32c-6.6 0-12 5.4-12 12s5.4 12 12 12h48v4h4v-8H74v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1c-4 0-8-3.6-8-8s3.6-8 8-8h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h4v-4.2c5-.9 8-5 8-9.8s-3-8.9-8-9.8V32c0-15.3-8.7-24-24-24H36c-15.3 0-24 8.7-24 24v12.2c-5 .9-8 5-8 9.8s3 8.9 8 9.8zm72-15.5c2 .8 4 3 4 5.7s-2 4.8-4 5.7V48.3zm-72 0v11.3c-2-.8-4-3-4-5.7s2-4.7 4-5.6z"/></g></svg></div>
181
+ </div>
182
+ </a>
183
+ </header>
184
+ <div class="chart">
185
+ <form id="selector">
186
+ </form>
187
+ <div id="chart">
188
+ </div>
189
+ <div id="legend">
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ <script type="text/javascript" src="src/d3.v4.min.js"></script>
195
+
196
+ <script>
197
+ var report_data;
198
+ var width = 360;
199
+ var legendWidth = 240;
200
+ var height = 360;
201
+ var radius = Math.min(width, height) / 2;
202
+ var donutWidth = 75;
203
+ var legendRectSize = 18;
204
+ var legendSpacing = 4;
205
+
206
+ var color = d3.scaleOrdinal(d3.schemeCategory20);
207
+
208
+ function pad(n, size) {
209
+ if(size == void 0) size = 2;
210
+
211
+ var str = n.toString();
212
+
213
+ while(str.length < size){
214
+ str = "0" + str;
215
+ }
216
+
217
+ return str;
218
+ }
219
+
220
+ function duration(t) {
221
+ return pad(t / 60|0) + ':' + pad((t % 60) |0) + '.' + pad(((t * 1000) % 1000)|0, 3);
222
+ }
223
+
224
+ var svg = d3.select('#chart')
225
+ .append('svg')
226
+ .attr('width', width)
227
+ .attr('height', height)
228
+ .append('g')
229
+ .attr('transform', 'translate(' + (width / 2) +
230
+ ',' + (height / 2) + ')');
231
+
232
+ var arc = d3.arc()
233
+ .innerRadius(radius - donutWidth)
234
+ .outerRadius(radius);
235
+
236
+ var labelArc = d3.arc()
237
+ .outerRadius(radius - donutWidth)
238
+ .innerRadius(radius);
239
+
240
+ var pie = d3.pie()
241
+ .value(function(d) { return d.time; })
242
+ .sort(function(a, b) {
243
+ return b.time - a.time;
244
+ });
245
+
246
+ var tooltip = d3.select('#chart')
247
+ .append('div')
248
+ .attr('class', 'tooltip');
249
+
250
+ tooltip.append('div')
251
+ .attr('class', 'label');
252
+
253
+ tooltip.append('div')
254
+ .attr('class', 'stats');
255
+
256
+ // var report_data = JSON.parse('{/**REPORT-DATA**/}');
257
+ var report_data = {
258
+ tag: "type",
259
+ events: [
260
+ "sql.active_record"
261
+ ],
262
+ data: [
263
+ { value: "model", count: 123, time: 324.2, "sql.active_record": 21.54 },
264
+ { value: "controller", count: 44, time: 514, "sql.active_record": 121 },
265
+ { value: "job", count: 33, time: 122.2 },
266
+ { value: "__unknown__", count: 2, time: 44, "sql.active_record": 0 }
267
+ ]
268
+ };
269
+
270
+ var dataset = report_data.data;
271
+
272
+ dataset.sort(function(a, b){
273
+ return b.time - a.time;
274
+ });
275
+
276
+ var totals = {};
277
+
278
+ ["count", "time"].concat(report_data.events).forEach(function(event) {
279
+ totals[event] = d3.sum(dataset.map(function(d){
280
+ return d[event] || 0;
281
+ }));
282
+ });
283
+
284
+ dataset.forEach(function(d) {
285
+ d.labels = {};
286
+
287
+ if (d.value == "__unknown__") {
288
+ d.color = "#cccccc";
289
+ };
290
+
291
+ d.labels.count = {
292
+ value: d.count,
293
+ percent: Math.round(1000 * d.count / totals.count) / 10
294
+ };
295
+
296
+ ["time"].concat(report_data.events).forEach(function(event){
297
+ d.labels[event] = {
298
+ value: duration(d[event]),
299
+ percent: d[event] ? Math.round(1000 * d[event] / totals[event]) / 10 : 0
300
+ }
301
+ });
302
+ });
303
+
304
+ var path = svg.selectAll('path')
305
+ .data(pie(dataset))
306
+ .enter()
307
+ .append('path')
308
+ .attr('d', arc)
309
+ .attr('class', 'd3-donut-path')
310
+ .attr('fill', function(d, i) {
311
+ return d.data.color || color(d.data.value);
312
+ })
313
+ .each(function(d) { this._current = d; });
314
+
315
+ var currentMeasure = "time";
316
+
317
+ pie.value(function(d) {
318
+ return d[currentMeasure];
319
+ });
320
+
321
+ path.on('mouseover', function(d) {
322
+ tooltip.select('.label').html("<span class='value-label'>" + report_data.tag + ":</span><span class='value-data'>" + d.data.value + "</span>");
323
+
324
+ var str = "";
325
+
326
+ ["count", "time"].concat(report_data.events).forEach(function(event) {
327
+ str += "<div class='label " + (event == currentMeasure ? 'active' : '') + "'><span class='value-label'>" + event + ":</span><span class='value-data'>" + d.data.labels[event].value + " ("+d.data.labels[event].percent + "%)</span></div>";
328
+ });
329
+ tooltip.select('.stats').html(str);
330
+ tooltip.style('display', 'block');
331
+ });
332
+
333
+ path.on('mousemove', function(d) {
334
+ tooltip.style('top', (d3.event.layerY + 10) + 'px')
335
+ .style('left', (d3.event.layerX + 10) + 'px');
336
+ });
337
+
338
+ path.on('mouseout', function() {
339
+ tooltip.style('display', 'none');
340
+ });
341
+
342
+ if(report_data.events && report_data.events.length) {
343
+ var selectOptions = ["time"].concat(report_data.events);
344
+
345
+ var selector = d3.select("form").selectAll("label")
346
+ .data(selectOptions)
347
+ .enter().append("label");
348
+
349
+ selector.append("input")
350
+ .attr("type", "radio")
351
+ .attr("name", "measurement")
352
+ .attr("value", function(d) { return d; })
353
+ .property("checked", function(d, i) { return i === 0 });
354
+
355
+ d3.select("form").style('width', legendWidth + 'px');
356
+
357
+ d3.selectAll(("input[name='measurement']")).on("change", function(e) {
358
+ render(e);
359
+ });
360
+
361
+ selector.append("span")
362
+ .text(function(d) { return d; });
363
+ } else {
364
+ d3.select("form").remove();
365
+ }
366
+
367
+ var legendContainer = d3.select('#legend')
368
+ .append('svg')
369
+ .attr('height', height)
370
+ .attr('width', legendWidth);
371
+
372
+ var legend = legendContainer.selectAll('legend')
373
+ .data(color.domain())
374
+ .enter()
375
+ .append('g')
376
+ .attr('class', 'legend')
377
+ .attr('transform', function(d, i) {
378
+ var height = legendRectSize + legendSpacing;
379
+ var horz = 2 * legendRectSize;
380
+ var vert = i * height;
381
+ return 'translate(' + horz + ',' + vert + ')';
382
+ });
383
+
384
+ legend.append('rect')
385
+ .attr('width', legendRectSize)
386
+ .attr('height', legendRectSize)
387
+ .style('fill', color)
388
+ .style('stroke', color);
389
+
390
+ legend.append('text')
391
+ .attr('x', legendRectSize + legendSpacing)
392
+ .attr('y', legendRectSize - legendSpacing)
393
+ .text(function(d) { return d; });
394
+
395
+ var totalsContainer = svg.append('g')
396
+ .attr('class', 'totals')
397
+ .attr('transform', 'translate(-' + (radius - 100) +', -'+ (radius - 120) + ')');
398
+
399
+ var totalsArray = d3.keys(totals).map(function(key){
400
+ if(key == "count"){
401
+ return { key: key, value: totals[key] };
402
+ } else {
403
+ return { key: key, value: duration(totals[key]) };
404
+ }
405
+ });
406
+
407
+ totalsContainer.append('text')
408
+ .text("TOTALS")
409
+ .attr('x', 50);
410
+
411
+ var totalsLegend = totalsContainer.selectAll('total')
412
+ .data(totalsArray)
413
+ .enter()
414
+ .append('g')
415
+ .attr('class', 'total')
416
+ .attr('transform', function(d, i) {
417
+ var height = legendRectSize + legendSpacing;
418
+ var horz = 0;
419
+ var vert = i * height + 20;
420
+ return 'translate(' + horz + ',' + vert + ')';
421
+ });
422
+
423
+ totalsLegend.append('text')
424
+ .attr('x', 0)
425
+ .attr('y', legendRectSize - legendSpacing)
426
+ .text(function(d) {
427
+ return d.key + ": " + d.value;
428
+ });
429
+
430
+ var render = function(measurement) {
431
+ currentMeasure = measurement;
432
+
433
+ path = path.data(pie(dataset));
434
+
435
+ path.transition()
436
+ .duration(750)
437
+ .attrTween('d', function(d) {
438
+ var interpolate = d3.interpolate(this._current, d);
439
+ this._current = interpolate(0);
440
+ return function(t) {
441
+ return arc(interpolate(t));
442
+ };
443
+ });
444
+ }
445
+ </script>
446
+ </body>
447
+ </html>