Gem::Command
taken straight out of rake
# File lib/rubygems/commands/test_command.rb, line 35 def initialize(spec=nil, on_install=false, gem_dir=nil) options = { } if spec options[:name] = spec.name options[:version] = spec.version end @spec = spec @gem_dir = gem_dir @on_install = on_install super 'test', description, options add_version_option add_option( '--force', 'ignore opt-in testing and just run the tests' ) do |v,o| o[:force] = true end add_option( '--dep-user-install', 'force installing the dependencies into the user path' ) do |v,o| o[:dep_user_install] = true end end
# File lib/rubygems/commands/test_command.rb, line 27 def arguments "GEM: name of gem" end
Get the config in our namespace
# File lib/rubygems/commands/test_command.rb, line 68 def config @config ||= Gem.configuration["test_options"] || { } end
# File lib/rubygems/commands/test_command.rb, line 23 def description 'Run the tests for a specific gem' end
Escapes a URI.
# File lib/rubygems/commands/test_command.rb, line 182 def escape(str) return nil unless str URI.escape str end
Execute routine. This is where the magic happens.
# File lib/rubygems/commands/test_command.rb, line 478 def execute begin version = options[:version] || Gem::Requirement.default (get_all_gem_names rescue [options[:name]]).each do |name| unless name alert_error "No gem specified." show_help terminate_interaction 1 end path, spec = if name =~ /\.gem$/ unless File.exist?(name) say "unable to find gem #{name}" next end inst = Gem::Installer.new(name) tmpdir = Dir.mktmpdir @created_tmpdir = true inst.unpack(tmpdir) unless inst.spec.extensions.empty? say "gem #{name} has extensions. Due to limitations in rubygems," say "the gem must be installed before it can be tested." next end [tmpdir, inst.spec] else if @spec and @gem_dir and File.directory?(@gem_dir) [@gem_dir, @spec] else spec = find_gem(name, version) unless spec say "unable to find gem #{name} #{version}" next end [spec.full_gem_path, spec] end end if File.exist?(File.join(path, '.gemtest')) or options[:force] # we find rake and the rakefile first to eliminate needlessly installing # dependencies. find_rakefile(path, spec) rake_path = find_rake unless $RG_T_INSTALLING_DEPENDENCIES and !config["test_development_dependencies"] install_dependencies(spec) run_tests(path, spec, rake_path) end else say "Gem '#{name}' (version #{version}) needs to opt-in for testing." say "" say "Locally available testing helps gems maintain high quality by" say "ensuring they work correctly on a wider array of platforms than the" say "original developer can access." say "" say "If you are the author: " say " * Add the file '.gemtest' to your spec.files" say " * Ensure 'rake test' works and doesn't do system damage" say " * Add your tests and Rakefile to your gem." say "" say "For more information, please see the rubygems-test README:" say "https://github.com/rubygems/rubygems-test/blob/master/README.txt" end if @created_tmpdir FileUtils.rm_rf path end end rescue Gem::TestError => e raise if @on_install terminate_interaction 1 end end
find a gem given a name and version
# File lib/rubygems/commands/test_command.rb, line 75 def find_gem(name, version) spec = Gem.source_index.find_name(name, version).last unless spec and (spec.installation_path rescue nil) alert_error "Could not find gem #{name} (#{version})" raise Gem::GemNotFoundException, "Could not find gem #{name}, (#{version})" end return spec end
Locate rake itself, prefer gems version.
# File lib/rubygems/commands/test_command.rb, line 102 def find_rake rake_path = nil; begin rake_path = Gem.bin_path('rake', 'rake') rescue if RUBY_VERSION > '1.9' and File.exist?(File.join(RbConfig::CONFIG["bindir"], Gem::Installer.exec_format % 'rake')) rake_path = File.join(RbConfig::CONFIG["bindir"], Gem::Installer.exec_format % 'rake') else alert_error "Couldn't find rake; rubygems-test will not work without it. Aborting." raise Gem::RakeNotFoundError, "Couldn't find rake; rubygems-test will not work without it." end end if RUBY_VERSION > '1.9' and !rake_path if RUBY_PLATFORM =~ /mswin/ # # XXX GarbageCollect breaks ruby -S with rake. # return File.join(RbConfig::CONFIG["bindir"], 'rake.bat') else return rake_path || 'rake' end else return rake_path end end
Locate the rakefile for a gem name and version
# File lib/rubygems/commands/test_command.rb, line 88 def find_rakefile(path, spec) rakefile = DEFAULT_RAKEFILES. map { |x| File.join(path, x) }. find { |x| File.exist?(x) } unless(File.exist?(rakefile) rescue nil) alert_error "Couldn't find rakefile -- this gem cannot be tested. Aborting." raise Gem::RakeNotFoundError, "Couldn't find rakefile, gem #{spec.name} (#{spec.version}) cannot be tested." end end
Gather system results, test results into a YAML format ready for delivery.
# File lib/rubygems/commands/test_command.rb, line 278 def gather_results(spec, output, result) { :arch => RbConfig::CONFIG["arch"], :vendor => RbConfig::CONFIG["target_vendor"], :os => RbConfig::CONFIG["target_os"], :machine_arch => RbConfig::CONFIG["target_cpu"], :name => spec.name, :version => { :release => spec.version.to_s, :prerelease => spec.version.prerelease? }, :platform => (Kernel.const_get("RUBY_ENGINE") rescue "ruby"), :ruby_version => RUBY_VERSION, :result => result, :test_output => output, :rubygems_test_version => VERSION }.to_yaml end
obtain the rake arguments for a specific platform and environment.
# File lib/rubygems/commands/test_command.rb, line 409 def get_rake_args(rake_path, *args) if RUBY_PLATFORM =~ /mswin/ and RUBY_VERSION > '1.9' # # XXX GarbageCollect breaks ruby -S with rake on 1.9. # rake_args = [ rake_path ] + args else rake_args = [ Gem.ruby, '-rubygems', '-S' ] + [ rake_path, '--' ] + args end if RUBY_PLATFORM =~ /mswin|mingw/ # we don't use shellwords for the rest because they use execve(). require 'shellwords' rake_args.map { |x| Shellwords.shellescape(x) }.join(' ') else rake_args end end
Install development dependencies for the gem we're about to test.
# File lib/rubygems/commands/test_command.rb, line 133 def install_dependencies(spec) di = nil if options[:dep_user_install] di = Gem::DependencyInstaller.new(:install_dir => Gem.user_dir) else di = Gem::DependencyInstaller.new end $RG_T_INSTALLING_DEPENDENCIES = true spec.development_dependencies.each do |dep| unless Gem.source_index.search(dep).last if config["install_development_dependencies"] || Gem.configuration.verbose == false say "Installing test dependency #{dep.name} (#{dep.requirement})" di.install(dep) else if ask_yes_no("Install development dependency #{dep.name} (#{dep.requirement})?", true) say "Installing test dependency #{dep.name} (#{dep.requirement})" di.install(dep) else alert_error "Failed to install dependencies required to run tests. Aborting." raise Gem::TestError, "dependencies not installed" end end end end $RG_T_INSTALLING_DEPENDENCIES = false true end
Normalize the URI by adding "http://" if it is missing.
# File lib/rubygems/commands/test_command.rb, line 171 def normalize_uri(uri) (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" end
platform-specific reading routines.
# File lib/rubygems/commands/test_command.rb, line 365 def platform_reader(rake_args) # jruby stuffs it under IO, so we'll use that if it's available # if we're on 1.9, use open3 regardless of platform. # If we're not: # * on windows use win32/open3 from win32-open3 gem # * on unix use open4-vendor output, exit_status = *[] if IO.respond_to?(:popen4) IO.popen4(*rake_args) do |pid, stdin, stdout, stderr| output = read_output(stdout, stderr) end exit_status = $? elsif RUBY_VERSION > '1.9' require 'open3' Open3.popen3(*rake_args) do |stdin, stdout, stderr, thr| output = read_output(stdout, stderr) exit_status = thr.value end elsif RUBY_PLATFORM =~ /mingw|mswin/ begin require 'win32/open3' Open3.popen3(*rake_args) do |stdin, stdout, stderr| output = read_output(stdout, stderr) end exit_status = $? rescue LoadError say "1.8/Windows users must install the 'win32-open3' gem to run tests" terminate_interaction 1 end else require 'open4-vendor' exit_status = Open4.popen4(*rake_args) do |pid, stdin, stdout, stderr| output = read_output(stdout, stderr) end end return output, exit_status end
if a proxy is supplied, return a URI
# File lib/rubygems/commands/test_command.rb, line 194 def proxy env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] return nil if env_proxy.nil? or env_proxy.empty? uri = URI.parse(normalize_uri(env_proxy)) if uri and uri.user.nil? and uri.password.nil? then # Probably we have http_proxy_* variables? uri.user = escape(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']) uri.password = escape(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']) end uri end
Inner loop for platform_reader
# File lib/rubygems/commands/test_command.rb, line 300 def read_output(stdout, stderr) require 'thread' [STDERR, $stderr, stderr, STDOUT, $stdout, stdout].map { |x| x.sync = true } reads = Queue.new output = "" err_t = Thread.new do while !stderr.eof? ary = [:stderr, nil, stderr.readline] ary[1] = Time.now.to_f reads << ary Thread.pass end end out_t = Thread.new do while !stdout.eof? ary = [:stdout, nil, stdout.read(1)] ary[1] = Time.now.to_f reads << ary Thread.pass end end tty_t = Thread.new do next_time = nil while true while reads.length > 0 cur_reads = [next_time || reads.shift] time = cur_reads[0][1] while next_time = reads.shift break if next_time[1] != time cur_reads << next_time end stderr_reads, stdout_reads = cur_reads.partition { |x| x[0] == :stderr } # stderr wins (stderr_reads + stdout_reads).each do |rec| output << rec[2] print rec[2] end end Thread.pass end end while !stderr.eof? or !stdout.eof? or !reads.empty? Thread.pass end sleep 1 tty_t.kill puts return output + "\n" end
Run the tests with the appropriate spec and rake_path, and capture all output.
# File lib/rubygems/commands/test_command.rb, line 433 def run_tests(path, spec, rake_path) Dir.chdir(path) do rake_args = get_rake_args(rake_path, 'test') @trapped = false ::Kernel.trap("INT") { @trapped = true } output, exit_status = platform_reader(rake_args) ::Kernel.trap("INT", "DEFAULT") if !@trapped and upload_results? upload_results(gather_results(spec, output, exit_status.exitstatus == 0)) end if exit_status.exitstatus != 0 if @trapped alert_error "You interrupted the test! Test runs are not valid unless you let them complete!" else alert_error "Tests did not pass. Examine the output and report it to the author!" end raise Gem::TestError, "tests failed" end end end
Upload yaml Results to results_url.
# File lib/rubygems/commands/test_command.rb, line 214 def upload_results(yaml, results_url=nil) begin results_url ||= config["upload_service_url"] || 'http://test.rubygems.org/test_results' url = URI.parse(results_url) net_http_args = [url.host, url.port] if proxy_uri = proxy net_http_args += [ proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password ] end http = Net::HTTP.new(*net_http_args) if ENV["RG_T_DEBUG_HTTP"] http.set_debug_output($stderr) end req = Net::HTTP::Post.new(url.path) req.set_form_data({:results => yaml}) response = http.start { |x| x.request(req) } case response when Net::HTTPSuccess body = YAML::load(response.body) if body[:success] url = body[:data][0] if body[:data] say "\nTest results posted successfully! \n\nresults url:\t#{url}\n\n" else body[:errors].each do |error| say error end if body[:errors] end when Net::HTTPRedirection location = response.fetch('Location') if !location or URI.parse(location) == url say %[Caught redirection but was unable to redirect to #{location}.] else upload_results yaml, location end when Net::HTTPNotFound say %[Unable to find where to put the test results. Try: `gem update rubygems-test`] when Net::HTTPClientError say %[Results server didn't like the results submission. Try: `gem update rubygems-test`] when Net::HTTPServerError say %[Oof. Something went wrong on the results server processing these results. Sorry!] else say %[Something weird happened. Probably a bug.] end rescue Errno::ECONNREFUSED => e say 'Unable to post test results. Can\t connect to the results server.' rescue => e say e.message say e.backtrace end end
Convenience predicate for upload_results option
# File lib/rubygems/commands/test_command.rb, line 463 def upload_results? !options[:force] and ( config["upload_results"] or ( !config.has_key?("upload_results") and Gem.configuration.verbose == false || ask_yes_no("Upload these results?", true) ) ) end
Generated with the Darkfish Rdoc Generator 2.