cane 1.4.0 → 2.0.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.
@@ -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 = described_class.new(files: file_name, max: 1).violations
24
+ violations = check(file_name, abc_max: 1).violations
21
25
  violations.length.should == 1
22
- violations[0].should be_instance_of(Cane::AbcMaxViolation)
23
- violations[0].columns.should == [file_name, "Harness > complex_method", 2]
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 = described_class.new(files: file_name, max: 0).violations
44
+ violations = check(file_name, abc_max: 0).violations
41
45
  violations.length.should == 2
42
- complexities = violations.map(&:complexity)
46
+ complexities = violations.map {|x| x[:value] }
43
47
  complexities.should == complexities.sort.reverse
44
48
  end
45
49
 
46
- it 'creates a SyntaxViolation when code cannot be parsed' do
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 = described_class.new(files: file_name).violations
55
+ violations = check(file_name).violations
52
56
  violations.length.should == 1
53
- violations[0].should be_instance_of(Cane::SyntaxViolation)
54
- violations[0].columns.should == [file_name]
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 = described_class.new(files: file_name, max: 0,
88
- exclusions: exclusions).violations
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 be_instance_of(Cane::AbcMaxViolation)
91
- columns = violations[0].columns
92
- columns.should == [file_name, "Harness > Nested > other_meth", 1]
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(method_name, label=method_name)
96
- it "creates an AbcMaxViolation for #{method_name}" do
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 #{method_name}(a)
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 = described_class.new(files: file_name, max: 1).violations
107
- violations[0].detail.should == "Harness > #{label}"
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 '`'
@@ -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 be_empty
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
- "--exclusions-file", exclusions_file
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
@@ -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 = described_class.new(files: file_name).violations
23
+ violations = check(file_name).violations
20
24
  violations.length.should == 2
21
25
 
22
- violations[0].should be_instance_of(Cane::UndocumentedClassViolation)
23
- violations[0].file_name.should == file_name
24
- violations[0].number.should == 3
25
- violations[0].columns.last.should eq("NoDoc")
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[1].should be_instance_of(Cane::UndocumentedClassViolation)
28
- violations[1].file_name.should == file_name
29
- violations[1].number.should == 4
30
- violations[1].columns.last.should eq("AlsoNoDoc")
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
@@ -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 = Cane::StyleCheck.new(files: file_name, measure: 80).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 = Cane::StyleCheck.new(files: file_name, measure: 80,
26
- exclusions: [file_name]).violations
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([[:>=, 'bogus_file', 20]])
7
+ check = Cane::ThresholdCheck.new(gte: [['bogus_file', 20]])
8
8
  violations = check.violations
9
9
  violations.length.should == 1
10
- violations[0].to_s.should include("unavailable")
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
- stub("violation",
8
- description: description,
9
- columns: []
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: 1.4.0
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-07-02 00:00:00.000000000 Z
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
@@ -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