#encoding: utf-8 require 'spec_helper' describe 'CLI' do before do `rm -rf #{folder}` end after do `rm -rf #{folder}` end def folder "/tmp/parallel_tests_tests" end def write(file, content) path = "#{folder}/#{file}" ensure_folder File.dirname(path) File.open(path, 'w'){|f| f.write content } path end def read(file) File.read "#{folder}/#{file}" end def bin_folder "#{File.expand_path(File.dirname(__FILE__))}/../bin" end def executable(options={}) "#{bin_folder}/parallel_#{options[:type] || 'test'}" end def ensure_folder(folder) `mkdir -p #{folder}` unless File.exist?(folder) end def run_tests(test_folder, options={}) ensure_folder folder processes = "-n #{options[:processes]||2}" unless options[:processes] == false command = "cd #{folder} && #{options[:export]} #{executable(options)} #{test_folder} #{processes} #{options[:add]} 2>&1" result = `#{command}` raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail] result end it "runs tests in parallel" do write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}' write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}' result = run_tests "spec", :type => 'rspec' # test ran and gave their puts result.should include('TEST1') result.should include('TEST2') # all results present result.scan('1 example, 0 failure').size.should == 2 # 2 results result.scan('2 examples, 0 failures').size.should == 1 # 1 summary result.scan(/Finished in \d+\.\d+ seconds/).size.should == 2 result.scan(/Took \d+\.\d+ seconds/).size.should == 1 # parallel summary end it "runs tests which outputs accented characters" do write "spec/xxx_spec.rb", "#encoding: utf-8\ndescribe('it'){it('should'){puts 'Byłem tu'}}" result = run_tests "spec", :type => 'rspec' # test ran and gave their puts result.should include('Byłem tu') end it "does not run any tests if there are none" do write 'spec/xxx_spec.rb', '1' result = run_tests "spec", :type => 'rspec' result.should include('No examples found') result.should include('Took') end it "fails when tests fail" do write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}' write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){1.should == 2}}' result = run_tests "spec", :fail => true, :type => 'rspec' result.scan('1 example, 1 failure').size.should == 1 result.scan('1 example, 0 failure').size.should == 1 result.scan('2 examples, 1 failure').size.should == 1 end it "can serialize stdout" do write 'spec/xxx_spec.rb', '5.times{describe("it"){it("should"){sleep 0.01; puts "TEST1"}}}' write 'spec/xxx2_spec.rb', 'sleep 0.01; 5.times{describe("it"){it("should"){sleep 0.01; puts "TEST2"}}}' result = run_tests "spec", :type => 'rspec', :add => "--serialize-stdout" result.should_not =~ /TEST1.*TEST2.*TEST1/m result.should_not =~ /TEST2.*TEST1.*TEST2/m end context "with given commands" do it "can exec given commands with ENV['TEST_ENV_NUM']" do result = `#{executable} -e 'ruby -e "print ENV[:TEST_ENV_NUMBER.to_s].to_i"' -n 4` result.gsub('"','').split('').sort.should == %w[0 2 3 4] end it "can exec given command non-parallel" do result = `#{executable} -e 'ruby -e "sleep(rand(10)/100.0); puts ENV[:TEST_ENV_NUMBER.to_s].inspect"' -n 4 --non-parallel` result.split("\n").should == %w["" "2" "3" "4"] end it "can serialize stdout" do result = `#{executable} -e 'ruby -e "5.times{sleep 0.01;puts ENV[:TEST_ENV_NUMBER.to_s].to_i;STDOUT.flush}"' -n 2 --serialize-stdout` result.should_not =~ /0.*2.*0/m result.should_not =~ /2.*0.*2/m end it "exists with success if all sub-processes returned success" do system("#{executable} -e 'cat /dev/null' -n 4").should == true end it "exists with failure if any sub-processes returned failure" do system("#{executable} -e 'test -e xxxx' -n 4").should == false end end it "runs through parallel_rspec" do version = `#{executable} -v` `#{bin_folder}/parallel_rspec -v`.should == version end it "runs through parallel_cucumber" do version = `#{executable} -v` `#{bin_folder}/parallel_cucumber -v`.should == version end it "runs through parallel_spinach" do version = `#{executable} -v` `#{bin_folder}/parallel_spinach -v`.should == version end it "runs with --group-by found" do # it only tests that it does not blow up, as it did before fixing... write "spec/x1_spec.rb", "puts '111'" run_tests "spec", :type => 'rspec', :add => '--group-by found' end it "runs faster with more processes" do pending if RUBY_PLATFORM == "java" # just too slow ... 2.times{|i| write "spec/xxx#{i}_spec.rb", 'describe("it"){it("should"){sleep 5}}; $stderr.puts ENV["TEST_ENV_NUMBER"]' } t = Time.now run_tests("spec", :processes => 2, :type => 'rspec') expected = 10 (Time.now - t).should <= expected end it "can run with given files" do write "spec/x1_spec.rb", "puts '111'" write "spec/x2_spec.rb", "puts '222'" write "spec/x3_spec.rb", "puts '333'" result = run_tests "spec/x1_spec.rb spec/x3_spec.rb", :type => 'rspec' result.should include('111') result.should include('333') result.should_not include('222') end it "runs successfully without any files" do results = run_tests "", :type => 'rspec' results.should include("2 processes for 0 specs") results.should include("Took") end it "can run with test-options" do write "spec/x1_spec.rb", "111" write "spec/x2_spec.rb", "111" result = run_tests "spec", :add => "--test-options ' --version'", :processes => 2, :type => 'rspec' result.should =~ /\d+\.\d+\.\d+.*\d+\.\d+\.\d+/m # prints version twice end it "runs with PARALLEL_TEST_PROCESSORS processes" do processes = 5 processes.times{|i| write "spec/x#{i}_spec.rb", "puts %{ENV-\#{ENV['TEST_ENV_NUMBER']}-}" } result = run_tests "spec", :export => "PARALLEL_TEST_PROCESSORS=#{processes}", :processes => processes, :type => 'rspec' result.scan(/ENV-.?-/).should =~ ["ENV--", "ENV-2-", "ENV-3-", "ENV-4-", "ENV-5-"] end it "filters test by given pattern and relative paths" do write "spec/x_spec.rb", "puts 'XXX'" write "spec/y_spec.rb", "puts 'YYY'" write "spec/z_spec.rb", "puts 'ZZZ'" result = run_tests "spec", :add => "-p '^spec/(x|z)'", :type => "rspec" result.should include('XXX') result.should_not include('YYY') result.should include('ZZZ') end it "can wait_for_other_processes_to_finish" do pending if RUBY_PLATFORM == "java" # just too slow ... write "test/a_test.rb", "require 'parallel_tests'; sleep 0.5 ; ParallelTests.wait_for_other_processes_to_finish; puts 'a'" write "test/b_test.rb", "sleep 1; puts 'b'" write "test/c_test.rb", "sleep 1.5; puts 'c'" write "test/d_test.rb", "sleep 2; puts 'd'" run_tests("test", :processes => 4).should include("b\nc\nd\na\n") end it "can run only a single group" do pending if RUBY_PLATFORM == "java" # just too slow ... write "test/long_test.rb", "puts 'this is a long test'" write "test/short_test.rb", "puts 'short test'" group_1_result = run_tests("test", :processes => 2, :add => '--only-group 1') group_1_result.should include("this is a long test") group_1_result.should_not include("short test") group_2_result = run_tests("test", :processes => 2, :add => '--only-group 2') group_2_result.should_not include("this is a long test") group_2_result.should include("short test") end context "Test::Unit" do it "runs" do write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end" result = run_tests("test") result.should include('1 test') end it "passes test options" do write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end" result = run_tests("test", :add => '--test-options "-v"') result.should include('test_xxx') # verbose output of every test end it "runs successfully without any files" do results = run_tests("") results.should include("2 processes for 0 tests") results.should include("Took") end end context "Cucumber" do before do write "features/steps/a.rb", " Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" } And('I sleep a bit'){ sleep 0.2 } And('I pass'){ true } And('I fail'){ fail } " end it "runs tests which outputs accented characters" do write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print accented characters" write "features/steps/a.rb", "#encoding: utf-8\nGiven('I print accented characters'){ puts \"I tu też\" }" result = run_tests "features", :type => "cucumber", :add => '--pattern good' result.should include('I tu też') end it "passes TEST_ENV_NUMBER when running with pattern (issue #86)" do write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" write "features/good2.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" write "features/b.feature", "Feature: xxx\n Scenario: xxx\n Given I FAIL" write "features/steps/a.rb", "Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }" result = run_tests "features", :type => "cucumber", :add => '--pattern good' result.should include('YOUR TEST ENV IS 2!') result.should include('YOUR TEST ENV IS !') result.should_not include('I FAIL') end it "writes a runtime log" do pending "TODO find out why this fails" if RUBY_PLATFORM == "java" log = "tmp/parallel_runtime_cucumber.log" write(log, "x") 2.times{|i| # needs sleep so that runtime loggers dont overwrite each other initially write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n And I sleep a bit" } run_tests "features", :type => "cucumber" read(log).gsub(/\.\d+/,'').split("\n").should =~ [ "features/good0.feature:0", "features/good1.feature:0" ] end it "runs each feature once when there are more processes then features (issue #89)" do 2.times{|i| write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" } result = run_tests "features", :type => "cucumber", :add => '-n 3' result.scan(/YOUR TEST ENV IS \d?!/).sort.should == ["YOUR TEST ENV IS !", "YOUR TEST ENV IS 2!"] end it "runs successfully without any files" do results = run_tests("", :type => "cucumber") results.should include("2 processes for 0 features") results.should include("Took") end it "collates failing scenarios" do write "features/pass.feature", "Feature: xxx\n Scenario: xxx\n Given I pass" write "features/fail1.feature", "Feature: xxx\n Scenario: xxx\n Given I fail" write "features/fail2.feature", "Feature: xxx\n Scenario: xxx\n Given I fail" results = run_tests "features", :processes => 3, :type => "cucumber", :fail => true results.should include """ Failing Scenarios: cucumber features/fail2.feature:2 # Scenario: xxx cucumber features/fail1.feature:2 # Scenario: xxx 3 scenarios (2 failed, 1 passed) 3 steps (2 failed, 1 passed) """ end it "groups by scenario" do write "features/long.feature", <<-EOS Feature: xxx Scenario: xxx Given I print TEST_ENV_NUMBER Scenario: xxx Given I print TEST_ENV_NUMBER Scenario Outline: xxx Given I print TEST_ENV_NUMBER Examples: | num | | one | | two | EOS result = run_tests "features", :type => "cucumber", :add => "--group-by scenarios" result.should include("2 processes for 4 scenarios") end it "groups by step" do write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" write "features/good2.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" result = run_tests "features", :type => "cucumber", :add => '--group-by steps' result.should include("2 processes for 2 features") end end context "Spinach", :fails_on_ruby_187 => true do before do write "features/steps/a.rb", "class A < Spinach::FeatureSteps\n Given 'I print TEST_ENV_NUMBER' do\n puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\"\n end\n And 'I sleep a bit' do\n sleep 0.2\n end\nend" end it "runs tests which outputs accented characters" do write "features/good1.feature", "Feature: a\n Scenario: xxx\n Given I print accented characters" write "features/steps/a.rb", "#encoding: utf-8\nclass A < Spinach::FeatureSteps\nGiven 'I print accented characters' do\n puts \"I tu też\" \n end\nend" result = run_tests "features", :type => "spinach", :add => 'features/good1.feature'#, :add => '--pattern good' result.should include('I tu też') end it "passes TEST_ENV_NUMBER when running with pattern (issue #86)" do write "features/good1.feature", "Feature: a\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" write "features/good2.feature", "Feature: a\n Scenario: xxx\n Given I print TEST_ENV_NUMBER" write "features/b.feature", "Feature: b\n Scenario: xxx\n Given I FAIL" #Expect this not to be run write "features/steps/a.rb", "class A < Spinach::FeatureSteps\nGiven('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }\nend" result = run_tests "features", :type => "spinach", :add => '--pattern good' result.should include('YOUR TEST ENV IS 2!') result.should include('YOUR TEST ENV IS !') result.should_not include('I FAIL') end it "writes a runtime log" do pending 'not yet implemented -- custom runtime logging' log = "tmp/parallel_runtime_spinach.log" write(log, "x") 2.times{|i| # needs sleep so that runtime loggers dont overwrite each other initially write "features/good#{i}.feature", "Feature: A\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n And I sleep a bit" } result = run_tests "features", :type => "spinach" read(log).gsub(/\.\d+/,'').split("\n").should =~ [ "features/good0.feature:0", "features/good1.feature:0" ] end it "runs each feature once when there are more processes then features (issue #89)" do 2.times{|i| write "features/good#{i}.feature", "Feature: A\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n" } result = run_tests "features", :type => "spinach", :add => '-n 3' result.scan(/YOUR TEST ENV IS \d?!/).sort.should == ["YOUR TEST ENV IS !", "YOUR TEST ENV IS 2!"] end it "runs successfully without any files" do results = run_tests("", :type => "spinach") results.should include("2 processes for 0 features") results.should include("Took") end end end