Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relax specs for Ruby 3.4 hash formatting #1597

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions features/setting_constraints/matching_arguments.feature
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Feature: Matching arguments
end
"""
When I run `rspec keyword_example_spec.rb`
Then it should fail with the following output:
Then it should fail with the following output, ignoring hash syntax:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo(bar: "incorrect") |
Expand All @@ -100,7 +100,7 @@ Feature: Matching arguments
end
"""
When I run `rspec keyword_example_spec.rb`
Then it should fail with the following output:
Then it should fail with the following output, ignoring hash syntax:
| 1 example, 1 failure |
| |
| Failure/Error: dbl.foo({bar: "also incorrect"}) |
Expand Down Expand Up @@ -156,7 +156,7 @@ Feature: Matching arguments
end
"""
When I run `rspec rspec_satisfy_spec.rb`
Then it should fail with the following output:
Then it should fail with the following output, ignoring hash syntax:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo({ :a => { :b => { :c => 3 } } }) |
Expand Down
7 changes: 6 additions & 1 deletion features/step_definitions/additional_cli_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@
diffable
end

Then /^it should fail with the following output:$/ do |table|
Then /^it should fail with the following output(, ignoring hash syntax)?:$/ do |ignore_hash_syntax, table|
step %q(the exit status should be 1)
lines = table.raw.flatten.reject(&:empty?)

if ignore_hash_syntax && RUBY_VERSION.to_f > 3.3
lines = lines.map { |line| line.gsub(/([^\s])=>/, '\1 => ') }
end

expect(all_output).to match_table(lines)
end
14 changes: 9 additions & 5 deletions spec/rspec/mocks/argument_matchers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ module Mocks
expect(a_double).to receive(:random_call).with(hash_including(:a => 1))
expect {
a_double.random_call(:a => 2)
}.to fail_including "expected: (hash_including(:a=>1))"
}.to fail_including "expected: (hash_including(#{hash_syntax(:a => 1)}))"
end
end

Expand All @@ -270,7 +270,7 @@ module Mocks
expect(a_double).to receive(:random_call).with(hash_excluding(:a => 1))
expect {
a_double.random_call(:a => 1)
}.to fail_including "expected: (hash_not_including(:a=>1))"
}.to fail_including "expected: (hash_not_including(#{hash_syntax(:a => 1)}))"
end
end

Expand Down Expand Up @@ -431,7 +431,7 @@ def ==(other)
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
expect do
a_double.random_call(opts)
end.to fail_with(/expected: \(\{(:a=>\"a\", :b=>\"b\"|:b=>\"b\", :a=>\"a\")\}\)/)
end.to fail_with(/expected: \(\{(:a\s*=>\s*\"a\", :b\s*=>\s*\"b\"|:b\s*=>\s*\"b\", :a\s*=>\s*\"a\")\}\)/)
end
else
it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do
Expand All @@ -445,14 +445,14 @@ def ==(other)
expect(a_double).to receive(:random_call).with(:a => "b", :c => "d")
expect do
a_double.random_call(:a => "b", :c => "e")
end.to fail_with(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/)
end.to fail_with(/expected: \(\{(:a\s*=>\s*\"b\", :c\s*=>\s*\"d\"|:c\s*=>\s*\"d\", :a\s*=>\s*\"b\")\}\)/)
end

it "fails for a hash w/ wrong keys", :reset => true do
expect(a_double).to receive(:random_call).with(:a => "b", :c => "d")
expect do
a_double.random_call("a" => "b", "c" => "d")
end.to fail_with(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/)
end.to fail_with(/expected: \(\{(:a\s*=>\s*\"b\", :c\s*=>\s*\"d\"|:c\s*=>\s*\"d\", :a\s*=>\s*\"b\")\}\)/)
end

it "matches a class against itself" do
Expand Down Expand Up @@ -502,6 +502,10 @@ def ==(other)
expect { a_double.msg 3 }.to fail_including "expected: (my_thing)"
end
end

def hash_syntax(hash)
hash.inspect.gsub(/\{(.*)\}/, '\1')
end
end
end
end
84 changes: 54 additions & 30 deletions spec/rspec/mocks/diffing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,18 @@
if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?
eval <<-'RUBY', nil, __FILE__, __LINE__ + 1
it "prints a diff when keyword argument were expected but got an option hash (using splat)" do
message =
"#<Double \"double\"> received :foo with unexpected arguments\n" \
" expected: ({:baz=>:quz, :foo=>:bar}) (keyword arguments)\n" \
" got: ({:baz=>:quz, :foo=>:bar}) (options hash)"

message = message.gsub('=>', ' => ') if RUBY_VERSION.to_f > 3.3

with_unfulfilled_double do |d|
expect(d).to receive(:foo).with(**expected_hash)
expect {
d.foo(expected_hash)
}.to fail_with(
"#<Double \"double\"> received :foo with unexpected arguments\n" \
" expected: ({:baz=>:quz, :foo=>:bar}) (keyword arguments)\n" \
" got: ({:baz=>:quz, :foo=>:bar}) (options hash)"
)
}.to fail_with(message)
end
end
RUBY
Expand All @@ -117,14 +120,18 @@
it "prints a diff when keyword argument were expected but got an option hash (literal)" do
with_unfulfilled_double do |d|
expect(d).to receive(:foo).with(:positional, keyword: 1)
expect {
options = { keyword: 1 }
d.foo(:positional, options)
}.to fail_with(

message =
"#<Double \"double\"> received :foo with unexpected arguments\n" \
" expected: (:positional, {:keyword=>1}) (keyword arguments)\n" \
" got: (:positional, {:keyword=>1}) (options hash)"
)

message = message.gsub('=>',' => ') if RUBY_VERSION.to_f > 3.3

expect {
options = { keyword: 1 }
d.foo(:positional, options)
}.to fail_with(message)
end
end
RUBY
Expand All @@ -139,18 +146,21 @@

expect(d).to receive(:foo).with(expected_input, one: 1)

expect {
options = { one: 1 }
d.foo(actual_input, options)
}.to fail_with(
message =
"#<Double \"double\"> received :foo with unexpected arguments\n" \
" expected: (#{expected_input.inspect}, {:one=>1}) (keyword arguments)\n" \
" got: (#{actual_input.inspect}, {:one=>1}) (options hash)\n" \
"Diff:\n" \
"@@ -1 +1 @@\n" \
"-[#{expected_input.inspect}, {:one=>1}]\n" \
"+[#{actual_input.inspect}, {:one=>1}]\n"
)

message = message.gsub('=>',' => ') if RUBY_VERSION.to_f > 3.3

expect {
options = { one: 1 }
d.foo(actual_input, options)
}.to fail_with(message)
end
end
RUBY
Expand All @@ -172,28 +182,34 @@
if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?
eval <<-'RUBY', nil, __FILE__, __LINE__ + 1
it "prints a diff when keyword argument were expected but got an option hash (using splat)" do
expect(d).to receive(:foo).with(:positional, **expected_hash)
expect {
d.foo(:positional, expected_hash)
}.to fail_with(
message =
"#{d.inspect} received :foo with unexpected arguments\n" \
" expected: (:positional, {:baz=>:quz, :foo=>:bar}) (keyword arguments)\n" \
" got: (:positional, {:baz=>:quz, :foo=>:bar}) (options hash)"
)

message.gsub!('=>',' => ') if RUBY_VERSION.to_f > 3.3

expect(d).to receive(:foo).with(:positional, **expected_hash)
expect {
d.foo(:positional, expected_hash)
}.to fail_with(message)
end
RUBY

eval <<-'RUBY', nil, __FILE__, __LINE__ + 1
it "prints a diff when keyword argument were expected but got an option hash (literal)" do
message =
"#{d.inspect} received :foo with unexpected arguments\n" \
" expected: (:positional, {:keyword=>1}) (keyword arguments)\n" \
" got: (:positional, {:keyword=>1}) (options hash)"

message.gsub!('=>',' => ') if RUBY_VERSION.to_f > 3.3

expect(d).to receive(:foo).with(:positional, keyword: 1)
expect {
options = { keyword: 1 }
d.foo(:positional, options)
}.to fail_with(
"#{d.inspect} received :foo with unexpected arguments\n" \
" expected: (:positional, {:keyword=>1}) (keyword arguments)\n" \
" got: (:positional, {:keyword=>1}) (options hash)"
)
}.to fail_with(message)
end
RUBY

Expand All @@ -206,18 +222,21 @@

expect(d).to receive(:foo).with(expected_input, one: 1)

expect {
options = { one: 1 }
d.foo(actual_input, options)
}.to fail_with(
message =
"#{d.inspect} received :foo with unexpected arguments\n" \
" expected: (#{expected_input.inspect}, {:one=>1}) (keyword arguments)\n" \
" got: (#{actual_input.inspect}, {:one=>1}) (options hash)\n" \
"Diff:\n" \
"@@ -1 +1 @@\n" \
"-[#{expected_input.inspect}, {:one=>1}]\n" \
"+[#{actual_input.inspect}, {:one=>1}]\n"
)

message = message.gsub('=>', ' => ') if RUBY_VERSION.to_f > 3.3

expect {
options = { one: 1 }
d.foo(actual_input, options)
}.to fail_with(message)
end
RUBY
end
Expand All @@ -232,6 +251,11 @@
def hash_regex_inspect(hash)
"\\{(#{hash.map { |key, value| "#{key.inspect}=>#{value.inspect}.*" }.join "|"}){#{hash.size}}\\}"
end
elsif RUBY_VERSION.to_f > 3.3
# Ruby head / 3.4 is changing the hash syntax inspect, but we use PP when diffing which just spaces out hashrockets
def hash_regex_inspect(hash)
Regexp.escape("{#{hash.map { |key, value| "#{key.inspect} => #{value.inspect}"}.join(", ")}}")
end
else
def hash_regex_inspect(hash)
Regexp.escape(hash.inspect)
Expand Down
9 changes: 8 additions & 1 deletion spec/rspec/mocks/double_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,17 @@ def initialize(amount, units)

if kw_args_supported?
it 'fails when calling yielding method with invalid kw args' do
message =
if RUBY_VERSION.to_f > 3.3
'#<Double "test double"> yielded |{:x => 1, :y => 2}| to block with optional keyword args (:x)'
else
'#<Double "test double"> yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)'
end

expect(@double).to receive(:yield_back).and_yield(:x => 1, :y => 2)
expect {
eval("@double.yield_back { |x: 1| }")
}.to fail_with '#<Double "test double"> yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)'
}.to fail_with message
end
end

Expand Down
9 changes: 8 additions & 1 deletion spec/rspec/mocks/hash_excluding_matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ module ArgumentMatchers
RSpec.describe HashExcludingMatcher do

it "describes itself properly" do
expect(HashExcludingMatcher.new(:a => 5).description).to eq "hash_not_including(:a=>5)"
message =
if RUBY_VERSION.to_f > 3.3
"hash_not_including(a: 5)"
else
"hash_not_including(:a=>5)"
end

expect(HashExcludingMatcher.new(:a => 5).description).to eq message
end

describe "passing" do
Expand Down
9 changes: 8 additions & 1 deletion spec/rspec/mocks/hash_including_matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ module ArgumentMatchers
RSpec.describe HashIncludingMatcher do

it "describes itself properly" do
expect(HashIncludingMatcher.new(:a => 1).description).to eq "hash_including(:a=>1)"
message =
if RUBY_VERSION.to_f > 3.3
"hash_including(a: 1)"
else
"hash_including(:a=>1)"
end

expect(HashIncludingMatcher.new(:a => 1).description).to eq message
end

it "describes passed matchers" do
Expand Down
26 changes: 19 additions & 7 deletions spec/rspec/mocks/matchers/receive_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,24 @@ def kw_args_method(a:, b:); end
dbl.kw_args_method(a: 1, b: 2)
end

if RUBY_VERSION >= '3.0'
if RUBY_VERSION.to_f >= 3.0
it "fails to expect to receive hash with keyword args" do
expect {
dbl = instance_double(TestObject)
expect(dbl).to receive(:kw_args_method).with(a: 1, b: 2)
dbl.kw_args_method({a: 1, b: 2})
}.to fail_with do |failure|
reset_all
expect(failure.message)
.to include('expected: ({:a=>1, :b=>2}) (keyword arguments)')
.and include('got: ({:a=>1, :b=>2}) (options hash)')

if RUBY_VERSION.to_f > 3.3
expect(failure.message)
.to include('expected: ({:a => 1, :b => 2}) (keyword arguments)')
.and include('got: ({:a => 1, :b => 2}) (options hash)')
else
expect(failure.message)
.to include('expected: ({:a=>1, :b=>2}) (keyword arguments)')
.and include('got: ({:a=>1, :b=>2}) (options hash)')
end
end
end
else
Expand Down Expand Up @@ -505,9 +512,14 @@ def receiver.method_missing(*); end # a poor man's stub...
receiver.foo(1, :bar => 2)
receiver.foo(1, :bar => 3)

expect { verify_all }.to(
raise_error(/received: 2 times with arguments: \(anything, hash_including\(:bar=>"anything"\)\)$/)
)
message =
if RUBY_VERSION.to_f > 3.3
/received: 2 times with arguments: \(anything, hash_including\(bar: "anything"\)\)$/
else
/received: 2 times with arguments: \(anything, hash_including\(:bar=>"anything"\)\)$/
end

expect { verify_all }.to raise_error(message)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,19 @@ module Mocks
dbl.kw_args_method(1, :required_arg => 2, :optional_arg => 3)
end

if RUBY_VERSION >= "3"
if RUBY_VERSION.to_f >= 3.0
it "fails to match against a hash submitted as a positional argument and received as keyword arguments in Ruby 3.0 or later", :reset => true do
messages =
if RUBY_VERSION.to_f > 3.3
["expected: (1, {:optional_arg => 3, :required_arg => 2}) (keyword arguments)", "got: (1, {:optional_arg => 3, :required_arg => 2}) (options hash)"]
else
["expected: (1, {:optional_arg=>3, :required_arg=>2}) (keyword arguments)", "got: (1, {:optional_arg=>3, :required_arg=>2}) (options hash)"]
end

expect(dbl).to receive(:kw_args_method).with(1, :required_arg => 2, :optional_arg => 3)
expect do
dbl.kw_args_method(1, {:required_arg => 2, :optional_arg => 3})
end.to fail_with(a_string_including("expected: (1, {:optional_arg=>3, :required_arg=>2}) (keyword arguments)", "got: (1, {:optional_arg=>3, :required_arg=>2}) (options hash)"))
end.to fail_with(a_string_including(*messages))
end
else
it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do
Expand Down
Loading