turbulence 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +51 -0
- data/CHANGELOG.md +208 -0
- data/Gemfile.lock +47 -24
- data/LICENSE.txt +7 -0
- data/README.md +100 -31
- data/bin/bule +1 -1
- data/docs/scatter-plot.png +0 -0
- data/docs/treemap.png +0 -0
- data/lib/turbulence/calculators/churn.rb +46 -34
- data/lib/turbulence/calculators/complexity.rb +19 -27
- data/lib/turbulence/checks_environment.rb +2 -1
- data/lib/turbulence/cli_parser.rb +53 -0
- data/lib/turbulence/command_line_interface.rb +30 -48
- data/lib/turbulence/configuration.rb +30 -0
- data/lib/turbulence/generators/scatterplot.rb +2 -2
- data/lib/turbulence/generators/treemap.rb +2 -2
- data/lib/turbulence/scm/git.rb +1 -1
- data/lib/turbulence/version.rb +1 -1
- data/lib/turbulence.rb +39 -20
- data/spec/turbulence/calculators/churn_spec.rb +53 -63
- data/spec/turbulence/calculators/complexity_spec.rb +5 -6
- data/spec/turbulence/cli_parser_spec.rb +50 -0
- data/spec/turbulence/command_line_interface_spec.rb +34 -19
- data/spec/turbulence/configuration_spec.rb +19 -0
- data/spec/turbulence/generators/scatter_plot_spec.rb +49 -44
- data/spec/turbulence/generators/treemap_spec.rb +7 -7
- data/spec/turbulence/scm/git_spec.rb +7 -6
- data/spec/turbulence/scm/perforce_spec.rb +44 -40
- data/spec/turbulence/turbulence_spec.rb +15 -7
- data/turbulence.gemspec +13 -9
- metadata +65 -32
- data/.travis.yml +0 -6
- data/spec/turbulence/checks_environoment_spec.rb +0 -11
|
@@ -1,39 +1,31 @@
|
|
|
1
1
|
require 'stringio'
|
|
2
2
|
require 'flog'
|
|
3
3
|
|
|
4
|
-
class Ruby19Parser < RubyParser
|
|
5
|
-
def process(ruby, file)
|
|
6
|
-
ruby.gsub!(/(\w+):\s+/, '"\1" =>')
|
|
7
|
-
super(ruby, file)
|
|
8
|
-
end
|
|
9
|
-
end unless defined?(:Ruby19Parser)
|
|
10
|
-
|
|
11
|
-
class Flog19 < Flog
|
|
12
|
-
def initialize option = {}
|
|
13
|
-
super(option)
|
|
14
|
-
@parser = Ruby19Parser.new
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
4
|
class Turbulence
|
|
19
5
|
module Calculators
|
|
20
6
|
class Complexity
|
|
21
|
-
|
|
22
|
-
def flogger
|
|
23
|
-
@flogger ||= Flog19.new(:continue => true)
|
|
24
|
-
end
|
|
25
|
-
def for_these_files(files)
|
|
26
|
-
files.each do |filename|
|
|
27
|
-
yield filename, score_for_file(filename)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
7
|
+
attr_reader :config, :type
|
|
30
8
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
def initialize(config = nil)
|
|
10
|
+
@config = config || Turbulence.config
|
|
11
|
+
@type = :complexity
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def flogger
|
|
15
|
+
@flogger ||= Flog.new(continue: true)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def for_these_files(files)
|
|
19
|
+
files.each do |filename|
|
|
20
|
+
yield filename, score_for_file(filename)
|
|
35
21
|
end
|
|
36
22
|
end
|
|
23
|
+
|
|
24
|
+
def score_for_file(filename)
|
|
25
|
+
flogger.reset
|
|
26
|
+
flogger.flog filename
|
|
27
|
+
flogger.total_score
|
|
28
|
+
end
|
|
37
29
|
end
|
|
38
30
|
end
|
|
39
31
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
class Turbulence
|
|
2
|
+
class CommandLineInterface
|
|
3
|
+
# I Update a Turbulence::Configuration instance to match the user's
|
|
4
|
+
# expectations (as expressed in ARGV)
|
|
5
|
+
module ConfigParser
|
|
6
|
+
def self.parse_argv_into_config(argv, config)
|
|
7
|
+
option_parser = OptionParser.new do |opts|
|
|
8
|
+
opts.banner = "Usage: bule [options] [dir]"
|
|
9
|
+
|
|
10
|
+
opts.on('--scm p4|git', String, 'scm to use (default: git)') do |s|
|
|
11
|
+
case s
|
|
12
|
+
when "git", "", nil
|
|
13
|
+
when "p4"
|
|
14
|
+
config.scm_name = 'Perforce'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
opts.on('--churn-range since..until', String, 'commit range to compute file churn') do |s|
|
|
19
|
+
config.commit_range = s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
opts.on('--churn-mean', 'calculate mean churn instead of cumulative') do
|
|
23
|
+
config.compute_mean = true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
opts.on('--exclude pattern', String, 'exclude files matching pattern') do |pattern|
|
|
27
|
+
config.exclusion_pattern = pattern
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on('--treemap', String, 'output treemap graph instead of scatterplot') do |s|
|
|
31
|
+
config.graph_type = "treemap"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
opts.on('--no-open', 'skip opening the report in a browser') do
|
|
35
|
+
config.no_open = true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
opts.on('--output DIR', String, 'output directory for reports (default: ./turbulence)') do |dir|
|
|
39
|
+
config.output_dir = dir
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
43
|
+
puts opts
|
|
44
|
+
exit
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
option_parser.parse!(argv)
|
|
48
|
+
|
|
49
|
+
config.directory = argv.first unless argv.empty?
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -1,60 +1,42 @@
|
|
|
1
1
|
require 'fileutils'
|
|
2
2
|
require 'launchy'
|
|
3
3
|
require 'optparse'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
|
|
6
|
+
require 'turbulence/configuration'
|
|
7
|
+
require 'turbulence/cli_parser'
|
|
4
8
|
require 'turbulence/scm/git'
|
|
5
9
|
require 'turbulence/scm/perforce'
|
|
6
10
|
|
|
7
11
|
class Turbulence
|
|
8
12
|
class CommandLineInterface
|
|
9
13
|
TURBULENCE_TEMPLATE_PATH = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "template")
|
|
10
|
-
TEMPLATE_FILES = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
TEMPLATE_FILES = [
|
|
15
|
+
'turbulence.html',
|
|
16
|
+
'highcharts.js',
|
|
17
|
+
'jquery.min.js',
|
|
18
|
+
'treemap.html',
|
|
19
|
+
].map do |filename|
|
|
14
20
|
File.join(TURBULENCE_TEMPLATE_PATH, filename)
|
|
15
21
|
end
|
|
16
22
|
|
|
17
|
-
attr_reader :exclusion_pattern
|
|
18
|
-
attr_reader :directory
|
|
19
23
|
def initialize(argv, additional_options = {})
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
opts.banner = "Usage: bule [options] [dir]"
|
|
24
|
-
|
|
25
|
-
opts.on('--scm p4|git', String, 'scm to use (default: git)') do |s|
|
|
26
|
-
case s
|
|
27
|
-
when "git", "", nil
|
|
28
|
-
when "p4"
|
|
29
|
-
Turbulence::Calculators::Churn.scm = Scm::Perforce
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
opts.on('--churn-range since..until', String, 'commit range to compute file churn') do |s|
|
|
34
|
-
Turbulence::Calculators::Churn.commit_range = s
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
opts.on('--churn-mean', 'calculate mean churn instead of cummulative') do
|
|
38
|
-
Turbulence::Calculators::Churn.compute_mean = true
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
opts.on('--exclude pattern', String, 'exclude files matching pattern') do |pattern|
|
|
42
|
-
@exclusion_pattern = pattern
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
opts.on('--treemap', String, 'output treemap graph instead of scatterplot') do |s|
|
|
46
|
-
@graph_type = "treemap"
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
|
51
|
-
puts opts
|
|
52
|
-
exit
|
|
53
|
-
end
|
|
54
|
-
end.parse!(argv)
|
|
24
|
+
ConfigParser.parse_argv_into_config argv, config
|
|
25
|
+
config.output = additional_options.fetch(:output, STDOUT)
|
|
26
|
+
end
|
|
55
27
|
|
|
56
|
-
|
|
57
|
-
|
|
28
|
+
extend Forwardable
|
|
29
|
+
def_delegators :Turbulence, :config
|
|
30
|
+
def_delegators :config, *[
|
|
31
|
+
:directory,
|
|
32
|
+
:graph_type,
|
|
33
|
+
:exclusion_pattern,
|
|
34
|
+
:no_open,
|
|
35
|
+
:output_dir,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def output_path
|
|
39
|
+
output_dir || File.join(Dir.pwd, "turbulence")
|
|
58
40
|
end
|
|
59
41
|
|
|
60
42
|
def copy_templates_into(directory)
|
|
@@ -62,12 +44,12 @@ class Turbulence
|
|
|
62
44
|
end
|
|
63
45
|
|
|
64
46
|
def generate_bundle
|
|
65
|
-
FileUtils.mkdir_p(
|
|
47
|
+
FileUtils.mkdir_p(output_path)
|
|
66
48
|
|
|
67
|
-
Dir.chdir(
|
|
68
|
-
turb = Turbulence.new(
|
|
49
|
+
Dir.chdir(output_path) do
|
|
50
|
+
turb = Turbulence.new(config)
|
|
69
51
|
|
|
70
|
-
generator = case
|
|
52
|
+
generator = case graph_type
|
|
71
53
|
when "treemap"
|
|
72
54
|
Turbulence::Generators::TreeMap.new({})
|
|
73
55
|
else
|
|
@@ -79,7 +61,7 @@ class Turbulence
|
|
|
79
61
|
end
|
|
80
62
|
|
|
81
63
|
def open_bundle
|
|
82
|
-
Launchy.open("file:///#{
|
|
64
|
+
Launchy.open("file:///#{output_path}/#{graph_type}.html")
|
|
83
65
|
end
|
|
84
66
|
end
|
|
85
67
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class Turbulence
|
|
2
|
+
class Configuration
|
|
3
|
+
attr_accessor *[
|
|
4
|
+
:directory,
|
|
5
|
+
:scm,
|
|
6
|
+
:scm_name,
|
|
7
|
+
:commit_range,
|
|
8
|
+
:compute_mean,
|
|
9
|
+
:exclusion_pattern,
|
|
10
|
+
:graph_type,
|
|
11
|
+
:output,
|
|
12
|
+
:output_dir,
|
|
13
|
+
:no_open,
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@directory = Dir.pwd
|
|
18
|
+
@graph_type = 'turbulence'
|
|
19
|
+
@scm_name = 'Git'
|
|
20
|
+
@output = STDOUT
|
|
21
|
+
@output_dir = nil
|
|
22
|
+
@no_open = false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# TODO: drop attr accessor and ivar once it stops getting set via Churn
|
|
26
|
+
def scm
|
|
27
|
+
@scm || Turbulence::Scm.const_get(scm_name)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -4,8 +4,8 @@ class Turbulence
|
|
|
4
4
|
attr_reader :metrics_hash, :x_metric, :y_metric
|
|
5
5
|
|
|
6
6
|
def initialize(metrics_hash,
|
|
7
|
-
x_metric =
|
|
8
|
-
y_metric =
|
|
7
|
+
x_metric = :churn,
|
|
8
|
+
y_metric = :complexity)
|
|
9
9
|
@x_metric = x_metric
|
|
10
10
|
@y_metric = y_metric
|
|
11
11
|
@metrics_hash = metrics_hash
|
|
@@ -4,8 +4,8 @@ class Turbulence
|
|
|
4
4
|
attr_reader :metrics_hash, :x_metric, :y_metric
|
|
5
5
|
|
|
6
6
|
def initialize(metrics_hash,
|
|
7
|
-
x_metric =
|
|
8
|
-
y_metric =
|
|
7
|
+
x_metric = :churn,
|
|
8
|
+
y_metric = :complexity)
|
|
9
9
|
@x_metric = x_metric
|
|
10
10
|
@y_metric = y_metric
|
|
11
11
|
@metrics_hash = metrics_hash
|
data/lib/turbulence/scm/git.rb
CHANGED
data/lib/turbulence/version.rb
CHANGED
data/lib/turbulence.rb
CHANGED
|
@@ -1,32 +1,47 @@
|
|
|
1
|
+
require 'turbulence/configuration'
|
|
1
2
|
require 'turbulence/file_name_mangler'
|
|
2
3
|
require 'turbulence/command_line_interface'
|
|
3
|
-
require 'turbulence/checks_environment'
|
|
4
4
|
require 'turbulence/calculators/churn'
|
|
5
5
|
require 'turbulence/calculators/complexity'
|
|
6
6
|
require 'turbulence/generators/treemap'
|
|
7
7
|
require 'turbulence/generators/scatterplot'
|
|
8
8
|
|
|
9
9
|
class Turbulence
|
|
10
|
-
CODE_DIRECTORIES = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
CODE_DIRECTORIES = [
|
|
11
|
+
"app/models",
|
|
12
|
+
"app/controllers",
|
|
13
|
+
"app/helpers",
|
|
14
|
+
"app/jobs",
|
|
15
|
+
"app/mailers",
|
|
16
|
+
"app/validators",
|
|
17
|
+
"lib",
|
|
18
|
+
]
|
|
19
|
+
CALCULATORS = [
|
|
20
|
+
Turbulence::Calculators::Complexity,
|
|
21
|
+
Turbulence::Calculators::Churn,
|
|
22
|
+
]
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
# Make a config instance available to anyone who wants one
|
|
25
|
+
def self.config
|
|
26
|
+
@config ||= Configuration.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_reader :config
|
|
21
30
|
attr_reader :metrics
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
extend Forwardable
|
|
33
|
+
def_delegators :config, *[
|
|
34
|
+
:directory,
|
|
35
|
+
:exclusion_pattern,
|
|
36
|
+
:output,
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
def initialize(config = nil)
|
|
40
|
+
@config = config || Turbulence.config
|
|
41
|
+
@metrics = {}
|
|
27
42
|
|
|
28
43
|
Dir.chdir(directory) do
|
|
29
|
-
|
|
44
|
+
calculators.each(&method(:calculate_metrics_with))
|
|
30
45
|
end
|
|
31
46
|
end
|
|
32
47
|
|
|
@@ -40,14 +55,14 @@ class Turbulence
|
|
|
40
55
|
|
|
41
56
|
calculator.for_these_files(files_of_interest) do |filename, score|
|
|
42
57
|
report "."
|
|
43
|
-
set_file_metric(filename, calculator, score)
|
|
58
|
+
set_file_metric(filename, calculator.type, score)
|
|
44
59
|
end
|
|
45
60
|
|
|
46
61
|
report "\n"
|
|
47
62
|
end
|
|
48
63
|
|
|
49
64
|
def report(this)
|
|
50
|
-
|
|
65
|
+
output.print this unless output.nil?
|
|
51
66
|
end
|
|
52
67
|
|
|
53
68
|
def set_file_metric(filename, metric, value)
|
|
@@ -60,9 +75,13 @@ class Turbulence
|
|
|
60
75
|
|
|
61
76
|
private
|
|
62
77
|
def exclude_files(files)
|
|
63
|
-
if not
|
|
64
|
-
files = files.reject { |f| f =~ Regexp.new(
|
|
78
|
+
if not exclusion_pattern.nil?
|
|
79
|
+
files = files.reject { |f| f =~ Regexp.new(exclusion_pattern) }
|
|
65
80
|
end
|
|
66
81
|
files
|
|
67
82
|
end
|
|
83
|
+
|
|
84
|
+
def calculators
|
|
85
|
+
CALCULATORS.map { |calc_class| calc_class.new(config) }
|
|
86
|
+
end
|
|
68
87
|
end
|
|
@@ -1,152 +1,142 @@
|
|
|
1
1
|
require 'turbulence/calculators/churn'
|
|
2
2
|
|
|
3
3
|
describe Turbulence::Calculators::Churn do
|
|
4
|
-
let(:calculator) { Turbulence::Calculators::Churn }
|
|
4
|
+
let(:calculator) { Turbulence::Calculators::Churn.new(config) }
|
|
5
|
+
let(:config) { Turbulence::Configuration.new }
|
|
6
|
+
|
|
5
7
|
before do
|
|
6
|
-
calculator.
|
|
8
|
+
allow(calculator).to receive(:scm_log_command).and_return("")
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
describe "::for_these_files" do
|
|
10
12
|
it "yields up the filename and score for each file" do
|
|
11
13
|
files = ["lib/corey.rb", "lib/chad.rb"]
|
|
12
|
-
calculator.
|
|
13
|
-
[
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
]
|
|
17
|
-
}
|
|
14
|
+
allow(calculator).to receive(:changes_by_ruby_file).and_return([
|
|
15
|
+
["lib/corey.rb", 5],
|
|
16
|
+
["lib/chad.rb", 10]
|
|
17
|
+
])
|
|
18
18
|
yielded_files = []
|
|
19
19
|
calculator.for_these_files(files) do |filename, score|
|
|
20
20
|
yielded_files << [filename, score]
|
|
21
21
|
end
|
|
22
|
-
yielded_files.
|
|
23
|
-
["lib/chad.rb",10]]
|
|
22
|
+
expect(yielded_files).to match_array([["lib/corey.rb", 5], ["lib/chad.rb", 10]])
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
it "filters the results by the passed-in files" do
|
|
27
26
|
files = ["lib/corey.rb"]
|
|
28
|
-
calculator.
|
|
29
|
-
[
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
]
|
|
33
|
-
}
|
|
27
|
+
allow(calculator).to receive(:changes_by_ruby_file).and_return([
|
|
28
|
+
["lib/corey.rb", 5],
|
|
29
|
+
["lib/chad.rb", 10]
|
|
30
|
+
])
|
|
34
31
|
yielded_files = []
|
|
35
32
|
calculator.for_these_files(files) do |filename, score|
|
|
36
33
|
yielded_files << [filename, score]
|
|
37
34
|
end
|
|
38
|
-
yielded_files.
|
|
35
|
+
expect(yielded_files).to match_array([["lib/corey.rb", 5]])
|
|
39
36
|
end
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
describe "::scm_log_file_lines" do
|
|
43
40
|
it "returns just the file lines" do
|
|
44
|
-
calculator.
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
allow(calculator).to receive(:scm_log_command).and_return(
|
|
42
|
+
"\n\n\n\n10\t6\tlib/turbulence.rb\n\n\n\n17\t2\tlib/eddies.rb\n"
|
|
43
|
+
)
|
|
47
44
|
|
|
48
|
-
calculator.scm_log_file_lines.
|
|
45
|
+
expect(calculator.scm_log_file_lines).to match_array([
|
|
49
46
|
"10\t6\tlib/turbulence.rb",
|
|
50
47
|
"17\t2\tlib/eddies.rb"
|
|
51
|
-
]
|
|
48
|
+
])
|
|
52
49
|
end
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
describe "::counted_line_changes_by_file_by_commit" do
|
|
56
53
|
before do
|
|
57
|
-
calculator.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
]
|
|
62
|
-
}
|
|
54
|
+
allow(calculator).to receive(:scm_log_file_lines).and_return([
|
|
55
|
+
"10\t6\tlib/turbulence.rb",
|
|
56
|
+
"17\t2\tlib/eddies.rb"
|
|
57
|
+
])
|
|
63
58
|
end
|
|
64
59
|
|
|
65
60
|
it "sums up the line changes" do
|
|
66
|
-
calculator.counted_line_changes_by_file_by_commit.
|
|
61
|
+
expect(calculator.counted_line_changes_by_file_by_commit).to match_array([["lib/turbulence.rb", 16], ["lib/eddies.rb", 19]])
|
|
67
62
|
end
|
|
68
63
|
end
|
|
69
|
-
|
|
64
|
+
|
|
70
65
|
describe "::changes_by_ruby_file" do
|
|
71
66
|
before do
|
|
72
|
-
calculator.
|
|
73
|
-
[
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
]
|
|
82
|
-
}
|
|
67
|
+
allow(calculator).to receive(:ruby_files_changed_in_scm).and_return([
|
|
68
|
+
['lib/eddies.rb', 4],
|
|
69
|
+
['lib/turbulence.rb', 5],
|
|
70
|
+
['lib/turbulence.rb', 16],
|
|
71
|
+
['lib/eddies.rb', 2],
|
|
72
|
+
['lib/turbulence.rb', 7],
|
|
73
|
+
['lib/eddies.rb', 19],
|
|
74
|
+
['lib/eddies.rb', 28]
|
|
75
|
+
])
|
|
83
76
|
end
|
|
84
|
-
|
|
77
|
+
|
|
85
78
|
it "groups and sums churns, excluding the last" do
|
|
86
79
|
calculator.compute_mean = false
|
|
87
|
-
calculator.changes_by_ruby_file.
|
|
80
|
+
expect(calculator.changes_by_ruby_file).to match_array([['lib/eddies.rb', 25], ['lib/turbulence.rb', 21]])
|
|
88
81
|
end
|
|
89
82
|
|
|
90
83
|
it "interprets a single entry as zero churn" do
|
|
91
|
-
calculator.
|
|
92
|
-
[
|
|
93
|
-
|
|
94
|
-
]
|
|
95
|
-
}
|
|
84
|
+
allow(calculator).to receive(:ruby_files_changed_in_scm).and_return([
|
|
85
|
+
['lib/eddies.rb', 4],
|
|
86
|
+
])
|
|
96
87
|
calculator.compute_mean = false
|
|
97
|
-
calculator.changes_by_ruby_file.
|
|
88
|
+
expect(calculator.changes_by_ruby_file).to match_array([['lib/eddies.rb', 0]])
|
|
98
89
|
end
|
|
99
|
-
|
|
90
|
+
|
|
100
91
|
it "groups and takes the mean of churns, excluding the last" do
|
|
101
92
|
calculator.compute_mean = true
|
|
102
|
-
calculator.changes_by_ruby_file.
|
|
93
|
+
expect(calculator.changes_by_ruby_file).to match_array([['lib/eddies.rb', 8], ['lib/turbulence.rb', 10]])
|
|
103
94
|
calculator.compute_mean = false
|
|
104
95
|
end
|
|
105
96
|
end
|
|
106
97
|
|
|
107
98
|
describe "::calculate_mean_of_churn" do
|
|
108
99
|
it "handles zero sample size" do
|
|
109
|
-
calculator.calculate_mean_of_churn(8,0).
|
|
100
|
+
expect(calculator.calculate_mean_of_churn(8, 0)).to eq 8
|
|
110
101
|
end
|
|
111
102
|
|
|
112
|
-
it "returns original churn for sample size = 1"
|
|
113
|
-
calculator.calculate_mean_of_churn(8,1).
|
|
103
|
+
it "returns original churn for sample size = 1" do
|
|
104
|
+
expect(calculator.calculate_mean_of_churn(8, 1)).to eq 8
|
|
114
105
|
end
|
|
115
106
|
|
|
116
107
|
it "returns churn divided by sample size" do
|
|
117
|
-
calculator.calculate_mean_of_churn(25,3).
|
|
108
|
+
expect(calculator.calculate_mean_of_churn(25, 3)).to eq 8
|
|
118
109
|
end
|
|
119
|
-
|
|
120
110
|
end
|
|
121
111
|
|
|
122
112
|
context "Full stack tests" do
|
|
123
113
|
context "when one ruby file is given" do
|
|
124
114
|
context "with two log entries for file" do
|
|
125
115
|
before do
|
|
126
|
-
calculator.
|
|
116
|
+
allow(calculator).to receive(:scm_log_command).and_return(
|
|
127
117
|
"\n\n\n\n10\t6\tlib/turbulence.rb\n" +
|
|
128
|
-
|
|
129
|
-
|
|
118
|
+
"\n\n\n\n11\t7\tlib/turbulence.rb\n"
|
|
119
|
+
)
|
|
130
120
|
end
|
|
131
121
|
it "gives the line change count for the file" do
|
|
132
122
|
yielded_files = []
|
|
133
123
|
calculator.for_these_files(["lib/turbulence.rb"]) do |filename, score|
|
|
134
124
|
yielded_files << [filename, score]
|
|
135
125
|
end
|
|
136
|
-
yielded_files.
|
|
126
|
+
expect(yielded_files).to match_array([["lib/turbulence.rb", 16]])
|
|
137
127
|
end
|
|
138
128
|
context "with only one log entry for file" do
|
|
139
129
|
before do
|
|
140
|
-
calculator.
|
|
130
|
+
allow(calculator).to receive(:scm_log_command).and_return(
|
|
141
131
|
"\n\n\n\n10\t6\tlib/turbulence.rb\n"
|
|
142
|
-
|
|
132
|
+
)
|
|
143
133
|
end
|
|
144
134
|
it "shows zero churn for the file" do
|
|
145
135
|
yielded_files = []
|
|
146
136
|
calculator.for_these_files(["lib/turbulence.rb"]) do |filename, score|
|
|
147
137
|
yielded_files << [filename, score]
|
|
148
138
|
end
|
|
149
|
-
yielded_files.
|
|
139
|
+
expect(yielded_files).to match_array([["lib/turbulence.rb", 0]])
|
|
150
140
|
end
|
|
151
141
|
end
|
|
152
142
|
end
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
require 'turbulence/calculators/complexity'
|
|
2
2
|
|
|
3
3
|
describe Turbulence::Calculators::Complexity do
|
|
4
|
-
let(:calculator) { Turbulence::Calculators::Complexity }
|
|
4
|
+
let(:calculator) { Turbulence::Calculators::Complexity.new(config) }
|
|
5
|
+
let(:config) { Turbulence::Configuration.new }
|
|
6
|
+
|
|
5
7
|
describe "::for_these_files" do
|
|
6
8
|
it "yields up the filename and score for each file" do
|
|
7
9
|
files = ["lib/corey.rb", "lib/chad.rb"]
|
|
8
|
-
calculator.
|
|
9
|
-
filename.size
|
|
10
|
-
}
|
|
10
|
+
allow(calculator).to receive(:score_for_file) { |filename| filename.size }
|
|
11
11
|
yielded_files = []
|
|
12
12
|
calculator.for_these_files(files) do |filename, score|
|
|
13
13
|
yielded_files << [filename, score]
|
|
14
14
|
end
|
|
15
|
-
yielded_files.
|
|
16
|
-
["lib/chad.rb",11]]
|
|
15
|
+
expect(yielded_files).to match_array([["lib/corey.rb", 12], ["lib/chad.rb", 11]])
|
|
17
16
|
end
|
|
18
17
|
end
|
|
19
18
|
end
|