1require 'rubygems/test_case'
2require 'rubygems/spec_fetcher'
3
4class TestGemSpecFetcher < Gem::TestCase
5
6  def tuple(*args)
7    Gem::NameTuple.new(*args)
8  end
9
10  def setup
11    super
12
13    @uri = URI.parse @gem_repo
14    @source = Gem::Source.new(@uri)
15
16    util_setup_fake_fetcher
17
18    @a_pre = new_spec 'a', '1.a'
19
20    install_specs @a_pre
21
22    Gem::Specification.remove_spec @b2
23
24    all = Gem::Specification.map { |spec|
25      Gem::NameTuple.new(spec.name, spec.version, spec.original_platform)
26    }.sort
27
28    @prerelease_specs, @specs = all.partition { |g| g.prerelease? }
29
30    # TODO: couldn't all of this come from the fake spec fetcher?
31    @latest_specs = Gem::Specification.latest_specs.sort.map { |spec|
32      Gem::NameTuple.new(spec.name, spec.version, spec.original_platform)
33    }
34
35    v = Gem.marshal_version
36    s_zip = util_gzip(Marshal.dump(Gem::NameTuple.to_basic(@specs)))
37    l_zip = util_gzip(Marshal.dump(Gem::NameTuple.to_basic(@latest_specs)))
38    p_zip = util_gzip(Marshal.dump(Gem::NameTuple.to_basic(@prerelease_specs)))
39    @fetcher.data["#{@gem_repo}specs.#{v}.gz"]            = s_zip
40    @fetcher.data["#{@gem_repo}latest_specs.#{v}.gz"]     = l_zip
41    @fetcher.data["#{@gem_repo}prerelease_specs.#{v}.gz"] = p_zip
42
43    @sf = Gem::SpecFetcher.new
44
45    @released = Gem::NameTuple.from_list \
46                 [["a",      Gem::Version.new("1"),   "ruby"],
47                  ["a",      Gem::Version.new("2"),   "ruby"],
48                  ["a_evil", Gem::Version.new("9"),   "ruby"],
49                  ["c",      Gem::Version.new("1.2"), "ruby"],
50                  ['dep_x',  Gem::Version.new(1),     'ruby'],
51                  ["pl",     Gem::Version.new("1"),   "i386-linux"],
52                  ['x',  Gem::Version.new(1),     'ruby']]
53  end
54
55  def test_initialize_unwritable_home_dir
56    skip 'chmod not supported' if Gem.win_platform?
57
58    FileUtils.chmod 0000, Gem.user_home
59
60    begin
61      assert Gem::SpecFetcher.new
62    ensure
63      FileUtils.chmod 0755, Gem.user_home
64    end
65  end
66
67  def test_spec_for_dependency_all
68    d = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}"
69    @fetcher.data["#{d}#{@a1.spec_name}.rz"]    = util_zip(Marshal.dump(@a1))
70    @fetcher.data["#{d}#{@a2.spec_name}.rz"]    = util_zip(Marshal.dump(@a2))
71    @fetcher.data["#{d}#{@a_pre.spec_name}.rz"] = util_zip(Marshal.dump(@a_pre))
72    @fetcher.data["#{d}#{@a3a.spec_name}.rz"]   = util_zip(Marshal.dump(@a3a))
73
74    dep = Gem::Dependency.new 'a', ">= 1"
75
76    specs_and_sources, _ = @sf.spec_for_dependency dep
77
78    spec_names = specs_and_sources.map do |spec, source_uri|
79      [spec.full_name, source_uri]
80    end
81
82    expected = [[@a1.full_name, @source], [@a2.full_name, @source]]
83
84    assert_equal expected, spec_names
85
86    assert_same specs_and_sources.first.last, specs_and_sources.last.last
87  end
88
89  def test_spec_for_dependency_latest
90    d = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}"
91    @fetcher.data["#{d}#{@a1.spec_name}.rz"]    = util_zip(Marshal.dump(@a1))
92    @fetcher.data["#{d}#{@a2.spec_name}.rz"]    = util_zip(Marshal.dump(@a2))
93    @fetcher.data["#{d}#{@a_pre.spec_name}.rz"] = util_zip(Marshal.dump(@a_pre))
94
95    dep = Gem::Dependency.new 'a'
96    specs_and_sources, _ = @sf.spec_for_dependency dep
97
98    spec_names = specs_and_sources.map do |spec, source_uri|
99      [spec.full_name, source_uri]
100    end
101
102    assert_equal [[@a2.full_name, Gem::Source.new(@gem_repo)]], spec_names
103  end
104
105  def test_spec_for_dependency_prerelease
106    d = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}"
107    @fetcher.data["#{d}#{@a1.spec_name}.rz"]    = util_zip(Marshal.dump(@a1))
108    @fetcher.data["#{d}#{@a2.spec_name}.rz"]    = util_zip(Marshal.dump(@a2))
109    @fetcher.data["#{d}#{@a_pre.spec_name}.rz"] = util_zip(Marshal.dump(@a_pre))
110
111    specs_and_sources, _ = @sf.spec_for_dependency dep('a', '1.a')
112
113    spec_names = specs_and_sources.map do |spec, source_uri|
114      [spec.full_name, source_uri]
115    end
116
117    assert_equal [[@a_pre.full_name, Gem::Source.new(@gem_repo)]], spec_names
118  end
119
120  def test_spec_for_dependency_platform
121    util_set_arch 'i386-linux'
122
123    @fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@pl1.original_name}.gemspec.rz"] =
124      util_zip(Marshal.dump(@pl1))
125
126    dep = Gem::Dependency.new 'pl', 1
127    specs_and_sources, _ = @sf.spec_for_dependency dep
128
129    spec_names = specs_and_sources.map do |spec, source_uri|
130      [spec.full_name, source_uri]
131    end
132
133    assert_equal [[@pl1.full_name, Gem::Source.new(@gem_repo)]], spec_names
134  end
135
136  def test_spec_for_dependency_mismatched_platform
137    util_set_arch 'hrpa-989'
138
139    @fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@pl1.original_name}.gemspec.rz"] =
140      util_zip(Marshal.dump(@pl1))
141
142    dep = Gem::Dependency.new 'pl', 1
143    specs_and_sources, errors = @sf.spec_for_dependency dep
144
145    assert_equal 0, specs_and_sources.size
146    assert_equal 1, errors.size
147    pmm = errors.first
148
149    assert_equal "i386-linux", pmm.platforms.first
150    assert_equal "Found pl (1), but was for platform i386-linux", pmm.wordy
151  end
152
153  def test_spec_for_dependency_bad_fetch_spec
154    src = Gem::Source.new(@gem_repo)
155    def src.fetch_spec(name)
156      raise Gem::RemoteFetcher::FetchError.new("bad news from the internet", @uri)
157    end
158
159    Gem.sources.replace [src]
160
161    d = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}"
162    @fetcher.data["#{d}#{@a1.spec_name}.rz"]    = util_zip(Marshal.dump(@a1))
163    @fetcher.data["#{d}#{@a2.spec_name}.rz"]    = util_zip(Marshal.dump(@a2))
164    @fetcher.data["#{d}#{@a_pre.spec_name}.rz"] = util_zip(Marshal.dump(@a_pre))
165    @fetcher.data["#{d}#{@a3a.spec_name}.rz"]   = util_zip(Marshal.dump(@a3a))
166
167    dep = Gem::Dependency.new 'a', ">= 1"
168
169    specs_and_sources, errors = @sf.spec_for_dependency dep
170
171    assert_equal [], specs_and_sources
172    sfp = errors.first
173
174    assert_kind_of Gem::SourceFetchProblem, sfp
175    assert_equal src, sfp.source
176    assert_equal "bad news from the internet (#{@gem_repo})", sfp.error.message
177  end
178
179  def test_available_specs_latest
180    specs, _ = @sf.available_specs(:latest)
181
182    assert_equal [@source], specs.keys
183    assert_equal @latest_specs, specs[@source].sort
184  end
185
186  def test_available_specs_released
187    specs, _ = @sf.available_specs(:released)
188
189    assert_equal [@source], specs.keys
190
191    assert_equal @released, specs[@source].sort
192  end
193
194  def test_available_specs_complete
195    specs, _ = @sf.available_specs(:complete)
196
197    assert_equal [@source], specs.keys
198
199    comp = @prerelease_specs + @released
200
201    assert_equal comp.sort, specs[@source].sort
202  end
203
204  def test_available_specs_complete_handles_no_prerelease
205    v = Gem.marshal_version
206    @fetcher.data.delete "#{@gem_repo}prerelease_specs.#{v}.gz"
207
208    specs, _ = @sf.available_specs(:complete)
209
210    assert_equal [@source], specs.keys
211
212    comp = @released
213
214    assert_equal comp.sort, specs[@source].sort
215  end
216
217
218  def test_available_specs_cache
219    specs, _ = @sf.available_specs(:latest)
220
221    refute specs[@source].empty?
222
223    @fetcher.data["#{@gem_repo}/latest_specs.#{Gem.marshal_version}.gz"] = nil
224
225    cached_specs, _ = @sf.available_specs(:latest)
226
227    assert_equal specs, cached_specs
228  end
229
230  def test_available_specs_cache_released
231    specs, _ = @sf.available_specs(:released)
232
233    refute specs[@source].empty?
234
235    @fetcher.data["#{@gem_repo}/specs.#{Gem.marshal_version}.gz"] = nil
236
237    cached_specs, _ = @sf.available_specs(:released)
238
239    assert_equal specs, cached_specs
240  end
241
242  def test_available_specs_prerelease
243    specs, _ = @sf.available_specs(:prerelease)
244
245    assert_equal @prerelease_specs, specs[@source].sort
246  end
247
248  def test_available_specs_with_bad_source
249    Gem.sources.replace ["http://not-there.nothing"]
250
251    specs, errors = @sf.available_specs(:latest)
252
253    assert_equal({}, specs)
254    assert_kind_of Gem::SourceFetchProblem, errors.first
255  end
256
257end
258
259