Ameba::Rule -> Ameba::Rule::Base

This commit is contained in:
Vitalii Elenhaupt 2017-11-07 23:50:25 +02:00
parent b6c0d3e8ad
commit 80e2ab4f55
No known key found for this signature in database
GPG key ID: 7558EF3A4056C706
39 changed files with 79 additions and 73 deletions

View file

@ -0,0 +1,26 @@
require "../../spec/spec_helper"
module Ameba
describe Rule::Base do
describe "#catch" do
it "accepts and returns source" do
s = Source.new "", ""
DummyRule.new.catch(s).should eq s
end
end
describe "#name" do
it "returns name of the rule" do
DummyRule.new.name.should eq "Ameba::DummyRule"
end
end
end
describe Rule do
describe ".rules" do
it "returns a list of all defined rules" do
Rule.rules.includes?(DummyRule).should be_true
end
end
end
end

View file

@ -0,0 +1,113 @@
require "../../spec_helper"
module Ameba::Rule
subject = ComparisonToBoolean.new
describe ComparisonToBoolean do
it "passes if there is no comparison to boolean" do
source = Source.new %(
a = true
if a
:ok
end
if true
:ok
end
unless s.empty?
:ok
end
:ok if a
:ok if a != 1
:ok if a == "true"
case a
when true
:ok
when false
:not_ok
end
)
subject.catch(source).should be_valid
end
context "boolean on the right" do
it "fails if there is == comparison to boolean" do
source = Source.new %(
if s.empty? == true
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is != comparison to boolean" do
source = Source.new %(
if a != false
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is case comparison to boolean" do
source = Source.new %(
a === true
)
subject.catch(source).should_not be_valid
end
it "reports rule, pos and message" do
source = Source.new "a != true", "source.cr"
subject.catch(source)
error = source.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Comparison to a boolean is pointless"
end
end
context "boolean on the left" do
it "fails if there is == comparison to boolean" do
source = Source.new %(
if true == s.empty?
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is != comparison to boolean" do
source = Source.new %(
if false != a
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is case comparison to boolean" do
source = Source.new %(
true === a
)
subject.catch(source).should_not be_valid
end
it "reports rule, pos and message" do
source = Source.new "true != a", "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Comparison to a boolean is pointless"
end
end
end
end

View file

@ -0,0 +1,51 @@
require "../../spec_helper"
module Ameba
subject = Rule::ConstantNames.new
private def it_reports_constant(code, expected)
it "reports constant name #{expected}" do
s = Source.new code
Rule::ConstantNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
end
describe Rule::ConstantNames do
it "passes if type names are screaming-cased" do
s = Source.new %(
LUCKY_NUMBERS = [3, 7, 11]
DOCUMENTATION_URL = "http://crystal-lang.org/docs"
Int32
s : String = "str"
def works(n : Int32)
end
a = 1
myVar = 2
m_var = 3
)
subject.catch(s).should be_valid
end
it_reports_constant "MyBadConstant=1", "MYBADCONSTANT"
it_reports_constant "Wrong_NAME=2", "WRONG_NAME"
it_reports_constant "Wrong_Name=3", "WRONG_NAME"
it "reports rule, pos and message" do
s = Source.new %(
Const = 1
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Constant name should be screaming-cased: CONST, not Const"
)
end
end
end

View file

@ -0,0 +1,44 @@
require "../../spec_helper"
module Ameba::Rule
subject = DebuggerStatement.new
describe DebuggerStatement do
it "passes if there is no debugger statement" do
s = Source.new %(
"this is not a debugger statement"
s = "debugger"
def debugger(program)
end
debugger ""
class A
def debugger
end
end
A.new.debugger
)
subject.catch(s).should be_valid
end
it "fails if there is a debugger statement" do
s = Source.new %(
a = 2
debugger
a = a + 1
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new "debugger", "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Possible forgotten debugger statement detected"
end
end
end

View file

@ -0,0 +1,85 @@
require "../../spec_helper"
module Ameba
subject = Rule::EmptyExpression.new
def it_detects_empty_expression(code)
it "detects empty expression" do
s = Source.new code
rule = Rule::EmptyExpression.new
rule.catch(s).should_not be_valid
end
end
describe Rule::EmptyExpression do
it "passes if there is no empty expression" do
s = Source.new %(
def method()
end
method()
method(1, 2, 3)
method(nil)
a = nil
a = ""
a = 0
)
subject.catch(s).should be_valid
end
it_detects_empty_expression %(())
it_detects_empty_expression %(((())))
it_detects_empty_expression %(a = ())
it_detects_empty_expression %((();()))
it_detects_empty_expression %(if (); end)
it_detects_empty_expression %(
if foo
1
elsif ()
2
end
)
it_detects_empty_expression %(
case foo
when :foo then ()
end
)
it_detects_empty_expression %(
case foo
when :foo then 1
else
()
end
)
it_detects_empty_expression %(
case foo
when () then 1
end
)
it_detects_empty_expression %(
def method
a = 1
()
end
)
it_detects_empty_expression %(
def method
rescue
()
end
)
it "reports rule, location and message" do
s = Source.new %(
if ()
end
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:12"
error.message.should eq "Avoid empty expression '()'"
end
end
end

View file

@ -0,0 +1,118 @@
require "../../spec_helper"
module Ameba
subject = Rule::LargeNumbers.new
private def it_transforms(number, expected)
it "transforms large number #{number}" do
s = Source.new number
Rule::LargeNumbers.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
end
describe Rule::LargeNumbers do
it "passes if large number does not require underscore" do
s = Source.new %q(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 30 40 50 60 70 80 90
100
1_000
10_000
100_000
200_000
300_000
400_000
500_000
600_000
700_000
800_000
900_000
1_000_000
-9_223_372_036_854_775_808
9_223_372_036_854_775_807
141_592_654
141_592_654.0
141_592_654.001
141_592_654.001_2
141_592_654.001_23
141_592_654.001_234
141_592_654.001_234_5
0b1101
0o123
0xFE012D
0xfe012d
0xfe012dd11
1_i8
12_i16
123_i32
1_234_i64
12_u8
123_u16
1_234_u32
9_223_372_036_854_775_808_u64
9_223_372_036_854_775_808.000_123_456_789_f64
+100_u32
-900_000_i32
1_234.5e-7
11_234e10_f32
+1.123
-0.000_5
)
subject.catch(s).should be_valid
end
it_transforms "10000", "10_000"
it_transforms "+10000", "+10_000"
it_transforms "-10000", "-10_000"
it_transforms "9223372036854775808", "9_223_372_036_854_775_808"
it_transforms "-9223372036854775808", "-9_223_372_036_854_775_808"
it_transforms "+9223372036854775808", "+9_223_372_036_854_775_808"
it_transforms "1_00000", "100_000"
it_transforms "1_23_i8", "123_i8"
it_transforms "1000_i16", "1_000_i16"
it_transforms "1000_i32", "1_000_i32"
it_transforms "1000_i64", "1_000_i64"
it_transforms "1_23_u8", "123_u8"
it_transforms "1000_u16", "1_000_u16"
it_transforms "1000_u32", "1_000_u32"
it_transforms "1000_u64", "1_000_u64"
it_transforms "123456_f32", "123_456_f32"
it_transforms "123456_f64", "123_456_f64"
it_transforms "123456.5e-7_f32", "123_456.5e-7_f32"
it_transforms "123456e10_f64", "123_456e10_f64"
it_transforms "123456.5e-7", "123_456.5e-7"
it_transforms "123456e10", "123_456e10"
it_transforms "3.00_1", "3.001"
it_transforms "3.0012", "3.001_2"
it_transforms "3.00123", "3.001_23"
it_transforms "3.001234", "3.001_234"
it_transforms "3.0012345", "3.001_234_5"
it "reports rule, pos and message" do
s = Source.new %q(
1200000
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:10"
error.message.should match /1_200_000/
end
end
end

View file

@ -0,0 +1,33 @@
require "../../spec_helper"
module Ameba::Rule
subject = LineLength.new
long_line = "*" * 81
describe LineLength do
it "passes if all lines are shorter than 80 symbols" do
source = Source.new "short line"
subject.catch(source).should be_valid
end
it "passes if line consists of 79 symbols" do
source = Source.new "*" * 80
subject.catch(source).should be_valid
end
it "fails if there is at least one line longer than 79 symbols" do
source = Source.new long_line
subject.catch(source).should_not be_valid
end
it "reports rule, pos and message" do
source = Source.new long_line, "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should eq subject
error.location.to_s.should eq "source.cr:1:81"
error.message.should eq "Line too long"
end
end
end

View file

@ -0,0 +1,72 @@
require "../../spec_helper"
module Ameba::Rule
subject = LiteralInCondition.new
describe LiteralInCondition do
it "passes if there is not literals in conditional" do
s = Source.new %(
if a == 2
:ok
end
:ok unless b
case string
when "a"
:ok
when "b"
:ok
end
unless a.nil?
:ok
end
)
subject.catch(s).should be_valid
end
it "fails if there is a predicate in if conditional" do
s = Source.new %(
if "string"
:ok
end
)
subject.catch(s).should_not be_valid
end
it "fails if there is a predicate in unless conditional" do
s = Source.new %(
unless true
:ok
end
)
subject.catch(s).should_not be_valid
end
it "fails if there is a predicate in case conditional" do
s = Source.new %(
case [1, 2, 3]
when :array
:ok
when :not_array
:also_ok
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new %(
puts "hello" if true
), "source.cr"
subject.catch(s).should_not be_valid
s.errors.size.should eq 1
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq "Literal value found in conditional"
end
end
end

View file

@ -0,0 +1,41 @@
require "../../spec_helper"
module Ameba::Rule
subject = LiteralInInterpolation.new
describe LiteralInInterpolation do
it "passes with good interpolation examples" do
s = Source.new %q(
name = "Ary"
"Hello, #{name}"
"#{name}"
"Name size: #{name.size}"
)
subject.catch(s).should be_valid
end
it "fails if there is useless interpolation" do
[
%q("#{:Ary}"),
%q("#{[1, 2, 3]}"),
%q("#{true}"),
%q("#{false}"),
%q("here are #{4} cats"),
].each do |str|
subject.catch(Source.new str).should_not be_valid
end
end
it "reports rule, pos and message" do
s = Source.new %q("#{4}"), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Literal value found in interpolation"
end
end
end

View file

@ -0,0 +1,55 @@
require "../../spec_helper"
module Ameba
subject = Rule::MethodNames.new
private def it_reports_method_name(code, expected)
it "reports method name #{expected}" do
s = Source.new code
Rule::MethodNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
end
describe Rule::MethodNames do
it "passes if method names are underscore-cased" do
s = Source.new %(
class Person
def first_name
end
def date_of_birth
end
def homepage_url
end
def valid?
end
def name
end
end
)
subject.catch(s).should be_valid
end
it_reports_method_name %(def firstName; end), "first_name"
it_reports_method_name %(def date_of_Birth; end), "date_of_birth"
it_reports_method_name %(def homepageURL; end), "homepage_url"
it "reports rule, pos and message" do
s = Source.new %(
def bad_Name
end
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Method name should be underscore-cased: bad_name, not bad_Name"
)
end
end
end

View file

@ -0,0 +1,68 @@
require "../../spec_helper"
module Ameba::Rule
subject = NegatedConditionsInUnless.new
describe NegatedConditionsInUnless do
it "passes with a unless without negated condition" do
s = Source.new %(
unless a
:ok
end
:ok unless b
unless s.empty?
:ok
end
)
subject.catch(s).should be_valid
end
it "fails if there is a negated condition in unless" do
s = Source.new %(
unless !a
:nok
end
)
subject.catch(s).should_not be_valid
end
it "fails if one of AND conditions is negated" do
s = Source.new %(
unless a && !b
:nok
end
)
subject.catch(s).should_not be_valid
end
it "fails if one of OR conditions is negated" do
s = Source.new %(
unless a || !b
:nok
end
)
subject.catch(s).should_not be_valid
end
it "fails if one of inner conditions is negated" do
s = Source.new %(
unless a && (b || !c)
:nok
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new ":nok unless !s.empty?", "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Avoid negated conditions in unless blocks"
end
end
end

View file

@ -0,0 +1,45 @@
require "../../spec_helper"
module Ameba::Rule
subject = PredicateName.new
describe PredicateName do
it "passes if predicate name is correct" do
s = Source.new %q(
def valid?(x)
end
class Image
def picture?(x)
end
end
)
subject.catch(s).should be_valid
end
it "fails if predicate name is wrong" do
s = Source.new %q(
def is_valid?(x)
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new %q(
class Image
def has_picture?(x)
true
end
end
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:3:11"
error.message.should eq(
"Favour method name 'picture?' over 'has_picture?'")
end
end
end

View file

@ -0,0 +1,37 @@
require "../../spec_helper"
module Ameba::Rule
subject = TrailingBlankLines.new
describe TrailingBlankLines do
it "passes if there is no blank lines at the end" do
source = Source.new "no-blankline"
subject.catch(source).should be_valid
end
it "fails if there is a blank line at the end of a source" do
source = Source.new "a = 1\n \n "
subject.catch(source).should_not be_valid
end
it "passes if source is empty" do
source = Source.new ""
subject.catch(source).should be_valid
end
it "passes if last line is not blank" do
source = Source.new "\n\n\n puts 22"
subject.catch(source).should be_valid
end
it "reports rule, pos and message" do
source = Source.new "a = 1\n\n ", "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:3:1"
error.message.should eq "Blank lines detected at the end of the file"
end
end
end

View file

@ -0,0 +1,27 @@
require "../../spec_helper"
module Ameba::Rule
subject = TrailingWhitespace.new
describe TrailingWhitespace do
it "passes if all lines do not have trailing whitespace" do
source = Source.new "no-whispace"
subject.catch(source).should be_valid
end
it "fails if there is a line with trailing whitespace" do
source = Source.new "whitespace at the end "
subject.catch(source).should_not be_valid
end
it "reports rule, pos and message" do
source = Source.new "a = 1\n b = 2 ", "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:7"
error.message.should eq "Trailing whitespace detected"
end
end
end

View file

@ -0,0 +1,60 @@
require "../../spec_helper"
module Ameba
subject = Rule::TypeNames.new
private def it_reports_name(code, expected)
it "reports type name #{expected}" do
s = Source.new code
Rule::TypeNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
end
describe Rule::TypeNames do
it "passes if type names are camelcased" do
s = Source.new %(
class ParseError < Exception
end
module HTTP
class RequestHandler
end
end
alias NumericValue = Float32 | Float64 | Int32 | Int64
lib LibYAML
end
struct TagDirective
end
enum Time::DayOfWeek
end
)
subject.catch(s).should be_valid
end
it_reports_name "class My_class; end", "MyClass"
it_reports_name "module HTT_p; end", "HTTP"
it_reports_name "alias Numeric_value = Int32", "NumericValue"
it_reports_name "lib Lib_YAML; end", "LibYAML"
it_reports_name "struct Tag_directive; end", "TagDirective"
it_reports_name "enum Time_enum::Day_of_week; end", "TimeEnum::DayOfWeek"
it "reports rule, pos and message" do
s = Source.new %(
class My_class
end
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Type name should be camelcased: MyClass, but it was My_class"
)
end
end
end

View file

@ -0,0 +1,44 @@
require "../../spec_helper"
module Ameba::Rule
subject = UnlessElse.new
describe UnlessElse do
it "passes if unless hasn't else" do
s = Source.new %(
unless something
:ok
end
)
subject.catch(s).should be_valid
end
it "fails if unless has else" do
s = Source.new %(
unless something
:one
else
:two
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new %(
unless something
:one
else
:two
end
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.should_not be_nil
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq "Favour if over unless with else"
end
end
end

View file

@ -0,0 +1,61 @@
require "../../spec_helper"
module Ameba
subject = Rule::VariableNames.new
private def it_reports_var_name(code, expected)
it "reports method name #{expected}" do
s = Source.new code
Rule::VariableNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
end
describe Rule::VariableNames do
it "passes if var names are underscore-cased" do
s = Source.new %(
class Greeting
@@default_greeting = "Hello world"
def initialize(@custom_greeting = nil)
end
def print_greeting
greeting = @custom_greeting || @@default_greeting
puts greeting
end
end
)
subject.catch(s).should be_valid
end
it_reports_var_name %(myBadNamedVar = 1), "my_bad_named_var"
it_reports_var_name %(wrong_Name = 'y'), "wrong_name"
it_reports_var_name %(
class Greeting
def initialize(@badNamed = nil)
end
end
), "bad_named"
it_reports_var_name %(
class Greeting
@@defaultGreeting = "Hello world"
end
), "default_greeting"
it "reports rule, pos and message" do
s = Source.new %(
badName = "Yeah"
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Var name should be underscore-cased: bad_name, not badName"
)
end
end
end