leh-metric_fu 0.9.2

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.
@@ -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
data/spec/flay_spec.rb ADDED
@@ -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
data/spec/flog_spec.rb ADDED
@@ -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
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe MetricFu::MD5Tracker do
4
+ before do
5
+ @tmp_dir = File.join(File.dirname(__FILE__), 'tmp')
6
+ FileUtils.mkdir_p(@tmp_dir, :verbose => false) unless File.directory?(@tmp_dir)
7
+ @file1 = File.new(File.join(@tmp_dir, 'file1.txt'), 'w')
8
+ @file2 = File.new(File.join(@tmp_dir, 'file2.txt'), 'w')
9
+ end
10
+
11
+ after do
12
+ FileUtils.rm_rf(@tmp_dir, :verbose => false)
13
+ end
14
+
15
+ it "identical files should match" do
16
+ @file1.puts("Hello World")
17
+ @file1.close
18
+ file1_md5 = MD5Tracker.track(@file1.path, @tmp_dir)
19
+
20
+ @file2.puts("Hello World")
21
+ @file2.close
22
+ file2_md5 = MD5Tracker.track(@file2.path, @tmp_dir)
23
+
24
+ file2_md5.should == file1_md5
25
+ end
26
+
27
+ it "different files should not match" do
28
+ @file1.puts("Hello World")
29
+ @file1.close
30
+ file1_md5 = MD5Tracker.track(@file1.path, @tmp_dir)
31
+
32
+ @file2.puts("Goodbye World")
33
+ @file2.close
34
+ file2_md5 = MD5Tracker.track(@file2.path, @tmp_dir)
35
+
36
+ file2_md5.should_not == file1_md5
37
+ end
38
+
39
+ it "file_changed? should detect a change" do
40
+ @file2.close
41
+
42
+ @file1.puts("Hello World")
43
+ @file1.close
44
+ file1_md5 = MD5Tracker.track(@file1.path, @tmp_dir)
45
+
46
+ @file1 = File.new(File.join(@tmp_dir, 'file1.txt'), 'w')
47
+ @file1.puts("Goodbye World")
48
+ @file1.close
49
+ MD5Tracker.file_changed?(@file1.path, @tmp_dir).should be_true
50
+ end
51
+
52
+ it "should detect a new file" do
53
+ @file2.close
54
+ MD5Tracker.file_changed?(@file1.path, @tmp_dir).should be_true
55
+ File.exist?(MD5Tracker.md5_file(@file1.path, @tmp_dir)).should be_true
56
+ end
57
+ end
data/spec/reek_spec.rb ADDED
@@ -0,0 +1,26 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ REEK_RESULT = %("lib/metric_fu/base.rb" -- 5 warnings:
4
+ [Utility Function] #configuration doesn't depend on instance state
5
+ [Utility Function] #open_in_browser? doesn't depend on instance state
6
+ [Long Method] Configuration#reset has approx 6 statements
7
+ [Utility Function] Generator#cycle doesn't depend on instance state
8
+ [Utility Function] Generator#link_to_filename doesn't depend on instance state)
9
+
10
+ describe MetricFu::Reek do
11
+
12
+ describe "generate_html" do
13
+ it "should create a new Generator and call generate_report on it" do
14
+ @generator = MetricFu::Reek.new('other_dir')
15
+ @generator.should_receive(:`).and_return(REEK_RESULT)
16
+ @generator.generate_html
17
+ end
18
+ end
19
+
20
+ describe "template_name" do
21
+ it "should return the class name in lowercase" do
22
+ flay = MetricFu::Reek.new('base_dir')
23
+ flay.template_name.should == 'reek'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'date'
4
+
5
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu/base')
6
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu/flay')
7
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu/flog')
8
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu/md5_tracker')
9
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu/churn')
10
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu/reek')
11
+ include MetricFu