1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/bundler/support/matchers.rb
2021-07-07 15:31:52 +09:00

237 lines
7.5 KiB
Ruby

# frozen_string_literal: true
require "forwardable"
require_relative "the_bundle"
module Spec
module Matchers
extend RSpec::Matchers
class Precondition
include RSpec::Matchers::Composable
extend Forwardable
def_delegators :failing_matcher,
:failure_message,
:actual,
:description,
:diffable?,
:expected,
:failure_message_when_negated
def initialize(matcher, preconditions)
@matcher = with_matchers_cloned(matcher)
@preconditions = with_matchers_cloned(preconditions)
@failure_index = nil
end
def matches?(target, &blk)
return false if @failure_index = @preconditions.index {|pc| !pc.matches?(target, &blk) }
@matcher.matches?(target, &blk)
end
def does_not_match?(target, &blk)
return false if @failure_index = @preconditions.index {|pc| !pc.matches?(target, &blk) }
if @matcher.respond_to?(:does_not_match?)
@matcher.does_not_match?(target, &blk)
else
!@matcher.matches?(target, &blk)
end
end
def expects_call_stack_jump?
@matcher.expects_call_stack_jump? || @preconditions.any?(&:expects_call_stack_jump)
end
def supports_block_expectations?
@matcher.supports_block_expectations? || @preconditions.any?(&:supports_block_expectations)
end
def failing_matcher
@failure_index ? @preconditions[@failure_index] : @matcher
end
end
def self.define_compound_matcher(matcher, preconditions, &declarations)
raise "Must have preconditions to define a compound matcher" if preconditions.empty?
define_method(matcher) do |*expected, &block_arg|
Precondition.new(
RSpec::Matchers::DSL::Matcher.new(matcher, declarations, self, *expected, &block_arg),
preconditions
)
end
end
RSpec::Matchers.define :have_dep do |*args|
dep = Bundler::Dependency.new(*args)
match do |actual|
actual.length == 1 && actual.all? {|d| d == dep }
end
end
RSpec::Matchers.define :have_gem do |*args|
match do |actual|
actual.length == args.length && actual.all? {|a| args.include?(a.full_name) }
end
end
RSpec::Matchers.define :be_sorted do
diffable
attr_reader :expected
match do |actual|
expected = block_arg ? actual.sort_by(&block_arg) : actual.sort
actual.==(expected).tap do
# HACK: since rspec won't show a diff when everything is a string
differ = RSpec::Support::Differ.new
@actual = differ.send(:object_to_string, actual)
@expected = differ.send(:object_to_string, expected)
end
end
end
RSpec::Matchers.define :be_well_formed do
match(&:empty?)
failure_message do |actual|
actual.join("\n")
end
end
define_compound_matcher :read_as, [exist] do |file_contents|
diffable
match do |actual|
@actual = Bundler.read_file(actual)
values_match?(file_contents, @actual)
end
end
def indent(string, padding = 4, indent_character = " ")
string.to_s.gsub(/^/, indent_character * padding).gsub("\t", " ")
end
define_compound_matcher :include_gems, [be_an_instance_of(Spec::TheBundle)] do |*names|
match do
opts = names.last.is_a?(Hash) ? names.pop : {}
source = opts.delete(:source)
groups = Array(opts.delete(:groups)).map(&:inspect).join(", ")
opts[:raise_on_error] = false
@errors = names.map do |full_name|
name, version, platform = full_name.split(/\s+/)
require_path = name.tr("-", "/")
version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name)
source_const = "#{Spec::Builders.constantize(name)}_SOURCE"
ruby <<~R, opts
require 'bundler'
Bundler.setup(#{groups})
require '#{require_path}'
actual_version, actual_platform = #{version_const}.split(/\s+/, 2)
unless Gem::Version.new(actual_version) == Gem::Version.new('#{version}')
puts actual_version
exit 64
end
unless actual_platform.to_s == '#{platform}'
puts actual_platform
exit 65
end
require '#{require_path}/source'
exit 0 if #{source.nil?}
actual_source = #{source_const}
unless actual_source == '#{source}'
puts actual_source
exit 66
end
R
next if exitstatus == 0
if exitstatus == 64
actual_version = out.split("\n").last
next "#{name} was expected to be at version #{version} but was #{actual_version}"
end
if exitstatus == 65
actual_platform = out.split("\n").last
next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
end
if exitstatus == 66
actual_source = out.split("\n").last
next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{actual_source}`"
end
next "Command to check for inclusion of gem #{full_name} failed"
end.compact
@errors.empty?
end
match_when_negated do
opts = names.last.is_a?(Hash) ? names.pop : {}
groups = Array(opts.delete(:groups)).map(&:inspect).join(", ")
opts[:raise_on_error] = false
@errors = names.map do |name|
name, version = name.split(/\s+/, 2)
ruby <<-R, opts
begin
require 'bundler'
Bundler.setup(#{groups})
rescue Bundler::GemNotFound, Bundler::GitError
exit 0
end
begin
require '#{name}'
name_constant = '#{Spec::Builders.constantize(name)}'
if #{version.nil?} || name_constant == '#{version}'
exit 64
else
exit 0
end
rescue LoadError, NameError
exit 0
end
R
next if exitstatus == 0
next "command to check version of #{name} installed failed" unless exitstatus == 64
next "expected #{name} to not be installed, but it was" if version.nil?
next "expected #{name} (#{version}) not to be installed, but it was"
end.compact
@errors.empty?
end
failure_message do
super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
end
failure_message_when_negated do
super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
end
end
RSpec::Matchers.define_negated_matcher :not_include_gems, :include_gems
RSpec::Matchers.alias_matcher :include_gem, :include_gems
def have_lockfile(expected)
read_as(strip_whitespace(expected))
end
def plugin_should_be_installed(*names)
names.each do |name|
expect(Bundler::Plugin).to be_installed(name)
path = Pathname.new(Bundler::Plugin.installed?(name))
expect(path + "plugins.rb").to exist
end
end
def plugin_should_not_be_installed(*names)
names.each do |name|
expect(Bundler::Plugin).not_to be_installed(name)
end
end
def lockfile_should_be(expected)
expect(bundled_app_lock).to have_lockfile(expected)
end
def gemfile_should_be(expected)
expect(bundled_app_gemfile).to read_as(strip_whitespace(expected))
end
end
end