1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/rubygems/test_utilities.rb
Olle Jonsson f61ee674d8 [rubygems/rubygems] Prefer start_with? and end_with? over regex.
- In one of the cases, filenames were checked for ending with "gz" -
    this is changed to check for ending with ".gz"
  - The change was made to make it even easier to read the code, and to
    match only from the start of the input (as opposed to start of the
    line)

https://github.com/rubygems/rubygems/commit/aac4290271
2020-06-05 07:32:42 +09:00

380 lines
8.9 KiB
Ruby

# frozen_string_literal: true
require 'tempfile'
require 'rubygems'
require 'rubygems/remote_fetcher'
##
# A fake Gem::RemoteFetcher for use in tests or to avoid real live HTTP
# requests when testing code that uses RubyGems.
#
# Example:
#
# @fetcher = Gem::FakeFetcher.new
# @fetcher.data['http://gems.example.com/yaml'] = source_index.to_yaml
# Gem::RemoteFetcher.fetcher = @fetcher
#
# use nested array if multiple response is needed
#
# @fetcher.data['http://gems.example.com/sequence'] = [['Success', 200, 'OK'], ['Failed', 401, 'Unauthorized']]
#
# @fetcher.fetch_path('http://gems.example.com/sequence') # => ['Success', 200, 'OK']
# @fetcher.fetch_path('http://gems.example.com/sequence') # => ['Failed', 401, 'Unauthorized']
#
# # invoke RubyGems code
#
# paths = @fetcher.paths
# assert_equal 'http://gems.example.com/yaml', paths.shift
# assert paths.empty?, paths.join(', ')
#
# See RubyGems' tests for more examples of FakeFetcher.
class Gem::FakeFetcher
attr_reader :data
attr_reader :last_request
attr_accessor :paths
def initialize
@data = {}
@paths = []
end
def find_data(path, nargs = 3)
return File.read path.path if URI === path and 'file' == path.scheme
if URI === path and "URI::#{path.scheme.upcase}" != path.class.name
raise ArgumentError,
"mismatch for scheme #{path.scheme} and class #{path.class}"
end
path = path.to_s
@paths << path
raise ArgumentError, 'need full URI' unless path.start_with?("https://", "http://")
unless @data.key? path
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
end
data = @data[path]
data.flatten! and return data.shift(nargs) if data.respond_to?(:flatten!)
data
end
def fetch_path(path, mtime = nil, head = false)
data = find_data(path)
if data.respond_to?(:call)
data.call
else
if path.to_s.end_with?(".gz") and not data.nil? and not data.empty?
data = Gem::Util.gunzip data
end
data
end
end
def cache_update_path(uri, path = nil, update = true)
if data = fetch_path(uri)
open(path, 'wb') { |io| io.write data } if path and update
data
else
Gem.read_binary(path) if path
end
end
# Thanks, FakeWeb!
def open_uri_or_path(path)
data = find_data(path)
body, code, msg = data
response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
response.instance_variable_set(:@body, body)
response.instance_variable_set(:@read, true)
response
end
def request(uri, request_class, last_modified = nil)
data = find_data(uri)
body, code, msg = (data.respond_to?(:call) ? data.call : data)
@last_request = request_class.new uri.request_uri
yield @last_request if block_given?
response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
response.instance_variable_set(:@body, body)
response.instance_variable_set(:@read, true)
response
end
def pretty_print(q) # :nodoc:
q.group 2, '[FakeFetcher', ']' do
q.breakable
q.text 'URIs:'
q.breakable
q.pp @data.keys
end
end
def fetch_size(path)
path = path.to_s
@paths << path
raise ArgumentError, 'need full URI' unless path =~ %r{^http://}
unless @data.key? path
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
end
data = @data[path]
data.respond_to?(:call) ? data.call : data.length
end
def download(spec, source_uri, install_dir = Gem.dir)
name = File.basename spec.cache_file
path = if Dir.pwd == install_dir # see fetch_command
install_dir
else
File.join install_dir, "cache"
end
path = File.join path, name
if source_uri =~ /^http/
File.open(path, "wb") do |f|
f.write fetch_path(File.join(source_uri, "gems", name))
end
else
FileUtils.cp source_uri, path
end
path
end
def download_to_cache(dependency)
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
return if found.empty?
spec, source = found.first
download spec, source.uri.to_s
end
end
# :stopdoc:
class Gem::RemoteFetcher
def self.fetcher=(fetcher)
@fetcher = fetcher
end
end
# :startdoc:
##
# The SpecFetcherSetup allows easy setup of a remote source in RubyGems tests:
#
# spec_fetcher do |f|
# f.gem 'a', 1
# f.spec 'a', 2
# f.gem 'b', 1' 'a' => '~> 1.0'
# end
#
# The above declaration creates two gems, a-1 and b-1, with a dependency from
# b to a. The declaration creates an additional spec a-2, but no gem for it
# (so it cannot be installed).
#
# After the gems are created they are removed from Gem.dir.
class Gem::TestCase::SpecFetcherSetup
##
# Executes a SpecFetcher setup block. Yields an instance then creates the
# gems and specifications defined in the instance.
def self.declare(test, repository)
setup = new test, repository
yield setup
setup.execute
end
def initialize(test, repository) # :nodoc:
@test = test
@repository = repository
@gems = {}
@downloaded = []
@installed = []
@operations = []
end
##
# Returns a Hash of created Specification full names and the corresponding
# Specification.
def created_specs
created = {}
@gems.keys.each do |spec|
created[spec.full_name] = spec
end
created
end
##
# Creates any defined gems or specifications
def execute # :nodoc:
execute_operations
setup_fetcher
created_specs
end
def execute_operations # :nodoc:
@operations.each do |operation, *arguments|
block = arguments.pop
case operation
when :gem then
spec, gem = @test.util_gem(*arguments, &block)
write_spec spec
@gems[spec] = gem
@installed << spec
when :download then
spec, gem = @test.util_gem(*arguments, &block)
@gems[spec] = gem
@downloaded << spec
when :spec then
spec = @test.util_spec(*arguments, &block)
write_spec spec
@gems[spec] = nil
@installed << spec
end
end
end
##
# Creates a gem with +name+, +version+ and +deps+. The created gem can be
# downloaded and installed.
#
# The specification will be yielded before gem creation for customization,
# but only the block or the dependencies may be set, not both.
def gem(name, version, dependencies = nil, &block)
@operations << [:gem, name, version, dependencies, block]
end
##
# Creates a gem with +name+, +version+ and +deps+. The created gem is
# downloaded in to the cache directory but is not installed
#
# The specification will be yielded before gem creation for customization,
# but only the block or the dependencies may be set, not both.
def download(name, version, dependencies = nil, &block)
@operations << [:download, name, version, dependencies, block]
end
##
# Creates a legacy platform spec with the name 'pl' and version 1
def legacy_platform
spec 'pl', 1 do |s|
s.platform = Gem::Platform.new 'i386-linux'
s.instance_variable_set :@original_platform, 'i386-linux'
end
end
def setup_fetcher # :nodoc:
require 'zlib'
require 'socket'
require 'rubygems/remote_fetcher'
unless @test.fetcher
@test.fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @test.fetcher
end
Gem::Specification.reset
begin
gem_repo, @test.gem_repo = @test.gem_repo, @repository
@test.uri = URI @repository
@test.util_setup_spec_fetcher(*@downloaded)
ensure
@test.gem_repo = gem_repo
@test.uri = URI gem_repo
end
@gems.each do |spec, gem|
next unless gem
@test.fetcher.data["#{@repository}gems/#{spec.file_name}"] =
Gem.read_binary(gem)
FileUtils.cp gem, spec.cache_file
end
end
##
# Creates a spec with +name+, +version+ and +deps+. The created gem can be
# downloaded and installed.
#
# The specification will be yielded before creation for customization,
# but only the block or the dependencies may be set, not both.
def spec(name, version, dependencies = nil, &block)
@operations << [:spec, name, version, dependencies, block]
end
def write_spec(spec) # :nodoc:
File.open spec.spec_file, 'w' do |io|
io.write spec.to_ruby_for_cache
end
end
end
##
# A StringIO duck-typed class that uses Tempfile instead of String as the
# backing store.
#
# This is available when rubygems/test_utilities is required.
#--
# This class was added to flush out problems in Rubinius' IO implementation.
class TempIO < Tempfile
##
# Creates a new TempIO that will be initialized to contain +string+.
def initialize(string = '')
super "TempIO"
binmode
write string
rewind
end
##
# The content of the TempIO as a String.
def string
flush
Gem.read_binary path
end
end