├── .gemtest ├── .gitignore ├── History.txt ├── LICENSE ├── Manifest.txt ├── README.txt ├── Rakefile ├── gems ├── Rakefile ├── template.gemspec └── test │ └── test_pass.rb ├── lib ├── exceptions.rb ├── open4-vendor.rb ├── rubygems │ ├── commands │ │ └── test_command.rb │ └── on_install_test.rb └── rubygems_plugin.rb └── test ├── helper.rb ├── interactive_test_on-install.rb └── test_execute.rb /.gemtest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubygems/rubygems-test/1110304006cc2b6379d571a71f063e61c2527439/.gemtest -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## MAC OS 2 | .DS_Store 3 | 4 | ## TEXTMATE 5 | *.tmproj 6 | tmtags 7 | 8 | ## EMACS 9 | *~ 10 | \#* 11 | .\#* 12 | 13 | ## VIM 14 | *.swp 15 | 16 | ## PROJECT::GENERAL 17 | coverage 18 | rdoc 19 | pkg 20 | 21 | ## PROJECT::SPECIFIC 22 | gems/*.gem 23 | *.rbc 24 | 25 | coverage.info 26 | proxy.rb 27 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | === 0.4.3 / 2011-05-20 2 | 3 | * Thread.pass in a few spots fixes hangs on linux. Version bump 4 | 5 | === 0.4.2 / 2011-05-09 6 | 7 | * Fix operation on 1.8.x 8 | 9 | === 0.4.1 / 2011-04-26 10 | 11 | * Fix for situations during on-install testing where the gem dir is not available (f.e., on a new installation) 12 | 13 | === 0.4.0 / 2011-04-23 14 | 15 | * Make rc1 official 0.4.0 16 | 17 | === 0.4.0.rc1 / 2011-04-17 18 | 19 | * Pretty much nailed the reader problem to the wall. 20 | * Now able to test .gem files directly on the commandline. 21 | * Use the new hooks to uninstall gems if they fail testing. Bump the rubygems 22 | requirement to 1.5 to reflect above changes. 23 | * Specify the exec_name for Gem.bin_path. [Postmodern] 24 | 25 | === 0.3.11 / 2011-03-13 26 | 27 | * Now sending :rubygems_test_version which is the version of rubygems-test used for the upload. 28 | 29 | === 0.3.10 / 2011-03-08 30 | 31 | * Fixed an issue locating the .gemtest file. 32 | 33 | === 0.3.9 / 2011-03-08 34 | 35 | * Fix for a bug where rake19, etc would not be located. 36 | 37 | === 0.3.8 / 2011-03-03 38 | 39 | * Fix path handling due to changes on rubygems 1.6 40 | 41 | === 0.3.7 / 2011-03-02 42 | 43 | * Support for proxy environment variables. If it works on rubygems, it probably works here now too! 44 | 45 | === 0.3.6 / 2011-02-27 46 | 47 | * Moved publish host default to test.rubygems.org 48 | 49 | === 0.3.5 / 2011-02-11 50 | 51 | * rebuild of rubygems-test 0.3.4 on rubygems 1.5.2 52 | 53 | === 0.3.2 / 2011-02-05 54 | 55 | * Trap sigint and refuse to upload 56 | 57 | === 0.3.1 / 2011-02-03 58 | 59 | * Formatted upload results output differently. Thanks postmodern. 60 | * Added a handy little message to tell people about the failure testing. Thanks Rick Hull. 61 | * If sudo was required to install gems, 'gem test' without sudo would fail. Fixed. 62 | * Set minimum rubygems version; should solve #10. 63 | * Install development dependencies without prompt if in quiet mode [Guillermo Álvarez] 64 | * Ensure quiet mode does not ask any question. [Guillermo Álvarez] 65 | 66 | === 0.3.0 / 2011-01-30 67 | 68 | * More fixes for garbagecollect 69 | * Removed --trace. It was buggy and didn't work as expected. 70 | * Reworked I/O loop. Some testing to do still but it's looking good for most cases. 71 | * Fixed issue with on_install_test.rb, solves issue #11 72 | * Fixes for Rake; should be a little cleaner and run in a few more places. 73 | * Use temporary paths rather than system paths during tests. [James Tucker] 74 | * Clarify predicates. 75 | 76 | === 0.2.6 / 2011-01-15 77 | 78 | * Big refactor of rake locating to get running on mswin32 79 | 80 | === 0.2.5 / 1/15/2011 81 | 82 | * use the input record separator instead of straight newlines - windows! 83 | 84 | * Yyyyyyyyyyeah. So 0.2.4 wasn't able to upload results at all. 85 | 86 | === 0.2.4 / 1/13/2011 87 | 88 | * Better I/O loop and terminal handling; test/unit dots should appear as they 89 | arrive again. 90 | 91 | * Better help for those who provide no gem names. 92 | 93 | === 0.2.2 / 1/10/2011 94 | 95 | * Windows + 1.8 support. Requires the 'win32-open3' gem. 96 | 97 | * --force option - run tests even if the gem author hasn't opted in. Do not 98 | upload these results ever. 99 | 100 | * --dep-user-install option - install any development dependencies to 101 | Gem.user_dir 102 | 103 | === 0.1.9 / Sometime before hoe 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Erik Hollensbe and Josiah Kiehl 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | ------------------------------------------------------------------------------- 23 | 24 | Contains code by Ara T. Howard in lib/open4.rb, which is distributed under this 25 | license: 26 | 27 | same as Ruby's 28 | 29 | http://www.ruby-lang.org/en/LICENSE.txt 30 | -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | .gemtest 2 | History.txt 3 | LICENSE 4 | Manifest.txt 5 | README.txt 6 | Rakefile 7 | gems/Rakefile 8 | gems/template.gemspec 9 | gems/test/test_pass.rb 10 | lib/exceptions.rb 11 | lib/open4-vendor.rb 12 | lib/rubygems/commands/test_command.rb 13 | lib/rubygems/on_install_test.rb 14 | lib/rubygems_plugin.rb 15 | test/helper.rb 16 | test/interactive_test_on-install.rb 17 | test/test_execute.rb 18 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | = rubygems-test - commands and facilities for automated rubygems testing and reporting. 2 | 3 | == For users 4 | 5 | This installs three major features: 6 | 7 | * a 'gem test' command. 8 | * the ability to test your gems on installation, and uninstall them if they fail testing. 9 | * A facility to upload your test results to rubygems.org (coming soon, see http://github.com/bluepojo/gem-testers) 10 | 11 | === .gemrc 12 | 13 | You can insert several things in your .gemrc to make things simpler. These all 14 | live in the 'test_options' subsection, e.g.: 15 | 16 | test_options: 17 | auto_test_on_install: true 18 | test_on_install: true 19 | install_development_dependencies: true 20 | test_development_dependencies: false 21 | upload_results: false 22 | force_install: false 23 | force_uninstall_on_failure: false 24 | 25 | All options are *false* by default. For some of them, if the value is unknown 26 | or +false+, you will be prompted when it is required for testing. 27 | 28 | The options: 29 | 30 | auto_test_on_install:: runs the unit tests when 'gem install' is invoked. 31 | test_on_install:: offer to test on install in general. 'auto_test_on_install' overrides this. 32 | install_development_dependencies:: automatically install the development dependencies when testing. This is recommended. 33 | test_development_dependencies:: recursively test dev dependencies installed by rubygems-test. This can significantly increase the time it takes to install a gem. 34 | upload_results:: upload results to http://gem-testers.org. 35 | upload_service_url:: Set to a URL, it will upload to that service instead. 36 | force_install:: always install, even on test failures. 37 | force_uninstall_on_failure:: force uninstall when tests fail. 38 | 39 | == For gem developers 40 | 41 | Want your gem to be testable? 42 | 43 | * Create (and add to your files section in your specification!) a '.gemtest' empty file. This is what signals the rubygems-test engine to process your gem as testable. 44 | * Ensure 'rake test' works and doesn't wipe out filesystems, databases, etc. Move those tests to another task. 45 | * Be sure to include your Rakefile in your gem, along with your tests. 46 | * Be sure to note any development dependencies that will be required for executing your rakefile and your tests in your gemspec. They will be installed as a part of the testing process. 47 | 48 | == Note on Patches/Pull Requests 49 | 50 | * Fork the project. 51 | * Make your feature addition or bug fix. 52 | * Add tests for it. This is important so I don't break it in a 53 | future version unintentionally. 54 | * Commit, do not mess with rakefile, version, or history. 55 | (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) 56 | * Send me a pull request. Bonus points for topic branches. 57 | 58 | == Copyright 59 | 60 | Copyright (c) 2010 Erik Hollensbe and Josiah Kiehl. See LICENSE for details. 61 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # -*- ruby -*- 2 | 3 | require 'rubygems' 4 | require 'hoe' 5 | 6 | $:.unshift 'lib' 7 | require 'rubygems/command' 8 | require 'rubygems/commands/test_command' 9 | 10 | Hoe.plugins.delete :rubyforge 11 | Hoe.plugin :git 12 | 13 | spec = Hoe.spec 'rubygems-test' do 14 | developer 'Erik Hollensbe', 'erik@hollensbe.org' 15 | developer 'Josiah Kiehl', 'bluepojo@gmail.com' 16 | 17 | self.version = Gem::Commands::TestCommand::VERSION 18 | 19 | self.rubyforge_name = nil 20 | 21 | self.description = <<-EOF 22 | This installs three major features: 23 | 24 | * a 'gem test' command. 25 | * the ability to test your gems on installation, and uninstall them if they fail testing. 26 | * A facility to upload your test results to http://www.gem-testers.org. 27 | EOF 28 | 29 | self.summary = 'commands and facilities for automated and user-contributed rubygems testing and reporting' 30 | self.url = %w[http://github.com/rubygems/rubygems-test] 31 | 32 | pluggable! 33 | 34 | require_ruby_version ">= 1.8.7" 35 | require_rubygems_version ">= 1.5.0" 36 | 37 | extra_deps << ['rake', '>= 0.8.7'] 38 | 39 | desc "install a gem without sudo" 40 | end 41 | 42 | task :install => [:package] do 43 | sh "gem install pkg/#{spec.name}-#{spec.version}.gem" 44 | end 45 | # vim: syntax=ruby 46 | -------------------------------------------------------------------------------- /gems/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | 4 | Rake::TestTask.new(:gemtest) do |test| 5 | test.libs << 'lib' << 'test' 6 | test.pattern = 'test/**/test_*.rb' 7 | test.verbose = true 8 | end 9 | -------------------------------------------------------------------------------- /gems/template.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = "test-gem" 3 | s.version = "0.0.0" 4 | 5 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 6 | s.authors = ["Erik Hollensbe"] 7 | s.date = %q{2010-11-12} 8 | s.description = "GEM_TEST_DESCRIPTION" 9 | s.email = %q{erik@hollensbe.org} 10 | s.files = [ 11 | <%= @files %> 12 | ].flatten 13 | s.homepage = %q{http://example.org} 14 | s.rdoc_options = ["--charset=UTF-8"] 15 | s.require_paths = ["lib"] 16 | s.rubygems_version = %q{1.3.7} 17 | s.summary = %q{Gem testing facility as a plugin} 18 | s.rubyforge_project = %q[foo] 19 | 20 | <%= @development_dependencies %> 21 | 22 | if s.respond_to? :specification_version then 23 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 24 | s.specification_version = 3 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /gems/test/test_pass.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | 3 | class TestPass < Test::Unit::TestCase 4 | def test_01_pass 5 | assert true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/exceptions.rb: -------------------------------------------------------------------------------- 1 | class Gem::TestError < Gem::Exception; end 2 | class Gem::RakeNotFoundError < Gem::Exception; end 3 | -------------------------------------------------------------------------------- /lib/open4-vendor.rb: -------------------------------------------------------------------------------- 1 | # vim: ts=2:sw=2:sts=2:et:fdm=marker 2 | require 'fcntl' 3 | require 'timeout' 4 | require 'thread' 5 | 6 | module Open4 7 | #--{{{ 8 | VERSION = '1.0.1' 9 | def self.version() VERSION end 10 | 11 | class Error < ::StandardError; end 12 | 13 | def popen4(*cmd, &b) 14 | #--{{{ 15 | pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe 16 | 17 | verbose = $VERBOSE 18 | begin 19 | $VERBOSE = nil 20 | ps.first.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) 21 | ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) 22 | 23 | cid = fork { 24 | pw.last.close 25 | STDIN.reopen pw.first 26 | pw.first.close 27 | 28 | pr.first.close 29 | STDOUT.reopen pr.last 30 | pr.last.close 31 | 32 | pe.first.close 33 | STDERR.reopen pe.last 34 | pe.last.close 35 | 36 | STDOUT.sync = STDERR.sync = true 37 | 38 | begin 39 | exec(*cmd) 40 | raise 'forty-two' 41 | rescue Exception => e 42 | Marshal.dump(e, ps.last) 43 | ps.last.flush 44 | end 45 | ps.last.close unless (ps.last.closed?) 46 | exit! 47 | } 48 | ensure 49 | $VERBOSE = verbose 50 | end 51 | 52 | [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close} 53 | 54 | begin 55 | e = Marshal.load ps.first 56 | raise(Exception === e ? e : "unknown failure!") 57 | rescue EOFError # If we get an EOF error, then the exec was successful 58 | 42 59 | ensure 60 | ps.first.close 61 | end 62 | 63 | pw.last.sync = true 64 | 65 | pi = [pw.last, pr.first, pe.first] 66 | 67 | if b 68 | begin 69 | b[cid, *pi] 70 | Process.waitpid2(cid).last 71 | ensure 72 | pi.each{|fd| fd.close unless fd.closed?} 73 | end 74 | else 75 | [cid, pw.last, pr.first, pe.first] 76 | end 77 | #--}}} 78 | end 79 | alias open4 popen4 80 | module_function :popen4 81 | module_function :open4 82 | 83 | class SpawnError < Error 84 | #--{{{ 85 | attr 'cmd' 86 | attr 'status' 87 | attr 'signals' 88 | def exitstatus 89 | @status.exitstatus 90 | end 91 | def initialize cmd, status 92 | @cmd, @status = cmd, status 93 | @signals = {} 94 | if status.signaled? 95 | @signals['termsig'] = status.termsig 96 | @signals['stopsig'] = status.stopsig 97 | end 98 | sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ') 99 | super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>" 100 | end 101 | #--}}} 102 | end 103 | 104 | class ThreadEnsemble 105 | #--{{{ 106 | attr 'threads' 107 | 108 | def initialize cid 109 | @cid, @threads, @argv, @done, @running = cid, [], [], Queue.new, false 110 | @killed = false 111 | end 112 | 113 | def add_thread *a, &b 114 | @running ? raise : (@argv << [a, b]) 115 | end 116 | 117 | # 118 | # take down process more nicely 119 | # 120 | def killall 121 | c = Thread.critical 122 | return nil if @killed 123 | Thread.critical = true 124 | (@threads - [Thread.current]).each{|t| t.kill rescue nil} 125 | @killed = true 126 | ensure 127 | Thread.critical = c 128 | end 129 | 130 | def run 131 | @running = true 132 | 133 | begin 134 | @argv.each do |a, b| 135 | @threads << Thread.new(*a) do |*a| 136 | begin 137 | b[*a] 138 | ensure 139 | killall rescue nil if $! 140 | @done.push Thread.current 141 | end 142 | end 143 | end 144 | rescue 145 | killall 146 | raise 147 | ensure 148 | all_done 149 | end 150 | 151 | @threads.map{|t| t.value} 152 | end 153 | 154 | def all_done 155 | @threads.size.times{ @done.pop } 156 | end 157 | #--}}} 158 | end 159 | 160 | def to timeout = nil 161 | #--{{{ 162 | Timeout.timeout(timeout){ yield } 163 | #--}}} 164 | end 165 | module_function :to 166 | 167 | def new_thread *a, &b 168 | #--{{{ 169 | cur = Thread.current 170 | Thread.new(*a) do |*a| 171 | begin 172 | b[*a] 173 | rescue Exception => e 174 | cur.raise e 175 | end 176 | end 177 | #--}}} 178 | end 179 | module_function :new_thread 180 | 181 | def getopts opts = {} 182 | #--{{{ 183 | lambda do |*args| 184 | keys, default, ignored = args 185 | catch('opt') do 186 | [keys].flatten.each do |key| 187 | [key, key.to_s, key.to_s.intern].each do |key| 188 | throw 'opt', opts[key] if opts.has_key?(key) 189 | end 190 | end 191 | default 192 | end 193 | end 194 | #--}}} 195 | end 196 | module_function :getopts 197 | 198 | def relay src, dst = nil, t = nil 199 | #--{{{ 200 | send_dst = 201 | if dst.respond_to?(:call) 202 | lambda{|buf| dst.call(buf)} 203 | else 204 | lambda{|buf| dst << buf} 205 | end 206 | 207 | unless src.nil? 208 | if src.respond_to? :gets 209 | while buf = to(t){ src.gets } 210 | send_dst[buf] 211 | end 212 | 213 | elsif src.respond_to? :each 214 | q = Queue.new 215 | th = nil 216 | 217 | timer_set = lambda do |t| 218 | th = new_thread{ to(t){ q.pop } } 219 | end 220 | 221 | timer_cancel = lambda do |t| 222 | th.kill if th rescue nil 223 | end 224 | 225 | timer_set[t] 226 | begin 227 | src.each do |buf| 228 | timer_cancel[t] 229 | send_dst[buf] 230 | timer_set[t] 231 | end 232 | ensure 233 | timer_cancel[t] 234 | end 235 | 236 | elsif src.respond_to? :read 237 | buf = to(t){ src.read } 238 | send_dst[buf] 239 | 240 | else 241 | buf = to(t){ src.to_s } 242 | send_dst[buf] 243 | end 244 | end 245 | #--}}} 246 | end 247 | module_function :relay 248 | 249 | def spawn arg, *argv 250 | #--{{{ 251 | argv.unshift(arg) 252 | opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {}) 253 | argv.flatten! 254 | cmd = argv.join(' ') 255 | 256 | 257 | getopt = getopts opts 258 | 259 | ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ] 260 | ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ] 261 | exitstatus = getopt[ %w( exitstatus exit_status status ) ] 262 | stdin = getopt[ %w( stdin in i 0 ) << 0 ] 263 | stdout = getopt[ %w( stdout out o 1 ) << 1 ] 264 | stderr = getopt[ %w( stderr err e 2 ) << 2 ] 265 | pid = getopt[ 'pid' ] 266 | timeout = getopt[ %w( timeout spawn_timeout ) ] 267 | stdin_timeout = getopt[ %w( stdin_timeout ) ] 268 | stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ] 269 | stderr_timeout = getopt[ %w( stderr_timeout ) ] 270 | status = getopt[ %w( status ) ] 271 | cwd = getopt[ %w( cwd dir ) ] 272 | 273 | exitstatus = 274 | case exitstatus 275 | when TrueClass, FalseClass 276 | ignore_exit_failure = true if exitstatus 277 | [0] 278 | else 279 | [*(exitstatus || 0)].map{|i| Integer i} 280 | end 281 | 282 | stdin ||= '' if stdin_timeout 283 | stdout ||= '' if stdout_timeout 284 | stderr ||= '' if stderr_timeout 285 | 286 | started = false 287 | 288 | status = 289 | begin 290 | chdir(cwd) do 291 | Timeout::timeout(timeout) do 292 | popen4(*argv) do |c, i, o, e| 293 | started = true 294 | 295 | %w( replace pid= << push update ).each do |msg| 296 | break(pid.send(msg, c)) if pid.respond_to? msg 297 | end 298 | 299 | te = ThreadEnsemble.new c 300 | 301 | te.add_thread(i, stdin) do |i, stdin| 302 | relay stdin, i, stdin_timeout 303 | i.close rescue nil 304 | end 305 | 306 | te.add_thread(o, stdout) do |o, stdout| 307 | relay o, stdout, stdout_timeout 308 | end 309 | 310 | te.add_thread(e, stderr) do |o, stderr| 311 | relay e, stderr, stderr_timeout 312 | end 313 | 314 | te.run 315 | end 316 | end 317 | end 318 | rescue 319 | raise unless(not started and ignore_exec_failure) 320 | end 321 | 322 | raise SpawnError.new(cmd, status) unless 323 | (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus)) 324 | 325 | status 326 | #--}}} 327 | end 328 | module_function :spawn 329 | 330 | def chdir cwd, &block 331 | return(block.call Dir.pwd) unless cwd 332 | Dir.chdir cwd, &block 333 | end 334 | module_function :chdir 335 | 336 | def background arg, *argv 337 | #--{{{ 338 | require 'thread' 339 | q = Queue.new 340 | opts = { 'pid' => q, :pid => q } 341 | case argv.last 342 | when Hash 343 | argv.last.update opts 344 | else 345 | argv.push opts 346 | end 347 | thread = Thread.new(arg, argv){|arg, argv| spawn arg, *argv} 348 | sc = class << thread; self; end 349 | sc.module_eval { 350 | define_method(:pid){ @pid ||= q.pop } 351 | define_method(:spawn_status){ @spawn_status ||= value } 352 | define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus } 353 | } 354 | thread 355 | #--}}} 356 | end 357 | alias bg background 358 | module_function :background 359 | module_function :bg 360 | 361 | def maim pid, opts = {} 362 | #--{{{ 363 | getopt = getopts opts 364 | sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ] 365 | suspend = getopt[ 'suspend', 4 ] 366 | pid = Integer pid 367 | existed = false 368 | sigs.each do |sig| 369 | begin 370 | Process.kill sig, pid 371 | existed = true 372 | rescue Errno::ESRCH 373 | return(existed ? nil : true) 374 | end 375 | return true unless alive? pid 376 | sleep suspend 377 | return true unless alive? pid 378 | end 379 | return(not alive?(pid)) 380 | #--}}} 381 | end 382 | module_function :maim 383 | 384 | def alive pid 385 | #--{{{ 386 | pid = Integer pid 387 | begin 388 | Process.kill 0, pid 389 | true 390 | rescue Errno::ESRCH 391 | false 392 | end 393 | #--}}} 394 | end 395 | alias alive? alive 396 | module_function :alive 397 | module_function :'alive?' 398 | #--}}} 399 | end 400 | 401 | def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end 402 | -------------------------------------------------------------------------------- /lib/rubygems/commands/test_command.rb: -------------------------------------------------------------------------------- 1 | Gem.autoload(:VersionOption, 'rubygems/version_option') 2 | Gem.autoload(:Specification, 'rubygems/specification') 3 | Gem.autoload(:DefaultUserInteraction, 'rubygems/user_interaction') 4 | Gem.autoload(:DependencyInstaller, 'rubygems/dependency_installer') 5 | Gem.autoload(:RakeNotFoundError, 'exceptions') 6 | Gem.autoload(:TestError, 'exceptions') 7 | Gem.autoload(:Installer, 'rubygems/installer') 8 | require 'rbconfig' 9 | autoload(:YAML, 'yaml') 10 | require 'net/http' 11 | require 'uri' 12 | require 'tempfile' 13 | 14 | class Gem::Commands::TestCommand < Gem::Command 15 | include Gem::VersionOption 16 | include Gem::DefaultUserInteraction 17 | 18 | VERSION = "0.4.3" 19 | 20 | # taken straight out of rake 21 | DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'] 22 | 23 | def description 24 | 'Run the tests for a specific gem' 25 | end 26 | 27 | def arguments 28 | "GEM: name of gem" 29 | end 30 | 31 | def usage 32 | "#{program_name} GEM [-v VERSION] [--force] [--dep-user-install]" 33 | end 34 | 35 | def initialize(spec=nil, on_install=false, gem_dir=nil) 36 | options = { } 37 | 38 | if spec 39 | options[:name] = spec.name 40 | options[:version] = spec.version 41 | end 42 | 43 | @spec = spec 44 | @gem_dir = gem_dir 45 | @on_install = on_install 46 | 47 | super 'test', description, options 48 | add_version_option 49 | 50 | add_option( 51 | '--force', 52 | 'ignore opt-in testing and just run the tests' 53 | ) do |v,o| 54 | o[:force] = true 55 | end 56 | 57 | add_option( 58 | '--dep-user-install', 59 | 'force installing the dependencies into the user path' 60 | ) do |v,o| 61 | o[:dep_user_install] = true 62 | end 63 | end 64 | 65 | # 66 | # Get the config in our namespace 67 | # 68 | def config 69 | @config ||= Gem.configuration["test_options"] || { } 70 | end 71 | 72 | # 73 | # find a gem given a name and version 74 | # 75 | def find_gem(name, version) 76 | spec = Gem.source_index.find_name(name, version).last 77 | unless spec and (spec.installation_path rescue nil) 78 | alert_error "Could not find gem #{name} (#{version})" 79 | raise Gem::GemNotFoundException, "Could not find gem #{name}, (#{version})" 80 | end 81 | 82 | return spec 83 | end 84 | 85 | # 86 | # Locate the rakefile for a gem name and version 87 | # 88 | def find_rakefile(path, spec) 89 | rakefile = DEFAULT_RAKEFILES. 90 | map { |x| File.join(path, x) }. 91 | find { |x| File.exist?(x) } 92 | 93 | unless(File.exist?(rakefile) rescue nil) 94 | alert_error "Couldn't find rakefile -- this gem cannot be tested. Aborting." 95 | raise Gem::RakeNotFoundError, "Couldn't find rakefile, gem #{spec.name} (#{spec.version}) cannot be tested." 96 | end 97 | end 98 | 99 | # 100 | # Locate rake itself, prefer gems version. 101 | # 102 | def find_rake 103 | rake_path = nil; 104 | 105 | begin 106 | rake_path = Gem.bin_path('rake', 'rake') 107 | rescue 108 | if RUBY_VERSION > '1.9' and File.exist?(File.join(RbConfig::CONFIG["bindir"], Gem::Installer.exec_format % 'rake')) 109 | rake_path = File.join(RbConfig::CONFIG["bindir"], Gem::Installer.exec_format % 'rake') 110 | else 111 | alert_error "Couldn't find rake; rubygems-test will not work without it. Aborting." 112 | raise Gem::RakeNotFoundError, "Couldn't find rake; rubygems-test will not work without it." 113 | end 114 | end 115 | 116 | if RUBY_VERSION > '1.9' and !rake_path 117 | if RUBY_PLATFORM =~ /mswin/ 118 | # 119 | # XXX GarbageCollect breaks ruby -S with rake. 120 | # 121 | return File.join(RbConfig::CONFIG["bindir"], 'rake.bat') 122 | else 123 | return rake_path || 'rake' 124 | end 125 | else 126 | return rake_path 127 | end 128 | end 129 | 130 | # 131 | # Install development dependencies for the gem we're about to test. 132 | # 133 | def install_dependencies(spec) 134 | di = nil 135 | 136 | if options[:dep_user_install] 137 | di = Gem::DependencyInstaller.new(:install_dir => Gem.user_dir) 138 | else 139 | di = Gem::DependencyInstaller.new 140 | end 141 | 142 | $RG_T_INSTALLING_DEPENDENCIES = true 143 | spec.development_dependencies.each do |dep| 144 | unless Gem.source_index.search(dep).last 145 | if config["install_development_dependencies"] || Gem.configuration.verbose == false 146 | say "Installing test dependency #{dep.name} (#{dep.requirement})" 147 | di.install(dep) 148 | else 149 | if ask_yes_no("Install development dependency #{dep.name} (#{dep.requirement})?", true) 150 | say "Installing test dependency #{dep.name} (#{dep.requirement})" 151 | di.install(dep) 152 | else 153 | alert_error "Failed to install dependencies required to run tests. Aborting." 154 | raise Gem::TestError, "dependencies not installed" 155 | end 156 | end 157 | end 158 | end 159 | $RG_T_INSTALLING_DEPENDENCIES = false 160 | true 161 | end 162 | 163 | ## 164 | # Normalize the URI by adding "http://" if it is missing. 165 | # 166 | #-- 167 | # 168 | # taken verbatim from rubygems. 169 | # 170 | 171 | def normalize_uri(uri) 172 | (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" 173 | end 174 | 175 | ## 176 | # Escapes a URI. 177 | # 178 | #-- 179 | # 180 | # Taken verbatim from rubygems. 181 | # 182 | def escape(str) 183 | return nil unless str 184 | URI.escape str 185 | end 186 | 187 | # 188 | # if a proxy is supplied, return a URI 189 | # 190 | #-- 191 | # 192 | # taken almost verbatim from rubygems. 193 | # 194 | def proxy 195 | env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] 196 | 197 | return nil if env_proxy.nil? or env_proxy.empty? 198 | 199 | uri = URI.parse(normalize_uri(env_proxy)) 200 | 201 | if uri and uri.user.nil? and uri.password.nil? then 202 | # Probably we have http_proxy_* variables? 203 | uri.user = escape(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']) 204 | uri.password = escape(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']) 205 | end 206 | 207 | uri 208 | end 209 | 210 | # 211 | # Upload +yaml+ Results to +results_url+. 212 | # 213 | 214 | def upload_results(yaml, results_url=nil) 215 | begin 216 | results_url ||= config["upload_service_url"] || 'http://test.rubygems.org/test_results' 217 | url = URI.parse(results_url) 218 | 219 | net_http_args = [url.host, url.port] 220 | 221 | if proxy_uri = proxy 222 | net_http_args += [ 223 | proxy_uri.host, 224 | proxy_uri.port, 225 | proxy_uri.user, 226 | proxy_uri.password 227 | ] 228 | end 229 | 230 | http = Net::HTTP.new(*net_http_args) 231 | 232 | if ENV["RG_T_DEBUG_HTTP"] 233 | http.set_debug_output($stderr) 234 | end 235 | 236 | req = Net::HTTP::Post.new(url.path) 237 | req.set_form_data({:results => yaml}) 238 | response = http.start { |x| x.request(req) } 239 | 240 | case response 241 | when Net::HTTPSuccess 242 | body = YAML::load(response.body) 243 | if body[:success] 244 | url = body[:data][0] if body[:data] 245 | say "\nTest results posted successfully! \n\nresults url:\t#{url}\n\n" 246 | else 247 | body[:errors].each do |error| 248 | say error 249 | end if body[:errors] 250 | end 251 | when Net::HTTPRedirection 252 | location = response.fetch('Location') 253 | if !location or URI.parse(location) == url 254 | say %[Caught redirection but was unable to redirect to #{location}.] 255 | else 256 | upload_results yaml, location 257 | end 258 | when Net::HTTPNotFound 259 | say %q[Unable to find where to put the test results. Try: `gem update rubygems-test`] 260 | when Net::HTTPClientError 261 | say %q[Results server didn't like the results submission. Try: `gem update rubygems-test`] 262 | when Net::HTTPServerError 263 | say %q[Oof. Something went wrong on the results server processing these results. Sorry!] 264 | else 265 | say %q[Something weird happened. Probably a bug.] 266 | end 267 | rescue Errno::ECONNREFUSED => e 268 | say 'Unable to post test results. Can\'t connect to the results server.' 269 | rescue => e 270 | say e.message 271 | say e.backtrace 272 | end 273 | end 274 | 275 | # 276 | # Gather system results, test results into a YAML format ready for delivery. 277 | # 278 | def gather_results(spec, output, result) 279 | { 280 | :arch => RbConfig::CONFIG["arch"], 281 | :vendor => RbConfig::CONFIG["target_vendor"], 282 | :os => RbConfig::CONFIG["target_os"], 283 | :machine_arch => RbConfig::CONFIG["target_cpu"], 284 | :name => spec.name, 285 | :version => { 286 | :release => spec.version.to_s, 287 | :prerelease => spec.version.prerelease? 288 | }, 289 | :platform => (Kernel.const_get("RUBY_ENGINE") rescue "ruby"), 290 | :ruby_version => RUBY_VERSION, 291 | :result => result, 292 | :test_output => output, 293 | :rubygems_test_version => VERSION 294 | }.to_yaml 295 | end 296 | 297 | # 298 | # Inner loop for platform_reader 299 | # 300 | def read_output(stdout, stderr) 301 | require 'thread' 302 | 303 | [STDERR, $stderr, stderr, STDOUT, $stdout, stdout].map { |x| x.sync = true } 304 | 305 | reads = Queue.new 306 | output = "" 307 | 308 | err_t = Thread.new do 309 | while !stderr.eof? 310 | ary = [:stderr, nil, stderr.readline] 311 | ary[1] = Time.now.to_f 312 | reads << ary 313 | Thread.pass 314 | end 315 | end 316 | 317 | out_t = Thread.new do 318 | while !stdout.eof? 319 | ary = [:stdout, nil, stdout.read(1)] 320 | ary[1] = Time.now.to_f 321 | reads << ary 322 | Thread.pass 323 | end 324 | end 325 | 326 | tty_t = Thread.new do 327 | next_time = nil 328 | while true 329 | while reads.length > 0 330 | cur_reads = [next_time || reads.shift] 331 | 332 | time = cur_reads[0][1] 333 | 334 | while next_time = reads.shift 335 | break if next_time[1] != time 336 | cur_reads << next_time 337 | end 338 | 339 | stderr_reads, stdout_reads = cur_reads.partition { |x| x[0] == :stderr } 340 | 341 | # stderr wins 342 | (stderr_reads + stdout_reads).each do |rec| 343 | output << rec[2] 344 | print rec[2] 345 | end 346 | end 347 | Thread.pass 348 | end 349 | end 350 | 351 | while !stderr.eof? or !stdout.eof? or !reads.empty? 352 | Thread.pass 353 | end 354 | 355 | sleep 1 356 | tty_t.kill 357 | puts 358 | 359 | return output + "\n" 360 | end 361 | 362 | # 363 | # platform-specific reading routines. 364 | # 365 | def platform_reader(rake_args) 366 | # jruby stuffs it under IO, so we'll use that if it's available 367 | # if we're on 1.9, use open3 regardless of platform. 368 | # If we're not: 369 | # * on windows use win32/open3 from win32-open3 gem 370 | # * on unix use open4-vendor 371 | 372 | output, exit_status = *[] 373 | 374 | if IO.respond_to?(:popen4) 375 | IO.popen4(*rake_args) do |pid, stdin, stdout, stderr| 376 | output = read_output(stdout, stderr) 377 | end 378 | exit_status = $? 379 | elsif RUBY_VERSION > '1.9' 380 | require 'open3' 381 | Open3.popen3(*rake_args) do |stdin, stdout, stderr, thr| 382 | output = read_output(stdout, stderr) 383 | exit_status = thr.value 384 | end 385 | elsif RUBY_PLATFORM =~ /mingw|mswin/ 386 | begin 387 | require 'win32/open3' 388 | Open3.popen3(*rake_args) do |stdin, stdout, stderr| 389 | output = read_output(stdout, stderr) 390 | end 391 | exit_status = $? 392 | rescue LoadError 393 | say "1.8/Windows users must install the 'win32-open3' gem to run tests" 394 | terminate_interaction 1 395 | end 396 | else 397 | require 'open4-vendor' 398 | exit_status = Open4.popen4(*rake_args) do |pid, stdin, stdout, stderr| 399 | output = read_output(stdout, stderr) 400 | end 401 | end 402 | 403 | return output, exit_status 404 | end 405 | 406 | # 407 | # obtain the rake arguments for a specific platform and environment. 408 | # 409 | def get_rake_args(rake_path, *args) 410 | if RUBY_PLATFORM =~ /mswin/ and RUBY_VERSION > '1.9' 411 | # 412 | # XXX GarbageCollect breaks ruby -S with rake on 1.9. 413 | # 414 | 415 | rake_args = [ rake_path ] + args 416 | else 417 | rake_args = [ Gem.ruby, '-rubygems', '-S' ] + [ rake_path, '--' ] + args 418 | end 419 | 420 | if RUBY_PLATFORM =~ /mswin|mingw/ 421 | # we don't use shellwords for the rest because they use execve(). 422 | require 'shellwords' 423 | rake_args.map { |x| Shellwords.shellescape(x) }.join(' ') 424 | else 425 | rake_args 426 | end 427 | end 428 | 429 | # 430 | # Run the tests with the appropriate spec and rake_path, and capture all 431 | # output. 432 | # 433 | def run_tests(path, spec, rake_path) 434 | Dir.chdir(path) do 435 | rake_args = get_rake_args(rake_path, 'test') 436 | 437 | @trapped = false 438 | ::Kernel.trap("INT") { @trapped = true } 439 | 440 | output, exit_status = platform_reader(rake_args) 441 | 442 | ::Kernel.trap("INT", "DEFAULT") 443 | 444 | if !@trapped and upload_results? 445 | upload_results(gather_results(spec, output, exit_status.exitstatus == 0)) 446 | end 447 | 448 | if exit_status.exitstatus != 0 449 | if @trapped 450 | alert_error "You interrupted the test! Test runs are not valid unless you let them complete!" 451 | else 452 | alert_error "Tests did not pass. Examine the output and report it to the author!" 453 | end 454 | 455 | raise Gem::TestError, "tests failed" 456 | end 457 | end 458 | end 459 | 460 | # 461 | # Convenience predicate for upload_results option 462 | # 463 | def upload_results? 464 | !options[:force] and ( 465 | config["upload_results"] or 466 | ( 467 | !config.has_key?("upload_results") and 468 | Gem.configuration.verbose == false || 469 | ask_yes_no("Upload these results?", true) 470 | ) 471 | ) 472 | end 473 | 474 | 475 | # 476 | # Execute routine. This is where the magic happens. 477 | # 478 | def execute 479 | begin 480 | version = options[:version] || Gem::Requirement.default 481 | 482 | (get_all_gem_names rescue [options[:name]]).each do |name| 483 | 484 | unless name 485 | alert_error "No gem specified." 486 | show_help 487 | terminate_interaction 1 488 | end 489 | 490 | path, spec = if name =~ /\.gem$/ 491 | unless File.exist?(name) 492 | say "unable to find gem #{name}" 493 | next 494 | end 495 | 496 | inst = Gem::Installer.new(name) 497 | tmpdir = Dir.mktmpdir 498 | @created_tmpdir = true 499 | inst.unpack(tmpdir) 500 | unless inst.spec.extensions.empty? 501 | say "gem #{name} has extensions. Due to limitations in rubygems," 502 | say "the gem must be installed before it can be tested." 503 | next 504 | end 505 | [tmpdir, inst.spec] 506 | else 507 | 508 | if @spec and @gem_dir and File.directory?(@gem_dir) 509 | [@gem_dir, @spec] 510 | else 511 | 512 | spec = find_gem(name, version) 513 | 514 | unless spec 515 | say "unable to find gem #{name} #{version}" 516 | next 517 | end 518 | 519 | [spec.full_gem_path, spec] 520 | end 521 | end 522 | 523 | if File.exist?(File.join(path, '.gemtest')) or options[:force] 524 | # we find rake and the rakefile first to eliminate needlessly installing 525 | # dependencies. 526 | find_rakefile(path, spec) 527 | rake_path = find_rake 528 | 529 | unless $RG_T_INSTALLING_DEPENDENCIES and !config["test_development_dependencies"] 530 | install_dependencies(spec) 531 | run_tests(path, spec, rake_path) 532 | end 533 | else 534 | say "Gem '#{name}' (version #{version}) needs to opt-in for testing." 535 | say "" 536 | say "Locally available testing helps gems maintain high quality by" 537 | say "ensuring they work correctly on a wider array of platforms than the" 538 | say "original developer can access." 539 | say "" 540 | say "If you are the author: " 541 | say " * Add the file '.gemtest' to your spec.files" 542 | say " * Ensure 'rake test' works and doesn't do system damage" 543 | say " * Add your tests and Rakefile to your gem." 544 | say "" 545 | say "For more information, please see the rubygems-test README:" 546 | say "https://github.com/rubygems/rubygems-test/blob/master/README.txt" 547 | end 548 | 549 | if @created_tmpdir 550 | FileUtils.rm_rf path 551 | end 552 | end 553 | rescue Gem::TestError => e 554 | raise if @on_install 555 | terminate_interaction 1 556 | end 557 | end 558 | end 559 | -------------------------------------------------------------------------------- /lib/rubygems/on_install_test.rb: -------------------------------------------------------------------------------- 1 | Gem.autoload(:Uninstaller, 'rubygems/uninstaller') 2 | Gem::Commands.autoload(:TestCommand, 'rubygems/commands/test_command') 3 | Gem.autoload(:RakeNotFoundError, 'exceptions') 4 | Gem.autoload(:TestError, 'exceptions') 5 | 6 | Gem.post_build do |gem| 7 | options = Gem.configuration["test_options"] || { } 8 | 9 | if options["auto_test_on_install"] or options["test_on_install"] 10 | if options["auto_test_on_install"] or 11 | gem.ui.ask_yes_no("Test #{gem.spec.name} (#{gem.spec.version})?", true) 12 | 13 | begin 14 | # HACK: request gem_dir be exposed in rubygems 15 | Gem::Commands::TestCommand.new(gem.spec, true, gem.instance_variable_get(:@gem_dir) || gem.instance_variable_get("@gem_dir")).execute 16 | true 17 | rescue Gem::RakeNotFoundError, Gem::TestError 18 | !( 19 | (options.has_key?("force_install") && !options["force_install"]) || 20 | options["force_uninstall_on_failure"] || 21 | gem.ui.ask_yes_no("Testing #{gem.spec.name} (#{gem.spec.version}) failed. Uninstall?", false) 22 | ) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/rubygems_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems/command_manager' 2 | require 'rubygems/on_install_test' 3 | 4 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.3.1') 5 | %w[test].each do |command| 6 | Gem::CommandManager.instance.register_command command.to_sym 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' unless defined? Gem 2 | require 'rubygems/builder' unless defined? Gem::Builder 3 | require 'rubygems/user_interaction' unless defined? Gem::UserInteraction 4 | require 'rubygems/installer' unless defined? Gem::Installer 5 | require 'rubygems/uninstaller' unless defined? Gem::Uninstaller 6 | require 'test/unit' 7 | require 'erb' 8 | require 'tempfile' 9 | require 'fileutils' 10 | 11 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 12 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 13 | 14 | require 'rubygems/commands/test_command' unless defined? Gem::Command::TestCommand 15 | 16 | $stderr.puts "----- ERROR messages are a part of this output. Do not be alarmed by them! -----" 17 | 18 | class Test::Unit::TestCase 19 | def install_stub_gem(hash) 20 | file = Tempfile.new("rubygems-test") 21 | file.write(template_gemspec(hash)) 22 | path = file.path 23 | file.close 24 | 25 | Dir.mktmpdir('rubygems-test') do |dir| 26 | FileUtils.chdir('gems') do 27 | Dir['*'].each { |x| FileUtils.cp_r x, dir } 28 | end 29 | 30 | FileUtils.chdir(dir) do 31 | spec = eval File.read(path) 32 | filename = Gem::Builder.new(spec).build 33 | Gem::Installer.new(filename).install 34 | Gem.refresh 35 | end 36 | end 37 | end 38 | 39 | def uninstall_stub_gem 40 | Gem::Uninstaller.new("test-gem").uninstall 41 | Gem.refresh 42 | end 43 | 44 | def template_gemspec(hash) 45 | erb = ERB.new(File.read(File.join('gems', 'template.gemspec'))) 46 | 47 | @development_dependencies = "" 48 | 49 | (hash[:development_dependencies] || []).each do |dep| 50 | @development_dependencies += "s.add_development_dependency '#{dep}'\n" 51 | end 52 | 53 | @files = hash[:files] || "'Rakefile', Dir['test/**/*']" 54 | 55 | if @files.kind_of?(Array) 56 | @files = @files.map { |x| "'#{x}'" }.join(",\n") 57 | end 58 | 59 | return erb.result(binding) 60 | end 61 | 62 | def set_configuration(hash) 63 | Gem.configuration["test_options"] = hash 64 | Gem.configuration.verbose = false 65 | end 66 | 67 | def set_paths 68 | if Gem::VERSION > '1.8' 69 | Gem.paths = { :home => @gem_temp_path } 70 | else 71 | Gem.send :set_home, @gem_temp_path 72 | end 73 | 74 | Gem.refresh 75 | end 76 | 77 | def set_gem_temp_paths 78 | @gem_temp_path = Dir.mktmpdir('rubygems-test') 79 | @gem_home = Gem.dir 80 | @gem_paths = Gem.path 81 | 82 | Gem.clear_paths 83 | if Gem.path.kind_of?(String) 84 | Gem.path.replace @gem_temp_path 85 | else 86 | Gem.path.replace [@gem_temp_path] 87 | end 88 | 89 | set_paths 90 | end 91 | 92 | def unset_gem_temp_paths 93 | FileUtils.rm_rf @gem_temp_path if @gem_temp_path 94 | Gem.clear_paths 95 | 96 | if Gem.path.kind_of?(String) 97 | Gem.path.replace @gem_paths.join(File::PATH_SEPARATOR) 98 | else 99 | Gem.path.replace @gem_paths 100 | end 101 | 102 | set_paths 103 | end 104 | 105 | def setup 106 | set_configuration({ }) 107 | set_gem_temp_paths 108 | end 109 | 110 | def teardown 111 | uninstall_stub_gem rescue nil 112 | unset_gem_temp_paths 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /test/interactive_test_on-install.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestRubyGemsOnInstallTest < Test::Unit::TestCase::Interactive 4 | def test_01_successfully_uninstalls_on_test_failure 5 | set_configuration({ "force_uninstall_on_failure" => true, "auto_test_on_install" => true }) 6 | install_stub_gem(:files => []) 7 | end 8 | 9 | def test_02_successfully_installs_on_test_pass 10 | set_configuration({ "auto_test_on_install" => true, "upload_results" => false }) 11 | install_stub_gem(:files => %w[test/test_pass.rb Rakefile]) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/test_execute.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | require 'rbconfig' 3 | require 'yaml' 4 | 5 | class TestExecute < Test::Unit::TestCase 6 | def setup 7 | super 8 | @test = Gem::Commands::TestCommand.new 9 | end 10 | 11 | def test_01_config 12 | set_configuration({ "auto_test_on_install" => true }) 13 | assert @test.config 14 | assert @test.config.has_key?("auto_test_on_install") 15 | assert @test.config["auto_test_on_install"] 16 | end 17 | 18 | def test_02_gem_command_attributes 19 | assert_equal @test.description, "Run the tests for a specific gem" 20 | assert_equal @test.arguments, "GEM: name of gem" 21 | assert_equal @test.usage, "#{@test.program_name} GEM [-v VERSION] [--force] [--dep-user-install]" 22 | end 23 | 24 | def test_04_find_gem 25 | install_stub_gem({ }) 26 | 27 | assert_kind_of Gem::Specification, @test.find_gem("test-gem", "0.0.0") 28 | 29 | uninstall_stub_gem 30 | 31 | assert_raises(Gem::GemNotFoundException) { 32 | @test.find_gem("test-gem", "0.0.0") 33 | } 34 | end 35 | 36 | def test_05_find_rakefile 37 | install_stub_gem({ :files => ["Rakefile"] }) 38 | 39 | spec = @test.find_gem('test-gem', '0.0.0') 40 | 41 | assert_nothing_raised { @test.find_rakefile(spec.full_gem_path, spec) } 42 | 43 | uninstall_stub_gem 44 | install_stub_gem({ :files => "" }) 45 | 46 | spec = @test.find_gem('test-gem', '0.0.0') 47 | 48 | assert_raises(Gem::RakeNotFoundError) { @test.find_rakefile(spec.full_gem_path, spec) } 49 | 50 | uninstall_stub_gem 51 | end 52 | 53 | def test_06_gather_results 54 | install_stub_gem({}) 55 | 56 | spec = @test.find_gem("test-gem", "0.0.0") 57 | output = "foo" 58 | 59 | hash = { 60 | :arch => RbConfig::CONFIG["arch"], 61 | :vendor => RbConfig::CONFIG["target_vendor"], 62 | :os => RbConfig::CONFIG["target_os"], 63 | :machine_arch => RbConfig::CONFIG["target_cpu"], 64 | :ruby_version => RUBY_VERSION, 65 | :result => true, 66 | :name => spec.name, 67 | :version => { 68 | :release => spec.version.release.to_s, 69 | :prerelease => spec.version.prerelease? 70 | }, 71 | :platform => (Kernel.const_get("RUBY_ENGINE") rescue "ruby"), 72 | :test_output => output, 73 | :rubygems_test_version => Gem::Commands::TestCommand::VERSION 74 | } 75 | 76 | assert_equal YAML.load(@test.gather_results(spec, output, true)), hash 77 | end 78 | 79 | def test_07_upload_results? 80 | old_verbose = Gem.configuration.verbose 81 | 82 | Gem.configuration.verbose = false 83 | assert @test.upload_results? 84 | 85 | ensure 86 | Gem.configuration.verbose = old_verbose 87 | end 88 | end 89 | --------------------------------------------------------------------------------