sparqcode_cane 1.3.0.1

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,3 @@
1
+ module Cane
2
+ VERSION = '1.3.0.1'
3
+ end
@@ -0,0 +1,49 @@
1
+ require 'stringio'
2
+
3
+ module Cane
4
+
5
+ # Computes a string to be displayed as output from an array of violations
6
+ # computed by the checks.
7
+ class ViolationFormatter < Struct.new(:violations)
8
+ def to_s
9
+ return '' if violations.empty?
10
+
11
+ grouped_violations.map do |description, violations|
12
+ format_group_header(description, violations) +
13
+ format_violations(violations)
14
+ end.flatten.join("\n") + "\n\n"
15
+ end
16
+
17
+ protected
18
+
19
+ def format_group_header(description, violations)
20
+ ["", "%s (%i):" % [description, violations.length], ""]
21
+ end
22
+
23
+ def format_violations(violations)
24
+ column_widths = calculate_columm_widths(violations)
25
+
26
+ violations.map do |violation|
27
+ format_violation(violation, column_widths)
28
+ end
29
+ end
30
+
31
+ def format_violation(violation, column_widths)
32
+ [
33
+ ' ' + violation.columns.map.with_index { |column, index|
34
+ "%-#{column_widths[index]}s" % column
35
+ }.join(' ')
36
+ ]
37
+ end
38
+
39
+ def calculate_columm_widths(violations)
40
+ violations.map { |violation|
41
+ violation.columns.map { |x| x.to_s.length }
42
+ }.transpose.map(&:max)
43
+ end
44
+
45
+ def grouped_violations
46
+ violations.group_by(&:description)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ require 'cane/abc_check'
4
+
5
+ describe Cane::AbcCheck do
6
+ it 'creates an AbcMaxViolation for each method above the threshold' do
7
+ file_name = make_file(<<-RUBY)
8
+ class Harness
9
+ def not_complex
10
+ true
11
+ end
12
+
13
+ def complex_method(a)
14
+ b = a
15
+ return b if b > 3
16
+ end
17
+ end
18
+ RUBY
19
+
20
+ violations = described_class.new(files: file_name, max: 1).violations
21
+ violations.length.should == 1
22
+ violations[0].should be_instance_of(Cane::AbcMaxViolation)
23
+ violations[0].columns.should == [file_name, "Harness > complex_method", 2]
24
+ end
25
+
26
+ it 'sorts violations by complexity' do
27
+ file_name = make_file(<<-RUBY)
28
+ class Harness
29
+ def not_complex
30
+ true
31
+ end
32
+
33
+ def complex_method(a)
34
+ b = a
35
+ return b if b > 3
36
+ end
37
+ end
38
+ RUBY
39
+
40
+ violations = described_class.new(files: file_name, max: 0).violations
41
+ violations.length.should == 2
42
+ complexities = violations.map(&:complexity)
43
+ complexities.should == complexities.sort.reverse
44
+ end
45
+
46
+ it 'creates a SyntaxViolation when code cannot be parsed' do
47
+ file_name = make_file(<<-RUBY)
48
+ class Harness
49
+ RUBY
50
+
51
+ violations = described_class.new(files: file_name).violations
52
+ 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)
56
+ end
57
+
58
+ def self.it_should_extract_method_name(method_name, label=method_name)
59
+ it "creates an AbcMaxViolation for #{method_name}" do
60
+ file_name = make_file(<<-RUBY)
61
+ class Harness
62
+ def #{method_name}(a)
63
+ b = a
64
+ return b if b > 3
65
+ end
66
+ end
67
+ RUBY
68
+
69
+ violations = described_class.new(files: file_name, max: 1).violations
70
+ violations[0].detail.should == "Harness > #{label}"
71
+ end
72
+ end
73
+
74
+ # These method names all create different ASTs. Which is weird.
75
+ it_should_extract_method_name 'a'
76
+ it_should_extract_method_name 'self.a', 'a'
77
+ it_should_extract_method_name 'next'
78
+ it_should_extract_method_name 'GET'
79
+ it_should_extract_method_name '`'
80
+ it_should_extract_method_name '>='
81
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+ require "stringio"
3
+ require 'cane/cli'
4
+
5
+ describe 'Cane' do
6
+ def capture_stdout &block
7
+ real_stdout, $stdout = $stdout, StringIO.new
8
+ yield
9
+ $stdout.string
10
+ ensure
11
+ $stdout = real_stdout
12
+ end
13
+
14
+ def run(cli_args)
15
+ result = nil
16
+ output = capture_stdout do
17
+ result = Cane::CLI.run(cli_args.split(' '))
18
+ end
19
+
20
+ [output, result ? 0 : 1]
21
+ end
22
+
23
+ it 'fails if ABC metric does not meet requirements' do
24
+ file_name = make_file(<<-RUBY)
25
+ class Harness
26
+ def complex_method(a)
27
+ if a < 2
28
+ return "low"
29
+ else
30
+ return "high"
31
+ end
32
+ end
33
+ end
34
+ RUBY
35
+
36
+ _, exitstatus = run("--abc-glob #{file_name} --abc-max 1")
37
+
38
+ exitstatus.should == 1
39
+ end
40
+
41
+ it 'fails if style metrics do not meet requirements' do
42
+ file_name = make_file("whitespace ")
43
+
44
+ output, exitstatus = run("--style-glob #{file_name}")
45
+ exitstatus.should == 1
46
+ output.should include("Lines violated style requirements")
47
+ end
48
+
49
+ it 'allows measure to be configured' do
50
+ file_name = make_file("toolong")
51
+
52
+ output, exitstatus = run("--style-glob #{file_name} --style-measure 3")
53
+ exitstatus.should == 1
54
+ output.should include("Lines violated style requirements")
55
+ end
56
+
57
+ it 'does not include trailing new lines in the character count' do
58
+ file_name = make_file('#' * 80 + "\n" + '#' * 80)
59
+
60
+ output, exitstatus = run("--style-glob #{file_name} --style-measure 80")
61
+ exitstatus.should == 0
62
+ output.should be_empty
63
+ end
64
+
65
+ it 'allows upper bound of failed checks' do
66
+ file_name = make_file("whitespace ")
67
+
68
+ output, exitstatus = run("--style-glob #{file_name} --max-violations 1")
69
+ exitstatus.should == 0
70
+ output.should include("Lines violated style requirements")
71
+ end
72
+
73
+ it 'allows checking of a value in a file' do
74
+ file_name = make_file("89")
75
+
76
+ output, exitstatus = run("--gte #{file_name},90")
77
+ exitstatus.should == 1
78
+ output.should include("Quality threshold crossed")
79
+ end
80
+
81
+ it 'allows checking of class documentation' do
82
+ file_name = make_file("class NoDoc")
83
+
84
+ output, exitstatus = run("--doc-glob #{file_name}")
85
+ exitstatus.should == 1
86
+ output.should include("Classes are not documented")
87
+ end
88
+
89
+ context 'with a .cane file' do
90
+ before(:each) do
91
+ file_name = make_file("class NoDoc")
92
+ make_dot_cane("--doc-glob #{file_name}")
93
+ end
94
+
95
+ after(:each) do
96
+ unmake_dot_cane
97
+ end
98
+
99
+ it 'loads options from a .cane file' do
100
+ output, exitstatus = run('')
101
+
102
+ exitstatus.should == 1
103
+ output.should include("Classes are not documented")
104
+ end
105
+ end
106
+
107
+ it 'displays a help message' do
108
+ output, exitstatus = run("--help")
109
+
110
+ exitstatus.should == 0
111
+ output.should include("Usage:")
112
+ end
113
+
114
+ it 'displays version' do
115
+ output, exitstatus = run("--version")
116
+
117
+ exitstatus.should == 0
118
+ output.should include(Cane::VERSION)
119
+ end
120
+
121
+ it 'uses the last of conflicting arguments' do
122
+ file_name = make_file("class NoDoc")
123
+
124
+ run("--doc-glob #{file_name} --no-doc").should ==
125
+ run("--no-doc")
126
+
127
+ run("--no-doc --doc-glob #{file_name}").should ==
128
+ run("--doc-glob #{file_name}")
129
+ end
130
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ require 'cane/doc_check'
4
+
5
+ describe Cane::DocCheck do
6
+ it 'creates a DocViolation for each undocumented class' do
7
+ file_name = make_file <<-RUBY
8
+ # This class is documented
9
+ class Doc; end
10
+ class NoDoc; end # No doc
11
+ class AlsoNoDoc; end
12
+ [:class]
13
+ # class Ignore
14
+ class Meta
15
+ class << self; end
16
+ end
17
+ RUBY
18
+
19
+ violations = described_class.new(files: file_name).violations
20
+ violations.length.should == 2
21
+
22
+ violations[0].should be_instance_of(Cane::UndocumentedClassViolation)
23
+ violations[0].file_name.should == file_name
24
+ violations[0].number.should == 3
25
+
26
+ violations[1].should be_instance_of(Cane::UndocumentedClassViolation)
27
+ violations[1].file_name.should == file_name
28
+ violations[1].number.should == 4
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ require 'tempfile'
2
+
3
+ # Keep a reference to all tempfiles so they are not garbage collected until the
4
+ # process exits.
5
+ $tempfiles = []
6
+
7
+ def make_file(content)
8
+ tempfile = Tempfile.new('cane')
9
+ $tempfiles << tempfile
10
+ tempfile.print(content)
11
+ tempfile.flush
12
+ tempfile.path
13
+ end
14
+
15
+ def make_dot_cane(content)
16
+ File.open('./.cane', 'w') do |f|
17
+ f.puts content
18
+ end
19
+ end
20
+
21
+ def unmake_dot_cane
22
+ FileUtils.rm('./.cane')
23
+ end
24
+
25
+ require 'simplecov'
26
+
27
+ class SimpleCov::Formatter::QualityFormatter
28
+ def format(result)
29
+ SimpleCov::Formatter::HTMLFormatter.new.format(result)
30
+ File.open("coverage/covered_percent", "w") do |f|
31
+ f.puts result.source_files.covered_percent.to_i
32
+ end
33
+ end
34
+ end
35
+
36
+ SimpleCov.formatter = SimpleCov::Formatter::QualityFormatter
37
+ SimpleCov.start
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ require 'cane/style_check'
4
+
5
+ describe Cane::StyleCheck do
6
+ it 'creates a StyleViolation for each method above the threshold' do
7
+ ruby = [
8
+ "def test ",
9
+ "\t1",
10
+ "end"
11
+ ].join("\n")
12
+ file_name = make_file(ruby)
13
+
14
+ violations = Cane::StyleCheck.new(files: file_name, measure: 80).violations
15
+ violations.length.should == 2
16
+ violations[0].should be_instance_of(StyleViolation)
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ require 'cane/threshold_check'
4
+
5
+ describe ThresholdCheck do
6
+ it 'returns a value of unavailable when file cannot be read' do
7
+ check = ThresholdCheck.new([[:>=, 'bogus_file', 20]])
8
+ violations = check.violations
9
+ violations.length.should == 1
10
+ violations[0].to_s.should include("unavailable")
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ require 'cane/violation_formatter'
4
+
5
+ describe Cane::ViolationFormatter do
6
+ def violation(description)
7
+ stub("violation",
8
+ description: description,
9
+ columns: []
10
+ )
11
+ end
12
+
13
+ it 'includes number of violations in the group header' do
14
+ described_class.new([violation("FAIL")]).to_s.should include("(1)")
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sparqcode_cane
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Xavier Shay, Mike Emery
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
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
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
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
+ description: Fails your build if code quality thresholds are not met
63
+ email:
64
+ - xavier@squareup.com, mike@sparqcode.com
65
+ executables:
66
+ - cane
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - spec/abc_check_spec.rb
71
+ - spec/cane_spec.rb
72
+ - spec/doc_check_spec.rb
73
+ - spec/spec_helper.rb
74
+ - spec/style_check_spec.rb
75
+ - spec/threshold_check_spec.rb
76
+ - spec/violation_formatter_spec.rb
77
+ - lib/cane/abc_check.rb
78
+ - lib/cane/abc_max_violation.rb
79
+ - lib/cane/cli/spec.rb
80
+ - lib/cane/cli/translator.rb
81
+ - lib/cane/cli.rb
82
+ - lib/cane/doc_check.rb
83
+ - lib/cane/rake_task.rb
84
+ - lib/cane/style_check.rb
85
+ - lib/cane/style_violation.rb
86
+ - lib/cane/syntax_violation.rb
87
+ - lib/cane/threshold_check.rb
88
+ - lib/cane/threshold_violation.rb
89
+ - lib/cane/version.rb
90
+ - lib/cane/violation_formatter.rb
91
+ - lib/cane.rb
92
+ - README.md
93
+ - HISTORY.md
94
+ - LICENSE
95
+ - cane.gemspec
96
+ - bin/cane
97
+ homepage: http://github.com/square/cane
98
+ licenses: []
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.24
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Fails your build if code quality thresholds are not met. Provides complexity
121
+ and style checkers built-in, and allows integration with with custom quality metrics. SPARQCode
122
+ changed it so that instead of ABC checks it does cyclomatic complexity.
123
+ test_files:
124
+ - spec/abc_check_spec.rb
125
+ - spec/cane_spec.rb
126
+ - spec/doc_check_spec.rb
127
+ - spec/spec_helper.rb
128
+ - spec/style_check_spec.rb
129
+ - spec/threshold_check_spec.rb
130
+ - spec/violation_formatter_spec.rb