paraduct 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
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