snap_ci-parallel_tests 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 64a3cfc18f6ec422dcec8eb046042c4cea62e645
4
+ data.tar.gz: e93d0f1bbd0c1318f185867dcf89c3f431983611
5
+ SHA512:
6
+ metadata.gz: d735091f3c891b157948e2642edbc69b1c57e23e36543299c2696d2a0ed4a85739d4e8dd084614a88c2bcb93a07c23fe40c6001249dac15e704d3c8971dccfbb
7
+ data.tar.gz: 4920b30f0b069989337624808180f995ce4d320f2ef5bbe4cc602d18dc107c97098f8d2fa22973eeb4726eb669222ffb974e0d82556bfc6874023ced4ffa84bc
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+ vendor/bundle
data/.init.sh ADDED
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ BUNDLE_PATH=/tmp/.bundle-$(basename $(pwd))
6
+ export BUNDLE_JOBS="${BUNDLE_JOBS:=4}"
7
+ if [ ! -z "$SNAP_CI" ]
8
+ then
9
+ BUNDLE_PATH=$HOME/.bundle
10
+ fi
11
+
12
+ mkdir -p $BUNDLE_PATH
13
+ rm -rf vendor/bundle
14
+ ln -sf $BUNDLE_PATH vendor/bundle
15
+
16
+ export NOKOGIRI_USE_SYSTEM_LIBRARIES=1
17
+
18
+ while read line; do
19
+ [[ -n ${SNAP_CI} || -n ${GO_SERVER_URL} ]] || echo -ne "Doing $((C++)) things...\r"
20
+ done < <(bundle check || bundle install --local --path vendor/bundle --clean)
21
+
22
+ echo
23
+ echo "Done!"
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ snap_ci-parallel_tests
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p598
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in snap_ci-parallel_tests.gemspec
4
+ gemspec
5
+
6
+ gem 'test-unit'
7
+ gem 'rspec', '>=2.12.0'
8
+ gem 'rspec-rails', '>=2.12.0'
9
+ gem 'rake'
data/Gemfile.lock ADDED
@@ -0,0 +1,76 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ snap_ci-parallel_tests (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actionpack (4.1.8)
10
+ actionview (= 4.1.8)
11
+ activesupport (= 4.1.8)
12
+ rack (~> 1.5.2)
13
+ rack-test (~> 0.6.2)
14
+ actionview (4.1.8)
15
+ activesupport (= 4.1.8)
16
+ builder (~> 3.1)
17
+ erubis (~> 2.7.0)
18
+ activesupport (4.1.8)
19
+ i18n (~> 0.6, >= 0.6.9)
20
+ json (~> 1.7, >= 1.7.7)
21
+ minitest (~> 5.1)
22
+ thread_safe (~> 0.1)
23
+ tzinfo (~> 1.1)
24
+ builder (3.2.2)
25
+ diff-lcs (1.2.5)
26
+ erubis (2.7.0)
27
+ i18n (0.6.11)
28
+ json (1.8.1)
29
+ minitest (5.4.3)
30
+ power_assert (0.2.2)
31
+ rack (1.5.2)
32
+ rack-test (0.6.2)
33
+ rack (>= 1.0)
34
+ railties (4.1.8)
35
+ actionpack (= 4.1.8)
36
+ activesupport (= 4.1.8)
37
+ rake (>= 0.8.7)
38
+ thor (>= 0.18.1, < 2.0)
39
+ rake (10.4.2)
40
+ rspec (3.1.0)
41
+ rspec-core (~> 3.1.0)
42
+ rspec-expectations (~> 3.1.0)
43
+ rspec-mocks (~> 3.1.0)
44
+ rspec-core (3.1.7)
45
+ rspec-support (~> 3.1.0)
46
+ rspec-expectations (3.1.2)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.1.0)
49
+ rspec-mocks (3.1.3)
50
+ rspec-support (~> 3.1.0)
51
+ rspec-rails (3.1.0)
52
+ actionpack (>= 3.0)
53
+ activesupport (>= 3.0)
54
+ railties (>= 3.0)
55
+ rspec-core (~> 3.1.0)
56
+ rspec-expectations (~> 3.1.0)
57
+ rspec-mocks (~> 3.1.0)
58
+ rspec-support (~> 3.1.0)
59
+ rspec-support (3.1.2)
60
+ test-unit (3.0.7)
61
+ power_assert
62
+ thor (0.19.1)
63
+ thread_safe (0.3.4)
64
+ tzinfo (1.2.2)
65
+ thread_safe (~> 0.1)
66
+
67
+ PLATFORMS
68
+ ruby
69
+
70
+ DEPENDENCIES
71
+ bundler (~> 1.7)
72
+ rake
73
+ rspec (>= 2.12.0)
74
+ rspec-rails (>= 2.12.0)
75
+ snap_ci-parallel_tests!
76
+ test-unit
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 ThoughtWorks, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # SnapCI::ParallelTests
2
+
3
+ Run Test::Unit / RSpec in parallel across multiple workers on [Snap CI](https://snap-ci.com).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'snap_ci-parallel_tests'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install snap_ci-parallel_tests
20
+
21
+ ## Setup for non-rails
22
+
23
+ Depending on the framework of your choice -
24
+
25
+ $ bundle exec snap_ci-parallel_rspec [options] [files or directories] [-- [rspec options]]
26
+ $ bundle exec snap_ci-parallel_test [options] [files or directories] [-- [Test::Unit or MiniTest options]]
27
+
28
+
29
+ ## Setup for Rails
30
+
31
+ Ensure that 'parallel_tests' is present in your development group
32
+
33
+ ```ruby
34
+ # Gemfile
35
+ gem "parallel_tests", :group => :development
36
+ ```
37
+
38
+ ### Run
39
+
40
+ $ bundle exec snap_ci-parallel_rspec [options] [files or directories] [-- [rspec options]]
41
+ $ bundle exec snap_ci-parallel_test [options] [files or directories] [-- [Test::Unit or MiniTest options]]
42
+
43
+ Alternatively -
44
+
45
+ ```shell
46
+ $ bundle exec rake snap-parallel # to run all specs
47
+ $ bundle exec rake snap-parallel:models # to run only model specs
48
+ $ bundle exec rake snap-parallel:controllers # to run only controllers specs
49
+ $ bundle exec rake -T snap-parallel # to list all tasks
50
+ ```
51
+
52
+
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it ( https://github.com/[my-github-username]/snap_ci-parallel_tests/fork )
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+ require 'snap_ci/parallel_tests'
4
+ require 'snap_ci/parallel_tests/cli'
5
+ require 'snap_ci/parallel_tests/rspec/runner'
6
+
7
+ SnapCI::ParallelTests::CLI.new(SnapCI::ParallelTests::RSpec::Runner.new, ARGV).run
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+ require 'snap_ci/parallel_tests'
4
+ require 'snap_ci/parallel_tests/cli'
5
+ require 'snap_ci/parallel_tests/test/runner'
6
+
7
+ SnapCI::ParallelTests::CLI.new(SnapCI::ParallelTests::Test::Runner.new, ARGV).run
@@ -0,0 +1,130 @@
1
+ require 'optparse'
2
+
3
+ module SnapCI
4
+ module ParallelTests
5
+ class CLI
6
+ attr_reader :runner, :argv, :test_opts
7
+
8
+ def initialize(runner, argv)
9
+ @runner = runner
10
+
11
+ @argv = argv.dup
12
+ @test_opts = nil
13
+
14
+ if split_index = @argv.index('--')
15
+ @test_opts = @argv.drop(split_index + 1).join(' ')
16
+ @argv = @argv.take(split_index)
17
+ end
18
+ end
19
+
20
+ def run
21
+ options = parse!
22
+ test_files = find_all_tests(options[:files_or_dirs], options)
23
+
24
+ if options[:trace]
25
+ $stderr.puts "Found the following #{runner.test_file_name}s"
26
+ $stderr.puts test_files.collect { |t| " - #{t}" }.join("\n")
27
+ end
28
+
29
+ report_number_of_tests(test_files, SnapCI::ParallelTests.total_workers)
30
+
31
+ test_files_to_run = SnapCI::ParallelTests.partition(
32
+ things: test_files,
33
+ total_workers: SnapCI::ParallelTests.total_workers,
34
+ current_worker_index: SnapCI::ParallelTests.worker_index,
35
+ group_by: options[:group_by]
36
+ )
37
+
38
+ if test_files_to_run.empty?
39
+ $stderr.puts 'No tests to run'
40
+ else
41
+ if options[:trace]
42
+ $stderr.puts "Will run the following #{runner.test_file_name}s and ignore others"
43
+ $stderr.puts test_files.collect { |t| " - #{t}" }.join("\n")
44
+ end
45
+ runner.execute(test_files_to_run, options)
46
+ end
47
+
48
+
49
+ end
50
+
51
+ private
52
+
53
+ def find_all_tests(tests, options = {})
54
+ (tests || []).map do |file_or_folder|
55
+ if File.directory?(file_or_folder)
56
+ files = files_in_folder(file_or_folder, options)
57
+ files.grep(runner.test_suffix).grep(options[:pattern]||//)
58
+ else
59
+ file_or_folder
60
+ end
61
+ end.flatten.uniq
62
+ end
63
+
64
+ def files_in_folder(folder, options={})
65
+ pattern = if options[:symlinks] == false # not nil or true
66
+ '**/*'
67
+ else
68
+ # follow one symlink and direct children
69
+ # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
70
+ '**{,/*/**}/*'
71
+ end
72
+ Dir[File.join(folder, pattern)].uniq
73
+ end
74
+
75
+ def report_number_of_tests(tests, total_workers)
76
+ name = runner.test_file_name
77
+ num_tests = tests.size
78
+ $stderr.puts "#{total_workers} workers for #{num_tests} #{name}s, ~ #{(num_tests.to_f/total_workers).ceil} #{name}s per process"
79
+ end
80
+
81
+ def parse!
82
+ options = {}
83
+
84
+ options[:group_by] = :filename
85
+
86
+ parser = OptionParser.new do |opts|
87
+ runner.cli_helper.render_header(opts)
88
+ opts.separator 'supported options:'
89
+
90
+ runner.cli_helper.render_options(opts)
91
+
92
+ opts.on('-g', '--group-by TYPE', <<-TEXT) do |type|
93
+ group tests by:
94
+ filename - order of finding files(default)
95
+ filesize - by size of the file
96
+ TEXT
97
+ raise unless %w(name filesize).include?(type)
98
+ options[:group_by] = type.to_sym
99
+ end
100
+
101
+ opts.on('-v', '--version', 'Show Version') do
102
+ puts "SnapCI Parallel Tests v#{SnapCI::ParallelTests::VERSION}"
103
+ exit
104
+ end
105
+
106
+ opts.on('-t', '--trace', 'Turn on verbose mode') do |trace|
107
+ options[:trace] = trace
108
+ end
109
+
110
+ opts.on('-h', '--help', 'Show this help screen.') do
111
+ puts opts
112
+ exit
113
+ end
114
+
115
+ runner.cli_helper.render_footer(opts)
116
+ end
117
+
118
+ parser.parse!(argv)
119
+
120
+ options[:files_or_dirs] = argv
121
+ options[:test_opts] = test_opts
122
+
123
+ if options[:trace]
124
+ $stderr.puts "trace - got options #{options.inspect}"
125
+ end
126
+ options
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,15 @@
1
+ module SnapCI
2
+ module ParallelTests
3
+ class Grouper
4
+ class <<self
5
+ def group_by_filename(things)
6
+ things.sort
7
+ end
8
+
9
+ def group_by_filesize(things)
10
+ things.sort { |a, b| File.size(a) <=> File.size(b) }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module SnapCI
2
+ module ParallelTests
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ load 'snap_ci/parallel_tests/tasks/parallel_specs.rake'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module SnapCI
2
+ module ParallelTests
3
+ module RSpec
4
+ module CLIHelper
5
+ def render_header(opts)
6
+ opts.banner = <<BANNER
7
+ Usage: #{opts.program_name} [options] [files or directories] [-- [rspec options]]
8
+
9
+ Example: #{opts.program_name} spec/models spec/controllers/foo_controller_spec.rb -- --format documentation --fail-fast
10
+ BANNER
11
+ end
12
+
13
+ def render_options(opts)
14
+ opts.on('-p', '--pattern [PATTERN]', 'run tests matching this pattern') do |pattern|
15
+ options[:pattern] = /#{pattern}/
16
+ end
17
+ end
18
+
19
+ def render_footer(opts)
20
+ opts.separator "\nRun `rspec --help' for supported rspec options."
21
+ end
22
+
23
+ extend CLIHelper
24
+ end #CLIHelper
25
+
26
+ end #RSpec
27
+ end #ParallelTests
28
+ end #SnapCI
@@ -0,0 +1,72 @@
1
+ require 'shellwords'
2
+ require 'snap_ci/parallel_tests/rspec/cli_helper'
3
+
4
+ module SnapCI
5
+ module ParallelTests
6
+ module RSpec
7
+ class Runner
8
+
9
+ def execute(test_files, options)
10
+ exe = determine_executable
11
+ version = (exe =~ /\brspec\b/ ? 2 : 1)
12
+
13
+ test_files = test_files.map { |f| Shellwords.escape(f) }
14
+
15
+ cmd = [exe, options[:test_opts], (rspec_2_color if version == 2), *test_files].compact.join(' ')
16
+ options = options.merge(env: rspec_1_color) #if version == 1
17
+
18
+ $stderr.puts(cmd)
19
+ exec((options[:env] || {}), cmd)
20
+ end
21
+
22
+ def cli_helper
23
+ CLIHelper
24
+ end
25
+
26
+ def test_suffix
27
+ /_spec\.rb$/
28
+ end
29
+
30
+ def test_file_name
31
+ 'spec'
32
+ end
33
+
34
+ private
35
+
36
+ def determine_executable
37
+ cmd = case
38
+ when File.exists?('bin/rspec')
39
+ WINDOWS ? 'ruby bin/rspec' : 'bin/rspec'
40
+ when File.file?('script/spec')
41
+ 'script/spec'
42
+ when SnapCI::ParallelTests.bundler_enabled?
43
+ cmd = (output_of('bundle show rspec-core') =~ %r{Could not find gem.*} ? 'spec' : 'rspec')
44
+ "bundle exec #{cmd}"
45
+ else
46
+ %w[spec rspec].detect { |cmd| system "#{cmd} --version > #{DEV_NULL} 2>&1" }
47
+ end
48
+
49
+ cmd or raise("Can't find executables rspec or spec")
50
+ end
51
+
52
+ def output_of(cmd)
53
+ `#{cmd}`
54
+ end
55
+
56
+ def rspec_2_color
57
+ '--color --tty' if $stdout.tty?
58
+ end
59
+
60
+ def rspec_1_color
61
+ if $stdout.tty?
62
+ { 'RSPEC_COLOR' => '1' }
63
+ else
64
+ {}
65
+ end
66
+ end
67
+
68
+ end #Runner
69
+
70
+ end #RSpec
71
+ end #ParallelTests
72
+ end #SnapCI
@@ -0,0 +1,59 @@
1
+ #try and see what frameworks are available
2
+
3
+ %w(
4
+ rspec-rails
5
+ rspec/core
6
+ rspec/core/rake_task
7
+ ).each do |library|
8
+ begin
9
+ require library
10
+ rescue LoadError
11
+ puts "could not require #{library}"
12
+ end
13
+ end
14
+
15
+ num_workers = SnapCI::ParallelTests.total_workers
16
+ worker_index = SnapCI::ParallelTests.worker_index
17
+
18
+ desc 'Run all specs in spec directory (excluding plugin specs)'
19
+ RSpec::Core::RakeTask.new(:'snap-parallel' => 'snap-parallel:prepare') do |t|
20
+ all_specs = FileList['./spec/**{,/*/**}/*_spec.rb'].sort
21
+ specs_to_run = SnapCI::ParallelTests.partition(things: all_specs, total_workers: num_workers, current_worker_index: worker_index)
22
+
23
+ if specs_to_run && specs_to_run.count > 0
24
+ t.pattern = specs_to_run
25
+ end
26
+ end
27
+
28
+ namespace :'snap-parallel' do
29
+ types = begin
30
+ dirs = Dir['./spec/**/*_spec.rb'].
31
+ map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') }.
32
+ uniq.
33
+ select { |f| File.directory?(f) }
34
+ Hash[dirs.map { |d| [d.split('/').last, d] }]
35
+ end
36
+
37
+ task :prepare do
38
+ ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test'
39
+ if Rails.configuration.generators.options[:rails][:orm] == :active_record
40
+ if Rake::Task.task_defined?('test:prepare')
41
+ Rake::Task['test:prepare'].invoke
42
+ end
43
+ end
44
+ end
45
+
46
+ types.each do |type, dir|
47
+ desc "Run the code examples in #{dir}"
48
+ all_specs = FileList["./#{dir}/**/*_spec.rb"].sort
49
+
50
+ RSpec::Core::RakeTask.new(type => 'snap-parallel:prepare') do |t|
51
+ specs_to_run = SnapCI::ParallelTests.partition(things: all_specs, total_workers: num_workers, current_worker_index: worker_index)
52
+
53
+ if specs_to_run && specs_to_run.count > 0
54
+ t.pattern = specs_to_run
55
+ end
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,28 @@
1
+ module SnapCI
2
+ module ParallelTests
3
+ module Test
4
+ module CLIHelper
5
+ def render_header(opts)
6
+ opts.banner = <<BANNER
7
+ Usage: #{opts.program_name} [options] [files or directories] [-- [Test::Unit or MiniTest options]]
8
+
9
+ Example: #{opts.program_name} test/models test/controllers/foo_controller_test.rb -- --verbose --seed 10
10
+ BANNER
11
+ end
12
+
13
+ def render_options(opts)
14
+ opts.on('-p', '--pattern [PATTERN]', 'run tests matching this pattern') do |pattern|
15
+ options[:pattern] = /#{pattern}/
16
+ end
17
+ end
18
+
19
+ def render_footer(opts)
20
+ opts.separator "\nRun `ruby -r test/test_helper -e1 --help' for supported Test::Unit or MiniTest options."
21
+ end
22
+
23
+ extend CLIHelper
24
+ end #CLIHelper
25
+
26
+ end #RSpec
27
+ end #ParallelTests
28
+ end #SnapCI
@@ -0,0 +1,53 @@
1
+ require 'shellwords'
2
+ require 'snap_ci/parallel_tests/test/cli_helper'
3
+
4
+ module SnapCI
5
+ module ParallelTests
6
+ module Test
7
+ class Runner
8
+
9
+ def execute(test_files, options)
10
+ test_files = test_files.map { |f| Shellwords.escape(f) }
11
+
12
+ cmd = ['ruby', '-Itest', test_files, options[:test_opts]].flatten.compact.join(' ')
13
+
14
+ $stderr.puts(cmd)
15
+
16
+ exec(cmd)
17
+ end
18
+
19
+ def cli_helper
20
+ CLIHelper
21
+ end
22
+
23
+ def test_suffix
24
+ /_(test|spec).rb$/
25
+ end
26
+
27
+ def test_file_name
28
+ 'test'
29
+ end
30
+
31
+ private
32
+
33
+ def output_of(cmd)
34
+ `#{cmd}`
35
+ end
36
+
37
+ def rspec_2_color
38
+ '--color --tty' if $stdout.tty?
39
+ end
40
+
41
+ def rspec_1_color
42
+ if $stdout.tty?
43
+ { 'RSPEC_COLOR' => '1' }
44
+ else
45
+ {}
46
+ end
47
+ end
48
+
49
+ end #Runner
50
+
51
+ end #RSpec
52
+ end #ParallelTests
53
+ end #SnapCI
@@ -0,0 +1,5 @@
1
+ module SnapCI
2
+ module ParallelTests
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,71 @@
1
+ require 'snap_ci/parallel_tests/version'
2
+ require 'snap_ci/parallel_tests/grouper'
3
+ require 'snap_ci/parallel_tests/railtie' if defined?(Rails::Railtie)
4
+
5
+ module SnapCI
6
+ module ParallelTests
7
+ WINDOWS = (RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
8
+
9
+ # Partitions the a bunch of things to be run on multiple workers, it partitions based on the following options
10
+ #
11
+ # ==== Options
12
+ # +things+ - the things to partition
13
+ # +total_workers+ - the total number of workers, defaults to ParallelTests.total_workers
14
+ # +current_worker_index+ - the current worker index (1 based, NOT 0 based), defaults to ParallelTests.worker_index
15
+ # +group_by+ - either :filename or :filesize (defaults to :filename). Determines how files are sorted before being partitioned
16
+ def partition(options={})
17
+ things = options[:things]
18
+ total_workers = options[:total_workers] || ParallelTests.total_workers
19
+ current_worker_index = options[:current_worker_index] || ParallelTests.worker_index
20
+ group_by = options[:group_by] || :filename
21
+
22
+ return [] if things.nil? || things.empty?
23
+ things = Grouper.send("group_by_#{group_by}", things)
24
+
25
+ result = []
26
+
27
+ # pick up things on a round-robin basis to distribute them evenly
28
+ index = current_worker_index - 1
29
+ while index <= things.count do
30
+ result << things[index]
31
+ index += total_workers
32
+ end
33
+ result.compact!
34
+
35
+ result
36
+ end
37
+
38
+ def total_workers
39
+ if ENV['SNAP_WORKER_TOTAL']
40
+ ENV['SNAP_WORKER_TOTAL'].to_i
41
+ else
42
+ 1
43
+ end
44
+ end
45
+
46
+ def worker_index
47
+ if ENV['SNAP_WORKER_INDEX']
48
+ ENV['SNAP_WORKER_INDEX'].to_i
49
+ else
50
+ 1
51
+ end
52
+ end
53
+
54
+ def bundler_enabled?
55
+ return true if Object.const_defined?(:Bundler)
56
+
57
+ previous = nil
58
+ current = File.expand_path(Dir.pwd)
59
+
60
+ until !File.directory?(current) || current == previous
61
+ filename = File.join(current, 'Gemfile')
62
+ return true if File.exists?(filename)
63
+ current, previous = File.expand_path('..', current), current
64
+ end
65
+
66
+ false
67
+ end
68
+
69
+ extend SnapCI::ParallelTests
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'snap_ci/parallel_tests/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "snap_ci-parallel_tests"
8
+ spec.version = SnapCI::ParallelTests::VERSION
9
+ spec.authors = ["Ketan Padegaonkar"]
10
+ spec.email = ["KetanPadegaonkar@gmail.com"]
11
+ spec.summary = %q{Run Test::Unit / RSpec in parallel across multiple workers on Snap CI}
12
+ spec.description = %q{Run Test::Unit / RSpec in parallel across multiple workers on Snap CI}
13
+ spec.homepage = "https://github.com/snap-ci/parallel-tests"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe SnapCI::ParallelTests do
4
+ include SnapCI::ParallelTests
5
+
6
+ it 'should create uniform partitions of things based on count and index' do
7
+ things = (1..10).to_a
8
+
9
+ partition1 = partition(things: things, total_workers: 3, current_worker_index: 1)
10
+ partition2 = partition(things: things, total_workers: 3, current_worker_index: 2)
11
+ partition3 = partition(things: things, total_workers: 3, current_worker_index: 3)
12
+
13
+ expect(partition1 + partition2 + partition3).to contain_exactly(*things)
14
+
15
+ expect(partition1).to eq([1, 4, 7, 10])
16
+ expect(partition2).to eq([2, 5, 8])
17
+ expect(partition3).to eq([3, 6, 9])
18
+ end
19
+
20
+ it 'should create empty partitions if things are empty' do
21
+ things = []
22
+
23
+ partition1 = partition(things: things, total_workers: 3, current_worker_index: 1)
24
+ partition2 = partition(things: things, total_workers: 3, current_worker_index: 2)
25
+ partition3 = partition(things: things, total_workers: 3, current_worker_index: 3)
26
+
27
+ expect(partition1).to eq([])
28
+ expect(partition2).to eq([])
29
+ expect(partition3).to eq([])
30
+ end
31
+
32
+ it 'should create empty partitions at end if partitions are more than number of things' do
33
+ things = [1, 2]
34
+
35
+ partition1 = partition(things: things, total_workers: 3, current_worker_index: 1)
36
+ partition2 = partition(things: things, total_workers: 3, current_worker_index: 2)
37
+ partition3 = partition(things: things, total_workers: 3, current_worker_index: 3)
38
+
39
+ expect(partition1).to eq([1])
40
+ expect(partition2).to eq([2])
41
+ expect(partition3).to eq([])
42
+ end
43
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'snap_ci/parallel_tests'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snap_ci-parallel_tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ketan Padegaonkar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Run Test::Unit / RSpec in parallel across multiple workers on Snap CI
42
+ email:
43
+ - KetanPadegaonkar@gmail.com
44
+ executables:
45
+ - snap_ci-parallel_rspec
46
+ - snap_ci-parallel_test
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - .gitignore
51
+ - .init.sh
52
+ - .ruby-gemset
53
+ - .ruby-version
54
+ - Gemfile
55
+ - Gemfile.lock
56
+ - LICENSE.txt
57
+ - README.md
58
+ - Rakefile
59
+ - bin/snap_ci-parallel_rspec
60
+ - bin/snap_ci-parallel_test
61
+ - lib/snap_ci/parallel_tests.rb
62
+ - lib/snap_ci/parallel_tests/cli.rb
63
+ - lib/snap_ci/parallel_tests/grouper.rb
64
+ - lib/snap_ci/parallel_tests/railtie.rb
65
+ - lib/snap_ci/parallel_tests/rspec/cli_helper.rb
66
+ - lib/snap_ci/parallel_tests/rspec/runner.rb
67
+ - lib/snap_ci/parallel_tests/tasks/parallel_specs.rake
68
+ - lib/snap_ci/parallel_tests/test/cli_helper.rb
69
+ - lib/snap_ci/parallel_tests/test/runner.rb
70
+ - lib/snap_ci/parallel_tests/version.rb
71
+ - snap_ci-parallel_tests.gemspec
72
+ - spec/snap_ci/parallel_tests_spec.rb
73
+ - spec/spec_helper.rb
74
+ homepage: https://github.com/snap-ci/parallel-tests
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.4.4
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Run Test::Unit / RSpec in parallel across multiple workers on Snap CI
98
+ test_files:
99
+ - spec/snap_ci/parallel_tests_spec.rb
100
+ - spec/spec_helper.rb