nielsm-metric_fu 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ <html>
2
+ <head>
3
+ <title>Reek Results</title>
4
+ <style>
5
+ <%= inline_css("default.css") %>
6
+ </style>
7
+ </head>
8
+
9
+ <body>
10
+ <h1>Reek Results</h1>
11
+ <p><a href="http://reek.rubyforge.org/">Reek</a> detects common code smells in ruby code.</p>
12
+ <table>
13
+ <tr>
14
+ <th>File Path</th>
15
+ <th>Code Smell</th>
16
+ </tr>
17
+ <% @matches.each_with_index do |match, count| %>
18
+ <tr class='<%= cycle("light", "dark", count) %>'>
19
+ <td><%= match.first %></td>
20
+ <td>
21
+ <% match[1..-1].each do |line| %>
22
+ <%= line %><br>
23
+ <% end %>
24
+ </td>
25
+ </tr>
26
+ <% end %>
27
+ </table>
28
+ <p>Generated on <%= Time.now.localtime %></p>
29
+ </body>
30
+ </html>
@@ -0,0 +1,26 @@
1
+ <html>
2
+ <head>
3
+ <title>Roodi Results</title>
4
+ <style>
5
+ <%= inline_css("default.css") %>
6
+ </style>
7
+ </head>
8
+
9
+ <body>
10
+ <h1>Roodi Results</h1>
11
+ <p><a href="http://roodi.rubyforge.org/">Roodi</a> parses your Ruby code and warns you about design issues you have based on the checks that is has configured.</p>
12
+ <table>
13
+ <tr>
14
+ <th>File Path</th>
15
+ <th>Warning</th>
16
+ </tr>
17
+ <% @matches.each_with_index do |match, count| %>
18
+ <tr class='<%= cycle("light", "dark", count) %>'>
19
+ <td><%= link_to_filename(match.first.split(':').first) if match.first %></td>
20
+ <td><%= match[1] %></td>
21
+ </tr>
22
+ <% end %>
23
+ </table>
24
+ <p>Generated on <%= Time.now.localtime %></p>
25
+ </body>
26
+ </html>
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe MetricFu::Base::Generator do
4
+ describe "save_html" do
5
+ it "should save to a index.html in the base_dir" do
6
+ @generator = MetricFu::Base::Generator.new
7
+ @generator.should_receive(:open).with("#{MetricFu::BASE_DIRECTORY}/generator/index.html", "w")
8
+ @generator.save_html("<html>")
9
+ end
10
+
11
+ it "should save to a custom.html to the base_dir if 'custom' is passed as name" do
12
+ @generator = MetricFu::Base::Generator.new
13
+ @generator.should_receive(:open).with("#{MetricFu::BASE_DIRECTORY}/generator/metric_fu/custom.html", "w")
14
+ @generator.save_html("<html>", 'metric_fu/custom.html')
15
+ end
16
+ end
17
+
18
+ describe "generate_report class method" do
19
+ it "should create a new Generator and call generate_report on it" do
20
+ @generator = mock('generator')
21
+ @generator.should_receive(:generate_report)
22
+ MetricFu::Base::Generator.should_receive(:new).and_return(@generator)
23
+ MetricFu::Base::Generator.generate_report('base_dir')
24
+ end
25
+ end
26
+
27
+ describe "generate_html" do
28
+ it "should create a new Generator and call generate_report on it" do
29
+ @generator = MetricFu::Base::Generator.new
30
+ @generator.should_receive(:open).with("#{MetricFu::BASE_DIRECTORY}/generator/index.html", "w")
31
+ @generator.should_receive(:generate_html).and_return('<html>')
32
+ @generator.generate_report
33
+ end
34
+ end
35
+
36
+ describe "cycle" do
37
+ it "should create a new Generator and call generate_report on it" do
38
+ @generator = MetricFu::Base::Generator.new
39
+ @generator.cycle("light", "dark", 0).should == 'light'
40
+ @generator.cycle("light", "dark", 1).should == 'dark'
41
+ end
42
+ end
43
+
44
+ describe "template_name" do
45
+ it "should return the class name in lowercase" do
46
+ @generator = MetricFu::Base::Generator.new
47
+ @generator.template_name.should == 'generator'
48
+ end
49
+ end
50
+
51
+ describe "metric_dir" do
52
+ it "should return tmp/metric_fu/{the class name in lowercase}" do
53
+ MetricFu::Base::Generator.metric_dir.should == "#{MetricFu::BASE_DIRECTORY}/generator"
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,117 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe MetricFu::Churn do
4
+ describe "generate_report" do
5
+ after do
6
+ load File.dirname(__FILE__) + '/../lib/metric_fu/churn.rb' #need to reload file to wipe out mock of private static
7
+ end
8
+
9
+ it "should strip out files that have less than the min count" do
10
+ logs = ["accept", "accept", "accept", "reject", "reject"]
11
+ git_mock = mock('git')
12
+ git_mock.should_receive(:get_logs).and_return(logs)
13
+ Churn::Git.should_receive(:new).and_return(git_mock)
14
+ churn = Churn.new(:scm => :git, :minimum_churn_count => 3)
15
+ churn.analyze
16
+ churn.instance_variable_get(:@changes).should == {"accept"=>3}
17
+ end
18
+
19
+ it "should have a default min count of 5" do
20
+ churn = Churn.new('base_dir')
21
+ churn.instance_variable_get(:@minimum_churn_count).should == 5
22
+ end
23
+
24
+ end
25
+
26
+ describe "template_name" do
27
+ it "should return the class name in lowercase" do
28
+ churn = Churn.new
29
+ churn.template_name.should == 'churn'
30
+ end
31
+ end
32
+
33
+ describe "parse_log_for_changes" do
34
+ it "should count the changes with git" do
35
+ logs = ["home_page/index.html", "README", "History.txt", "README", "History.txt", "README"]
36
+ git_mock = mock('git')
37
+ git_mock.should_receive(:get_logs).and_return(logs)
38
+ Churn::Git.should_receive(:new).and_return(git_mock)
39
+ File.should_receive(:exist?).with(".git").and_return(true)
40
+ changes = Churn.new.send(:parse_log_for_changes)
41
+ changes["home_page/index.html"].should == 1
42
+ changes["History.txt"].should == 2
43
+ changes["README"].should == 3
44
+ end
45
+
46
+ it "should count the changes with svn" do
47
+ logs = ["home_page/index.html", "README", "History.txt", "README", "History.txt", "README"]
48
+ svn_mock = mock('svn')
49
+ svn_mock.should_receive(:get_logs).and_return(logs)
50
+ Churn::Svn.should_receive(:new).and_return(svn_mock)
51
+ File.should_receive(:exist?).with(".git").and_return(false)
52
+ File.should_receive(:exist?).with(".svn").and_return(true)
53
+ changes = Churn.new.send(:parse_log_for_changes)
54
+ changes["home_page/index.html"].should == 1
55
+ changes["History.txt"].should == 2
56
+ changes["README"].should == 3
57
+ end
58
+ end
59
+ end
60
+
61
+ describe MetricFu::Churn::Svn do
62
+
63
+ describe "get_logs" do
64
+ it "should use the start date if supplied" do
65
+ @churn = Churn::Svn.new
66
+ @churn.should_receive(:`).with('svn log --verbose').and_return("")
67
+ @churn.get_logs
68
+ end
69
+ it "should use the start date if supplied" do
70
+ Time.should_receive(:now).and_return(Date.new(2001, 1, 2))
71
+ @churn = Churn::Svn.new(lambda{Date.new(2000, 1, 1)})
72
+ @churn.should_receive(:require_rails_env)
73
+ @churn.should_receive(:`).with("svn log --revision {2000-01-01}:{2001-01-02} --verbose").and_return("")
74
+ @churn.get_logs
75
+ end
76
+ end
77
+
78
+ describe "clean_up_svn_line" do
79
+ it "should return nil for non matches" do
80
+ Churn::Svn.new.send(:clean_up_svn_line, "Adding Google analytics").should be_nil
81
+ Churn::Svn.new.send(:clean_up_svn_line, "A bunch of new files").should be_nil
82
+ end
83
+
84
+ it "should strip out all but the full path" do
85
+ Churn::Svn.new.send(:clean_up_svn_line, " A /trunk/lib/server.rb ").should == "/trunk/lib/server.rb"
86
+ Churn::Svn.new.send(:clean_up_svn_line, "A /trunk/lib/server.rb ").should == "/trunk/lib/server.rb"
87
+ Churn::Svn.new.send(:clean_up_svn_line, " A /trunk/lib/server.rb ").should == "/trunk/lib/server.rb"
88
+ Churn::Svn.new.send(:clean_up_svn_line, " A /trunk/lib/server.rb").should == "/trunk/lib/server.rb"
89
+ Churn::Svn.new.send(:clean_up_svn_line, "A /trunk/lib/server.rb").should == "/trunk/lib/server.rb"
90
+ Churn::Svn.new.send(:clean_up_svn_line, "A /trunk/lib/server.rb").should == "/trunk/lib/server.rb"
91
+
92
+ Churn::Svn.new.send(:clean_up_svn_line, " M /trunk/lib/server.rb ").should == "/trunk/lib/server.rb"
93
+ Churn::Svn.new.send(:clean_up_svn_line, "M /trunk/lib/server.rb ").should == "/trunk/lib/server.rb"
94
+ Churn::Svn.new.send(:clean_up_svn_line, " M /trunk/lib/server.rb ").should == "/trunk/lib/server.rb"
95
+ Churn::Svn.new.send(:clean_up_svn_line, " M /trunk/lib/server.rb").should == "/trunk/lib/server.rb"
96
+ Churn::Svn.new.send(:clean_up_svn_line, "M /trunk/lib/server.rb").should == "/trunk/lib/server.rb"
97
+ Churn::Svn.new.send(:clean_up_svn_line, "M /trunk/lib/server.rb").should == "/trunk/lib/server.rb"
98
+ end
99
+ end
100
+ end
101
+
102
+ describe MetricFu::Churn::Git do
103
+
104
+ describe "get_logs" do
105
+ it "should use the start date if supplied" do
106
+ @churn = Churn::Git.new
107
+ @churn.should_receive(:`).with('git log --name-only --pretty=format:').and_return("")
108
+ @churn.get_logs
109
+ end
110
+ it "should use the start date if supplied" do
111
+ @churn = Churn::Git.new(lambda{Date.new(2000, 1, 1)})
112
+ @churn.should_receive(:require_rails_env)
113
+ @churn.should_receive(:`).with("git log --after=2000-01-01 --name-only --pretty=format:").and_return("")
114
+ @churn.get_logs
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,110 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe MetricFu::Configuration do
4
+ before do
5
+ MetricFu.configuration.reset
6
+ end
7
+ after do
8
+ ENV['CC_BUILD_ARTIFACTS'] = nil
9
+ end
10
+ describe "open_in_browser" do
11
+ it "should return false if running in cruise" do
12
+ unless ENV['CC_BUILD_ARTIFACTS']
13
+ MetricFu.open_in_browser?.should == !!PLATFORM['darwin']
14
+ ENV['CC_BUILD_ARTIFACTS'] = ''
15
+ MetricFu.open_in_browser?.should == false
16
+ end
17
+ end
18
+ end
19
+
20
+ describe "metrics" do
21
+ it "should be configurable" do
22
+ MetricFu.metrics.should == [:coverage, :churn, :flog, :flay, :reek, :roodi, :saikuro]
23
+ MetricFu::Configuration.run do |config|
24
+ config.metrics = [:coverage, :flog]
25
+ end
26
+ MetricFu.metrics.should == [:coverage, :flog]
27
+ end
28
+ end
29
+
30
+ describe "churn" do
31
+ it "should be configurable" do
32
+ now = Time.now
33
+ MetricFu.churn.should == {}
34
+ MetricFu::Configuration.run do |config|
35
+ config.churn[:start_date] = now
36
+ end
37
+ MetricFu.churn.should == {:start_date => now }
38
+ end
39
+ end
40
+
41
+ describe "coverage" do
42
+ it "should be configurable" do
43
+ MetricFu.coverage[:test_files].should == ['test/**/*_test.rb', 'spec/**/*_spec.rb']
44
+ MetricFu::Configuration.run do |config|
45
+ config.coverage[:test_files] = ['test/**/test_*.rb']
46
+ end
47
+ MetricFu.coverage[:test_files].should == ['test/**/test_*.rb']
48
+ end
49
+ end
50
+
51
+ describe "flay" do
52
+ it "should be configurable" do
53
+ now = Time.now
54
+ MetricFu.flay.should == { :dirs_to_flay => ['lib'] }
55
+ MetricFu::Configuration.run do |config|
56
+ config.flay[:dirs_to_flay] = ['cms/app', 'cms/lib']
57
+ end
58
+ MetricFu.flay.should == { :dirs_to_flay => ['cms/app', 'cms/lib'] }
59
+ end
60
+ end
61
+
62
+ describe "flog" do
63
+ it "should be configurable" do
64
+ MetricFu.flog.should == { :dirs_to_flog => ['lib'] }
65
+ MetricFu::Configuration.run do |config|
66
+ config.flog[:dirs_to_flog] = ['cms/app', 'cms/lib']
67
+ end
68
+ MetricFu.flog.should == { :dirs_to_flog => ['cms/app', 'cms/lib'] }
69
+ end
70
+ end
71
+
72
+ describe "saikuro" do
73
+ it "should be configurable" do
74
+ MetricFu.saikuro.should == {}
75
+ MetricFu::Configuration.run do |config|
76
+ config.saikuro = { "--warn_cyclo" => "3", "--error_cyclo" => "4" }
77
+ end
78
+ MetricFu.saikuro.should == { "--warn_cyclo" => "3", "--error_cyclo" => "4" }
79
+ end
80
+
81
+ it "should only accept a Hash" do
82
+ MetricFu.saikuro.should == {}
83
+ lambda {
84
+ MetricFu::Configuration.run do |config|
85
+ config.saikuro = ''
86
+ end
87
+ }.should raise_error
88
+ end
89
+ end
90
+
91
+ describe "reek" do
92
+ it "should be configurable" do
93
+ MetricFu.reek.should == { :dirs_to_reek => ['lib'] }
94
+ MetricFu::Configuration.run do |config|
95
+ config.reek[:dirs_to_reek] = ['cms/app', 'cms/lib']
96
+ end
97
+ MetricFu.reek.should == { :dirs_to_reek => ['cms/app', 'cms/lib'] }
98
+ end
99
+ end
100
+
101
+ describe "roodi" do
102
+ it "should be configurable" do
103
+ MetricFu.roodi.should == { :dirs_to_roodi => ['lib'] }
104
+ MetricFu::Configuration.run do |config|
105
+ config.roodi[:dirs_to_roodi] = ['cms/app', 'cms/lib']
106
+ end
107
+ MetricFu.roodi.should == { :dirs_to_roodi => ['cms/app', 'cms/lib'] }
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe MetricFu::Flay do
4
+
5
+ describe "generate_html" do
6
+ it "should create a new Generator and call generate_report on it" do
7
+ @generator = MetricFu::Flay.new('other_dir')
8
+ @generator.should_receive(:`).and_return("Matches found in :call (mass = 55)\n\tlib/metric_fu/flog_reporter.rb:2\n\tlib/metric_fu/flog_reporter.rb:3")
9
+ @generator.generate_html
10
+ end
11
+ end
12
+
13
+ describe "template_name" do
14
+ it "should return the class name in lowercase" do
15
+ flay = MetricFu::Flay.new('base_dir')
16
+ flay.template_name.should == 'flay'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,208 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ include MetricFu::Flog
3
+
4
+ describe "Flog::Base" do
5
+ before do
6
+ @alpha_only_method = <<-AOM
7
+ Total flog = 13.6283678106927
8
+
9
+ ErrorMailer#errormail: (12.5)
10
+ 12.0: assignment
11
+ 1.2: []
12
+ 1.2: now
13
+ 1.2: content_type
14
+ AOM
15
+
16
+ @method_that_has_digits = <<-MTHD
17
+ Total flog = 7.08378429936994
18
+
19
+ NoImmunizationReason#to_c32: (7.1)
20
+ 3.0: code
21
+ 2.3: branch
22
+ 1.4: templateId
23
+ 1.2: act
24
+ 1.1: entryRelationship
25
+ MTHD
26
+
27
+ @bang_method = <<-BM
28
+ Total flog = 7.08378429936994
29
+
30
+ NoImmunizationReason#to_c32!: (7.1)
31
+ 3.0: code
32
+ 2.3: branch
33
+ 1.4: templateId
34
+ 1.2: act
35
+ 1.1: entryRelationship
36
+ BM
37
+
38
+ @invalid_method = <<-IM
39
+ Total flog = 7.08378429936994
40
+
41
+ 3.0: code
42
+ 2.3: branch
43
+ 1.4: templateId
44
+ 1.2: act
45
+ 1.1: entryRelationship
46
+ IM
47
+
48
+ @assignment_method = <<-MTHD
49
+ Total Flog = 21.6 (5.4 +/- 3.3 flog / method)
50
+
51
+ ActivityReport#existing_measure_attributes=: (8.5)
52
+ 4.1: assignment
53
+ 1.8: id
54
+ 1.6: to_s
55
+ 1.4: []
56
+ 1.4: activity_report_measures
57
+ 1.2: each
58
+ 1.2: branch
59
+ MTHD
60
+
61
+ @class_methods_grouped_together = <<-MTHD
62
+ Total Flog = 61.8 (7.7 +/- 95.3 flog / method)
63
+
64
+ User#none: (32.8)
65
+ 7.2: include
66
+ 3.6: validates_length_of
67
+ 3.6: validates_format_of
68
+ 2.4: validates_presence_of
69
+ 2.4: validates_uniqueness_of
70
+ 1.4: bad_login_message
71
+ 1.4: name_regex
72
+ 1.4: bad_email_message
73
+ 1.4: bad_name_message
74
+ 1.4: login_regex
75
+ 1.4: email_regex
76
+ 1.2: private
77
+ 1.2: has_and_belongs_to_many
78
+ 1.2: before_create
79
+ 1.2: attr_accessible
80
+ 0.4: lit_fixnum
81
+ MTHD
82
+ end
83
+
84
+ it "should be able to parse class_methods_grouped_together" do
85
+ page = Base.parse(@class_methods_grouped_together)
86
+ page.should_not be_nil
87
+ page.score.should == 61.8
88
+ page.scanned_methods.size.should == 1
89
+ sm = page.scanned_methods.first
90
+ sm.name.should == 'User#none'
91
+ sm.score.should == 32.8
92
+
93
+ sm.operators.size.should == 16
94
+ sm.operators.first.score.should == 7.2
95
+ sm.operators.first.operator.should == "include"
96
+
97
+ sm.operators.last.score.should == 0.4
98
+ sm.operators.last.operator.should == "lit_fixnum"
99
+ end
100
+
101
+ it "should be able to parse an assignment method" do
102
+ page = Base.parse(@assignment_method)
103
+ page.should_not be_nil
104
+ page.score.should == 21.6
105
+ page.scanned_methods.size.should == 1
106
+ sm = page.scanned_methods.first
107
+ sm.name.should == 'ActivityReport#existing_measure_attributes='
108
+ sm.score.should == 8.5
109
+ end
110
+
111
+ it "should be able to parse an alpha only method" do
112
+ page = Base.parse(@alpha_only_method)
113
+ page.should_not be_nil
114
+ page.score.should == 13.6283678106927
115
+ page.scanned_methods.size.should == 1
116
+ sm = page.scanned_methods.first
117
+ sm.name.should == 'ErrorMailer#errormail'
118
+ sm.score.should == 12.5
119
+ end
120
+
121
+ it "should be able to parse method that has digits" do
122
+ page = Base.parse(@method_that_has_digits)
123
+ page.should_not be_nil
124
+ page.score.should == 7.08378429936994
125
+ page.scanned_methods.size.should == 1
126
+ sm = page.scanned_methods.first
127
+ sm.name.should == 'NoImmunizationReason#to_c32'
128
+ sm.score.should == 7.1
129
+ end
130
+
131
+ it "should be able to parse bang method" do
132
+ page = Base.parse(@bang_method)
133
+ page.should_not be_nil
134
+ page.score.should == 7.08378429936994
135
+ page.scanned_methods.size.should == 1
136
+ sm = page.scanned_methods.first
137
+ sm.name.should == 'NoImmunizationReason#to_c32!'
138
+ sm.score.should == 7.1
139
+ end
140
+
141
+ it "should return nil when parsing invalid method" do
142
+ page = Base.parse(@invalid_method)
143
+ page.should be_nil
144
+ end
145
+ end
146
+
147
+ IM = <<-IM
148
+ Total flog = 7.08378429936994
149
+
150
+ 3.0: code
151
+ 2.3: branch
152
+ 1.4: templateId
153
+ 1.2: act
154
+ 1.1: entryRelationship
155
+ IM
156
+ describe MetricFu::Flog do
157
+
158
+ describe "generate_report" do
159
+ it "should generate reports" do
160
+ generator = Flog::Generator.new('other_dir')
161
+ generator.should_receive(:flog_results).and_return(['A', 'B'])
162
+ generator.should_receive(:save_html).at_least(3).times.and_return('')
163
+ generator.should_receive(:open).any_number_of_times.and_return(['Total Flog = 1273.9 (9.3 +/- 259.2 flog / method)', 'TokenCounter#list_tokens_per_line: (15.2)', '9.0: assignment'].join("\n"))
164
+ generator.generate_report
165
+ end
166
+
167
+ it "should be able to handle InvalidFlogs" do
168
+ generator = Flog::Generator.new('other_dir')
169
+ generator.should_receive(:flog_results).and_return(['A', 'B'])
170
+ generator.should_receive(:inline_css).any_number_of_times.and_return('')
171
+ generator.should_receive(:save_html).once
172
+ generator.should_receive(:open).any_number_of_times.and_return(IM)
173
+ generator.generate_report
174
+ end
175
+ end
176
+
177
+ describe "template_name" do
178
+ it "should return the class name in lowercase" do
179
+ flog = Flog::Generator.new('base_dir')
180
+ Flog::Generator.template_name.should == 'flog'
181
+ end
182
+ end
183
+ end
184
+
185
+ describe MetricFu::Flog::Page do
186
+
187
+ describe "average_score" do
188
+ it "should calculate the average score" do
189
+ page = Page.new(10)
190
+ page.should_receive(:scanned_methods).any_number_of_times.and_return([ScannedMethod.new(:test, 10), ScannedMethod.new(:test, 20)])
191
+ page.average_score.should == 15
192
+ end
193
+
194
+ it "should be able to handle divide by zero" do
195
+ page = Page.new(10)
196
+ page.should_receive(:scanned_methods).any_number_of_times.and_return([])
197
+ page.average_score.should == 0
198
+ end
199
+ end
200
+
201
+ describe "highest_score" do
202
+ it "should calculate the average score" do
203
+ page = Page.new(10)
204
+ page.should_receive(:scanned_methods).any_number_of_times.and_return([ScannedMethod.new(:test, 10), ScannedMethod.new(:test, 20)])
205
+ page.highest_score.should == 20
206
+ end
207
+ end
208
+ end