friendlyfashion-parallel_tests 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +8 -0
  3. data/Gemfile.lock +44 -0
  4. data/Rakefile +6 -0
  5. data/Readme.md +232 -0
  6. data/ReadmeRails2.md +48 -0
  7. data/bin/parallel_cucumber +2 -0
  8. data/bin/parallel_rspec +2 -0
  9. data/bin/parallel_test +6 -0
  10. data/lib/parallel_tests.rb +30 -0
  11. data/lib/parallel_tests/cli.rb +159 -0
  12. data/lib/parallel_tests/cucumber/gherkin_listener.rb +60 -0
  13. data/lib/parallel_tests/cucumber/runner.rb +90 -0
  14. data/lib/parallel_tests/cucumber/runtime_logger.rb +58 -0
  15. data/lib/parallel_tests/grouper.rb +53 -0
  16. data/lib/parallel_tests/railtie.rb +8 -0
  17. data/lib/parallel_tests/rspec/failures_logger.rb +44 -0
  18. data/lib/parallel_tests/rspec/logger_base.rb +52 -0
  19. data/lib/parallel_tests/rspec/runner.rb +59 -0
  20. data/lib/parallel_tests/rspec/runtime_logger.rb +34 -0
  21. data/lib/parallel_tests/rspec/summary_logger.rb +19 -0
  22. data/lib/parallel_tests/tasks.rb +134 -0
  23. data/lib/parallel_tests/test/runner.rb +134 -0
  24. data/lib/parallel_tests/test/runtime_logger.rb +92 -0
  25. data/lib/parallel_tests/version.rb +3 -0
  26. data/parallel_tests.gemspec +14 -0
  27. data/spec/integration_spec.rb +244 -0
  28. data/spec/parallel_tests/cli_spec.rb +36 -0
  29. data/spec/parallel_tests/cucumber/gherkin_listener_spec.rb +48 -0
  30. data/spec/parallel_tests/cucumber/runner_spec.rb +173 -0
  31. data/spec/parallel_tests/grouper_spec.rb +52 -0
  32. data/spec/parallel_tests/rspec/failure_logger_spec.rb +82 -0
  33. data/spec/parallel_tests/rspec/runner_spec.rb +178 -0
  34. data/spec/parallel_tests/rspec/runtime_logger_spec.rb +76 -0
  35. data/spec/parallel_tests/rspec/summary_logger_spec.rb +37 -0
  36. data/spec/parallel_tests/tasks_spec.rb +151 -0
  37. data/spec/parallel_tests/test/runner_spec.rb +273 -0
  38. data/spec/parallel_tests/test/runtime_logger_spec.rb +84 -0
  39. data/spec/parallel_tests_spec.rb +73 -0
  40. data/spec/spec_helper.rb +151 -0
  41. metadata +109 -0
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe ParallelTests::Test::RuntimeLogger do
4
+ describe :writing do
5
+ around do |example|
6
+ use_temporary_directory_for do
7
+ FileUtils.mkdir_p(File.dirname(log))
8
+ example.call
9
+ end
10
+ end
11
+
12
+ let(:log) { ParallelTests::Test::Runner.runtime_log }
13
+
14
+ it "overwrites the runtime_log file on first log invocation" do
15
+ class FakeTest
16
+ end
17
+ test = FakeTest.new
18
+ time = Time.now
19
+ File.open(log, 'w'){ |f| f.puts("FooBar") }
20
+ ParallelTests::Test::RuntimeLogger.send(:class_variable_set,:@@has_started, false)
21
+ ParallelTests::Test::RuntimeLogger.log(test, time, Time.at(time.to_f+2.00))
22
+ result = File.read(log)
23
+ result.should_not include('FooBar')
24
+ result.should include('test/fake_test.rb:2.00')
25
+ end
26
+
27
+ it "appends to the runtime_log file after first log invocation" do
28
+ class FakeTest
29
+ end
30
+ test = FakeTest.new
31
+ class OtherFakeTest
32
+ end
33
+ other_test = OtherFakeTest.new
34
+
35
+ time = Time.now
36
+ File.open(log, 'w'){ |f| f.puts("FooBar") }
37
+ ParallelTests::Test::RuntimeLogger.send(:class_variable_set,:@@has_started, false)
38
+ ParallelTests::Test::RuntimeLogger.log(test, time, Time.at(time.to_f+2.00))
39
+ ParallelTests::Test::RuntimeLogger.log(other_test, time, Time.at(time.to_f+2.00))
40
+ result = File.read(log)
41
+ result.should_not include('FooBar')
42
+ result.should include('test/fake_test.rb:2.00')
43
+ result.should include('test/other_fake_test.rb:2.00')
44
+ end
45
+ end
46
+
47
+ describe :formatting do
48
+ def call(*args)
49
+ ParallelTests::Test::RuntimeLogger.message(*args)
50
+ end
51
+
52
+ it "formats results for simple test names" do
53
+ class FakeTest
54
+ end
55
+ test = FakeTest.new
56
+ time = Time.now
57
+ call(test, time, Time.at(time.to_f+2.00)).should == 'test/fake_test.rb:2.00'
58
+ end
59
+
60
+ it "formats results for complex test names" do
61
+ class AVeryComplex
62
+ class FakeTest
63
+ end
64
+ end
65
+ test = AVeryComplex::FakeTest.new
66
+ time = Time.now
67
+ call(test, time, Time.at(time.to_f+2.00)).should == 'test/a_very_complex/fake_test.rb:2.00'
68
+ end
69
+
70
+ it "guesses subdirectory structure for rails test classes" do
71
+ module Rails
72
+ end
73
+ class ActionController
74
+ class TestCase
75
+ end
76
+ end
77
+ class FakeControllerTest < ActionController::TestCase
78
+ end
79
+ test = FakeControllerTest.new
80
+ time = Time.now
81
+ call(test, time, Time.at(time.to_f+2.00)).should == 'test/functional/fake_controller_test.rb:2.00'
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,73 @@
1
+ require "spec_helper"
2
+
3
+ describe ParallelTests do
4
+ describe ".determine_number_of_processes" do
5
+ before do
6
+ ENV.delete('PARALLEL_TEST_PROCESSORS')
7
+ Parallel.stub(:processor_count).and_return 20
8
+ end
9
+
10
+ def call(count)
11
+ ParallelTests.determine_number_of_processes(count)
12
+ end
13
+
14
+ it "uses the given count if set" do
15
+ call('5').should == 5
16
+ end
17
+
18
+ it "uses the processor count from Parallel" do
19
+ call(nil).should == 20
20
+ end
21
+
22
+ it "uses the processor count from ENV before Parallel" do
23
+ ENV['PARALLEL_TEST_PROCESSORS'] = '22'
24
+ call(nil).should == 22
25
+ end
26
+
27
+ it "does not use blank count" do
28
+ call(' ').should == 20
29
+ end
30
+
31
+ it "does not use blank env" do
32
+ ENV['PARALLEL_TEST_PROCESSORS'] = ' '
33
+ call(nil).should == 20
34
+ end
35
+ end
36
+
37
+ describe :bundler_enabled? do
38
+ before do
39
+ Object.stub!(:const_defined?).with(:Bundler).and_return false
40
+ end
41
+
42
+ it "should return false" do
43
+ use_temporary_directory_for do
44
+ ParallelTests.send(:bundler_enabled?).should == false
45
+ end
46
+ end
47
+
48
+ it "should return true when there is a constant called Bundler" do
49
+ use_temporary_directory_for do
50
+ Object.stub!(:const_defined?).with(:Bundler).and_return true
51
+ ParallelTests.send(:bundler_enabled?).should == true
52
+ end
53
+ end
54
+
55
+ it "should be true when there is a Gemfile" do
56
+ use_temporary_directory_for do
57
+ FileUtils.touch("Gemfile")
58
+ ParallelTests.send(:bundler_enabled?).should == true
59
+ end
60
+ end
61
+
62
+ it "should be true when there is a Gemfile in the parent directory" do
63
+ use_temporary_directory_for do
64
+ FileUtils.touch(File.join("..", "Gemfile"))
65
+ ParallelTests.send(:bundler_enabled?).should == true
66
+ end
67
+ end
68
+ end
69
+
70
+ it "has a version" do
71
+ ParallelTests::VERSION.should =~ /^\d+\.\d+\.\d+/
72
+ end
73
+ end
@@ -0,0 +1,151 @@
1
+ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
2
+
3
+ FAKE_RAILS_ROOT = './tmp/pspecs/fixtures'
4
+
5
+ require 'tempfile'
6
+ require 'parallel_tests'
7
+ require 'parallel_tests/test/runner'
8
+ require 'parallel_tests/test/runtime_logger'
9
+
10
+ require 'parallel_tests/rspec/runner'
11
+ require 'parallel_tests/rspec/runtime_logger'
12
+ require 'parallel_tests/rspec/summary_logger'
13
+
14
+ require 'parallel_tests/cucumber/runner'
15
+
16
+ OutputLogger = Struct.new(:output) do
17
+ attr_reader :flock, :flush
18
+ def puts(s=nil)
19
+ self.output << s.to_s
20
+ end
21
+ end
22
+
23
+ def mocked_process
24
+ open('|cat /dev/null')
25
+ end
26
+
27
+ def size_of(group)
28
+ group.inject(0) { |sum, test| sum += File.stat(test).size }
29
+ end
30
+
31
+ # Uses /tmp/parallel_tests/application as the cwd so we can create and remove
32
+ # files as we want to. After execution it changes cwd back to the original one.
33
+ def use_temporary_directory_for
34
+ require 'fileutils'
35
+
36
+ dir = File.join("/tmp", "parallel_tests")
37
+ new_dir = File.join(dir, "application")
38
+
39
+ begin
40
+ # just in case the temporary dir already exists
41
+ FileUtils.rm_rf(dir) if File.exists?(dir)
42
+
43
+ # create the temporary directory
44
+ FileUtils.mkdir_p(new_dir)
45
+
46
+ # chdir changes cwd back to the original one after it is done
47
+ Dir.chdir(new_dir) do
48
+ yield
49
+ end
50
+ ensure
51
+ FileUtils.rm_rf(dir) if File.exists?(dir)
52
+ end
53
+ end
54
+
55
+ def test_tests_in_groups(klass, folder, suffix)
56
+ test_root = "#{FAKE_RAILS_ROOT}/#{folder}"
57
+
58
+ describe :tests_in_groups do
59
+ before :all do
60
+ system "rm -rf #{FAKE_RAILS_ROOT}; mkdir -p #{test_root}/temp"
61
+
62
+ @files = [0,1,2,3,4,5,6,7].map do |i|
63
+ size = 99
64
+ file = "#{test_root}/temp/x#{i}#{suffix}"
65
+ File.open(file, 'w') { |f| f.puts 'x' * size }
66
+ file
67
+ end
68
+
69
+ @log = klass.runtime_log
70
+ `mkdir -p #{File.dirname(@log)}`
71
+ `rm -f #{@log}`
72
+ end
73
+
74
+ after :all do
75
+ `rm -f #{klass.runtime_log}`
76
+ end
77
+
78
+ def setup_runtime_log
79
+ File.open(@log,'w') do |f|
80
+ @files[1..-1].each{|file| f.puts "#{file}:#{@files.index(file)}"}
81
+ f.puts "#{@files[0]}:10"
82
+ end
83
+ end
84
+
85
+ it "groups when given an array of files" do
86
+ list_of_files = Dir["#{test_root}/**/*#{suffix}"]
87
+ found = klass.with_runtime_info(list_of_files)
88
+ found.should =~ list_of_files.map{ |file| [file, File.stat(file).size]}
89
+ end
90
+
91
+ it "finds all tests" do
92
+ found = klass.tests_in_groups([test_root], 1)
93
+ all = [ Dir["#{test_root}/**/*#{suffix}"] ]
94
+ (found.flatten - all.flatten).should == []
95
+ end
96
+
97
+ it "partitions them into groups by equal size" do
98
+ groups = klass.tests_in_groups([test_root], 2)
99
+ groups.map{|g| size_of(g)}.should == [400, 400]
100
+ end
101
+
102
+ it 'should partition correctly with a group size of 4' do
103
+ groups = klass.tests_in_groups([test_root], 4)
104
+ groups.map{|g| size_of(g)}.should == [200, 200, 200, 200]
105
+ end
106
+
107
+ it 'should partition correctly with an uneven group size' do
108
+ groups = klass.tests_in_groups([test_root], 3)
109
+ groups.map{|g| size_of(g)}.should =~ [300, 300, 200]
110
+ end
111
+
112
+ it "partitions by runtime when runtime-data is available" do
113
+ klass.stub!(:puts)
114
+ setup_runtime_log
115
+
116
+ groups = klass.tests_in_groups([test_root], 2)
117
+ groups.size.should == 2
118
+ # 10 + 1 + 3 + 5 = 19
119
+ groups[0].should == [@files[0],@files[1],@files[3],@files[5]]
120
+ # 2 + 4 + 6 + 7 = 19
121
+ groups[1].should == [@files[2],@files[4],@files[6],@files[7]]
122
+ end
123
+
124
+ it "alpha-sorts partitions when runtime-data is available" do
125
+ klass.stub!(:puts)
126
+ setup_runtime_log
127
+
128
+ groups = klass.tests_in_groups([test_root], 2)
129
+ groups.size.should == 2
130
+
131
+ groups[0].should == groups[0].sort
132
+ groups[1].should == groups[1].sort
133
+ end
134
+
135
+ it "partitions by round-robin when not sorting" do
136
+ files = ["file1.rb", "file2.rb", "file3.rb", "file4.rb"]
137
+ klass.should_receive(:find_tests).and_return(files)
138
+ groups = klass.tests_in_groups(files, 2, :group_by => :found).sort
139
+ groups[0].should == ["file1.rb", "file3.rb"]
140
+ groups[1].should == ["file2.rb", "file4.rb"]
141
+ end
142
+
143
+ it "alpha-sorts partitions when not sorting by runtime" do
144
+ files = %w[q w e r t y u i o p a s d f g h j k l z x c v b n m]
145
+ klass.should_receive(:find_tests).and_return(files)
146
+ groups = klass.tests_in_groups(files, 2, :group_by => :found).sort
147
+ groups[0].should == groups[0].sort
148
+ groups[1].should == groups[1].sort
149
+ end
150
+ end
151
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: friendlyfashion-parallel_tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Laurynas Butkus
9
+ - Tomas Varaneckas
10
+ - Justas Janauskas
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2013-02-24 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: parallel
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: '0'
32
+ description:
33
+ email:
34
+ - laurynas.butkus@gmail.com
35
+ - tomas.varaneckas@gmail.com
36
+ - jjanauskas@gmail.com
37
+ executables:
38
+ - parallel_cucumber
39
+ - parallel_rspec
40
+ - parallel_test
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - Gemfile.lock
47
+ - Rakefile
48
+ - Readme.md
49
+ - ReadmeRails2.md
50
+ - bin/parallel_cucumber
51
+ - bin/parallel_rspec
52
+ - bin/parallel_test
53
+ - lib/parallel_tests.rb
54
+ - lib/parallel_tests/cli.rb
55
+ - lib/parallel_tests/cucumber/gherkin_listener.rb
56
+ - lib/parallel_tests/cucumber/runner.rb
57
+ - lib/parallel_tests/cucumber/runtime_logger.rb
58
+ - lib/parallel_tests/grouper.rb
59
+ - lib/parallel_tests/railtie.rb
60
+ - lib/parallel_tests/rspec/failures_logger.rb
61
+ - lib/parallel_tests/rspec/logger_base.rb
62
+ - lib/parallel_tests/rspec/runner.rb
63
+ - lib/parallel_tests/rspec/runtime_logger.rb
64
+ - lib/parallel_tests/rspec/summary_logger.rb
65
+ - lib/parallel_tests/tasks.rb
66
+ - lib/parallel_tests/test/runner.rb
67
+ - lib/parallel_tests/test/runtime_logger.rb
68
+ - lib/parallel_tests/version.rb
69
+ - parallel_tests.gemspec
70
+ - spec/integration_spec.rb
71
+ - spec/parallel_tests/cli_spec.rb
72
+ - spec/parallel_tests/cucumber/gherkin_listener_spec.rb
73
+ - spec/parallel_tests/cucumber/runner_spec.rb
74
+ - spec/parallel_tests/grouper_spec.rb
75
+ - spec/parallel_tests/rspec/failure_logger_spec.rb
76
+ - spec/parallel_tests/rspec/runner_spec.rb
77
+ - spec/parallel_tests/rspec/runtime_logger_spec.rb
78
+ - spec/parallel_tests/rspec/summary_logger_spec.rb
79
+ - spec/parallel_tests/tasks_spec.rb
80
+ - spec/parallel_tests/test/runner_spec.rb
81
+ - spec/parallel_tests/test/runtime_logger_spec.rb
82
+ - spec/parallel_tests_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: http://github.com/friendlyfashion/parallel_tests
85
+ licenses:
86
+ - MIT
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.24
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Run Test::Unit / RSpec / Cucumber in parallel
109
+ test_files: []