1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
|
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
#
# # 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_reader :api_endpoints
attr_accessor :paths
def initialize
@data = {}
@paths = []
@api_endpoints = {}
end
def api_endpoint(uri)
@api_endpoints[uri] || uri
end
def find_data(path)
return File.read path.path if URI === path and 'file' == path.scheme
if URI === path and "URI::#{path.scheme.upcase}" != path.class.name then
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 =~ %r'^https?://'
unless @data.key? path then
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
end
@data[path]
end
def fetch_path path, mtime = nil, head = false
data = find_data(path)
if data.respond_to?(:call) then
data.call
else
if path.to_s =~ /gz$/ and not data.nil? and not data.empty? then
data = Gem.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
@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
unless @api_endpoints.empty? then
q.breakable
q.text 'API endpoints:'
q.breakable
q.pp @api_endpoints.keys
end
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 then
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 then # see fetch_command
install_dir
else
File.join install_dir, "cache"
end
path = File.join path, name
if source_uri =~ /^http/ then
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'
# f.clear
# 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 = {}
@installed = []
@operations = []
end
##
# Removes any created gems or specifications from Gem.dir (the default
# install location).
def clear
@operations << [:clear]
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|
case operation
when :clear then
@test.util_clear_gems
@installed.clear
when :gem then
spec, gem = @test.util_gem(*arguments, &arguments.pop)
write_spec spec
@gems[spec] = gem
@installed << spec
when :spec then
spec = @test.util_spec(*arguments, &arguments.pop)
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 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 then
@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(*@gems.keys)
ensure
@test.gem_repo = gem_repo
@test.uri = URI gem_repo
end
# This works around util_setup_spec_fetcher adding all created gems to the
# installed set.
Gem::Specification.reset
Gem::Specification.add_specs(*@installed)
@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:
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
|