Updated RubySpec submodule to 9f66d0b1.
[rbx.git] / test / rubygems / test_gem_source_index.rb
blob140f3ad067daba224a9aceab9fe0bbe5e31922cd
1 #--
2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3 # All rights reserved.
4 # See LICENSE.txt for permissions.
5 #++
7 require 'test/unit'
8 require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
9 require 'rubygems/source_index'
10 require 'rubygems/config_file'
12 class Gem::SourceIndex
13   public :fetcher, :fetch_bulk_index, :fetch_quick_index,
14          :find_missing, :gems, :remove_extra,
15          :update_with_missing, :unzip
16 end
18 class TestGemSourceIndex < RubyGemTestCase
20   def setup
21     super
23     util_setup_fake_fetcher
24   end
26   def test_create_from_directory
27     # TODO
28   end
30   def test_fetcher
31     assert_equal @fetcher, @source_index.fetcher
32   end
34   def test_fetch_bulk_index_compressed
35     util_setup_bulk_fetch true
37     use_ui @ui do
38       fetched_index = @source_index.fetch_bulk_index @uri
39       assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
40                     @c1_2.full_name].sort,
41                    fetched_index.gems.map { |n,s| n }.sort
42     end
44     paths = @fetcher.paths
46     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
48     assert paths.empty?, paths.join(', ')
49   end
51   def test_fetch_bulk_index_error
52     @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = proc { raise SocketError }
53     @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = proc { raise SocketError }
54     @fetcher.data["#{@gem_repo}/yaml.Z"] = proc { raise SocketError }
55     @fetcher.data["#{@gem_repo}/yaml"] = proc { raise SocketError }
57     e = assert_raise Gem::RemoteSourceException do
58       use_ui @ui do
59         @source_index.fetch_bulk_index @uri
60       end
61     end
63     paths = @fetcher.paths
65     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
66     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
67     assert_equal "#{@gem_repo}/yaml.Z", paths.shift
68     assert_equal "#{@gem_repo}/yaml", paths.shift
70     assert paths.empty?, paths.join(', ')
72     assert_equal 'Error fetching remote gem cache: SocketError',
73                  e.message
74   end
76   def test_fetch_bulk_index_fallback
77     @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] =
78       proc { raise SocketError }
79     @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
80       proc { raise SocketError }
81     @fetcher.data["#{@gem_repo}/yaml.Z"] = proc { raise SocketError }
82     @fetcher.data["#{@gem_repo}/yaml"] = @source_index.to_yaml
84     use_ui @ui do
85       fetched_index = @source_index.fetch_bulk_index @uri
86       assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
87                     @c1_2.full_name].sort,
88                    fetched_index.gems.map { |n,s| n }.sort
89     end
91     paths = @fetcher.paths
93     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
94     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
95     assert_equal "#{@gem_repo}/yaml.Z", paths.shift
96     assert_equal "#{@gem_repo}/yaml", paths.shift
98     assert paths.empty?, paths.join(', ')
99   end
101   def test_fetch_bulk_index_marshal_mismatch
102     marshal = @source_index.dump
103     marshal[0] = (Marshal::MAJOR_VERSION - 1).chr
105     @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = marshal
106     @fetcher.data["#{@gem_repo}/yaml"] = @source_index.to_yaml
108     use_ui @ui do
109       fetched_index = @source_index.fetch_bulk_index @uri
110       assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
111                     @c1_2.full_name].sort,
112                    fetched_index.gems.map { |n,s| n }.sort
113     end
115     paths = @fetcher.paths
117     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
118     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
119     assert_equal "#{@gem_repo}/yaml.Z", paths.shift
120     assert_equal "#{@gem_repo}/yaml", paths.shift
122     assert paths.empty?, paths.join(', ')
123   end
125   def test_fetch_bulk_index_uncompressed
126     util_setup_bulk_fetch false
127     use_ui @ui do
128       fetched_index = @source_index.fetch_bulk_index @uri
129       assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
130                     @c1_2.full_name].sort,
131                    fetched_index.gems.map { |n,s| n }.sort
132     end
134     paths = @fetcher.paths
136     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
137     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
139     assert paths.empty?, paths.join(', ')
140   end
142   def test_fetch_quick_index
143     index = util_zip @gem_names
144     latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
146     @fetcher.data["#{@gem_repo}/quick/index.rz"] = index
147     @fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index
149     quick_index = @source_index.fetch_quick_index @uri, false
150     assert_equal [@a2.full_name, @b2.full_name].sort,
151                  quick_index.sort
153     paths = @fetcher.paths
155     assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
157     assert paths.empty?, paths.join(', ')
158   end
160   def test_fetch_quick_index_all
161     index = util_zip @gem_names
162     latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
164     @fetcher.data["#{@gem_repo}/quick/index.rz"] = index
165     @fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index
167     quick_index = @source_index.fetch_quick_index @uri, true
168     assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort,
169                  quick_index.sort
171     paths = @fetcher.paths
173     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
175     assert paths.empty?, paths.join(', ')
176   end
178   def test_fetch_quick_index_error
179     @fetcher.data["#{@gem_repo}/quick/index.rz"] =
180       proc { raise Exception }
182     e = assert_raise Gem::OperationNotSupportedError do
183       @source_index.fetch_quick_index @uri, true
184     end
186     assert_equal 'No quick index found: Exception', e.message
188     paths = @fetcher.paths
190     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
192     assert paths.empty?, paths.join(', ')
193   end
195   def test_fetch_quick_index_fallback
196     index = util_zip @gem_names
198     @fetcher.data["#{@gem_repo}/quick/index.rz"] = index
200     quick_index = @source_index.fetch_quick_index @uri, false
201     assert_equal @gem_names.split, quick_index.sort
203     paths = @fetcher.paths
205     assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
206     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
208     assert paths.empty?, paths.join(', ')
209   end
211   def test_fetch_quick_index_subdir
212     latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
213     repo = URI.parse "#{@gem_repo}/~nobody/mirror/"
215     @fetcher.data["#{repo}quick/latest_index.rz"] = latest_index
217     quick_index = @source_index.fetch_quick_index repo, false
218     assert_equal [@a2.full_name, @b2.full_name].sort,
219                  quick_index.sort
221     paths = @fetcher.paths
223     assert_equal "#{repo}quick/latest_index.rz", paths.shift
225     assert paths.empty?, paths.join(', ')
226   end
228   def test_fetch_single_spec
229     a1_spec_url = "#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz"
230     @fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
232     spec = @source_index.send :fetch_single_spec, URI.parse(@gem_repo),
233                               @a1.full_name
235     assert_equal @a1.full_name, spec.full_name
237     paths = @fetcher.paths
239     assert_equal a1_spec_url, paths.shift
241     assert paths.empty?, paths.join(', ')
242   end
244   def test_fetch_single_spec_subdir
245     repo = URI.parse "#{@gem_repo}/~nobody/mirror/"
247     a1_spec_url = "#{repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz"
248     @fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
250     spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
252     assert_equal @a1.full_name, spec.full_name
254     paths = @fetcher.paths
256     assert_equal a1_spec_url, paths.shift
258     assert paths.empty?, paths.join(', ')
259   end
261   def test_fetch_single_spec_yaml
262     a1_spec_url = "#{@gem_repo}/quick/#{@a1.full_name}.gemspec.rz"
263     @fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
265     repo = URI.parse @gem_repo
267     spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
269     assert_equal @a1.full_name, spec.full_name
271     paths = @fetcher.paths
273     assert_equal "#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz", paths.shift
274     assert_equal a1_spec_url, paths.shift
276     assert paths.empty?, paths.join(', ')
277   end
279   def test_fetch_single_spec_yaml_subdir
280     repo = URI.parse "#{@gem_repo}/~nobody/mirror/"
282     a1_spec_url = "#{repo}quick/#{@a1.full_name}.gemspec.rz"
283     @fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
285     spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
287     assert_equal @a1.full_name, spec.full_name
289     paths = @fetcher.paths
291     assert_equal "#{repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz", paths.shift
292     assert_equal a1_spec_url, paths.shift
294     assert paths.empty?, paths.join(', ')
295   end
297   def test_find_missing
298     missing = @source_index.find_missing [@b2.full_name]
299     assert_equal [@b2.full_name], missing
300   end
302   def test_find_missing_none_missing
303     missing = @source_index.find_missing [
304       @a1.full_name, @a2.full_name, @c1_2.full_name
305     ]
307     assert_equal [], missing
308   end
310   def test_latest_specs
311     p1_ruby = quick_gem 'p', '1'
312     p1_platform = quick_gem 'p', '1' do |spec|
313       spec.platform = Gem::Platform::CURRENT
314     end
316     a1_platform = quick_gem @a1.name, (@a1.version) do |s|
317       s.platform = Gem::Platform.new 'x86-my_platform1'
318     end
320     a2_platform = quick_gem @a2.name, (@a2.version) do |s|
321       s.platform = Gem::Platform.new 'x86-my_platform1'
322     end
324     a2_platform_other = quick_gem @a2.name, (@a2.version) do |s|
325       s.platform = Gem::Platform.new 'x86-other_platform1'
326     end
328     a3_platform_other = quick_gem @a2.name, (@a2.version.bump) do |s|
329       s.platform = Gem::Platform.new 'x86-other_platform1'
330     end
332     @source_index.add_spec p1_ruby
333     @source_index.add_spec p1_platform
334     @source_index.add_spec a1_platform
335     @source_index.add_spec a2_platform
336     @source_index.add_spec a2_platform_other
337     @source_index.add_spec a3_platform_other
339     expected = [
340       @a2.full_name,
341       a2_platform.full_name,
342       a3_platform_other.full_name,
343       @c1_2.full_name,
344       @a_evil9.full_name,
345       p1_ruby.full_name,
346       p1_platform.full_name,
347     ].sort
349     latest_specs = @source_index.latest_specs.map { |s| s.full_name }.sort
351     assert_equal expected, latest_specs
352   end
354   def test_load_gems_in
355     spec_dir1 = File.join @gemhome, 'specifications'
356     spec_dir2 = File.join @tempdir, 'gemhome2', 'specifications'
358     FileUtils.rm_r spec_dir1
360     FileUtils.mkdir_p spec_dir1
361     FileUtils.mkdir_p spec_dir2
363     a1 = quick_gem 'a', '1' do |spec| spec.author = 'author 1' end
364     a2 = quick_gem 'a', '1' do |spec| spec.author = 'author 2' end
366     File.open File.join(spec_dir1, "#{a1.full_name}.gemspec"), 'w' do |fp|
367       fp.write a1.to_ruby
368     end
370     File.open File.join(spec_dir2, "#{a2.full_name}.gemspec"), 'w' do |fp|
371       fp.write a2.to_ruby
372     end
374     @source_index.load_gems_in spec_dir1, spec_dir2
376     assert_equal a1.author, @source_index.specification(a1.full_name).author
377   end
379   def test_outdated
380     util_setup_source_info_cache
382     assert_equal [], @source_index.outdated
384     updated = quick_gem @a2.name, (@a2.version.bump)
385     util_setup_source_info_cache updated
387     assert_equal [updated.name], @source_index.outdated
389     updated_platform = quick_gem @a2.name, (updated.version.bump) do |s|
390       s.platform = Gem::Platform.new 'x86-other_platform1'
391     end
393     util_setup_source_info_cache updated, updated_platform
395     assert_equal [updated_platform.name], @source_index.outdated
396   end
398   def test_refresh_bang
399     a1_spec = File.join @gemhome, "specifications", "#{@a1.full_name}.gemspec" 
401     FileUtils.mv a1_spec, @tempdir
403     source_index = Gem::SourceIndex.from_installed_gems
405     assert !source_index.gems.include?(@a1.full_name)
407     FileUtils.mv File.join(@tempdir, "#{@a1.full_name}.gemspec"), a1_spec
409     source_index.refresh!
411     assert source_index.gems.include?(@a1.full_name)
412   end
414   def test_remove_extra
415     @source_index.add_spec @a1
416     @source_index.add_spec @a2
417     @source_index.add_spec @pl1
419     @source_index.remove_extra [@a1.full_name, @pl1.full_name]
421     assert_equal [@a1.full_name],
422                  @source_index.gems.map { |n,s| n }.sort
423   end
425   def test_remove_extra_no_changes
426     gems = [@a1.full_name, @a2.full_name]
427     @source_index.add_spec @a1
428     @source_index.add_spec @a2
430     @source_index.remove_extra gems
432     assert_equal gems, @source_index.gems.map { |n,s| n }.sort
433   end
435   def test_search
436     assert_equal [@a1, @a2, @a_evil9], @source_index.search('a')
437     assert_equal [@a2], @source_index.search('a', '= 2')
439     assert_equal [], @source_index.search('bogusstring')
440     assert_equal [], @source_index.search('a', '= 3')
442     source_index = Gem::SourceIndex.new
443     source_index.add_spec @a1
444     source_index.add_spec @a2
446     assert_equal [@a1], source_index.search(@a1.name, '= 1')
448     r1 = Gem::Requirement.create '= 1'
449     assert_equal [@a1], source_index.search(@a1.name, r1)
451     dep = Gem::Dependency.new @a1.name, r1
452     assert_equal [@a1], source_index.search(dep)
453   end
455   def test_search_empty_cache
456     empty_source_index = Gem::SourceIndex.new({})
457     assert_equal [], empty_source_index.search("foo")
458   end
460   def test_search_platform
461     util_set_arch 'x86-my_platform1'
463     a1 = quick_gem 'a', '1'
464     a1_mine = quick_gem 'a', '1' do |s|
465       s.platform = Gem::Platform.new 'x86-my_platform1'
466     end
467     a1_other = quick_gem 'a', '1' do |s|
468       s.platform = Gem::Platform.new 'x86-other_platform1'
469     end
471     si = Gem::SourceIndex.new(a1.full_name => a1, a1_mine.full_name => a1_mine,
472                               a1_other.full_name => a1_other)
474     dep = Gem::Dependency.new 'a', Gem::Requirement.new('1')
476     gems = si.search dep, true
478     assert_equal [a1, a1_mine], gems.sort
479   end
481   def test_signature
482     sig = @source_index.gem_signature('foo-1.2.3')
483     assert_equal 64, sig.length
484     assert_match(/^[a-f0-9]{64}$/, sig)
485   end
487   def test_specification
488     assert_equal @a1, @source_index.specification(@a1.full_name)
490     assert_nil @source_index.specification("foo-1.2.4")
491   end
493   def test_index_signature
494     sig = @source_index.index_signature
495     assert_match(/^[a-f0-9]{64}$/, sig)
496   end
498   def test_unzip
499     input = "x\234+\316\317MU(I\255(\001\000\021\350\003\232"
500     assert_equal 'some text', @source_index.unzip(input)
501   end
503   def test_update_bulk
504     util_setup_bulk_fetch true
506     @source_index.gems.replace({})
507     assert_equal [], @source_index.gems.keys.sort
509     use_ui @ui do
510       @source_index.update @uri, true
512       assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
513                     @c1_2.full_name],
514                    @source_index.gems.keys.sort
515     end
517     paths = @fetcher.paths
519     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
520     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
522     assert paths.empty?, paths.join(', ')
523   end
525   def test_update_incremental
526     old_gem_conf = Gem.configuration
527     Gem.configuration = Gem::ConfigFile.new([])
529     latest_names = [@a2, @a_evil9, @b2, @c1_2].map { |s| s.full_name }
530     latest_index = util_zip latest_names.join("\n")
531     @fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index
533     marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
534                             "#{@b2.full_name}.gemspec.rz"
535     @fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
537     use_ui @ui do
538       @source_index.update @uri, false
540       assert_equal latest_names, @source_index.gems.keys.sort
541     end
543     paths = @fetcher.paths
544     assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
545     assert_equal marshal_uri, paths.shift
547     assert paths.empty?, paths.join(', ')
548   ensure
549     Gem.configuration = old_gem_conf
550   end
552   def test_update_incremental_all
553     old_gem_conf = Gem.configuration
554     Gem.configuration = Gem::ConfigFile.new([])
556     quick_index = util_zip @all_gem_names.join("\n")
557     @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
559     marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
560                             "#{@b2.full_name}.gemspec.rz"
561     @fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
563     use_ui @ui do
564       @source_index.update @uri, true
566       assert_equal @all_gem_names, @source_index.gems.keys.sort
567     end
569     paths = @fetcher.paths
570     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
571     assert_equal marshal_uri, paths.shift
573     assert paths.empty?, paths.join(', ')
574   ensure
575     Gem.configuration = old_gem_conf
576   end
578   def test_update_incremental_fallback
579     old_gem_conf = Gem.configuration
580     Gem.configuration = Gem::ConfigFile.new([])
582     quick_index = util_zip @all_gem_names.join("\n")
583     @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
585     marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
586                             "#{@b2.full_name}.gemspec.rz"
588     yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
589     @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
591     use_ui @ui do
592       @source_index.update @uri, true
594       assert_equal @all_gem_names, @source_index.gems.keys.sort
595     end
597     paths = @fetcher.paths
598     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
599     assert_equal marshal_uri, paths.shift
600     assert_equal yaml_uri, paths.shift
602     assert paths.empty?, paths.join(', ')
603   ensure
604     Gem.configuration = old_gem_conf
605   end
607   def test_update_incremental_marshal_mismatch
608     old_gem_conf = Gem.configuration
609     Gem.configuration = Gem::ConfigFile.new([])
611     quick_index = util_zip @all_gem_names.join("\n")
612     @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
614     marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
615                             "#{@b2.full_name}.gemspec.rz"
616     marshal_data = Marshal.dump(@b2)
617     marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
618     @fetcher.data[marshal_uri] = util_zip marshal_data
620     yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
621     @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
623     use_ui @ui do
624       @source_index.update @uri, true
626       assert_equal @all_gem_names, @source_index.gems.keys.sort
627     end
629     paths = @fetcher.paths
630     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
631     assert_equal marshal_uri, paths.shift
632     assert_equal yaml_uri, paths.shift
634     assert paths.empty?, paths.join(', ')
635   ensure
636     Gem.configuration = old_gem_conf
637   end
639   def test_update_subdir
640     @gem_repo = @gem_repo + "/subdir"
642     util_setup_bulk_fetch true
644     @source_index.gems.replace({})
645     assert_equal [], @source_index.gems.keys.sort
647     uri = @uri.to_s + "/subdir"
649     use_ui @ui do
650       @source_index.update uri, true
652       assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
653                     @c1_2.full_name],
654                    @source_index.gems.keys.sort
655     end
657     paths = @fetcher.paths
659     assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
660     assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
662     assert paths.empty?, paths.join(', ')
663   end
665   def test_update_with_missing
666     marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
667                             "#{@c1_2.full_name}.gemspec.rz"
668     dumped = Marshal.dump @c1_2
669     @fetcher.data[marshal_uri] = util_zip(dumped)
671     use_ui @ui do
672       @source_index.update_with_missing @uri, [@c1_2.full_name]
673     end
675     spec = @source_index.specification(@c1_2.full_name)
676     # We don't care about the equality of undumped attributes
677     @c1_2.files = spec.files
678     @c1_2.loaded_from = spec.loaded_from
680     assert_equal @c1_2, spec
681   end
683   def util_setup_bulk_fetch(compressed)
684     source_index = @source_index.dump
686     if compressed then
687       @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = util_zip source_index
688     else
689       @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index
690     end
691   end