fork_break 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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