require "spec_helper" require "parallel_tests/test/runner" describe ParallelTests::Test::Runner do test_tests_in_groups(ParallelTests::Test::Runner, 'test', '_test.rb') describe ".run_tests" do def call(*args) ParallelTests::Test::Runner.run_tests(*args) end it "allows to override runner executable via PARALLEL_TESTS_EXECUTABLE" do begin ENV['PARALLEL_TESTS_EXECUTABLE'] = 'script/custom_rspec' ParallelTests::Test::Runner.should_receive(:execute_command).with{|a,b,c,d| a.include?("script/custom_rspec") } call(['xxx'], 1, 22, {}) ensure ENV.delete('PARALLEL_TESTS_EXECUTABLE') end end it "uses options" do ParallelTests::Test::Runner.should_receive(:execute_command).with{|a,b,c,d| a =~ %r{ruby -Itest .* -- -v}} call(['xxx'], 1, 22, :test_options => '-v') end it "returns the output" do ParallelTests::Test::Runner.should_receive(:execute_command).and_return({:x => 1}) call(['xxx'], 1, 22, {}).should == {:x => 1} end end describe ".test_in_groups" do def call(*args) ParallelTests::Test::Runner.tests_in_groups(*args) end it "does not sort when passed false do_sort option" do ParallelTests::Test::Runner.should_not_receive(:smallest_first) call([], 1, :group_by => :found) end it "does sort when not passed do_sort option" do ParallelTests::Test::Runner.stub!(:tests_with_runtime).and_return([]) ParallelTests::Grouper.should_receive(:largest_first).and_return([]) call([], 1) end it "groups by single_process pattern and then via size" do ParallelTests::Test::Runner.should_receive(:with_runtime_info). and_return([ ['aaa', 5], ['aaa2', 5], ['bbb', 2], ['ccc', 1], ['ddd', 1] ]) result = call([], 3, :single_process => [/^a.a/]) result.should == [["aaa", "aaa2"], ["bbb"], ["ccc", "ddd"]] end it "groups by size and adds isolated separately" do ParallelTests::Test::Runner.should_receive(:with_runtime_info). and_return([ ['aaa', 0], ['bbb', 3], ['ccc', 1], ['ddd', 2], ['eee', 1] ]) result = call([], 3, :isolate => true, :single_process => [/^aaa/]) result.should == [["aaa"], ["bbb", "eee"], ["ccc", "ddd"]] end end describe ".find_results" do def call(*args) ParallelTests::Test::Runner.find_results(*args) end it "finds multiple results in test output" do output = < false).sort.should == [ "#{root}/b/b_test.rb", ] end end it "finds test files nested in different folders" do with_files(['a/a_test.rb','b/b_test.rb', 'c/c_test.rb']) do |root| call(["#{root}/a", "#{root}/b"]).sort.should == [ "#{root}/a/a_test.rb", "#{root}/b/b_test.rb", ] end end it "only finds tests in folders" do with_files(['a/a_test.rb', 'a/test.rb', 'a/test_helper.rb']) do |root| call(["#{root}/a"]).sort.should == [ "#{root}/a/a_test.rb" ] end end it "finds tests in nested folders" do with_files(['a/b/c/d/a_test.rb']) do |root| call(["#{root}/a"]).sort.should == [ "#{root}/a/b/c/d/a_test.rb" ] end end it "does not expand paths" do with_files(['a/x_test.rb']) do |root| inside_dir root do call(['a']).sort.should == [ "a/x_test.rb" ] end end end it "finds test files in folders by pattern" do with_files(['a/x_test.rb','a/y_test.rb','a/z_test.rb']) do |root| inside_dir root do call(["a"], :pattern => /^a\/(y|z)_test/).sort.should == [ "a/y_test.rb", "a/z_test.rb", ] end end end it "finds nothing if I pass nothing" do call(nil).should == [] end it "finds nothing if I pass nothing (empty array)" do call([]).should == [] end it "keeps invalid files" do call(['baz']).should == ['baz'] end it "discards duplicates" do call(['baz','baz']).should == ['baz'] end end describe ".summarize_results" do def call(*args) ParallelTests::Test::Runner.summarize_results(*args) end it "adds results" do call(['1 foo 3 bar','2 foo 5 bar']).should == '8 bars, 3 foos' end it "adds results with braces" do call(['1 foo(s) 3 bar(s)','2 foo 5 bar']).should == '8 bars, 3 foos' end it "adds same results with plurals" do call(['1 foo 3 bar','2 foos 5 bar']).should == '8 bars, 3 foos' end it "adds non-similar results" do call(['1 xxx 2 yyy','1 xxx 2 zzz']).should == '2 xxxs, 2 yyys, 2 zzzs' end it "does not pluralize 1" do call(['1 xxx 2 yyy']).should == '1 xxx, 2 yyys' end end describe ".execute_command" do def call(*args) ParallelTests::Test::Runner.execute_command(*args) end def capture_output $stdout, $stderr = StringIO.new, StringIO.new yield [$stdout.string, $stderr.string] ensure $stdout, $stderr = STDOUT, STDERR end def run_with_file(content) capture_output do Tempfile.open("xxx") do |f| f.write(content) f.flush yield f.path end end end it "sets process number to 2 for 1" do run_with_file("puts ENV['TEST_ENV_NUMBER']") do |path| result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "2\n", :stderr => "", :exit_status => 0 } end end it "sets process number to '' for 0" do run_with_file("puts ENV['TEST_ENV_NUMBER'].inspect") do |path| result = call("ruby #{path}", 0, 4, {}) result.should == { :stdout => "\"\"\n", :stderr => "", :exit_status => 0 } end end it 'sets PARALLEL_TEST_GROUPS so child processes know that they are being run under parallel_tests' do run_with_file("puts ENV['PARALLEL_TEST_GROUPS']") do |path| result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "4\n", :stderr => "", :exit_status => 0 } end end it "skips reads from stdin" do if RUBY_VERSION =~ /^1\.8/ pending else run_with_file("$stdin.read; puts 123") do |path| result = call("ruby #{path}", 1, 2, {}) result.should == { :stdout => "123\n", :stderr => "", :exit_status => 0 } end end end it "waits for process to finish" do run_with_file("sleep 0.5; puts 123; sleep 0.5; puts 345") do |path| result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "123\n345\n", :stderr => "", :exit_status => 0 } end end it "prints output while running" do run_with_file("$stdout.sync = true; puts 123; sleep 0.1; print 345; sleep 0.1; puts 567") do |path| $stdout.should_receive(:print).with("123\n") if RUBY_VERSION =~ /^1\.8/ $stdout.should_receive(:print).with("345") $stdout.should_receive(:print).with("567\n") else $stdout.should_receive(:print).with("345567\n") end result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "123\n345567\n", :stderr => "", :exit_status => 0 } end end it "works with synced stdout" do run_with_file("$stdout.sync = true; puts 123; sleep 0.1; puts 345") do |path| result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "123\n345\n", :stderr => "", :exit_status => 0 } end end it "does not print to stdout with :serialize_stdout" do run_with_file("puts 123") do |path| $stdout.should_not_receive(:print) result = call("ruby #{path}", 1, 4, :serialize_stdout => true) result.should == { :stdout => "123\n", :stderr => "", :exit_status => 0 } end end it "returns correct exit status" do run_with_file("puts 123; exit 5") do |path| result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "123\n", :stderr => "", :exit_status => 5 } end end it "prints each stream to the correct stream" do if RUBY_VERSION =~ /^1\.8/ pending else out, err = run_with_file("puts 123 ; $stderr.puts 345 ; exit 5") do |path| result = call("ruby #{path}", 1, 4, {}) result.should == { :stdout => "123\n", :stderr => "345\n", :exit_status => 5 } end err.should == "345\n" end end it "uses a lower priority process when the nice option is used" do priority_cmd = "puts Process.getpriority(Process::PRIO_PROCESS, 0)" priority_without_nice = run_with_file(priority_cmd){ |cmd| call("ruby #{cmd}", 1, 4, {}) }.first.to_i priority_with_nice = run_with_file(priority_cmd){ |cmd| call("ruby #{cmd}", 1, 4, :nice => true) }.first.to_i priority_without_nice.should < priority_with_nice end end end