diff options
author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2021-01-21 14:35:56 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-21 14:35:56 +0900 |
commit | 151e469a6259837246d88b3abbb4d9e46ff38b9d (patch) | |
tree | c071c3e89e5d9ce493f754ce0536499373eab286 | |
parent | 41d0c708122d0f6389410d503b7f4e6342bf56a0 (diff) |
Merge RubyGems 3.2.6 and Bundler 2.2.6 (#4103)
24 files changed, 236 insertions, 51 deletions
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index b22363d119..7683047984 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -818,11 +818,6 @@ module Bundler # commonly happens if the version changed in the gemspec next unless new_spec - new_runtime_deps = new_spec.dependencies.select {|d| d.type != :development } - old_runtime_deps = s.dependencies.select {|d| d.type != :development } - # If the dependencies of the path source have changed and locked spec can't satisfy new dependencies, unlock it - next unless new_runtime_deps.sort == old_runtime_deps.sort || new_runtime_deps.all? {|d| satisfies_locked_spec?(d) } - s.dependencies.replace(new_spec.dependencies) end @@ -897,7 +892,7 @@ module Bundler def expand_dependency_with_platforms(dep, platforms) platforms.map do |p| - DepProxy.new(dep, p) + DepProxy.get_proxy(dep, p) end end @@ -977,7 +972,7 @@ module Bundler next requirements if @locked_gems.dependencies[name] != dependency next requirements if dependency.source.is_a?(Source::Path) dep = Gem::Dependency.new(name, ">= #{locked_spec.version}") - requirements[name] = DepProxy.new(dep, locked_spec.platform) + requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform) requirements end.values end diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb index bb09fe9ea6..a32dc37b49 100644 --- a/lib/bundler/dep_proxy.rb +++ b/lib/bundler/dep_proxy.rb @@ -4,19 +4,18 @@ module Bundler class DepProxy attr_reader :__platform, :dep + @proxies = {} + + def self.get_proxy(dep, platform) + @proxies[[dep, platform]] ||= new(dep, platform).freeze + end + def initialize(dep, platform) @dep = dep @__platform = platform end - def hash - @hash ||= [dep, __platform].hash - end - - def ==(other) - return false if other.class != self.class - dep == other.dep && __platform == other.__platform - end + private_class_method :new alias_method :eql?, :== @@ -39,6 +38,14 @@ module Bundler s end + def dup + raise NoMethodError.new("DepProxy cannot be duplicated") + end + + def clone + raise NoMethodError.new("DepProxy cannot be cloned") + end + private def method_missing(*args, &blk) diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index 2e87b7d443..3cce3f2139 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -81,8 +81,8 @@ module Bundler sort_dep_specs(spec_groups, locked_spec) end.tap do |specs| if DEBUG - warn before_result - warn " after sort_versions: #{debug_format_result(dep, specs).inspect}" + puts before_result + puts " after sort_versions: #{debug_format_result(dep, specs).inspect}" end end end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 4972d85e08..319f9bb137 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -32,7 +32,7 @@ module Bundler @base_dg = Molinillo::DependencyGraph.new @base.each do |ls| dep = Dependency.new(ls.name, ls.version) - @base_dg.add_vertex(ls.name, DepProxy.new(dep, ls.platform), true) + @base_dg.add_vertex(ls.name, DepProxy.get_proxy(dep, ls.platform), true) end additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) } @platforms = platforms @@ -75,7 +75,7 @@ module Bundler return unless debug? debug_info = yield debug_info = debug_info.inspect unless debug_info.is_a?(String) - puts debug_info.split("\n").map {|s| "BUNDLER: " + " " * depth + s } + puts debug_info.split("\n").map {|s| depth == 0 ? "BUNDLER: #{s}" : "BUNDLER(#{depth}): #{s}" } end def debug? diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 34780f9528..1042a2a4a0 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -99,7 +99,7 @@ module Bundler spec.dependencies.each do |dep| next if dep.type == :development next if @ignores_bundler_dependencies && dep.name == "bundler".freeze - dependencies[platform] << DepProxy.new(dep, platform) + dependencies[platform] << DepProxy.get_proxy(dep, platform) end end dependencies[platform] @@ -110,10 +110,10 @@ module Bundler return [] unless spec && spec.is_a?(Gem::Specification) dependencies = [] if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) + dependencies << DepProxy.get_proxy(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) end if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) + dependencies << DepProxy.get_proxy(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) end dependencies end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 0322b06d07..2bd2dcb451 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -158,6 +158,22 @@ module Gem end end + if Gem::Requirement.new("~> 2.0").hash == Gem::Requirement.new("~> 2.0.0").hash + class Requirement + module CorrectHashForLambdaOperator + def hash + if requirements.any? {|r| r.first == "~>" } + requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash + else + super + end + end + end + + prepend CorrectHashForLambdaOperator + end + end + class Platform JAVA = Gem::Platform.new("java") unless defined?(JAVA) MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN) diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index a0b9552c18..951e80231e 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -28,7 +28,7 @@ module Bundler specs_for_dep.first.dependencies.each do |d| next if d.type == :development - d = DepProxy.new(d, dep.__platform) unless match_current_platform + d = DepProxy.get_proxy(d, dep.__platform) unless match_current_platform deps << d end elsif check diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb index 26b8bc745c..f48d333462 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb @@ -329,11 +329,11 @@ module Bundler::Molinillo # Look for past conflicts that could be unwound to affect the # requirement tree for the current conflict + all_reqs = last_detail_for_current_unwind.all_requirements + all_reqs_size = all_reqs.size relevant_unused_unwinds = unused_unwind_options.select do |alternative| - intersecting_requirements = - last_detail_for_current_unwind.all_requirements & - alternative.requirements_unwound_to_instead - next if intersecting_requirements.empty? + diff_reqs = all_reqs - alternative.requirements_unwound_to_instead + next if diff_reqs.size == all_reqs_size # Find the highest index unwind whilst looping through current_detail = alternative if alternative > current_detail alternative @@ -344,8 +344,12 @@ module Bundler::Molinillo state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 } # Update the requirements_unwound_to_instead on any relevant unused unwinds - relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } - unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } + relevant_unused_unwinds.each do |d| + (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! + end + unwind_details.each do |d| + (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! + end current_detail end diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 5a49ea546e..b9fd12cda8 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.2.5".freeze + VERSION = "2.2.6".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 6d72006dd4..fc11bf4fa7 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -8,7 +8,7 @@ require 'rbconfig' module Gem - VERSION = "3.2.5".freeze + VERSION = "3.2.6".freeze end # Must be first since it unloads the prelude from 1.9.2 @@ -469,7 +469,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} next if File.exist? subdir begin FileUtils.mkdir_p subdir, **options - rescue Errno::EACCES + rescue SystemCallError end end ensure diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb index 222fb9aa5e..e4af450565 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -28,13 +28,14 @@ class Gem::Ext::Builder unless make_program make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' end + make_program = Shellwords.split(make_program) destdir = 'DESTDIR=%s' % ENV['DESTDIR'] ['clean', '', 'install'].each do |target| # Pass DESTDIR via command line to override what's in MAKEFLAGS cmd = [ - make_program, + *make_program, destdir, target, ].reject(&:empty?) diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index a500fd24c8..fd1c0a62ac 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -121,10 +121,6 @@ class Gem::Platform end end - def inspect - "%s @cpu=%p, @os=%p, @version=%p>" % [super[0..-2], *to_a] - end - def to_a [@cpu, @os, @version] end diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 430060e2ff..6721de4055 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -190,7 +190,7 @@ class Gem::Requirement end def hash # :nodoc: - requirements.sort.hash + requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash end def marshal_dump # :nodoc: diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb index 0f8d6b1076..84243d2ee2 100644 --- a/spec/bundler/bundler/dep_proxy_spec.rb +++ b/spec/bundler/bundler/dep_proxy_spec.rb @@ -2,10 +2,10 @@ RSpec.describe Bundler::DepProxy do let(:dep) { Bundler::Dependency.new("rake", ">= 0") } - subject { described_class.new(dep, Gem::Platform::RUBY) } + subject { described_class.get_proxy(dep, Gem::Platform::RUBY) } let(:same) { subject } - let(:other) { subject.dup } - let(:different) { described_class.new(dep, Gem::Platform::JAVA) } + let(:other) { described_class.get_proxy(dep, Gem::Platform::RUBY) } + let(:different) { described_class.get_proxy(dep, Gem::Platform::JAVA) } describe "#eql?" do it { expect(subject.eql?(same)).to be true } @@ -15,8 +15,18 @@ RSpec.describe Bundler::DepProxy do it { expect(subject.eql?("foobar")).to be false } end - describe "#hash" do - it { expect(subject.hash).to eq(same.hash) } - it { expect(subject.hash).to eq(other.hash) } + describe "must use factory methods" do + it { expect { described_class.new(dep, Gem::Platform::RUBY) }.to raise_error NoMethodError } + it { expect { subject.dup }.to raise_error NoMethodError } + it { expect { subject.clone }.to raise_error NoMethodError } + end + + describe "frozen" do + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.5.0") + error = Object.const_get("FrozenError") + else + error = RuntimeError + end + it { expect { subject.instance_variable_set(:@__platform, {}) }.to raise_error error } end end diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb index 01e0232fba..af51a270b6 100644 --- a/spec/bundler/bundler/gem_version_promoter_spec.rb +++ b/spec/bundler/bundler/gem_version_promoter_spec.rb @@ -170,7 +170,7 @@ RSpec.describe Bundler::GemVersionPromoter do context "debug output" do it "should not kerblooie on its own debug output" do gvp = unlocking(:level => :patch) - dep = Bundler::DepProxy.new(dep("foo", "1.2.0").first, "ruby") + dep = Bundler::DepProxy.get_proxy(dep("foo", "1.2.0").first, "ruby") result = gvp.send(:debug_format_result, dep, build_spec_groups("foo", %w[1.2.0 1.3.0])) expect(result.class).to eq Array end diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb index f19fe39721..6cd981e3d3 100644 --- a/spec/bundler/install/gemfile/path_spec.rb +++ b/spec/bundler/install/gemfile/path_spec.rb @@ -581,6 +581,71 @@ RSpec.describe "bundle install with explicit source paths" do expect(the_bundle).to include_gems "rack 0.9.1" end + + it "keeps using the same version even when another dependency is added" do + build_lib "foo", "1.0", :path => lib_path("foo") do |s| + s.add_dependency "rack", "0.9.1" + end + + bundle "install" + + expect(the_bundle).to include_gems "rack 0.9.1" + + lockfile_should_be <<-G + PATH + remote: #{lib_path("foo")} + specs: + foo (1.0) + rack (= 0.9.1) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rack (0.9.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + G + + build_lib "foo", "1.0", :path => lib_path("foo") do |s| + s.add_dependency "rack" + s.add_dependency "rake", "13.0.1" + end + + bundle "install" + + lockfile_should_be <<-G + PATH + remote: #{lib_path("foo")} + specs: + foo (1.0) + rack + rake (= 13.0.1) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rack (0.9.1) + rake (13.0.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + G + + expect(the_bundle).to include_gems "rack 0.9.1" + end end describe "switching sources" do diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index d4cccbe051..8ea61afca2 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -88,7 +88,7 @@ RSpec.describe "bundle install with install-time dependencies" do end it "installs plugins depended on by other plugins" do - install_gemfile <<-G + install_gemfile <<-G, :env => { "DEBUG" => "1" } source "#{file_uri_for(gem_repo2)}" gem "net_a" G @@ -97,7 +97,7 @@ RSpec.describe "bundle install with install-time dependencies" do end it "installs multiple levels of dependencies" do - install_gemfile <<-G + install_gemfile <<-G, :env => { "DEBUG" => "1" } source "#{file_uri_for(gem_repo2)}" gem "net_c" gem "net_e" @@ -114,7 +114,7 @@ RSpec.describe "bundle install with install-time dependencies" do gem "net_e" G - bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1" } + bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" } expect(out).to include("BUNDLER: Starting resolution") end @@ -128,7 +128,7 @@ RSpec.describe "bundle install with install-time dependencies" do gem "net_e" G - bundle :install, :env => { "DEBUG_RESOLVER" => "1" } + bundle :install, :env => { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" } expect(out).to include("BUNDLER: Starting resolution") end diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb new file mode 100644 index 0000000000..518da2800b --- /dev/null +++ b/spec/bundler/realworld/slow_perf_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "bundle install with complex dependencies", :realworld => true do + it "resolves quickly" do + start_time = Time.now + + install_gemfile <<-G + source 'https://rubygems.org' + + gem "actionmailer" + gem "mongoid", ">= 0.10.2" + G + + duration = Time.now - start_time + + expect(duration.to_f).to be < 120 # seconds + end +end diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb index 57897f89b4..ee62dc3577 100644 --- a/spec/bundler/resolver/basic_spec.rb +++ b/spec/bundler/resolver/basic_spec.rb @@ -176,7 +176,7 @@ Bundler could not find compatible versions for gem "a": deps = [] @deps.each do |d| - deps << Bundler::DepProxy.new(d, "ruby") + deps << Bundler::DepProxy.get_proxy(d, "ruby") end should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]] diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb index 7440523fc9..45b68541f5 100644 --- a/spec/bundler/support/indexes.rb +++ b/spec/bundler/support/indexes.rb @@ -22,7 +22,7 @@ module Spec @deps.each do |d| @platforms.each do |p| source_requirements[d.name] = d.source = default_source - deps << Bundler::DepProxy.new(d, p) + deps << Bundler::DepProxy.get_proxy(d, p) end end source_requirements ||= {} diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index 344b03be9b..68e3eccdaf 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -692,6 +692,11 @@ class TestGem < Gem::TestCase ensure FileUtils.chmod 0600, parent end + + def test_self_ensure_gem_directories_non_existent_paths + Gem.ensure_gem_subdirectories '/proc/0123456789/bogus' # should not raise + Gem.ensure_gem_subdirectories 'classpath:/bogus/x' # JRuby embed scenario + end end def test_self_extension_dir_shared diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb index 0fe650b8a5..6bebfa7a03 100644 --- a/test/rubygems/test_gem_ext_builder.rb +++ b/test/rubygems/test_gem_ext_builder.rb @@ -14,6 +14,7 @@ class TestGemExtBuilder < Gem::TestCase FileUtils.mkdir_p @dest_path @orig_DESTDIR = ENV['DESTDIR'] + @orig_make = ENV['make'] @spec = util_spec 'a' @@ -22,6 +23,7 @@ class TestGemExtBuilder < Gem::TestCase def teardown ENV['DESTDIR'] = @orig_DESTDIR + ENV['make'] = @orig_make super end @@ -81,6 +83,28 @@ install: assert_match %r{DESTDIR\\=#{ENV['DESTDIR']} install$}, results end + def test_custom_make_with_options + ENV['make'] = 'make V=1' + results = [] + File.open File.join(@ext, 'Makefile'), 'w' do |io| + io.puts <<-MAKEFILE +all: +\t@#{Gem.ruby} -e "puts 'all: OK'" + +clean: +\t@#{Gem.ruby} -e "puts 'clean: OK'" + +install: +\t@#{Gem.ruby} -e "puts 'install: OK'" + MAKEFILE + end + Gem::Ext::Builder.make @dest_path, results, @ext + results = results.join("\n").b + assert_match %r{clean: OK}, results + assert_match %r{all: OK}, results + assert_match %r{install: OK}, results + end + def test_build_extensions @spec.extensions << 'ext/extconf.rb' diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index 83a6f24de4..84754402ad 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -356,6 +356,14 @@ class TestGemPlatform < Gem::TestCase assert_local_match 'sparc-solaris2.8-mq5.3' end + def test_inspect + result = Gem::Platform.new("universal-java11").inspect + + assert_equal 1, result.scan(/@cpu=/).size + assert_equal 1, result.scan(/@os=/).size + assert_equal 1, result.scan(/@version=/).size + end + def assert_local_match(name) assert_match Gem::Platform.local, name end diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index 20127a1e15..837f60fbb2 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -402,6 +402,27 @@ class TestGemRequirement < Gem::TestCase assert_equal r1.hash, r2.hash end + def test_hash_returns_equal_hashes_for_equivalent_requirements + refute_requirement_hash_equal "= 1.2", "= 1.3" + refute_requirement_hash_equal "= 1.3", "= 1.2" + + refute_requirement_hash_equal "~> 1.3", "~> 1.3.0" + refute_requirement_hash_equal "~> 1.3.0", "~> 1.3" + + assert_requirement_hash_equal ["> 2", "~> 1.3", "~> 1.3.1"], ["~> 1.3.1", "~> 1.3", "> 2"] + + assert_requirement_hash_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"] + assert_requirement_hash_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"] + + assert_requirement_hash_equal "= 1.0", "= 1.0.0" + assert_requirement_hash_equal "= 1.1", "= 1.1.0" + assert_requirement_hash_equal "= 1", "= 1.0.0" + + assert_requirement_hash_equal "1.0", "1.0.0" + assert_requirement_hash_equal "1.1", "1.1.0" + assert_requirement_hash_equal "1", "1.0.0" + end + # Assert that two requirements are equal. Handles Gem::Requirements, # strings, arrays, numbers, and versions. @@ -416,6 +437,13 @@ class TestGemRequirement < Gem::TestCase "#{requirement} is satisfied by #{version}" end + # Assert that two requirement hashes are equal. Handles Gem::Requirements, + # strings, arrays, numbers, and versions. + + def assert_requirement_hash_equal(expected, actual) + assert_equal req(expected).hash, req(actual).hash + end + # Refute the assumption that two requirements are equal. def refute_requirement_equal(unexpected, actual) @@ -428,4 +456,10 @@ class TestGemRequirement < Gem::TestCase refute req(requirement).satisfied_by?(v(version)), "#{requirement} is not satisfied by #{version}" end + + # Refute the assumption that two requirements hashes are equal. + + def refute_requirement_hash_equal(unexpected, actual) + refute_equal req(unexpected).hash, req(actual).hash + end end |