Class: Tinderbox::GemRunner
- Inherits:
-
Object
- Object
- Tinderbox::GemRunner
- Defined in:
- lib/tinderbox/gem_runner.rb
Overview
Tinderbox::GemRunner tests a gem and creates a Tinderbox::Build holding the results of the test run.
You can use tinderbox_gem_build to test your gem in a sandbox.
Defined Under Namespace
Classes: RunTimeout
Instance Attribute Summary collapse
-
#gem_name ⇒ Object
readonly
Name of the gem to test.
-
#gem_version ⇒ Object
readonly
Version of the gem to test.
-
#gemspec ⇒ Object
readonly
Gemspec of the gem to test.
-
#host_gem_dir ⇒ Object
readonly
Host’s gem repository directory.
-
#sandbox_dir ⇒ Object
readonly
Sandbox directory for rubygems.
-
#timeout ⇒ Object
Maximum time to wait for run_command to complete.
Instance Method Summary collapse
-
#gem_lib_paths ⇒ Object
The gem’s library paths.
-
#initialize(gem_name, gem_version, root = nil) ⇒ GemRunner
constructor
Creates a new GemRunner that will test the latest gem named
gem
usingroot
for the sandbox. -
#install ⇒ Object
Install the gem into the sandbox.
-
#install_rake ⇒ Object
Installs the rake gem into the sandbox.
-
#install_rspec(message) ⇒ Object
Installs the RSpec gem into the sandbox.
-
#install_sources ⇒ Object
Install the sources gem into the sandbox gem repository.
-
#passed?(process_status) ⇒ Boolean
Checks to see if #process_status exited successfully, ran at least one assertion or specification and the run finished without error or failure.
-
#rake_installed? ⇒ Boolean
Checks to see if the rake gem was installed by the gem under test.
-
#rspec_installed? ⇒ Boolean
Checks to see if the rspec gem was installed by the gem under test.
-
#ruby ⇒ Object
Path to ruby.
-
#run ⇒ Object
Sets up a sandbox, installs the gem, runs the tests and returns a Build object.
-
#run_command(command) ⇒ Object
Runs shell command
command
and records the command’s output and the time it took to run. -
#sandbox_cleanup ⇒ Object
Cleans up the gem sandbox.
-
#sandbox_setup ⇒ Object
Sets up a new gem sandbox.
-
#test ⇒ Object
Tries a best-effort at running the tests or specifications for a gem.
-
#testrb ⇒ Object
Path to testrb.
Constructor Details
#initialize(gem_name, gem_version, root = nil) ⇒ GemRunner
Creates a new GemRunner that will test the latest gem named gem
using root
for the sandbox. If no root
is given, ./tinderbox is used for the sandbox.
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 |
# File 'lib/tinderbox/gem_runner.rb', line 61 def initialize(gem_name, gem_version, root = nil) root = File.join Dir.pwd, 'tinderbox' if root.nil? raise ArgumentError, 'root must not be relative' unless root[0] == ?/ @sandbox_dir = File. File.join(root, 'sandbox') @cache_dir = File. File.join(root, 'cache') FileUtils.mkpath @cache_dir unless File.exist? @cache_dir ENV['GEM_HOME'] = nil Gem.clear_paths @host_gem_dir = Gem.dir @host_gem_source_index = Gem::SourceInfoCache.new.cache_file @gem_name = gem_name @gem_version = gem_version @remote_installer = Gem::RemoteInstaller.new :include_dependencies => true, :cache_dir => @cache_dir @remote_installer.ui = Gem::SilentUI.new @gemspec = nil @installed_gems = nil @timeout = 120 @log = '' @duration = 0 @successful = :not_tested end |
Instance Attribute Details
#gem_name ⇒ Object (readonly)
Name of the gem to test
39 40 41 |
# File 'lib/tinderbox/gem_runner.rb', line 39 def gem_name @gem_name end |
#gem_version ⇒ Object (readonly)
Version of the gem to test
44 45 46 |
# File 'lib/tinderbox/gem_runner.rb', line 44 def gem_version @gem_version end |
#gemspec ⇒ Object (readonly)
Gemspec of the gem to test
49 50 51 |
# File 'lib/tinderbox/gem_runner.rb', line 49 def gemspec @gemspec end |
#host_gem_dir ⇒ Object (readonly)
Host’s gem repository directory
34 35 36 |
# File 'lib/tinderbox/gem_runner.rb', line 34 def host_gem_dir @host_gem_dir end |
#sandbox_dir ⇒ Object (readonly)
Sandbox directory for rubygems
29 30 31 |
# File 'lib/tinderbox/gem_runner.rb', line 29 def sandbox_dir @sandbox_dir end |
#timeout ⇒ Object
Maximum time to wait for run_command to complete
54 55 56 |
# File 'lib/tinderbox/gem_runner.rb', line 54 def timeout @timeout end |
Instance Method Details
#gem_lib_paths ⇒ Object
The gem’s library paths.
92 93 94 |
# File 'lib/tinderbox/gem_runner.rb', line 92 def gem_lib_paths @gemspec.require_paths.join Config::CONFIG['PATH_SEPARATOR'] end |
#install ⇒ Object
Install the gem into the sandbox.
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 |
# File 'lib/tinderbox/gem_runner.rb', line 99 def install retries = 5 begin @installed_gems = @remote_installer.install @gem_name, @gem_version @gemspec = @installed_gems.first "### #{@installed_gems.map { |s| s.full_name }.join "\n### "}" rescue Gem::RemoteInstallationCancelled => e raise Tinderbox::ManualInstallError, "Installation of #{@gem_name}-#{@gem_version} requires manual intervention" rescue Gem::Installer::ExtensionBuildError => e raise Tinderbox::BuildError, "Unable to build #{@gem_name}-#{@gem_version}:\n\n#{e.}" rescue Gem::InstallError, Gem::GemNotFoundException => e FileUtils.rm_rf File.join(@cache_dir, "#{@gem_name}-#{@gem_version}.gem") raise Tinderbox::InstallError, "Installation of #{@gem_name}-#{@gem_version} failed (#{e.class}):\n\n#{e.}" rescue SystemCallError => e # HACK push into Rubygems retries -= 1 retry if retries >= 0 raise Tinderbox::InstallError, "Installation of #{@gem_name}-#{@gem_version} failed after 5 tries" rescue OpenURI::HTTPError => e # HACK push into Rubygems raise Tinderbox::InstallError, "Could not download #{@gem_name}-#{@gem_version}" rescue SystemStackError => e raise Tinderbox::InstallError, "Installation of #{@gem_name}-#{@gem_version} caused an infinite loop:\n\n\t#{e.backtrace.join "\n\t"}" end end |
#install_rake ⇒ Object
Installs the rake gem into the sandbox
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/tinderbox/gem_runner.rb', line 144 def install_rake log = [] log << "!!! HAS Rakefile, DOES NOT DEPEND ON RAKE! NEEDS s.add_dependency 'rake'" retries = 5 rake_version = Gem::SourceInfoCache.search(/^rake$/).last.version.to_s begin @installed_gems.push(*@remote_installer.install('rake', rake_version)) log << "### rake installed, even though you claim not to need it" rescue Gem::InstallError, Gem::GemNotFoundException => e log << "Installation of rake failed (#{e.class}):\n\n#{e.}" rescue SystemCallError => e retries -= 1 retry if retries >= 0 log << "Installation of rake failed after 5 tries" rescue OpenURI::HTTPError => e log << "Could not download rake" end @log << (log.join("\n") + "\n") end |
#install_rspec(message) ⇒ Object
Installs the RSpec gem into the sandbox
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/tinderbox/gem_runner.rb', line 171 def install_rspec() log = [] log << "!!! HAS #{}, DOES NOT DEPEND ON RSPEC! NEEDS s.add_dependency 'rspec'" retries = 5 rspec_version = Gem::SourceInfoCache.search(/^rspec$/).last.version.to_s begin @installed_gems.push(*@remote_installer.install('rspec', rspec_version)) log << "### RSpec installed, even though you claim not to need it" rescue Gem::InstallError, Gem::GemNotFoundException => e log << "Installation of RSpec failed (#{e.class}):\n\n#{e.}" rescue SystemCallError => e retries -= 1 retry if retries >= 0 log << "Installation of RSpec failed after 5 tries" rescue OpenURI::HTTPError => e log << "Could not download rspec" end @log << (log.join("\n") + "\n") end |
#install_sources ⇒ Object
Install the sources gem into the sandbox gem repository.
132 133 134 135 136 137 138 139 |
# File 'lib/tinderbox/gem_runner.rb', line 132 def install_sources sources_gem = Dir[File.join(@host_gem_dir, 'cache', 'sources-*gem')].max installer = Gem::Installer.new sources_gem installer.install FileUtils.copy @host_gem_source_index, Gem::SourceInfoCache.new.cache_file end |
#passed?(process_status) ⇒ Boolean
Checks to see if #process_status exited successfully, ran at least one assertion or specification and the run finished without error or failure.
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/tinderbox/gem_runner.rb', line 199 def passed?(process_status) tested = @log =~ /^\d+ tests, \d+ assertions, \d+ failures, \d+ errors$/ || @log =~ /^\d+ specifications?, \d+ failures?$/ @successful = process_status.exitstatus == 0 if not tested and @successful then @successful = false return tested end if @log =~ / (\d+) failures, (\d+) errors/ and ($1 != '0' or $2 != '0') then @log << "!!! Project has broken test target, exited with 0 after test failure\n" if @successful @successful = false elsif @log =~ /\d+ specifications?, (\d+) failures?$/ and $1 != '0' then @log << "!!! Project has broken spec target, exited with 0 after spec failure\n" if @successful @successful = false elsif (@log =~ / 0 assertions/ or @log !~ / \d+ assertions/) and (@log =~ /0 specifications/ or @log !~ /\d+ specification/) then @successful = false @log << "!!! No output indicating success found\n" end return tested end |
#rake_installed? ⇒ Boolean
Checks to see if the rake gem was installed by the gem under test
227 228 229 230 |
# File 'lib/tinderbox/gem_runner.rb', line 227 def rake_installed? raise 'you haven\'t installed anything yet' if @installed_gems.nil? @installed_gems.any? { |s| s.name == 'rake' } end |
#rspec_installed? ⇒ Boolean
Checks to see if the rspec gem was installed by the gem under test
235 236 237 238 |
# File 'lib/tinderbox/gem_runner.rb', line 235 def rspec_installed? raise 'you haven\'t installed anything yet' if @installed_gems.nil? @installed_gems.any? { |s| s.name == 'rspec' } end |
#ruby ⇒ Object
Path to ruby
243 244 245 246 |
# File 'lib/tinderbox/gem_runner.rb', line 243 def ruby ruby_exe = Config::CONFIG['ruby_install_name'] + Config::CONFIG['EXEEXT'] File.join Config::CONFIG['bindir'], ruby_exe end |
#run ⇒ Object
Sets up a sandbox, installs the gem, runs the tests and returns a Build object.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/tinderbox/gem_runner.rb', line 252 def run sandbox_cleanup # don't clean up at the end so we can review sandbox_setup install_sources build = Tinderbox::Build.new full_log = [] run_log = nil full_log << "### installing #{@gem_name}-#{@gem_version} + dependencies" full_log << install full_log << "### testing #{@gemspec.full_name}" test full_log << @log build.duration = @duration build.successful = @successful build.log = full_log.join "\n" return build end |
#run_command(command) ⇒ Object
Runs shell command command
and records the command’s output and the time it took to run. Returns true if evidence of a test run were found in the command output.
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/tinderbox/gem_runner.rb', line 280 def run_command(command) start = Time.now @log << "### #{command}\n" begin Timeout.timeout @timeout, RunTimeout do @log << `#{command} 2>&1` end rescue RunTimeout @log << "!!! failed to complete in under #{@timeout} seconds\n" `ruby -e 'exit 1'` # force $? end @duration += Time.now - start passed? $CHILD_STATUS end |
#sandbox_cleanup ⇒ Object
Cleans up the gem sandbox.
299 300 301 302 303 |
# File 'lib/tinderbox/gem_runner.rb', line 299 def sandbox_cleanup FileUtils.remove_dir @sandbox_dir rescue nil raise "#{@sandbox_dir} not empty" if File.exist? @sandbox_dir end |
#sandbox_setup ⇒ Object
Sets up a new gem sandbox.
308 309 310 311 312 313 314 315 316 317 |
# File 'lib/tinderbox/gem_runner.rb', line 308 def sandbox_setup raise "#{@sandbox_dir} already exists" if File.exist? @sandbox_dir FileUtils.mkpath @sandbox_dir FileUtils.mkpath File.join(@sandbox_dir, 'gems') ENV['GEM_HOME'] = @sandbox_dir Gem.clear_paths end |
#test ⇒ Object
Tries a best-effort at running the tests or specifications for a gem. The following commands are tried, and #test stops on the first evidence of a test run.
-
rake test
-
rake spec
-
make test
-
ruby -Ilib -S testrb test
-
spec spec/*
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 |
# File 'lib/tinderbox/gem_runner.rb', line 330 def test Dir.chdir @gemspec.full_gem_path do if File.exist? 'Rakefile' then install_rake unless rake_installed? return if run_command "#{ruby} -S rake test" end if File.exist? 'Rakefile' and `rake -T` =~ /^rake spec/ then install_rspec '`rake spec`' unless rspec_installed? return if run_command "#{ruby} -S rake spec" end if File.exist? 'Makefile' then return if run_command 'make test' end if File.directory? 'test' then return if run_command "#{ruby} -I#{gem_lib_paths} -S #{testrb} test" end if File.directory? 'spec' then install_rspec 'spec DIRECTORY' unless rake_installed? return if run_command "#{ruby} -S spec spec/*" end @log << "!!! could not figure out how to test #{@gemspec.full_name}" @successful = false end end |
#testrb ⇒ Object
Path to testrb
363 364 365 366 |
# File 'lib/tinderbox/gem_runner.rb', line 363 def testrb testrb_exe = 'testrb' + (RUBY_PLATFORM =~ /mswin/ ? '.bat' : '') File.join Config::CONFIG['bindir'], testrb_exe end |