churn 0.0.33 → 0.0.34
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +2 -1
- data/Gemfile.lock +6 -1
- data/README.md +19 -13
- data/bin/churn +14 -1
- data/churn.gemspec +1 -0
- data/lib/churn/churn_calculator.rb +42 -17
- data/lib/churn/git_analyzer.rb +4 -0
- data/lib/churn/version.rb +1 -1
- data/lib/churn_options.rb +10 -3
- data/test/test_helper.rb +7 -0
- data/test/unit/churn_calculator_test.rb +44 -1
- data/test/unit/git_analyzer_test.rb +72 -1
- data/test/unit/source_control_test.rb +22 -0
- data/test/unit/svn_analyzer_test.rb +40 -0
- metadata +189 -163
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
churn (0.0.
|
4
|
+
churn (0.0.34)
|
5
5
|
chronic (>= 0.2.3)
|
6
6
|
hirb
|
7
7
|
json_pure
|
@@ -48,6 +48,10 @@ GEM
|
|
48
48
|
shoulda-context (1.0.2)
|
49
49
|
shoulda-matchers (1.4.1)
|
50
50
|
activesupport (>= 3.0.0)
|
51
|
+
simplecov (0.7.1)
|
52
|
+
multi_json (~> 1.0)
|
53
|
+
simplecov-html (~> 0.7.1)
|
54
|
+
simplecov-html (0.7.1)
|
51
55
|
test-construct (1.2.0)
|
52
56
|
|
53
57
|
PLATFORMS
|
@@ -59,4 +63,5 @@ DEPENDENCIES
|
|
59
63
|
mocha (~> 0.9.5)
|
60
64
|
rake
|
61
65
|
shoulda
|
66
|
+
simplecov
|
62
67
|
test-construct
|
data/README.md
CHANGED
@@ -6,8 +6,9 @@ Churn for files is immediate, but classes and methods requires buildings up a hi
|
|
6
6
|
|
7
7
|
Currently has full Git, Mercurial (hg), and Bazaar (bzr) support, and partial SVN support (supports only file level churn currently)
|
8
8
|
|
9
|
-
|
9
|
+
File changes can be calculated on any single commit to look at method changes you need to be running churn over time. Using a git post-commit hook, configuring your CI to run churn, or using [churn-site](http://churn.picoappz.com) is the best way to build up your churn history. See the --past_history (-p) option to do a one time run building up past class and method level churn.
|
10
10
|
|
11
|
+
####Authors:
|
11
12
|
* danmayer
|
12
13
|
* ajwalters
|
13
14
|
* cldwalker
|
@@ -20,11 +21,17 @@ Authors:
|
|
20
21
|
|
21
22
|
This project runs [travis-ci.org](http://travis-ci.org)
|
22
23
|
|
24
|
+
## TODO
|
25
|
+
|
26
|
+
Want to help out, there are easy tasks ready for some attention. The list of items has been moved to the [churn wafflie.io](http://waffle.io/danmayer/churn)
|
27
|
+
|
28
|
+
[![Stories in Ready](https://badge.waffle.io/danmayer/churn.png)](http://waffle.io/danmayer/churn)
|
29
|
+
|
23
30
|
## Churn Usage
|
24
31
|
|
25
32
|
Install with `gem install churn` or for bundler add to your Gemfile `gem 'churn', :require => false`.
|
26
33
|
|
27
|
-
The reason you want require false is that when required by default churn is expecting to add some rake tasks, you don't really want or need it loading when running your server or tests.
|
34
|
+
The reason you want require false is that when required by default churn is expecting to add some rake tasks, you don't really want or need it loading when running your server or tests.
|
28
35
|
|
29
36
|
* rake:
|
30
37
|
* add `require 'churn'` to Rakefile
|
@@ -45,6 +52,8 @@ The reason you want require false is that when required by default churn is expe
|
|
45
52
|
churn -c 10 #set minimum churn count on a file to 10
|
46
53
|
churn -c 5 -y -i "Gemfile" #mix and match
|
47
54
|
churn --start_date "6 months ago" #Start looking at file changes from 6 months ago
|
55
|
+
churn -p "4 months ago" #churn the past history to build up data for the last 4 months
|
56
|
+
churn --past_history #churn the past history for default 3 months to build up data
|
48
57
|
|
49
58
|
|
50
59
|
## Example Output
|
@@ -125,17 +134,14 @@ The reason you want require false is that when required by default churn is expe
|
|
125
134
|
churn [options]+
|
126
135
|
|
127
136
|
PARAMETERS
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
The list of items has been moved to the [churn wafflie.io](http://waffle.io/danmayer/churn)
|
137
|
-
|
138
|
-
[![Stories in Ready](https://badge.waffle.io/danmayer/churn.png)](http://waffle.io/danmayer/churn)
|
137
|
+
--minimum_churn_count=minimum_churn_count, -c (0 ~>
|
138
|
+
int(minimum_churn_count=3))
|
139
|
+
--yaml, -y
|
140
|
+
--ignore_files=[ignore_files], -i (0 ~> string(ignore_files=))
|
141
|
+
--start_date=[start_date], -s (0 ~> string(start_date=))
|
142
|
+
--data_directory=[data_directory], -d (0 ~> string(data_directory=))
|
143
|
+
--past_history=[past_history], -p (0 ~> string(past_history=))
|
144
|
+
--help, -h
|
139
145
|
|
140
146
|
## Notes on Patches/Pull Requests
|
141
147
|
|
data/bin/churn
CHANGED
@@ -33,9 +33,22 @@ Main do
|
|
33
33
|
default ''
|
34
34
|
end
|
35
35
|
|
36
|
+
#grrr h is already taken by --help / -h so whent with 'p'
|
37
|
+
option('past_history', 'p') do
|
38
|
+
cast :string
|
39
|
+
argument :optional
|
40
|
+
default ''
|
41
|
+
end
|
42
|
+
|
36
43
|
def report_churn(output_string)
|
37
44
|
require File.join(File.dirname(__FILE__), '..', 'lib', 'churn', 'churn_calculator')
|
38
|
-
|
45
|
+
options = {:minimum_churn_count => params['minimum_churn_count'].value,
|
46
|
+
:ignore_files => params['ignore_files'].value,
|
47
|
+
:start_date => params['start_date'].value,
|
48
|
+
:data_directory => params['data_directory'].value,
|
49
|
+
:history => params['past_history'].value
|
50
|
+
}
|
51
|
+
result = Churn::ChurnCalculator.new(options).report(output_string)
|
39
52
|
unless output_string
|
40
53
|
result = YAML::dump(result)
|
41
54
|
end
|
data/churn.gemspec
CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_development_dependency(%q<test-construct>, [">= 0"])
|
32
32
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
33
33
|
s.add_development_dependency(%q<mocha>, ["~> 0.9.5"])
|
34
|
+
s.add_development_dependency(%q<simplecov>,[">= 0"])
|
34
35
|
#s.add_development_dependency(%q<ruby-debug>, ["~> 0.10.4"])
|
35
36
|
s.add_runtime_dependency(%q<main>, [">= 0"])
|
36
37
|
s.add_runtime_dependency(%q<json_pure>, [">= 0"])
|
@@ -44,9 +44,37 @@ module Churn
|
|
44
44
|
# @param [Bolean] format to return the data, true for string or false for hash
|
45
45
|
# @return [Object] returns either a pretty string or a hash representing the chrun of the project
|
46
46
|
def report(print = true)
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
if @churn_options.history
|
48
|
+
generate_history
|
49
|
+
else
|
50
|
+
self.emit
|
51
|
+
self.analyze
|
52
|
+
print ? self.to_s : self.to_h
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# this method generates the past history of a churn project from first commit to current
|
57
|
+
# running the report for oldest commits first so they are built up correctly
|
58
|
+
def generate_history
|
59
|
+
if @source_control.is_a?(GitAnalyzer)
|
60
|
+
begin
|
61
|
+
history_starting_point = Chronic.parse(@churn_options.history)
|
62
|
+
@source_control.get_commit_history.each do |commit|
|
63
|
+
`git checkout #{commit}`
|
64
|
+
commit_date = `git show -s --format="%ci"`
|
65
|
+
commit_date = Time.parse(commit_date)
|
66
|
+
next if commit_date < history_starting_point
|
67
|
+
#7776000 == 3.months without adding active support depenancy
|
68
|
+
start_date = (commit_date - 7776000)
|
69
|
+
`churn -s "#{start_date}"`
|
70
|
+
end
|
71
|
+
ensure
|
72
|
+
`git checkout master`
|
73
|
+
end
|
74
|
+
"churn history complete, this has munipulated git please make sure you are back on HEAD where you expect to be"
|
75
|
+
else
|
76
|
+
raise "currently generate history only supports git"
|
77
|
+
end
|
50
78
|
end
|
51
79
|
|
52
80
|
# Emits various data from source control to be analyses later... Currently this is broken up like this as a throwback to metric_fu
|
@@ -87,6 +115,11 @@ module Churn
|
|
87
115
|
hash
|
88
116
|
end
|
89
117
|
|
118
|
+
def to_s
|
119
|
+
ChurnCalculator.to_s(to_h[:churn])
|
120
|
+
end
|
121
|
+
|
122
|
+
# Pretty print the data as a string for the user
|
90
123
|
def self.to_s(hash)
|
91
124
|
result = seperator
|
92
125
|
result +="* Revision Changes \n"
|
@@ -110,14 +143,10 @@ module Churn
|
|
110
143
|
result += display_array(method_churn)
|
111
144
|
end
|
112
145
|
|
113
|
-
# Pretty print the data as a string for the user
|
114
|
-
def to_s
|
115
|
-
ChurnCalculator.to_s(to_h[:churn])
|
116
|
-
end
|
117
|
-
|
118
146
|
private
|
119
147
|
|
120
148
|
def self.collect_items(collection, match)
|
149
|
+
return [] unless collection
|
121
150
|
collection.map {|item| (item.delete(match) || {}).merge(item) }
|
122
151
|
end
|
123
152
|
|
@@ -137,20 +166,16 @@ module Churn
|
|
137
166
|
"*"*70+"\n"
|
138
167
|
end
|
139
168
|
|
140
|
-
def seperator
|
141
|
-
ChurnCalculator.seperator
|
142
|
-
end
|
143
|
-
|
144
169
|
def self.git?
|
145
|
-
|
170
|
+
!!(`git branch 2>&1` && $?.success?)
|
146
171
|
end
|
147
|
-
|
172
|
+
|
148
173
|
def self.hg?
|
149
|
-
|
174
|
+
!!(`hg branch 2>&1` && $?.success?)
|
150
175
|
end
|
151
|
-
|
176
|
+
|
152
177
|
def self.bzr?
|
153
|
-
|
178
|
+
!!(`bzr nick 2>&1` && $?.success?)
|
154
179
|
end
|
155
180
|
|
156
181
|
def set_source_control(start_date)
|
data/lib/churn/git_analyzer.rb
CHANGED
@@ -9,6 +9,10 @@ module Churn
|
|
9
9
|
def get_revisions
|
10
10
|
`git log #{date_range} --pretty=format:"%H"`.split(/\n/).reject{|line| line == ""}
|
11
11
|
end
|
12
|
+
|
13
|
+
def get_commit_history
|
14
|
+
`git log --reverse --pretty=format:"%H"`.split(/\n/).reject{|line| line == ""}
|
15
|
+
end
|
12
16
|
|
13
17
|
private
|
14
18
|
|
data/lib/churn/version.rb
CHANGED
data/lib/churn_options.rb
CHANGED
@@ -7,14 +7,16 @@ module Churn
|
|
7
7
|
include Singleton
|
8
8
|
DEFAULT_CHURN_DIRECTORY = "tmp/churn"
|
9
9
|
DEFAULT_MINIMUM_CHURN_COUNT = 5
|
10
|
+
DEFAULT_START_TIME = '3 months ago'
|
10
11
|
|
11
|
-
attr_accessor :data_directory, :minimum_churn_count, :ignore_files, :start_date
|
12
|
+
attr_accessor :data_directory, :minimum_churn_count, :ignore_files, :start_date, :history
|
12
13
|
|
13
14
|
def initialize()
|
14
15
|
@data_directory = DEFAULT_CHURN_DIRECTORY
|
15
16
|
@minimum_churn_count = DEFAULT_MINIMUM_CHURN_COUNT
|
16
17
|
@ignore_files = ['/dev/null']
|
17
|
-
@start_date =
|
18
|
+
@start_date = DEFAULT_START_TIME
|
19
|
+
@history = nil
|
18
20
|
end
|
19
21
|
|
20
22
|
def set_options(options = {})
|
@@ -22,7 +24,12 @@ module Churn
|
|
22
24
|
@minimum_churn_count = options.fetch(:minimum_churn_count){ @minimum_churn_count }.to_i
|
23
25
|
@ignore_files = (options.fetch(:ignore_files){ @ignore_files }).to_s.split(',').map(&:strip)
|
24
26
|
@ignore_files << '/dev/null' unless @ignore_files.include?('/dev/null')
|
25
|
-
@start_date = options[:start_date] if options[:start_date].nil?
|
27
|
+
@start_date = options[:start_date] if !options[:start_date].nil? && options[:start_date]!=''
|
28
|
+
@history = options[:history] if !options[:history].nil? && options[:history]!=''
|
29
|
+
if @history=='true'
|
30
|
+
@history = DEFAULT_START_TIME
|
31
|
+
end
|
32
|
+
|
26
33
|
self
|
27
34
|
end
|
28
35
|
|
data/test/test_helper.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'simplecov'
|
2
3
|
require 'test/unit'
|
3
4
|
require 'shoulda'
|
4
5
|
require 'construct'
|
5
6
|
require 'mocha'
|
6
7
|
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter 'specs/ruby/1.9.1/gems/'
|
10
|
+
add_filter '/test/'
|
11
|
+
add_filter '/config/'
|
12
|
+
end
|
13
|
+
|
7
14
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
15
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
16
|
require 'churn/churn_calculator'
|
@@ -102,12 +102,55 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
should "have expected output for self.to_s" do
|
106
|
+
output = Churn::ChurnCalculator.to_s({})
|
107
|
+
assert_match /Revision Changes/, output
|
108
|
+
assert_match /Project Churn/, output
|
109
|
+
end
|
110
|
+
|
111
|
+
should "have expected output for to_s" do
|
112
|
+
calc = Churn::ChurnCalculator.new
|
113
|
+
calc.expects(:to_h).returns({:churn => {}})
|
114
|
+
output = calc.to_s
|
115
|
+
assert_match /Revision Changes/, output
|
116
|
+
assert_match /Project Churn/, output
|
117
|
+
end
|
118
|
+
|
105
119
|
should "initialize a churn calculator for hg repositories" do
|
106
120
|
Churn::ChurnCalculator.stubs(:git?).returns(false)
|
107
|
-
Churn::ChurnCalculator.
|
121
|
+
Churn::ChurnCalculator.expects(:`).with("hg branch 2>&1").returns(true) #` fix syntax hilighting
|
108
122
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
109
123
|
assert churn.instance_variable_get(:@source_control).is_a?(Churn::HgAnalyzer)
|
110
124
|
end
|
111
125
|
|
126
|
+
should "initialize a churn calculator for bzr repositories" do
|
127
|
+
Churn::ChurnCalculator.stubs(:git?).returns(false)
|
128
|
+
Churn::ChurnCalculator.stubs(:hg?).returns(false)
|
129
|
+
Churn::ChurnCalculator.expects(:`).with("bzr nick 2>&1").returns(true) #` fix syntax hilighting
|
130
|
+
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
131
|
+
assert churn.instance_variable_get(:@source_control).is_a?(Churn::BzrAnalyzer)
|
132
|
+
end
|
133
|
+
|
134
|
+
should "initialize a churn calculator for svn repositories" do
|
135
|
+
Churn::ChurnCalculator.stubs(:git?).returns(false)
|
136
|
+
Churn::ChurnCalculator.stubs(:hg?).returns(false)
|
137
|
+
Churn::ChurnCalculator.stubs(:bzr?).returns(false)
|
138
|
+
File.stubs(:exist?).returns(true)
|
139
|
+
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
140
|
+
assert churn.instance_variable_get(:@source_control).is_a?(Churn::SvnAnalyzer)
|
141
|
+
end
|
142
|
+
|
143
|
+
should "raise exception on a churn calculator for unknown repositories" do
|
144
|
+
Churn::ChurnCalculator.stubs(:git?).returns(false)
|
145
|
+
Churn::ChurnCalculator.stubs(:hg?).returns(false)
|
146
|
+
Churn::ChurnCalculator.stubs(:bzr?).returns(false)
|
147
|
+
File.stubs(:exist?).returns(false)
|
148
|
+
assert_raises RuntimeError do
|
149
|
+
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
|
112
155
|
|
113
156
|
end
|
@@ -12,6 +12,77 @@ class GitAnalyzerTest < Test::Unit::TestCase
|
|
12
12
|
expected_hash = {"lib/churn/churn_calculator.rb"=>[18..18, 19..19]}
|
13
13
|
assert_equal = updated
|
14
14
|
end
|
15
|
+
|
16
|
+
should "run get_logs correctly" do
|
17
|
+
git_analyzer = Churn::GitAnalyzer.new
|
18
|
+
git_analyzer.expects(:`).returns(git_logs_output) #`fix syntax hilighting
|
19
|
+
assert_equal ["public/css/application.css", "views/layout.erb", "README.md", "app.rb"], git_analyzer.get_logs
|
20
|
+
end
|
21
|
+
|
22
|
+
should "run get_revisions correctly" do
|
23
|
+
git_analyzer = Churn::GitAnalyzer.new
|
24
|
+
git_analyzer.expects(:`).returns(git_revisions_output) #`fix syntax hilighting
|
25
|
+
assert_equal ["0aef1f56d5b3b546457e996450fd9ceca379f0a8",
|
26
|
+
"8038f1b17c3749540650aaab3f4e5e846cfc3b47",
|
27
|
+
"4d7e4859b2ed8a7e4f73e3540e7879c00cba9783"], git_analyzer.get_revisions
|
28
|
+
end
|
15
29
|
|
16
|
-
|
30
|
+
should "run date range correctly" do
|
31
|
+
git_analyzer = Churn::GitAnalyzer.new(Date.parse('3/3/2010'))
|
32
|
+
assert_equal "--after=2010-03-03", git_analyzer.send(:date_range)
|
33
|
+
end
|
17
34
|
|
35
|
+
should "run get_diff correctly" do
|
36
|
+
git_analyzer = Churn::GitAnalyzer.new
|
37
|
+
git_analyzer.expects(:`).returns(git_git_diff_output) #`fix syntax hilighting
|
38
|
+
assert_equal ["--- a/public/css/application.css",
|
39
|
+
"+++ b/public/css/application.css",
|
40
|
+
"@@ -18,0 +19,4 @@ footer{",
|
41
|
+
"--- a/views/layout.erb",
|
42
|
+
"+++ b/views/layout.erb",
|
43
|
+
"@@ -43,0 +44 @@"], git_analyzer.send(:get_diff, 'rev', 'prev_rev')
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
# ran in a project
|
49
|
+
# git log --after=2013-09-05 --name-only --pretty=format:
|
50
|
+
def git_logs_output
|
51
|
+
"public/css/application.css
|
52
|
+
views/layout.erb
|
53
|
+
|
54
|
+
README.md
|
55
|
+
app.rb"
|
56
|
+
end
|
57
|
+
|
58
|
+
# ran in a project
|
59
|
+
# git log --after=2013-09-05 --pretty=format:"%H"
|
60
|
+
def git_revisions_output
|
61
|
+
"0aef1f56d5b3b546457e996450fd9ceca379f0a8
|
62
|
+
8038f1b17c3749540650aaab3f4e5e846cfc3b47
|
63
|
+
4d7e4859b2ed8a7e4f73e3540e7879c00cba9783"
|
64
|
+
end
|
65
|
+
|
66
|
+
# ran in a project
|
67
|
+
# git diff 4d7e4859b2ed8a7e4f73e3540e7879c00cba9783 8038f1b17c3749540650aaab3f4e5e846cfc3b47 --unified=0
|
68
|
+
def git_git_diff_output
|
69
|
+
output = <<EOF
|
70
|
+
diff --git a/public/css/application.css b/public/css/application.css
|
71
|
+
index 522ca1a..730eb1e 100644
|
72
|
+
--- a/public/css/application.css
|
73
|
+
+++ b/public/css/application.css
|
74
|
+
@@ -18,0 +19,4 @@ footer{
|
75
|
+
+
|
76
|
+
+footer .container .right {
|
77
|
+
+ float:right;
|
78
|
+
+}
|
79
|
+
diff --git a/views/layout.erb b/views/layout.erb
|
80
|
+
index f8d3aea..7f4fb2f 100644
|
81
|
+
--- a/views/layout.erb
|
82
|
+
+++ b/views/layout.erb
|
83
|
+
@@ -43,0 +44 @@
|
84
|
+
+ <span class="right">a part of <a href="http://picoappz.com">picoappz</a></span>
|
85
|
+
EOF
|
86
|
+
output
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class SourceControlTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "get_updated_files_from_log if revision and previous revision" do
|
6
|
+
sc = Churn::SourceControl.new(Date.today)
|
7
|
+
current = 'current'
|
8
|
+
revisions = ['future',current,'past']
|
9
|
+
def sc.get_diff(revision, previous_revision)
|
10
|
+
[previous_revision]
|
11
|
+
end
|
12
|
+
assert_equal ['past'], sc.get_updated_files_from_log(current,revisions)
|
13
|
+
end
|
14
|
+
|
15
|
+
should "get_updated_files_from_log get empty array when no revisions found" do
|
16
|
+
sc = Churn::SourceControl.new(Date.today)
|
17
|
+
current = 'current'
|
18
|
+
revisions = ['future',current]
|
19
|
+
assert_equal [], sc.get_updated_files_from_log(current,revisions)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class SvnAnalyzerTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "parses logs correctly" do
|
6
|
+
svn_analyzer = Churn::SvnAnalyzer.new
|
7
|
+
revision = 'first'
|
8
|
+
revisions = ['first']
|
9
|
+
lines = ["--- a/lib/churn/churn_calculator.rb", "+++ b/lib/churn/churn_calculator.rb", "@@ -18,0 +19 @@ module Churn"]
|
10
|
+
svn_analyzer.stubs(:get_updated_files_from_log).returns(lines)
|
11
|
+
updated = svn_analyzer.get_updated_files_change_info(revision, revisions)
|
12
|
+
expected_hash = {"lib/churn/churn_calculator.rb"=>[18..18, 19..19]}
|
13
|
+
assert_equal = updated
|
14
|
+
end
|
15
|
+
|
16
|
+
should "run get_logs correctly" do
|
17
|
+
svn_analyzer = Churn::SvnAnalyzer.new
|
18
|
+
svn_analyzer.expects(:`).returns(svn_output) #`fix syntax hilighting
|
19
|
+
assert_equal ["/trunk", "/trunk/test.txt"], svn_analyzer.get_logs
|
20
|
+
end
|
21
|
+
|
22
|
+
should "run date range correctly" do
|
23
|
+
svn_analyzer = Churn::SvnAnalyzer.new(Date.parse('3/3/2010'))
|
24
|
+
assert_equal "--revision {2010-03-03}:{#{Date.today.to_s}}", svn_analyzer.send(:date_range)
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def svn_output
|
30
|
+
"------------------------------------------------------------------------
|
31
|
+
r1 | danmayer | 2013-09-07 10:45:32 -0400 (Sat, 07 Sep 2013) | 1 line
|
32
|
+
Changed paths:
|
33
|
+
A /trunk
|
34
|
+
A /trunk/test.txt
|
35
|
+
|
36
|
+
Initial import of project1
|
37
|
+
------------------------------------------------------------------------"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
metadata
CHANGED
@@ -1,192 +1,219 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: churn
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.34
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 33
|
10
|
-
version: 0.0.33
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Dan Mayer
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
21
|
-
version_requirements: &id001 !ruby/object:Gem::Requirement
|
22
|
-
none: false
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
hash: 3
|
27
|
-
segments:
|
28
|
-
- 0
|
29
|
-
version: "0"
|
30
|
-
requirement: *id001
|
31
|
-
prerelease: false
|
12
|
+
date: 2012-12-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
32
15
|
name: shoulda
|
33
|
-
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
version_requirements: &id002 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
36
17
|
none: false
|
37
|
-
requirements:
|
38
|
-
- -
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
|
41
|
-
|
42
|
-
- 0
|
43
|
-
version: "0"
|
44
|
-
requirement: *id002
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
45
23
|
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
46
31
|
name: jeweler
|
47
|
-
|
48
|
-
- !ruby/object:Gem::Dependency
|
49
|
-
version_requirements: &id003 !ruby/object:Gem::Requirement
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
50
33
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
55
|
-
|
56
|
-
- 0
|
57
|
-
version: "0"
|
58
|
-
requirement: *id003
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
59
39
|
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
60
47
|
name: test-construct
|
61
|
-
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
version_requirements: &id004 !ruby/object:Gem::Requirement
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
64
49
|
none: false
|
65
|
-
requirements:
|
66
|
-
- -
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
|
69
|
-
|
70
|
-
- 0
|
71
|
-
version: "0"
|
72
|
-
requirement: *id004
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
73
55
|
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
74
63
|
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
75
70
|
type: :development
|
76
|
-
|
77
|
-
version_requirements:
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
73
|
none: false
|
79
|
-
requirements:
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: mocha
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
80
83
|
- - ~>
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
hash: 49
|
83
|
-
segments:
|
84
|
-
- 0
|
85
|
-
- 9
|
86
|
-
- 5
|
84
|
+
- !ruby/object:Gem::Version
|
87
85
|
version: 0.9.5
|
88
|
-
requirement: *id005
|
89
|
-
prerelease: false
|
90
|
-
name: mocha
|
91
86
|
type: :development
|
92
|
-
|
93
|
-
version_requirements:
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.9.5
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
94
97
|
none: false
|
95
|
-
requirements:
|
96
|
-
- -
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
|
99
|
-
|
100
|
-
- 0
|
101
|
-
version: "0"
|
102
|
-
requirement: *id006
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
103
|
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
104
111
|
name: main
|
105
|
-
|
106
|
-
- !ruby/object:Gem::Dependency
|
107
|
-
version_requirements: &id007 !ruby/object:Gem::Requirement
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
108
113
|
none: false
|
109
|
-
requirements:
|
110
|
-
- -
|
111
|
-
- !ruby/object:Gem::Version
|
112
|
-
|
113
|
-
|
114
|
-
- 0
|
115
|
-
version: "0"
|
116
|
-
requirement: *id007
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
117
119
|
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
118
127
|
name: json_pure
|
119
|
-
|
120
|
-
- !ruby/object:Gem::Dependency
|
121
|
-
version_requirements: &id008 !ruby/object:Gem::Requirement
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
122
129
|
none: false
|
123
|
-
requirements:
|
124
|
-
- -
|
125
|
-
- !ruby/object:Gem::Version
|
126
|
-
|
127
|
-
|
128
|
-
- 0
|
129
|
-
- 2
|
130
|
-
- 3
|
131
|
-
version: 0.2.3
|
132
|
-
requirement: *id008
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
133
135
|
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
134
143
|
name: chronic
|
135
|
-
|
136
|
-
- !ruby/object:Gem::Dependency
|
137
|
-
version_requirements: &id009 !ruby/object:Gem::Requirement
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
138
145
|
none: false
|
139
|
-
requirements:
|
140
|
-
- -
|
141
|
-
- !ruby/object:Gem::Version
|
142
|
-
|
143
|
-
|
144
|
-
- 4
|
145
|
-
- 1
|
146
|
-
version: "4.1"
|
147
|
-
requirement: *id009
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 0.2.3
|
150
|
+
type: :runtime
|
148
151
|
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 0.2.3
|
158
|
+
- !ruby/object:Gem::Dependency
|
149
159
|
name: sexp_processor
|
150
|
-
|
151
|
-
- !ruby/object:Gem::Dependency
|
152
|
-
version_requirements: &id010 !ruby/object:Gem::Requirement
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
153
161
|
none: false
|
154
|
-
requirements:
|
162
|
+
requirements:
|
155
163
|
- - ~>
|
156
|
-
- !ruby/object:Gem::Version
|
157
|
-
|
158
|
-
|
159
|
-
- 3
|
160
|
-
- 0
|
161
|
-
version: "3.0"
|
162
|
-
requirement: *id010
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '4.1'
|
166
|
+
type: :runtime
|
163
167
|
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '4.1'
|
174
|
+
- !ruby/object:Gem::Dependency
|
164
175
|
name: ruby_parser
|
165
|
-
|
166
|
-
- !ruby/object:Gem::Dependency
|
167
|
-
version_requirements: &id011 !ruby/object:Gem::Requirement
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
168
177
|
none: false
|
169
|
-
requirements:
|
170
|
-
- -
|
171
|
-
- !ruby/object:Gem::Version
|
172
|
-
|
173
|
-
|
174
|
-
- 0
|
175
|
-
version: "0"
|
176
|
-
requirement: *id011
|
178
|
+
requirements:
|
179
|
+
- - ~>
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '3.0'
|
182
|
+
type: :runtime
|
177
183
|
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ~>
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '3.0'
|
190
|
+
- !ruby/object:Gem::Dependency
|
178
191
|
name: hirb
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ! '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
179
198
|
type: :runtime
|
180
|
-
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
206
|
+
description: ! 'High method and class churn has been shown to have increased bug and
|
207
|
+
error rates. This gem helps you know what is changing a lot so you can do additional
|
208
|
+
testing, code review, or refactoring to try to tame the volatile code. '
|
181
209
|
email: dan@mayerdan.com
|
182
|
-
executables:
|
210
|
+
executables:
|
183
211
|
- churn
|
184
212
|
extensions: []
|
185
|
-
|
186
|
-
extra_rdoc_files:
|
213
|
+
extra_rdoc_files:
|
187
214
|
- LICENSE
|
188
215
|
- README.md
|
189
|
-
files:
|
216
|
+
files:
|
190
217
|
- .deferred_server
|
191
218
|
- .document
|
192
219
|
- .gitignore
|
@@ -222,40 +249,37 @@ files:
|
|
222
249
|
- test/unit/git_analyzer_test.rb
|
223
250
|
- test/unit/hg_analyzer_test.rb
|
224
251
|
- test/unit/location_mapping_test.rb
|
252
|
+
- test/unit/source_control_test.rb
|
253
|
+
- test/unit/svn_analyzer_test.rb
|
225
254
|
homepage: http://github.com/danmayer/churn
|
226
|
-
licenses:
|
255
|
+
licenses:
|
227
256
|
- MIT
|
228
257
|
post_install_message:
|
229
258
|
rdoc_options: []
|
230
|
-
|
231
|
-
require_paths:
|
259
|
+
require_paths:
|
232
260
|
- lib
|
233
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
261
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
234
262
|
none: false
|
235
|
-
requirements:
|
236
|
-
- -
|
237
|
-
- !ruby/object:Gem::Version
|
238
|
-
|
239
|
-
segments:
|
263
|
+
requirements:
|
264
|
+
- - ! '>='
|
265
|
+
- !ruby/object:Gem::Version
|
266
|
+
version: '0'
|
267
|
+
segments:
|
240
268
|
- 0
|
241
|
-
|
242
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
269
|
+
hash: 837759494224756390
|
270
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
243
271
|
none: false
|
244
|
-
requirements:
|
245
|
-
- -
|
246
|
-
- !ruby/object:Gem::Version
|
247
|
-
|
248
|
-
segments:
|
249
|
-
- 0
|
250
|
-
version: "0"
|
272
|
+
requirements:
|
273
|
+
- - ! '>='
|
274
|
+
- !ruby/object:Gem::Version
|
275
|
+
version: '0'
|
251
276
|
requirements: []
|
252
|
-
|
253
277
|
rubyforge_project: churn
|
254
|
-
rubygems_version: 1.8.
|
278
|
+
rubygems_version: 1.8.25
|
255
279
|
signing_key:
|
256
280
|
specification_version: 3
|
257
281
|
summary: Providing additional churn metrics over the original metric_fu churn
|
258
|
-
test_files:
|
282
|
+
test_files:
|
259
283
|
- test/data/churn_calculator.rb
|
260
284
|
- test/data/test_helper.rb
|
261
285
|
- test/test_helper.rb
|
@@ -266,3 +290,5 @@ test_files:
|
|
266
290
|
- test/unit/git_analyzer_test.rb
|
267
291
|
- test/unit/hg_analyzer_test.rb
|
268
292
|
- test/unit/location_mapping_test.rb
|
293
|
+
- test/unit/source_control_test.rb
|
294
|
+
- test/unit/svn_analyzer_test.rb
|