Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 4532c88bb1bc387b5a51e5a0c78bf3855d6394a8 @lsegal committed May 10, 2012
Showing with 196 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +53 −0 README.md
  3. +126 −0 lib/rvm-tester.rb
  4. +14 −0 rvm-tester.gemspec
@@ -0,0 +1,3 @@
+.yardoc
+doc
+*.gem
@@ -0,0 +1,53 @@
+# RVM Tester
+
+Runs tests across Ruby installations in RVM using concurrent worker processes.
+This library does not sandbox installations, and requires that you have already
+installed the necessary Ruby installations under RVM.
+
+## Installing
+
+In addition to installing this library with `gem install rvm-tester`, you will also need
+to setup
+
+## Usage
+
+You can either use this library via the Rake task or directly via the Runner class:
+
+### Rake Task
+
+Place the following in your `Rakefile`:
+
+```ruby
+require 'rvm-tester'
+RVM::Tester::TesterTask.new(:suite) do |t|
+ t.rubies = %w(1.8.6 1.8.7 1.9.2 1.9.3) # which versions to test (required!)
+ t.bundle_install = true # updates Gemfile.lock, default is true
+ t.command = "bundle exec rake test" # runs plain "rake" by default
+ t.env = {"VERBOSE" => "1"} # set any ENV vars
+ t.num_workers = 5 # defaults to 3
+ t.verbose = true # shows more output, off by default
+end
+```
+
+And simply run the task with `rake suite`.
+
+### Runner Class
+
+You can also run the runner directly. The "t" object passed in the task above
+is the runner class, so you would set it up in the same way. You can also pass
+all attributes via the options hash.
+
+```ruby
+runner = RVM::Tester::Runner.new(:rubies => %w(rbx 1.9.3), :verbose => true)
+runner.run
+```
+
+## Dependencies
+
+This library depends on [RVM](http://rvm.beginrescueend.com) to control
+Ruby installations and [mob_spawner](http://github.com/lsegal/mob_spawner) to
+spawn worker processes.
+
+## License & Copyright
+
+MobSpawner is licensed under the MIT license, © 2012 Loren Segal
@@ -0,0 +1,126 @@
+require 'rake'
+require 'rake/tasklib'
+require 'mob_spawner'
+
+module RVM
+ module Tester
+ VERSION = '1.0.0'
+
+ class TesterTask < ::Rake::TaskLib
+ def initialize(name = :suite)
+ runner = Runner.new
+ yield(runner) if block_given?
+ desc "Runs tests across RVM Rubies: #{runner.rubies.join(",")}"
+ task(name) { exit runner.run }
+ end
+ end
+
+ class Runner
+ attr_accessor :num_workers
+ attr_accessor :rubies
+ attr_accessor :env
+ attr_accessor :command
+ attr_accessor :verbose
+ attr_accessor :bundle_install
+
+ def initialize(opts = {})
+ super()
+ self.command = "rake"
+ self.num_workers = 3
+ self.env = {}
+ self.rubies = []
+ self.verbose = false
+ self.bundle_install = true
+ opts.each {|k,v| meth = "#{k}="; send(meth, v) if respond_to?(meth) }
+ end
+
+ def run
+ exit_status = 0
+ outputs = []
+ use_gemfile if bundle_install
+ commands = rubies.map do |ruby|
+ MobSpawner::Command.new(
+ :command => "rvm #{ruby} do #{command}",
+ :env => env, :data => {:ruby => ruby, :time => nil})
+
+ end
+ spawner = MobSpawner.new
+ spawner.commands = commands
+ spawner.num_workers = num_workers
+ spawner.before_worker do |data|
+ worker, cmd = data[:worker], data[:command]
+ debug "Worker #{worker} running tests in #{cmd.data[:ruby]}"
+ data[:command].data[:time] = Time.now
+ end
+ spawner.after_worker do |data|
+ worker, cmd = data[:worker], data[:command]
+ next(outputs << data) if data[:output].nil?
+ testinfo = data[:output][/(\d+ examples, \d+ failures)/, 1]
+ testinfo += ", " if testinfo
+ time = Time.now - data[:command].data[:time]
+ passfail = data[:status] == 0 ? "passed" : "failed"
+ msg = "Tests #{passfail} in #{cmd.data[:ruby]} (#{testinfo}#{"%.2f" % time}sec)"
+ if data[:status] == 0
+ outputs << data if verbose
+ success(msg)
+ else
+ outputs << data
+ exit_status = 255
+ fail(msg)
+ end
+ end
+ spawner.run
+
+ return(exit_status) if outputs.size == 0
+ puts
+ outputs.each do |data|
+ if data[:exception]
+ bright "Exception while running #{data[:command].data[:ruby]}:"
+ puts data[:exception].message
+ puts data[:exception].backtrace
+ else
+ bright "Output for #{data[:command].data[:ruby]} " +
+ "(#{data[:status]}, cmd=#{data[:command].command}):"
+ puts data[:output]
+ end
+ puts
+ end
+
+ exit_status
+ end
+
+ def use_gemfile
+ return unless File.file?("Gemfile")
+ print "Installing Bundler and dependencies on #{rubies.join(",")}..."
+ cmds = rubies.map do |r|
+ ver = r == '1.8.6' ? '-v 1.0.22' : '' # Bundler compat for 1.8.6
+ "rvm #{r} do gem install bundler #{ver} && rvm #{r} do bundle update"
+ end
+ MobSpawner.new(cmds).run
+ puts "Done."
+ end
+
+ def debug(info)
+ return unless verbose
+ puts(info)
+ end
+
+ def success(msg)
+ puts "\e[32;1m#{msg}\e[0m"
+ end
+
+ def fail(msg)
+ puts "\e[31;1m#{msg}\e[0m"
+ end
+
+ def bright(msg)
+ puts "\e[1m#{msg}\e[0m"
+ end
+
+ def puts(msg = '')
+ super(msg)
+ $stdout.flush
+ end
+ end
+ end
+end
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + "/lib/rvm-tester"
+Gem::Specification.new do |s|
+ s.name = "rvm-tester"
+ s.summary = "Runs tests across Ruby installations in RVM"
+ s.version = RVM::Tester::VERSION
+ s.author = "Loren Segal"
+ s.email = "lsegal@soen.ca"
+ s.homepage = "http://github.com/lsegal/rvm-tester"
+ s.platform = Gem::Platform::RUBY
+ s.files = Dir.glob("{lib}/**/*") + ['README.md']
+ s.require_paths = ['lib']
+ s.add_dependency 'rake'
+ s.add_dependency 'mob_spawner'
+end

0 comments on commit 4532c88

Please sign in to comment.