code_metric_fu 4.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.metrics +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +15 -0
  6. data/.rubocop_todo.yml +69 -0
  7. data/.simplecov +74 -0
  8. data/.travis.yml +22 -0
  9. data/.yardopts +4 -0
  10. data/AUTHORS +12 -0
  11. data/CONTRIBUTING.md +47 -0
  12. data/CONTRIBUTORS +76 -0
  13. data/DEV.md +76 -0
  14. data/Gemfile +74 -0
  15. data/Guardfile +30 -0
  16. data/HISTORY.md +705 -0
  17. data/MIT-LICENSE +22 -0
  18. data/README.md +299 -0
  19. data/Rakefile +27 -0
  20. data/TODO.md +118 -0
  21. data/appveyor.yml +31 -0
  22. data/bin/metric_fu +9 -0
  23. data/bin/mf-cane +10 -0
  24. data/bin/mf-churn +10 -0
  25. data/bin/mf-flay +10 -0
  26. data/bin/mf-reek +10 -0
  27. data/bin/mf-roodi +10 -0
  28. data/bin/mf-saikuro +10 -0
  29. data/certs/bf4.pem +22 -0
  30. data/checksum/.gitkeep +0 -0
  31. data/checksum/metric_fu-4.10.0.gem.sha512 +1 -0
  32. data/checksum/metric_fu-4.11.0.gem.sha512 +1 -0
  33. data/checksum/metric_fu-4.11.1.gem.sha512 +1 -0
  34. data/checksum/metric_fu-4.11.2.gem.sha512 +1 -0
  35. data/checksum/metric_fu-4.11.3.gem.sha512 +1 -0
  36. data/checksum/metric_fu-4.11.4.gem.sha512 +1 -0
  37. data/checksum/metric_fu-4.12.0.gem.sha512 +1 -0
  38. data/checksum/metric_fu-4.2.0.gem.sha512 +1 -0
  39. data/checksum/metric_fu-4.2.1.gem.sha512 +1 -0
  40. data/checksum/metric_fu-4.3.0.gem.sha512 +1 -0
  41. data/checksum/metric_fu-4.3.1.gem.sha512 +1 -0
  42. data/checksum/metric_fu-4.4.0.gem.sha512 +1 -0
  43. data/checksum/metric_fu-4.4.1.gem.sha512 +1 -0
  44. data/checksum/metric_fu-4.4.2.gem.sha512 +1 -0
  45. data/checksum/metric_fu-4.4.3.gem.sha512 +1 -0
  46. data/checksum/metric_fu-4.4.4.gem.sha512 +1 -0
  47. data/checksum/metric_fu-4.5.0.gem.sha512 +1 -0
  48. data/checksum/metric_fu-4.5.1.gem.sha512 +1 -0
  49. data/checksum/metric_fu-4.5.2.gem.sha512 +1 -0
  50. data/checksum/metric_fu-4.6.0.gem.sha512 +1 -0
  51. data/checksum/metric_fu-4.7.0.gem.sha512 +1 -0
  52. data/checksum/metric_fu-4.7.1.gem.sha512 +1 -0
  53. data/checksum/metric_fu-4.7.2.gem.sha512 +1 -0
  54. data/checksum/metric_fu-4.7.3.gem.sha512 +1 -0
  55. data/checksum/metric_fu-4.7.4.gem.sha512 +1 -0
  56. data/checksum/metric_fu-4.8.0.gem.sha512 +1 -0
  57. data/checksum/metric_fu-4.9.0.gem.sha512 +1 -0
  58. data/config/roodi_config.yml +22 -0
  59. data/config/rubocop.yml +269 -0
  60. data/gem_tasks/build.rake +197 -0
  61. data/gem_tasks/rubocop.rake +10 -0
  62. data/gem_tasks/usage_test.rake +19 -0
  63. data/gem_tasks/yard.rake +24 -0
  64. data/lib/metric_fu/calculate.rb +10 -0
  65. data/lib/metric_fu/cli/client.rb +26 -0
  66. data/lib/metric_fu/cli/helper.rb +80 -0
  67. data/lib/metric_fu/cli/parser.rb +138 -0
  68. data/lib/metric_fu/configuration.rb +150 -0
  69. data/lib/metric_fu/constantize.rb +57 -0
  70. data/lib/metric_fu/data_structures/line_numbers.rb +112 -0
  71. data/lib/metric_fu/data_structures/location.rb +110 -0
  72. data/lib/metric_fu/data_structures/sexp_node.rb +107 -0
  73. data/lib/metric_fu/environment.rb +129 -0
  74. data/lib/metric_fu/errors/analysis_error.rb +4 -0
  75. data/lib/metric_fu/formatter/html.rb +96 -0
  76. data/lib/metric_fu/formatter/syntax.rb +45 -0
  77. data/lib/metric_fu/formatter/yaml.rb +18 -0
  78. data/lib/metric_fu/formatter.rb +40 -0
  79. data/lib/metric_fu/gem_run.rb +70 -0
  80. data/lib/metric_fu/gem_version.rb +92 -0
  81. data/lib/metric_fu/generator.rb +135 -0
  82. data/lib/metric_fu/io.rb +132 -0
  83. data/lib/metric_fu/loader.rb +105 -0
  84. data/lib/metric_fu/logger.rb +62 -0
  85. data/lib/metric_fu/logging/mf_debugger.rb +23 -0
  86. data/lib/metric_fu/metric.rb +143 -0
  87. data/lib/metric_fu/metrics/cane/generator.rb +95 -0
  88. data/lib/metric_fu/metrics/cane/grapher.rb +37 -0
  89. data/lib/metric_fu/metrics/cane/metric.rb +34 -0
  90. data/lib/metric_fu/metrics/cane/report.html.erb +87 -0
  91. data/lib/metric_fu/metrics/cane/violations.rb +46 -0
  92. data/lib/metric_fu/metrics/churn/generator.rb +37 -0
  93. data/lib/metric_fu/metrics/churn/hotspot.rb +43 -0
  94. data/lib/metric_fu/metrics/churn/metric.rb +29 -0
  95. data/lib/metric_fu/metrics/churn/report.html.erb +58 -0
  96. data/lib/metric_fu/metrics/flay/generator.rb +51 -0
  97. data/lib/metric_fu/metrics/flay/grapher.rb +37 -0
  98. data/lib/metric_fu/metrics/flay/hotspot.rb +52 -0
  99. data/lib/metric_fu/metrics/flay/metric.rb +28 -0
  100. data/lib/metric_fu/metrics/flay/report.html.erb +29 -0
  101. data/lib/metric_fu/metrics/flog/generator.rb +113 -0
  102. data/lib/metric_fu/metrics/flog/grapher.rb +77 -0
  103. data/lib/metric_fu/metrics/flog/hotspot.rb +46 -0
  104. data/lib/metric_fu/metrics/flog/metric.rb +29 -0
  105. data/lib/metric_fu/metrics/flog/report.html.erb +50 -0
  106. data/lib/metric_fu/metrics/hotspots/analysis/analyzed_problems.rb +34 -0
  107. data/lib/metric_fu/metrics/hotspots/analysis/analyzer_tables.rb +114 -0
  108. data/lib/metric_fu/metrics/hotspots/analysis/grouping.rb +23 -0
  109. data/lib/metric_fu/metrics/hotspots/analysis/groupings.rb +12 -0
  110. data/lib/metric_fu/metrics/hotspots/analysis/problems.rb +20 -0
  111. data/lib/metric_fu/metrics/hotspots/analysis/ranked_problem_location.rb +70 -0
  112. data/lib/metric_fu/metrics/hotspots/analysis/ranking.rb +29 -0
  113. data/lib/metric_fu/metrics/hotspots/analysis/rankings.rb +91 -0
  114. data/lib/metric_fu/metrics/hotspots/analysis/record.rb +32 -0
  115. data/lib/metric_fu/metrics/hotspots/analysis/scoring_strategies.rb +24 -0
  116. data/lib/metric_fu/metrics/hotspots/analysis/table.rb +67 -0
  117. data/lib/metric_fu/metrics/hotspots/generator.rb +40 -0
  118. data/lib/metric_fu/metrics/hotspots/hotspot.rb +87 -0
  119. data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +61 -0
  120. data/lib/metric_fu/metrics/hotspots/metric.rb +20 -0
  121. data/lib/metric_fu/metrics/hotspots/report.html.erb +60 -0
  122. data/lib/metric_fu/metrics/rails_best_practices/generator.rb +47 -0
  123. data/lib/metric_fu/metrics/rails_best_practices/grapher.rb +38 -0
  124. data/lib/metric_fu/metrics/rails_best_practices/metric.rb +31 -0
  125. data/lib/metric_fu/metrics/rails_best_practices/report.html.erb +22 -0
  126. data/lib/metric_fu/metrics/rcov/external_client.rb +22 -0
  127. data/lib/metric_fu/metrics/rcov/generator.rb +75 -0
  128. data/lib/metric_fu/metrics/rcov/grapher.rb +37 -0
  129. data/lib/metric_fu/metrics/rcov/hotspot.rb +46 -0
  130. data/lib/metric_fu/metrics/rcov/metric.rb +61 -0
  131. data/lib/metric_fu/metrics/rcov/rcov_format_coverage.rb +149 -0
  132. data/lib/metric_fu/metrics/rcov/rcov_line.rb +48 -0
  133. data/lib/metric_fu/metrics/rcov/report.html.erb +40 -0
  134. data/lib/metric_fu/metrics/rcov/simplecov_formatter.rb +74 -0
  135. data/lib/metric_fu/metrics/reek/generator.rb +97 -0
  136. data/lib/metric_fu/metrics/reek/grapher.rb +55 -0
  137. data/lib/metric_fu/metrics/reek/hotspot.rb +95 -0
  138. data/lib/metric_fu/metrics/reek/metric.rb +26 -0
  139. data/lib/metric_fu/metrics/reek/report.html.erb +35 -0
  140. data/lib/metric_fu/metrics/roodi/generator.rb +41 -0
  141. data/lib/metric_fu/metrics/roodi/grapher.rb +37 -0
  142. data/lib/metric_fu/metrics/roodi/hotspot.rb +39 -0
  143. data/lib/metric_fu/metrics/roodi/metric.rb +24 -0
  144. data/lib/metric_fu/metrics/roodi/report.html.erb +22 -0
  145. data/lib/metric_fu/metrics/saikuro/generator.rb +145 -0
  146. data/lib/metric_fu/metrics/saikuro/hotspot.rb +51 -0
  147. data/lib/metric_fu/metrics/saikuro/metric.rb +31 -0
  148. data/lib/metric_fu/metrics/saikuro/parsing_element.rb +37 -0
  149. data/lib/metric_fu/metrics/saikuro/report.html.erb +71 -0
  150. data/lib/metric_fu/metrics/saikuro/scratch_file.rb +108 -0
  151. data/lib/metric_fu/metrics/stats/generator.rb +82 -0
  152. data/lib/metric_fu/metrics/stats/grapher.rb +40 -0
  153. data/lib/metric_fu/metrics/stats/hotspot.rb +35 -0
  154. data/lib/metric_fu/metrics/stats/metric.rb +28 -0
  155. data/lib/metric_fu/metrics/stats/report.html.erb +44 -0
  156. data/lib/metric_fu/reporter.rb +37 -0
  157. data/lib/metric_fu/reporting/graphs/graph.rb +69 -0
  158. data/lib/metric_fu/reporting/graphs/grapher.rb +66 -0
  159. data/lib/metric_fu/reporting/result.rb +59 -0
  160. data/lib/metric_fu/run.rb +82 -0
  161. data/lib/metric_fu/tasks/metric_fu.rake +54 -0
  162. data/lib/metric_fu/templates/_gem_info.html.erb +8 -0
  163. data/lib/metric_fu/templates/_graph.html.erb +2 -0
  164. data/lib/metric_fu/templates/_report_footer.html.erb +1 -0
  165. data/lib/metric_fu/templates/configuration.rb +25 -0
  166. data/lib/metric_fu/templates/css/bluff.css +15 -0
  167. data/lib/metric_fu/templates/css/buttons.css +82 -0
  168. data/lib/metric_fu/templates/css/default.css +43 -0
  169. data/lib/metric_fu/templates/css/integrity.css +337 -0
  170. data/lib/metric_fu/templates/css/rcov.css +32 -0
  171. data/lib/metric_fu/templates/css/reset.css +7 -0
  172. data/lib/metric_fu/templates/css/syntax.css +19 -0
  173. data/lib/metric_fu/templates/index.html.erb +13 -0
  174. data/lib/metric_fu/templates/javascripts/bluff-min.js +1 -0
  175. data/lib/metric_fu/templates/javascripts/bluff_graph.js +15 -0
  176. data/lib/metric_fu/templates/javascripts/excanvas.js +35 -0
  177. data/lib/metric_fu/templates/javascripts/highcharts.js +294 -0
  178. data/lib/metric_fu/templates/javascripts/highcharts_graph.js +38 -0
  179. data/lib/metric_fu/templates/javascripts/js-class.js +1 -0
  180. data/lib/metric_fu/templates/javascripts/standalone-framework.js +17 -0
  181. data/lib/metric_fu/templates/javascripts/utils.js +9 -0
  182. data/lib/metric_fu/templates/layout.html.erb +41 -0
  183. data/lib/metric_fu/templates/metrics_template.rb +86 -0
  184. data/lib/metric_fu/templates/report.html.erb +31 -0
  185. data/lib/metric_fu/templates/report.rb +41 -0
  186. data/lib/metric_fu/templates/template.rb +247 -0
  187. data/lib/metric_fu/utility.rb +79 -0
  188. data/lib/metric_fu/version.rb +9 -0
  189. data/lib/metric_fu.rb +143 -0
  190. data/metric_fu.gemspec +72 -0
  191. data/spec/capture_warnings.rb +55 -0
  192. data/spec/cli/helper_spec.rb +165 -0
  193. data/spec/dummy/.gitignore +1 -0
  194. data/spec/dummy/.gitkeep +0 -0
  195. data/spec/dummy/.metrics +4 -0
  196. data/spec/dummy/lib/.gitkeep +0 -0
  197. data/spec/dummy/lib/bad_encoding.rb +6 -0
  198. data/spec/dummy/spec/.gitkeep +0 -0
  199. data/spec/fixtures/20090630.yml +7922 -0
  200. data/spec/fixtures/coverage-153.rb +11 -0
  201. data/spec/fixtures/coverage.rb +13 -0
  202. data/spec/fixtures/exit0.sh +3 -0
  203. data/spec/fixtures/exit1.sh +3 -0
  204. data/spec/fixtures/hotspots/flog.yml +86 -0
  205. data/spec/fixtures/hotspots/generator.yml +47 -0
  206. data/spec/fixtures/hotspots/generator_analysis.yml +53 -0
  207. data/spec/fixtures/hotspots/reek.yml +14 -0
  208. data/spec/fixtures/hotspots/roodi.yml +13 -0
  209. data/spec/fixtures/hotspots/saikuro.yml +27 -0
  210. data/spec/fixtures/hotspots/several_metrics.yml +47 -0
  211. data/spec/fixtures/hotspots/stats.yml +4 -0
  212. data/spec/fixtures/hotspots/three_metrics_on_same_file.yml +36 -0
  213. data/spec/fixtures/line_numbers/foo.rb +33 -0
  214. data/spec/fixtures/line_numbers/module.rb +11 -0
  215. data/spec/fixtures/line_numbers/module_surrounds_class.rb +15 -0
  216. data/spec/fixtures/line_numbers/two_classes.rb +11 -0
  217. data/spec/fixtures/metric_missing.yml +1 -0
  218. data/spec/fixtures/rcov_output.txt +135 -0
  219. data/spec/fixtures/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
  220. data/spec/fixtures/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
  221. data/spec/fixtures/saikuro/index_cyclo.html +155 -0
  222. data/spec/fixtures/saikuro_sfiles/thing.rb_cyclo.html +11 -0
  223. data/spec/metric_fu/calculate_spec.rb +21 -0
  224. data/spec/metric_fu/configuration_spec.rb +90 -0
  225. data/spec/metric_fu/data_structures/line_numbers_spec.rb +63 -0
  226. data/spec/metric_fu/data_structures/location_spec.rb +110 -0
  227. data/spec/metric_fu/formatter/configuration_spec.rb +44 -0
  228. data/spec/metric_fu/formatter/html_spec.rb +138 -0
  229. data/spec/metric_fu/formatter/yaml_spec.rb +61 -0
  230. data/spec/metric_fu/formatter_spec.rb +49 -0
  231. data/spec/metric_fu/gem_version_spec.rb +12 -0
  232. data/spec/metric_fu/generator_spec.rb +130 -0
  233. data/spec/metric_fu/loader_spec.rb +10 -0
  234. data/spec/metric_fu/metric_spec.rb +46 -0
  235. data/spec/metric_fu/metrics/cane/configuration_spec.rb +22 -0
  236. data/spec/metric_fu/metrics/cane/generator_spec.rb +184 -0
  237. data/spec/metric_fu/metrics/churn/configuration_spec.rb +13 -0
  238. data/spec/metric_fu/metrics/churn/generator_spec.rb +64 -0
  239. data/spec/metric_fu/metrics/flay/configuration_spec.rb +13 -0
  240. data/spec/metric_fu/metrics/flay/generator_spec.rb +105 -0
  241. data/spec/metric_fu/metrics/flay/grapher_spec.rb +57 -0
  242. data/spec/metric_fu/metrics/flog/configuration_spec.rb +18 -0
  243. data/spec/metric_fu/metrics/flog/generator_spec.rb +77 -0
  244. data/spec/metric_fu/metrics/flog/grapher_spec.rb +107 -0
  245. data/spec/metric_fu/metrics/hotspots/analysis/analyzed_problems_spec.rb +104 -0
  246. data/spec/metric_fu/metrics/hotspots/analysis/analyzer_tables_spec.rb +71 -0
  247. data/spec/metric_fu/metrics/hotspots/analysis/ranking_spec.rb +30 -0
  248. data/spec/metric_fu/metrics/hotspots/analysis/rankings_spec.rb +97 -0
  249. data/spec/metric_fu/metrics/hotspots/analysis/table_spec.rb +6 -0
  250. data/spec/metric_fu/metrics/hotspots/generator_spec.rb +46 -0
  251. data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +10 -0
  252. data/spec/metric_fu/metrics/hotspots/hotspot_spec.rb +16 -0
  253. data/spec/metric_fu/metrics/rails_best_practices/configuration_spec.rb +55 -0
  254. data/spec/metric_fu/metrics/rails_best_practices/generator_spec.rb +33 -0
  255. data/spec/metric_fu/metrics/rails_best_practices/grapher_spec.rb +62 -0
  256. data/spec/metric_fu/metrics/rcov/configuration_spec.rb +28 -0
  257. data/spec/metric_fu/metrics/rcov/generator_spec.rb +22 -0
  258. data/spec/metric_fu/metrics/rcov/grapher_spec.rb +57 -0
  259. data/spec/metric_fu/metrics/rcov/hotspot_spec.rb +20 -0
  260. data/spec/metric_fu/metrics/rcov/rcov_line_spec.rb +89 -0
  261. data/spec/metric_fu/metrics/rcov/simplecov_formatter_spec.rb +67 -0
  262. data/spec/metric_fu/metrics/reek/configuration_spec.rb +13 -0
  263. data/spec/metric_fu/metrics/reek/generator_spec.rb +169 -0
  264. data/spec/metric_fu/metrics/reek/grapher_spec.rb +66 -0
  265. data/spec/metric_fu/metrics/roodi/configuration_spec.rb +14 -0
  266. data/spec/metric_fu/metrics/roodi/generator_spec.rb +82 -0
  267. data/spec/metric_fu/metrics/roodi/grapher_spec.rb +57 -0
  268. data/spec/metric_fu/metrics/saikuro/configuration_spec.rb +25 -0
  269. data/spec/metric_fu/metrics/saikuro/generator_spec.rb +71 -0
  270. data/spec/metric_fu/metrics/stats/generator_spec.rb +96 -0
  271. data/spec/metric_fu/metrics/stats/grapher_spec.rb +69 -0
  272. data/spec/metric_fu/reporter_spec.rb +41 -0
  273. data/spec/metric_fu/reporting/graphs/graph_spec.rb +44 -0
  274. data/spec/metric_fu/reporting/graphs/grapher_spec.rb +24 -0
  275. data/spec/metric_fu/reporting/result_spec.rb +50 -0
  276. data/spec/metric_fu/run_spec.rb +197 -0
  277. data/spec/metric_fu/templates/configuration_spec.rb +51 -0
  278. data/spec/metric_fu/templates/metrics_template_spec.rb +11 -0
  279. data/spec/metric_fu/templates/report_spec.rb +15 -0
  280. data/spec/metric_fu/templates/template_spec.rb +233 -0
  281. data/spec/metric_fu/utility_spec.rb +12 -0
  282. data/spec/metric_fu_spec.rb +33 -0
  283. data/spec/quality_spec.rb +114 -0
  284. data/spec/shared/configured.rb +45 -0
  285. data/spec/shared/test_coverage.rb +95 -0
  286. data/spec/spec_helper.rb +54 -0
  287. data/spec/support/deferred_garbaged_collection.rb +33 -0
  288. data/spec/support/helper_methods.rb +32 -0
  289. data/spec/support/matcher_create_file.rb +37 -0
  290. data/spec/support/matcher_create_files.rb +43 -0
  291. data/spec/support/suite.rb +26 -0
  292. data/spec/support/test_fixtures.rb +37 -0
  293. data/spec/support/timeout.rb +7 -0
  294. data/spec/support/usage_test.rb +150 -0
  295. data/spec/usage_test_spec.rb +93 -0
  296. metadata +757 -0
@@ -0,0 +1,149 @@
1
+ module MetricFu
2
+ class RCovFormatCoverage
3
+ NEW_FILE_MARKER = /^={80}$/.freeze
4
+
5
+ def initialize(rcov_text)
6
+ fail "no rcov text" if rcov_text.nil?
7
+ @rcov_text = rcov_text
8
+ end
9
+
10
+ def to_h
11
+ rcov_text = @rcov_text.split(NEW_FILE_MARKER)
12
+
13
+ rcov_text.shift # Throw away the first entry - it's the execution time etc.
14
+
15
+ files = assemble_files(rcov_text)
16
+
17
+ TestCoverage.new(files).to_h
18
+ end
19
+
20
+ private
21
+
22
+ def assemble_files(rcov_text)
23
+ files = {}
24
+ rcov_text.each_slice(2) { |out| files[out.first.strip] = out.last }
25
+ files.each_pair { |fname, content| files[fname] = content.split("\n") }
26
+ files.each_pair do |fname, content|
27
+ content.map! do |raw_line|
28
+ covered_line = if raw_line.start_with?("--")
29
+ nil # simplecov ignores some lines
30
+ elsif raw_line.start_with?("!!")
31
+ 0
32
+ else
33
+ 1
34
+ end
35
+ RCovLine.new(raw_line[3..-1], covered_line).to_h
36
+ end
37
+ content.reject! { |line| line[:content].to_s == "" }
38
+ files[fname] = { lines: content }
39
+ end
40
+ files
41
+ end
42
+
43
+ class TestCoverage
44
+ def initialize(filename_content)
45
+ @files = filename_content
46
+ @global_total_lines = 0
47
+ @global_total_lines_run = 0
48
+ end
49
+
50
+ def to_h
51
+ @test_coverage ||= begin
52
+ add_coverage_percentage(@files)
53
+ add_method_data(@files)
54
+ add_global_percent_run(@files, @global_total_lines, @global_total_lines_run)
55
+ @files
56
+ end
57
+ end
58
+
59
+ def self.floating_percent(numerator, denominator)
60
+ (numerator * 100.0) / denominator.to_f
61
+ end
62
+
63
+ def self.integer_percent(numerator, denominator)
64
+ ::MetricFu::Calculate.integer_percent(numerator, denominator)
65
+ end
66
+
67
+ def self.percent_run(lines)
68
+ line_coverage = RCovLine.line_coverage(lines)
69
+ covered_lines = RCovLine.covered_lines(line_coverage)
70
+ ignored_lines = RCovLine.ignored_lines(line_coverage)
71
+ relevant_lines = lines.count - ignored_lines
72
+ if block_given?
73
+ yield covered_lines, relevant_lines
74
+ else
75
+ floating_percent(covered_lines, relevant_lines)
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # TODO: remove multiple side effects
82
+ # sets global ivars and
83
+ # modifies the param passed in
84
+ def add_coverage_percentage(files)
85
+ files.each_pair do |fname, content|
86
+ lines = content[:lines]
87
+ percent_run =
88
+ self.class.percent_run(lines) do |covered, relevant|
89
+ @global_total_lines_run += covered
90
+ @global_total_lines += relevant
91
+ self.class.integer_percent(covered, relevant)
92
+ end
93
+ files[fname][:percent_run] = percent_run
94
+ end
95
+ end
96
+
97
+ def add_global_percent_run(test_coverage, total_lines, total_lines_run)
98
+ percentage = self.class.floating_percent(total_lines_run, total_lines)
99
+ test_coverage.update(
100
+ global_percent_run: round_to_tenths(percentage)
101
+ )
102
+ end
103
+
104
+ def add_method_data(test_coverage)
105
+ test_coverage.each_pair do |file_path, info|
106
+ file_contents = ""
107
+ coverage = []
108
+
109
+ info[:lines].each_with_index do |line, _index|
110
+ file_contents << "#{line[:content]}\n"
111
+ coverage << line[:was_run]
112
+ end
113
+
114
+ begin
115
+ line_numbers = MetricFu::LineNumbers.new(file_contents)
116
+ rescue StandardError => e
117
+ raise e unless e.message =~ /you shouldn't be able to get here/
118
+ mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level TestCoverage information for this file."
119
+ next
120
+ end
121
+
122
+ method_coverage_map = {}
123
+ coverage.each_with_index do |covered, index|
124
+ line_number = index + 1
125
+ if line_numbers.in_method?(line_number)
126
+ method_name = line_numbers.method_at_line(line_number)
127
+ method_coverage_map[method_name] ||= {}
128
+ method_coverage_map[method_name][:total] ||= 0
129
+ method_coverage_map[method_name][:total] += 1
130
+ method_coverage_map[method_name][:uncovered] ||= 0
131
+ method_coverage_map[method_name][:uncovered] += 1 if !covered
132
+ end
133
+ end
134
+
135
+ test_coverage[file_path][:methods] = {}
136
+
137
+ method_coverage_map.each do |method_name, coverage_data|
138
+ test_coverage[file_path][:methods][method_name] = (coverage_data[:uncovered] / coverage_data[:total].to_f) * 100.0
139
+ end
140
+ end
141
+ end
142
+
143
+ def round_to_tenths(decimal)
144
+ decimal = 0.0 if decimal.to_s.eql?("NaN")
145
+ (decimal * 10).round / 10.0
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,48 @@
1
+ module MetricFu
2
+ class RCovLine
3
+ attr_accessor :content, :was_run
4
+
5
+ def initialize(content, was_run)
6
+ @content = content
7
+ @was_run = was_run
8
+ end
9
+
10
+ def to_h
11
+ { content: @content, was_run: @was_run }
12
+ end
13
+
14
+ def covered?
15
+ @was_run == 1
16
+ end
17
+
18
+ def missed?
19
+ @was_run == 0
20
+ end
21
+
22
+ def ignored?
23
+ @was_run.nil?
24
+ end
25
+
26
+ def self.line_coverage(lines)
27
+ lines.map { |line| line[:was_run] }
28
+ end
29
+
30
+ def self.covered_lines(line_coverage)
31
+ line_coverage.count(1)
32
+ end
33
+
34
+ def self.missed_lines(line_coverage)
35
+ line_coverage.count(0)
36
+ end
37
+
38
+ def self.ignored_lines(line_coverage)
39
+ line_coverage.count(nil)
40
+ end
41
+
42
+ def css_class
43
+ return "rcov_not_run" if missed?
44
+
45
+ "rcov_run"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ <style data-file="rcov">
2
+ <%= inline_css("css/rcov.css") %>
3
+ </style>
4
+ <h3>Rcov Code Coverage Results</h3>
5
+
6
+ <p>C0 code coverage information.</p>
7
+
8
+ <%= render_partial 'graph', {:graph_name => 'rcov'} %>
9
+
10
+ <p>Total Coverage: <%= @rcov.delete(:global_percent_run) %>% </p>
11
+ <table>
12
+ <tr>
13
+ <th>File Path</th>
14
+ <th>Percent run</th>
15
+ </tr>
16
+ <% count = 0 %>
17
+ <% @rcov.sort_by{ |k,v| v[:percent_run] }.each do |fname, file| %>
18
+ <tr>
19
+ <td><a href="#<%= fname.gsub(/[^a-z]+/, '_') %>"><%= fname %></a></td>
20
+ <td><%= file[:percent_run] %></td>
21
+ </tr>
22
+ <% count += 1 %>
23
+ <% end %>
24
+ </table>
25
+
26
+ <% @rcov.sort_by{ |k,v| v[:percent_run] }.each do |fname, file| %>
27
+ <h2 id="<%= fname.gsub(/[^a-z]+/, '_') %>"> <%= fname %></h2>
28
+ <div class="rcov_overflow">
29
+ <table class="rcov_code">
30
+ <% file[:lines].each_with_index do |line, index| %>
31
+ <tr>
32
+ <% rcov_line = RCovLine.new(line[:content], line[:was_run]) %>
33
+ <td class="<%= rcov_line.css_class %>"><%= link_to_filename(fname, index + 1, "<pre>#{line[:content]}</pre>") %></td>
34
+ </tr>
35
+ <% end %>
36
+ </table>
37
+ </div>
38
+ <% end %>
39
+
40
+ <%= render_partial 'report_footer' %>
@@ -0,0 +1,74 @@
1
+ if defined?(JRUBY_VERSION)
2
+ if ENV["JRUBY_OPTS"].to_s !~ /-Xcli.debug=true/
3
+ warn "Coverage may be inaccurate; Try setting JRUBY_OPTS=\"-Xcli.debug=true --debug\""
4
+ # see https://github.com/metricfu/metric_fu/pull/226
5
+ # https://github.com/jruby/jruby/issues/1196
6
+ # https://jira.codehaus.org/browse/JRUBY-6106
7
+ # https://github.com/colszowka/simplecov/issues/86
8
+ end
9
+ end
10
+ require "simplecov"
11
+ require "metric_fu/logger"
12
+ require_relative "external_client"
13
+ require_relative "rcov_format_coverage"
14
+
15
+ class SimpleCov::Formatter::MetricFu
16
+ def format(result)
17
+ rcov_text = FormatLikeRCov.new(result).format
18
+ client = MetricFu::RCovTestCoverageClient.new(coverage_file_path)
19
+ client.post_results(rcov_text)
20
+ end
21
+
22
+ attr_writer :coverage_file_path
23
+
24
+ def coverage_file_path
25
+ @coverage_file_path || self.coverage_file_path = default_coverage_file_path
26
+ end
27
+
28
+ def default_coverage_file_path
29
+ File.join(SimpleCov.root, "coverage", "rcov", output_file_name)
30
+ end
31
+
32
+ # TODO: Read in from legacy coverage/rcov/rcov.txt path, when set
33
+ # write to date-specific report file, read from if present
34
+ # e.g.
35
+ # MetricFu::Metric.get_metric(:rcov).run_options[:output_directory]
36
+ # or
37
+ # metric_directory = MetricFu::Io::FileSystem.scratch_directory('Ymd-coverage')
38
+ # MetricFu::Utility.mkdir_p(metric_directory, :verbose => false)
39
+ # @note legacy file name is 'rcov.txt'
40
+ # going forward, the file name will be in a date-stamped
41
+ # format like for all other reported metrics.
42
+ def output_file_name
43
+ "rcov.txt"
44
+ end
45
+
46
+ # report should reference file used to build it
47
+ class FormatLikeRCov
48
+ def initialize(result)
49
+ @result = result
50
+ end
51
+
52
+ def format
53
+ content = "metric_fu shift the first line\n"
54
+ @result.source_files.each do |source_file|
55
+ content << "=" * 80
56
+ content << "\n #{simple_file_name(source_file)}\n"
57
+ content << "=" * 80
58
+ content << "\n"
59
+ source_file.lines.each do |line|
60
+ content << "!!" if line.missed?
61
+ content << "--" if line.never? || line.skipped?
62
+ content << " " if line.covered?
63
+ content << " #{line.src.chomp}\n"
64
+ end
65
+ content << "\n"
66
+ end
67
+ content
68
+ end
69
+
70
+ def simple_file_name(source_file)
71
+ source_file.filename.gsub(SimpleCov.root, ".")
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,97 @@
1
+ module MetricFu
2
+ class ReekGenerator < Generator
3
+ def self.metric
4
+ :reek
5
+ end
6
+
7
+ def emit
8
+ files = files_to_analyze
9
+ if files.empty?
10
+ mf_log "Skipping Reek, no files found to analyze"
11
+ @output = run!([], config_files)
12
+ else
13
+ @output = run!(files, config_files)
14
+ end
15
+ end
16
+
17
+ def run!(files, config_files)
18
+ smells = []
19
+ files.each do |file|
20
+ e = examiner.new(File.new(file))
21
+ smells += e.smells
22
+ end
23
+ smells
24
+ end
25
+
26
+ def analyze
27
+ @matches = @output.group_by(&:source).collect do |file_path, smells|
28
+ { file_path: file_path,
29
+ code_smells: analyze_smells(smells) }
30
+ end
31
+ end
32
+
33
+ def to_h
34
+ { reek: { matches: @matches } }
35
+ end
36
+
37
+ def per_file_info(out)
38
+ @matches.each do |file_data|
39
+ file_path = file_data[:file_path]
40
+ next if File.extname(file_path) =~ /\.erb|\.html|\.haml/
41
+ begin
42
+ line_numbers = MetricFu::LineNumbers.new(File.read(file_path), file_path)
43
+ rescue StandardError => e
44
+ raise e unless e.message =~ /you shouldn't be able to get here/
45
+ mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level reek information for this file."
46
+ next
47
+ end
48
+
49
+ file_data[:code_smells].each do |smell_data|
50
+ line = line_numbers.start_line_for_method(smell_data[:method])
51
+ out[file_data[:file_path]][line.to_s] << { type: :reek,
52
+ description: "#{smell_data[:type]} - #{smell_data[:message]}" }
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def files_to_analyze
60
+ dirs_to_reek = options[:dirs_to_reek]
61
+ files_to_reek = dirs_to_reek.map { |dir| Dir[File.join(dir, "**", "*.rb")] }.flatten
62
+ remove_excluded_files(files_to_reek)
63
+ end
64
+
65
+ # TODO: Check that specified line config file exists
66
+ def config_files
67
+ Array(options[:config_file_pattern])
68
+ end
69
+
70
+ def analyze_smells(smells)
71
+ smells.collect(&method(:smell_data))
72
+ end
73
+
74
+ def smell_data(smell)
75
+ { method: smell.context,
76
+ message: smell.message,
77
+ type: smell_type(smell),
78
+ lines: smell.lines }
79
+ end
80
+
81
+ def smell_type(smell)
82
+ return smell.subclass if smell.respond_to?(:subclass)
83
+
84
+ smell.smell_type
85
+ end
86
+
87
+ def examiner
88
+ require "reek"
89
+ # To load any changing dependencies such as "reek/configuration/app_configuration"
90
+ # Added in 1.6.0 https://github.com/troessner/reek/commit/7f4ed2be442ca926e08ccc41945e909e8f710947
91
+ # But not always loaded
92
+ require "reek/cli/application"
93
+
94
+ Reek.const_defined?(:Examiner) ? Reek.const_get(:Examiner) : Reek.const_get(:Core).const_get(:Examiner)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,55 @@
1
+ MetricFu.reporting_require { "graphs/grapher" }
2
+ module MetricFu
3
+ class ReekGrapher < Grapher
4
+ attr_accessor :reek_count, :labels
5
+
6
+ def self.metric
7
+ :reek
8
+ end
9
+
10
+ def initialize
11
+ super
12
+ @reek_count = {}
13
+ @labels = {}
14
+ end
15
+
16
+ def get_metrics(metrics, date)
17
+ if metrics && metrics[:reek]
18
+ counter = @labels.size
19
+ @labels.update(@labels.size => date)
20
+
21
+ metrics[:reek][:matches].each do |reek_chunk|
22
+ reek_chunk[:code_smells].each do |code_smell|
23
+ # speaking of code smell...
24
+ @reek_count[code_smell[:type]] = [] if @reek_count[code_smell[:type]].nil?
25
+ if @reek_count[code_smell[:type]][counter].nil?
26
+ @reek_count[code_smell[:type]][counter] = 1
27
+ else
28
+ @reek_count[code_smell[:type]][counter] += 1
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def title
36
+ "Reek: code smells"
37
+ end
38
+
39
+ def data
40
+ @reek_count.map do |name, count|
41
+ [name, nil_counts_to_zero(count).join(",")]
42
+ end
43
+ end
44
+
45
+ def output_filename
46
+ "reek.js"
47
+ end
48
+
49
+ private
50
+
51
+ def nil_counts_to_zero(counts)
52
+ counts.map { |count| count || 0 }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,95 @@
1
+ # coding: utf-8
2
+ class MetricFu::ReekHotspot < MetricFu::Hotspot
3
+ # Note that in practice, the prefix reek__ is appended to each one
4
+ # This was a partially implemented idea to avoid column name collisions
5
+ # but it is only done in the ReekHotspot
6
+ COLUMNS = %w{type_name message value value_description comparable_message}
7
+
8
+ def columns
9
+ COLUMNS.map { |column| "#{name}__#{column}" }
10
+ end
11
+
12
+ def name
13
+ :reek
14
+ end
15
+
16
+ def map_strategy
17
+ :present
18
+ end
19
+
20
+ def reduce_strategy
21
+ :sum
22
+ end
23
+
24
+ def score_strategy
25
+ :percentile
26
+ end
27
+
28
+ def generate_records(data, table)
29
+ return if data == nil
30
+ data[:matches].each do |match|
31
+ file_path = match[:file_path]
32
+ match[:code_smells].each do |smell|
33
+ location = MetricFu::Location.for(smell[:method])
34
+ smell_type = smell[:type]
35
+ message = smell[:message]
36
+ table << {
37
+ "metric" => name, # important
38
+ "file_path" => file_path, # important
39
+ # NOTE: ReekHotspot is currently different than other hotspots with regard
40
+ # to column name. Note the COLUMNS constant and #columns method
41
+ "reek__message" => message,
42
+ "reek__type_name" => smell_type,
43
+ "reek__value" => parse_value(message),
44
+ "reek__value_description" => build_value_description(smell_type, message),
45
+ "reek__comparable_message" => comparable_message(smell_type, message),
46
+ "class_name" => location.class_name, # important
47
+ "method_name" => location.method_name, # important
48
+ }
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.numeric_smell?(type)
54
+ ["Large Class", "Long Method", "Long Parameter List"].include?(type)
55
+ end
56
+
57
+ def present_group(group)
58
+ occurences = group.size
59
+ "found #{occurences} code smells"
60
+ end
61
+
62
+ private
63
+
64
+ def comparable_message(type_name, message)
65
+ if self.class.numeric_smell?(type_name)
66
+ match = message.match(/\d+/)
67
+ if match
68
+ match.pre_match + match.post_match
69
+ else
70
+ message
71
+ end
72
+ else
73
+ message
74
+ end
75
+ end
76
+
77
+ def build_value_description(type_name, message)
78
+ item_type = message.match(/\d+ (.*)$/)
79
+ if item_type
80
+ "number of #{item_type[1]} in #{type_name.downcase}"
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ def parse_value(message)
87
+ # mf_debug "parsing #{message}"
88
+ match = message.match(/\d+/)
89
+ if match
90
+ match[0].to_i
91
+ else
92
+ nil
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,26 @@
1
+ module MetricFu
2
+ class MetricReek < Metric
3
+ def name
4
+ :reek
5
+ end
6
+
7
+ def default_run_options
8
+ {
9
+ dirs_to_reek: MetricFu::Io::FileSystem.directory("code_dirs"),
10
+ config_file_pattern: nil,
11
+ }
12
+ end
13
+
14
+ def has_graph?
15
+ true
16
+ end
17
+
18
+ def enable
19
+ super
20
+ end
21
+
22
+ def activate
23
+ super
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ <h3>Reek Results</h3>
2
+
3
+ <p><a href="http://github.com/troessner/reek">Reek</a> detects common code smells in ruby code.</p>
4
+
5
+ <%= render_partial 'graph', {:graph_name => 'reek'} %>
6
+
7
+ <table>
8
+ <tr>
9
+ <th>File Path</th>
10
+ <th>Method</th>
11
+ <th>Description</th>
12
+ <th>Type</th>
13
+ </tr>
14
+ <% count = 0 %>
15
+ <% @reek[:matches].each do |match| %>
16
+ <% match[:code_smells].each do |smell| %>
17
+ <tr class='<%= cycle("light", "dark", count) %>'>
18
+ <td><%= link_to_filename(match[:file_path]) %></td>
19
+ <td>
20
+ <%= smell[:method] %>
21
+ </td>
22
+ <td>
23
+ <%= smell[:message] %>
24
+ </td>
25
+ <td>
26
+ <%= smell[:type] %>
27
+ </td>
28
+ </tr>
29
+ <% count += 1 %>
30
+ <% end %>
31
+ <% end %>
32
+
33
+ </table>
34
+
35
+ <%= render_partial 'report_footer' %>
@@ -0,0 +1,41 @@
1
+ module MetricFu
2
+ class RoodiGenerator < Generator
3
+ def self.metric
4
+ :roodi
5
+ end
6
+
7
+ def emit
8
+ files_to_analyze = options[:dirs_to_roodi].map { |dir| Dir[File.join(dir, "**/*.rb")] }
9
+ files = remove_excluded_files(files_to_analyze.flatten)
10
+ config = options[:roodi_config] ? "-config=#{options[:roodi_config]}" : ""
11
+ args = "#{config} #{files.join(' ')}"
12
+ @output = run!(args)
13
+ end
14
+
15
+ def analyze
16
+ @output = MetricFu::Utility.strip_escape_codes(@output)
17
+ @matches = @output.chomp.split("\n").map { |m| m.split(" - ") }
18
+ total = @matches.pop
19
+ @matches.reject! { |array| array.size < 2 }
20
+ @matches.map! do |match|
21
+ file, line = match[0].split(":")
22
+ problem = match[1]
23
+ { file: file, line: line, problem: problem }
24
+ end
25
+ total << "Found #{@matches.size} errors."
26
+ @roodi_results = { total: total, problems: @matches }
27
+ end
28
+
29
+ def to_h
30
+ { roodi: @roodi_results }
31
+ end
32
+
33
+ def per_file_info(out)
34
+ @matches.each do |match|
35
+ out[match[:file]] ||= {}
36
+ out[match[:file]][match[:line]] ||= []
37
+ out[match[:file]][match[:line]] << { type: :roodi, description: match[:problem] }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ MetricFu.reporting_require { "graphs/grapher" }
2
+ module MetricFu
3
+ class RoodiGrapher < Grapher
4
+ attr_accessor :roodi_count, :labels
5
+
6
+ def self.metric
7
+ :roodi
8
+ end
9
+
10
+ def initialize
11
+ super
12
+ @roodi_count = []
13
+ @labels = {}
14
+ end
15
+
16
+ def get_metrics(metrics, date)
17
+ if metrics && metrics[:roodi]
18
+ @roodi_count.push(metrics[:roodi][:problems].size)
19
+ @labels.update(@labels.size => date)
20
+ end
21
+ end
22
+
23
+ def title
24
+ "Roodi: design problems"
25
+ end
26
+
27
+ def data
28
+ [
29
+ ["roodi", @roodi_count.join(",")]
30
+ ]
31
+ end
32
+
33
+ def output_filename
34
+ "roodi.js"
35
+ end
36
+ end
37
+ end