paraduct 0.0.1.beta1

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.
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ repo_token: URGfczWN3SlKQMXnocUt2yZbyeFtMThPg
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ spec/tmp/
16
+ vendor/bundle/
17
+ .ruby-version
data/.hound.yml ADDED
@@ -0,0 +1,25 @@
1
+ Style/LineLength:
2
+ Description: 'Limit lines to 130 characters.'
3
+ Max: 130
4
+ Style/SpaceInsideParens:
5
+ Enabled: false
6
+ Style/SpaceBeforeBlockBraces:
7
+ Enabled: false
8
+ StringLiterals:
9
+ Enabled: false
10
+ Style/TrailingComma:
11
+ Enabled: false
12
+ Style/BlockComments:
13
+ Enabled: false
14
+ Style/NilComparison:
15
+ Enabled: false
16
+ Style/Documentation:
17
+ Enabled: false
18
+ Style/RegexpLiteral:
19
+ Enabled: false
20
+ Style/SignalException:
21
+ Enabled: false
22
+ Style/CaseEquality:
23
+ Enabled: false
24
+ Style/SpaceBeforeComma:
25
+ Enabled: false
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - ruby-head
8
+ bundler_args: --jobs=2
9
+ before_script:
10
+ - export CODECLIMATE_REPO_TOKEN=bfa8a7ca9d8e18e9cad7593885aa11477a3c4b6b53f70f6be8ce60f470b48770
11
+ script:
12
+ - bundle exec rspec
13
+ branches:
14
+ only:
15
+ - master
16
+ notifications:
17
+ email: false
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: 2.2
21
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in paraduct.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 sue445
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,38 @@
1
+ # Paraduct
2
+
3
+ [![Build Status](https://travis-ci.org/sue445/paraduct.svg?branch=master)](https://travis-ci.org/sue445/paraduct)
4
+ [![Code Climate](https://codeclimate.com/github/sue445/paraduct/badges/gpa.svg)](https://codeclimate.com/github/sue445/paraduct)
5
+ [![Coverage Status](https://img.shields.io/coveralls/sue445/paraduct.svg)](https://coveralls.io/r/sue445/paraduct)
6
+ [![Dependency Status](https://gemnasium.com/sue445/paraduct.svg)](https://gemnasium.com/sue445/paraduct)
7
+
8
+ TODO: Write a gem description
9
+
10
+ [![Stories in Ready](https://badge.waffle.io/sue445/paraduct.svg?label=ready&title=Ready)](http://waffle.io/sue445/paraduct)
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'paraduct'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install paraduct
27
+
28
+ ## Usage
29
+
30
+ TODO: Write usage instructions here
31
+
32
+ ## Contributing
33
+
34
+ 1. Fork it ( https://github.com/sue445/paraduct/fork )
35
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
36
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
37
+ 4. Push to the branch (`git push origin my-new-feature`)
38
+ 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
+
data/bin/paraduct ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'paraduct/cli'
4
+
5
+ Paraduct::CLI.start
data/lib/paraduct.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "paraduct/version"
2
+ require "paraduct/configuration"
3
+ require "paraduct/variable_converter"
4
+ require "paraduct/runner"
5
+ require "paraduct/parallel_runner"
6
+ require "active_support/core_ext"
7
+
8
+ module Paraduct
9
+ class << self
10
+ def configuration
11
+ Paraduct::Configuration.instance
12
+ end
13
+ alias :config :configuration
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require 'paraduct'
2
+
3
+ module Paraduct
4
+ module CLI
5
+ def self.start
6
+ script = Paraduct.config.script
7
+ raise "require script" if script.blank?
8
+
9
+ variables = Paraduct.config.variables
10
+ raise "require variables" if variables.blank?
11
+
12
+ product_variables = Paraduct::VariableConverter.product(variables)
13
+ Paraduct::ParallelRunner.perform_all(script, product_variables)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module Paraduct
2
+ require "singleton"
3
+ require "yaml"
4
+
5
+ class Configuration
6
+ include Singleton
7
+
8
+ def initialize
9
+ @config = YAML.load_file(config_file)
10
+ end
11
+
12
+ def variables
13
+ @config["variables"]
14
+ end
15
+
16
+ def script
17
+ @config["script"]
18
+ end
19
+
20
+ def work_dir
21
+ _work_dir = @config["work_dir"] || "tmp/paraduct_workspace"
22
+ root_dir.join(_work_dir)
23
+ end
24
+
25
+ def config_file
26
+ root_dir.join(".paraduct.yml")
27
+ end
28
+
29
+ def root_dir
30
+ Pathname.pwd
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ module Paraduct
2
+ class ParallelRunner
3
+ # @param script [String, Array<String>] script file, script(s)
4
+ # @param product_variables [Array<Hash{String => String}>]
5
+ # @return [Array<String>] stdout messages of each job
6
+ def self.perform_all(script, product_variables)
7
+ threads = []
8
+ stdout_messages = []
9
+ base_job_dir = Paraduct.config.work_dir
10
+ FileUtils.mkdir_p(base_job_dir) unless base_job_dir.exist?
11
+
12
+ puts <<-EOS
13
+ ======================================================
14
+ START matrix test
15
+ EOS
16
+ product_variables.each do |params|
17
+ puts "params: #{params}"
18
+ end
19
+
20
+ product_variables.each do |params|
21
+ threads << Thread.new(base_job_dir, script, params) do |_base_job_dir, _script, _params|
22
+ job_dir = setup_runner(_base_job_dir, _params)
23
+ stdout =
24
+ begin
25
+ Paraduct::Runner.perform(_script, _params)
26
+ rescue Paraduct::ProcessError => e
27
+ e.message
28
+ end
29
+
30
+ puts <<-EOS
31
+ ======================================================
32
+ params: #{_params}
33
+ job_dir: #{job_dir}
34
+
35
+ #{stdout}
36
+ EOS
37
+
38
+ stdout_messages << stdout
39
+ end
40
+ end
41
+ threads.map(&:join)
42
+
43
+ stdout_messages
44
+ end
45
+
46
+ # @param source_dir [Pathname]
47
+ # @param destination_dir [Pathname]
48
+ def self.copy_recursive(source_dir, destination_dir)
49
+ FileUtils.mkdir_p(destination_dir)
50
+ source_dir.children.each do |source_child_dir|
51
+ begin
52
+ FileUtils.cp_r(source_child_dir, destination_dir)
53
+ rescue ArgumentError => e
54
+ # TODO: refactoring
55
+ raise unless e.message =~ /^cannot copy directory .+ to itself /
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.setup_runner(base_job_dir, params)
61
+ job_dir = Paraduct::Runner.parameterized_job_dir(base_job_dir, params)
62
+ FileUtils.mkdir_p(job_dir) unless job_dir.exist?
63
+ copy_recursive(Paraduct.config.root_dir, job_dir)
64
+ Dir.chdir(job_dir)
65
+ job_dir
66
+ end
67
+ private_class_method :setup_runner
68
+ end
69
+ end
@@ -0,0 +1,50 @@
1
+ module Paraduct
2
+ require "open3"
3
+
4
+ class ProcessError < StandardError
5
+ attr_reader :status
6
+
7
+ # @param message [String] stdout and stderr
8
+ # @param status [Process::Status]
9
+ def initialize(message, status)
10
+ super(message)
11
+ @status = status
12
+ end
13
+ end
14
+
15
+ class Runner
16
+ # run script with params
17
+ # @param script [String, Array<String>] script file, script(s)
18
+ # @param params [Hash{String => String}] key is capitalized and value is quoted (ex. foo=1 => FOO="1" )
19
+ # @return [String] stdout
20
+ # @raise [Paraduct::ProcessError] command exited error status
21
+ def self.perform(script, params)
22
+ variable_string = capitalize_keys(params).map{ |key, value| %(export #{key}="#{value}";) }.join(" ")
23
+
24
+ Array.wrap(script).inject("") do |stdout, command|
25
+ stdout << run_command("#{variable_string} #{command}")
26
+ stdout
27
+ end
28
+ end
29
+
30
+ def self.parameterized_job_dir(base_job_dir, params)
31
+ dir_name = capitalize_keys(params).map{ |key, value| "#{key}_#{value}" }.join("_")
32
+ Pathname(base_job_dir).join(dir_name)
33
+ end
34
+
35
+ def self.run_command(command)
36
+ stdout, stderr, status = Open3.capture3(command)
37
+ raise ProcessError.new("#{stdout}\n#{stderr}", status) unless status.success?
38
+ stdout
39
+ end
40
+ private_class_method :run_command
41
+
42
+ def self.capitalize_keys(params)
43
+ params.inject({}) do |res, (key, value)|
44
+ res[key.upcase] = value
45
+ res
46
+ end
47
+ end
48
+ private_class_method :capitalize_keys
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ module Paraduct
2
+ class VariableConverter
3
+ # @param variables [Hash{String => Array<String>}] Key: variable name, Value: values_pattern which you want to product
4
+ # @return [Array<Hash{String => String}>]
5
+ def self.product(variables)
6
+ return [] if variables.empty?
7
+
8
+ values_patterns = product_array(variables.values)
9
+
10
+ product_variables = []
11
+ values_patterns.each do |values_pattern|
12
+ entry = {}
13
+ keys = variables.keys.to_enum
14
+ values = values_pattern.to_enum
15
+
16
+ loop do
17
+ key = keys.next
18
+ value = values.next
19
+ entry[key] = value
20
+ end
21
+
22
+ product_variables << entry
23
+ end
24
+ product_variables
25
+ end
26
+
27
+ def self.product_array(array)
28
+ first_values = array.shift
29
+ if array.empty?
30
+ first_values.product
31
+ else
32
+ first_values.product(*array)
33
+ end
34
+ end
35
+ private_class_method :product_array
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Paraduct
2
+ VERSION = "0.0.1.beta1"
3
+ end
data/paraduct.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'paraduct/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "paraduct"
8
+ spec.version = Paraduct::VERSION
9
+ spec.authors = ["sue445"]
10
+ spec.email = ["sue445@sue445.net"]
11
+ spec.summary = %q{matrix test runner}
12
+ spec.description = %q{matrix test runner}
13
+ spec.homepage = "https://github.com/sue445/paraduct"
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_dependency "activesupport"
22
+
23
+ spec.add_development_dependency "bundler", ">= 1.5"
24
+ spec.add_development_dependency "codeclimate-test-reporter"
25
+ spec.add_development_dependency "coveralls"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "pry-nav"
28
+ spec.add_development_dependency "pry-remote"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.1.0"
31
+ spec.add_development_dependency "rspec-collection_matchers"
32
+ spec.add_development_dependency "rspec-temp_dir"
33
+ end
@@ -0,0 +1,14 @@
1
+ script: ./script/build_success.sh
2
+ work_dir: tmp/paraduct_workspace
3
+ variables:
4
+ ruby:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.2
8
+ database:
9
+ - mysql
10
+ - postgresql
11
+ rails:
12
+ - 3.2.0
13
+ - 4.0.0
14
+ - 4.1.0
@@ -0,0 +1,36 @@
1
+ describe Paraduct::CLI do
2
+ describe "#start" do
3
+ subject{ Paraduct::CLI.start }
4
+
5
+ include_context :within_temp_work_dir
6
+
7
+ let(:script){ "./script/build_success.sh" }
8
+ let(:product_variables) do
9
+ [
10
+ { "ruby" => "1.9.3", "database" => "mysql" , "rails" => "3.2.0" },
11
+ { "ruby" => "1.9.3", "database" => "mysql" , "rails" => "4.0.0" },
12
+ { "ruby" => "1.9.3", "database" => "mysql" , "rails" => "4.1.0" },
13
+ { "ruby" => "1.9.3", "database" => "postgresql", "rails" => "3.2.0" },
14
+ { "ruby" => "1.9.3", "database" => "postgresql", "rails" => "4.0.0" },
15
+ { "ruby" => "1.9.3", "database" => "postgresql", "rails" => "4.1.0" },
16
+ { "ruby" => "2.0.0", "database" => "mysql" , "rails" => "3.2.0" },
17
+ { "ruby" => "2.0.0", "database" => "mysql" , "rails" => "4.0.0" },
18
+ { "ruby" => "2.0.0", "database" => "mysql" , "rails" => "4.1.0" },
19
+ { "ruby" => "2.0.0", "database" => "postgresql", "rails" => "3.2.0" },
20
+ { "ruby" => "2.0.0", "database" => "postgresql", "rails" => "4.0.0" },
21
+ { "ruby" => "2.0.0", "database" => "postgresql", "rails" => "4.1.0" },
22
+ { "ruby" => "2.1.2", "database" => "mysql" , "rails" => "3.2.0" },
23
+ { "ruby" => "2.1.2", "database" => "mysql" , "rails" => "4.0.0" },
24
+ { "ruby" => "2.1.2", "database" => "mysql" , "rails" => "4.1.0" },
25
+ { "ruby" => "2.1.2", "database" => "postgresql", "rails" => "3.2.0" },
26
+ { "ruby" => "2.1.2", "database" => "postgresql", "rails" => "4.0.0" },
27
+ { "ruby" => "2.1.2", "database" => "postgresql", "rails" => "4.1.0" },
28
+ ]
29
+ end
30
+
31
+ it "should call perform_all" do
32
+ expect(Paraduct::ParallelRunner).to receive(:perform_all).with(script, product_variables)
33
+ subject
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ describe Paraduct::Configuration do
2
+ let(:config){ Paraduct.configuration }
3
+
4
+ before do
5
+ # use spec/.paraduct.yml
6
+ allow_any_instance_of(Paraduct::Configuration).to receive(:root_dir){ spec_dir }
7
+ end
8
+
9
+ describe "#variables" do
10
+ subject{ config.variables }
11
+
12
+ it "should get variables in .paraduct.yml" do
13
+ is_expected.to match(
14
+ "ruby" => ["1.9.3", "2.0.0", "2.1.2"],
15
+ "database" => ["mysql", "postgresql"],
16
+ "rails" => ["3.2.0", "4.0.0", "4.1.0"],
17
+ )
18
+ end
19
+ end
20
+
21
+ describe "#work_dir" do
22
+ subject{ config.work_dir }
23
+
24
+ it { should eq spec_dir.join("tmp/paraduct_workspace") }
25
+ end
26
+
27
+ end
@@ -0,0 +1,103 @@
1
+ describe Paraduct::ParallelRunner do
2
+ describe "#perform_all" do
3
+ subject{ Paraduct::ParallelRunner.perform_all(script, product_variables) }
4
+
5
+ include_context :within_temp_work_dir
6
+
7
+ # after do
8
+ # puts `tree #{temp_dir}`
9
+ # end
10
+
11
+ shared_examples :create_job_directories do
12
+ let(:job_dir) { temp_dir_path.join("tmp/paraduct_workspace/#{job_name}") }
13
+ let(:copied_file){ job_dir.join("script/build_success.sh") }
14
+
15
+ it { expect(job_dir).to be_exist }
16
+ it { expect(copied_file).to be_exist }
17
+ end
18
+
19
+ describe "with script file test" do
20
+ let(:script){ "./script/build_success.sh" }
21
+ let(:product_variables) do
22
+ [
23
+ { "ruby" => "1.9", "database" => "mysql" },
24
+ { "ruby" => "2.0", "database" => "postgresql" },
25
+ ]
26
+ end
27
+
28
+ it { should include "RUBY=1.9\nDATABASE=mysql\n" }
29
+ it { should include "RUBY=2.0\nDATABASE=postgresql\n" }
30
+
31
+ describe "should create job directories" do
32
+ before do
33
+ # exercise
34
+ subject
35
+ end
36
+
37
+ it_behaves_like :create_job_directories do
38
+ let(:job_name){ "RUBY_1.9_DATABASE_mysql" }
39
+ end
40
+
41
+ it_behaves_like :create_job_directories do
42
+ let(:job_name){ "RUBY_2.0_DATABASE_postgresql" }
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "without script file test" do
48
+ let(:script){ %q(echo "RUBY=${RUBY} DATABASE=${DATABASE}") }
49
+ let(:product_variables) do
50
+ [
51
+ { "ruby" => "1.9", "database" => "mysql" },
52
+ { "ruby" => "2.0", "database" => "postgresql" },
53
+ ]
54
+ end
55
+
56
+ it { should include "RUBY=1.9 DATABASE=mysql\n" }
57
+ it { should include "RUBY=2.0 DATABASE=postgresql\n" }
58
+
59
+ describe "should create job directories" do
60
+ before do
61
+ # exercise
62
+ subject
63
+ end
64
+
65
+ it_behaves_like :create_job_directories do
66
+ let(:job_name){ "RUBY_1.9_DATABASE_mysql" }
67
+ end
68
+
69
+ it_behaves_like :create_job_directories do
70
+ let(:job_name){ "RUBY_2.0_DATABASE_postgresql" }
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#copy_recursive" do
77
+ subject{ Paraduct::ParallelRunner.copy_recursive(source_dir, destination_dir) }
78
+
79
+ include_context "uses temp dir"
80
+
81
+ let(:source_dir) { temp_dir_path }
82
+ let(:destination_dir){ temp_dir_path.join("tmp/paraduct_workspace/RUBY_1.9_DATABASE_mysql") }
83
+ let(:copied_file) { destination_dir.join("build_success.sh") }
84
+ let(:not_copied_file){ destination_dir.join("tmp/paraduct_workspace/dummy.txt") }
85
+
86
+ before do
87
+ # setup
88
+ FileUtils.cp_r(spec_dir.join("script/tmp/paraduct_workspace"), source_dir)
89
+ FileUtils.cp_r(spec_dir.join("script/build_success.sh"), source_dir)
90
+
91
+ # exercise
92
+ subject
93
+ end
94
+
95
+ # after do
96
+ # puts `tree #{source_dir}`
97
+ # end
98
+
99
+ it { expect(destination_dir).to be_exist }
100
+ it { expect(copied_file).to be_exist }
101
+ it { expect(not_copied_file).not_to be_exist }
102
+ end
103
+ end
@@ -0,0 +1,60 @@
1
+ describe Paraduct::Runner do
2
+ describe "#perform" do
3
+ subject{ Paraduct::Runner.perform(script, params) }
4
+
5
+ let(:script) { "./script/build_success.sh" }
6
+ let(:params) { { "ruby" => "1.9", "database" => "mysql" } }
7
+ let(:command){ 'export RUBY="1.9"; export DATABASE="mysql"; ./script/build_success.sh' }
8
+
9
+ context "with mock system" do
10
+ it "script is call with capitalized variable" do
11
+ expect(Paraduct::Runner).to receive(:run_command).with(command).and_return("stdout")
12
+ subject
13
+ end
14
+ end
15
+
16
+ context "with real system" do
17
+ include_context :within_spec_dir
18
+
19
+ context "when success" do
20
+ it { should match /RUBY=1.9/ }
21
+ it { should match /DATABASE=mysql/ }
22
+ end
23
+
24
+ context "when error in script file" do
25
+ let(:script){ "./script/build_error.sh" }
26
+
27
+ let(:stdout) do
28
+ <<-EOS
29
+ RUBY=1.9
30
+ DATABASE=mysql
31
+
32
+ EOS
33
+ end
34
+
35
+ it { expect{ subject }.to raise_error(Paraduct::ProcessError, stdout) }
36
+ end
37
+ end
38
+
39
+ context "with single command" do
40
+ let(:script){ 'echo RUBY=${RUBY}' }
41
+
42
+ it { should eq "RUBY=1.9\n" }
43
+ end
44
+
45
+ context "with multiple commands" do
46
+ let(:script){ ['echo RUBY=${RUBY}', 'echo DATABASE=${DATABASE}'] }
47
+
48
+ it { should eq "RUBY=1.9\nDATABASE=mysql\n" }
49
+ end
50
+ end
51
+
52
+ describe "#parameterized_job_dir" do
53
+ subject{ Paraduct::Runner.parameterized_job_dir(base_job_dir, params) }
54
+
55
+ let(:base_job_dir){ "/tmp/jobs" }
56
+ let(:params) { { "ruby" => "1.9", "database" => "mysql" } }
57
+
58
+ it { should eq Pathname("/tmp/jobs/RUBY_1.9_DATABASE_mysql") }
59
+ end
60
+ end
@@ -0,0 +1,61 @@
1
+ describe Paraduct::VariableConverter do
2
+ describe "#product" do
3
+ subject { Paraduct::VariableConverter.product(variables) }
4
+
5
+ context "with single variable" do
6
+ let(:variables) do
7
+ {
8
+ "ruby" => ["1.9", "2.0", "2.1"],
9
+ }
10
+ end
11
+
12
+ it { should have(3).entries }
13
+ it { should include("ruby" => "1.9") }
14
+ it { should include("ruby" => "2.0") }
15
+ it { should include("ruby" => "2.1") }
16
+ end
17
+
18
+ context "with double variables" do
19
+ let(:variables) do
20
+ {
21
+ "ruby" => ["1.9", "2.0", "2.1"],
22
+ "database" => ["mysql", "postgresql"],
23
+ }
24
+ end
25
+
26
+ it { should have(6).entries }
27
+ it { should include("ruby" => "1.9", "database" => "mysql") }
28
+ it { should include("ruby" => "2.0", "database" => "mysql") }
29
+ it { should include("ruby" => "2.1", "database" => "mysql") }
30
+ it { should include("ruby" => "1.9", "database" => "postgresql") }
31
+ it { should include("ruby" => "2.0", "database" => "postgresql") }
32
+ it { should include("ruby" => "2.1", "database" => "postgresql") }
33
+ end
34
+
35
+ context "with triple variables" do
36
+ let(:variables) do
37
+ {
38
+ "ruby" => ["1.9", "2.0", "2.1"],
39
+ "database" => ["mysql", "postgresql"],
40
+ "rails" => ["3.2", "4.0"],
41
+ }
42
+ end
43
+
44
+ it { should have(12).entries }
45
+ it { should include("ruby" => "1.9", "database" => "mysql" , "rails" => "3.2") }
46
+ it { should include("ruby" => "2.0", "database" => "mysql" , "rails" => "3.2") }
47
+ it { should include("ruby" => "2.1", "database" => "mysql" , "rails" => "3.2") }
48
+ it { should include("ruby" => "1.9", "database" => "postgresql", "rails" => "3.2") }
49
+ it { should include("ruby" => "2.0", "database" => "postgresql", "rails" => "3.2") }
50
+ it { should include("ruby" => "2.1", "database" => "postgresql", "rails" => "3.2") }
51
+ it { should include("ruby" => "1.9", "database" => "mysql" , "rails" => "4.0") }
52
+ it { should include("ruby" => "2.0", "database" => "mysql" , "rails" => "4.0") }
53
+ it { should include("ruby" => "2.1", "database" => "mysql" , "rails" => "4.0") }
54
+ it { should include("ruby" => "1.9", "database" => "postgresql", "rails" => "4.0") }
55
+ it { should include("ruby" => "2.0", "database" => "postgresql", "rails" => "4.0") }
56
+ it { should include("ruby" => "2.1", "database" => "postgresql", "rails" => "4.0") }
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,5 @@
1
+ describe Paraduct do
2
+ it 'has a version number' do
3
+ expect(Paraduct::VERSION).not_to be nil
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ echo "RUBY=${RUBY}"
4
+ echo "DATABASE=${DATABASE}"
5
+ exit 1
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ echo "RUBY=${RUBY}"
4
+ echo "DATABASE=${DATABASE}"
File without changes
@@ -0,0 +1,118 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+ require 'codeclimate-test-reporter'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ CodeClimate::TestReporter::Formatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+ SimpleCov.start do
10
+ %w(/bin/ /vendor/ /spec/).each do |ignore_path|
11
+ add_filter(ignore_path)
12
+ end
13
+ end
14
+
15
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
16
+ require 'paraduct'
17
+ require 'paraduct/cli'
18
+ require 'rspec/collection_matchers'
19
+ require 'rspec/temp_dir'
20
+ require 'pry'
21
+
22
+ def spec_dir
23
+ Pathname(File.dirname(__FILE__))
24
+ end
25
+
26
+ Dir["#{spec_dir}/support/**/*.rb"].sort.each { |f| require f }
27
+
28
+ # This file was generated by the `rspec --init` command. Conventionally, all
29
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
30
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
31
+ # file to always be loaded, without a need to explicitly require it in any files.
32
+ #
33
+ # Given that it is always loaded, you are encouraged to keep this file as
34
+ # light-weight as possible. Requiring heavyweight dependencies from this file
35
+ # will add to the boot time of your test suite on EVERY test run, even for an
36
+ # individual file that may not need all of that loaded. Instead, consider making
37
+ # a separate helper file that requires the additional dependencies and performs
38
+ # the additional setup, and require it from the spec files that actually need it.
39
+ #
40
+ # The `.rspec` file also contains a few flags that are not defaults but that
41
+ # users commonly want.
42
+ #
43
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
44
+ RSpec.configure do |config|
45
+ # rspec-expectations config goes here. You can use an alternate
46
+ # assertion/expectation library such as wrong or the stdlib/minitest
47
+ # assertions if you prefer.
48
+ config.expect_with :rspec do |expectations|
49
+ # This option will default to `true` in RSpec 4. It makes the `description`
50
+ # and `failure_message` of custom matchers include text for helper methods
51
+ # defined using `chain`, e.g.:
52
+ # be_bigger_than(2).and_smaller_than(4).description
53
+ # # => "be bigger than 2 and smaller than 4"
54
+ # ...rather than:
55
+ # # => "be bigger than 2"
56
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
57
+ end
58
+
59
+ # rspec-mocks config goes here. You can use an alternate test double
60
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
61
+ config.mock_with :rspec do |mocks|
62
+ # Prevents you from mocking or stubbing a method that does not exist on
63
+ # a real object. This is generally recommended, and will default to
64
+ # `true` in RSpec 4.
65
+ mocks.verify_partial_doubles = true
66
+ end
67
+
68
+ # The settings below are suggested to provide a good initial experience
69
+ # with RSpec, but feel free to customize to your heart's content.
70
+ =begin
71
+ # These two settings work together to allow you to limit a spec run
72
+ # to individual examples or groups you care about by tagging them with
73
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
74
+ # get run.
75
+ config.filter_run :focus
76
+ config.run_all_when_everything_filtered = true
77
+
78
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
79
+ # For more details, see:
80
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
81
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
82
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
83
+ config.disable_monkey_patching!
84
+
85
+ # This setting enables warnings. It's recommended, but in some cases may
86
+ # be too noisy due to issues in dependencies.
87
+ config.warnings = true
88
+
89
+ # Many RSpec users commonly either run the entire suite or an individual
90
+ # file, and it's useful to allow more verbose output when running an
91
+ # individual spec file.
92
+ if config.files_to_run.one?
93
+ # Use the documentation formatter for detailed output,
94
+ # unless a formatter has already been configured
95
+ # (e.g. via a command-line flag).
96
+ config.default_formatter = 'doc'
97
+ end
98
+
99
+ # Print the 10 slowest examples and example groups at the
100
+ # end of the spec run, to help surface which specs are running
101
+ # particularly slow.
102
+ config.profile_examples = 10
103
+
104
+ # Run specs in random order to surface order dependencies. If you find an
105
+ # order dependency and want to debug it, you can fix the order by providing
106
+ # the seed, which is printed after each run.
107
+ # --seed 1234
108
+ config.order = :random
109
+
110
+ # Seed global randomization in this process using the `--seed` CLI option.
111
+ # Setting this allows you to use `--seed` to deterministically reproduce
112
+ # test failures related to randomization by passing the same `--seed` value
113
+ # as the one that triggered the failure.
114
+ Kernel.srand config.seed
115
+ =end
116
+
117
+ config.order = :random
118
+ end
@@ -0,0 +1,7 @@
1
+ shared_context :within_spec_dir do
2
+ around do |example|
3
+ Dir.chdir(spec_dir) do
4
+ example.run
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ shared_context :within_temp_work_dir do
2
+ include_context "uses temp dir"
3
+
4
+ before do
5
+ # avoid "warning: conflicting chdir during another chdir block"
6
+ @current_dir = Pathname.pwd
7
+ Dir.chdir(temp_dir)
8
+ FileUtils.cp_r(spec_dir.join("script"), temp_dir)
9
+ FileUtils.cp_r(spec_dir.join(".paraduct.yml"), temp_dir)
10
+ end
11
+
12
+ after do
13
+ Dir.chdir(@current_dir)
14
+ end
15
+
16
+ end
metadata ADDED
@@ -0,0 +1,270 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paraduct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.beta1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - sue445
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-10-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '1.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '1.5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: codeclimate-test-reporter
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: coveralls
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: pry-nav
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry-remote
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rake
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: '10.0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: '10.0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rspec
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: 3.1.0
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 3.1.0
158
+ - !ruby/object:Gem::Dependency
159
+ name: rspec-collection_matchers
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: rspec-temp_dir
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ description: matrix test runner
191
+ email:
192
+ - sue445@sue445.net
193
+ executables:
194
+ - paraduct
195
+ extensions: []
196
+ extra_rdoc_files: []
197
+ files:
198
+ - .coveralls.yml
199
+ - .gitignore
200
+ - .hound.yml
201
+ - .rspec
202
+ - .travis.yml
203
+ - Gemfile
204
+ - LICENSE.txt
205
+ - README.md
206
+ - Rakefile
207
+ - bin/paraduct
208
+ - lib/paraduct.rb
209
+ - lib/paraduct/cli.rb
210
+ - lib/paraduct/configuration.rb
211
+ - lib/paraduct/parallel_runner.rb
212
+ - lib/paraduct/runner.rb
213
+ - lib/paraduct/variable_converter.rb
214
+ - lib/paraduct/version.rb
215
+ - paraduct.gemspec
216
+ - spec/.paraduct.yml
217
+ - spec/paraduct/cli_spec.rb
218
+ - spec/paraduct/configuration_spec.rb
219
+ - spec/paraduct/parallel_runner_spec.rb
220
+ - spec/paraduct/runner_spec.rb
221
+ - spec/paraduct/variable_converter_spec.rb
222
+ - spec/paraduct_spec.rb
223
+ - spec/script/build_error.sh
224
+ - spec/script/build_success.sh
225
+ - spec/script/tmp/paraduct_workspace/dummy.txt
226
+ - spec/spec_helper.rb
227
+ - spec/support/contexts/within_spec_dir.rb
228
+ - spec/support/contexts/within_temp_work_dir.rb
229
+ homepage: https://github.com/sue445/paraduct
230
+ licenses:
231
+ - MIT
232
+ post_install_message:
233
+ rdoc_options: []
234
+ require_paths:
235
+ - lib
236
+ required_ruby_version: !ruby/object:Gem::Requirement
237
+ none: false
238
+ requirements:
239
+ - - ! '>='
240
+ - !ruby/object:Gem::Version
241
+ version: '0'
242
+ segments:
243
+ - 0
244
+ hash: 4070294482478458137
245
+ required_rubygems_version: !ruby/object:Gem::Requirement
246
+ none: false
247
+ requirements:
248
+ - - ! '>'
249
+ - !ruby/object:Gem::Version
250
+ version: 1.3.1
251
+ requirements: []
252
+ rubyforge_project:
253
+ rubygems_version: 1.8.23.2
254
+ signing_key:
255
+ specification_version: 3
256
+ summary: matrix test runner
257
+ test_files:
258
+ - spec/.paraduct.yml
259
+ - spec/paraduct/cli_spec.rb
260
+ - spec/paraduct/configuration_spec.rb
261
+ - spec/paraduct/parallel_runner_spec.rb
262
+ - spec/paraduct/runner_spec.rb
263
+ - spec/paraduct/variable_converter_spec.rb
264
+ - spec/paraduct_spec.rb
265
+ - spec/script/build_error.sh
266
+ - spec/script/build_success.sh
267
+ - spec/script/tmp/paraduct_workspace/dummy.txt
268
+ - spec/spec_helper.rb
269
+ - spec/support/contexts/within_spec_dir.rb
270
+ - spec/support/contexts/within_temp_work_dir.rb