classx 0.0.3 → 0.0.4

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 (57) hide show
  1. data/ChangeLog +354 -0
  2. data/README +5 -0
  3. data/Rakefile +2 -2
  4. data/bench/attribute_get.rb +73 -0
  5. data/bench/attribute_set.rb +53 -19
  6. data/bench/define_attribute.rb +226 -0
  7. data/bench/initialize.rb +25 -7
  8. data/doc/output/coverage/index.html +74 -128
  9. data/doc/output/coverage/lib-classx-attribute_rb.html +291 -190
  10. data/doc/output/coverage/lib-classx-attributes_rb.html +167 -101
  11. data/doc/output/coverage/lib-classx-bracketable_rb.html +671 -0
  12. data/doc/output/coverage/lib-classx-class_attributes_rb.html +775 -0
  13. data/doc/output/coverage/lib-classx-commandable_rb.html +727 -0
  14. data/doc/output/coverage/{-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-block_rb.html → lib-classx-declare_rb.html} +60 -62
  15. data/doc/output/coverage/lib-classx-validate_rb.html +43 -41
  16. data/doc/output/coverage/lib-classx_rb.html +208 -78
  17. data/example/commandable.rb +1 -0
  18. data/lib/classx.rb +146 -16
  19. data/lib/classx/attribute.rb +244 -143
  20. data/lib/classx/attributes.rb +93 -27
  21. data/lib/classx/bracketable.rb +61 -0
  22. data/lib/classx/class_attributes.rb +165 -0
  23. data/lib/classx/commandable.rb +55 -4
  24. data/lib/classx/declare.rb +40 -3
  25. data/lib/classx/role/logger.rb +17 -13
  26. data/lib/classx/validate.rb +26 -24
  27. data/spec/classx/handles_spec.rb +21 -6
  28. data/spec/classx/serialize_spec.rb +57 -0
  29. data/spec/classx/{with_coerce.rb → with_coerce_spec.rb} +0 -0
  30. data/spec/classx/with_extend_spec.rb +58 -0
  31. data/spec/classx/with_include_spec.rb +58 -0
  32. data/spec/classx/with_validate_spec.rb +363 -0
  33. data/spec/classx/without_anyoption_spec.rb +1 -1
  34. data/spec/classx/writable_option_spec.rb +29 -4
  35. data/spec/classx_bracketable_spec.rb +160 -0
  36. data/spec/classx_class_attributes/default_option_spec.rb +121 -0
  37. data/spec/classx_class_attributes/dsl_accessor_spec.rb +88 -0
  38. data/spec/classx_class_attributes/handles_spec.rb +77 -0
  39. data/spec/classx_class_attributes/with_coerce_spec.rb +119 -0
  40. data/spec/classx_class_attributes/with_extend_spec.rb +64 -0
  41. data/spec/classx_class_attributes/with_include_spec.rb +64 -0
  42. data/spec/classx_class_attributes/with_multiple_class_spec.rb +60 -0
  43. data/spec/classx_class_attributes/with_validate_spec.rb +248 -0
  44. data/spec/classx_class_attributes/without_accessor_spec.rb +19 -0
  45. data/spec/classx_class_attributes/without_anyoption_spec.rb +21 -0
  46. data/spec/classx_class_attributes/writable_option_spec.rb +100 -0
  47. data/spec/classx_declare_spec.rb +117 -0
  48. data/spec/classx_validate_spec.rb +1 -3
  49. data/tasks/basic_tasks.rake +1 -1
  50. metadata +36 -26
  51. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-callbacks_rb.html +0 -932
  52. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-change_rb.html +0 -779
  53. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-hunk_rb.html +0 -867
  54. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs_rb.html +0 -1715
  55. data/doc/output/coverage/-Library-Ruby-Gems-gems-rcov-0_8_1_2_0-lib-rcov_rb.html +0 -1598
  56. data/spec/classx/with_extend.rb +0 -27
  57. data/spec/classx/with_include.rb +0 -27
@@ -1,1598 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
- <html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'><head><title>/Library/Ruby/Gems/gems/rcov-0.8.1.2.0/lib/rcov.rb - C0 code coverage information</title>
3
- <style type='text/css'>body { background-color: rgb(240, 240, 245); }</style>
4
- <style type='text/css'>span.cross-ref-title {
5
- font-size: 140%;
6
- }
7
- span.cross-ref a {
8
- text-decoration: none;
9
- }
10
- span.cross-ref {
11
- background-color:#f3f7fa;
12
- border: 1px dashed #333;
13
- margin: 1em;
14
- padding: 0.5em;
15
- overflow: hidden;
16
- }
17
- a.crossref-toggle {
18
- text-decoration: none;
19
- }
20
- span.marked0 {
21
- background-color: rgb(185, 210, 200);
22
- display: block;
23
- }
24
- span.marked1 {
25
- background-color: rgb(190, 215, 205);
26
- display: block;
27
- }
28
- span.inferred0 {
29
- background-color: rgb(175, 200, 200);
30
- display: block;
31
- }
32
- span.inferred1 {
33
- background-color: rgb(180, 205, 205);
34
- display: block;
35
- }
36
- span.uncovered0 {
37
- background-color: rgb(225, 110, 110);
38
- display: block;
39
- }
40
- span.uncovered1 {
41
- background-color: rgb(235, 120, 120);
42
- display: block;
43
- }
44
- span.overview {
45
- border-bottom: 8px solid black;
46
- }
47
- div.overview {
48
- border-bottom: 8px solid black;
49
- }
50
- body {
51
- font-family: verdana, arial, helvetica;
52
- }
53
- div.footer {
54
- font-size: 68%;
55
- margin-top: 1.5em;
56
- }
57
- h1, h2, h3, h4, h5, h6 {
58
- margin-bottom: 0.5em;
59
- }
60
- h5 {
61
- margin-top: 0.5em;
62
- }
63
- .hidden {
64
- display: none;
65
- }
66
- div.separator {
67
- height: 10px;
68
- }
69
- /* Commented out for better readability, esp. on IE */
70
- /*
71
- table tr td, table tr th {
72
- font-size: 68%;
73
- }
74
- td.value table tr td {
75
- font-size: 11px;
76
- }
77
- */
78
- table.percent_graph {
79
- height: 12px;
80
- border: #808080 1px solid;
81
- empty-cells: show;
82
- }
83
- table.percent_graph td.covered {
84
- height: 10px;
85
- background: #00f000;
86
- }
87
- table.percent_graph td.uncovered {
88
- height: 10px;
89
- background: #e00000;
90
- }
91
- table.percent_graph td.NA {
92
- height: 10px;
93
- background: #eaeaea;
94
- }
95
- table.report {
96
- border-collapse: collapse;
97
- width: 100%;
98
- }
99
- table.report td.heading {
100
- background: #dcecff;
101
- border: #d0d0d0 1px solid;
102
- font-weight: bold;
103
- text-align: center;
104
- }
105
- table.report td.heading:hover {
106
- background: #c0ffc0;
107
- }
108
- table.report td.text {
109
- border: #d0d0d0 1px solid;
110
- }
111
- table.report td.value,
112
- table.report td.lines_total,
113
- table.report td.lines_code {
114
- text-align: right;
115
- border: #d0d0d0 1px solid;
116
- }
117
- table.report tr.light {
118
- background-color: rgb(240, 240, 245);
119
- }
120
- table.report tr.dark {
121
- background-color: rgb(230, 230, 235);
122
- }
123
- </style>
124
- <script type='text/javascript'>
125
- // <![CDATA[
126
- function toggleCode( id ) {
127
- if ( document.getElementById )
128
- elem = document.getElementById( id );
129
- else if ( document.all )
130
- elem = eval( "document.all." + id );
131
- else
132
- return false;
133
-
134
- elemStyle = elem.style;
135
-
136
- if ( elemStyle.display != "block" ) {
137
- elemStyle.display = "block"
138
- } else {
139
- elemStyle.display = "none"
140
- }
141
-
142
- return true;
143
- }
144
-
145
- // Make cross-references hidden by default
146
- document.writeln( "<style type=\"text/css\">span.cross-ref { display: none }</style>" )
147
- // ]]>
148
- </script>
149
- <style type='text/css'>span.run0 {
150
- background-color: rgb(178, 204, 255);
151
- display: block;
152
- }
153
- span.run1 {
154
- background-color: rgb(178, 206, 255);
155
- display: block;
156
- }
157
- span.run2 {
158
- background-color: rgb(178, 209, 255);
159
- display: block;
160
- }
161
- span.run3 {
162
- background-color: rgb(178, 211, 255);
163
- display: block;
164
- }
165
- span.run4 {
166
- background-color: rgb(178, 214, 255);
167
- display: block;
168
- }
169
- span.run5 {
170
- background-color: rgb(178, 218, 255);
171
- display: block;
172
- }
173
- span.run6 {
174
- background-color: rgb(178, 220, 255);
175
- display: block;
176
- }
177
- span.run7 {
178
- background-color: rgb(178, 223, 255);
179
- display: block;
180
- }
181
- span.run8 {
182
- background-color: rgb(178, 225, 255);
183
- display: block;
184
- }
185
- span.run9 {
186
- background-color: rgb(178, 228, 255);
187
- display: block;
188
- }
189
- span.run10 {
190
- background-color: rgb(178, 232, 255);
191
- display: block;
192
- }
193
- span.run11 {
194
- background-color: rgb(178, 234, 255);
195
- display: block;
196
- }
197
- span.run12 {
198
- background-color: rgb(178, 237, 255);
199
- display: block;
200
- }
201
- span.run13 {
202
- background-color: rgb(178, 239, 255);
203
- display: block;
204
- }
205
- span.run14 {
206
- background-color: rgb(178, 242, 255);
207
- display: block;
208
- }
209
- span.run15 {
210
- background-color: rgb(178, 246, 255);
211
- display: block;
212
- }
213
- span.run16 {
214
- background-color: rgb(178, 248, 255);
215
- display: block;
216
- }
217
- span.run17 {
218
- background-color: rgb(178, 251, 255);
219
- display: block;
220
- }
221
- span.run18 {
222
- background-color: rgb(178, 253, 255);
223
- display: block;
224
- }
225
- span.run19 {
226
- background-color: rgb(178, 255, 253);
227
- display: block;
228
- }
229
- span.run20 {
230
- background-color: rgb(178, 255, 249);
231
- display: block;
232
- }
233
- span.run21 {
234
- background-color: rgb(178, 255, 247);
235
- display: block;
236
- }
237
- span.run22 {
238
- background-color: rgb(178, 255, 244);
239
- display: block;
240
- }
241
- span.run23 {
242
- background-color: rgb(178, 255, 242);
243
- display: block;
244
- }
245
- span.run24 {
246
- background-color: rgb(178, 255, 239);
247
- display: block;
248
- }
249
- span.run25 {
250
- background-color: rgb(178, 255, 235);
251
- display: block;
252
- }
253
- span.run26 {
254
- background-color: rgb(178, 255, 233);
255
- display: block;
256
- }
257
- span.run27 {
258
- background-color: rgb(178, 255, 230);
259
- display: block;
260
- }
261
- span.run28 {
262
- background-color: rgb(178, 255, 228);
263
- display: block;
264
- }
265
- span.run29 {
266
- background-color: rgb(178, 255, 225);
267
- display: block;
268
- }
269
- span.run30 {
270
- background-color: rgb(178, 255, 221);
271
- display: block;
272
- }
273
- span.run31 {
274
- background-color: rgb(178, 255, 219);
275
- display: block;
276
- }
277
- span.run32 {
278
- background-color: rgb(178, 255, 216);
279
- display: block;
280
- }
281
- span.run33 {
282
- background-color: rgb(178, 255, 214);
283
- display: block;
284
- }
285
- span.run34 {
286
- background-color: rgb(178, 255, 211);
287
- display: block;
288
- }
289
- span.run35 {
290
- background-color: rgb(178, 255, 207);
291
- display: block;
292
- }
293
- span.run36 {
294
- background-color: rgb(178, 255, 205);
295
- display: block;
296
- }
297
- span.run37 {
298
- background-color: rgb(178, 255, 202);
299
- display: block;
300
- }
301
- span.run38 {
302
- background-color: rgb(178, 255, 200);
303
- display: block;
304
- }
305
- span.run39 {
306
- background-color: rgb(178, 255, 197);
307
- display: block;
308
- }
309
- span.run40 {
310
- background-color: rgb(178, 255, 193);
311
- display: block;
312
- }
313
- span.run41 {
314
- background-color: rgb(178, 255, 191);
315
- display: block;
316
- }
317
- span.run42 {
318
- background-color: rgb(178, 255, 188);
319
- display: block;
320
- }
321
- span.run43 {
322
- background-color: rgb(178, 255, 186);
323
- display: block;
324
- }
325
- span.run44 {
326
- background-color: rgb(178, 255, 183);
327
- display: block;
328
- }
329
- span.run45 {
330
- background-color: rgb(178, 255, 179);
331
- display: block;
332
- }
333
- span.run46 {
334
- background-color: rgb(179, 255, 178);
335
- display: block;
336
- }
337
- span.run47 {
338
- background-color: rgb(182, 255, 178);
339
- display: block;
340
- }
341
- span.run48 {
342
- background-color: rgb(184, 255, 178);
343
- display: block;
344
- }
345
- span.run49 {
346
- background-color: rgb(187, 255, 178);
347
- display: block;
348
- }
349
- span.run50 {
350
- background-color: rgb(191, 255, 178);
351
- display: block;
352
- }
353
- span.run51 {
354
- background-color: rgb(193, 255, 178);
355
- display: block;
356
- }
357
- span.run52 {
358
- background-color: rgb(196, 255, 178);
359
- display: block;
360
- }
361
- span.run53 {
362
- background-color: rgb(198, 255, 178);
363
- display: block;
364
- }
365
- span.run54 {
366
- background-color: rgb(201, 255, 178);
367
- display: block;
368
- }
369
- span.run55 {
370
- background-color: rgb(205, 255, 178);
371
- display: block;
372
- }
373
- span.run56 {
374
- background-color: rgb(207, 255, 178);
375
- display: block;
376
- }
377
- span.run57 {
378
- background-color: rgb(210, 255, 178);
379
- display: block;
380
- }
381
- span.run58 {
382
- background-color: rgb(212, 255, 178);
383
- display: block;
384
- }
385
- span.run59 {
386
- background-color: rgb(215, 255, 178);
387
- display: block;
388
- }
389
- span.run60 {
390
- background-color: rgb(219, 255, 178);
391
- display: block;
392
- }
393
- span.run61 {
394
- background-color: rgb(221, 255, 178);
395
- display: block;
396
- }
397
- span.run62 {
398
- background-color: rgb(224, 255, 178);
399
- display: block;
400
- }
401
- span.run63 {
402
- background-color: rgb(226, 255, 178);
403
- display: block;
404
- }
405
- span.run64 {
406
- background-color: rgb(229, 255, 178);
407
- display: block;
408
- }
409
- span.run65 {
410
- background-color: rgb(233, 255, 178);
411
- display: block;
412
- }
413
- span.run66 {
414
- background-color: rgb(235, 255, 178);
415
- display: block;
416
- }
417
- span.run67 {
418
- background-color: rgb(238, 255, 178);
419
- display: block;
420
- }
421
- span.run68 {
422
- background-color: rgb(240, 255, 178);
423
- display: block;
424
- }
425
- span.run69 {
426
- background-color: rgb(243, 255, 178);
427
- display: block;
428
- }
429
- span.run70 {
430
- background-color: rgb(247, 255, 178);
431
- display: block;
432
- }
433
- span.run71 {
434
- background-color: rgb(249, 255, 178);
435
- display: block;
436
- }
437
- span.run72 {
438
- background-color: rgb(252, 255, 178);
439
- display: block;
440
- }
441
- span.run73 {
442
- background-color: rgb(255, 255, 178);
443
- display: block;
444
- }
445
- span.run74 {
446
- background-color: rgb(255, 252, 178);
447
- display: block;
448
- }
449
- span.run75 {
450
- background-color: rgb(255, 248, 178);
451
- display: block;
452
- }
453
- span.run76 {
454
- background-color: rgb(255, 246, 178);
455
- display: block;
456
- }
457
- span.run77 {
458
- background-color: rgb(255, 243, 178);
459
- display: block;
460
- }
461
- span.run78 {
462
- background-color: rgb(255, 240, 178);
463
- display: block;
464
- }
465
- span.run79 {
466
- background-color: rgb(255, 238, 178);
467
- display: block;
468
- }
469
- span.run80 {
470
- background-color: rgb(255, 234, 178);
471
- display: block;
472
- }
473
- span.run81 {
474
- background-color: rgb(255, 232, 178);
475
- display: block;
476
- }
477
- span.run82 {
478
- background-color: rgb(255, 229, 178);
479
- display: block;
480
- }
481
- span.run83 {
482
- background-color: rgb(255, 226, 178);
483
- display: block;
484
- }
485
- span.run84 {
486
- background-color: rgb(255, 224, 178);
487
- display: block;
488
- }
489
- span.run85 {
490
- background-color: rgb(255, 220, 178);
491
- display: block;
492
- }
493
- span.run86 {
494
- background-color: rgb(255, 218, 178);
495
- display: block;
496
- }
497
- span.run87 {
498
- background-color: rgb(255, 215, 178);
499
- display: block;
500
- }
501
- span.run88 {
502
- background-color: rgb(255, 212, 178);
503
- display: block;
504
- }
505
- span.run89 {
506
- background-color: rgb(255, 210, 178);
507
- display: block;
508
- }
509
- span.run90 {
510
- background-color: rgb(255, 206, 178);
511
- display: block;
512
- }
513
- span.run91 {
514
- background-color: rgb(255, 204, 178);
515
- display: block;
516
- }
517
- span.run92 {
518
- background-color: rgb(255, 201, 178);
519
- display: block;
520
- }
521
- span.run93 {
522
- background-color: rgb(255, 198, 178);
523
- display: block;
524
- }
525
- span.run94 {
526
- background-color: rgb(255, 196, 178);
527
- display: block;
528
- }
529
- span.run95 {
530
- background-color: rgb(255, 192, 178);
531
- display: block;
532
- }
533
- span.run96 {
534
- background-color: rgb(255, 189, 178);
535
- display: block;
536
- }
537
- span.run97 {
538
- background-color: rgb(255, 187, 178);
539
- display: block;
540
- }
541
- span.run98 {
542
- background-color: rgb(255, 184, 178);
543
- display: block;
544
- }
545
- span.run99 {
546
- background-color: rgb(255, 182, 178);
547
- display: block;
548
- }
549
- span.run100 {
550
- background-color: rgb(255, 178, 178);
551
- display: block;
552
- }
553
- </style>
554
- </head>
555
- <body><h3>C0 code coverage information</h3>
556
- <p>Generated on Sat Aug 23 14:29:19 +0900 2008 with <a href='http://eigenclass.org/hiki/rcov'>rcov 0.8.1.2</a>
557
- </p>
558
- <hr/>
559
- <pre><span class='marked0'>Code reported as executed by Ruby looks like this...
560
- </span><span class='marked1'>and this: this line is also marked as covered.
561
- </span><span class='inferred0'>Lines considered as run by rcov, but not reported by Ruby, look like this,
562
- </span><span class='inferred1'>and this: these lines were inferred by rcov (using simple heuristics).
563
- </span><span class='uncovered0'>Finally, here&apos;s a line marked as not executed.
564
- </span></pre>
565
- <table class='report'><thead><tr><td class='heading'>Name</td>
566
- <td class='heading'>Total lines</td>
567
- <td class='heading'>Lines of code</td>
568
- <td class='heading'>Total coverage</td>
569
- <td class='heading'>Code coverage</td>
570
- </tr>
571
- </thead>
572
- <tbody><tr class='light'><td><a href='-Library-Ruby-Gems-gems-rcov-0_8_1_2_0-lib-rcov_rb.html'>/Library/Ruby/Gems/gems/rcov-0.8.1.2.0/lib/rcov.rb</a>
573
- </td>
574
- <td class='lines_total'><tt>988</tt>
575
- </td>
576
- <td class='lines_code'><tt>602</tt>
577
- </td>
578
- <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>3.3%</tt>
579
- &nbsp;</td>
580
- <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='3'/>
581
- <td class='uncovered' width='97'/>
582
- </tr>
583
- </table>
584
- </td>
585
- </tr>
586
- </table>
587
- </td>
588
- <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>2.8%</tt>
589
- &nbsp;</td>
590
- <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='3'/>
591
- <td class='uncovered' width='97'/>
592
- </tr>
593
- </table>
594
- </td>
595
- </tr>
596
- </table>
597
- </td>
598
- </tr>
599
- </tbody>
600
- </table>
601
- <pre><span class="uncovered1"><a name="line1"></a> 1 # rcov Copyright (c) 2004-2006 Mauricio Fernandez &lt;mfp@acm.org&gt;
602
- </span><span class="uncovered0"><a name="line2"></a> 2 #
603
- </span><span class="uncovered1"><a name="line3"></a> 3 # See LEGAL and LICENSE for licensing information.
604
- </span><span class="uncovered0"><a name="line4"></a> 4
605
- </span><span class="uncovered1"><a name="line5"></a> 5 # NOTE: if you're reading this in the XHTML code coverage report generated by
606
- </span><span class="uncovered0"><a name="line6"></a> 6 # rcov, you'll notice that only code inside methods is reported as covered,
607
- </span><span class="uncovered1"><a name="line7"></a> 7 # very much like what happens when you run it with --test-unit-only.
608
- </span><span class="uncovered0"><a name="line8"></a> 8 # This is due to the fact that we're running rcov on itself: the code below is
609
- </span><span class="uncovered1"><a name="line9"></a> 9 # already loaded before coverage tracing is activated, so only code inside
610
- </span><span class="uncovered0"><a name="line10"></a> 10 # methods is actually executed under rcov's inspection.
611
- </span><span class="uncovered1"><a name="line11"></a> 11
612
- </span><span class="uncovered0"><a name="line12"></a> 12 require 'rcov/version'
613
- </span><span class="uncovered1"><a name="line13"></a> 13
614
- </span><span class="uncovered0"><a name="line14"></a> 14 SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
615
- </span><span class="uncovered1"><a name="line15"></a> 15
616
- </span><span class="uncovered0"><a name="line16"></a> 16 module Rcov
617
- </span><span class="uncovered1"><a name="line17"></a> 17
618
- </span><span class="uncovered0"><a name="line18"></a> 18 # Rcov::CoverageInfo is but a wrapper for an array, with some additional
619
- </span><span class="uncovered1"><a name="line19"></a> 19 # checks. It is returned by FileStatistics#coverage.
620
- </span><span class="uncovered0"><a name="line20"></a> 20 class CoverageInfo
621
- </span><span class="uncovered1"><a name="line21"></a> 21 def initialize(coverage_array)
622
- </span><span class="uncovered0"><a name="line22"></a> 22 @cover = coverage_array.clone
623
- </span><span class="uncovered1"><a name="line23"></a> 23 end
624
- </span><span class="uncovered0"><a name="line24"></a> 24
625
- </span><span class="uncovered1"><a name="line25"></a> 25 # Return the coverage status for the requested line. There are four possible
626
- </span><span class="uncovered0"><a name="line26"></a> 26 # return values:
627
- </span><span class="uncovered1"><a name="line27"></a> 27 # * nil if there's no information for the requested line (i.e. it doesn't exist)
628
- </span><span class="uncovered0"><a name="line28"></a> 28 # * true if the line was reported by Ruby as executed
629
- </span><span class="uncovered1"><a name="line29"></a> 29 # * :inferred if rcov inferred it was executed, despite not being reported
630
- </span><span class="uncovered0"><a name="line30"></a> 30 # by Ruby.
631
- </span><span class="uncovered1"><a name="line31"></a> 31 # * false otherwise, i.e. if it was not reported by Ruby and rcov's
632
- </span><span class="uncovered0"><a name="line32"></a> 32 # heuristics indicated that it was not executed
633
- </span><span class="uncovered1"><a name="line33"></a> 33 def [](line)
634
- </span><span class="uncovered0"><a name="line34"></a> 34 @cover[line]
635
- </span><span class="uncovered1"><a name="line35"></a> 35 end
636
- </span><span class="uncovered0"><a name="line36"></a> 36
637
- </span><span class="uncovered1"><a name="line37"></a> 37 def []=(line, val) # :nodoc:
638
- </span><span class="uncovered0"><a name="line38"></a> 38 unless [true, false, :inferred].include? val
639
- </span><span class="uncovered1"><a name="line39"></a> 39 raise RuntimeError, &quot;What does #{val} mean?&quot;
640
- </span><span class="uncovered0"><a name="line40"></a> 40 end
641
- </span><span class="uncovered1"><a name="line41"></a> 41 return if line &lt; 0 || line &gt;= @cover.size
642
- </span><span class="uncovered0"><a name="line42"></a> 42 @cover[line] = val
643
- </span><span class="uncovered1"><a name="line43"></a> 43 end
644
- </span><span class="uncovered0"><a name="line44"></a> 44
645
- </span><span class="uncovered1"><a name="line45"></a> 45 # Return an Array holding the code coverage information.
646
- </span><span class="uncovered0"><a name="line46"></a> 46 def to_a
647
- </span><span class="uncovered1"><a name="line47"></a> 47 @cover.clone
648
- </span><span class="uncovered0"><a name="line48"></a> 48 end
649
- </span><span class="uncovered1"><a name="line49"></a> 49
650
- </span><span class="uncovered0"><a name="line50"></a> 50 def method_missing(meth, *a, &amp;b) # :nodoc:
651
- </span><span class="uncovered1"><a name="line51"></a> 51 @cover.send(meth, *a, &amp;b)
652
- </span><span class="uncovered0"><a name="line52"></a> 52 end
653
- </span><span class="uncovered1"><a name="line53"></a> 53 end
654
- </span><span class="uncovered0"><a name="line54"></a> 54
655
- </span><span class="uncovered1"><a name="line55"></a> 55 # A FileStatistics object associates a filename to:
656
- </span><span class="uncovered0"><a name="line56"></a> 56 # 1. its source code
657
- </span><span class="uncovered1"><a name="line57"></a> 57 # 2. the per-line coverage information after correction using rcov's heuristics
658
- </span><span class="uncovered0"><a name="line58"></a> 58 # 3. the per-line execution counts
659
- </span><span class="uncovered1"><a name="line59"></a> 59 #
660
- </span><span class="uncovered0"><a name="line60"></a> 60 # A FileStatistics object can be therefore be built given the filename, the
661
- </span><span class="uncovered1"><a name="line61"></a> 61 # associated source code, and an array holding execution counts (i.e. how many
662
- </span><span class="uncovered0"><a name="line62"></a> 62 # times each line has been executed).
663
- </span><span class="uncovered1"><a name="line63"></a> 63 #
664
- </span><span class="uncovered0"><a name="line64"></a> 64 # FileStatistics is relatively intelligent: it handles normal comments,
665
- </span><span class="uncovered1"><a name="line65"></a> 65 # &lt;tt&gt;=begin/=end&lt;/tt&gt;, heredocs, many multiline-expressions... It uses a
666
- </span><span class="uncovered0"><a name="line66"></a> 66 # number of heuristics to determine what is code and what is a comment, and to
667
- </span><span class="uncovered1"><a name="line67"></a> 67 # refine the initial (incomplete) coverage information.
668
- </span><span class="uncovered0"><a name="line68"></a> 68 #
669
- </span><span class="uncovered1"><a name="line69"></a> 69 # Basic usage is as follows:
670
- </span><span class="uncovered0"><a name="line70"></a> 70 # sf = FileStatistics.new(&quot;foo.rb&quot;, [&quot;puts 1&quot;, &quot;if true &amp;&amp;&quot;, &quot; false&quot;,
671
- </span><span class="uncovered1"><a name="line71"></a> 71 # &quot;puts 2&quot;, &quot;end&quot;], [1, 1, 0, 0, 0])
672
- </span><span class="uncovered0"><a name="line72"></a> 72 # sf.num_lines # =&gt; 5
673
- </span><span class="uncovered1"><a name="line73"></a> 73 # sf.num_code_lines # =&gt; 5
674
- </span><span class="uncovered0"><a name="line74"></a> 74 # sf.coverage[2] # =&gt; true
675
- </span><span class="uncovered1"><a name="line75"></a> 75 # sf.coverage[3] # =&gt; :inferred
676
- </span><span class="uncovered0"><a name="line76"></a> 76 # sf.code_coverage # =&gt; 0.6
677
- </span><span class="uncovered1"><a name="line77"></a> 77 #
678
- </span><span class="uncovered0"><a name="line78"></a> 78 # The array of strings representing the source code and the array of execution
679
- </span><span class="uncovered1"><a name="line79"></a> 79 # counts would normally be obtained from a Rcov::CodeCoverageAnalyzer.
680
- </span><span class="uncovered0"><a name="line80"></a> 80 class FileStatistics
681
- </span><span class="uncovered1"><a name="line81"></a> 81 attr_reader :name, :lines, :coverage, :counts
682
- </span><span class="uncovered0"><a name="line82"></a> 82 def initialize(name, lines, counts, comments_run_by_default = false)
683
- </span><span class="uncovered1"><a name="line83"></a> 83 @name = name
684
- </span><span class="uncovered0"><a name="line84"></a> 84 @lines = lines
685
- </span><span class="uncovered1"><a name="line85"></a> 85 initial_coverage = counts.map{|x| (x || 0) &gt; 0 ? true : false }
686
- </span><span class="uncovered0"><a name="line86"></a> 86 @coverage = CoverageInfo.new initial_coverage
687
- </span><span class="uncovered1"><a name="line87"></a> 87 @counts = counts
688
- </span><span class="uncovered0"><a name="line88"></a> 88 @is_begin_comment = nil
689
- </span><span class="uncovered1"><a name="line89"></a> 89 # points to the line defining the heredoc identifier
690
- </span><span class="uncovered0"><a name="line90"></a> 90 # but only if it was marked (we don't care otherwise)
691
- </span><span class="uncovered1"><a name="line91"></a> 91 @heredoc_start = Array.new(lines.size, false)
692
- </span><span class="uncovered0"><a name="line92"></a> 92 @multiline_string_start = Array.new(lines.size, false)
693
- </span><span class="uncovered1"><a name="line93"></a> 93 extend_heredocs
694
- </span><span class="uncovered0"><a name="line94"></a> 94 find_multiline_strings
695
- </span><span class="uncovered1"><a name="line95"></a> 95 precompute_coverage comments_run_by_default
696
- </span><span class="uncovered0"><a name="line96"></a> 96 end
697
- </span><span class="uncovered1"><a name="line97"></a> 97
698
- </span><span class="uncovered0"><a name="line98"></a> 98 # Merge code coverage and execution count information.
699
- </span><span class="uncovered1"><a name="line99"></a> 99 # As for code coverage, a line will be considered
700
- </span><span class="uncovered0"><a name="line100"></a>100 # * covered for sure (true) if it is covered in either +self+ or in the
701
- </span><span class="uncovered1"><a name="line101"></a>101 # +coverage+ array
702
- </span><span class="uncovered0"><a name="line102"></a>102 # * considered &lt;tt&gt;:inferred&lt;/tt&gt; if the neither +self+ nor the +coverage+ array
703
- </span><span class="uncovered1"><a name="line103"></a>103 # indicate that it was definitely executed, but it was &lt;tt&gt;inferred&lt;/tt&gt;
704
- </span><span class="uncovered0"><a name="line104"></a>104 # in either one
705
- </span><span class="uncovered1"><a name="line105"></a>105 # * not covered (&lt;tt&gt;false&lt;/tt&gt;) if it was uncovered in both
706
- </span><span class="uncovered0"><a name="line106"></a>106 #
707
- </span><span class="uncovered1"><a name="line107"></a>107 # Execution counts are just summated on a per-line basis.
708
- </span><span class="uncovered0"><a name="line108"></a>108 def merge(lines, coverage, counts)
709
- </span><span class="uncovered1"><a name="line109"></a>109 coverage.each_with_index do |v, idx|
710
- </span><span class="uncovered0"><a name="line110"></a>110 case @coverage[idx]
711
- </span><span class="uncovered1"><a name="line111"></a>111 when :inferred
712
- </span><span class="uncovered0"><a name="line112"></a>112 @coverage[idx] = v || @coverage[idx]
713
- </span><span class="uncovered1"><a name="line113"></a>113 when false
714
- </span><span class="uncovered0"><a name="line114"></a>114 @coverage[idx] ||= v
715
- </span><span class="uncovered1"><a name="line115"></a>115 end
716
- </span><span class="uncovered0"><a name="line116"></a>116 end
717
- </span><span class="uncovered1"><a name="line117"></a>117 counts.each_with_index{|v, idx| @counts[idx] += v }
718
- </span><span class="uncovered0"><a name="line118"></a>118 precompute_coverage false
719
- </span><span class="uncovered1"><a name="line119"></a>119 end
720
- </span><span class="uncovered0"><a name="line120"></a>120
721
- </span><span class="uncovered1"><a name="line121"></a>121 # Total coverage rate if comments are also considered &quot;executable&quot;, given as
722
- </span><span class="uncovered0"><a name="line122"></a>122 # a fraction, i.e. from 0 to 1.0.
723
- </span><span class="uncovered1"><a name="line123"></a>123 # A comment is attached to the code following it (RDoc-style): it will be
724
- </span><span class="uncovered0"><a name="line124"></a>124 # considered executed if the the next statement was executed.
725
- </span><span class="uncovered1"><a name="line125"></a>125 def total_coverage
726
- </span><span class="uncovered0"><a name="line126"></a>126 return 0 if @coverage.size == 0
727
- </span><span class="uncovered1"><a name="line127"></a>127 @coverage.inject(0.0) {|s,a| s + (a ? 1:0) } / @coverage.size
728
- </span><span class="uncovered0"><a name="line128"></a>128 end
729
- </span><span class="uncovered1"><a name="line129"></a>129
730
- </span><span class="uncovered0"><a name="line130"></a>130 # Code coverage rate: fraction of lines of code executed, relative to the
731
- </span><span class="uncovered1"><a name="line131"></a>131 # total amount of lines of code (loc). Returns a float from 0 to 1.0.
732
- </span><span class="uncovered0"><a name="line132"></a>132 def code_coverage
733
- </span><span class="uncovered1"><a name="line133"></a>133 indices = (0...@lines.size).select{|i| is_code? i }
734
- </span><span class="uncovered0"><a name="line134"></a>134 return 0 if indices.size == 0
735
- </span><span class="uncovered1"><a name="line135"></a>135 count = 0
736
- </span><span class="uncovered0"><a name="line136"></a>136 indices.each {|i| count += 1 if @coverage[i] }
737
- </span><span class="uncovered1"><a name="line137"></a>137 1.0 * count / indices.size
738
- </span><span class="uncovered0"><a name="line138"></a>138 end
739
- </span><span class="uncovered1"><a name="line139"></a>139
740
- </span><span class="uncovered0"><a name="line140"></a>140 # Number of lines of code (loc).
741
- </span><span class="uncovered1"><a name="line141"></a>141 def num_code_lines
742
- </span><span class="uncovered0"><a name="line142"></a>142 (0...@lines.size).select{|i| is_code? i}.size
743
- </span><span class="uncovered1"><a name="line143"></a>143 end
744
- </span><span class="uncovered0"><a name="line144"></a>144
745
- </span><span class="uncovered1"><a name="line145"></a>145 # Total number of lines.
746
- </span><span class="uncovered0"><a name="line146"></a>146 def num_lines
747
- </span><span class="uncovered1"><a name="line147"></a>147 @lines.size
748
- </span><span class="uncovered0"><a name="line148"></a>148 end
749
- </span><span class="uncovered1"><a name="line149"></a>149
750
- </span><span class="uncovered0"><a name="line150"></a>150 # Returns true if the given line number corresponds to code, as opposed to a
751
- </span><span class="uncovered1"><a name="line151"></a>151 # comment (either # or =begin/=end blocks).
752
- </span><span class="uncovered0"><a name="line152"></a>152 def is_code?(lineno)
753
- </span><span class="uncovered1"><a name="line153"></a>153 unless @is_begin_comment
754
- </span><span class="uncovered0"><a name="line154"></a>154 @is_begin_comment = Array.new(@lines.size, false)
755
- </span><span class="uncovered1"><a name="line155"></a>155 pending = []
756
- </span><span class="uncovered0"><a name="line156"></a>156 state = :code
757
- </span><span class="uncovered1"><a name="line157"></a>157 @lines.each_with_index do |line, index|
758
- </span><span class="uncovered0"><a name="line158"></a>158 case state
759
- </span><span class="uncovered1"><a name="line159"></a>159 when :code
760
- </span><span class="uncovered0"><a name="line160"></a>160 if /^=begin\b/ =~ line
761
- </span><span class="uncovered1"><a name="line161"></a>161 state = :comment
762
- </span><span class="uncovered0"><a name="line162"></a>162 pending &lt;&lt; index
763
- </span><span class="uncovered1"><a name="line163"></a>163 end
764
- </span><span class="uncovered0"><a name="line164"></a>164 when :comment
765
- </span><span class="uncovered1"><a name="line165"></a>165 pending &lt;&lt; index
766
- </span><span class="uncovered0"><a name="line166"></a>166 if /^=end\b/ =~ line
767
- </span><span class="uncovered1"><a name="line167"></a>167 state = :code
768
- </span><span class="uncovered0"><a name="line168"></a>168 pending.each{|idx| @is_begin_comment[idx] = true}
769
- </span><span class="uncovered1"><a name="line169"></a>169 pending.clear
770
- </span><span class="uncovered0"><a name="line170"></a>170 end
771
- </span><span class="uncovered1"><a name="line171"></a>171 end
772
- </span><span class="uncovered0"><a name="line172"></a>172 end
773
- </span><span class="uncovered1"><a name="line173"></a>173 end
774
- </span><span class="uncovered0"><a name="line174"></a>174 @lines[lineno] &amp;&amp; !@is_begin_comment[lineno] &amp;&amp;
775
- </span><span class="uncovered1"><a name="line175"></a>175 @lines[lineno] !~ /^\s*(#|$)/
776
- </span><span class="uncovered0"><a name="line176"></a>176 end
777
- </span><span class="uncovered1"><a name="line177"></a>177
778
- </span><span class="uncovered0"><a name="line178"></a>178 private
779
- </span><span class="uncovered1"><a name="line179"></a>179
780
- </span><span class="uncovered0"><a name="line180"></a>180 def find_multiline_strings
781
- </span><span class="uncovered1"><a name="line181"></a>181 state = :awaiting_string
782
- </span><span class="uncovered0"><a name="line182"></a>182 wanted_delimiter = nil
783
- </span><span class="uncovered1"><a name="line183"></a>183 string_begin_line = 0
784
- </span><span class="uncovered0"><a name="line184"></a>184 @lines.each_with_index do |line, i|
785
- </span><span class="uncovered1"><a name="line185"></a>185 matching_delimiters = Hash.new{|h,k| k}
786
- </span><span class="uncovered0"><a name="line186"></a>186 matching_delimiters.update(&quot;{&quot; =&gt; &quot;}&quot;, &quot;[&quot; =&gt; &quot;]&quot;, &quot;(&quot; =&gt; &quot;)&quot;)
787
- </span><span class="uncovered1"><a name="line187"></a>187 case state
788
- </span><span class="uncovered0"><a name="line188"></a>188 when :awaiting_string
789
- </span><span class="uncovered1"><a name="line189"></a>189 # very conservative, doesn't consider the last delimited string but
790
- </span><span class="uncovered0"><a name="line190"></a>190 # only the very first one
791
- </span><span class="uncovered1"><a name="line191"></a>191 if md = /^[^#]*%(?:[qQ])?(.)/.match(line)
792
- </span><span class="uncovered0"><a name="line192"></a>192 wanted_delimiter = /(?!\\).#{Regexp.escape(matching_delimiters[md[1]])}/
793
- </span><span class="uncovered1"><a name="line193"></a>193 # check if closed on the very same line
794
- </span><span class="uncovered0"><a name="line194"></a>194 # conservative again, we might have several quoted strings with the
795
- </span><span class="uncovered1"><a name="line195"></a>195 # same delimiter on the same line, leaving the last one open
796
- </span><span class="uncovered0"><a name="line196"></a>196 unless wanted_delimiter.match(md.post_match)
797
- </span><span class="uncovered1"><a name="line197"></a>197 state = :want_end_delimiter
798
- </span><span class="uncovered0"><a name="line198"></a>198 string_begin_line = i
799
- </span><span class="uncovered1"><a name="line199"></a>199 end
800
- </span><span class="uncovered0"><a name="line200"></a>200 end
801
- </span><span class="uncovered1"><a name="line201"></a>201 when :want_end_delimiter
802
- </span><span class="uncovered0"><a name="line202"></a>202 @multiline_string_start[i] = string_begin_line
803
- </span><span class="uncovered1"><a name="line203"></a>203 if wanted_delimiter.match(line)
804
- </span><span class="uncovered0"><a name="line204"></a>204 state = :awaiting_string
805
- </span><span class="uncovered1"><a name="line205"></a>205 end
806
- </span><span class="uncovered0"><a name="line206"></a>206 end
807
- </span><span class="uncovered1"><a name="line207"></a>207 end
808
- </span><span class="uncovered0"><a name="line208"></a>208 end
809
- </span><span class="uncovered1"><a name="line209"></a>209
810
- </span><span class="uncovered0"><a name="line210"></a>210 def precompute_coverage(comments_run_by_default = true)
811
- </span><span class="uncovered1"><a name="line211"></a>211 changed = false
812
- </span><span class="uncovered0"><a name="line212"></a>212 lastidx = lines.size - 1
813
- </span><span class="uncovered1"><a name="line213"></a>213 if (!is_code?(lastidx) || /^__END__$/ =~ @lines[-1]) &amp;&amp; !@coverage[lastidx]
814
- </span><span class="uncovered0"><a name="line214"></a>214 # mark the last block of comments
815
- </span><span class="uncovered1"><a name="line215"></a>215 @coverage[lastidx] ||= :inferred
816
- </span><span class="uncovered0"><a name="line216"></a>216 (lastidx-1).downto(0) do |i|
817
- </span><span class="uncovered1"><a name="line217"></a>217 break if is_code?(i)
818
- </span><span class="uncovered0"><a name="line218"></a>218 @coverage[i] ||= :inferred
819
- </span><span class="uncovered1"><a name="line219"></a>219 end
820
- </span><span class="uncovered0"><a name="line220"></a>220 end
821
- </span><span class="uncovered1"><a name="line221"></a>221 (0...lines.size).each do |i|
822
- </span><span class="uncovered0"><a name="line222"></a>222 next if @coverage[i]
823
- </span><span class="uncovered1"><a name="line223"></a>223 line = @lines[i]
824
- </span><span class="uncovered0"><a name="line224"></a>224 if /^\s*(begin|ensure|else|case)\s*(?:#.*)?$/ =~ line &amp;&amp; next_expr_marked?(i) or
825
- </span><span class="uncovered1"><a name="line225"></a>225 /^\s*(?:end|\})\s*(?:#.*)?$/ =~ line &amp;&amp; prev_expr_marked?(i) or
826
- </span><span class="uncovered0"><a name="line226"></a>226 /^\s*(?:end\b|\})/ =~ line &amp;&amp; prev_expr_marked?(i) &amp;&amp; next_expr_marked?(i) or
827
- </span><span class="uncovered1"><a name="line227"></a>227 /^\s*rescue\b/ =~ line &amp;&amp; next_expr_marked?(i) or
828
- </span><span class="uncovered0"><a name="line228"></a>228 /(do|\{)\s*(\|[^|]*\|\s*)?(?:#.*)?$/ =~ line &amp;&amp; next_expr_marked?(i) or
829
- </span><span class="uncovered1"><a name="line229"></a>229 prev_expr_continued?(i) &amp;&amp; prev_expr_marked?(i) or
830
- </span><span class="uncovered0"><a name="line230"></a>230 comments_run_by_default &amp;&amp; !is_code?(i) or
831
- </span><span class="uncovered1"><a name="line231"></a>231 /^\s*((\)|\]|\})\s*)+(?:#.*)?$/ =~ line &amp;&amp; prev_expr_marked?(i) or
832
- </span><span class="uncovered0"><a name="line232"></a>232 prev_expr_continued?(i+1) &amp;&amp; next_expr_marked?(i)
833
- </span><span class="uncovered1"><a name="line233"></a>233 @coverage[i] ||= :inferred
834
- </span><span class="uncovered0"><a name="line234"></a>234 changed = true
835
- </span><span class="uncovered1"><a name="line235"></a>235 end
836
- </span><span class="uncovered0"><a name="line236"></a>236 end
837
- </span><span class="uncovered1"><a name="line237"></a>237 (@lines.size-1).downto(0) do |i|
838
- </span><span class="uncovered0"><a name="line238"></a>238 next if @coverage[i]
839
- </span><span class="uncovered1"><a name="line239"></a>239 if !is_code?(i) and @coverage[i+1]
840
- </span><span class="uncovered0"><a name="line240"></a>240 @coverage[i] = :inferred
841
- </span><span class="uncovered1"><a name="line241"></a>241 changed = true
842
- </span><span class="uncovered0"><a name="line242"></a>242 end
843
- </span><span class="uncovered1"><a name="line243"></a>243 end
844
- </span><span class="uncovered0"><a name="line244"></a>244
845
- </span><span class="uncovered1"><a name="line245"></a>245 extend_heredocs if changed
846
- </span><span class="uncovered0"><a name="line246"></a>246
847
- </span><span class="uncovered1"><a name="line247"></a>247 # if there was any change, we have to recompute; we'll eventually
848
- </span><span class="uncovered0"><a name="line248"></a>248 # reach a fixed point and stop there
849
- </span><span class="uncovered1"><a name="line249"></a>249 precompute_coverage(comments_run_by_default) if changed
850
- </span><span class="uncovered0"><a name="line250"></a>250 end
851
- </span><span class="uncovered1"><a name="line251"></a>251
852
- </span><span class="uncovered0"><a name="line252"></a>252 require 'strscan'
853
- </span><span class="uncovered1"><a name="line253"></a>253 def extend_heredocs
854
- </span><span class="uncovered0"><a name="line254"></a>254 i = 0
855
- </span><span class="uncovered1"><a name="line255"></a>255 while i &lt; @lines.size
856
- </span><span class="uncovered0"><a name="line256"></a>256 unless is_code? i
857
- </span><span class="uncovered1"><a name="line257"></a>257 i += 1
858
- </span><span class="uncovered0"><a name="line258"></a>258 next
859
- </span><span class="uncovered1"><a name="line259"></a>259 end
860
- </span><span class="uncovered0"><a name="line260"></a>260 #FIXME: using a restrictive regexp so that only &lt;&lt;[A-Z_a-z]\w*
861
- </span><span class="uncovered1"><a name="line261"></a>261 # matches when unquoted, so as to avoid problems with 1&lt;&lt;2
862
- </span><span class="uncovered0"><a name="line262"></a>262 # (keep in mind that whereas puts &lt;&lt;2 is valid, puts 1&lt;&lt;2 is a
863
- </span><span class="uncovered1"><a name="line263"></a>263 # parse error, but a = 1&lt;&lt;2 is of course fine)
864
- </span><span class="uncovered0"><a name="line264"></a>264 scanner = StringScanner.new(@lines[i])
865
- </span><span class="uncovered1"><a name="line265"></a>265 j = k = i
866
- </span><span class="uncovered0"><a name="line266"></a>266 loop do
867
- </span><span class="uncovered1"><a name="line267"></a>267 scanned_text = scanner.search_full(/&lt;&lt;(-?)(?:(['&quot;`])((?:(?!\2).)+)\2|([A-Z_a-z]\w*))/,
868
- </span><span class="uncovered0"><a name="line268"></a>268 true, true)
869
- </span><span class="uncovered1"><a name="line269"></a>269 # k is the first line after the end delimiter for the last heredoc
870
- </span><span class="uncovered0"><a name="line270"></a>270 # scanned so far
871
- </span><span class="uncovered1"><a name="line271"></a>271 unless scanner.matched?
872
- </span><span class="uncovered0"><a name="line272"></a>272 i = k
873
- </span><span class="uncovered1"><a name="line273"></a>273 break
874
- </span><span class="uncovered0"><a name="line274"></a>274 end
875
- </span><span class="uncovered1"><a name="line275"></a>275 term = scanner[3] || scanner[4]
876
- </span><span class="uncovered0"><a name="line276"></a>276 # try to ignore symbolic bitshifts like 1&lt;&lt;LSHIFT
877
- </span><span class="uncovered1"><a name="line277"></a>277 ident_text = &quot;&lt;&lt;#{scanner[1]}#{scanner[2]}#{term}#{scanner[2]}&quot;
878
- </span><span class="uncovered0"><a name="line278"></a>278 if scanned_text[/\d+\s*#{Regexp.escape(ident_text)}/]
879
- </span><span class="uncovered1"><a name="line279"></a>279 # it was preceded by a number, ignore
880
- </span><span class="uncovered0"><a name="line280"></a>280 i = k
881
- </span><span class="uncovered1"><a name="line281"></a>281 break
882
- </span><span class="uncovered0"><a name="line282"></a>282 end
883
- </span><span class="uncovered1"><a name="line283"></a>283 must_mark = []
884
- </span><span class="uncovered0"><a name="line284"></a>284 end_of_heredoc = (scanner[1] == &quot;-&quot;) ?
885
- </span><span class="uncovered1"><a name="line285"></a>285 /^\s*#{Regexp.escape(term)}$/ : /^#{Regexp.escape(term)}$/
886
- </span><span class="uncovered0"><a name="line286"></a>286 loop do
887
- </span><span class="uncovered1"><a name="line287"></a>287 break if j == @lines.size
888
- </span><span class="uncovered0"><a name="line288"></a>288 must_mark &lt;&lt; j
889
- </span><span class="uncovered1"><a name="line289"></a>289 if end_of_heredoc =~ @lines[j]
890
- </span><span class="uncovered0"><a name="line290"></a>290 must_mark.each do |n|
891
- </span><span class="uncovered1"><a name="line291"></a>291 @heredoc_start[n] = i
892
- </span><span class="uncovered0"><a name="line292"></a>292 end
893
- </span><span class="uncovered1"><a name="line293"></a>293 if (must_mark + [i]).any?{|lineidx| @coverage[lineidx]}
894
- </span><span class="uncovered0"><a name="line294"></a>294 @coverage[i] ||= :inferred
895
- </span><span class="uncovered1"><a name="line295"></a>295 must_mark.each{|lineidx| @coverage[lineidx] ||= :inferred}
896
- </span><span class="uncovered0"><a name="line296"></a>296 end
897
- </span><span class="uncovered1"><a name="line297"></a>297 # move the &quot;first line after heredocs&quot; index
898
- </span><span class="uncovered0"><a name="line298"></a>298 k = (j += 1)
899
- </span><span class="uncovered1"><a name="line299"></a>299 break
900
- </span><span class="uncovered0"><a name="line300"></a>300 end
901
- </span><span class="uncovered1"><a name="line301"></a>301 j += 1
902
- </span><span class="uncovered0"><a name="line302"></a>302 end
903
- </span><span class="uncovered1"><a name="line303"></a>303 end
904
- </span><span class="uncovered0"><a name="line304"></a>304
905
- </span><span class="uncovered1"><a name="line305"></a>305 i += 1
906
- </span><span class="uncovered0"><a name="line306"></a>306 end
907
- </span><span class="uncovered1"><a name="line307"></a>307 end
908
- </span><span class="uncovered0"><a name="line308"></a>308
909
- </span><span class="uncovered1"><a name="line309"></a>309 def next_expr_marked?(lineno)
910
- </span><span class="uncovered0"><a name="line310"></a>310 return false if lineno &gt;= @lines.size
911
- </span><span class="uncovered1"><a name="line311"></a>311 found = false
912
- </span><span class="uncovered0"><a name="line312"></a>312 idx = (lineno+1).upto(@lines.size-1) do |i|
913
- </span><span class="uncovered1"><a name="line313"></a>313 next unless is_code? i
914
- </span><span class="uncovered0"><a name="line314"></a>314 found = true
915
- </span><span class="uncovered1"><a name="line315"></a>315 break i
916
- </span><span class="uncovered0"><a name="line316"></a>316 end
917
- </span><span class="uncovered1"><a name="line317"></a>317 return false unless found
918
- </span><span class="uncovered0"><a name="line318"></a>318 @coverage[idx]
919
- </span><span class="uncovered1"><a name="line319"></a>319 end
920
- </span><span class="uncovered0"><a name="line320"></a>320
921
- </span><span class="uncovered1"><a name="line321"></a>321 def prev_expr_marked?(lineno)
922
- </span><span class="uncovered0"><a name="line322"></a>322 return false if lineno &lt;= 0
923
- </span><span class="uncovered1"><a name="line323"></a>323 found = false
924
- </span><span class="uncovered0"><a name="line324"></a>324 idx = (lineno-1).downto(0) do |i|
925
- </span><span class="uncovered1"><a name="line325"></a>325 next unless is_code? i
926
- </span><span class="uncovered0"><a name="line326"></a>326 found = true
927
- </span><span class="uncovered1"><a name="line327"></a>327 break i
928
- </span><span class="uncovered0"><a name="line328"></a>328 end
929
- </span><span class="uncovered1"><a name="line329"></a>329 return false unless found
930
- </span><span class="uncovered0"><a name="line330"></a>330 @coverage[idx]
931
- </span><span class="uncovered1"><a name="line331"></a>331 end
932
- </span><span class="uncovered0"><a name="line332"></a>332
933
- </span><span class="uncovered1"><a name="line333"></a>333 def prev_expr_continued?(lineno)
934
- </span><span class="uncovered0"><a name="line334"></a>334 return false if lineno &lt;= 0
935
- </span><span class="uncovered1"><a name="line335"></a>335 return false if lineno &gt;= @lines.size
936
- </span><span class="uncovered0"><a name="line336"></a>336 found = false
937
- </span><span class="uncovered1"><a name="line337"></a>337 if @multiline_string_start[lineno] &amp;&amp;
938
- </span><span class="uncovered0"><a name="line338"></a>338 @multiline_string_start[lineno] &lt; lineno
939
- </span><span class="uncovered1"><a name="line339"></a>339 return true
940
- </span><span class="uncovered0"><a name="line340"></a>340 end
941
- </span><span class="uncovered1"><a name="line341"></a>341 # find index of previous code line
942
- </span><span class="uncovered0"><a name="line342"></a>342 idx = (lineno-1).downto(0) do |i|
943
- </span><span class="uncovered1"><a name="line343"></a>343 if @heredoc_start[i]
944
- </span><span class="uncovered0"><a name="line344"></a>344 found = true
945
- </span><span class="uncovered1"><a name="line345"></a>345 break @heredoc_start[i]
946
- </span><span class="uncovered0"><a name="line346"></a>346 end
947
- </span><span class="uncovered1"><a name="line347"></a>347 next unless is_code? i
948
- </span><span class="uncovered0"><a name="line348"></a>348 found = true
949
- </span><span class="uncovered1"><a name="line349"></a>349 break i
950
- </span><span class="uncovered0"><a name="line350"></a>350 end
951
- </span><span class="uncovered1"><a name="line351"></a>351 return false unless found
952
- </span><span class="uncovered0"><a name="line352"></a>352 #TODO: write a comprehensive list
953
- </span><span class="uncovered1"><a name="line353"></a>353 if is_code?(lineno) &amp;&amp; /^\s*((\)|\]|\})\s*)+(?:#.*)?$/.match(@lines[lineno])
954
- </span><span class="uncovered0"><a name="line354"></a>354 return true
955
- </span><span class="uncovered1"><a name="line355"></a>355 end
956
- </span><span class="uncovered0"><a name="line356"></a>356 #FIXME: / matches regexps too
957
- </span><span class="uncovered1"><a name="line357"></a>357 # the following regexp tries to reject #{interpolation}
958
- </span><span class="uncovered0"><a name="line358"></a>358 r = /(,|\.|\+|-|\*|\/|&lt;|&gt;|%|&amp;&amp;|\|\||&lt;&lt;|\(|\[|\{|=|and|or|\\)\s*(?:#(?![{$@]).*)?$/.match @lines[idx]
959
- </span><span class="uncovered1"><a name="line359"></a>359 # try to see if a multi-line expression with opening, closing delimiters
960
- </span><span class="uncovered0"><a name="line360"></a>360 # started on that line
961
- </span><span class="uncovered1"><a name="line361"></a>361 [%w!( )!].each do |opening_str, closing_str|
962
- </span><span class="uncovered0"><a name="line362"></a>362 # conservative: only consider nesting levels opened in that line, not
963
- </span><span class="uncovered1"><a name="line363"></a>363 # previous ones too.
964
- </span><span class="uncovered0"><a name="line364"></a>364 # next regexp considers interpolation too
965
- </span><span class="uncovered1"><a name="line365"></a>365 line = @lines[idx].gsub(/#(?![{$@]).*$/, &quot;&quot;)
966
- </span><span class="uncovered0"><a name="line366"></a>366 opened = line.scan(/#{Regexp.escape(opening_str)}/).size
967
- </span><span class="uncovered1"><a name="line367"></a>367 closed = line.scan(/#{Regexp.escape(closing_str)}/).size
968
- </span><span class="uncovered0"><a name="line368"></a>368 return true if opened - closed &gt; 0
969
- </span><span class="uncovered1"><a name="line369"></a>369 end
970
- </span><span class="uncovered0"><a name="line370"></a>370 if /(do|\{)\s*\|[^|]*\|\s*(?:#.*)?$/.match @lines[idx]
971
- </span><span class="uncovered1"><a name="line371"></a>371 return false
972
- </span><span class="uncovered0"><a name="line372"></a>372 end
973
- </span><span class="uncovered1"><a name="line373"></a>373
974
- </span><span class="uncovered0"><a name="line374"></a>374 r
975
- </span><span class="uncovered1"><a name="line375"></a>375 end
976
- </span><span class="uncovered0"><a name="line376"></a>376 end
977
- </span><span class="uncovered1"><a name="line377"></a>377
978
- </span><span class="uncovered0"><a name="line378"></a>378
979
- </span><span class="uncovered1"><a name="line379"></a>379 autoload :RCOV__, &quot;rcov/lowlevel.rb&quot;
980
- </span><span class="uncovered0"><a name="line380"></a>380
981
- </span><span class="uncovered1"><a name="line381"></a>381 class DifferentialAnalyzer
982
- </span><span class="uncovered0"><a name="line382"></a>382 require 'thread'
983
- </span><span class="uncovered1"><a name="line383"></a>383 @@mutex = Mutex.new
984
- </span><span class="uncovered0"><a name="line384"></a>384
985
- </span><span class="uncovered1"><a name="line385"></a>385 def initialize(install_hook_meth, remove_hook_meth, reset_meth)
986
- </span><span class="uncovered0"><a name="line386"></a>386 @cache_state = :wait
987
- </span><span class="uncovered1"><a name="line387"></a>387 @start_raw_data = data_default
988
- </span><span class="uncovered0"><a name="line388"></a>388 @end_raw_data = data_default
989
- </span><span class="uncovered1"><a name="line389"></a>389 @aggregated_data = data_default
990
- </span><span class="uncovered0"><a name="line390"></a>390 @install_hook_meth = install_hook_meth
991
- </span><span class="uncovered1"><a name="line391"></a>391 @remove_hook_meth= remove_hook_meth
992
- </span><span class="uncovered0"><a name="line392"></a>392 @reset_meth= reset_meth
993
- </span><span class="uncovered1"><a name="line393"></a>393 end
994
- </span><span class="uncovered0"><a name="line394"></a>394
995
- </span><span class="uncovered1"><a name="line395"></a>395 # Execute the code in the given block, monitoring it in order to gather
996
- </span><span class="uncovered0"><a name="line396"></a>396 # information about which code was executed.
997
- </span><span class="uncovered1"><a name="line397"></a>397 def run_hooked
998
- </span><span class="uncovered0"><a name="line398"></a>398 install_hook
999
- </span><span class="uncovered1"><a name="line399"></a>399 yield
1000
- </span><span class="uncovered0"><a name="line400"></a>400 ensure
1001
- </span><span class="uncovered1"><a name="line401"></a>401 remove_hook
1002
- </span><span class="uncovered0"><a name="line402"></a>402 end
1003
- </span><span class="uncovered1"><a name="line403"></a>403
1004
- </span><span class="uncovered0"><a name="line404"></a>404 # Start monitoring execution to gather information. Such data will be
1005
- </span><span class="uncovered1"><a name="line405"></a>405 # collected until #remove_hook is called.
1006
- </span><span class="uncovered0"><a name="line406"></a>406 #
1007
- </span><span class="uncovered1"><a name="line407"></a>407 # Use #run_hooked instead if possible.
1008
- </span><span class="uncovered0"><a name="line408"></a>408 def install_hook
1009
- </span><span class="marked1"><a name="line409"></a>409 @start_raw_data = raw_data_absolute
1010
- </span><span class="uncovered0"><a name="line410"></a>410 Rcov::RCOV__.send(@install_hook_meth)
1011
- </span><span class="marked1"><a name="line411"></a>411 @cache_state = :hooked
1012
- </span><span class="marked0"><a name="line412"></a>412 @@mutex.synchronize{ self.class.hook_level += 1 }
1013
- </span><span class="inferred1"><a name="line413"></a>413 end
1014
- </span><span class="inferred0"><a name="line414"></a>414
1015
- </span><span class="inferred1"><a name="line415"></a>415 # Stop collecting information.
1016
- </span><span class="inferred0"><a name="line416"></a>416 # #remove_hook will also stop collecting info if it is run inside a
1017
- </span><span class="inferred1"><a name="line417"></a>417 # #run_hooked block.
1018
- </span><span class="marked0"><a name="line418"></a>418 def remove_hook
1019
- </span><span class="marked1"><a name="line419"></a>419 @@mutex.synchronize do
1020
- </span><span class="marked0"><a name="line420"></a>420 self.class.hook_level -= 1
1021
- </span><span class="marked1"><a name="line421"></a>421 Rcov::RCOV__.send(@remove_hook_meth) if self.class.hook_level == 0
1022
- </span><span class="inferred0"><a name="line422"></a>422 end
1023
- </span><span class="uncovered1"><a name="line423"></a>423 @end_raw_data = raw_data_absolute
1024
- </span><span class="uncovered0"><a name="line424"></a>424 @cache_state = :done
1025
- </span><span class="uncovered1"><a name="line425"></a>425 # force computation of the stats for the traced code in this run;
1026
- </span><span class="uncovered0"><a name="line426"></a>426 # we cannot simply let it be if self.class.hook_level == 0 because
1027
- </span><span class="uncovered1"><a name="line427"></a>427 # some other analyzer could install a hook, causing the raw_data_absolute
1028
- </span><span class="uncovered0"><a name="line428"></a>428 # to change again.
1029
- </span><span class="uncovered1"><a name="line429"></a>429 # TODO: lazy computation of raw_data_relative, only when the hook gets
1030
- </span><span class="uncovered0"><a name="line430"></a>430 # activated again.
1031
- </span><span class="uncovered1"><a name="line431"></a>431 raw_data_relative
1032
- </span><span class="uncovered0"><a name="line432"></a>432 end
1033
- </span><span class="uncovered1"><a name="line433"></a>433
1034
- </span><span class="uncovered0"><a name="line434"></a>434 # Remove the data collected so far. Further collection will start from
1035
- </span><span class="uncovered1"><a name="line435"></a>435 # scratch.
1036
- </span><span class="uncovered0"><a name="line436"></a>436 def reset
1037
- </span><span class="uncovered1"><a name="line437"></a>437 @@mutex.synchronize do
1038
- </span><span class="uncovered0"><a name="line438"></a>438 if self.class.hook_level == 0
1039
- </span><span class="uncovered1"><a name="line439"></a>439 # Unfortunately there's no way to report this as covered with rcov:
1040
- </span><span class="uncovered0"><a name="line440"></a>440 # if we run the tests under rcov self.class.hook_level will be &gt;= 1 !
1041
- </span><span class="uncovered1"><a name="line441"></a>441 # It is however executed when we run the tests normally.
1042
- </span><span class="uncovered0"><a name="line442"></a>442 Rcov::RCOV__.send(@reset_meth)
1043
- </span><span class="uncovered1"><a name="line443"></a>443 @start_raw_data = data_default
1044
- </span><span class="uncovered0"><a name="line444"></a>444 @end_raw_data = data_default
1045
- </span><span class="uncovered1"><a name="line445"></a>445 else
1046
- </span><span class="uncovered0"><a name="line446"></a>446 @start_raw_data = @end_raw_data = raw_data_absolute
1047
- </span><span class="uncovered1"><a name="line447"></a>447 end
1048
- </span><span class="uncovered0"><a name="line448"></a>448 @raw_data_relative = data_default
1049
- </span><span class="uncovered1"><a name="line449"></a>449 @aggregated_data = data_default
1050
- </span><span class="uncovered0"><a name="line450"></a>450 end
1051
- </span><span class="uncovered1"><a name="line451"></a>451 end
1052
- </span><span class="uncovered0"><a name="line452"></a>452
1053
- </span><span class="uncovered1"><a name="line453"></a>453 protected
1054
- </span><span class="uncovered0"><a name="line454"></a>454
1055
- </span><span class="uncovered1"><a name="line455"></a>455 def data_default
1056
- </span><span class="uncovered0"><a name="line456"></a>456 raise &quot;must be implemented by the subclass&quot;
1057
- </span><span class="uncovered1"><a name="line457"></a>457 end
1058
- </span><span class="uncovered0"><a name="line458"></a>458
1059
- </span><span class="uncovered1"><a name="line459"></a>459 def self.hook_level
1060
- </span><span class="uncovered0"><a name="line460"></a>460 raise &quot;must be implemented by the subclass&quot;
1061
- </span><span class="uncovered1"><a name="line461"></a>461 end
1062
- </span><span class="uncovered0"><a name="line462"></a>462
1063
- </span><span class="uncovered1"><a name="line463"></a>463 def raw_data_absolute
1064
- </span><span class="uncovered0"><a name="line464"></a>464 raise &quot;must be implemented by the subclass&quot;
1065
- </span><span class="uncovered1"><a name="line465"></a>465 end
1066
- </span><span class="uncovered0"><a name="line466"></a>466
1067
- </span><span class="uncovered1"><a name="line467"></a>467 def aggregate_data(aggregated_data, delta)
1068
- </span><span class="uncovered0"><a name="line468"></a>468 raise &quot;must be implemented by the subclass&quot;
1069
- </span><span class="uncovered1"><a name="line469"></a>469 end
1070
- </span><span class="uncovered0"><a name="line470"></a>470
1071
- </span><span class="uncovered1"><a name="line471"></a>471 def compute_raw_data_difference(first, last)
1072
- </span><span class="uncovered0"><a name="line472"></a>472 raise &quot;must be implemented by the subclass&quot;
1073
- </span><span class="uncovered1"><a name="line473"></a>473 end
1074
- </span><span class="uncovered0"><a name="line474"></a>474
1075
- </span><span class="uncovered1"><a name="line475"></a>475 private
1076
- </span><span class="uncovered0"><a name="line476"></a>476 def raw_data_relative
1077
- </span><span class="uncovered1"><a name="line477"></a>477 case @cache_state
1078
- </span><span class="uncovered0"><a name="line478"></a>478 when :wait
1079
- </span><span class="uncovered1"><a name="line479"></a>479 return @aggregated_data
1080
- </span><span class="uncovered0"><a name="line480"></a>480 when :hooked
1081
- </span><span class="uncovered1"><a name="line481"></a>481 new_start = raw_data_absolute
1082
- </span><span class="uncovered0"><a name="line482"></a>482 new_diff = compute_raw_data_difference(@start_raw_data, new_start)
1083
- </span><span class="uncovered1"><a name="line483"></a>483 @start_raw_data = new_start
1084
- </span><span class="uncovered0"><a name="line484"></a>484 when :done
1085
- </span><span class="uncovered1"><a name="line485"></a>485 @cache_state = :wait
1086
- </span><span class="uncovered0"><a name="line486"></a>486 new_diff = compute_raw_data_difference(@start_raw_data,
1087
- </span><span class="uncovered1"><a name="line487"></a>487 @end_raw_data)
1088
- </span><span class="uncovered0"><a name="line488"></a>488 end
1089
- </span><span class="uncovered1"><a name="line489"></a>489
1090
- </span><span class="uncovered0"><a name="line490"></a>490 aggregate_data(@aggregated_data, new_diff)
1091
- </span><span class="uncovered1"><a name="line491"></a>491
1092
- </span><span class="uncovered0"><a name="line492"></a>492 @aggregated_data
1093
- </span><span class="uncovered1"><a name="line493"></a>493 end
1094
- </span><span class="uncovered0"><a name="line494"></a>494
1095
- </span><span class="uncovered1"><a name="line495"></a>495 end
1096
- </span><span class="uncovered0"><a name="line496"></a>496
1097
- </span><span class="uncovered1"><a name="line497"></a>497 # A CodeCoverageAnalyzer is responsible for tracing code execution and
1098
- </span><span class="uncovered0"><a name="line498"></a>498 # returning code coverage and execution count information.
1099
- </span><span class="uncovered1"><a name="line499"></a>499 #
1100
- </span><span class="uncovered0"><a name="line500"></a>500 # Note that you must &lt;tt&gt;require 'rcov'&lt;/tt&gt; before the code you want to
1101
- </span><span class="uncovered1"><a name="line501"></a>501 # analyze is parsed (i.e. before it gets loaded or required). You can do that
1102
- </span><span class="uncovered0"><a name="line502"></a>502 # by either invoking ruby with the &lt;tt&gt;-rrcov&lt;/tt&gt; command-line option or
1103
- </span><span class="uncovered1"><a name="line503"></a>503 # just:
1104
- </span><span class="uncovered0"><a name="line504"></a>504 # require 'rcov'
1105
- </span><span class="uncovered1"><a name="line505"></a>505 # require 'mycode'
1106
- </span><span class="uncovered0"><a name="line506"></a>506 # # ....
1107
- </span><span class="uncovered1"><a name="line507"></a>507 #
1108
- </span><span class="uncovered0"><a name="line508"></a>508 # == Example
1109
- </span><span class="uncovered1"><a name="line509"></a>509 #
1110
- </span><span class="uncovered0"><a name="line510"></a>510 # analyzer = Rcov::CodeCoverageAnalyzer.new
1111
- </span><span class="uncovered1"><a name="line511"></a>511 # analyzer.run_hooked do
1112
- </span><span class="uncovered0"><a name="line512"></a>512 # do_foo
1113
- </span><span class="uncovered1"><a name="line513"></a>513 # # all the code executed as a result of this method call is traced
1114
- </span><span class="uncovered0"><a name="line514"></a>514 # end
1115
- </span><span class="uncovered1"><a name="line515"></a>515 # # ....
1116
- </span><span class="uncovered0"><a name="line516"></a>516 #
1117
- </span><span class="uncovered1"><a name="line517"></a>517 # analyzer.run_hooked do
1118
- </span><span class="uncovered0"><a name="line518"></a>518 # do_bar
1119
- </span><span class="uncovered1"><a name="line519"></a>519 # # the code coverage information generated in this run is aggregated
1120
- </span><span class="uncovered0"><a name="line520"></a>520 # # to the previously recorded one
1121
- </span><span class="uncovered1"><a name="line521"></a>521 # end
1122
- </span><span class="uncovered0"><a name="line522"></a>522 #
1123
- </span><span class="uncovered1"><a name="line523"></a>523 # analyzer.analyzed_files # =&gt; [&quot;foo.rb&quot;, &quot;bar.rb&quot;, ... ]
1124
- </span><span class="uncovered0"><a name="line524"></a>524 # lines, marked_info, count_info = analyzer.data(&quot;foo.rb&quot;)
1125
- </span><span class="uncovered1"><a name="line525"></a>525 #
1126
- </span><span class="uncovered0"><a name="line526"></a>526 # In this example, two pieces of code are monitored, and the data generated in
1127
- </span><span class="uncovered1"><a name="line527"></a>527 # both runs are aggregated. +lines+ is an array of strings representing the
1128
- </span><span class="uncovered0"><a name="line528"></a>528 # source code of &lt;tt&gt;foo.rb&lt;/tt&gt;. +marked_info+ is an array holding false,
1129
- </span><span class="uncovered1"><a name="line529"></a>529 # true values indicating whether the corresponding lines of code were reported
1130
- </span><span class="uncovered0"><a name="line530"></a>530 # as executed by Ruby. +count_info+ is an array of integers representing how
1131
- </span><span class="uncovered1"><a name="line531"></a>531 # many times each line of code has been executed (more precisely, how many
1132
- </span><span class="uncovered0"><a name="line532"></a>532 # events where reported by Ruby --- a single line might correspond to several
1133
- </span><span class="uncovered1"><a name="line533"></a>533 # events, e.g. many method calls).
1134
- </span><span class="uncovered0"><a name="line534"></a>534 #
1135
- </span><span class="uncovered1"><a name="line535"></a>535 # You can have several CodeCoverageAnalyzer objects at a time, and it is
1136
- </span><span class="uncovered0"><a name="line536"></a>536 # possible to nest the #run_hooked / #install_hook/#remove_hook blocks: each
1137
- </span><span class="uncovered1"><a name="line537"></a>537 # analyzer will manage its data separately. Note however that no special
1138
- </span><span class="uncovered0"><a name="line538"></a>538 # provision is taken to ignore code executed &quot;inside&quot; the CodeCoverageAnalyzer
1139
- </span><span class="uncovered1"><a name="line539"></a>539 # class. At any rate this will not pose a problem since it's easy to ignore it
1140
- </span><span class="uncovered0"><a name="line540"></a>540 # manually: just don't do
1141
- </span><span class="uncovered1"><a name="line541"></a>541 # lines, coverage, counts = analyzer.data(&quot;/path/to/lib/rcov.rb&quot;)
1142
- </span><span class="uncovered0"><a name="line542"></a>542 # if you're not interested in that information.
1143
- </span><span class="uncovered1"><a name="line543"></a>543 class CodeCoverageAnalyzer &lt; DifferentialAnalyzer
1144
- </span><span class="uncovered0"><a name="line544"></a>544 @hook_level = 0
1145
- </span><span class="inferred1"><a name="line545"></a>545 # defined this way instead of attr_accessor so that it's covered
1146
- </span><span class="marked0"><a name="line546"></a>546 def self.hook_level # :nodoc:
1147
- </span><span class="marked1"><a name="line547"></a>547 @hook_level
1148
- </span><span class="marked0"><a name="line548"></a>548 end
1149
- </span><span class="marked1"><a name="line549"></a>549 def self.hook_level=(x) # :nodoc:
1150
- </span><span class="marked0"><a name="line550"></a>550 @hook_level = x
1151
- </span><span class="marked1"><a name="line551"></a>551 end
1152
- </span><span class="uncovered0"><a name="line552"></a>552
1153
- </span><span class="uncovered1"><a name="line553"></a>553 def initialize
1154
- </span><span class="uncovered0"><a name="line554"></a>554 @script_lines__ = SCRIPT_LINES__
1155
- </span><span class="uncovered1"><a name="line555"></a>555 super(:install_coverage_hook, :remove_coverage_hook,
1156
- </span><span class="uncovered0"><a name="line556"></a>556 :reset_coverage)
1157
- </span><span class="uncovered1"><a name="line557"></a>557 end
1158
- </span><span class="uncovered0"><a name="line558"></a>558
1159
- </span><span class="uncovered1"><a name="line559"></a>559 # Return an array with the names of the files whose code was executed inside
1160
- </span><span class="uncovered0"><a name="line560"></a>560 # the block given to #run_hooked or between #install_hook and #remove_hook.
1161
- </span><span class="uncovered1"><a name="line561"></a>561 def analyzed_files
1162
- </span><span class="uncovered0"><a name="line562"></a>562 update_script_lines__
1163
- </span><span class="uncovered1"><a name="line563"></a>563 raw_data_relative.select do |file, lines|
1164
- </span><span class="uncovered0"><a name="line564"></a>564 @script_lines__.has_key?(file)
1165
- </span><span class="uncovered1"><a name="line565"></a>565 end.map{|fname,| fname}
1166
- </span><span class="uncovered0"><a name="line566"></a>566 end
1167
- </span><span class="uncovered1"><a name="line567"></a>567
1168
- </span><span class="uncovered0"><a name="line568"></a>568 # Return the available data about the requested file, or nil if none of its
1169
- </span><span class="uncovered1"><a name="line569"></a>569 # code was executed or it cannot be found.
1170
- </span><span class="uncovered0"><a name="line570"></a>570 # The return value is an array with three elements:
1171
- </span><span class="uncovered1"><a name="line571"></a>571 # lines, marked_info, count_info = analyzer.data(&quot;foo.rb&quot;)
1172
- </span><span class="uncovered0"><a name="line572"></a>572 # +lines+ is an array of strings representing the
1173
- </span><span class="uncovered1"><a name="line573"></a>573 # source code of &lt;tt&gt;foo.rb&lt;/tt&gt;. +marked_info+ is an array holding false,
1174
- </span><span class="uncovered0"><a name="line574"></a>574 # true values indicating whether the corresponding lines of code were reported
1175
- </span><span class="uncovered1"><a name="line575"></a>575 # as executed by Ruby. +count_info+ is an array of integers representing how
1176
- </span><span class="uncovered0"><a name="line576"></a>576 # many times each line of code has been executed (more precisely, how many
1177
- </span><span class="uncovered1"><a name="line577"></a>577 # events where reported by Ruby --- a single line might correspond to several
1178
- </span><span class="uncovered0"><a name="line578"></a>578 # events, e.g. many method calls).
1179
- </span><span class="uncovered1"><a name="line579"></a>579 #
1180
- </span><span class="uncovered0"><a name="line580"></a>580 # The returned data corresponds to the aggregation of all the statistics
1181
- </span><span class="uncovered1"><a name="line581"></a>581 # collected in each #run_hooked or #install_hook/#remove_hook runs. You can
1182
- </span><span class="uncovered0"><a name="line582"></a>582 # reset the data at any time with #reset to start from scratch.
1183
- </span><span class="uncovered1"><a name="line583"></a>583 def data(filename)
1184
- </span><span class="uncovered0"><a name="line584"></a>584 raw_data = raw_data_relative
1185
- </span><span class="uncovered1"><a name="line585"></a>585 update_script_lines__
1186
- </span><span class="uncovered0"><a name="line586"></a>586 unless @script_lines__.has_key?(filename) &amp;&amp;
1187
- </span><span class="uncovered1"><a name="line587"></a>587 raw_data.has_key?(filename)
1188
- </span><span class="uncovered0"><a name="line588"></a>588 return nil
1189
- </span><span class="uncovered1"><a name="line589"></a>589 end
1190
- </span><span class="uncovered0"><a name="line590"></a>590 refine_coverage_info(@script_lines__[filename], raw_data[filename])
1191
- </span><span class="uncovered1"><a name="line591"></a>591 end
1192
- </span><span class="uncovered0"><a name="line592"></a>592
1193
- </span><span class="uncovered1"><a name="line593"></a>593 # Data for the first file matching the given regexp.
1194
- </span><span class="uncovered0"><a name="line594"></a>594 # See #data.
1195
- </span><span class="uncovered1"><a name="line595"></a>595 def data_matching(filename_re)
1196
- </span><span class="uncovered0"><a name="line596"></a>596 raw_data = raw_data_relative
1197
- </span><span class="uncovered1"><a name="line597"></a>597 update_script_lines__
1198
- </span><span class="uncovered0"><a name="line598"></a>598
1199
- </span><span class="uncovered1"><a name="line599"></a>599 match = raw_data.keys.sort.grep(filename_re).first
1200
- </span><span class="uncovered0"><a name="line600"></a>600 return nil unless match
1201
- </span><span class="uncovered1"><a name="line601"></a>601
1202
- </span><span class="uncovered0"><a name="line602"></a>602 refine_coverage_info(@script_lines__[match], raw_data[match])
1203
- </span><span class="uncovered1"><a name="line603"></a>603 end
1204
- </span><span class="uncovered0"><a name="line604"></a>604
1205
- </span><span class="uncovered1"><a name="line605"></a>605 # Execute the code in the given block, monitoring it in order to gather
1206
- </span><span class="uncovered0"><a name="line606"></a>606 # information about which code was executed.
1207
- </span><span class="uncovered1"><a name="line607"></a>607 def run_hooked; super end
1208
- </span><span class="inferred0"><a name="line608"></a>608
1209
- </span><span class="inferred1"><a name="line609"></a>609 # Start monitoring execution to gather code coverage and execution count
1210
- </span><span class="inferred0"><a name="line610"></a>610 # information. Such data will be collected until #remove_hook is called.
1211
- </span><span class="inferred1"><a name="line611"></a>611 #
1212
- </span><span class="inferred0"><a name="line612"></a>612 # Use #run_hooked instead if possible.
1213
- </span><span class="marked1"><a name="line613"></a>613 def install_hook; super end
1214
- </span><span class="inferred0"><a name="line614"></a>614
1215
- </span><span class="inferred1"><a name="line615"></a>615 # Stop collecting code coverage and execution count information.
1216
- </span><span class="inferred0"><a name="line616"></a>616 # #remove_hook will also stop collecting info if it is run inside a
1217
- </span><span class="inferred1"><a name="line617"></a>617 # #run_hooked block.
1218
- </span><span class="marked0"><a name="line618"></a>618 def remove_hook; super end
1219
- </span><span class="uncovered1"><a name="line619"></a>619
1220
- </span><span class="uncovered0"><a name="line620"></a>620 # Remove the data collected so far. The coverage and execution count
1221
- </span><span class="uncovered1"><a name="line621"></a>621 # &quot;history&quot; will be erased, and further collection will start from scratch:
1222
- </span><span class="uncovered0"><a name="line622"></a>622 # no code is considered executed, and therefore all execution counts are 0.
1223
- </span><span class="uncovered1"><a name="line623"></a>623 # Right after #reset, #analyzed_files will return an empty array, and
1224
- </span><span class="uncovered0"><a name="line624"></a>624 # #data(filename) will return nil.
1225
- </span><span class="uncovered1"><a name="line625"></a>625 def reset; super end
1226
- </span><span class="uncovered0"><a name="line626"></a>626
1227
- </span><span class="uncovered1"><a name="line627"></a>627 def dump_coverage_info(formatters) # :nodoc:
1228
- </span><span class="uncovered0"><a name="line628"></a>628 update_script_lines__
1229
- </span><span class="uncovered1"><a name="line629"></a>629 raw_data_relative.each do |file, lines|
1230
- </span><span class="uncovered0"><a name="line630"></a>630 next if @script_lines__.has_key?(file) == false
1231
- </span><span class="uncovered1"><a name="line631"></a>631 lines = @script_lines__[file]
1232
- </span><span class="uncovered0"><a name="line632"></a>632 raw_coverage_array = raw_data_relative[file]
1233
- </span><span class="uncovered1"><a name="line633"></a>633
1234
- </span><span class="uncovered0"><a name="line634"></a>634 line_info, marked_info,
1235
- </span><span class="uncovered1"><a name="line635"></a>635 count_info = refine_coverage_info(lines, raw_coverage_array)
1236
- </span><span class="uncovered0"><a name="line636"></a>636 formatters.each do |formatter|
1237
- </span><span class="uncovered1"><a name="line637"></a>637 formatter.add_file(file, line_info, marked_info, count_info)
1238
- </span><span class="uncovered0"><a name="line638"></a>638 end
1239
- </span><span class="uncovered1"><a name="line639"></a>639 end
1240
- </span><span class="uncovered0"><a name="line640"></a>640 formatters.each{|formatter| formatter.execute}
1241
- </span><span class="uncovered1"><a name="line641"></a>641 end
1242
- </span><span class="uncovered0"><a name="line642"></a>642
1243
- </span><span class="uncovered1"><a name="line643"></a>643 private
1244
- </span><span class="uncovered0"><a name="line644"></a>644
1245
- </span><span class="uncovered1"><a name="line645"></a>645 def data_default; {} end
1246
- </span><span class="uncovered0"><a name="line646"></a>646
1247
- </span><span class="uncovered1"><a name="line647"></a>647 def raw_data_absolute
1248
- </span><span class="uncovered0"><a name="line648"></a>648 Rcov::RCOV__.generate_coverage_info
1249
- </span><span class="uncovered1"><a name="line649"></a>649 end
1250
- </span><span class="uncovered0"><a name="line650"></a>650
1251
- </span><span class="uncovered1"><a name="line651"></a>651 def aggregate_data(aggregated_data, delta)
1252
- </span><span class="uncovered0"><a name="line652"></a>652 delta.each_pair do |file, cov_arr|
1253
- </span><span class="uncovered1"><a name="line653"></a>653 dest = (aggregated_data[file] ||= Array.new(cov_arr.size, 0))
1254
- </span><span class="uncovered0"><a name="line654"></a>654 cov_arr.each_with_index{|x,i| dest[i] += x}
1255
- </span><span class="uncovered1"><a name="line655"></a>655 end
1256
- </span><span class="uncovered0"><a name="line656"></a>656 end
1257
- </span><span class="uncovered1"><a name="line657"></a>657
1258
- </span><span class="uncovered0"><a name="line658"></a>658 def compute_raw_data_difference(first, last)
1259
- </span><span class="uncovered1"><a name="line659"></a>659 difference = {}
1260
- </span><span class="uncovered0"><a name="line660"></a>660 last.each_pair do |fname, cov_arr|
1261
- </span><span class="uncovered1"><a name="line661"></a>661 unless first.has_key?(fname)
1262
- </span><span class="uncovered0"><a name="line662"></a>662 difference[fname] = cov_arr.clone
1263
- </span><span class="uncovered1"><a name="line663"></a>663 else
1264
- </span><span class="uncovered0"><a name="line664"></a>664 orig_arr = first[fname]
1265
- </span><span class="uncovered1"><a name="line665"></a>665 diff_arr = Array.new(cov_arr.size, 0)
1266
- </span><span class="uncovered0"><a name="line666"></a>666 changed = false
1267
- </span><span class="uncovered1"><a name="line667"></a>667 cov_arr.each_with_index do |x, i|
1268
- </span><span class="uncovered0"><a name="line668"></a>668 diff_arr[i] = diff = (x || 0) - (orig_arr[i] || 0)
1269
- </span><span class="uncovered1"><a name="line669"></a>669 changed = true if diff != 0
1270
- </span><span class="uncovered0"><a name="line670"></a>670 end
1271
- </span><span class="uncovered1"><a name="line671"></a>671 difference[fname] = diff_arr if changed
1272
- </span><span class="uncovered0"><a name="line672"></a>672 end
1273
- </span><span class="uncovered1"><a name="line673"></a>673 end
1274
- </span><span class="uncovered0"><a name="line674"></a>674 difference
1275
- </span><span class="uncovered1"><a name="line675"></a>675 end
1276
- </span><span class="uncovered0"><a name="line676"></a>676
1277
- </span><span class="uncovered1"><a name="line677"></a>677
1278
- </span><span class="uncovered0"><a name="line678"></a>678 def refine_coverage_info(lines, covers)
1279
- </span><span class="uncovered1"><a name="line679"></a>679 marked_info = []
1280
- </span><span class="uncovered0"><a name="line680"></a>680 count_info = []
1281
- </span><span class="uncovered1"><a name="line681"></a>681 lines.size.times do |i|
1282
- </span><span class="uncovered0"><a name="line682"></a>682 c = covers[i]
1283
- </span><span class="uncovered1"><a name="line683"></a>683 marked_info &lt;&lt; ((c &amp;&amp; c &gt; 0) ? true : false)
1284
- </span><span class="uncovered0"><a name="line684"></a>684 count_info &lt;&lt; (c || 0)
1285
- </span><span class="uncovered1"><a name="line685"></a>685 end
1286
- </span><span class="uncovered0"><a name="line686"></a>686
1287
- </span><span class="uncovered1"><a name="line687"></a>687 script_lines_workaround(lines, marked_info, count_info)
1288
- </span><span class="uncovered0"><a name="line688"></a>688 end
1289
- </span><span class="uncovered1"><a name="line689"></a>689
1290
- </span><span class="uncovered0"><a name="line690"></a>690 # Try to detect repeated data, based on observed repetitions in line_info:
1291
- </span><span class="uncovered1"><a name="line691"></a>691 # this is a workaround for SCRIPT_LINES__[filename] including as many copies
1292
- </span><span class="uncovered0"><a name="line692"></a>692 # of the file as the number of times it was parsed.
1293
- </span><span class="uncovered1"><a name="line693"></a>693 def script_lines_workaround(line_info, coverage_info, count_info)
1294
- </span><span class="uncovered0"><a name="line694"></a>694 is_repeated = lambda do |div|
1295
- </span><span class="uncovered1"><a name="line695"></a>695 n = line_info.size / div
1296
- </span><span class="uncovered0"><a name="line696"></a>696 break false unless line_info.size % div == 0 &amp;&amp; n &gt; 1
1297
- </span><span class="uncovered1"><a name="line697"></a>697 different = false
1298
- </span><span class="uncovered0"><a name="line698"></a>698 n.times do |i|
1299
- </span><span class="uncovered1"><a name="line699"></a>699 if (0...div).map{|j| line_info[i+j*n]}.uniq.size != 1
1300
- </span><span class="uncovered0"><a name="line700"></a>700 different = true
1301
- </span><span class="uncovered1"><a name="line701"></a>701 break
1302
- </span><span class="uncovered0"><a name="line702"></a>702 end
1303
- </span><span class="uncovered1"><a name="line703"></a>703 end
1304
- </span><span class="uncovered0"><a name="line704"></a>704
1305
- </span><span class="uncovered1"><a name="line705"></a>705 ! different
1306
- </span><span class="uncovered0"><a name="line706"></a>706 end
1307
- </span><span class="uncovered1"><a name="line707"></a>707
1308
- </span><span class="uncovered0"><a name="line708"></a>708 factors = braindead_factorize(line_info.size)
1309
- </span><span class="uncovered1"><a name="line709"></a>709 factors.each do |n|
1310
- </span><span class="uncovered0"><a name="line710"></a>710 if is_repeated[n]
1311
- </span><span class="uncovered1"><a name="line711"></a>711 line_info = line_info[0, line_info.size / n]
1312
- </span><span class="uncovered0"><a name="line712"></a>712 coverage_info = coverage_info[0, coverage_info.size / n]
1313
- </span><span class="uncovered1"><a name="line713"></a>713 count_info = count_info[0, count_info.size / n]
1314
- </span><span class="uncovered0"><a name="line714"></a>714 end
1315
- </span><span class="uncovered1"><a name="line715"></a>715 end if factors.size &gt; 1 # don't even try if it's prime
1316
- </span><span class="uncovered0"><a name="line716"></a>716
1317
- </span><span class="uncovered1"><a name="line717"></a>717 [line_info, coverage_info, count_info]
1318
- </span><span class="uncovered0"><a name="line718"></a>718 end
1319
- </span><span class="uncovered1"><a name="line719"></a>719
1320
- </span><span class="uncovered0"><a name="line720"></a>720 def braindead_factorize(num)
1321
- </span><span class="uncovered1"><a name="line721"></a>721 return [0] if num == 0
1322
- </span><span class="uncovered0"><a name="line722"></a>722 return [-1] + braindead_factorize(-num) if num &lt; 0
1323
- </span><span class="uncovered1"><a name="line723"></a>723 factors = []
1324
- </span><span class="uncovered0"><a name="line724"></a>724 while num % 2 == 0
1325
- </span><span class="uncovered1"><a name="line725"></a>725 factors &lt;&lt; 2
1326
- </span><span class="uncovered0"><a name="line726"></a>726 num /= 2
1327
- </span><span class="uncovered1"><a name="line727"></a>727 end
1328
- </span><span class="uncovered0"><a name="line728"></a>728 size = num
1329
- </span><span class="uncovered1"><a name="line729"></a>729 n = 3
1330
- </span><span class="uncovered0"><a name="line730"></a>730 max = Math.sqrt(num)
1331
- </span><span class="uncovered1"><a name="line731"></a>731 while n &lt;= max &amp;&amp; n &lt;= size
1332
- </span><span class="uncovered0"><a name="line732"></a>732 while size % n == 0
1333
- </span><span class="uncovered1"><a name="line733"></a>733 size /= n
1334
- </span><span class="uncovered0"><a name="line734"></a>734 factors &lt;&lt; n
1335
- </span><span class="uncovered1"><a name="line735"></a>735 end
1336
- </span><span class="uncovered0"><a name="line736"></a>736 n += 2
1337
- </span><span class="uncovered1"><a name="line737"></a>737 end
1338
- </span><span class="uncovered0"><a name="line738"></a>738 factors &lt;&lt; size if size != 1
1339
- </span><span class="uncovered1"><a name="line739"></a>739 factors
1340
- </span><span class="uncovered0"><a name="line740"></a>740 end
1341
- </span><span class="uncovered1"><a name="line741"></a>741
1342
- </span><span class="uncovered0"><a name="line742"></a>742 def update_script_lines__
1343
- </span><span class="uncovered1"><a name="line743"></a>743 @script_lines__ = @script_lines__.merge(SCRIPT_LINES__)
1344
- </span><span class="uncovered0"><a name="line744"></a>744 end
1345
- </span><span class="uncovered1"><a name="line745"></a>745
1346
- </span><span class="uncovered0"><a name="line746"></a>746 public
1347
- </span><span class="uncovered1"><a name="line747"></a>747 def marshal_dump # :nodoc:
1348
- </span><span class="uncovered0"><a name="line748"></a>748 # @script_lines__ is updated just before serialization so as to avoid
1349
- </span><span class="uncovered1"><a name="line749"></a>749 # missing files in SCRIPT_LINES__
1350
- </span><span class="uncovered0"><a name="line750"></a>750 ivs = {}
1351
- </span><span class="uncovered1"><a name="line751"></a>751 update_script_lines__
1352
- </span><span class="uncovered0"><a name="line752"></a>752 instance_variables.each{|iv| ivs[iv] = instance_variable_get(iv)}
1353
- </span><span class="uncovered1"><a name="line753"></a>753 ivs
1354
- </span><span class="uncovered0"><a name="line754"></a>754 end
1355
- </span><span class="uncovered1"><a name="line755"></a>755
1356
- </span><span class="uncovered0"><a name="line756"></a>756 def marshal_load(ivs) # :nodoc:
1357
- </span><span class="uncovered1"><a name="line757"></a>757 ivs.each_pair{|iv, val| instance_variable_set(iv, val)}
1358
- </span><span class="uncovered0"><a name="line758"></a>758 end
1359
- </span><span class="uncovered1"><a name="line759"></a>759
1360
- </span><span class="uncovered0"><a name="line760"></a>760 end # CodeCoverageAnalyzer
1361
- </span><span class="uncovered1"><a name="line761"></a>761
1362
- </span><span class="uncovered0"><a name="line762"></a>762 # A CallSiteAnalyzer can be used to obtain information about:
1363
- </span><span class="uncovered1"><a name="line763"></a>763 # * where a method is defined (&quot;+defsite+&quot;)
1364
- </span><span class="uncovered0"><a name="line764"></a>764 # * where a method was called from (&quot;+callsite+&quot;)
1365
- </span><span class="uncovered1"><a name="line765"></a>765 #
1366
- </span><span class="uncovered0"><a name="line766"></a>766 # == Example
1367
- </span><span class="uncovered1"><a name="line767"></a>767 # &lt;tt&gt;example.rb&lt;/tt&gt;:
1368
- </span><span class="uncovered0"><a name="line768"></a>768 # class X
1369
- </span><span class="uncovered1"><a name="line769"></a>769 # def f1; f2 end
1370
- </span><span class="uncovered0"><a name="line770"></a>770 # def f2; 1 + 1 end
1371
- </span><span class="uncovered1"><a name="line771"></a>771 # def f3; f1 end
1372
- </span><span class="uncovered0"><a name="line772"></a>772 # end
1373
- </span><span class="uncovered1"><a name="line773"></a>773 #
1374
- </span><span class="uncovered0"><a name="line774"></a>774 # analyzer = Rcov::CallSiteAnalyzer.new
1375
- </span><span class="uncovered1"><a name="line775"></a>775 # x = X.new
1376
- </span><span class="uncovered0"><a name="line776"></a>776 # analyzer.run_hooked do
1377
- </span><span class="uncovered1"><a name="line777"></a>777 # x.f1
1378
- </span><span class="uncovered0"><a name="line778"></a>778 # end
1379
- </span><span class="uncovered1"><a name="line779"></a>779 # # ....
1380
- </span><span class="uncovered0"><a name="line780"></a>780 #
1381
- </span><span class="uncovered1"><a name="line781"></a>781 # analyzer.run_hooked do
1382
- </span><span class="uncovered0"><a name="line782"></a>782 # x.f3
1383
- </span><span class="uncovered1"><a name="line783"></a>783 # # the information generated in this run is aggregated
1384
- </span><span class="uncovered0"><a name="line784"></a>784 # # to the previously recorded one
1385
- </span><span class="uncovered1"><a name="line785"></a>785 # end
1386
- </span><span class="uncovered0"><a name="line786"></a>786 #
1387
- </span><span class="uncovered1"><a name="line787"></a>787 # analyzer.analyzed_classes # =&gt; [&quot;X&quot;, ... ]
1388
- </span><span class="uncovered0"><a name="line788"></a>788 # analyzer.methods_for_class(&quot;X&quot;) # =&gt; [&quot;f1&quot;, &quot;f2&quot;, &quot;f3&quot;]
1389
- </span><span class="uncovered1"><a name="line789"></a>789 # analyzer.defsite(&quot;X#f1&quot;) # =&gt; DefSite object
1390
- </span><span class="uncovered0"><a name="line790"></a>790 # analyzer.callsites(&quot;X#f2&quot;) # =&gt; hash with CallSite =&gt; count
1391
- </span><span class="uncovered1"><a name="line791"></a>791 # # associations
1392
- </span><span class="uncovered0"><a name="line792"></a>792 # defsite = analyzer.defsite(&quot;X#f1&quot;)
1393
- </span><span class="uncovered1"><a name="line793"></a>793 # defsite.file # =&gt; &quot;example.rb&quot;
1394
- </span><span class="uncovered0"><a name="line794"></a>794 # defsite.line # =&gt; 2
1395
- </span><span class="uncovered1"><a name="line795"></a>795 #
1396
- </span><span class="uncovered0"><a name="line796"></a>796 # You can have several CallSiteAnalyzer objects at a time, and it is
1397
- </span><span class="uncovered1"><a name="line797"></a>797 # possible to nest the #run_hooked / #install_hook/#remove_hook blocks: each
1398
- </span><span class="uncovered0"><a name="line798"></a>798 # analyzer will manage its data separately. Note however that no special
1399
- </span><span class="uncovered1"><a name="line799"></a>799 # provision is taken to ignore code executed &quot;inside&quot; the CallSiteAnalyzer
1400
- </span><span class="uncovered0"><a name="line800"></a>800 # class.
1401
- </span><span class="uncovered1"><a name="line801"></a>801 #
1402
- </span><span class="uncovered0"><a name="line802"></a>802 # +defsite+ information is only available for methods that were called under
1403
- </span><span class="uncovered1"><a name="line803"></a>803 # the inspection of the CallSiteAnalyzer, i.o.w. you will only have +defsite+
1404
- </span><span class="uncovered0"><a name="line804"></a>804 # information for those methods for which callsite information is
1405
- </span><span class="uncovered1"><a name="line805"></a>805 # available.
1406
- </span><span class="uncovered0"><a name="line806"></a>806 class CallSiteAnalyzer &lt; DifferentialAnalyzer
1407
- </span><span class="uncovered1"><a name="line807"></a>807 # A method definition site.
1408
- </span><span class="uncovered0"><a name="line808"></a>808 class DefSite &lt; Struct.new(:file, :line)
1409
- </span><span class="uncovered1"><a name="line809"></a>809 end
1410
- </span><span class="uncovered0"><a name="line810"></a>810
1411
- </span><span class="uncovered1"><a name="line811"></a>811 # Object representing a method call site.
1412
- </span><span class="uncovered0"><a name="line812"></a>812 # It corresponds to a part of the callstack starting from the context that
1413
- </span><span class="uncovered1"><a name="line813"></a>813 # called the method.
1414
- </span><span class="uncovered0"><a name="line814"></a>814 class CallSite &lt; Struct.new(:backtrace)
1415
- </span><span class="uncovered1"><a name="line815"></a>815 # The depth of a CallSite is the number of stack frames
1416
- </span><span class="uncovered0"><a name="line816"></a>816 # whose information is included in the CallSite object.
1417
- </span><span class="uncovered1"><a name="line817"></a>817 def depth
1418
- </span><span class="uncovered0"><a name="line818"></a>818 backtrace.size
1419
- </span><span class="uncovered1"><a name="line819"></a>819 end
1420
- </span><span class="uncovered0"><a name="line820"></a>820
1421
- </span><span class="uncovered1"><a name="line821"></a>821 # File where the method call originated.
1422
- </span><span class="uncovered0"><a name="line822"></a>822 # Might return +nil+ or &quot;&quot; if it is not meaningful (C extensions, etc).
1423
- </span><span class="uncovered1"><a name="line823"></a>823 def file(level = 0)
1424
- </span><span class="uncovered0"><a name="line824"></a>824 stack_frame = backtrace[level]
1425
- </span><span class="uncovered1"><a name="line825"></a>825 stack_frame ? stack_frame[2] : nil
1426
- </span><span class="uncovered0"><a name="line826"></a>826 end
1427
- </span><span class="uncovered1"><a name="line827"></a>827
1428
- </span><span class="uncovered0"><a name="line828"></a>828 # Line where the method call originated.
1429
- </span><span class="uncovered1"><a name="line829"></a>829 # Might return +nil+ or 0 if it is not meaningful (C extensions, etc).
1430
- </span><span class="uncovered0"><a name="line830"></a>830 def line(level = 0)
1431
- </span><span class="uncovered1"><a name="line831"></a>831 stack_frame = backtrace[level]
1432
- </span><span class="uncovered0"><a name="line832"></a>832 stack_frame ? stack_frame[3] : nil
1433
- </span><span class="uncovered1"><a name="line833"></a>833 end
1434
- </span><span class="uncovered0"><a name="line834"></a>834
1435
- </span><span class="uncovered1"><a name="line835"></a>835 # Name of the method where the call originated.
1436
- </span><span class="uncovered0"><a name="line836"></a>836 # Returns +nil+ if the call originated in +toplevel+.
1437
- </span><span class="uncovered1"><a name="line837"></a>837 # Might return +nil+ if it could not be determined.
1438
- </span><span class="uncovered0"><a name="line838"></a>838 def calling_method(level = 0)
1439
- </span><span class="uncovered1"><a name="line839"></a>839 stack_frame = backtrace[level]
1440
- </span><span class="uncovered0"><a name="line840"></a>840 stack_frame ? stack_frame[1] : nil
1441
- </span><span class="uncovered1"><a name="line841"></a>841 end
1442
- </span><span class="uncovered0"><a name="line842"></a>842
1443
- </span><span class="uncovered1"><a name="line843"></a>843 # Name of the class holding the method where the call originated.
1444
- </span><span class="uncovered0"><a name="line844"></a>844 # Might return +nil+ if it could not be determined.
1445
- </span><span class="uncovered1"><a name="line845"></a>845 def calling_class(level = 0)
1446
- </span><span class="uncovered0"><a name="line846"></a>846 stack_frame = backtrace[level]
1447
- </span><span class="uncovered1"><a name="line847"></a>847 stack_frame ? stack_frame[0] : nil
1448
- </span><span class="uncovered0"><a name="line848"></a>848 end
1449
- </span><span class="uncovered1"><a name="line849"></a>849 end
1450
- </span><span class="uncovered0"><a name="line850"></a>850
1451
- </span><span class="uncovered1"><a name="line851"></a>851 @hook_level = 0
1452
- </span><span class="uncovered0"><a name="line852"></a>852 # defined this way instead of attr_accessor so that it's covered
1453
- </span><span class="uncovered1"><a name="line853"></a>853 def self.hook_level # :nodoc:
1454
- </span><span class="uncovered0"><a name="line854"></a>854 @hook_level
1455
- </span><span class="uncovered1"><a name="line855"></a>855 end
1456
- </span><span class="uncovered0"><a name="line856"></a>856 def self.hook_level=(x) # :nodoc:
1457
- </span><span class="uncovered1"><a name="line857"></a>857 @hook_level = x
1458
- </span><span class="uncovered0"><a name="line858"></a>858 end
1459
- </span><span class="uncovered1"><a name="line859"></a>859
1460
- </span><span class="uncovered0"><a name="line860"></a>860 def initialize
1461
- </span><span class="uncovered1"><a name="line861"></a>861 super(:install_callsite_hook, :remove_callsite_hook,
1462
- </span><span class="uncovered0"><a name="line862"></a>862 :reset_callsite)
1463
- </span><span class="uncovered1"><a name="line863"></a>863 end
1464
- </span><span class="uncovered0"><a name="line864"></a>864
1465
- </span><span class="uncovered1"><a name="line865"></a>865 # Classes whose methods have been called.
1466
- </span><span class="uncovered0"><a name="line866"></a>866 # Returns an array of strings describing the classes (just klass.to_s for
1467
- </span><span class="uncovered1"><a name="line867"></a>867 # each of them). Singleton classes are rendered as:
1468
- </span><span class="uncovered0"><a name="line868"></a>868 # #&lt;Class:MyNamespace::MyClass&gt;
1469
- </span><span class="uncovered1"><a name="line869"></a>869 def analyzed_classes
1470
- </span><span class="uncovered0"><a name="line870"></a>870 raw_data_relative.first.keys.map{|klass, meth| klass}.uniq.sort
1471
- </span><span class="uncovered1"><a name="line871"></a>871 end
1472
- </span><span class="uncovered0"><a name="line872"></a>872
1473
- </span><span class="uncovered1"><a name="line873"></a>873 # Methods that were called for the given class. See #analyzed_classes for
1474
- </span><span class="uncovered0"><a name="line874"></a>874 # the notation used for singleton classes.
1475
- </span><span class="uncovered1"><a name="line875"></a>875 # Returns an array of strings or +nil+
1476
- </span><span class="uncovered0"><a name="line876"></a>876 def methods_for_class(classname)
1477
- </span><span class="uncovered1"><a name="line877"></a>877 a = raw_data_relative.first.keys.select{|kl,_| kl == classname}.map{|_,meth| meth}.sort
1478
- </span><span class="uncovered0"><a name="line878"></a>878 a.empty? ? nil : a
1479
- </span><span class="uncovered1"><a name="line879"></a>879 end
1480
- </span><span class="uncovered0"><a name="line880"></a>880 alias_method :analyzed_methods, :methods_for_class
1481
- </span><span class="uncovered1"><a name="line881"></a>881
1482
- </span><span class="uncovered0"><a name="line882"></a>882 # Returns a hash with &lt;tt&gt;CallSite =&gt; call count&lt;/tt&gt; associations or +nil+
1483
- </span><span class="uncovered1"><a name="line883"></a>883 # Can be called in two ways:
1484
- </span><span class="uncovered0"><a name="line884"></a>884 # analyzer.callsites(&quot;Foo#f1&quot;) # instance method
1485
- </span><span class="uncovered1"><a name="line885"></a>885 # analyzer.callsites(&quot;Foo.g1&quot;) # singleton method of the class
1486
- </span><span class="uncovered0"><a name="line886"></a>886 # or
1487
- </span><span class="uncovered1"><a name="line887"></a>887 # analyzer.callsites(&quot;Foo&quot;, &quot;f1&quot;)
1488
- </span><span class="uncovered0"><a name="line888"></a>888 # analyzer.callsites(&quot;#&lt;class:Foo&gt;&quot;, &quot;g1&quot;)
1489
- </span><span class="uncovered1"><a name="line889"></a>889 def callsites(classname_or_fullname, methodname = nil)
1490
- </span><span class="uncovered0"><a name="line890"></a>890 rawsites = raw_data_relative.first[expand_name(classname_or_fullname, methodname)]
1491
- </span><span class="uncovered1"><a name="line891"></a>891 return nil unless rawsites
1492
- </span><span class="uncovered0"><a name="line892"></a>892 ret = {}
1493
- </span><span class="uncovered1"><a name="line893"></a>893 # could be a job for inject but it's slow and I don't mind the extra loc
1494
- </span><span class="uncovered0"><a name="line894"></a>894 rawsites.each_pair do |backtrace, count|
1495
- </span><span class="uncovered1"><a name="line895"></a>895 ret[CallSite.new(backtrace)] = count
1496
- </span><span class="uncovered0"><a name="line896"></a>896 end
1497
- </span><span class="uncovered1"><a name="line897"></a>897 ret
1498
- </span><span class="uncovered0"><a name="line898"></a>898 end
1499
- </span><span class="uncovered1"><a name="line899"></a>899
1500
- </span><span class="uncovered0"><a name="line900"></a>900 # Returns a DefSite object corresponding to the given method
1501
- </span><span class="uncovered1"><a name="line901"></a>901 # Can be called in two ways:
1502
- </span><span class="uncovered0"><a name="line902"></a>902 # analyzer.defsite(&quot;Foo#f1&quot;) # instance method
1503
- </span><span class="uncovered1"><a name="line903"></a>903 # analyzer.defsite(&quot;Foo.g1&quot;) # singleton method of the class
1504
- </span><span class="uncovered0"><a name="line904"></a>904 # or
1505
- </span><span class="uncovered1"><a name="line905"></a>905 # analyzer.defsite(&quot;Foo&quot;, &quot;f1&quot;)
1506
- </span><span class="uncovered0"><a name="line906"></a>906 # analyzer.defsite(&quot;#&lt;class:Foo&gt;&quot;, &quot;g1&quot;)
1507
- </span><span class="uncovered1"><a name="line907"></a>907 def defsite(classname_or_fullname, methodname = nil)
1508
- </span><span class="uncovered0"><a name="line908"></a>908 file, line = raw_data_relative[1][expand_name(classname_or_fullname, methodname)]
1509
- </span><span class="uncovered1"><a name="line909"></a>909 return nil unless file &amp;&amp; line
1510
- </span><span class="uncovered0"><a name="line910"></a>910 DefSite.new(file, line)
1511
- </span><span class="uncovered1"><a name="line911"></a>911 end
1512
- </span><span class="uncovered0"><a name="line912"></a>912
1513
- </span><span class="uncovered1"><a name="line913"></a>913 private
1514
- </span><span class="uncovered0"><a name="line914"></a>914
1515
- </span><span class="uncovered1"><a name="line915"></a>915 def expand_name(classname_or_fullname, methodname = nil)
1516
- </span><span class="uncovered0"><a name="line916"></a>916 if methodname.nil?
1517
- </span><span class="uncovered1"><a name="line917"></a>917 case classname_or_fullname
1518
- </span><span class="uncovered0"><a name="line918"></a>918 when /(.*)#(.*)/: classname, methodname = $1, $2
1519
- </span><span class="uncovered1"><a name="line919"></a>919 when /(.*)\.(.*)/: classname, methodname = &quot;#&lt;Class:#{$1}&gt;&quot;, $2
1520
- </span><span class="uncovered0"><a name="line920"></a>920 else
1521
- </span><span class="uncovered1"><a name="line921"></a>921 raise ArgumentError, &quot;Incorrect method name&quot;
1522
- </span><span class="uncovered0"><a name="line922"></a>922 end
1523
- </span><span class="uncovered1"><a name="line923"></a>923
1524
- </span><span class="uncovered0"><a name="line924"></a>924 return [classname, methodname]
1525
- </span><span class="uncovered1"><a name="line925"></a>925 end
1526
- </span><span class="uncovered0"><a name="line926"></a>926
1527
- </span><span class="uncovered1"><a name="line927"></a>927 [classname_or_fullname, methodname]
1528
- </span><span class="uncovered0"><a name="line928"></a>928 end
1529
- </span><span class="uncovered1"><a name="line929"></a>929
1530
- </span><span class="uncovered0"><a name="line930"></a>930 def data_default; [{}, {}] end
1531
- </span><span class="uncovered1"><a name="line931"></a>931
1532
- </span><span class="uncovered0"><a name="line932"></a>932 def raw_data_absolute
1533
- </span><span class="uncovered1"><a name="line933"></a>933 raw, method_def_site = RCOV__.generate_callsite_info
1534
- </span><span class="uncovered0"><a name="line934"></a>934 ret1 = {}
1535
- </span><span class="uncovered1"><a name="line935"></a>935 ret2 = {}
1536
- </span><span class="uncovered0"><a name="line936"></a>936 raw.each_pair do |(klass, method), hash|
1537
- </span><span class="uncovered1"><a name="line937"></a>937 begin
1538
- </span><span class="uncovered0"><a name="line938"></a>938 key = [klass.to_s, method.to_s]
1539
- </span><span class="uncovered1"><a name="line939"></a>939 ret1[key] = hash.clone #Marshal.load(Marshal.dump(hash))
1540
- </span><span class="uncovered0"><a name="line940"></a>940 ret2[key] = method_def_site[[klass, method]]
1541
- </span><span class="uncovered1"><a name="line941"></a>941 #rescue Exception
1542
- </span><span class="uncovered0"><a name="line942"></a>942 end
1543
- </span><span class="uncovered1"><a name="line943"></a>943 end
1544
- </span><span class="uncovered0"><a name="line944"></a>944
1545
- </span><span class="uncovered1"><a name="line945"></a>945 [ret1, ret2]
1546
- </span><span class="uncovered0"><a name="line946"></a>946 end
1547
- </span><span class="uncovered1"><a name="line947"></a>947
1548
- </span><span class="uncovered0"><a name="line948"></a>948 def aggregate_data(aggregated_data, delta)
1549
- </span><span class="uncovered1"><a name="line949"></a>949 callsites1, defsites1 = aggregated_data
1550
- </span><span class="uncovered0"><a name="line950"></a>950 callsites2, defsites2 = delta
1551
- </span><span class="uncovered1"><a name="line951"></a>951
1552
- </span><span class="uncovered0"><a name="line952"></a>952 callsites2.each_pair do |(klass, method), hash|
1553
- </span><span class="uncovered1"><a name="line953"></a>953 dest_hash = (callsites1[[klass, method]] ||= {})
1554
- </span><span class="uncovered0"><a name="line954"></a>954 hash.each_pair do |callsite, count|
1555
- </span><span class="uncovered1"><a name="line955"></a>955 dest_hash[callsite] ||= 0
1556
- </span><span class="uncovered0"><a name="line956"></a>956 dest_hash[callsite] += count
1557
- </span><span class="uncovered1"><a name="line957"></a>957 end
1558
- </span><span class="uncovered0"><a name="line958"></a>958 end
1559
- </span><span class="uncovered1"><a name="line959"></a>959
1560
- </span><span class="uncovered0"><a name="line960"></a>960 defsites1.update(defsites2)
1561
- </span><span class="uncovered1"><a name="line961"></a>961 end
1562
- </span><span class="uncovered0"><a name="line962"></a>962
1563
- </span><span class="uncovered1"><a name="line963"></a>963 def compute_raw_data_difference(first, last)
1564
- </span><span class="uncovered0"><a name="line964"></a>964 difference = {}
1565
- </span><span class="uncovered1"><a name="line965"></a>965 default = Hash.new(0)
1566
- </span><span class="uncovered0"><a name="line966"></a>966
1567
- </span><span class="uncovered1"><a name="line967"></a>967 callsites1, defsites1 = *first
1568
- </span><span class="uncovered0"><a name="line968"></a>968 callsites2, defsites2 = *last
1569
- </span><span class="uncovered1"><a name="line969"></a>969
1570
- </span><span class="uncovered0"><a name="line970"></a>970 callsites2.each_pair do |(klass, method), hash|
1571
- </span><span class="uncovered1"><a name="line971"></a>971 old_hash = callsites1[[klass, method]] || default
1572
- </span><span class="uncovered0"><a name="line972"></a>972 hash.each_pair do |callsite, count|
1573
- </span><span class="uncovered1"><a name="line973"></a>973 diff = hash[callsite] - (old_hash[callsite] || 0)
1574
- </span><span class="uncovered0"><a name="line974"></a>974 if diff &gt; 0
1575
- </span><span class="uncovered1"><a name="line975"></a>975 difference[[klass, method]] ||= {}
1576
- </span><span class="uncovered0"><a name="line976"></a>976 difference[[klass, method]][callsite] = diff
1577
- </span><span class="uncovered1"><a name="line977"></a>977 end
1578
- </span><span class="uncovered0"><a name="line978"></a>978 end
1579
- </span><span class="uncovered1"><a name="line979"></a>979 end
1580
- </span><span class="uncovered0"><a name="line980"></a>980
1581
- </span><span class="uncovered1"><a name="line981"></a>981 [difference, defsites1.update(defsites2)]
1582
- </span><span class="uncovered0"><a name="line982"></a>982 end
1583
- </span><span class="uncovered1"><a name="line983"></a>983
1584
- </span><span class="uncovered0"><a name="line984"></a>984 end
1585
- </span><span class="uncovered1"><a name="line985"></a>985
1586
- </span><span class="uncovered0"><a name="line986"></a>986 end # Rcov
1587
- </span><span class="inferred1"><a name="line987"></a>987
1588
- </span><span class="inferred0"><a name="line988"></a>988 # vi: set sw=2:
1589
- </span></pre><hr/>
1590
- <p>Generated using the <a href='http://eigenclass.org/hiki.rb?rcov'>rcov code coverage analysis tool for Ruby</a>
1591
- version 0.8.1.2.</p>
1592
- <p><a href='http://validator.w3.org/check/referer'><img src='http://www.w3.org/Icons/valid-xhtml10' height='31' alt='Valid XHTML 1.0!' width='88'/>
1593
- </a>
1594
- <a href='http://jigsaw.w3.org/css-validator/check/referer'><img src='http://jigsaw.w3.org/css-validator/images/vcss' alt='Valid CSS!' style='border:0;width:88px;height:31px'/>
1595
- </a>
1596
- </p>
1597
- </body>
1598
- </html>