fork_break 0.1.2 → 0.1.3

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: 47b8e876f9207b88a02d344a00c2d88bc9c39b60
4
- data.tar.gz: 3dd3b0fb931ffb70c12e29b58a663d3bcd68bb06
3
+ metadata.gz: b499c0a1fefcb573b97b0b927c0715efc4b570b7
4
+ data.tar.gz: 4245c08783830dd2819a342e9e02a6247641e110
5
5
  SHA512:
6
- metadata.gz: dca4960c3a050d933cf8628907c74d5fce4663530b3afa393863920e2d21665f900690d36f1f711bc4b5e8448b5b6b295f0f54af83c3a24be2fb2aef14e45930
7
- data.tar.gz: cb72d3c1a9676f5bfd77bff21d254bc6d37c55ef743ca745f64f74e32b84998222a09f8cbf1b39cfd83c3a3f0ff86e79c53a6b2125d64d56361b1886eb7f0154
6
+ metadata.gz: b2ae727120864671c37f8422ff1e9a541829a6751e36cf02b7db2296735e29a1b82134ad5f4241763be90d5d967a1935333b21206fd9efad7d2ab62a9187e866
7
+ data.tar.gz: 58c7081315b18fa3d8296a67006c2dd0caeb229b787b2443ef67af6c6cfa8eedf31a57aedeb79314669a4218b5ffc1391d0e87e20e3862da76d88a20ab4fa7ef
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ Documentation:
2
+ Enabled: false
3
+
4
+ LineLength:
5
+ Max: 120
6
+
7
+ Lint/RescueException:
8
+ Enabled: false
9
+
10
+ SignalException:
11
+ EnforcedStyle: only_raise
12
+ SupportedStyles:
13
+ - only_raise
14
+ - only_fail
15
+ - semantic
16
+
17
+ Metrics/AbcSize:
18
+ Max: 18
19
+
20
+ Metrics/MethodLength:
21
+ Max: 15
data/README.md CHANGED
@@ -98,6 +98,16 @@ puts counter_after_synced_execution("counter_with_lock", true) # => 2
98
98
  puts counter_after_synced_execution("counter_without_lock", false) # => 1
99
99
  ```
100
100
 
101
+ There's also the possibility of adding a predefined timeout to the wait function and having it raise an exception.
102
+
103
+ ```ruby
104
+ process = ForkBreak::Process.new do
105
+ sleep(5)
106
+ end
107
+
108
+ process.finish.wait(timeout: 1) # will raise ForkBreak::WaitTimeout after 1 second
109
+ ```
110
+
101
111
  ## Contributing
102
112
 
103
113
  1. Fork it
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
2
+ require 'bundler/gem_tasks'
data/fork_break.gemspec CHANGED
@@ -1,20 +1,23 @@
1
- # -*- encoding: utf-8 -*-
1
+ require_relative 'lib/fork_break/version'
2
2
 
3
3
  Gem::Specification.new do |gem|
4
- gem.authors = ["Petter Remen"]
5
- gem.email = ["petter.remen@gmail.com"]
6
- gem.summary = %q{Testing multiprocess behaviour is difficult and requires a way to synchronize processes at
7
- specific execution points. This gem allows the parent process to control the behaviour of child processes using
8
- breakpoints. It was originally built for testing the behaviour of database transactions and locking mechanisms. }
9
- gem.description = %q{Fork with breakpoints for syncing child process execution}
10
- gem.homepage = "http://github.com/remen/fork_break"
4
+ gem.authors = ['Petter Remen']
5
+ gem.email = ['petter.remen@gmail.com']
6
+ gem.summary =
7
+ 'Testing multiprocess behaviour is difficult and requires a way to synchronize processes at specific execution ' \
8
+ 'points. This gem allows the parent process to control the behaviour of child processes using breakpoints. It was' \
9
+ 'originally built for testing the behaviour of database transactions and locking mechanisms.'
10
+ gem.description = 'Fork with breakpoints for syncing child process execution'
11
+ gem.homepage = 'http://github.com/remen/fork_break'
12
+ gem.licenses = ['MIT']
11
13
 
12
- gem.files = `git ls-files`.split($\)
13
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
- gem.name = "fork_break"
16
- gem.require_paths = ["lib"]
17
- gem.version = "0.1.2"
18
- gem.add_dependency "fork" , "= 1.0.1"
19
- gem.add_development_dependency "rspec" , "= 3.1.0"
14
+ gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
15
+ gem.executables = gem.files.grep(/^bin\//).map { |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(/^(test|spec|features)\//)
17
+ gem.name = 'fork_break'
18
+ gem.require_paths = ['lib']
19
+ gem.version = ForkBreak::VERSION
20
+ gem.add_dependency 'fork', '= 1.0.1'
21
+ gem.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.0'
22
+ gem.add_development_dependency 'rubocop', '~> 0.27', '>= 0.27.1'
20
23
  end
@@ -0,0 +1,19 @@
1
+ module ForkBreak
2
+ class BreakpointSetter
3
+ def initialize(fork, debug = false)
4
+ @fork = fork
5
+ @next_breakpoint = :forkbreak_start
6
+ @debug = debug
7
+ end
8
+
9
+ def <<(symbol)
10
+ @fork.send_object(symbol)
11
+ if symbol == @next_breakpoint
12
+ @next_breakpoint = @fork.receive_object unless symbol == :forkbreak_end
13
+ puts "#{@fork.pid} received #{@next_breakpoint}" if @debug
14
+ end
15
+ rescue EOFError => exception
16
+ raise @fork.exception || exception
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module ForkBreak
2
+ module Breakpoints
3
+ def breakpoints
4
+ ForkBreak::Process.breakpoint_setter
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,49 @@
1
+ module ForkBreak
2
+ class Process
3
+ class << self
4
+ attr_accessor :breakpoint_setter
5
+ end
6
+
7
+ def initialize(debug = false, &block)
8
+ @debug = debug
9
+ @fork = Fork.new(:return, :to_fork, :from_fork) do |child_fork|
10
+ self.class.breakpoint_setter = breakpoints = BreakpointSetter.new(child_fork, debug)
11
+
12
+ breakpoints << :forkbreak_start
13
+ block.call(breakpoints)
14
+ breakpoints << :forkbreak_end
15
+
16
+ self.class.breakpoint_setter = nil
17
+ end
18
+ end
19
+
20
+ def run_until(breakpoint)
21
+ @next_breakpoint = breakpoint
22
+ @fork.execute unless @fork.pid
23
+ puts "Parent is sending object #{breakpoint} to #{@fork.pid}" if @debug
24
+ @fork.send_object(breakpoint)
25
+ self
26
+ end
27
+
28
+ def wait(options = {})
29
+ # A timeout value of nil will execute the block without any timeout
30
+ Timeout.timeout(options[:timeout], WaitTimeout) do
31
+ loop do
32
+ brk = @fork.receive_object
33
+ puts "Parent is receiving object #{brk} from #{@fork.pid}" if @debug
34
+ if brk == @next_breakpoint
35
+ return self
36
+ elsif brk == :forkbreak_end
37
+ raise BreakpointNotReachedError, "Never reached breakpoint #{@next_breakpoint.inspect}"
38
+ end
39
+ end
40
+ end
41
+ rescue EOFError => exception
42
+ raise @fork.exception || exception
43
+ end
44
+
45
+ def finish
46
+ run_until(:forkbreak_end)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module ForkBreak
2
+ VERSION = '0.1.3'.freeze
3
+ end
data/lib/fork_break.rb CHANGED
@@ -1,74 +1,10 @@
1
1
  require 'fork'
2
+ require 'fork_break/breakpoint_setter'
3
+ require 'fork_break/breakpoints'
4
+ require 'fork_break/process'
5
+ require 'timeout'
2
6
 
3
7
  module ForkBreak
4
- class BreakpointNotReachedError < StandardError ; end
5
-
6
- module Breakpoints
7
- def breakpoints
8
- return ForkBreak::Process.breakpoint_setter
9
- end
10
- end
11
-
12
- class Process
13
- class << self
14
- attr_accessor :breakpoint_setter
15
- end
16
-
17
- def initialize(debug = false, &block)
18
- @debug = debug
19
- @fork = Fork.new(:return, :to_fork, :from_fork) do |child_fork|
20
- self.class.breakpoint_setter = breakpoints = BreakpointSetter.new(child_fork, debug)
21
-
22
- breakpoints << :forkbreak_start
23
- block.call(breakpoints)
24
- breakpoints << :forkbreak_end
25
-
26
- self.class.breakpoint_setter = nil
27
- end
28
- end
29
-
30
- def run_until(breakpoint)
31
- @next_breakpoint = breakpoint
32
- @fork.execute unless @fork.pid
33
- puts "Parent is sending object #{breakpoint} to #{@fork.pid}" if @debug
34
- @fork.send_object(breakpoint)
35
- self
36
- end
37
-
38
- def wait
39
- loop do
40
- brk = @fork.receive_object
41
- puts "Parent is receiving object #{brk} from #{@fork.pid}" if @debug
42
- if brk == @next_breakpoint
43
- return self
44
- elsif brk == :forkbreak_end
45
- raise BreakpointNotReachedError.new("Never reached breakpoint #{@next_breakpoint.inspect}")
46
- end
47
- end
48
- rescue EOFError => exception
49
- raise @fork.exception || exception
50
- end
51
-
52
- def finish
53
- run_until(:forkbreak_end)
54
- end
55
- end
56
-
57
- class BreakpointSetter
58
- def initialize(fork, debug = false)
59
- @fork = fork
60
- @next_breakpoint = :forkbreak_start
61
- @debug = debug
62
- end
63
-
64
- def <<(symbol)
65
- @fork.send_object(symbol)
66
- if symbol == @next_breakpoint
67
- @next_breakpoint = @fork.receive_object unless symbol == :forkbreak_end
68
- puts "#{@fork.pid} received #{@next_breakpoint}" if @debug
69
- end
70
- rescue EOFError => exception
71
- raise @fork.exception || exception
72
- end
73
- end
8
+ class BreakpointNotReachedError < StandardError; end
9
+ class WaitTimeout < StandardError; end
74
10
  end
@@ -1,104 +1,106 @@
1
1
  require 'spec_helper'
2
2
  require 'tmpdir'
3
3
 
4
- module ForkBreak
5
- describe Process do
6
- it "works as intented" do
7
- Dir.mktmpdir do |tmpdir|
8
- first_file = File.join(tmpdir, "first_file")
9
- second_file = File.join(tmpdir, "second_file")
10
- process = Process.new do |breakpoints|
11
- FileUtils.touch(first_file)
12
- breakpoints << :after_first_file
13
- FileUtils.touch(second_file)
14
- end
15
- expect(File.exists?(first_file)).to be_falsey
16
- expect(File.exists?(second_file)).to be_falsey
17
-
18
- process.run_until(:after_first_file).wait
19
- expect(File.exists?(first_file)).to be_truthy
20
- expect(File.exists?(second_file)).to be_falsey
21
-
22
- process.finish.wait
23
- expect(File.exists?(first_file)).to be_truthy
24
- expect(File.exists?(second_file)).to be_truthy
4
+ describe ForkBreak::Process do
5
+ it 'works as intented' do
6
+ Dir.mktmpdir do |tmpdir|
7
+ first_file = File.join(tmpdir, 'first_file')
8
+ second_file = File.join(tmpdir, 'second_file')
9
+ process = ForkBreak::Process.new do |breakpoints|
10
+ FileUtils.touch(first_file)
11
+ breakpoints << :after_first_file
12
+ FileUtils.touch(second_file)
25
13
  end
14
+ expect(File.exist?(first_file)).to be_falsey
15
+ expect(File.exist?(second_file)).to be_falsey
16
+
17
+ process.run_until(:after_first_file).wait
18
+ expect(File.exist?(first_file)).to be_truthy
19
+ expect(File.exist?(second_file)).to be_falsey
20
+
21
+ process.finish.wait
22
+ expect(File.exist?(first_file)).to be_truthy
23
+ expect(File.exist?(second_file)).to be_truthy
26
24
  end
27
-
28
- it "raises an error (on wait) if a breakpoint is not encountered" do
29
- foo = Process.new do |breakpoints|
30
- if false
31
- breakpoints << :will_not_run
32
- end
33
- end
34
- expect do
35
- foo.run_until(:will_not_run).wait
36
- end.to raise_error(BreakpointNotReachedError)
25
+ end
26
+
27
+ it 'raises an error (on wait) if a breakpoint is not encountered' do
28
+ foo = ForkBreak::Process.new do |breakpoints|
29
+ breakpoints << :will_not_run if false # rubocop:disable LiteralInCondition
37
30
  end
31
+ expect do
32
+ foo.run_until(:will_not_run).wait
33
+ end.to raise_error(ForkBreak::BreakpointNotReachedError)
34
+ end
35
+
36
+ it 'works for the documentation example' do
37
+ class FileCounter
38
+ include ForkBreak::Breakpoints
39
+
40
+ def self.open(path, use_lock = true)
41
+ file = File.open(path, File::RDWR | File::CREAT, 0600)
42
+ new(file, use_lock)
43
+ end
44
+
45
+ def initialize(file, use_lock = true)
46
+ @file = file
47
+ @use_lock = use_lock
48
+ end
38
49
 
39
- it "works for the documentation example" do
40
- class FileCounter
41
- include ForkBreak::Breakpoints
42
-
43
- def self.open(path, use_lock = true)
44
- file = File.open(path, File::RDWR|File::CREAT, 0600)
45
- return new(file, use_lock)
46
- end
47
-
48
- def initialize(file, use_lock = true)
49
- @file = file
50
- @use_lock = use_lock
51
- end
52
-
53
- def increase
54
- breakpoints << :before_lock
55
- @file.flock(File::LOCK_EX) if @use_lock
56
- value = @file.read.to_i + 1
57
- breakpoints << :after_read
58
- @file.rewind
59
- @file.write("#{value}\n")
60
- @file.flush
61
- @file.truncate(@file.pos)
62
- end
50
+ def increase
51
+ breakpoints << :before_lock
52
+ @file.flock(File::LOCK_EX) if @use_lock
53
+ value = @file.read.to_i + 1
54
+ breakpoints << :after_read
55
+ @file.rewind
56
+ @file.write("#{value}\n")
57
+ @file.flush
58
+ @file.truncate(@file.pos)
63
59
  end
60
+ end
64
61
 
65
- def counter_after_synced_execution(counter_path, with_lock)
66
- process1, process2 = 2.times.map do
67
- ForkBreak::Process.new do
68
- FileCounter.open(counter_path, with_lock).increase
69
- end
70
- end
62
+ def counter_after_synced_execution(counter_path, with_lock) # rubocop:disable Metrics/AbcSize
63
+ process1, process2 = 2.times.map do
64
+ ForkBreak::Process.new { FileCounter.open(counter_path, with_lock).increase }
65
+ end
71
66
 
72
- process1.run_until(:after_read).wait
67
+ process1.run_until(:after_read).wait
73
68
 
74
- # process2 can't wait for read since it will block
75
- process2.run_until(:before_lock).wait
76
- process2.run_until(:after_read) && sleep(0.1)
69
+ # process2 can't wait for read since it will block
70
+ process2.run_until(:before_lock).wait
71
+ process2.run_until(:after_read) && sleep(0.1)
77
72
 
78
- process1.finish.wait # Finish process1
79
- process2.finish.wait # Finish process2
73
+ process1.finish.wait # Finish process1
74
+ process2.finish.wait # Finish process2
80
75
 
81
- File.read(counter_path).to_i
82
- end
76
+ File.read(counter_path).to_i
77
+ end
83
78
 
84
- Dir.mktmpdir do |tmpdir|
85
- counter_path = File.join(tmpdir, "counter")
79
+ Dir.mktmpdir do |tmpdir|
80
+ counter_path = File.join(tmpdir, 'counter')
86
81
 
87
- expect(counter_after_synced_execution(counter_path, with_lock = true)).to eq(2)
82
+ expect(counter_after_synced_execution(counter_path, true)).to eq(2)
88
83
 
89
- File.unlink(counter_path)
90
- expect(counter_after_synced_execution(counter_path, with_lock = false)).to eq(1)
91
- end
84
+ File.unlink(counter_path)
85
+ expect(counter_after_synced_execution(counter_path, false)).to eq(1)
92
86
  end
87
+ end
93
88
 
94
- it "raises the process exception" do
95
- class MyException < StandardError; end
89
+ it 'raises the process exception' do
90
+ class MyException < StandardError; end
96
91
 
97
- process = ForkBreak::Process.new do
98
- raise MyException
99
- end
92
+ process = ForkBreak::Process.new do
93
+ raise MyException
94
+ end
100
95
 
101
- expect { process.finish.wait }.to raise_error(MyException)
96
+ expect { process.finish.wait }.to raise_error(MyException)
97
+ end
98
+
99
+ it 'raises a wait timeout eror when the process takes longer than the specified wait timeout' do
100
+ process = ForkBreak::Process.new do
101
+ sleep(1)
102
102
  end
103
+
104
+ expect { process.finish.wait(timeout: 0.01) }.to raise_error(ForkBreak::WaitTimeout)
103
105
  end
104
106
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,6 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
- require 'fork_break' # and any other gems you need
1
+ require_relative '../lib/fork_break'
4
2
 
5
3
  RSpec.configure do |config|
6
- config.treat_symbols_as_metadata_keys_with_true_values = true
7
- config.run_all_when_everything_filtered = true
8
- config.filter_run :focus
9
-
10
4
  # Run specs in random order to surface order dependencies. If you find an
11
5
  # order dependency and want to debug it, you can fix the order by providing
12
6
  # the seed, which is printed after each run.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fork_break
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petter Remen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-28 00:00:00.000000000 Z
11
+ date: 2015-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fork
@@ -28,16 +28,42 @@ dependencies:
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '='
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ - - ">="
32
35
  - !ruby/object:Gem::Version
33
36
  version: 3.1.0
34
37
  type: :development
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - '='
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.1'
44
+ - - ">="
39
45
  - !ruby/object:Gem::Version
40
46
  version: 3.1.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: rubocop
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.27'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.27.1
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.27'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.27.1
41
67
  description: Fork with breakpoints for syncing child process execution
42
68
  email:
43
69
  - petter.remen@gmail.com
@@ -47,16 +73,22 @@ extra_rdoc_files: []
47
73
  files:
48
74
  - ".gitignore"
49
75
  - ".rspec"
76
+ - ".rubocop.yml"
50
77
  - Gemfile
51
78
  - LICENSE
52
79
  - README.md
53
80
  - Rakefile
54
81
  - fork_break.gemspec
55
82
  - lib/fork_break.rb
83
+ - lib/fork_break/breakpoint_setter.rb
84
+ - lib/fork_break/breakpoints.rb
85
+ - lib/fork_break/process.rb
86
+ - lib/fork_break/version.rb
56
87
  - spec/fork_break_spec.rb
57
88
  - spec/spec_helper.rb
58
89
  homepage: http://github.com/remen/fork_break
59
- licenses: []
90
+ licenses:
91
+ - MIT
60
92
  metadata: {}
61
93
  post_install_message:
62
94
  rdoc_options: []
@@ -79,8 +111,8 @@ signing_key:
79
111
  specification_version: 4
80
112
  summary: Testing multiprocess behaviour is difficult and requires a way to synchronize
81
113
  processes at specific execution points. This gem allows the parent process to control
82
- the behaviour of child processes using breakpoints. It was originally built for
83
- testing the behaviour of database transactions and locking mechanisms.
114
+ the behaviour of child processes using breakpoints. It wasoriginally built for testing
115
+ the behaviour of database transactions and locking mechanisms.
84
116
  test_files:
85
117
  - spec/fork_break_spec.rb
86
118
  - spec/spec_helper.rb