summaryrefslogtreecommitdiff
path: root/test/rubygems/test_utilities.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/rubygems/test_utilities.rb')
-rw-r--r--test/rubygems/test_utilities.rb373
1 files changed, 373 insertions, 0 deletions
diff --git a/test/rubygems/test_utilities.rb b/test/rubygems/test_utilities.rb
new file mode 100644
index 0000000000..08faef6578
--- /dev/null
+++ b/test/rubygems/test_utilities.rb
@@ -0,0 +1,373 @@
+# 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)
+ return Gem.read_binary 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
+
+ if @data[path].kind_of?(Array) && @data[path].first.kind_of?(Array)
+ @data[path].shift
+ else
+ @data[path]
+ end
+ 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)
+ File.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