Skip to content

Commit 7eefadb

Browse files
ashmarolijekyllbot
authored andcommitted
Scan assert_equal methods and rectify any offenses with a custom Rubocop cop (jekyll#7130)
Merge pull request 7130
1 parent 1e970b5 commit 7eefadb

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Jekyll
6+
# Checks for `assert_equal(exp, act, msg = nil)` calls containing literal values as
7+
# second argument. The second argument should ideally be a method called on the tested
8+
# instance.
9+
#
10+
# @example
11+
# # bad
12+
# assert_equal @foo.bar, "foobar"
13+
# assert_equal @alpha.beta, { "foo" => "bar", "lorem" => "ipsum" }
14+
# assert_equal @alpha.omega, ["foobar", "lipsum"]
15+
#
16+
# # good
17+
# assert_equal "foobar", @foo.bar
18+
#
19+
# assert_equal(
20+
# { "foo" => "bar", "lorem" => "ipsum" },
21+
# @alpha.beta
22+
# )
23+
#
24+
# assert_equal(
25+
# ["foobar", "lipsum"],
26+
# @alpha.omega
27+
# )
28+
#
29+
class AssertEqualLiteralActual < Cop
30+
MSG = "Provide the 'expected value' as the first argument to `assert_equal`.".freeze
31+
32+
SIMPLE_LITERALS = %i(
33+
true
34+
false
35+
nil
36+
int
37+
float
38+
str
39+
sym
40+
complex
41+
rational
42+
regopt
43+
).freeze
44+
45+
COMPLEX_LITERALS = %i(
46+
array
47+
hash
48+
pair
49+
irange
50+
erange
51+
regexp
52+
).freeze
53+
54+
def_node_matcher :literal_actual?, <<-PATTERN
55+
(send nil? :assert_equal $(send ...) $#literal?)
56+
PATTERN
57+
58+
def_node_matcher :literal_actual_with_msg?, <<-PATTERN
59+
(send nil? :assert_equal $(send ...) $#literal? $#opt_msg?)
60+
PATTERN
61+
62+
def on_send(node)
63+
return unless literal_actual?(node) || literal_actual_with_msg?(node)
64+
add_offense(node, location: :expression)
65+
end
66+
67+
def autocorrect(node)
68+
lambda do |corrector|
69+
corrector.replace(node.loc.expression, replacement(node))
70+
end
71+
end
72+
73+
private
74+
75+
def opt_msg?(node)
76+
node&.source
77+
end
78+
79+
# This is not implement using a NodePattern because it seems
80+
# to not be able to match against an explicit (nil) sexp
81+
def literal?(node)
82+
node && (simple_literal?(node) || complex_literal?(node))
83+
end
84+
85+
def simple_literal?(node)
86+
SIMPLE_LITERALS.include?(node.type)
87+
end
88+
89+
def complex_literal?(node)
90+
COMPLEX_LITERALS.include?(node.type) &&
91+
node.each_child_node.all?(&method(:literal?))
92+
end
93+
94+
def replacement(node)
95+
_, _, first_param, second_param, optional_param = *node
96+
97+
replaced_text = \
98+
if second_param.type == :hash
99+
replace_hash_with_variable(first_param.source, second_param.source)
100+
elsif second_param.type == :array && second_param.source != "[]"
101+
replace_array_with_variable(first_param.source, second_param.source)
102+
else
103+
replace_based_on_line_length(first_param.source, second_param.source)
104+
end
105+
106+
return "#{replaced_text}, #{optional_param.source}" if optional_param
107+
replaced_text
108+
end
109+
110+
def replace_based_on_line_length(first_expression, second_expression)
111+
result = "assert_equal #{second_expression}, #{first_expression}"
112+
return result if result.length < 80
113+
114+
# fold long lines independent of Rubocop configuration for better readability
115+
<<~TEXT
116+
assert_equal(
117+
#{second_expression},
118+
#{first_expression}
119+
)
120+
TEXT
121+
end
122+
123+
def replace_hash_with_variable(first_expression, second_expression)
124+
expect_expression = if second_expression.start_with?("{")
125+
second_expression
126+
else
127+
"{#{second_expression}}"
128+
end
129+
<<~TEXT
130+
expected = #{expect_expression}
131+
assert_equal expected, #{first_expression}
132+
TEXT
133+
end
134+
135+
def replace_array_with_variable(first_expression, second_expression)
136+
expect_expression = if second_expression.start_with?("%")
137+
second_expression
138+
else
139+
Array(second_expression)
140+
end
141+
<<~TEXT
142+
expected = #{expect_expression}
143+
assert_equal expected, #{first_expression}
144+
TEXT
145+
end
146+
end
147+
end
148+
end
149+
end

0 commit comments

Comments
 (0)