kwala 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +62 -0
- data/bin/kwala +39 -0
- data/lib/kwala.rb +59 -0
- data/lib/kwala/actions/code_change.rb +128 -0
- data/lib/kwala/actions/code_coverage.rb +303 -0
- data/lib/kwala/actions/code_duplication.rb +222 -0
- data/lib/kwala/actions/code_formatting.rb +358 -0
- data/lib/kwala/actions/cycle_detection.rb +118 -0
- data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
- data/lib/kwala/actions/dead_code.rb +68 -0
- data/lib/kwala/actions/executable_files_check.rb +111 -0
- data/lib/kwala/actions/flagged_comments.rb +128 -0
- data/lib/kwala/actions/gem_plugin.rb +31 -0
- data/lib/kwala/actions/loc_count.rb +153 -0
- data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
- data/lib/kwala/actions/outside_links.rb +85 -0
- data/lib/kwala/actions/rails_migrate.rb +62 -0
- data/lib/kwala/actions/strange_requires.rb +106 -0
- data/lib/kwala/actions/syntax_check.rb +130 -0
- data/lib/kwala/actions/unit_test.rb +368 -0
- data/lib/kwala/build_action.rb +88 -0
- data/lib/kwala/build_context.rb +49 -0
- data/lib/kwala/command_line_runner.rb +184 -0
- data/lib/kwala/ext/demos.jar +0 -0
- data/lib/kwala/ext/pmd.jar +0 -0
- data/lib/kwala/ext/prefuse.jar +0 -0
- data/lib/kwala/extensions.rb +36 -0
- data/lib/kwala/lib/code_analyzer.rb +678 -0
- data/lib/kwala/lib/cycle_detector.rb +793 -0
- data/lib/kwala/lib/strange_requires_detector.rb +145 -0
- data/lib/kwala/notification.rb +45 -0
- data/lib/kwala/notifications/email.rb +54 -0
- data/lib/kwala/notifications/rss.rb +151 -0
- data/lib/kwala/project_builder_utils.rb +178 -0
- data/lib/kwala/templates/build_template.html +33 -0
- data/lib/kwala/templates/code_change_summary.html +27 -0
- data/lib/kwala/templates/code_coverage_detailed.html +25 -0
- data/lib/kwala/templates/code_coverage_summary.html +30 -0
- data/lib/kwala/templates/code_duplication_detailed.html +45 -0
- data/lib/kwala/templates/code_duplication_summary.html +9 -0
- data/lib/kwala/templates/code_formatting_detailed.html +27 -0
- data/lib/kwala/templates/code_formatting_summary.html +11 -0
- data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
- data/lib/kwala/templates/cycle_detection_summary.html +7 -0
- data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
- data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
- data/lib/kwala/templates/executable_files_check_summary.html +20 -0
- data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
- data/lib/kwala/templates/flagged_comments_summary.html +10 -0
- data/lib/kwala/templates/loc_count_detailed.html +24 -0
- data/lib/kwala/templates/loc_count_summary.html +14 -0
- data/lib/kwala/templates/mdd/1.png +0 -0
- data/lib/kwala/templates/mdd/2.png +0 -0
- data/lib/kwala/templates/mdd/3.png +0 -0
- data/lib/kwala/templates/mdd/4.png +0 -0
- data/lib/kwala/templates/mdd/5.png +0 -0
- data/lib/kwala/templates/outside_links_summary.html +4 -0
- data/lib/kwala/templates/strange_requires_detailed.html +23 -0
- data/lib/kwala/templates/strange_requires_summary.html +13 -0
- data/lib/kwala/templates/style.css +95 -0
- data/lib/kwala/templates/syntax_check_detailed.html +21 -0
- data/lib/kwala/templates/syntax_check_summary.html +7 -0
- data/lib/kwala/templates/unit_test_detailed.html +66 -0
- data/lib/kwala/templates/unit_test_summary.html +70 -0
- data/test/tc_build_action.rb +32 -0
- data/test/tc_templates.rb +24 -0
- 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
|
data/bin/kwala
ADDED
@@ -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'
|
data/lib/kwala.rb
ADDED
@@ -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
|
+
|