Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #53 from square/issue-52
Add more threshold operators.
  • Loading branch information
xaviershay committed Jan 19, 2013
2 parents 93819f1 + 38cb79b commit a0cf38b
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 49 deletions.
58 changes: 31 additions & 27 deletions README.md
Expand Up @@ -34,33 +34,37 @@ Customize behaviour with a wealth of options:

Default options are loaded from a .cane file in the current directory.

-r, --require FILE Load a Ruby file containing user-defined checks
-c, --check CLASS Use the given user-defined check

--abc-glob GLOB Glob to run ABC metrics over (default: {app,lib}/**/*.rb)
--abc-max VALUE Ignore methods under this complexity (default: 15)
--abc-exclude METHOD Exclude method from analysis (eg. Foo::Bar#method)
--no-abc Disable ABC checking

--style-glob GLOB Glob to run style checks over (default: {app,lib,spec}/**/*.rb)
--style-measure VALUE Max line length (default: 80)
--style-exclude GLOB Exclude file or glob from style checking
--no-style Disable style checking

--doc-glob GLOB Glob to run doc checks over (default: {app,lib}/**/*.rb)
--doc-exclude GLOB Exclude file or glob from documentation checking
--no-readme Disable readme checking
--no-doc Disable documentation checking

--gte FILE,THRESHOLD Check the number in FILE is >= to THRESHOLD (a number or another file name)

-f, --all FILE Apply all checks to given file
--max-violations VALUE Max allowed violations (default: 0)
--json Output as JSON
--parallel Use all processors. Slower on small projects, faster on large.

-v, --version Show version
-h, --help Show this message
-r, --require FILE Load a Ruby file containing user-defined checks
-c, --check CLASS Use the given user-defined check

--abc-glob GLOB Glob to run ABC metrics over (default: {app,lib}/**/*.rb)
--abc-max VALUE Ignore methods under this complexity (default: 15)
--abc-exclude METHOD Exclude method from analysis (eg. Foo::Bar#method)
--no-abc Disable ABC checking

--style-glob GLOB Glob to run style checks over (default: {app,lib,spec}/**/*.rb)
--style-measure VALUE Max line length (default: 80)
--style-exclude GLOB Exclude file or glob from style checking
--no-style Disable style checking

--doc-glob GLOB Glob to run doc checks over (default: {app,lib}/**/*.rb)
--doc-exclude GLOB Exclude file or glob from documentation checking
--no-readme Disable readme checking
--no-doc Disable documentation checking

--lt FILE,THRESHOLD Check the number in FILE is < to THRESHOLD (a number or another file name)
--lte FILE,THRESHOLD Check the number in FILE is <= to THRESHOLD (a number or another file name)
--eq FILE,THRESHOLD Check the number in FILE is == to THRESHOLD (a number or another file name)
--gte FILE,THRESHOLD Check the number in FILE is >= to THRESHOLD (a number or another file name)
--gt FILE,THRESHOLD Check the number in FILE is > to THRESHOLD (a number or another file name)

-f, --all FILE Apply all checks to given file
--max-violations VALUE Max allowed violations (default: 0)
--json Output as JSON
--parallel Use all processors. Slower on small projects, faster on large.

-v, --version Show version
-h, --help Show this message

Set default options using a `.cane` file:

Expand Down
27 changes: 18 additions & 9 deletions lib/cane/threshold_check.rb
Expand Up @@ -5,15 +5,22 @@ module Cane
# Configurable check that allows the contents of a file to be compared against
# a given value.
class ThresholdCheck < Struct.new(:opts)
THRESHOLDS = {
lt: :<,
lte: :<=,
eq: :==,
gte: :>=,
gt: :>
}

def self.key; :threshold; end
def self.options
{
gte: ["Check the number in FILE is >= to THRESHOLD " +
"(a number or another file name)",
variable: "FILE,THRESHOLD",
type: Array]
}
THRESHOLDS.each_with_object({}) do |(key, value), h|
h[key] = ["Check the number in FILE is #{value} to THRESHOLD " +
"(a number or another file name)",
variable: "FILE,THRESHOLD",
type: Array]
end
end

def violations
Expand Down Expand Up @@ -54,9 +61,11 @@ def value_from_file(file)
end

def thresholds
(opts[:gte] || []).map do |x|
x.unshift(:>=)
end
THRESHOLDS.map do |k, v|
opts.fetch(k, []).map do |x|
x.unshift(v)
end
end.reduce(:+)
end

# Null object for all cases when the value to be compared against cannot be
Expand Down
22 changes: 21 additions & 1 deletion spec/parser_spec.rb
Expand Up @@ -17,11 +17,31 @@ def run(cli_args)
result[:style_measure].should == 3
end

it 'allows checking of a value in a file' do
it 'allows checking gte of a value in a file' do
output, result = run("--gte myfile,90")
result[:gte].should == [['myfile', '90']]
end

it 'allows checking eq of a value in a file' do
output, result = run("--eq myfile,90")
result[:eq].should == [['myfile', '90']]
end

it 'allows checking lte of a value in a file' do
output, result = run("--lte myfile,90")
result[:lte].should == [['myfile', '90']]
end

it 'allows checking lt of a value in a file' do
output, result = run("--lt myfile,90")
result[:lt].should == [['myfile', '90']]
end

it 'allows checking gt of a value in a file' do
output, resugt = run("--gt myfile,90")
resugt[:gt].should == [['myfile', '90']]
end

it 'allows upper bound of failed checks' do
output, result = run("--max-violations 1")
result[:max_violations].should == 1
Expand Down
15 changes: 15 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -28,6 +28,21 @@ def make_file(content)
tempfile.path
end

RSpec::Matchers.define :have_violation do |label|
match do |check|
violations = check.violations
violations.length.should == 1
violations[0][:label].should == label
end
end

RSpec::Matchers.define :have_no_violations do |label|
match do |check|
violations = check.violations
violations.length.should == 0
end
end

require 'simplecov'

class SimpleCov::Formatter::QualityFormatter
Expand Down
60 changes: 48 additions & 12 deletions spec/threshold_check_spec.rb
Expand Up @@ -16,23 +16,59 @@

context "checking violations" do

def run(threshold, value)
described_class.new(threshold => [['x', value]])
end

context "when the current coverage cannot be read" do
it 'reports a violation' do
check = Cane::ThresholdCheck.new(gte: [['bogus_file', '20']])
violations = check.violations
violations.length.should == 1
violations[0][:label].should ==
'bogus_file is unavailable, should be >= 20.0'
it do
run(:gte, 20).should \
have_violation('x is unavailable, should be >= 20.0')
end
end

context "when the coverage threshold is incorrectly specified" do
it 'reports a violation' do
check = Cane::ThresholdCheck.new(gte: [['20', 'bogus_file']])
violations = check.violations
violations.length.should == 1
violations[0][:label].should ==
'bogus_file is not a number or a file'
it do
described_class.new(gte: [['20', 'bogus_file']]).should \
have_violation('bogus_file is not a number or a file')
end
end

context 'when coverage threshold is valid' do
before do
file = fire_replaced_class_double("Cane::File")
stub_const("Cane::File", file)
file.should_receive(:contents).with('x').and_return("8\n")
end

context '>' do
it { run(:gt, 7).should have_no_violations }
it { run(:gt, 8).should have_violation('x is 8.0, should be > 8.0') }
it { run(:gt, 9).should have_violation('x is 8.0, should be > 9.0') }
end

context '>=' do
it { run(:gte, 7).should have_no_violations }
it { run(:gte, 8).should have_no_violations }
it { run(:gte, 9).should have_violation('x is 8.0, should be >= 9.0') }
end

context '==' do
it { run(:eq, 7).should have_violation('x is 8.0, should be == 7.0') }
it { run(:eq, 8).should have_no_violations }
it { run(:eq, 9).should have_violation('x is 8.0, should be == 9.0') }
end

context '<=' do
it { run(:lte, 7).should have_violation('x is 8.0, should be <= 7.0') }
it { run(:lte, 8).should have_no_violations }
it { run(:lte, 9).should have_no_violations }
end

context '<' do
it { run(:lt, 7).should have_violation('x is 8.0, should be < 7.0') }
it { run(:lt, 8).should have_violation('x is 8.0, should be < 8.0') }
it { run(:lt, 9).should have_no_violations }
end
end

Expand Down

0 comments on commit a0cf38b

Please sign in to comment.