From 90318ab33aca2786853d67b99f461ec0eec62d21 Mon Sep 17 00:00:00 2001 From: Jake Sorce Date: Tue, 17 Apr 2012 17:33:02 -0600 Subject: [PATCH] using custom parallelized specs gem for rspec tests Change-Id: Ia7d9ca7fd19267e9a3a66611b50d64bfc7bf3445 Reviewed-on: https://gerrit.instructure.com/10115 Tested-by: Hudson Reviewed-by: Bryan Madsen --- Gemfile | 2 +- Rakefile | 2 +- lib/tasks/parallelized.rake | 33 ++++ lib/tasks/rspec.rake | 348 ++++++++++++++++-------------------- script/spec | 3 - 5 files changed, 194 insertions(+), 194 deletions(-) create mode 100644 lib/tasks/parallelized.rake diff --git a/Gemfile b/Gemfile index d6a17f55b2c..ceb21e550e5 100644 --- a/Gemfile +++ b/Gemfile @@ -72,7 +72,7 @@ group :test do gem 'coffee-script-source', '1.1.2' #pinned so everyone's compiled output matches gem 'bluecloth', '2.0.10' # for generating api docs gem 'parallel', '0.5.16' - gem 'parallel_tests-instructure', '0.6.19' + gem 'parallelized_specs', '0.0.8' gem 'mocha', '0.10.0' gem 'rcov', '0.9.9' gem 'rspec', '1.3.2' diff --git a/Rakefile b/Rakefile index a6b9b773eb9..2c1ddf9878e 100644 --- a/Rakefile +++ b/Rakefile @@ -9,4 +9,4 @@ require 'rdoc/task' require 'tasks/rails' -begin; require 'parallel_tests/tasks'; rescue LoadError; end +begin; require 'parallelized_specs/tasks'; rescue LoadError; end diff --git a/lib/tasks/parallelized.rake b/lib/tasks/parallelized.rake new file mode 100644 index 00000000000..790d40b5a2c --- /dev/null +++ b/lib/tasks/parallelized.rake @@ -0,0 +1,33 @@ +# Don't load rspec if running "rake gems:*" +unless ARGV.any? { |a| a =~ /\Agems/ } + + namespace :parallel do + task :nonselenium, :count do |t, args| + require "parallelized_specs" + count = args[:count] + test_files = FileList['vendor/plugins/*/spec_canvas/**/*_spec.rb'].exclude('vendor/plugins/*/spec_canvas/selenium/*_spec.rb') + FileList['spec/**/*_spec.rb'].exclude('spec/selenium/*_spec.rb') + test_files.map! { |f| "#{Rails.root}/#{f}" } + Rake::Task['parallel:spec'].invoke(count, '', '', test_files.join(' ')) + end + + task :selenium, :count do |t, args| + require "parallelized_specs" + count = args[:count] + test_files = FileList['spec/selenium/*_spec.rb'] + FileList['vendor/plugins/*/spec_canvas/selenium/*_spec.rb'] + test_files.map! { |f| "#{Rails.root}/#{f}" } + Rake::Task['parallel:spec'].invoke(count, '', '', test_files.join(' ')) + end + + task :pattern, :count, :file_pattern do |t, args| + require "parallelized_specs" + count = args[:count] + file_pattern = args[:file_pattern] + if count.nil? || file_pattern.nil? + raise "Must specify a thread count and file pattern" + end + test_files = FileList[file_pattern] + test_files.map! { |f| "#{Rails.root}/#{f}" } + Rake::Task['parallel:spec'].invoke(count, '', '', test_files.join(' ')) + end + end +end \ No newline at end of file diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake index 71820955374..963d79dc868 100644 --- a/lib/tasks/rspec.rake +++ b/lib/tasks/rspec.rake @@ -1,226 +1,196 @@ # Don't load rspec if running "rake gems:*" unless ARGV.any? { |a| a =~ /\Agems/ } -begin - require 'spec/rake/spectask' -rescue MissingSourceFile - module Spec - module Rake - class SpecTask - include ::Rake::DSL - def initialize(name) - task name do - # if rspec-rails is a configured gem, this will output helpful material and exit ... - require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") + begin + require 'spec/rake/spectask' + rescue MissingSourceFile + module Spec + module Rake + class SpecTask + include ::Rake::DSL - # ... otherwise, do this: - raise <<-MSG + def initialize(name) + task name do + # if rspec-rails is a configured gem, this will output helpful material and exit ... + require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") + + # ... otherwise, do this: + raise <<-MSG #{"*" * 80} * You are trying to run an rspec rake task defined in * #{__FILE__}, * but rspec can not be found in vendor/gems, vendor/plugins or system gems. #{"*" * 80} - MSG + MSG + end end end end end end -end -Rake.application.instance_variable_get('@tasks').delete('default') + Rake.application.instance_variable_get('@tasks').delete('default') -task :default => :spec -task :stats => "spec:statsetup" + task :default => :spec + task :stats => "spec:statsetup" -desc "Run all specs in spec directory (excluding plugin specs)" -Spec::Rake::SpecTask.new(:spec) do |t| - # you can also do SPEC_OPTS='-e "test name"' but this is a little easier I - # suppose. - if ENV['SINGLE_TEST'] - t.spec_opts += ['-e', %{"#{ENV['SINGLE_TEST']}"}] - end - spec_files = FileList['vendor/plugins/*/spec_canvas/**/*_spec.rb'].exclude('vendor/plugins/*/spec_canvas/selenium/*_spec.rb') + FileList['spec/**/*_spec.rb'].exclude('spec/selenium/*_spec.rb') - Gem.loaded_specs.values.each do |spec| - path = spec.full_gem_path - spec_canvas_path = File.expand_path(path+"/spec_canvas") - next unless File.directory?(spec_canvas_path) - spec_files << spec_canvas_path - end - if ENV['IN_MEMORY_DB'] - N_PROCESSES = [ENV['IN_MEMORY_DB'].to_i, 1].max - spec_files = spec_files.map { |x| Dir[x + "/[^selenium]**/*_spec.rb"] }.flatten.sort.in_groups_of(N_PROCESSES) - processes = [] - Signal.trap "SIGINT", (lambda { Process.kill "-KILL", Process.getpgid(0) }) - child = false - N_PROCESSES.times do |j| - pid = Process.fork - unless pid - child = true - t.spec_files = spec_files.map { |x| x[j] }.compact - break + desc "Run all specs in spec directory (excluding plugin specs)" + Spec::Rake::SpecTask.new(:spec) do |t| + # you can also do SPEC_OPTS='-e "test name"' but this is a little easier I + # suppose. + if ENV['SINGLE_TEST'] + t.spec_opts += ['-e', %{"#{ENV['SINGLE_TEST']}"}] + end + spec_files = FileList['vendor/plugins/*/spec_canvas/**/*_spec.rb'].exclude('vendor/plugins/*/spec_canvas/selenium/*_spec.rb') + FileList['spec/**/*_spec.rb'].exclude('spec/selenium/*_spec.rb') + Gem.loaded_specs.values.each do |spec| + path = spec.full_gem_path + spec_canvas_path = File.expand_path(path+"/spec_canvas") + next unless File.directory?(spec_canvas_path) + spec_files << spec_canvas_path + end + if ENV['IN_MEMORY_DB'] + N_PROCESSES = [ENV['IN_MEMORY_DB'].to_i, 1].max + spec_files = spec_files.map { |x| Dir[x + "/[^selenium]**/*_spec.rb"] }.flatten.sort.in_groups_of(N_PROCESSES) + processes = [] + Signal.trap "SIGINT", (lambda { Process.kill "-KILL", Process.getpgid(0) }) + child = false + N_PROCESSES.times do |j| + pid = Process.fork + unless pid + child = true + t.spec_files = spec_files.map { |x| x[j] }.compact + break + end + processes << pid end - processes << pid - end - exit Process.waitall.map(&:last).map(&:exitstatus).count { |x| x != 0 } unless child - else - t.spec_files = spec_files - end -end - -namespace :spec do - desc "Run all specs in spec directory, wiping the database first" - task :wipedb do - ENV["RAILS_ENV"] ||= "test" - Rake::Task["db:test:prepare"].execute - Rake::Task["spec"].execute - end - - desc "Run all specs in spec directory with RCov (excluding plugin specs)" - Spec::Rake::SpecTask.new(:rcov) do |t| - t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] - t.spec_files = FileList['spec/**/*/*_spec.rb'].exclude('spec/selenium/*_spec.rb') - t.rcov = true - t.rcov_opts = lambda do - IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map { |l| l.chomp.split " " }.flatten + exit Process.waitall.map(&:last).map(&:exitstatus).count { |x| x != 0 } unless child + else + t.spec_files = spec_files end end - desc "Print Specdoc for all specs (excluding plugin specs)" - Spec::Rake::SpecTask.new(:doc) do |t| - t.spec_opts = ["--format", "specdoc", "--dry-run"] - t.spec_files = FileList['spec/**/*/*_spec.rb'] - end + namespace :spec do + desc "Run all specs in spec directory, wiping the database first" + task :wipedb do + ENV["RAILS_ENV"] ||= "test" + Rake::Task["db:test:prepare"].execute + Rake::Task["spec"].execute + end - desc "Print Specdoc for all plugin examples" - Spec::Rake::SpecTask.new(:plugin_doc) do |t| - t.spec_opts = ["--format", "specdoc", "--dry-run"] - t.spec_files = FileList['vendor/plugins/**/spec/**/*/*_spec.rb'].exclude('vendor/plugins/rspec/*') - end - - [:models, :controllers, :views, :helpers, :lib, :selenium].each do |sub| - desc "Run the code examples in spec/#{sub}" - Spec::Rake::SpecTask.new(sub) do |t| + desc "Run all specs in spec directory with RCov (excluding plugin specs)" + Spec::Rake::SpecTask.new(:rcov) do |t| t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] - t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] + t.spec_files = FileList['spec/**/*/*_spec.rb'].exclude('spec/selenium/*_spec.rb') + t.rcov = true + t.rcov_opts = lambda do + IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map { |l| l.chomp.split " " }.flatten + end end - end - desc "Run the code examples in vendor/plugins (except RSpec's own)" - Spec::Rake::SpecTask.new(:plugins) do |t| - t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] - t.spec_files = FileList['vendor/plugins/**/spec/**/*/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*") - end + desc "Print Specdoc for all specs (excluding plugin specs)" + Spec::Rake::SpecTask.new(:doc) do |t| + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['spec/**/*/*_spec.rb'] + end - namespace :plugins do - desc "Runs the examples for rspec_on_rails" - Spec::Rake::SpecTask.new(:rspec_on_rails) do |t| + desc "Print Specdoc for all plugin examples" + Spec::Rake::SpecTask.new(:plugin_doc) do |t| + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['vendor/plugins/**/spec/**/*/*_spec.rb'].exclude('vendor/plugins/rspec/*') + end + + [:models, :controllers, :views, :helpers, :lib, :selenium].each do |sub| + desc "Run the code examples in spec/#{sub}" + Spec::Rake::SpecTask.new(sub) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] + end + end + + desc "Run the code examples in vendor/plugins (except RSpec's own)" + Spec::Rake::SpecTask.new(:plugins) do |t| t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] - t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*/*_spec.rb'] + t.spec_files = FileList['vendor/plugins/**/spec/**/*/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*") end - end - # Setup specs for stats - task :statsetup do - require 'code_statistics' - ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models') - ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views') - ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers') - ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers') - ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib') - ::STATS_DIRECTORIES << %w(Routing\ specs spec/lib) if File.exist?('spec/routing') - ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models') - ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views') - ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers') - ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers') - ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib') - ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing') - end + namespace :plugins do + desc "Runs the examples for rspec_on_rails" + Spec::Rake::SpecTask.new(:rspec_on_rails) do |t| + t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""] + t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*/*_spec.rb'] + end + end - namespace :db do - namespace :fixtures do - desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z." - task :load => :environment do - ActiveRecord::Base.establish_connection(Rails.env) - base_dir = File.join(Rails.root, 'spec', 'fixtures') - fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir + # Setup specs for stats + task :statsetup do + require 'code_statistics' + ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models') + ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views') + ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers') + ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers') + ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib') + ::STATS_DIRECTORIES << %w(Routing\ specs spec/lib) if File.exist?('spec/routing') + ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models') + ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views') + ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers') + ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers') + ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib') + ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing') + end - require 'active_record/fixtures' - (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map { |f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| - Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) + namespace :db do + namespace :fixtures do + desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z." + task :load => :environment do + ActiveRecord::Base.establish_connection(Rails.env) + base_dir = File.join(Rails.root, 'spec', 'fixtures') + fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir + + require 'active_record/fixtures' + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map { |f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| + Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) + end + end + end + end + + namespace :server do + daemonized_server_pid = File.expand_path("#{RAILS_ROOT}/tmp/pids/spec_server.pid") + + desc "start spec_server." + task :start do + if File.exist?(daemonized_server_pid) + $stderr.puts "spec_server is already running." + else + $stderr.puts %Q{Starting up spec_server ...} + FileUtils.mkdir_p('tmp/pids') unless test ?d, 'tmp/pids' + system("ruby", "script/spec_server", "--daemon", "--pid", daemonized_server_pid) + end + end + + desc "stop spec_server." + task :stop do + unless File.exist?(daemonized_server_pid) + $stderr.puts "No server running." + else + $stderr.puts "Shutting down spec_server ..." + system("kill", "-s", "TERM", File.read(daemonized_server_pid).strip) && + File.delete(daemonized_server_pid) + end + end + + desc "restart spec_server." + task :restart => [:stop, :start] + + desc "check if spec server is running" + task :status do + if File.exist?(daemonized_server_pid) + $stderr.puts %Q{spec_server is running (PID: #{File.read(daemonized_server_pid).gsub("\n", "")})} + else + $stderr.puts "No server running." end end end end - - namespace :server do - daemonized_server_pid = File.expand_path("#{RAILS_ROOT}/tmp/pids/spec_server.pid") - - desc "start spec_server." - task :start do - if File.exist?(daemonized_server_pid) - $stderr.puts "spec_server is already running." - else - $stderr.puts %Q{Starting up spec_server ...} - FileUtils.mkdir_p('tmp/pids') unless test ?d, 'tmp/pids' - system("ruby", "script/spec_server", "--daemon", "--pid", daemonized_server_pid) - end - end - - desc "stop spec_server." - task :stop do - unless File.exist?(daemonized_server_pid) - $stderr.puts "No server running." - else - $stderr.puts "Shutting down spec_server ..." - system("kill", "-s", "TERM", File.read(daemonized_server_pid).strip) && - File.delete(daemonized_server_pid) - end - end - - desc "restart spec_server." - task :restart => [:stop, :start] - - desc "check if spec server is running" - task :status do - if File.exist?(daemonized_server_pid) - $stderr.puts %Q{spec_server is running (PID: #{File.read(daemonized_server_pid).gsub("\n", "")})} - else - $stderr.puts "No server running." - end - end - end -end - -namespace :parallel do - task :nonselenium, :count do |t, args| - require "parallel_tests" - count = args[:count] - test_files = FileList['vendor/plugins/*/spec_canvas/**/*_spec.rb'].exclude('vendor/plugins/*/spec_canvas/selenium/*_spec.rb') + FileList['spec/**/*_spec.rb'].exclude('spec/selenium/*_spec.rb') - test_files.map! { |f| "#{Rails.root}/#{f}" } - Rake::Task['parallel:spec'].invoke(count, '', '', test_files.join(' ')) - end - - task :selenium, :count do |t, args| - require "parallel_tests" - count = args[:count] - test_files = FileList['spec/selenium/*_spec.rb'] + FileList['vendor/plugins/*/spec_canvas/selenium/*_spec.rb'] - test_files.map! { |f| "#{Rails.root}/#{f}" } - Rake::Task['parallel:spec'].invoke(count, '', '', test_files.join(' ')) - end - - task :pattern, :count, :file_pattern do |t, args| - require "parallel_tests" - count = args[:count] - file_pattern = args[:file_pattern] - if count.nil? || file_pattern.nil? - raise "Must specify a thread count and file pattern" - end - test_files = FileList[file_pattern] - test_files.map! { |f| "#{Rails.root}/#{f}" } - Rake::Task['parallel:spec'].invoke(count, '', '', test_files.join(' ')) - end -end - end diff --git a/script/spec b/script/spec index 9413ee57100..46fdbe6e446 100755 --- a/script/spec +++ b/script/spec @@ -7,7 +7,4 @@ else require File.expand_path(File.dirname(__FILE__) + "/../config/environment") unless defined?(RAILS_ROOT) end require 'spec/autorun' -require 'parallel_specs/spec_error_logger' -require 'parallel_specs/spec_error_count_logger' -require 'parallel_specs/spec_start_finish_logger' exit ::Spec::Runner::CommandLine.run