1# TODO: $SAFE = 1
2
3begin
4  gem 'minitest', '~> 4.0'
5rescue NoMethodError
6  # for ruby tests
7end
8
9if defined? Gem::QuickLoader
10  Gem::QuickLoader.load_full_rubygems_library
11else
12  require 'rubygems'
13end
14
15begin
16  gem 'minitest'
17rescue Gem::LoadError
18end
19
20# We have to load these up front because otherwise we'll try to load
21# them while we're testing rubygems, and thus we can't actually load them.
22unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
23  gem 'rdoc'
24  gem 'json'
25end
26
27require 'rubygems/deprecate'
28require 'minitest/autorun'
29require 'fileutils'
30require 'tmpdir'
31require 'uri'
32require 'rubygems/package'
33require 'rubygems/test_utilities'
34require 'pp'
35require 'zlib'
36require 'pathname'
37require 'shellwords'
38Gem.load_yaml
39
40require 'rubygems/mock_gem_ui'
41
42module Gem
43
44  ##
45  # Allows setting the gem path searcher.  This method is available when
46  # requiring 'rubygems/test_case'
47
48  def self.searcher=(searcher)
49    @searcher = searcher
50  end
51
52  ##
53  # Allows toggling Windows behavior.  This method is available when requiring
54  # 'rubygems/test_case'
55
56  def self.win_platform=(val)
57    @@win_platform = val
58  end
59
60  ##
61  # Allows setting path to ruby.  This method is available when requiring
62  # 'rubygems/test_case'
63
64  def self.ruby= ruby
65    @ruby = ruby
66  end
67
68  ##
69  # When rubygems/test_case is required the default user interaction is a
70  # MockGemUi.
71
72  module DefaultUserInteraction
73    @ui = Gem::MockGemUi.new
74  end
75end
76
77##
78# RubyGemTestCase provides a variety of methods for testing rubygems and
79# gem-related behavior in a sandbox.  Through RubyGemTestCase you can install
80# and uninstall gems, fetch remote gems through a stub fetcher and be assured
81# your normal set of gems is not affected.
82#
83# Tests are always run at a safe level of 1.
84
85class Gem::TestCase < MiniTest::Unit::TestCase
86
87  # TODO: move to minitest
88  def assert_path_exists path, msg = nil
89    msg = message(msg) { "Expected path '#{path}' to exist" }
90    assert File.exist?(path), msg
91  end
92
93  # TODO: move to minitest
94  def refute_path_exists path, msg = nil
95    msg = message(msg) { "Expected path '#{path}' to not exist" }
96    refute File.exist?(path), msg
97  end
98
99  def scan_make_command_lines(output)
100    output.scan(/^#{Regexp.escape make_command}(?:[[:blank:]].*)?$/)
101  end
102
103  def parse_make_command_line(line)
104    command, *args = line.shellsplit
105
106    targets = []
107    macros = {}
108
109    args.each do |arg|
110      case arg
111      when /\A(\w+)=/
112        macros[$1] = $'
113      else
114        targets << arg
115      end
116    end
117
118    targets << '' if targets.empty?
119
120    {
121      :command => command,
122      :targets => targets,
123      :macros => macros,
124    }
125  end
126
127  def assert_contains_make_command(target, output, msg = nil)
128    if output.match(/\n/)
129      msg = message(msg) {
130        'Expected output containing make command "%s": %s' % [
131          ('%s %s' % [make_command, target]).rstrip,
132          output.inspect
133        ]
134      }
135    else
136      msg = message(msg) {
137        'Expected make command "%s": %s' % [
138          ('%s %s' % [make_command, target]).rstrip,
139          output.inspect
140        ]
141      }
142    end
143
144    assert scan_make_command_lines(output).any? { |line|
145      make = parse_make_command_line(line)
146
147      if make[:targets].include?(target)
148        yield make, line if block_given?
149        true
150      else
151        false
152      end
153    }, msg
154  end
155
156  include Gem::DefaultUserInteraction
157
158  undef_method :default_test if instance_methods.include? 'default_test' or
159                                instance_methods.include? :default_test
160
161  @@project_dir = Dir.pwd.untaint unless defined?(@@project_dir)
162
163  @@initial_reset = false
164
165  ##
166  # #setup prepares a sandboxed location to install gems.  All installs are
167  # directed to a temporary directory.  All install plugins are removed.
168  #
169  # If the +RUBY+ environment variable is set the given path is used for
170  # Gem::ruby.  The local platform is set to <tt>i386-mswin32</tt> for Windows
171  # or <tt>i686-darwin8.10.1</tt> otherwise.
172  #
173  # If the +KEEP_FILES+ environment variable is set the files will not be
174  # removed from <tt>/tmp/test_rubygems_#{$$}.#{Time.now.to_i}</tt>.
175
176  def setup
177    super
178
179    @orig_gem_home = ENV['GEM_HOME']
180    @orig_gem_path = ENV['GEM_PATH']
181
182    @current_dir = Dir.pwd
183    @ui = Gem::MockGemUi.new
184
185    tmpdir = File.expand_path Dir.tmpdir
186    tmpdir.untaint
187
188    if ENV['KEEP_FILES'] then
189      @tempdir = File.join(tmpdir, "test_rubygems_#{$$}.#{Time.now.to_i}")
190    else
191      @tempdir = File.join(tmpdir, "test_rubygems_#{$$}")
192    end
193    @tempdir.untaint
194
195    FileUtils.mkdir_p @tempdir
196
197    # This makes the tempdir consistent on OS X.
198    # File.expand_path Dir.tmpdir                      #=> "/var/..."
199    # Dir.chdir Dir.tmpdir do File.expand_path '.' end #=> "/private/var/..."
200    # TODO use File#realpath above instead of #expand_path once 1.8 support is
201    # dropped.
202    Dir.chdir @tempdir do
203      @tempdir = File.expand_path '.'
204      @tempdir.untaint
205    end
206
207    @gemhome  = File.join @tempdir, 'gemhome'
208    @userhome = File.join @tempdir, 'userhome'
209
210    @orig_ruby = if ENV['RUBY'] then
211                   ruby = Gem.instance_variable_get :@ruby
212                   Gem.instance_variable_set :@ruby, ENV['RUBY']
213                   ruby
214                 end
215
216    Gem.ensure_gem_subdirectories @gemhome
217
218    @orig_LOAD_PATH = $LOAD_PATH.dup
219    $LOAD_PATH.map! { |s| File.expand_path(s).untaint }
220
221    Dir.chdir @tempdir
222
223    @orig_ENV_HOME = ENV['HOME']
224    ENV['HOME'] = @userhome
225    Gem.instance_variable_set :@user_home, nil
226
227    FileUtils.mkdir_p @gemhome
228    FileUtils.mkdir_p @userhome
229
230    @default_dir = File.join @tempdir, 'default'
231    @default_spec_dir = File.join @default_dir, "specifications", "default"
232    Gem.instance_variable_set :@default_dir, @default_dir
233    FileUtils.mkdir_p @default_spec_dir
234
235    # We use Gem::Specification.reset the first time only so that if there
236    # are unresolved deps that leak into the whole test suite, they're at least
237    # reported once.
238    if @@initial_reset
239      Gem::Specification.unresolved_deps.clear # done to avoid cross-test warnings
240    else
241      @@initial_reset = true
242      Gem::Specification.reset
243    end
244    Gem.use_paths(@gemhome)
245
246    Gem::Security.reset
247
248    Gem.loaded_specs.clear
249    Gem.clear_default_specs
250    Gem::Specification.unresolved_deps.clear
251
252    Gem.configuration.verbose = true
253    Gem.configuration.update_sources = true
254
255    Gem::RemoteFetcher.fetcher = Gem::FakeFetcher.new
256
257    @gem_repo = "http://gems.example.com/"
258    @uri = URI.parse @gem_repo
259    Gem.sources.replace [@gem_repo]
260
261    Gem.searcher = nil
262    Gem::SpecFetcher.fetcher = nil
263    @orig_BASERUBY = Gem::ConfigMap[:BASERUBY]
264    Gem::ConfigMap[:BASERUBY] = Gem::ConfigMap[:ruby_install_name]
265
266    @orig_arch = Gem::ConfigMap[:arch]
267
268    if win_platform?
269      util_set_arch 'i386-mswin32'
270    else
271      util_set_arch 'i686-darwin8.10.1'
272    end
273
274    @marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
275
276    # TODO: move to installer test cases
277    Gem.post_build_hooks.clear
278    Gem.post_install_hooks.clear
279    Gem.done_installing_hooks.clear
280    Gem.post_reset_hooks.clear
281    Gem.post_uninstall_hooks.clear
282    Gem.pre_install_hooks.clear
283    Gem.pre_reset_hooks.clear
284    Gem.pre_uninstall_hooks.clear
285
286    # TODO: move to installer test cases
287    Gem.post_build do |installer|
288      @post_build_hook_arg = installer
289      true
290    end
291
292    Gem.post_install do |installer|
293      @post_install_hook_arg = installer
294    end
295
296    Gem.post_uninstall do |uninstaller|
297      @post_uninstall_hook_arg = uninstaller
298    end
299
300    Gem.pre_install do |installer|
301      @pre_install_hook_arg = installer
302      true
303    end
304
305    Gem.pre_uninstall do |uninstaller|
306      @pre_uninstall_hook_arg = uninstaller
307    end
308  end
309
310  ##
311  # #teardown restores the process to its original state and removes the
312  # tempdir unless the +KEEP_FILES+ environment variable was set.
313
314  def teardown
315    $LOAD_PATH.replace @orig_LOAD_PATH if @orig_LOAD_PATH
316
317    Gem::ConfigMap[:BASERUBY] = @orig_BASERUBY
318    Gem::ConfigMap[:arch] = @orig_arch
319
320    if defined? Gem::RemoteFetcher then
321      Gem::RemoteFetcher.fetcher = nil
322    end
323
324    Dir.chdir @current_dir
325
326    FileUtils.rm_rf @tempdir unless ENV['KEEP_FILES']
327
328    ENV['GEM_HOME'] = @orig_gem_home
329    ENV['GEM_PATH'] = @orig_gem_path
330
331    _ = @orig_ruby
332    Gem.instance_variable_set :@ruby, @orig_ruby if @orig_ruby
333
334    if @orig_ENV_HOME then
335      ENV['HOME'] = @orig_ENV_HOME
336    else
337      ENV.delete 'HOME'
338    end
339
340    Gem.instance_variable_set :@default_dir, nil
341  end
342
343  ##
344  # Builds and installs the Gem::Specification +spec+
345
346  def install_gem spec, options = {}
347    require 'rubygems/installer'
348
349    gem = File.join @tempdir, "gems", "#{spec.full_name}.gem"
350
351    unless File.exists? gem
352      use_ui Gem::MockGemUi.new do
353        Dir.chdir @tempdir do
354          Gem::Package.build spec
355        end
356      end
357
358      gem = File.join(@tempdir, File.basename(spec.cache_file)).untaint
359    end
360
361    Gem::Installer.new(gem, options.merge({:wrappers => true})).install
362  end
363
364  ##
365  # Builds and installs the Gem::Specification +spec+ into the user dir
366
367  def install_gem_user spec
368    install_gem spec, :user_install => true
369  end
370
371  ##
372  # Uninstalls the Gem::Specification +spec+
373  def uninstall_gem spec
374    require 'rubygems/uninstaller'
375
376    Gem::Uninstaller.new(spec.name,
377                         :executables => true, :user_install => true).uninstall
378  end
379
380  ##
381  # creates a temporary directory with hax
382  # TODO: deprecate and remove
383
384  def create_tmpdir
385    tmpdir = nil
386    Dir.chdir Dir.tmpdir do tmpdir = Dir.pwd end # HACK OSX /private/tmp
387    tmpdir = File.join tmpdir, "test_rubygems_#{$$}"
388    FileUtils.mkdir_p tmpdir
389    return tmpdir
390  end
391
392  ##
393  # Enables pretty-print for all tests
394
395  def mu_pp(obj)
396    s = ''
397    s = PP.pp obj, s
398    s = s.force_encoding(Encoding.default_external) if defined? Encoding
399    s.chomp
400  end
401
402  ##
403  # Reads a Marshal file at +path+
404
405  def read_cache(path)
406    open path.dup.untaint, 'rb' do |io|
407      Marshal.load io.read
408    end
409  end
410
411  ##
412  # Reads a binary file at +path+
413
414  def read_binary(path)
415    Gem.read_binary path
416  end
417
418  ##
419  # Writes a binary file to +path+ which is relative to +@gemhome+
420
421  def write_file(path)
422    path = File.join @gemhome, path unless Pathname.new(path).absolute?
423    dir = File.dirname path
424    FileUtils.mkdir_p dir
425
426    open path, 'wb' do |io|
427      yield io if block_given?
428    end
429
430    path
431  end
432
433  def all_spec_names
434    Gem::Specification.map(&:full_name)
435  end
436
437  ##
438  # Creates a Gem::Specification with a minimum of extra work.  +name+ and
439  # +version+ are the gem's name and version,  platform, author, email,
440  # homepage, summary and description are defaulted.  The specification is
441  # yielded for customization.
442  #
443  # The gem is added to the installed gems in +@gemhome+ and the runtime.
444  #
445  # Use this with #write_file to build an installed gem.
446
447  def quick_gem(name, version='2')
448    require 'rubygems/specification'
449
450    spec = Gem::Specification.new do |s|
451      s.platform    = Gem::Platform::RUBY
452      s.name        = name
453      s.version     = version
454      s.author      = 'A User'
455      s.email       = 'example@example.com'
456      s.homepage    = 'http://example.com'
457      s.summary     = "this is a summary"
458      s.description = "This is a test description"
459
460      yield(s) if block_given?
461    end
462
463    Gem::Specification.map # HACK: force specs to (re-)load before we write
464
465    written_path = write_file spec.spec_file do |io|
466      io.write spec.to_ruby_for_cache
467    end
468
469    spec.loaded_from = spec.loaded_from = written_path
470
471    Gem::Specification.add_spec spec.for_cache
472
473    return spec
474  end
475
476  def quick_spec name, version = '2'
477    # TODO: deprecate
478    require 'rubygems/specification'
479
480    spec = Gem::Specification.new do |s|
481      s.platform    = Gem::Platform::RUBY
482      s.name        = name
483      s.version     = version
484      s.author      = 'A User'
485      s.email       = 'example@example.com'
486      s.homepage    = 'http://example.com'
487      s.summary     = "this is a summary"
488      s.description = "This is a test description"
489
490      yield(s) if block_given?
491    end
492
493    spec.loaded_from = spec.spec_file
494
495    Gem::Specification.add_spec spec
496
497    return spec
498  end
499
500  ##
501  # Builds a gem from +spec+ and places it in <tt>File.join @gemhome,
502  # 'cache'</tt>.  Automatically creates files based on +spec.files+
503
504  def util_build_gem(spec)
505    dir = spec.gem_dir
506    FileUtils.mkdir_p dir
507
508    Dir.chdir dir do
509      spec.files.each do |file|
510        next if File.exist? file
511        FileUtils.mkdir_p File.dirname(file)
512        File.open file, 'w' do |fp| fp.puts "# #{file}" end
513      end
514
515      use_ui Gem::MockGemUi.new do
516        Gem::Package.build spec
517      end
518
519      cache = spec.cache_file
520      FileUtils.mv File.basename(cache), cache
521    end
522  end
523
524  def util_remove_gem(spec)
525    FileUtils.rm_rf spec.cache_file
526    FileUtils.rm_rf spec.spec_file
527  end
528
529  ##
530  # Removes all installed gems from +@gemhome+.
531
532  def util_clear_gems
533    FileUtils.rm_rf File.join(@gemhome, "gems") # TODO: use Gem::Dirs
534    FileUtils.rm_rf File.join(@gemhome, "specifications")
535    Gem::Specification.reset
536  end
537
538  ##
539  # Install the provided specs
540
541  def install_specs(*specs)
542    Gem::Specification.add_specs(*specs)
543    Gem.searcher = nil
544  end
545
546  ##
547  # Installs the provided default specs including writing the spec file
548
549  def install_default_gems(*specs)
550    install_default_specs(*specs)
551
552    specs.each do |spec|
553      open spec.loaded_from, 'w' do |io|
554        io.write spec.to_ruby_for_cache
555      end
556    end
557  end
558
559  ##
560  # Install the provided default specs
561
562  def install_default_specs(*specs)
563    install_specs(*specs)
564    specs.each do |spec|
565      Gem.register_default_spec(spec)
566    end
567  end
568
569  ##
570  # Create a new spec (or gem if passed an array of files) and set it
571  # up properly. Use this instead of util_spec and util_gem.
572
573  def new_spec name, version, deps = nil, *files
574    require 'rubygems/specification'
575
576    spec = Gem::Specification.new do |s|
577      s.platform    = Gem::Platform::RUBY
578      s.name        = name
579      s.version     = version
580      s.author      = 'A User'
581      s.email       = 'example@example.com'
582      s.homepage    = 'http://example.com'
583      s.summary     = "this is a summary"
584      s.description = "This is a test description"
585
586      Array(deps).each do |n, req|
587        s.add_dependency n, (req || '>= 0')
588      end
589
590      s.files.push(*files) unless files.empty?
591
592      yield s if block_given?
593    end
594
595    spec.loaded_from = spec.spec_file
596
597    unless files.empty? then
598      write_file spec.spec_file do |io|
599        io.write spec.to_ruby_for_cache
600      end
601
602      util_build_gem spec
603
604      cache_file = File.join @tempdir, 'gems', "#{spec.full_name}.gem"
605      FileUtils.mkdir_p File.dirname cache_file
606      FileUtils.mv spec.cache_file, cache_file
607      FileUtils.rm spec.spec_file
608    end
609
610    spec
611  end
612
613  def new_default_spec(name, version, deps = nil, *files)
614    spec = new_spec(name, version, deps)
615    spec.loaded_from = File.join(@default_spec_dir, spec.spec_name)
616    spec.files = files
617
618    lib_dir = File.join(@tempdir, "default_gems", "lib")
619    $LOAD_PATH.unshift(lib_dir)
620    files.each do |file|
621      rb_path = File.join(lib_dir, file)
622      FileUtils.mkdir_p(File.dirname(rb_path))
623      File.open(rb_path, "w") do |rb|
624        rb << "# #{file}"
625      end
626    end
627
628    spec
629  end
630
631  ##
632  # Creates a spec with +name+, +version+ and +deps+.
633
634  def util_spec(name, version, deps = nil, &block)
635    # TODO: deprecate
636    raise "deps or block, not both" if deps and block
637
638    if deps then
639      block = proc do |s|
640        # Since Hash#each is unordered in 1.8, sort
641        # the keys and iterate that way so the tests are
642        # deteriminstic on all implementations.
643        deps.keys.sort.each do |n|
644          s.add_dependency n, (deps[n] || '>= 0')
645        end
646      end
647    end
648
649    quick_spec(name, version, &block)
650  end
651
652  ##
653  # Creates a gem with +name+, +version+ and +deps+.  The specification will
654  # be yielded before gem creation for customization.  The gem will be placed
655  # in <tt>File.join @tempdir, 'gems'</tt>.  The specification and .gem file
656  # location are returned.
657
658  def util_gem(name, version, deps = nil, &block)
659    # TODO: deprecate
660    raise "deps or block, not both" if deps and block
661
662    if deps then
663      block = proc do |s|
664        # Since Hash#each is unordered in 1.8, sort
665        # the keys and iterate that way so the tests are
666        # deterministic on all implementations.
667        deps.keys.sort.each do |n|
668          s.add_dependency n, (deps[n] || '>= 0')
669        end
670      end
671    end
672
673    spec = quick_gem(name, version, &block)
674
675    util_build_gem spec
676
677    cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
678    FileUtils.mkdir_p File.dirname cache_file
679    FileUtils.mv spec.cache_file, cache_file
680    FileUtils.rm spec.spec_file
681
682    spec.loaded_from = nil
683
684    [spec, cache_file]
685  end
686
687  ##
688  # Gzips +data+.
689
690  def util_gzip(data)
691    out = StringIO.new
692
693    Zlib::GzipWriter.wrap out do |io|
694      io.write data
695    end
696
697    out.string
698  end
699
700  ##
701  # Creates several default gems which all have a lib/code.rb file.  The gems
702  # are not installed but are available in the cache dir.
703  #
704  # +@a1+:: gem a version 1, this is the best-described gem.
705  # +@a2+:: gem a version 2
706  # +@a3a:: gem a version 3.a
707  # +@a_evil9+:: gem a_evil version 9, use this to ensure similarly-named gems
708  #              don't collide with a.
709  # +@b2+:: gem b version 2
710  # +@c1_2+:: gem c version 1.2
711  # +@pl1+:: gem pl version 1, this gem has a legacy platform of i386-linux.
712  #
713  # Additional +prerelease+ gems may also be created:
714  #
715  # +@a2_pre+:: gem a version 2.a
716  # TODO: nuke this and fix tests. this should speed up a lot
717
718  def util_make_gems(prerelease = false)
719    @a1 = quick_gem 'a', '1' do |s|
720      s.files = %w[lib/code.rb]
721      s.require_paths = %w[lib]
722      s.date = Gem::Specification::TODAY - 86400
723      s.homepage = 'http://a.example.com'
724      s.email = %w[example@example.com example2@example.com]
725      s.authors = %w[Example Example2]
726      s.description = <<-DESC
727This line is really, really long.  So long, in fact, that it is more than eighty characters long!  The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters.  Without the wrapping, the text might not look good in the RSS feed.
728
729Also, a list:
730  * An entry that\'s actually kind of sort
731  * an entry that\'s really long, which will probably get wrapped funny.  That's ok, somebody wasn't thinking straight when they made it more than eighty characters.
732      DESC
733    end
734
735    init = proc do |s|
736      s.files = %w[lib/code.rb]
737      s.require_paths = %w[lib]
738    end
739
740    @a2      = quick_gem('a', '2',      &init)
741    @a3a     = quick_gem('a', '3.a',    &init)
742    @a_evil9 = quick_gem('a_evil', '9', &init)
743    @b2      = quick_gem('b', '2',      &init)
744    @c1_2    = quick_gem('c', '1.2',    &init)
745    @x       = quick_gem('x', '1', &init)
746    @dep_x   = quick_gem('dep_x', '1') do |s|
747      s.files = %w[lib/code.rb]
748      s.require_paths = %w[lib]
749      s.add_dependency 'x', '>= 1'
750    end
751
752    @pl1     = quick_gem 'pl', '1' do |s| # l for legacy
753      s.files = %w[lib/code.rb]
754      s.require_paths = %w[lib]
755      s.platform = Gem::Platform.new 'i386-linux'
756      s.instance_variable_set :@original_platform, 'i386-linux'
757    end
758
759    if prerelease
760      @a2_pre = quick_gem('a', '2.a', &init)
761      write_file File.join(*%W[gems #{@a2_pre.original_name} lib code.rb])
762      util_build_gem @a2_pre
763    end
764
765    write_file File.join(*%W[gems #{@a1.original_name}   lib code.rb])
766    write_file File.join(*%W[gems #{@a2.original_name}   lib code.rb])
767    write_file File.join(*%W[gems #{@a3a.original_name}  lib code.rb])
768    write_file File.join(*%W[gems #{@b2.original_name}   lib code.rb])
769    write_file File.join(*%W[gems #{@c1_2.original_name} lib code.rb])
770    write_file File.join(*%W[gems #{@pl1.original_name}  lib code.rb])
771    write_file File.join(*%W[gems #{@x.original_name}  lib code.rb])
772    write_file File.join(*%W[gems #{@dep_x.original_name}  lib code.rb])
773
774    [@a1, @a2, @a3a, @a_evil9, @b2, @c1_2, @pl1, @x, @dep_x].each do |spec|
775      util_build_gem spec
776    end
777
778    FileUtils.rm_r File.join(@gemhome, "gems", @pl1.original_name)
779  end
780
781  ##
782  # Set the platform to +arch+
783
784  def util_set_arch(arch)
785    Gem::ConfigMap[:arch] = arch
786    platform = Gem::Platform.new arch
787
788    Gem.instance_variable_set :@platforms, nil
789    Gem::Platform.instance_variable_set :@local, nil
790
791    platform
792  end
793
794  ##
795  # Sets up a fake fetcher using the gems from #util_make_gems.  Optionally
796  # additional +prerelease+ gems may be included.
797  #
798  # Gems created by this method may be fetched using Gem::RemoteFetcher.
799
800  def util_setup_fake_fetcher(prerelease = false)
801    require 'zlib'
802    require 'socket'
803    require 'rubygems/remote_fetcher'
804
805    @fetcher = Gem::FakeFetcher.new
806
807    util_make_gems(prerelease)
808    Gem::Specification.reset
809
810    @all_gems = [@a1, @a2, @a3a, @a_evil9, @b2, @c1_2].sort
811    @all_gem_names = @all_gems.map { |gem| gem.full_name }
812
813    gem_names = [@a1.full_name, @a2.full_name, @a3a.full_name, @b2.full_name]
814    @gem_names = gem_names.sort.join("\n")
815
816    Gem::RemoteFetcher.fetcher = @fetcher
817  end
818
819  ##
820  # Add +spec+ to +@fetcher+ serving the data in the file +path+.
821  # +repo+ indicates which repo to make +spec+ appear to be in.
822
823  def add_to_fetcher(spec, path=nil, repo=@gem_repo)
824    path ||= spec.cache_file
825    @fetcher.data["#{@gem_repo}gems/#{spec.file_name}"] = read_binary(path)
826  end
827
828  ##
829  # Sets up Gem::SpecFetcher to return information from the gems in +specs+.
830  # Best used with +@all_gems+ from #util_setup_fake_fetcher.
831
832  def util_setup_spec_fetcher(*specs)
833    specs -= Gem::Specification._all
834    Gem::Specification.add_specs(*specs)
835
836    spec_fetcher = Gem::SpecFetcher.fetcher
837
838    prerelease, all = Gem::Specification.partition { |spec|
839      spec.version.prerelease?
840    }
841
842    spec_fetcher.specs[@uri] = []
843    all.each do |spec|
844      spec_fetcher.specs[@uri] << spec.name_tuple
845    end
846
847    spec_fetcher.latest_specs[@uri] = []
848    Gem::Specification.latest_specs.each do |spec|
849      spec_fetcher.latest_specs[@uri] << spec.name_tuple
850    end
851
852    spec_fetcher.prerelease_specs[@uri] = []
853    prerelease.each do |spec|
854      spec_fetcher.prerelease_specs[@uri] << spec.name_tuple
855    end
856
857    v = Gem.marshal_version
858
859    Gem::Specification.each do |spec|
860      path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
861      data = Marshal.dump spec
862      data_deflate = Zlib::Deflate.deflate data
863      @fetcher.data[path] = data_deflate
864    end unless Gem::RemoteFetcher === @fetcher # HACK for test_download_to_cache
865
866    nil # force errors
867  end
868
869  ##
870  # Deflates +data+
871
872  def util_zip(data)
873    Zlib::Deflate.deflate data
874  end
875
876  ##
877  # Is this test being run on a Windows platform?
878
879  def self.win_platform?
880    Gem.win_platform?
881  end
882
883  ##
884  # Is this test being run on a Windows platform?
885
886  def win_platform?
887    Gem.win_platform?
888  end
889
890  ##
891  # Returns whether or not we're on a version of Ruby built with VC++ (or
892  # Borland) versus Cygwin, Mingw, etc.
893
894  def self.vc_windows?
895    RUBY_PLATFORM.match('mswin')
896  end
897
898  ##
899  # Returns whether or not we're on a version of Ruby built with VC++ (or
900  # Borland) versus Cygwin, Mingw, etc.
901
902  def vc_windows?
903    RUBY_PLATFORM.match('mswin')
904  end
905
906  ##
907  # Returns the make command for the current platform. For versions of Ruby
908  # built on MS Windows with VC++ or Borland it will return 'nmake'. On all
909  # other platforms, including Cygwin, it will return 'make'.
910
911  def self.make_command
912    ENV["make"] || (vc_windows? ? 'nmake' : 'make')
913  end
914
915  ##
916  # Returns the make command for the current platform. For versions of Ruby
917  # built on MS Windows with VC++ or Borland it will return 'nmake'. On all
918  # other platforms, including Cygwin, it will return 'make'.
919
920  def make_command
921    ENV["make"] || (vc_windows? ? 'nmake' : 'make')
922  end
923
924  ##
925  # Returns whether or not the nmake command could be found.
926
927  def nmake_found?
928    system('nmake /? 1>NUL 2>&1')
929  end
930
931  # In case we're building docs in a background process, this method waits for
932  # that process to exit (or if it's already been reaped, or never happened,
933  # swallows the Errno::ECHILD error).
934  def wait_for_child_process_to_exit
935    Process.wait if Process.respond_to?(:fork)
936  rescue Errno::ECHILD
937  end
938
939  ##
940  # Allows tests to use a random (but controlled) port number instead of
941  # a hardcoded one. This helps CI tools when running parallels builds on
942  # the same builder slave.
943
944  def self.process_based_port
945    @@process_based_port ||= 8000 + $$ % 1000
946  end
947
948  ##
949  # See ::process_based_port
950
951  def process_based_port
952    self.class.process_based_port
953  end
954
955  ##
956  # Allows the proper version of +rake+ to be used for the test.
957
958  def build_rake_in(good=true)
959    gem_ruby = Gem.ruby
960    Gem.ruby = @@ruby
961    env_rake = ENV["rake"]
962    rake = (good ? @@good_rake : @@bad_rake)
963    ENV["rake"] = rake
964    yield rake
965  ensure
966    Gem.ruby = gem_ruby
967    if env_rake
968      ENV["rake"] = env_rake
969    else
970      ENV.delete("rake")
971    end
972  end
973
974  ##
975  # Finds the path to the ruby executable
976
977  def self.rubybin
978    ruby = ENV["RUBY"]
979    return ruby if ruby
980    ruby = "ruby"
981    rubyexe = "#{ruby}.exe"
982
983    3.times do
984      if File.exist? ruby and File.executable? ruby and !File.directory? ruby
985        return File.expand_path(ruby)
986      end
987      if File.exist? rubyexe and File.executable? rubyexe
988        return File.expand_path(rubyexe)
989      end
990      ruby = File.join("..", ruby)
991    end
992
993    begin
994      require "rbconfig"
995      File.join(RbConfig::CONFIG["bindir"],
996                RbConfig::CONFIG["ruby_install_name"] +
997                RbConfig::CONFIG["EXEEXT"])
998    rescue LoadError
999      "ruby"
1000    end
1001  end
1002
1003  @@ruby = rubybin
1004  @@good_rake = "#{rubybin} #{File.expand_path('../../../test/rubygems/good_rake.rb', __FILE__)}"
1005  @@bad_rake = "#{rubybin} #{File.expand_path('../../../test/rubygems/bad_rake.rb', __FILE__)}"
1006
1007  ##
1008  # Construct a new Gem::Dependency.
1009
1010  def dep name, *requirements
1011    Gem::Dependency.new name, *requirements
1012  end
1013
1014  ##
1015  # Constructs a new Gem::Requirement.
1016
1017  def req *requirements
1018    return requirements.first if Gem::Requirement === requirements.first
1019    Gem::Requirement.create requirements
1020  end
1021
1022  ##
1023  # Constructs a new Gem::Specification.
1024
1025  def spec name, version, &block
1026    Gem::Specification.new name, v(version), &block
1027  end
1028
1029  ##
1030  # Construct a new Gem::Version.
1031
1032  def v string
1033    Gem::Version.create string
1034  end
1035
1036  class StaticSet
1037    def initialize(specs)
1038      @specs = specs.sort_by { |s| s.full_name }
1039    end
1040
1041    def find_spec(dep)
1042      @specs.reverse_each do |s|
1043        return s if dep.matches_spec? s
1044      end
1045    end
1046
1047    def find_all(dep)
1048      @specs.find_all { |s| dep.matches_spec? s }
1049    end
1050
1051    def prefetch(reqs)
1052    end
1053  end
1054
1055  ##
1056  # Loads certificate named +cert_name+ from <tt>test/rubygems/</tt>.
1057
1058  def self.load_cert cert_name
1059    cert_file = cert_path cert_name
1060
1061    cert = File.read cert_file
1062
1063    OpenSSL::X509::Certificate.new cert
1064  end
1065
1066  ##
1067  # Returns the path to the certificate named +cert_name+ from
1068  # <tt>test/rubygems/</tt>.
1069
1070  def self.cert_path cert_name
1071    if 32 == (Time.at(2**32) rescue 32) then
1072      cert_file =
1073        File.expand_path "../../../test/rubygems/#{cert_name}_cert_32.pem",
1074                         __FILE__
1075
1076      return cert_file if File.exist? cert_file
1077    end
1078
1079    File.expand_path "../../../test/rubygems/#{cert_name}_cert.pem", __FILE__
1080  end
1081
1082  ##
1083  # Loads an RSA private key named +key_name+ in <tt>test/rubygems/</tt>
1084
1085  def self.load_key key_name
1086    key_file = key_path key_name
1087
1088    key = File.read key_file
1089
1090    OpenSSL::PKey::RSA.new key
1091  end
1092
1093  ##
1094  # Returns the path tot he key named +key_name+ from <tt>test/rubygems</tt>
1095
1096  def self.key_path key_name
1097    File.expand_path "../../../test/rubygems/#{key_name}_key.pem", __FILE__
1098  end
1099
1100  # :stopdoc:
1101  # only available in RubyGems tests
1102
1103  begin
1104    PRIVATE_KEY      = load_key 'private'
1105    PRIVATE_KEY_PATH = key_path 'private'
1106    PUBLIC_KEY       = PRIVATE_KEY.public_key
1107
1108    PUBLIC_CERT      = load_cert 'public'
1109    PUBLIC_CERT_PATH = cert_path 'public'
1110  rescue Errno::ENOENT
1111    PRIVATE_KEY = nil
1112    PUBLIC_KEY  = nil
1113    PUBLIC_CERT = nil
1114  end
1115
1116end
1117