tinderbox 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/LICENSE.txt +27 -0
- data/Manifest.txt +14 -0
- data/README.txt +33 -0
- data/Rakefile +21 -0
- data/bin/tinderbox_gem_build +14 -0
- data/bin/tinderbox_gem_run +7 -0
- data/lib/tinderbox.rb +34 -0
- data/lib/tinderbox/build.rb +22 -0
- data/lib/tinderbox/gem_runner.rb +369 -0
- data/lib/tinderbox/gem_tinderbox.rb +285 -0
- data/test/test_tinderbox_build.rb +103 -0
- data/test/test_tinderbox_gem_runner.rb +688 -0
- data/test/test_tinderbox_gem_tinderbox.rb +236 -0
- metadata +97 -0
@@ -0,0 +1,285 @@
|
|
1
|
+
$TESTING = false unless defined? $TESTING
|
2
|
+
require 'tinderbox'
|
3
|
+
require 'tinderbox/gem_runner'
|
4
|
+
require 'tinderbox/build'
|
5
|
+
require 'rubygems/source_info_cache'
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
require 'rbconfig'
|
9
|
+
require 'socket'
|
10
|
+
|
11
|
+
require 'rubygems'
|
12
|
+
require 'firebrigade/cache'
|
13
|
+
|
14
|
+
##
|
15
|
+
# GemTinderbox is a tinderbox for RubyGems.
|
16
|
+
|
17
|
+
class Tinderbox::GemTinderbox
|
18
|
+
|
19
|
+
##
|
20
|
+
# Root directory that the GemTinderbox will use.
|
21
|
+
|
22
|
+
attr_accessor :root
|
23
|
+
|
24
|
+
##
|
25
|
+
# Timeout for GemRunner.
|
26
|
+
|
27
|
+
attr_accessor :timeout
|
28
|
+
|
29
|
+
##
|
30
|
+
# Processes +args+ into options.
|
31
|
+
|
32
|
+
def self.process_args(args)
|
33
|
+
opts_file = File.expand_path '~/.gem_tinderbox'
|
34
|
+
options = {}
|
35
|
+
|
36
|
+
if File.exist? opts_file then
|
37
|
+
File.readlines(opts_file).map { |l| l.chomp.split '=', 2 }.each do |k,v|
|
38
|
+
v = true if v == 'true'
|
39
|
+
v = false if v == 'false'
|
40
|
+
v = Integer(v) if k == 'Timeout'
|
41
|
+
options[k.intern] = v
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
options[:Daemon] ||= false
|
46
|
+
options[:Timeout] ||= 120
|
47
|
+
|
48
|
+
opts = OptionParser.new do |opts|
|
49
|
+
opts.banner = "Usage: #{File.basename $0} [options]"
|
50
|
+
opts.separator ''
|
51
|
+
opts.separator 'Options may also be set in the options file ~/.gem_tinderbox.'
|
52
|
+
opts.separator ''
|
53
|
+
opts.separator 'Example ~/.gem_tinderbox'
|
54
|
+
opts.separator "\tServer=firebrigade.example.com"
|
55
|
+
opts.separator "\tUsername=my username"
|
56
|
+
opts.separator "\tPassword=my password"
|
57
|
+
opts.separator "\tRoot=/path/to/tinderbox/root"
|
58
|
+
|
59
|
+
opts.separator ''
|
60
|
+
|
61
|
+
opts.on("-s", "--server HOST",
|
62
|
+
"Firebrigade server host",
|
63
|
+
"Default: #{options[:Server].inspect}",
|
64
|
+
"Options file name: Server") do |server|
|
65
|
+
options[:Server] = server
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on("-u", "--username USERNAME",
|
69
|
+
"Firebrigade username",
|
70
|
+
"Default: #{options[:Username].inspect}",
|
71
|
+
"Options file name: Username") do |username|
|
72
|
+
options[:Username] = username
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on("-p", "--password PASSWORD",
|
76
|
+
"Firebrigade password",
|
77
|
+
"Default: Read from ~/.gem_tinderbox",
|
78
|
+
"Options file name: Password") do |password|
|
79
|
+
options[:Password] = password
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.separator ''
|
83
|
+
|
84
|
+
opts.on("-t", "--timeout TIMEOUT",
|
85
|
+
"Maximum time to wait for a gem's tests to",
|
86
|
+
"finish",
|
87
|
+
"Default: #{options[:Timeout]}",
|
88
|
+
Numeric) do |timeout|
|
89
|
+
options[:Timeout] = timeout
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on("-r", "--root ROOT",
|
93
|
+
"Root directory for gem tinderbox",
|
94
|
+
"Default: #{options[:Root]}",
|
95
|
+
"Gems will be lit on fire here.") do |root|
|
96
|
+
options[:Root] = root
|
97
|
+
end
|
98
|
+
|
99
|
+
opts.on("-d", "--daemonize",
|
100
|
+
"Run as a daemon process",
|
101
|
+
"Default: #{options[:Daemon]}") do |daemon|
|
102
|
+
options[:Daemon] = true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
opts.version = Tinderbox::VERSION
|
107
|
+
opts.release = nil
|
108
|
+
|
109
|
+
opts.parse! args
|
110
|
+
|
111
|
+
if options[:Server].nil? or
|
112
|
+
options[:Username].nil? or
|
113
|
+
options[:Password].nil? then
|
114
|
+
$stderr.puts opts
|
115
|
+
$stderr.puts
|
116
|
+
$stderr.puts "Firebrigade Server not set" if options[:Server].nil?
|
117
|
+
$stderr.puts "Firebrigade Username not set" if options[:Username].nil?
|
118
|
+
$stderr.puts "Firebrigade Password not set" if options[:Password].nil?
|
119
|
+
exit 1
|
120
|
+
end
|
121
|
+
|
122
|
+
return options
|
123
|
+
rescue OptionParser::ParseError
|
124
|
+
$stderr.puts opts
|
125
|
+
exit 1
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Starts a GemTinderbox.
|
130
|
+
|
131
|
+
def self.run(args = ARGV)
|
132
|
+
options = process_args args
|
133
|
+
|
134
|
+
tinderbox = new options[:Server], options[:Username], options[:Password]
|
135
|
+
tinderbox.root = options[:Root]
|
136
|
+
tinderbox.timeout = options[:Timeout]
|
137
|
+
|
138
|
+
if options[:Daemon] then
|
139
|
+
require 'webrick/server'
|
140
|
+
WEBrick::Daemon.start
|
141
|
+
end
|
142
|
+
|
143
|
+
tinderbox.run
|
144
|
+
|
145
|
+
rescue Interrupt, SystemExit
|
146
|
+
exit
|
147
|
+
rescue Exception => e
|
148
|
+
puts "#{e.message}(#{e.class}):"
|
149
|
+
puts "\t#{e.backtrace.join "\n\t"}"
|
150
|
+
exit 1
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Creates a new GemTinderbox that will submit results to +host+ as
|
155
|
+
# +username+ using +password+.
|
156
|
+
|
157
|
+
def initialize(host, username, password)
|
158
|
+
@host = host
|
159
|
+
@username = username
|
160
|
+
@password = password
|
161
|
+
@root = nil
|
162
|
+
@timeout = 120
|
163
|
+
|
164
|
+
@source_info_cache = nil
|
165
|
+
@seen_gem_names = []
|
166
|
+
@wait_time = 300
|
167
|
+
|
168
|
+
@fc = Firebrigade::Cache.new @host, @username, @password
|
169
|
+
@target_id = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Finds new gems in the source_info_cache
|
174
|
+
|
175
|
+
def new_gems
|
176
|
+
update_gems
|
177
|
+
|
178
|
+
latest_gems = {}
|
179
|
+
source_info_cache.cache_data.each do |source, sic_e|
|
180
|
+
sic_e.source_index.latest_specs.each do |name, spec|
|
181
|
+
latest_gems[name] = spec
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
new_gem_names = latest_gems.keys - @seen_gem_names
|
186
|
+
|
187
|
+
@seen_gem_names = latest_gems.keys
|
188
|
+
|
189
|
+
latest_gems.values_at(*new_gem_names)
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Tests all the gems, then waits a while and tests anything that is new in
|
194
|
+
# the index. If an unhandled error is encountered, GemTinderbox waits a
|
195
|
+
# minute then starts from the beginning. (Since information is cached,
|
196
|
+
# GemTinderbox won't pound on Firebrigade.)
|
197
|
+
|
198
|
+
def run
|
199
|
+
@seen_gem_names = []
|
200
|
+
@target_id ||= @fc.get_target_id
|
201
|
+
|
202
|
+
loop do
|
203
|
+
new_gems.each do |spec| run_spec spec end
|
204
|
+
sleep @wait_time
|
205
|
+
end
|
206
|
+
rescue RCRest::CommunicationError, Gem::RemoteFetcher::FetchError,
|
207
|
+
Gem::RemoteSourceException => e
|
208
|
+
wait = Time.now + 60
|
209
|
+
|
210
|
+
$stderr.puts e.message
|
211
|
+
$stderr.puts "Will retry at #{wait}"
|
212
|
+
|
213
|
+
unless $TESTING then
|
214
|
+
sleep 60
|
215
|
+
retry
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# Runs Gem::Specification +spec+ using a GemRunner then submits the results
|
221
|
+
# to Firebrigade.
|
222
|
+
|
223
|
+
def run_spec(spec)
|
224
|
+
$stderr.puts "*** Checking #{spec.full_name}"
|
225
|
+
|
226
|
+
version_id = @fc.get_version_id spec
|
227
|
+
return if tested? version_id
|
228
|
+
|
229
|
+
$stderr.puts "*** Igniting (http://#{@host}/gem/show/#{spec.name}/#{spec.version})"
|
230
|
+
begin
|
231
|
+
build = test_gem spec
|
232
|
+
rescue Tinderbox::BuildError, Tinderbox::InstallError => e
|
233
|
+
@seen_gem_names.delete spec.full_name
|
234
|
+
$stderr.puts "*** Failed to install (#{e.class})"
|
235
|
+
return
|
236
|
+
rescue Tinderbox::InstallError => e
|
237
|
+
$stderr.puts "*** Failed to install (#{e.class}), will try again later"
|
238
|
+
return
|
239
|
+
end
|
240
|
+
|
241
|
+
if build.successful then
|
242
|
+
$stderr.puts "*** I couldn't light #{spec.full_name} on fire"
|
243
|
+
else
|
244
|
+
$stderr.puts "*** I lit #{spec.full_name} on fire!"
|
245
|
+
end
|
246
|
+
|
247
|
+
build.submit version_id, @target_id, @host, @username, @password
|
248
|
+
|
249
|
+
build
|
250
|
+
end
|
251
|
+
|
252
|
+
##
|
253
|
+
# Rubygems' source info cache
|
254
|
+
|
255
|
+
def source_info_cache
|
256
|
+
return @source_info_cache if @source_info_cache
|
257
|
+
@source_info_cache = Gem::SourceInfoCache.cache
|
258
|
+
end
|
259
|
+
|
260
|
+
##
|
261
|
+
# Tests the Gem::Specification +spec+ and returns a Build containing its
|
262
|
+
# results.
|
263
|
+
|
264
|
+
def test_gem(spec)
|
265
|
+
runner = Tinderbox::GemRunner.new spec.name, spec.version.to_s, root
|
266
|
+
runner.timeout = @timeout
|
267
|
+
runner.run
|
268
|
+
end
|
269
|
+
|
270
|
+
##
|
271
|
+
# Checks the server to see if +version_id+ has been tested.
|
272
|
+
|
273
|
+
def tested?(version_id)
|
274
|
+
!!@fc.get_build_id(version_id, @target_id)
|
275
|
+
end
|
276
|
+
|
277
|
+
##
|
278
|
+
# Refreshes Rubygems' source info cache
|
279
|
+
|
280
|
+
def update_gems
|
281
|
+
source_info_cache.refresh
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rc_rest/uri_stub'
|
4
|
+
require 'rc_rest/net_http_stub'
|
5
|
+
require 'test/zentest_assertions'
|
6
|
+
|
7
|
+
require 'tinderbox/build'
|
8
|
+
|
9
|
+
class TestTinderboxBuild < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def setup
|
12
|
+
URI::HTTP.responses = []
|
13
|
+
URI::HTTP.uris = []
|
14
|
+
|
15
|
+
Net::HTTP.params = []
|
16
|
+
Net::HTTP.paths = []
|
17
|
+
Net::HTTP.responses = []
|
18
|
+
|
19
|
+
@build = Tinderbox::Build.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_duration
|
23
|
+
@build.duration = 5
|
24
|
+
assert_equal 5, @build.duration
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_log
|
28
|
+
@build.log = 'some crap'
|
29
|
+
assert_equal 'some crap', @build.log
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_successful
|
33
|
+
@build.successful = true
|
34
|
+
assert_equal true, @build.successful
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_submit
|
38
|
+
Net::HTTP.responses << <<-EOF
|
39
|
+
<ok>
|
40
|
+
<build>
|
41
|
+
<id>100</id>
|
42
|
+
<created_on>#{Time.now}</created_on>
|
43
|
+
<duration>1.5</duration>
|
44
|
+
<guilty_party></guilty_party>
|
45
|
+
<successful>true</successful>
|
46
|
+
<target_id>100</target_id>
|
47
|
+
<version_id>101</version_id>
|
48
|
+
</build>
|
49
|
+
</ok>
|
50
|
+
EOF
|
51
|
+
|
52
|
+
@build.log = "*** blah\nfailed"
|
53
|
+
@build.successful = false
|
54
|
+
@build.duration = 1.5
|
55
|
+
|
56
|
+
srand 0
|
57
|
+
|
58
|
+
@build.submit 101, 100, 'firebrigade.example.com', 'username', 'password'
|
59
|
+
|
60
|
+
assert_empty Net::HTTP.responses
|
61
|
+
|
62
|
+
assert_equal 1, Net::HTTP.paths.length
|
63
|
+
assert_equal '/api/REST/add_build', Net::HTTP.paths.shift
|
64
|
+
|
65
|
+
assert_equal 1, Net::HTTP.params.length
|
66
|
+
|
67
|
+
expected = <<-EOF.strip
|
68
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
69
|
+
Content-Disposition: form-data; name=\"duration\"\r
|
70
|
+
\r
|
71
|
+
1.5\r
|
72
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
73
|
+
Content-Disposition: form-data; name=\"hash\"\r
|
74
|
+
\r
|
75
|
+
e99435ecca5025c0e3a6f7e98fc91b7e\r
|
76
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
77
|
+
Content-Disposition: form-data; name=\"log\"\r
|
78
|
+
\r
|
79
|
+
*** blah
|
80
|
+
failed\r
|
81
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
82
|
+
Content-Disposition: form-data; name=\"successful\"\r
|
83
|
+
\r
|
84
|
+
false\r
|
85
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
86
|
+
Content-Disposition: form-data; name=\"target_id\"\r
|
87
|
+
\r
|
88
|
+
100\r
|
89
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
90
|
+
Content-Disposition: form-data; name=\"user\"\r
|
91
|
+
\r
|
92
|
+
username\r
|
93
|
+
--ac_2f_75_c0_43_fb_c3_67\r
|
94
|
+
Content-Disposition: form-data; name=\"version_id\"\r
|
95
|
+
\r
|
96
|
+
101\r
|
97
|
+
--ac_2f_75_c0_43_fb_c3_67--
|
98
|
+
EOF
|
99
|
+
assert_equal expected, Net::HTTP.params.shift
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,688 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/zentest_assertions'
|
5
|
+
|
6
|
+
require 'rbconfig'
|
7
|
+
require 'tmpdir'
|
8
|
+
|
9
|
+
require 'tinderbox/gem_runner'
|
10
|
+
|
11
|
+
class Tinderbox::GemRunner
|
12
|
+
attr_writer :gemspec
|
13
|
+
attr_accessor :installed_gems, :remote_installer, :log
|
14
|
+
attr_reader :duration, :successful
|
15
|
+
end
|
16
|
+
|
17
|
+
class Gem::RemoteInstaller
|
18
|
+
alias orig_install install
|
19
|
+
def install(gem_name, version = '1.2.3')
|
20
|
+
full_gem_name = "#{gem_name}-#{version}"
|
21
|
+
gem_path = File.join Gem.dir, 'gems', full_gem_name
|
22
|
+
FileUtils.mkpath gem_path
|
23
|
+
s = Gem::Specification.new
|
24
|
+
s.name = gem_name
|
25
|
+
s.version = version
|
26
|
+
s.loaded_from = File.join Gem.dir, 'gems', full_gem_name
|
27
|
+
return [s]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Gem::SourceInfoCache
|
32
|
+
attr_writer :cache_data
|
33
|
+
class << self; attr_writer :cache; end
|
34
|
+
end
|
35
|
+
|
36
|
+
class TestTinderboxGemRunner < Test::Unit::TestCase
|
37
|
+
|
38
|
+
def setup
|
39
|
+
@gem_name = 'some_test_gem'
|
40
|
+
@gem_version = '1.2.3'
|
41
|
+
@gem_full_name = "#{@gem_name}-#{@gem_version}"
|
42
|
+
|
43
|
+
@rake = Gem::Specification.new
|
44
|
+
@rake.name = 'rake'
|
45
|
+
@rake.version = '999.999.999'
|
46
|
+
|
47
|
+
@rspec = Gem::Specification.new
|
48
|
+
@rspec.name = 'rspec'
|
49
|
+
@rspec.version = '999.999.999'
|
50
|
+
|
51
|
+
@gemspec = Gem::Specification.new
|
52
|
+
@gemspec.name = @gem_name
|
53
|
+
@gemspec.version = @gem_version
|
54
|
+
@gemspec.loaded_from = File.join Gem.dir, 'gems', @gem_full_name
|
55
|
+
@gemspec.require_paths = ['lib', 'other']
|
56
|
+
|
57
|
+
@root = File.join Dir.tmpdir, 'tinderbox_test'
|
58
|
+
@sandbox_dir = File.join @root, 'sandbox'
|
59
|
+
@tgr = Tinderbox::GemRunner.new @gem_name, @gem_version, @root
|
60
|
+
|
61
|
+
@util_test_setup = false
|
62
|
+
end
|
63
|
+
|
64
|
+
def teardown
|
65
|
+
FileUtils.remove_dir @root rescue nil
|
66
|
+
ENV['GEM_HOME'] = nil
|
67
|
+
Gem.clear_paths
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_gem_lib_paths
|
71
|
+
@tgr.gemspec = @gemspec
|
72
|
+
assert_equal "lib#{Config::CONFIG['PATH_SEPARATOR']}other",
|
73
|
+
@tgr.gem_lib_paths
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_initialize
|
77
|
+
assert_equal @sandbox_dir, @tgr.sandbox_dir
|
78
|
+
assert_equal File.join(Config::CONFIG['libdir'], 'ruby', 'gems',
|
79
|
+
Config::CONFIG['ruby_version']),
|
80
|
+
@tgr.host_gem_dir
|
81
|
+
assert_equal @gem_name, @tgr.gem_name
|
82
|
+
assert_equal @gem_version, @tgr.gem_version
|
83
|
+
assert_equal nil, @tgr.gemspec
|
84
|
+
|
85
|
+
e = assert_raise ArgumentError do
|
86
|
+
tgr = Tinderbox::GemRunner.new @gem_name, @gem_version, 'relative'
|
87
|
+
end
|
88
|
+
|
89
|
+
assert_equal 'root must not be relative', e.message
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_install
|
93
|
+
@tgr.sandbox_setup
|
94
|
+
@tgr.install_sources
|
95
|
+
@tgr.install
|
96
|
+
|
97
|
+
deny_empty Dir[File.join(@sandbox_dir, 'gems', @gem_full_name)]
|
98
|
+
assert_equal true, File.directory?(File.join(@root, 'cache'))
|
99
|
+
assert_equal @gem_full_name, @tgr.gemspec.full_name
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_install_bad_gem
|
103
|
+
ri = @tgr.remote_installer
|
104
|
+
def ri.install(*a) raise Gem::InstallError end
|
105
|
+
|
106
|
+
@tgr.sandbox_setup
|
107
|
+
@tgr.install_sources
|
108
|
+
assert_raise Tinderbox::InstallError do
|
109
|
+
@tgr.install
|
110
|
+
end
|
111
|
+
|
112
|
+
assert_empty Dir[File.join(@sandbox_dir, 'gems', @gem_full_name)]
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_install_ext_build_error
|
116
|
+
ri = @tgr.remote_installer
|
117
|
+
def ri.install(*a) raise Gem::Installer::ExtensionBuildError end
|
118
|
+
|
119
|
+
@tgr.sandbox_setup
|
120
|
+
@tgr.install_sources
|
121
|
+
assert_raise Tinderbox::BuildError do
|
122
|
+
@tgr.install
|
123
|
+
end
|
124
|
+
|
125
|
+
assert_empty Dir[File.join(@sandbox_dir, 'gems', @gem_full_name)]
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_install_wrong_platform
|
129
|
+
ri = @tgr.remote_installer
|
130
|
+
def ri.install(*a) raise Gem::RemoteInstallationCancelled end
|
131
|
+
|
132
|
+
@tgr.sandbox_setup
|
133
|
+
@tgr.install_sources
|
134
|
+
|
135
|
+
assert_raise Tinderbox::ManualInstallError do
|
136
|
+
@tgr.install
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_install_rake
|
141
|
+
o = Object.new
|
142
|
+
def o.search(pattern)
|
143
|
+
rake = Gem::Specification.new
|
144
|
+
rake.name = 'rake'
|
145
|
+
rake.version = '999.999.999'
|
146
|
+
rake
|
147
|
+
end
|
148
|
+
sic = Gem::SourceInfoCache.new
|
149
|
+
sic_e = Gem::SourceInfoCacheEntry.new o, 0
|
150
|
+
sic.cache_data = { 'foo' => sic_e }
|
151
|
+
Gem::SourceInfoCache.cache = sic
|
152
|
+
|
153
|
+
@tgr.sandbox_setup
|
154
|
+
@tgr.installed_gems = []
|
155
|
+
@tgr.install_sources
|
156
|
+
log = @tgr.install_rake
|
157
|
+
|
158
|
+
deny_empty Dir[File.join(@sandbox_dir, 'gems', 'rake-*')]
|
159
|
+
|
160
|
+
expected = <<-EOF
|
161
|
+
!!! HAS Rakefile, DOES NOT DEPEND ON RAKE! NEEDS s.add_dependency 'rake'
|
162
|
+
### rake installed, even though you claim not to need it
|
163
|
+
EOF
|
164
|
+
|
165
|
+
assert_equal expected, log
|
166
|
+
ensure
|
167
|
+
Gem::SourceInfoCache.instance_variable_set :@cache, nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_install_rspec
|
171
|
+
o = Object.new
|
172
|
+
def o.search(pattern)
|
173
|
+
rake = Gem::Specification.new
|
174
|
+
rake.name = 'rspec'
|
175
|
+
rake.version = '999.999.999'
|
176
|
+
rake
|
177
|
+
end
|
178
|
+
sic = Gem::SourceInfoCache.new
|
179
|
+
sic_e = Gem::SourceInfoCacheEntry.new o, 0
|
180
|
+
sic.cache_data = { 'foo' => sic_e }
|
181
|
+
Gem::SourceInfoCache.cache = sic
|
182
|
+
|
183
|
+
@tgr.sandbox_setup
|
184
|
+
@tgr.installed_gems = []
|
185
|
+
@tgr.install_sources
|
186
|
+
log = @tgr.install_rspec 'message'
|
187
|
+
|
188
|
+
deny_empty Dir[File.join(@sandbox_dir, 'gems', 'rspec-*')]
|
189
|
+
|
190
|
+
expected = <<-EOF
|
191
|
+
!!! HAS message, DOES NOT DEPEND ON RSPEC! NEEDS s.add_dependency 'rspec'
|
192
|
+
### RSpec installed, even though you claim not to need it
|
193
|
+
EOF
|
194
|
+
|
195
|
+
assert_equal expected, log
|
196
|
+
ensure
|
197
|
+
Gem::SourceInfoCache.instance_variable_set :@cache, nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_install_sources
|
201
|
+
@tgr.sandbox_setup
|
202
|
+
@tgr.install_sources
|
203
|
+
|
204
|
+
assert_equal true, File.exist?(File.join(@sandbox_dir, 'source_cache'))
|
205
|
+
deny_empty Dir["#{File.join @sandbox_dir, 'gems', 'sources'}-*"]
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_passed_eh
|
209
|
+
status = Object.new
|
210
|
+
def status.exitstatus() 0; end
|
211
|
+
|
212
|
+
log = ""
|
213
|
+
tested = @tgr.passed? status
|
214
|
+
|
215
|
+
assert_equal "", @tgr.log
|
216
|
+
|
217
|
+
deny tested
|
218
|
+
deny @tgr.successful
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_passed_eh_spec
|
222
|
+
status = Object.new
|
223
|
+
def status.exitstatus() 0; end
|
224
|
+
|
225
|
+
@tgr.log = "1 specification, 0 failures\n"
|
226
|
+
tested = @tgr.passed? status
|
227
|
+
|
228
|
+
assert_equal "1 specification, 0 failures\n", @tgr.log
|
229
|
+
|
230
|
+
assert tested
|
231
|
+
assert @tgr.successful
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_passed_eh_spec_broken
|
235
|
+
status = Object.new
|
236
|
+
def status.exitstatus() 0; end
|
237
|
+
|
238
|
+
@tgr.log = "1 specification, 1 failure\n"
|
239
|
+
tested = @tgr.passed? status
|
240
|
+
|
241
|
+
expected = <<-EOF
|
242
|
+
1 specification, 1 failure
|
243
|
+
!!! Project has broken spec target, exited with 0 after spec failure
|
244
|
+
EOF
|
245
|
+
|
246
|
+
assert_equal expected, @tgr.log
|
247
|
+
|
248
|
+
assert tested
|
249
|
+
deny @tgr.successful
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_passed_eh_spec_exit_failed
|
253
|
+
status = Object.new
|
254
|
+
def status.exitstatus() 1; end
|
255
|
+
|
256
|
+
@tgr.log = "1 specification, 0 failures\n"
|
257
|
+
tested = @tgr.passed? status
|
258
|
+
assert_equal "1 specification, 0 failures\n", @tgr.log
|
259
|
+
|
260
|
+
assert tested
|
261
|
+
deny @tgr.successful
|
262
|
+
end
|
263
|
+
|
264
|
+
def test_passed_eh_spec_no_assertions
|
265
|
+
status = Object.new
|
266
|
+
def status.exitstatus() 0; end
|
267
|
+
|
268
|
+
@tgr.log = "0 specifications, 0 failures\n"
|
269
|
+
tested = @tgr.passed? status
|
270
|
+
|
271
|
+
expected = <<-EOF
|
272
|
+
0 specifications, 0 failures
|
273
|
+
!!! No output indicating success found
|
274
|
+
EOF
|
275
|
+
|
276
|
+
assert_equal expected, @tgr.log
|
277
|
+
|
278
|
+
assert tested
|
279
|
+
deny @tgr.successful
|
280
|
+
end
|
281
|
+
|
282
|
+
def test_passed_eh_test
|
283
|
+
status = Object.new
|
284
|
+
def status.exitstatus() 0; end
|
285
|
+
|
286
|
+
@tgr.log = "1 tests, 1 assertions, 0 failures, 0 errors\n"
|
287
|
+
tested = @tgr.passed? status
|
288
|
+
|
289
|
+
assert_equal "1 tests, 1 assertions, 0 failures, 0 errors\n", @tgr.log
|
290
|
+
|
291
|
+
assert tested
|
292
|
+
assert @tgr.successful
|
293
|
+
end
|
294
|
+
|
295
|
+
def test_passed_eh_test_broken
|
296
|
+
status = Object.new
|
297
|
+
def status.exitstatus() 0; end
|
298
|
+
|
299
|
+
@tgr.log = "1 tests, 1 assertions, 1 failures, 0 errors\n"
|
300
|
+
tested = @tgr.passed? status
|
301
|
+
|
302
|
+
expected = <<-EOF
|
303
|
+
1 tests, 1 assertions, 1 failures, 0 errors
|
304
|
+
!!! Project has broken test target, exited with 0 after test failure
|
305
|
+
EOF
|
306
|
+
|
307
|
+
assert_equal expected, @tgr.log
|
308
|
+
|
309
|
+
assert tested
|
310
|
+
deny @tgr.successful
|
311
|
+
end
|
312
|
+
|
313
|
+
def test_passed_eh_test_exit_failed
|
314
|
+
status = Object.new
|
315
|
+
def status.exitstatus() 1; end
|
316
|
+
|
317
|
+
@tgr.log = "1 tests, 1 assertions, 0 failures, 0 errors\n"
|
318
|
+
tested = @tgr.passed? status
|
319
|
+
assert_equal "1 tests, 1 assertions, 0 failures, 0 errors\n", @tgr.log
|
320
|
+
|
321
|
+
assert tested
|
322
|
+
deny @tgr.successful
|
323
|
+
end
|
324
|
+
|
325
|
+
def test_passed_eh_test_no_assertions
|
326
|
+
status = Object.new
|
327
|
+
def status.exitstatus() 0; end
|
328
|
+
|
329
|
+
@tgr.log = "1 tests, 0 assertions, 0 failures, 0 errors\n"
|
330
|
+
tested = @tgr.passed? status
|
331
|
+
|
332
|
+
expected = <<-EOF
|
333
|
+
1 tests, 0 assertions, 0 failures, 0 errors
|
334
|
+
!!! No output indicating success found
|
335
|
+
EOF
|
336
|
+
|
337
|
+
assert_equal expected, @tgr.log
|
338
|
+
|
339
|
+
assert tested
|
340
|
+
deny @tgr.successful
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_rake_installed_eh
|
344
|
+
e = assert_raises RuntimeError do
|
345
|
+
@tgr.rake_installed?
|
346
|
+
end
|
347
|
+
|
348
|
+
assert_equal 'you haven\'t installed anything yet', e.message
|
349
|
+
|
350
|
+
@tgr.installed_gems = []
|
351
|
+
|
352
|
+
assert_equal false, @tgr.rake_installed?
|
353
|
+
|
354
|
+
@tgr.installed_gems = [@rake]
|
355
|
+
|
356
|
+
assert_equal true, @tgr.rake_installed?
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_rspec_installed_eh
|
360
|
+
e = assert_raises RuntimeError do
|
361
|
+
@tgr.rspec_installed?
|
362
|
+
end
|
363
|
+
|
364
|
+
assert_equal 'you haven\'t installed anything yet', e.message
|
365
|
+
|
366
|
+
@tgr.installed_gems = []
|
367
|
+
|
368
|
+
assert_equal false, @tgr.rspec_installed?
|
369
|
+
|
370
|
+
@tgr.installed_gems = [@rspec]
|
371
|
+
|
372
|
+
assert_equal true, @tgr.rspec_installed?
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_ruby # HACK lame
|
376
|
+
ruby_exe = Config::CONFIG['ruby_install_name'] + Config::CONFIG['EXEEXT']
|
377
|
+
ruby = File.join Config::CONFIG['bindir'], ruby_exe
|
378
|
+
|
379
|
+
assert_equal ruby, @tgr.ruby
|
380
|
+
end
|
381
|
+
|
382
|
+
def test_run
|
383
|
+
build = @tgr.run
|
384
|
+
|
385
|
+
assert_equal true, File.exist?(File.join(@sandbox_dir, 'source_cache'))
|
386
|
+
|
387
|
+
assert_equal 0, build.duration
|
388
|
+
assert_equal false, build.successful
|
389
|
+
|
390
|
+
expected = <<-EOF.strip
|
391
|
+
### installing some_test_gem-1.2.3 + dependencies
|
392
|
+
### some_test_gem-1.2.3
|
393
|
+
### testing some_test_gem-1.2.3
|
394
|
+
!!! could not figure out how to test some_test_gem-1.2.3
|
395
|
+
EOF
|
396
|
+
|
397
|
+
assert_equal expected, build.log
|
398
|
+
end
|
399
|
+
|
400
|
+
def test_run_pass
|
401
|
+
def @tgr.test
|
402
|
+
@log = "passed!"
|
403
|
+
@successful = true
|
404
|
+
@duration = 1.0
|
405
|
+
nil
|
406
|
+
end
|
407
|
+
|
408
|
+
build = @tgr.run
|
409
|
+
|
410
|
+
assert_equal true, File.exist?(File.join(@sandbox_dir, 'source_cache'))
|
411
|
+
|
412
|
+
assert_equal 1.0, build.duration
|
413
|
+
assert_equal true, build.successful
|
414
|
+
|
415
|
+
expected = <<-EOF.strip
|
416
|
+
### installing some_test_gem-1.2.3 + dependencies
|
417
|
+
### some_test_gem-1.2.3
|
418
|
+
### testing some_test_gem-1.2.3
|
419
|
+
passed!
|
420
|
+
EOF
|
421
|
+
|
422
|
+
assert_equal expected, build.log
|
423
|
+
end
|
424
|
+
|
425
|
+
def test_run_command
|
426
|
+
tested = @tgr.run_command "ruby -e '$stderr.puts \"bye\"; $stdout.puts \"hi\"'"
|
427
|
+
|
428
|
+
expected = <<-EOF
|
429
|
+
### ruby -e '$stderr.puts \"bye\"; $stdout.puts \"hi\"'
|
430
|
+
bye
|
431
|
+
hi
|
432
|
+
EOF
|
433
|
+
|
434
|
+
assert_equal expected, @tgr.log
|
435
|
+
assert_operator 0, :<, @tgr.duration
|
436
|
+
deny tested
|
437
|
+
end
|
438
|
+
|
439
|
+
def test_run_command_tested
|
440
|
+
tested = @tgr.run_command "ruby -e \"puts '1 specification, 1 failure'\""
|
441
|
+
|
442
|
+
assert tested, @tgr.log
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_sandbox_cleanup
|
446
|
+
FileUtils.mkpath @sandbox_dir
|
447
|
+
|
448
|
+
assert_equal true, File.exist?(@sandbox_dir)
|
449
|
+
|
450
|
+
@tgr.sandbox_cleanup
|
451
|
+
|
452
|
+
assert_equal false, File.exist?(@sandbox_dir)
|
453
|
+
end
|
454
|
+
|
455
|
+
def test_sandbox_cleanup_no_dir
|
456
|
+
assert_equal false, File.exist?(@sandbox_dir)
|
457
|
+
|
458
|
+
@tgr.sandbox_cleanup
|
459
|
+
|
460
|
+
assert_equal false, File.exist?(@sandbox_dir)
|
461
|
+
end
|
462
|
+
|
463
|
+
def test_sandbox_setup
|
464
|
+
@tgr.sandbox_setup
|
465
|
+
|
466
|
+
assert_equal true, File.exist?(@sandbox_dir)
|
467
|
+
assert_equal true, File.exist?(File.join(@sandbox_dir, 'gems'))
|
468
|
+
assert_equal @sandbox_dir, ENV['GEM_HOME']
|
469
|
+
assert_equal @sandbox_dir, Gem.dir
|
470
|
+
|
471
|
+
e = assert_raise RuntimeError do
|
472
|
+
@tgr.sandbox_setup
|
473
|
+
end
|
474
|
+
|
475
|
+
assert_equal "#{@sandbox_dir} already exists", e.message
|
476
|
+
end
|
477
|
+
|
478
|
+
def test_test_best_effort
|
479
|
+
util_test_setup
|
480
|
+
|
481
|
+
File.open File.join(@gemspec.full_gem_path, 'Rakefile'), 'w' do |fp|
|
482
|
+
fp.write <<-EOF
|
483
|
+
require 'rake/testtask'
|
484
|
+
|
485
|
+
task :test do exit 1 end
|
486
|
+
EOF
|
487
|
+
end
|
488
|
+
|
489
|
+
File.open File.join(@gemspec.full_gem_path, 'Makefile'), 'w' do |fp|
|
490
|
+
fp.write <<-EOF
|
491
|
+
test:
|
492
|
+
\texit 1
|
493
|
+
|
494
|
+
.PHONY: test
|
495
|
+
EOF
|
496
|
+
end
|
497
|
+
|
498
|
+
util_test_assertions :test, true, -1
|
499
|
+
end
|
500
|
+
|
501
|
+
def test_test_no_tests
|
502
|
+
util_test_setup
|
503
|
+
|
504
|
+
@tgr.gemspec = @gemspec
|
505
|
+
@tgr.test
|
506
|
+
|
507
|
+
assert_equal 0, @tgr.duration
|
508
|
+
assert_equal false, @tgr.successful
|
509
|
+
|
510
|
+
expected = <<-EOF.strip
|
511
|
+
!!! could not figure out how to test some_test_gem-1.2.3
|
512
|
+
EOF
|
513
|
+
|
514
|
+
assert_equal expected, @tgr.log
|
515
|
+
end
|
516
|
+
|
517
|
+
def test_test_Makefile_fail
|
518
|
+
util_test_add_Makefile
|
519
|
+
util_test_assertions :test, false, -2
|
520
|
+
end
|
521
|
+
|
522
|
+
def test_test_Makefile_pass
|
523
|
+
util_test_add_Makefile
|
524
|
+
util_test_assertions :test, true, -1
|
525
|
+
end
|
526
|
+
|
527
|
+
def test_test_Rakefile_spec_fail
|
528
|
+
util_test_add_Rakefile :spec
|
529
|
+
util_test_assertions :spec, false, -1
|
530
|
+
end
|
531
|
+
|
532
|
+
def test_test_Rakefile_spec_pass
|
533
|
+
util_test_add_Rakefile :spec
|
534
|
+
util_test_assertions :spec, true, -1
|
535
|
+
end
|
536
|
+
|
537
|
+
def test_test_Rakefile_test_fail
|
538
|
+
util_test_add_Rakefile
|
539
|
+
util_test_assertions :test, false, -1
|
540
|
+
end
|
541
|
+
|
542
|
+
def test_test_Rakefile_test_pass
|
543
|
+
util_test_add_Rakefile
|
544
|
+
util_test_assertions :test, true, -1
|
545
|
+
end
|
546
|
+
|
547
|
+
def test_test_testrb_fail
|
548
|
+
util_test_assertions :test, false, -1
|
549
|
+
end
|
550
|
+
|
551
|
+
def test_test_testrb_pass
|
552
|
+
util_test_assertions :test, true, -1
|
553
|
+
|
554
|
+
assert_equal "### #{@tgr.ruby} -Ilib:other -S #{@tgr.testrb} test",
|
555
|
+
@tgr.log.split("\n").first
|
556
|
+
end
|
557
|
+
|
558
|
+
def test_test_spec_fail
|
559
|
+
util_test_assertions :spec, false, -1
|
560
|
+
end
|
561
|
+
|
562
|
+
def test_test_spec_pass
|
563
|
+
util_test_assertions :spec, true, -1
|
564
|
+
end
|
565
|
+
|
566
|
+
def test_testrb # HACK lame
|
567
|
+
testrb_exe = 'testrb' + (RUBY_PLATFORM =~ /mswin/ ? '.bat' : '')
|
568
|
+
testrb = File.join Config::CONFIG['bindir'], testrb_exe
|
569
|
+
|
570
|
+
assert_equal testrb, @tgr.testrb
|
571
|
+
end
|
572
|
+
|
573
|
+
def util_test_add_Makefile
|
574
|
+
util_test_setup
|
575
|
+
File.open File.join(@gemspec.full_gem_path, 'Makefile'), 'w' do |fp|
|
576
|
+
fp.write <<-EOF
|
577
|
+
test:
|
578
|
+
\ttestrb test
|
579
|
+
|
580
|
+
.PHONY: test
|
581
|
+
EOF
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
def util_test_add_Rakefile(type = :test)
|
586
|
+
util_test_setup
|
587
|
+
File.open File.join(@gemspec.full_gem_path, 'Rakefile'), 'w' do |fp|
|
588
|
+
fp.write <<-EOF
|
589
|
+
require 'rake/testtask'
|
590
|
+
require 'spec/rake/spectask
|
591
|
+
|
592
|
+
EOF
|
593
|
+
|
594
|
+
case type
|
595
|
+
when :test then
|
596
|
+
fp.write <<-EOF
|
597
|
+
Rake::TestTask.new :test do |t|
|
598
|
+
t.test_files = FileList['test/test_*.rb']
|
599
|
+
end
|
600
|
+
EOF
|
601
|
+
when :spec then
|
602
|
+
fp.write <<-EOF
|
603
|
+
Spec::Rake::SpecTask.new
|
604
|
+
EOF
|
605
|
+
else
|
606
|
+
raise ArgumentError, "unknown type #{type.inspect}"
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def util_test_add_test(passes)
|
612
|
+
test_dir = File.join @gemspec.full_gem_path, 'test'
|
613
|
+
@test_file = File.join test_dir, 'test_something.rb'
|
614
|
+
FileUtils.mkpath test_dir
|
615
|
+
|
616
|
+
File.open @test_file, 'w' do |fp|
|
617
|
+
fp.write <<-EOF
|
618
|
+
require 'test/unit'
|
619
|
+
|
620
|
+
class TestSomething < Test::Unit::TestCase
|
621
|
+
def test_something
|
622
|
+
assert #{passes}
|
623
|
+
end
|
624
|
+
end
|
625
|
+
EOF
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
def util_test_add_spec(passes)
|
630
|
+
test_dir = File.join @gemspec.full_gem_path, 'spec'
|
631
|
+
@spec_file = File.join test_dir, 'true_spec.rb'
|
632
|
+
FileUtils.mkpath test_dir
|
633
|
+
|
634
|
+
File.open @spec_file, 'w' do |fp|
|
635
|
+
fp.write <<-EOF
|
636
|
+
context "dummy spec" do
|
637
|
+
specify "should be true" do
|
638
|
+
#{passes}.should == true
|
639
|
+
end
|
640
|
+
end
|
641
|
+
EOF
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
def util_test_assertions(type, passes, count_index)
|
646
|
+
util_test_setup
|
647
|
+
case type
|
648
|
+
when :test then util_test_add_test passes
|
649
|
+
when :spec then util_test_add_spec passes
|
650
|
+
else raise ArgumentError, "unknown test type #{type.inspect}"
|
651
|
+
end
|
652
|
+
|
653
|
+
@tgr.gemspec = @gemspec
|
654
|
+
|
655
|
+
ENV['GEM_HOME'] = nil
|
656
|
+
successful = @tgr.test
|
657
|
+
|
658
|
+
assert_operator 0, :<, @tgr.duration, 'Time taken too short'
|
659
|
+
assert_equal passes, @tgr.successful, 'The tests failed'
|
660
|
+
|
661
|
+
log = @tgr.log.split "\n"
|
662
|
+
|
663
|
+
failures = passes ? 0 : 1
|
664
|
+
|
665
|
+
expected = case type
|
666
|
+
when :test then
|
667
|
+
"1 tests, 1 assertions, #{failures} failures, 0 errors"
|
668
|
+
when :spec then
|
669
|
+
"1 specification, #{failures} failure#{passes ? 's' : ''}"
|
670
|
+
end
|
671
|
+
|
672
|
+
#log.each_with_index do |l, i| puts "#{i}\t#{l[0..10]}" end
|
673
|
+
assert_equal expected, log[count_index]
|
674
|
+
end
|
675
|
+
|
676
|
+
def util_test_setup
|
677
|
+
return if @util_test_setup
|
678
|
+
@util_test_setup = true
|
679
|
+
@tgr.installed_gems = [@rake]
|
680
|
+
|
681
|
+
ENV['GEM_HOME'] = @sandbox_dir
|
682
|
+
Gem.clear_paths
|
683
|
+
@gemspec.loaded_from = File.join Gem.dir, 'gems', @gem_full_name
|
684
|
+
FileUtils.mkpath @gemspec.full_gem_path
|
685
|
+
end
|
686
|
+
|
687
|
+
end
|
688
|
+
|