module-import 0.1.0 → 0.2.2

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