kwala 0.9.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 (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
+