kwala 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README +62 -0
  2. data/bin/kwala +39 -0
  3. data/lib/kwala.rb +59 -0
  4. data/lib/kwala/actions/code_change.rb +128 -0
  5. data/lib/kwala/actions/code_coverage.rb +303 -0
  6. data/lib/kwala/actions/code_duplication.rb +222 -0
  7. data/lib/kwala/actions/code_formatting.rb +358 -0
  8. data/lib/kwala/actions/cycle_detection.rb +118 -0
  9. data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
  10. data/lib/kwala/actions/dead_code.rb +68 -0
  11. data/lib/kwala/actions/executable_files_check.rb +111 -0
  12. data/lib/kwala/actions/flagged_comments.rb +128 -0
  13. data/lib/kwala/actions/gem_plugin.rb +31 -0
  14. data/lib/kwala/actions/loc_count.rb +153 -0
  15. data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
  16. data/lib/kwala/actions/outside_links.rb +85 -0
  17. data/lib/kwala/actions/rails_migrate.rb +62 -0
  18. data/lib/kwala/actions/strange_requires.rb +106 -0
  19. data/lib/kwala/actions/syntax_check.rb +130 -0
  20. data/lib/kwala/actions/unit_test.rb +368 -0
  21. data/lib/kwala/build_action.rb +88 -0
  22. data/lib/kwala/build_context.rb +49 -0
  23. data/lib/kwala/command_line_runner.rb +184 -0
  24. data/lib/kwala/ext/demos.jar +0 -0
  25. data/lib/kwala/ext/pmd.jar +0 -0
  26. data/lib/kwala/ext/prefuse.jar +0 -0
  27. data/lib/kwala/extensions.rb +36 -0
  28. data/lib/kwala/lib/code_analyzer.rb +678 -0
  29. data/lib/kwala/lib/cycle_detector.rb +793 -0
  30. data/lib/kwala/lib/strange_requires_detector.rb +145 -0
  31. data/lib/kwala/notification.rb +45 -0
  32. data/lib/kwala/notifications/email.rb +54 -0
  33. data/lib/kwala/notifications/rss.rb +151 -0
  34. data/lib/kwala/project_builder_utils.rb +178 -0
  35. data/lib/kwala/templates/build_template.html +33 -0
  36. data/lib/kwala/templates/code_change_summary.html +27 -0
  37. data/lib/kwala/templates/code_coverage_detailed.html +25 -0
  38. data/lib/kwala/templates/code_coverage_summary.html +30 -0
  39. data/lib/kwala/templates/code_duplication_detailed.html +45 -0
  40. data/lib/kwala/templates/code_duplication_summary.html +9 -0
  41. data/lib/kwala/templates/code_formatting_detailed.html +27 -0
  42. data/lib/kwala/templates/code_formatting_summary.html +11 -0
  43. data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
  44. data/lib/kwala/templates/cycle_detection_summary.html +7 -0
  45. data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
  46. data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
  47. data/lib/kwala/templates/executable_files_check_summary.html +20 -0
  48. data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
  49. data/lib/kwala/templates/flagged_comments_summary.html +10 -0
  50. data/lib/kwala/templates/loc_count_detailed.html +24 -0
  51. data/lib/kwala/templates/loc_count_summary.html +14 -0
  52. data/lib/kwala/templates/mdd/1.png +0 -0
  53. data/lib/kwala/templates/mdd/2.png +0 -0
  54. data/lib/kwala/templates/mdd/3.png +0 -0
  55. data/lib/kwala/templates/mdd/4.png +0 -0
  56. data/lib/kwala/templates/mdd/5.png +0 -0
  57. data/lib/kwala/templates/outside_links_summary.html +4 -0
  58. data/lib/kwala/templates/strange_requires_detailed.html +23 -0
  59. data/lib/kwala/templates/strange_requires_summary.html +13 -0
  60. data/lib/kwala/templates/style.css +95 -0
  61. data/lib/kwala/templates/syntax_check_detailed.html +21 -0
  62. data/lib/kwala/templates/syntax_check_summary.html +7 -0
  63. data/lib/kwala/templates/unit_test_detailed.html +66 -0
  64. data/lib/kwala/templates/unit_test_summary.html +70 -0
  65. data/test/tc_build_action.rb +32 -0
  66. data/test/tc_templates.rb +24 -0
  67. metadata +118 -0
data/README ADDED
@@ -0,0 +1,62 @@
1
+ Kwala
2
+
3
+ Description:
4
+ Kwala is a build tool that contains a number of actions to check the "quality"
5
+ of your ruby source code. Some of the included actions are checks for code
6
+ duplication, cyclomatic complexity, lines of code, unit tests, various
7
+ formatting checks, and require cycles. In addition, Kwala is designed to
8
+ allow users to add and create new actions to check against the code. It
9
+ generates both an HTML summary page and detail pages for the various actions.
10
+
11
+ License: BSD
12
+
13
+ Usage:
14
+
15
+ Kwala is a command line tool that allows you to specify which actions to run
16
+ on your project. Because Kwala is a command line tool it is easy to setup as
17
+ a cron job to perform periodic checks against your code base.
18
+
19
+ An example for hourly builds:
20
+
21
+ kwala -p MyProject -d projects/MyProject -o builds -a syntax_check -a formatting -a unit_test -a cyclomatic -a cycles
22
+
23
+ For more details on how to use it, please see the usage notes by running
24
+ "kwala -?""
25
+
26
+ Install:
27
+
28
+ As root run
29
+ # ruby setup.rb all
30
+
31
+ Dependencies:
32
+ Kwala uses Amrita 1 to generate the HTML results pages.
33
+ You can get it from here: http://amrita.sourceforge.jp/
34
+ At the moment Amrita 1 is not available as a RubyGem.
35
+
36
+ External Tools Used:
37
+ Some actions use external tools to perform the actual check or present
38
+ the results. If an action cannot find its required tools, that action
39
+ will simply not generate any output. For your convenience, we already
40
+ bundled a few jar files (see in the ext directory) that some actions use.
41
+ Here is a complete list of the projects from which those tools originate:
42
+
43
+ PMD's CPD for code duplication.
44
+ http://pmd.sourceforge.net/
45
+
46
+ Prefuse for require graph visualization.
47
+ http://www.prefuse.org/
48
+
49
+ Saikuro for cyclomatic complexity checks
50
+ http://saikuro.rubyforge.org/
51
+
52
+ rcov for code coverage
53
+ http://eigenclass.org/hiki.rb?rcov
54
+
55
+ MultiRuby for running unit tests against various versions of Ruby.
56
+ http://www.zenspider.com/ZSS/Products/ZenTest/
57
+
58
+ Authors and Contributors:
59
+ Zev Blut
60
+ Pierre Baumard
61
+ Michael Reinsch
62
+ Paul McMahon
@@ -0,0 +1,39 @@
1
+ #! /usr/bin/ruby1.8
2
+
3
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are
8
+ # met:
9
+ #
10
+ #
11
+ # * Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # * Redistributions in binary form must reproduce the above
15
+ # copyright notice, this list of conditions and the following
16
+ # disclaimer in the documentation and/or other materials provided
17
+ # with the distribution.
18
+ #
19
+ # * Neither the name of Ubit nor the names of its
20
+ # contributors may be used to endorse or promote products derived
21
+ # from this software without specific prior written permission.
22
+ #
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # == Author
37
+ # Zev Blut (zb@ubit.com)
38
+
39
+ require 'kwala/command_line_runner'
@@ -0,0 +1,59 @@
1
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ #
9
+ # * Redistributions of source code must retain the above copyright
10
+ # notice, this list of conditions and the following disclaimer.
11
+ #
12
+ # * Redistributions in binary form must reproduce the above
13
+ # copyright notice, this list of conditions and the following
14
+ # disclaimer in the documentation and/or other materials provided
15
+ # with the distribution.
16
+ #
17
+ # * Neither the name of Ubit nor the names of its
18
+ # contributors may be used to endorse or promote products derived
19
+ # from this software without specific prior written permission.
20
+ #
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+ #
34
+ # == Author
35
+ # Zev Blut (zb@ubit.com)
36
+
37
+
38
+ require "find"
39
+ require "open3"
40
+ require "fileutils"
41
+ require "stringio"
42
+ require "yaml"
43
+ require "timeout"
44
+ require "amrita/template"
45
+ include Amrita
46
+ require 'erb'
47
+
48
+ BASE_DIR = File.expand_path(File.dirname(__FILE__)) + "/kwala"
49
+ TEMPLATE_DIR = "#{BASE_DIR}/templates/"
50
+ ACTION_DIR = "#{BASE_DIR}/actions"
51
+ EXTERNAL_DIR = "#{BASE_DIR}/ext/"
52
+ LIB_DIR = "#{BASE_DIR}/lib"
53
+ NOTIFICATION_DIR = "#{BASE_DIR}/notifications"
54
+
55
+ require 'kwala/extensions'
56
+ require 'kwala/build_action'
57
+ require 'kwala/build_context'
58
+ require 'kwala/project_builder_utils'
59
+ require 'kwala/notification'
@@ -0,0 +1,128 @@
1
+ # $Id: code_change.rb 34 2008-04-03 06:01:19Z zev $
2
+ #
3
+ # This file implements the logic to check for additions and removals
4
+ # of methods and classes in a source base.
5
+
6
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions are
11
+ # met:
12
+ #
13
+ #
14
+ # * Redistributions of source code must retain the above copyright
15
+ # notice, this list of conditions and the following disclaimer.
16
+ #
17
+ # * Redistributions in binary form must reproduce the above
18
+ # copyright notice, this list of conditions and the following
19
+ # disclaimer in the documentation and/or other materials provided
20
+ # with the distribution.
21
+ #
22
+ # * Neither the name of Ubit nor the names of its
23
+ # contributors may be used to endorse or promote products derived
24
+ # from this software without specific prior written permission.
25
+ #
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+ #
39
+ # == Author
40
+ # Zev Blut (zb@ubit.com)
41
+
42
+ class CodeChangeAction < BuildAction
43
+
44
+ def initialize
45
+ @code_changes = nil
46
+ end
47
+
48
+ def build_action(context)
49
+ @code_changes = find_code_changes(context.ri_dir_1, context.ri_dir_2)
50
+ end
51
+
52
+ def summary_display(context)
53
+ template = TemplateFile.new(self.class.summary_template_file)
54
+ context.amrita_data[:code_change_results] = @code_changes
55
+ summary_expand(template, context)
56
+ end
57
+
58
+ def self.detailed_template_file
59
+ nil
60
+ end
61
+
62
+ protected
63
+
64
+ def filter_method_from_classes?(method, classes)
65
+ m = /(.*)\/.+\.yaml$/.match(method)
66
+ base = m[1]
67
+ classes.include?(base)
68
+ end
69
+
70
+ def strip_ri_method_name(meth)
71
+ m = /(.*)-(i|c)\.yaml/.match(meth)
72
+ m[1]
73
+ end
74
+
75
+ def find_code_changes(ri_dir="/tmp/ri", new_ri_dir="/tmp/new_ri")
76
+ results = Hash.new
77
+
78
+ new_ri_files = find_ri_files(new_ri_dir)
79
+ old_ri_files = find_ri_files(ri_dir)
80
+
81
+ new_classes = new_ri_files.reject {|f| f =~ /\.(yaml|rid)$/ || f == new_ri_dir }
82
+ old_classes = old_ri_files.reject {|f| f =~ /\.(yaml|rid)$/ || f == ri_dir }
83
+
84
+ new_methods = new_ri_files.find_all {|f| f =~/\.yaml$/}
85
+ old_methods = old_ri_files.find_all {|f| f =~/\.yaml$/}
86
+
87
+ rep_new_classes = new_classes - old_classes
88
+ rep_remove_classes = old_classes - new_classes
89
+
90
+ rep_new_methods = new_methods - old_methods
91
+ rep_remove_methods = old_methods - new_methods
92
+
93
+ rep_new_methods = rep_new_methods.find_all {|f| !filter_method_from_classes?(f, rep_new_classes)}
94
+ rep_remove_methods = rep_remove_methods.find_all {|f| !filter_method_from_classes?(f, rep_remove_classes)}
95
+
96
+ rep_new_class_methods, rep_new_instance_methods = rep_new_methods.partition {|m| m =~ /-c\.yaml$/}
97
+ rep_remove_class_methods, rep_remove_instance_methods = rep_remove_methods.partition {|m| m =~ /-c\.yaml$/}
98
+
99
+ rep_new_instance_methods = rep_new_instance_methods.map {|m| strip_ri_method_name(m)}
100
+ rep_remove_instance_methods = rep_remove_instance_methods.map {|m| strip_ri_method_name(m)}
101
+
102
+ rep_new_class_methods = rep_new_class_methods.map {|m| strip_ri_method_name(m)}
103
+ rep_remove_class_methods = rep_remove_class_methods.map {|m| strip_ri_method_name(m)}
104
+
105
+ results[:new_classes] = make_table_iteration( rep_new_classes )
106
+ results[:removed_classes] = make_table_iteration( rep_remove_classes )
107
+ results[:new_instance_methods] = make_table_iteration( rep_new_instance_methods )
108
+ results[:removed_instance_methods] = make_table_iteration( rep_remove_instance_methods )
109
+ results[:new_class_methods] = make_table_iteration( rep_new_class_methods )
110
+ results[:removed_class_methods] = make_table_iteration( rep_remove_class_methods )
111
+
112
+ results
113
+ end
114
+
115
+ def make_table_iteration(data)
116
+ {:item => data.map { |c| { :name => c } } }
117
+ end
118
+
119
+ def find_ri_files(ri_dir)
120
+ files = Array.new
121
+ Find.find(ri_dir) do |file|
122
+ files << ProjectBuilderUtils.strip_base_dir(ri_dir, file)
123
+ end
124
+ files
125
+ end
126
+
127
+ end
128
+
@@ -0,0 +1,303 @@
1
+ # Code coverage analysis on Unit Tests.
2
+ #
3
+ #
4
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ #
12
+ # * Redistributions of source code must retain the above copyright
13
+ # notice, this list of conditions and the following disclaimer.
14
+ #
15
+ # * Redistributions in binary form must reproduce the above
16
+ # copyright notice, this list of conditions and the following
17
+ # disclaimer in the documentation and/or other materials provided
18
+ # with the distribution.
19
+ #
20
+ # * Neither the name of Ubit nor the names of its
21
+ # contributors may be used to endorse or promote products derived
22
+ # from this software without specific prior written permission.
23
+ #
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ #
37
+ # == Author
38
+ # Zev Blut, Pierre Baumard
39
+
40
+ require "kwala"
41
+ require "#{ACTION_DIR}/loc_count"
42
+ require 'tempfile'
43
+
44
+ # run with
45
+ # ./kwala -p TestProj -d ~/projects/TestProj
46
+ # -a code_coverage -o ~/tmp/builder
47
+
48
+ class CodeCoverageAction < BuildAction
49
+ include LOCCountModule
50
+
51
+ CodeCoverageFileData = Struct.new(:ruby_file, :percent, :rcov_index,
52
+ :file_cov_path, :loc)
53
+
54
+ RCOV = '/usr/bin/env rcov'
55
+
56
+ SUMMARY_COUNT = 30
57
+
58
+ PERCENT_ERROR = 70
59
+
60
+ PERCENT_WARNING = 90
61
+
62
+ DEBUG = false
63
+
64
+ def initialize
65
+ @output_data = Array.new
66
+ end
67
+
68
+ def build_action(context)
69
+ @ouput_dir = File.expand_path(context.output_directory)
70
+ if !File.exists?("#{@ouput_dir}/coverage")
71
+ FileUtils.mkdir("#{@ouput_dir}/coverage")
72
+ end
73
+
74
+ @test_cases_count = context.test_files.size
75
+
76
+ run_test_cases(context)
77
+
78
+ context.ruby_files.each do |ruby_file|
79
+ test_path = test_index_path(context, ruby_file)
80
+ rcov_index = test_path + "index.html"
81
+ file_cov_path = test_path + file_coverage_filename(context, ruby_file)
82
+ loc = count_loc(ruby_file).first
83
+ if File.exist?(test_path)
84
+ percent = parse_coverage_file(file_cov_path)
85
+ @output_data << CodeCoverageFileData.new(ruby_file, percent,
86
+ rcov_index, file_cov_path, loc)
87
+ else
88
+ @output_data << CodeCoverageFileData.new(ruby_file, nil, rcov_index,
89
+ file_cov_path, loc)
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+
96
+ def summary_display(context)
97
+ template = TemplateFile.new(self.class.summary_template_file)
98
+ unit_base = "#{context.project_name}_code_coverage.html"
99
+
100
+ cov_loc_weighted_sum = @output_data.inject(0) do |sum, file_data|
101
+ sum + (file_data.percent.to_i * file_data.loc)
102
+ end
103
+ loc_sum = @output_data.inject(0){|sum, file_data| sum + file_data.loc}
104
+
105
+ global_cov = cov_loc_weighted_sum / loc_sum
106
+
107
+ context.amrita_data[:code_coverage_results] = {
108
+ :c_num => @test_cases_count,
109
+ :global_cov => global_cov
110
+ }
111
+
112
+ output_summary = @output_data.sort do |x, y|
113
+ non_covered_loc(y) <=> non_covered_loc(x)
114
+ end
115
+ context.amrita_data[:code_coverage_summary] = {
116
+ :entry => entries(output_summary[0..SUMMARY_COUNT])
117
+ }
118
+
119
+ context.amrita_data[:code_coverage_details] =
120
+ (Amrita::e(:a, :href => unit_base) { "Code Coverage Details" })
121
+ summary_expand(template, context)
122
+ end
123
+
124
+ def detailed_display(context)
125
+ template = TemplateFile.new(self.class.detailed_template_file)
126
+
127
+ context.amrita_data[:code_coverage_details] = {
128
+ :entry => entries(@output_data)
129
+ }
130
+
131
+ det_res = ProjectBuilderUtils.expand_template(template, context.amrita_data)
132
+ det_file = "#{context.output_directory}/#{context.project_name}_code_coverage.html"
133
+ [det_file, det_res]
134
+ end
135
+
136
+ ###########################
137
+ # Protected Methods
138
+ ###########################
139
+ protected
140
+
141
+ def non_covered_loc(file_data)
142
+ (((100.0 - file_data.percent.to_f) / 100.0) * file_data.loc).to_i
143
+ end
144
+
145
+ def url_alias(url)
146
+ url.sub(@ouput_dir, '').sub(/^(\/)*/, '')
147
+ end
148
+
149
+ def entries(output_data)
150
+ output_data.map do |file|
151
+ {
152
+ :file => amrita_file(file.percent, file.ruby_file, file.rcov_index),
153
+ :loc => file.loc.to_s,
154
+ :nloc => non_covered_loc(file).to_s,
155
+ :result => put_class(file.percent, amrita_result(file.percent, file.file_cov_path))
156
+ }
157
+ end
158
+
159
+ end
160
+
161
+ def put_class(percent, amrita_e)
162
+ percent = percent.to_i
163
+ if percent < PERCENT_ERROR
164
+ Amrita.a(:class => "error") {amrita_e}
165
+ elsif percent < PERCENT_WARNING
166
+ Amrita.a(:class => "warning") {amrita_e}
167
+ else
168
+ Amrita.a(:class => "ok") {amrita_e}
169
+ end
170
+ end
171
+
172
+ def amrita_file(percent, ruby_file, rcov_index)
173
+ if percent
174
+ Amrita.e(:a, :href => url_alias(rcov_index)) {ruby_file}
175
+ else
176
+ Amrita.e(:span, :title => "no file #{rcov_index}") {ruby_file}
177
+ end
178
+ end
179
+
180
+ def amrita_result(percent, file_cov_path)
181
+ if percent.to_i > 0
182
+ Amrita.e(:a, :href => url_alias(file_cov_path)) {"#{percent} %"}
183
+ else
184
+ Amrita.e(:span, :title => "bad file #{file_cov_path}") {"0 %"}
185
+ end
186
+ end
187
+
188
+ def test_index_path(context, file)
189
+ proj_dir = context.project_directory.sub(/\/$/, '')
190
+ test_file = File.expand_path(proj_dir) + '_tests' +
191
+ File.dirname(file).sub(proj_dir, '') +
192
+ '_tc_' + File.basename(file)
193
+ out_file_dir(test_file, context.output_directory)[1]
194
+ end
195
+
196
+ def file_coverage_filename(context, file)
197
+ # from /usr/local/bin/rcov:74:in `normalize_filename' (rcov 0.5)
198
+ file = File.expand_path(file).gsub(/^#{Regexp.escape(Dir.getwd)}\//, '')
199
+
200
+ file.split("/").join("-").gsub('.', '_') + ".html"
201
+ end
202
+
203
+ COV_PATTERN = "<td class='covered' width='(\\d*)'"
204
+ REGEXP = /#{COV_PATTERN}.*#{COV_PATTERN}/m
205
+
206
+ def parse_coverage_file(file)
207
+ if File.exists?(file) and (match = REGEXP.match(File.read(file)))
208
+ match[2].to_i
209
+ else
210
+ 0
211
+ end
212
+ end
213
+
214
+ def out_file_dir(test_file, out_dir)
215
+ f_out = test_file.split("/").join("_") + "_coverage"
216
+ o_dir = "#{out_dir}/coverage/#{f_out}/"
217
+ [f_out, o_dir]
218
+ end
219
+
220
+ def exclude_cmd(context)
221
+ #if context.project_name =~ LIB_PROJECTS
222
+ # # discard the default regexp list, do not exclude site_ruby lib
223
+ # "--exclude-only lib/ruby/1.8"
224
+ #else
225
+ "--exclude lib/ruby"
226
+ #end
227
+ end
228
+
229
+ def run_test_cases(context)
230
+ context.test_files.each do |test_file|
231
+ run_test_case_with_rcov(test_file, context)
232
+ end
233
+ end
234
+
235
+ def run_test_case_with_rcov(test_file, context)
236
+ test_file = File.expand_path(test_file)
237
+ f_out, o_dir = out_file_dir(test_file, @ouput_dir)
238
+ o_dir = File.expand_path(o_dir)
239
+
240
+ if File.exists?(o_dir)
241
+ FileUtils.rm_rf(o_dir)
242
+ end
243
+ FileUtils.mkdir(o_dir)
244
+
245
+ cmd = "#{RCOV} #{exclude_cmd(context)} -o #{o_dir} #{test_file}"
246
+ puts cmd if DEBUG
247
+ system(cmd)
248
+ end
249
+
250
+ end
251
+
252
+
253
+ # use --aggregate option of rcov >= 0.7
254
+ class AggregateCodeCoverageAction < CodeCoverageAction
255
+
256
+ def self.summary_template_file
257
+ superclass.summary_template_file
258
+ end
259
+
260
+ def self.detailed_template_file
261
+ superclass.detailed_template_file
262
+ end
263
+
264
+ ##################
265
+ # protected
266
+ ##################
267
+ protected
268
+
269
+ def out_file_dir(test_file, out_dir)
270
+ f_out = test_file.split("/").join("_") + "_coverage"
271
+ [f_out, "#{out_dir}/coverage/aggregate/"]
272
+ end
273
+
274
+ def run_test_cases(context)
275
+ test_files = context.test_files.dup.map {|file| File.expand_path(file)}
276
+
277
+ o_dir = File.expand_path("#{File.expand_path(context.output_directory)}/coverage/aggregate")
278
+ puts o_dir
279
+ if File.exists?(o_dir)
280
+ FileUtils.rm_rf(o_dir)
281
+ end
282
+ FileUtils.mkdir(o_dir)
283
+
284
+ rcov_data = Tempfile.new('rcov_aggregate').path
285
+
286
+ one_file = test_files.shift
287
+
288
+ # first do not generate html, just aggregate to rcov file data
289
+ test_files.each do |file|
290
+ # "No file to analyze was found" with just --no-html
291
+ cmd = "#{RCOV} #{exclude_cmd(context)} --no-html -t --aggregate #{rcov_data} #{file}"
292
+ puts cmd if DEBUG
293
+ system(cmd)
294
+ end
295
+
296
+ # generate HTML for last file to be run
297
+ cmd = "#{RCOV} #{exclude_cmd(context)} --aggregate #{rcov_data} -o #{o_dir} #{one_file}"
298
+ system(cmd)
299
+
300
+ end
301
+
302
+ end
303
+