parallel_rspec 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1df19064c0de30bc25fe56463553c51354d38319
4
- data.tar.gz: 41552dac37c0fff6e8f04612493d4593ea68aa25
3
+ metadata.gz: cd859eb40b317c48862eb67b4f24f3fa50e4bea8
4
+ data.tar.gz: 6c04d37e7c0828a2b3bfa7112e9819a28b814c91
5
5
  SHA512:
6
- metadata.gz: 7458d557acec03dfce6c25e3908d363b30c69d3bb896c1fd633471c25ce32f862d8b3b23ab4419247cd9730def619578fee671a1fa797881fc72372dacdd39d1
7
- data.tar.gz: b7d6e86fe55ddc5e926c5452243e9e035c0dc6024f8570d6d4bb306b32a1dbf42cc0dd19fad10ac25ec47133dbe3c7dd7c96f6afff8a4e8e7008ebc672633cf5
6
+ metadata.gz: 10d65506bd40f980d529362af9b5e95e3da72d0be1eda7bdb86ec52f16ac903b38fb9a717c2ca63da7c2533e639b7a1368aa067a7623287af42337a4aa7ca076
7
+ data.tar.gz: ef27474707581225d8514111a55c8859459979932730dade8872f62a70781dd4318ae51db90c33405aaac2674ffb067ef9fc71b39376030084824fd4dd514414
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Will Bryant
3
+ Copyright (c) 2015 Powershop New Zealand Ltd
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -12,13 +12,17 @@ group :development, :test do
12
12
  end
13
13
  ```
14
14
 
15
- And then execute:
15
+ Or if you use Spring:
16
16
 
17
- $ bundle
17
+ ```ruby
18
+ group :development, :test do
19
+ gem 'spring-prspec'
20
+ end
21
+ ```
18
22
 
19
- Or install it yourself as:
23
+ And then execute:
20
24
 
21
- $ gem install parallel_rspec
25
+ $ bundle
22
26
 
23
27
  This version of ParallelRSpec has been tested with RSpec 3.3.
24
28
 
@@ -38,6 +42,17 @@ You're then ready to run specs in parallel:
38
42
 
39
43
  $ bundle exec prspec spec/my_spec.rb spec/another_spec.rb
40
44
 
45
+ Or if you use Spring:
46
+
47
+ $ bundle exec spring prspec spec/my_spec.rb spec/another_spec.rb
48
+
49
+ You may like to make an alias:
50
+
51
+ $ alias prspec='bundle exec spring prspec'
52
+ $ prspec spec/my_spec.rb spec/another_spec.rb
53
+
54
+ When you change WORKERS, don't forget to restart Spring and re-run the create and populate steps above if necessary.
55
+
41
56
  ## Contributing
42
57
 
43
58
  Bug reports and pull requests are welcome on GitHub at https://github.com/willbryant/parallel_rspec.
@@ -1,4 +1,8 @@
1
1
  require "parallel_rspec/version"
2
+ require "parallel_rspec/channel"
2
3
  require "parallel_rspec/workers"
4
+ require "parallel_rspec/example"
5
+ require "parallel_rspec/server"
6
+ require "parallel_rspec/client"
3
7
  require "parallel_rspec/runner"
4
8
  require "parallel_rspec/railtie"
@@ -0,0 +1,54 @@
1
+ module ParallelRSpec
2
+ # Adapted from nitra.
3
+ # Copyright 2012-2013 Roger Nesbitt, Powershop Limited, YouDo Limited. MIT licence.
4
+ class Channel
5
+ ProtocolInvalidError = Class.new(StandardError)
6
+
7
+ attr_reader :rd, :wr
8
+ attr_accessor :raise_epipe_on_write_error
9
+
10
+ def initialize(rd, wr)
11
+ @rd = rd
12
+ @wr = wr
13
+ @rd.binmode
14
+ @wr.binmode
15
+ end
16
+
17
+ def self.pipe
18
+ c_rd, s_wr = IO.pipe("ASCII-8BIT", "ASCII-8BIT")
19
+ s_rd, c_wr = IO.pipe("ASCII-8BIT", "ASCII-8BIT")
20
+ [new(c_rd, c_wr), new(s_rd, s_wr)]
21
+ end
22
+
23
+ def self.read_select(channels)
24
+ fds = IO.select(channels.collect(&:rd))
25
+ fds.first.collect do |fd|
26
+ channels.detect {|c| c.rd == fd}
27
+ end
28
+ end
29
+
30
+ def close
31
+ rd.close
32
+ wr.close
33
+ end
34
+
35
+ def read
36
+ return unless line = rd.gets
37
+ if result = line.strip.match(/\ACOMMAND,(\d+)\z/)
38
+ data = rd.read(result[1].to_i)
39
+ Marshal.load(data)
40
+ else
41
+ raise ProtocolInvalidError, "Expected command length line, got #{line.inspect}"
42
+ end
43
+ end
44
+
45
+ def write(data)
46
+ encoded = Marshal.dump(data)
47
+ wr.write("COMMAND,#{encoded.bytesize}\n")
48
+ wr.write(encoded)
49
+ wr.flush
50
+ rescue Errno::EPIPE
51
+ raise if raise_epipe_on_write_error
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,61 @@
1
+ module ParallelRSpec
2
+ class Client
3
+ attr_reader :channel_to_server
4
+
5
+ def initialize(channel_to_server)
6
+ @channel_to_server = channel_to_server
7
+ end
8
+
9
+ def example_group_started(group)
10
+ # not implemented yet - would need the same extraction/simplification for serialization as Example below
11
+ end
12
+
13
+ def example_group_finished(group)
14
+ # ditto
15
+ end
16
+
17
+ def example_started(example)
18
+ channel_to_server.write([:example_started, serialize_example(example)])
19
+ end
20
+
21
+ def example_passed(example)
22
+ channel_to_server.write([:example_passed, serialize_example(example)])
23
+ end
24
+
25
+ def example_failed(example)
26
+ channel_to_server.write([:example_failed, serialize_example(example)])
27
+ end
28
+
29
+ def example_pending(example)
30
+ channel_to_server.write([:example_pending, serialize_example(example)])
31
+ end
32
+
33
+ def deprecation(hash)
34
+ channel_to_server.write([:deprecation, hash])
35
+ end
36
+
37
+ def serialize_example(example)
38
+ Example.new(
39
+ example.id,
40
+ example.description,
41
+ example.exception,
42
+ example.location_rerun_argument,
43
+ example.metadata.slice(
44
+ :absolute_file_path,
45
+ :described_class,
46
+ :description,
47
+ :description_args,
48
+ :execution_result,
49
+ :full_description,
50
+ :file_path,
51
+ :last_run_status,
52
+ :line_number,
53
+ :location,
54
+ :pending,
55
+ :rerun_file_path,
56
+ :scoped_id,
57
+ :shared_group_inclusion_backtrace,
58
+ :type))
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,16 @@
1
+ module ParallelRSpec
2
+ # only the good bits of RSpec's Example class, those needed by the reporters and formatters and
3
+ # marshallable.
4
+ Example = Struct.new(:id, :description, :exception, :location_rerun_argument, :metadata) do
5
+ def self.delegate_to_metadata(key)
6
+ define_method(key) { metadata[key] }
7
+ end
8
+
9
+ delegate_to_metadata :execution_result
10
+ delegate_to_metadata :file_path
11
+ delegate_to_metadata :full_description
12
+ delegate_to_metadata :location
13
+ delegate_to_metadata :pending
14
+ delegate_to_metadata :skip
15
+ end
16
+ end
@@ -62,8 +62,10 @@ module ParallelRSpec
62
62
  end
63
63
 
64
64
  def run_in_parallel(example_groups, reporter)
65
+ server = Server.new(reporter)
65
66
  workers = Workers.new
66
- workers.run_test_workers do |worker|
67
+ workers.run_test_workers_with_server(server) do |worker, channel_to_server|
68
+ client = Client.new(channel_to_server)
67
69
  index = 0
68
70
  RSpec.world.filtered_examples.each do |group, examples|
69
71
  examples.reject! do |example|
@@ -71,10 +73,10 @@ module ParallelRSpec
71
73
  (index % workers.number_of_workers) != (worker % workers.number_of_workers)
72
74
  end
73
75
  end
74
- success = example_groups.map { |g| g.run(reporter) }.all?
75
- reporter.finish
76
- success
76
+ success = example_groups.map { |g| g.run(client) }.all?
77
+ channel_to_server.write([:result, success])
77
78
  end
79
+ server.success?
78
80
  end
79
81
  end
80
82
  end
@@ -0,0 +1,38 @@
1
+ module ParallelRSpec
2
+ class Server
3
+ attr_reader :reporter
4
+
5
+ def initialize(reporter)
6
+ @reporter = reporter
7
+ @success = true
8
+ end
9
+
10
+ def example_started(example)
11
+ reporter.example_started(example)
12
+ end
13
+
14
+ def example_passed(example)
15
+ reporter.example_passed(example)
16
+ end
17
+
18
+ def example_failed(example)
19
+ reporter.example_failed(example)
20
+ end
21
+
22
+ def example_pending(example)
23
+ reporter.example_pending(example)
24
+ end
25
+
26
+ def deprecation(hash)
27
+ reporter.deprecation(hash)
28
+ end
29
+
30
+ def result(result)
31
+ @success &&= result
32
+ end
33
+
34
+ def success?
35
+ @success
36
+ end
37
+ end
38
+ end
@@ -1,17 +1,17 @@
1
- require 'lib/parallel_workers.rb'
1
+ require 'parallel_rspec/workers.rb'
2
2
 
3
3
  db_namespace = namespace :db do
4
4
  namespace :parallel do
5
5
  # desc "Creates the test database"
6
6
  task :create => [:load_config] do
7
- ParallelWorkers.new.run_test_workers do |worker|
7
+ ParallelRSpec::Workers.new.run_test_workers do |worker|
8
8
  ActiveRecord::Tasks::DatabaseTasks.create ActiveRecord::Base.configurations['test']
9
9
  end
10
10
  end
11
11
 
12
12
  # desc "Empty the test database"
13
13
  task :purge => %w(environment load_config) do
14
- ParallelWorkers.new.run_test_workers do |worker|
14
+ ParallelRSpec::Workers.new.run_test_workers do |worker|
15
15
  ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
16
16
  end
17
17
  end
@@ -20,7 +20,7 @@ db_namespace = namespace :db do
20
20
  task :load_schema => %w(db:parallel:purge) do
21
21
  should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
22
22
  begin
23
- ParallelWorkers.new.run_test_workers do |worker|
23
+ ParallelRSpec::Workers.new.run_test_workers do |worker|
24
24
  ActiveRecord::Schema.verbose = false
25
25
  ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
26
26
  end
@@ -33,7 +33,7 @@ db_namespace = namespace :db do
33
33
 
34
34
  # desc "Recreate the test database from an existent structure.sql file"
35
35
  task :load_structure => %w(db:parallel:purge) do
36
- ParallelWorkers.new.run_test_workers do |worker|
36
+ ParallelRSpec::Workers.new.run_test_workers do |worker|
37
37
  ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA']
38
38
  end
39
39
  end
@@ -1,3 +1,3 @@
1
1
  module ParallelRSpec
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -13,24 +13,55 @@ module ParallelRSpec
13
13
  end
14
14
 
15
15
  def run_test_workers
16
- children = (1..number_of_workers).collect do |worker|
16
+ child_pids = (1..number_of_workers).collect do |worker|
17
17
  fork do
18
18
  establish_test_database_connection(worker)
19
19
  yield worker
20
20
  end
21
21
  end
22
22
 
23
- verify_children(children)
23
+ verify_children(child_pids)
24
+ end
25
+
26
+ def run_test_workers_with_server(server)
27
+ child_pids, channels = (1..number_of_workers).collect do |worker|
28
+ channel_to_client, channel_to_server = ParallelRSpec::Channel.pipe
29
+
30
+ pid = fork do
31
+ channel_to_client.close
32
+ establish_test_database_connection(worker)
33
+ yield worker, channel_to_server
34
+ end
35
+
36
+ channel_to_server.close
37
+ [pid, channel_to_client]
38
+ end.transpose
39
+
40
+ invoke_server_for_channels(server, channels)
41
+
42
+ verify_children(child_pids)
43
+ end
44
+
45
+ def invoke_server_for_channels(server, channels)
46
+ while !channels.empty?
47
+ Channel.read_select(channels).each do |channel|
48
+ if command = channel.read
49
+ server.send(*command)
50
+ else
51
+ channels.delete(channel)
52
+ end
53
+ end
54
+ end
24
55
  end
25
56
 
26
57
  def establish_test_database_connection(worker)
27
58
  ENV['TEST_ENV_NUMBER'] = worker.to_s
28
- ActiveRecord::Base.configurations['test']['database'] << worker.to_s unless worker.zero?
59
+ ActiveRecord::Base.configurations['test']['database'] << worker.to_s unless worker == 1
29
60
  ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
30
61
  end
31
62
 
32
- def verify_children(children)
33
- results = children.collect { |pid| Process.wait2(pid).last }.reject(&:success?)
63
+ def verify_children(child_pids)
64
+ results = child_pids.collect { |pid| Process.wait2(pid).last }.reject(&:success?)
34
65
 
35
66
  unless results.empty?
36
67
  STDERR.puts "\n#{results.size} worker#{'s' unless results.size == 1} failed"
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Will Bryant, Powershop New Zealand Ltd"]
10
10
  spec.email = ["will.bryant@gmail.com"]
11
11
 
12
- spec.summary = %q{This gem lets you run your RSpec examples in parallel across across your CPUs..}
13
- spec.description = %q{This gem lets you run your RSpec examples in parallel across across your CPUs. Each worker automatically gets its own database to avoid conflicts.}
12
+ spec.summary = %q{This gem lets you run your RSpec examples in parallel across across your CPUs.}
13
+ spec.description = %q{This gem lets you run your RSpec examples in parallel across across your CPUs. Each worker automatically gets its own database to avoid conflicts. The optional spring-prspec gem adds support for running under Spring.}
14
14
  spec.homepage = "https://github.com/willbryant/parallel_rspec"
15
15
  spec.license = "MIT"
16
16
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Bryant, Powershop New Zealand Ltd
@@ -39,7 +39,8 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  description: This gem lets you run your RSpec examples in parallel across across your
42
- CPUs. Each worker automatically gets its own database to avoid conflicts.
42
+ CPUs. Each worker automatically gets its own database to avoid conflicts. The
43
+ optional spring-prspec gem adds support for running under Spring.
43
44
  email:
44
45
  - will.bryant@gmail.com
45
46
  executables:
@@ -55,8 +56,12 @@ files:
55
56
  - Rakefile
56
57
  - exe/prspec
57
58
  - lib/parallel_rspec.rb
59
+ - lib/parallel_rspec/channel.rb
60
+ - lib/parallel_rspec/client.rb
61
+ - lib/parallel_rspec/example.rb
58
62
  - lib/parallel_rspec/railtie.rb
59
63
  - lib/parallel_rspec/runner.rb
64
+ - lib/parallel_rspec/server.rb
60
65
  - lib/parallel_rspec/tasks.rake
61
66
  - lib/parallel_rspec/version.rb
62
67
  - lib/parallel_rspec/workers.rb
@@ -85,5 +90,5 @@ rubygems_version: 2.2.2
85
90
  signing_key:
86
91
  specification_version: 4
87
92
  summary: This gem lets you run your RSpec examples in parallel across across your
88
- CPUs..
93
+ CPUs.
89
94
  test_files: []