cane 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +12 -0
- data/README.md +9 -25
- data/lib/cane.rb +7 -18
- data/lib/cane/abc_check.rb +85 -22
- data/lib/cane/cli.rb +3 -5
- data/lib/cane/cli/spec.rb +59 -53
- data/lib/cane/doc_check.rb +30 -16
- data/lib/cane/encoding_aware_iterator.rb +35 -0
- data/lib/cane/file.rb +22 -0
- data/lib/cane/rake_task.rb +12 -41
- data/lib/cane/style_check.rb +36 -8
- data/lib/cane/threshold_check.rb +47 -22
- data/lib/cane/version.rb +1 -1
- data/lib/cane/violation_formatter.rb +33 -25
- data/spec/abc_check_spec.rb +53 -21
- data/spec/cane_spec.rb +21 -12
- data/spec/doc_check_spec.rb +30 -9
- data/spec/encoding_aware_iterator_spec.rb +24 -0
- data/spec/rake_task_spec.rb +13 -0
- data/spec/style_check_spec.rb +10 -4
- data/spec/threshold_check_spec.rb +4 -3
- data/spec/violation_formatter_spec.rb +4 -5
- metadata +8 -7
- data/lib/cane/abc_max_violation.rb +0 -15
- data/lib/cane/cli/translator.rb +0 -71
- data/lib/cane/style_violation.rb +0 -10
- data/lib/cane/syntax_violation.rb +0 -20
- data/lib/cane/threshold_violation.rb +0 -15
data/spec/abc_check_spec.rb
CHANGED
@@ -3,6 +3,10 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/abc_check'
|
4
4
|
|
5
5
|
describe Cane::AbcCheck do
|
6
|
+
def check(file_name, opts = {})
|
7
|
+
described_class.new(opts.merge(abc_glob: file_name))
|
8
|
+
end
|
9
|
+
|
6
10
|
it 'creates an AbcMaxViolation for each method above the threshold' do
|
7
11
|
file_name = make_file(<<-RUBY)
|
8
12
|
class Harness
|
@@ -17,10 +21,10 @@ describe Cane::AbcCheck do
|
|
17
21
|
end
|
18
22
|
RUBY
|
19
23
|
|
20
|
-
violations =
|
24
|
+
violations = check(file_name, abc_max: 1).violations
|
21
25
|
violations.length.should == 1
|
22
|
-
violations[0].should
|
23
|
-
|
26
|
+
violations[0].values_at(:file, :label, :value).should ==
|
27
|
+
[file_name, "Harness#complex_method", 2]
|
24
28
|
end
|
25
29
|
|
26
30
|
it 'sorts violations by complexity' do
|
@@ -37,22 +41,21 @@ describe Cane::AbcCheck do
|
|
37
41
|
end
|
38
42
|
RUBY
|
39
43
|
|
40
|
-
violations =
|
44
|
+
violations = check(file_name, abc_max: 0).violations
|
41
45
|
violations.length.should == 2
|
42
|
-
complexities = violations.map
|
46
|
+
complexities = violations.map {|x| x[:value] }
|
43
47
|
complexities.should == complexities.sort.reverse
|
44
48
|
end
|
45
49
|
|
46
|
-
it 'creates a
|
50
|
+
it 'creates a violation when code cannot be parsed' do
|
47
51
|
file_name = make_file(<<-RUBY)
|
48
52
|
class Harness
|
49
53
|
RUBY
|
50
54
|
|
51
|
-
violations =
|
55
|
+
violations = check(file_name).violations
|
52
56
|
violations.length.should == 1
|
53
|
-
violations[0].should
|
54
|
-
violations[0].
|
55
|
-
violations[0].description.should be_instance_of(String)
|
57
|
+
violations[0][:file].should == file_name
|
58
|
+
violations[0][:description].should be_instance_of(String)
|
56
59
|
end
|
57
60
|
|
58
61
|
it 'skips declared exclusions' do
|
@@ -84,33 +87,62 @@ describe Cane::AbcCheck do
|
|
84
87
|
|
85
88
|
exclusions = %w[ Harness#instance_meth Harness.class_meth
|
86
89
|
Harness::Nested#i_meth Harness::Nested.c_meth ]
|
87
|
-
violations =
|
88
|
-
|
90
|
+
violations = check(file_name,
|
91
|
+
abc_max: 0,
|
92
|
+
abc_exclude: exclusions
|
93
|
+
).violations
|
89
94
|
violations.length.should == 1
|
90
|
-
violations[0].should
|
91
|
-
|
92
|
-
|
95
|
+
violations[0].values_at(:file, :label, :value).should ==
|
96
|
+
[file_name, "Harness::Nested#other_meth", 1]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "creates an AbcMaxViolation for method in assigned anonymous class" do
|
100
|
+
file_name = make_file(<<-RUBY)
|
101
|
+
MyClass = Struct.new(:foo) do
|
102
|
+
def test_method(a)
|
103
|
+
b = a
|
104
|
+
return b if b > 3
|
105
|
+
end
|
106
|
+
end
|
107
|
+
RUBY
|
108
|
+
|
109
|
+
violations = check(file_name, abc_max: 1).violations
|
110
|
+
violations[0][:label] == "MyClass#test_method"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "creates an AbcMaxViolation for method in anonymous class" do
|
114
|
+
file_name = make_file(<<-RUBY)
|
115
|
+
Class.new do
|
116
|
+
def test_method(a)
|
117
|
+
b = a
|
118
|
+
return b if b > 3
|
119
|
+
end
|
120
|
+
end
|
121
|
+
RUBY
|
122
|
+
|
123
|
+
violations = check(file_name, abc_max: 1).violations
|
124
|
+
violations[0][:label].should == "(anon)#test_method"
|
93
125
|
end
|
94
126
|
|
95
|
-
def self.it_should_extract_method_name(
|
96
|
-
it "creates an AbcMaxViolation for #{
|
127
|
+
def self.it_should_extract_method_name(name, label=name, sep='#')
|
128
|
+
it "creates an AbcMaxViolation for #{name}" do
|
97
129
|
file_name = make_file(<<-RUBY)
|
98
130
|
class Harness
|
99
|
-
def #{
|
131
|
+
def #{name}(a)
|
100
132
|
b = a
|
101
133
|
return b if b > 3
|
102
134
|
end
|
103
135
|
end
|
104
136
|
RUBY
|
105
137
|
|
106
|
-
violations =
|
107
|
-
violations[0].
|
138
|
+
violations = check(file_name, abc_max: 1).violations
|
139
|
+
violations[0][:label].should == "Harness#{sep}#{label}"
|
108
140
|
end
|
109
141
|
end
|
110
142
|
|
111
143
|
# These method names all create different ASTs. Which is weird.
|
112
144
|
it_should_extract_method_name 'a'
|
113
|
-
it_should_extract_method_name 'self.a', 'a'
|
145
|
+
it_should_extract_method_name 'self.a', 'a', '.'
|
114
146
|
it_should_extract_method_name 'next'
|
115
147
|
it_should_extract_method_name 'GET'
|
116
148
|
it_should_extract_method_name '`'
|
data/spec/cane_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe 'Cane' do
|
|
14
14
|
def run(cli_args)
|
15
15
|
result = nil
|
16
16
|
output = capture_stdout do
|
17
|
-
result = Cane::CLI.run(cli_args.split(' '))
|
17
|
+
result = Cane::CLI.run(['--no-abc'] + cli_args.split(' '))
|
18
18
|
end
|
19
19
|
|
20
20
|
[output, result ? 0 : 1]
|
@@ -42,8 +42,8 @@ describe 'Cane' do
|
|
42
42
|
file_name = make_file("whitespace ")
|
43
43
|
|
44
44
|
output, exitstatus = run("--style-glob #{file_name}")
|
45
|
-
exitstatus.should == 1
|
46
45
|
output.should include("Lines violated style requirements")
|
46
|
+
exitstatus.should == 1
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'allows measure to be configured' do
|
@@ -59,7 +59,7 @@ describe 'Cane' do
|
|
59
59
|
|
60
60
|
output, exitstatus = run("--style-glob #{file_name} --style-measure 80")
|
61
61
|
exitstatus.should == 0
|
62
|
-
output.should
|
62
|
+
output.should == ""
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'allows upper bound of failed checks' do
|
@@ -74,8 +74,8 @@ describe 'Cane' do
|
|
74
74
|
file_name = make_file("89")
|
75
75
|
|
76
76
|
output, exitstatus = run("--gte #{file_name},90")
|
77
|
-
exitstatus.should == 1
|
78
77
|
output.should include("Quality threshold crossed")
|
78
|
+
exitstatus.should == 1
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'allows checking of class documentation' do
|
@@ -143,21 +143,30 @@ describe 'Cane' do
|
|
143
143
|
end
|
144
144
|
RUBY
|
145
145
|
|
146
|
-
exclusions_file = make_file(<<-YAML)
|
147
|
-
abc:
|
148
|
-
- Harness#complex_method
|
149
|
-
style:
|
150
|
-
- #{file_name}
|
151
|
-
YAML
|
152
|
-
|
153
146
|
options = [
|
154
147
|
"--abc-glob", file_name,
|
148
|
+
"--abc-exclude", "Harness#complex_method",
|
155
149
|
"--abc-max", 1,
|
156
150
|
"--style-glob", file_name,
|
157
|
-
"--
|
151
|
+
"--style-exclude", file_name
|
158
152
|
].join(' ')
|
159
153
|
|
160
154
|
_, exitstatus = run(options)
|
161
155
|
exitstatus.should == 0
|
162
156
|
end
|
157
|
+
|
158
|
+
it 'handles invalid unicode input' do
|
159
|
+
fn = make_file("\xc3\x28")
|
160
|
+
|
161
|
+
_, exitstatus = run("--style-glob #{fn} --abc-glob #{fn} --doc-glob #{fn}")
|
162
|
+
|
163
|
+
exitstatus.should == 0
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'handles invalid options by showing help' do
|
167
|
+
out, exitstatus = run("--bogus")
|
168
|
+
|
169
|
+
out.should include("Usage:")
|
170
|
+
exitstatus.should == 1
|
171
|
+
end
|
163
172
|
end
|
data/spec/doc_check_spec.rb
CHANGED
@@ -3,6 +3,10 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/doc_check'
|
4
4
|
|
5
5
|
describe Cane::DocCheck do
|
6
|
+
def check(file_name, opts = {})
|
7
|
+
described_class.new(opts.merge(doc_glob: file_name))
|
8
|
+
end
|
9
|
+
|
6
10
|
it 'creates a DocViolation for each undocumented class' do
|
7
11
|
file_name = make_file <<-RUBY
|
8
12
|
# This class is documented
|
@@ -16,17 +20,34 @@ class Meta
|
|
16
20
|
end
|
17
21
|
RUBY
|
18
22
|
|
19
|
-
violations =
|
23
|
+
violations = check(file_name).violations
|
20
24
|
violations.length.should == 2
|
21
25
|
|
22
|
-
violations[0].should
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
violations[0].values_at(:file, :line, :label).should == [
|
27
|
+
file_name, 3, "NoDoc"
|
28
|
+
]
|
29
|
+
|
30
|
+
violations[1].values_at(:file, :line, :label).should == [
|
31
|
+
file_name, 4, "AlsoNoDoc"
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'ignores magic encoding comments' do
|
36
|
+
file_name = make_file <<-RUBY
|
37
|
+
# coding = utf-8
|
38
|
+
class NoDoc; end
|
39
|
+
# -*- encoding : utf-8 -*-
|
40
|
+
class AlsoNoDoc; end
|
41
|
+
RUBY
|
42
|
+
|
43
|
+
violations = check(file_name).violations
|
44
|
+
violations.length.should == 2
|
26
45
|
|
27
|
-
violations[
|
28
|
-
|
29
|
-
|
30
|
-
violations[1].
|
46
|
+
violations[0].values_at(:file, :line, :label).should == [
|
47
|
+
file_name, 2, "NoDoc"
|
48
|
+
]
|
49
|
+
violations[1].values_at(:file, :line, :label).should == [
|
50
|
+
file_name, 4, "AlsoNoDoc"
|
51
|
+
]
|
31
52
|
end
|
32
53
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
require 'cane/encoding_aware_iterator'
|
5
|
+
|
6
|
+
# Example bad input from:
|
7
|
+
# http://stackoverflow.com/questions/1301402/example-invalid-utf8-string
|
8
|
+
describe Cane::EncodingAwareIterator do
|
9
|
+
it 'handles non-UTF8 input' do
|
10
|
+
lines = ["\xc3\x28"]
|
11
|
+
result = described_class.new(lines).map_with_index do |line, number|
|
12
|
+
[line =~ /\s/, number]
|
13
|
+
end
|
14
|
+
result.should == [[nil, 0]]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not enter an infinite loop on persistently bad input' do
|
18
|
+
->{
|
19
|
+
described_class.new([""]).map_with_index do |line, number|
|
20
|
+
"\xc3\x28" =~ /\s/
|
21
|
+
end
|
22
|
+
}.should raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'cane/rake_task'
|
4
|
+
|
5
|
+
describe Cane::RakeTask do
|
6
|
+
it 'adds a new threshold' do
|
7
|
+
task = described_class.new do |t|
|
8
|
+
t.add_threshold 'coverage', :>=, 99
|
9
|
+
end
|
10
|
+
|
11
|
+
task.options[:gte].should == [["coverage", 99]]
|
12
|
+
end
|
13
|
+
end
|
data/spec/style_check_spec.rb
CHANGED
@@ -3,6 +3,10 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/style_check'
|
4
4
|
|
5
5
|
describe Cane::StyleCheck do
|
6
|
+
def check(file_name, opts = {})
|
7
|
+
described_class.new(opts.merge(style_glob: file_name))
|
8
|
+
end
|
9
|
+
|
6
10
|
let(:ruby_with_style_issue) do
|
7
11
|
[
|
8
12
|
"def test ",
|
@@ -14,16 +18,18 @@ describe Cane::StyleCheck do
|
|
14
18
|
it 'creates a StyleViolation for each method above the threshold' do
|
15
19
|
file_name = make_file(ruby_with_style_issue)
|
16
20
|
|
17
|
-
violations =
|
21
|
+
violations = check(file_name, style_measure: 80).violations
|
18
22
|
violations.length.should == 2
|
19
|
-
violations[0].should be_instance_of(StyleViolation)
|
20
23
|
end
|
21
24
|
|
22
25
|
it 'skips declared exclusions' do
|
23
26
|
file_name = make_file(ruby_with_style_issue)
|
24
27
|
|
25
|
-
violations =
|
26
|
-
|
28
|
+
violations = check(file_name,
|
29
|
+
style_measure: 80,
|
30
|
+
style_exclude: [file_name]
|
31
|
+
).violations
|
32
|
+
|
27
33
|
violations.length.should == 0
|
28
34
|
end
|
29
35
|
end
|
@@ -2,11 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
require 'cane/threshold_check'
|
4
4
|
|
5
|
-
describe ThresholdCheck do
|
5
|
+
describe Cane::ThresholdCheck do
|
6
6
|
it 'returns a value of unavailable when file cannot be read' do
|
7
|
-
check = ThresholdCheck.new([[
|
7
|
+
check = Cane::ThresholdCheck.new(gte: [['bogus_file', 20]])
|
8
8
|
violations = check.violations
|
9
9
|
violations.length.should == 1
|
10
|
-
violations[0].
|
10
|
+
violations[0][:label].should ==
|
11
|
+
'bogus_file is unavailable, should be >= 20'
|
11
12
|
end
|
12
13
|
end
|
@@ -4,10 +4,9 @@ require 'cane/violation_formatter'
|
|
4
4
|
|
5
5
|
describe Cane::ViolationFormatter do
|
6
6
|
def violation(description)
|
7
|
-
|
8
|
-
description: description
|
9
|
-
|
10
|
-
)
|
7
|
+
{
|
8
|
+
description: description
|
9
|
+
}
|
11
10
|
end
|
12
11
|
|
13
12
|
it 'includes number of violations in the group header' do
|
@@ -15,7 +14,7 @@ describe Cane::ViolationFormatter do
|
|
15
14
|
end
|
16
15
|
|
17
16
|
it 'includes total number of violations' do
|
18
|
-
violations = [violation("FAIL1"),violation("FAIL2")]
|
17
|
+
violations = [violation("FAIL1"), violation("FAIL2")]
|
19
18
|
result = described_class.new(violations).to_s
|
20
19
|
result.should include("Total Violations: 2")
|
21
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cane
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -70,22 +70,21 @@ files:
|
|
70
70
|
- spec/abc_check_spec.rb
|
71
71
|
- spec/cane_spec.rb
|
72
72
|
- spec/doc_check_spec.rb
|
73
|
+
- spec/encoding_aware_iterator_spec.rb
|
74
|
+
- spec/rake_task_spec.rb
|
73
75
|
- spec/spec_helper.rb
|
74
76
|
- spec/style_check_spec.rb
|
75
77
|
- spec/threshold_check_spec.rb
|
76
78
|
- spec/violation_formatter_spec.rb
|
77
79
|
- lib/cane/abc_check.rb
|
78
|
-
- lib/cane/abc_max_violation.rb
|
79
80
|
- lib/cane/cli/spec.rb
|
80
|
-
- lib/cane/cli/translator.rb
|
81
81
|
- lib/cane/cli.rb
|
82
82
|
- lib/cane/doc_check.rb
|
83
|
+
- lib/cane/encoding_aware_iterator.rb
|
84
|
+
- lib/cane/file.rb
|
83
85
|
- lib/cane/rake_task.rb
|
84
86
|
- lib/cane/style_check.rb
|
85
|
-
- lib/cane/style_violation.rb
|
86
|
-
- lib/cane/syntax_violation.rb
|
87
87
|
- lib/cane/threshold_check.rb
|
88
|
-
- lib/cane/threshold_violation.rb
|
89
88
|
- lib/cane/version.rb
|
90
89
|
- lib/cane/violation_formatter.rb
|
91
90
|
- lib/cane.rb
|
@@ -123,6 +122,8 @@ test_files:
|
|
123
122
|
- spec/abc_check_spec.rb
|
124
123
|
- spec/cane_spec.rb
|
125
124
|
- spec/doc_check_spec.rb
|
125
|
+
- spec/encoding_aware_iterator_spec.rb
|
126
|
+
- spec/rake_task_spec.rb
|
126
127
|
- spec/spec_helper.rb
|
127
128
|
- spec/style_check_spec.rb
|
128
129
|
- spec/threshold_check_spec.rb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Cane
|
2
|
-
|
3
|
-
# Value object used by AbcCheck for a method that is too complicated.
|
4
|
-
class AbcMaxViolation < Struct.new(:file_name, :detail, :complexity)
|
5
|
-
def columns
|
6
|
-
[file_name, detail, complexity]
|
7
|
-
end
|
8
|
-
|
9
|
-
def description
|
10
|
-
"Methods exceeded maximum allowed ABC complexity"
|
11
|
-
end
|
12
|
-
|
13
|
-
alias_method :sort_index, :complexity
|
14
|
-
end
|
15
|
-
end
|
data/lib/cane/cli/translator.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
module Cane
|
4
|
-
module CLI
|
5
|
-
|
6
|
-
# Translates CLI options with given defaults to a hash suitable to be
|
7
|
-
# passed to `Cane.run`.
|
8
|
-
class Translator < Struct.new(:options, :defaults)
|
9
|
-
def to_hash
|
10
|
-
result = {}
|
11
|
-
translate_abc_options(result)
|
12
|
-
translate_doc_options(result)
|
13
|
-
translate_style_options(result)
|
14
|
-
|
15
|
-
result[:threshold] = options.fetch(:threshold, [])
|
16
|
-
result[:max_violations] = option_with_default(:max_violations).to_i
|
17
|
-
|
18
|
-
result
|
19
|
-
end
|
20
|
-
|
21
|
-
def translate_abc_options(result)
|
22
|
-
result[:abc] = {
|
23
|
-
files: option_with_default(:abc_glob),
|
24
|
-
max: option_with_default(:abc_max).to_i,
|
25
|
-
exclusions: exclusions_for('abc')
|
26
|
-
} unless check_disabled(:no_abc, [:abc_glob, :abc_max])
|
27
|
-
end
|
28
|
-
|
29
|
-
def translate_style_options(result)
|
30
|
-
result[:style] = {
|
31
|
-
files: option_with_default(:style_glob),
|
32
|
-
measure: option_with_default(:style_measure).to_i,
|
33
|
-
exclusions: exclusions_for('style')
|
34
|
-
} unless check_disabled(:no_style, [:style_glob])
|
35
|
-
end
|
36
|
-
|
37
|
-
def translate_doc_options(result)
|
38
|
-
result[:doc] = {
|
39
|
-
files: option_with_default(:doc_glob),
|
40
|
-
} unless check_disabled(:no_doc, [:doc_glob])
|
41
|
-
end
|
42
|
-
|
43
|
-
def check_disabled(check, params)
|
44
|
-
relevant_options = options.keys & params + [check]
|
45
|
-
|
46
|
-
check == relevant_options[-1]
|
47
|
-
end
|
48
|
-
|
49
|
-
def option_with_default(key)
|
50
|
-
options.fetch(key, defaults.fetch(key))
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def exclusions_for(tool)
|
56
|
-
Array(exclusions[tool])
|
57
|
-
end
|
58
|
-
|
59
|
-
def exclusions
|
60
|
-
@exclusions ||= begin
|
61
|
-
if file = options[:exclusions_file]
|
62
|
-
YAML.load_file(file)
|
63
|
-
else
|
64
|
-
{}
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|