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.
- data/HISTORY.md +24 -0
- data/LICENSE +14 -0
- data/README.md +136 -0
- data/bin/cane +7 -0
- data/cane.gemspec +32 -0
- data/lib/cane.rb +49 -0
- data/lib/cane/abc_check.rb +138 -0
- data/lib/cane/abc_max_violation.rb +15 -0
- data/lib/cane/cli.rb +21 -0
- data/lib/cane/cli/spec.rb +135 -0
- data/lib/cane/cli/translator.rb +51 -0
- data/lib/cane/doc_check.rb +51 -0
- data/lib/cane/rake_task.rb +82 -0
- data/lib/cane/style_check.rb +45 -0
- data/lib/cane/style_violation.rb +10 -0
- data/lib/cane/syntax_violation.rb +20 -0
- data/lib/cane/threshold_check.rb +30 -0
- data/lib/cane/threshold_violation.rb +15 -0
- data/lib/cane/version.rb +3 -0
- data/lib/cane/violation_formatter.rb +49 -0
- data/spec/abc_check_spec.rb +81 -0
- data/spec/cane_spec.rb +130 -0
- data/spec/doc_check_spec.rb +30 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/style_check_spec.rb +18 -0
- data/spec/threshold_check_spec.rb +12 -0
- data/spec/violation_formatter_spec.rb +16 -0
- metadata +130 -0
data/lib/cane/version.rb
ADDED
@@ -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
|
data/spec/cane_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|