foreground 0.0.2 → 0.0.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.
@@ -4,16 +4,40 @@ Feature: Sample daemon
4
4
  As an BDD guy
5
5
  I want a sample daemon along with cucumber steps
6
6
 
7
- Scenario: Run sample daemon
8
- When I successfully run `foreground_sample_daemon`
7
+ Scenario: Start and stop sample daemon
8
+ When I run the sample daemon
9
9
  Then the sample daemon should run
10
10
  And a file named "/tmp/foreground_sample_daemon.pid" should exist
11
+ And the file "/tmp/foreground_sample_daemon.log" should contain exactly:
12
+ """
13
+ Daemon started...
14
+
15
+ """
16
+
17
+ When I kill the sample daemon
18
+ Then the sample daemon should not run
19
+ And a file named "/tmp/foreground_sample_daemon.pid" should not exist
20
+ But a file named "/tmp/foreground_sample_daemon.log" should exist
11
21
 
12
22
  Scenario: Run only one sample daemon at once
13
- When I successfully run `foreground_sample_daemon`
14
- And I successfully run `foreground_sample_daemon`
23
+ When I run the sample daemon
24
+ And I run the sample daemon
15
25
  Then the sample daemon should run
16
26
 
27
+ Scenario Outline: Log received signals
28
+ Given I run the sample daemon
29
+ When I send the sample daemon a <signal> signal
30
+ Then the sample daemon should <run_or_not>
31
+ And the file "/tmp/foreground_sample_daemon.log" should contain "received <signal> signal"
32
+ And the file "/tmp/foreground_sample_daemon.log" should contain "Daemon <message>."
33
+
34
+ Examples:
35
+ | signal | run_or_not | message |
36
+ | TERM | not run | stopped |
37
+ | INT | not run | stopped |
38
+ | QUIT | not run | stopped |
39
+ | HUP | run | refreshed |
40
+
17
41
  Scenario: Don't mess up the system with running sample daemons
18
42
  Then the sample daemon should not run
19
43
  And a file named "/tmp/foreground_sample_daemon.pid" should not exist
@@ -9,4 +9,7 @@ Feature: Start and stop
9
9
  Then the sample daemon should run
10
10
 
11
11
  When I kill foreground
12
- Then the sample daemon should not run
12
+ And I run `sleep 1`
13
+ Then foreground should not run
14
+ And the sample daemon should not run
15
+ And the sample daemon should have received a TERM signal
@@ -1,3 +1,5 @@
1
+ STDOUT.sync = true # for debugging
2
+
1
3
  def sample_daemons
2
4
  proclist = `/bin/ps -A -o pid,command`
3
5
  proclist.to_a.grep(%r{^\s*\d+\s+ruby\s+.*foreground_sample_daemon$}) { |l| l[/^\s*(\d+)\s+/,1].to_i }
@@ -5,25 +7,56 @@ end
5
7
 
6
8
  def kill_foreground
7
9
  if @foreground
8
- Process.kill(:TERM, @foreground) unless Process.waitpid(@foreground, Process::WNOHANG)
10
+ begin
11
+ Process.kill(:TERM, @foreground)
12
+ Process.waitpid(@foreground)
13
+ rescue Errno::ESRCH, Errno::ECHILD
14
+ end
9
15
  end
10
16
  end
11
17
 
18
+ def kill_all_sample_daemons(signal=:TERM)
19
+ sample_daemons.each { |pid| system("kill -#{signal} #{pid}") }
20
+ sleep 1 # Give the process time to say goodbye.
21
+ end
22
+
12
23
  After do
13
24
  kill_foreground
14
- sample_daemons.each { |pid| system("kill #{pid}") }
25
+ kill_all_sample_daemons
26
+ end
27
+
28
+ When /^I run the sample daemon$/ do
29
+ steps %q{
30
+ When I successfully run `foreground_sample_daemon`
31
+ }
15
32
  end
16
33
 
17
34
  When /^I run the sample daemon via foreground$/ do
18
35
  @foreground = fork do
19
- exec('foreground --pid_file /tmp/foreground_sample_daemon.pid foreground_sample_daemon')
36
+ exec('foreground', '--pid_file', '/tmp/foreground_sample_daemon.pid', 'foreground_sample_daemon')
20
37
  end
21
38
  end
22
39
 
40
+ When /^I kill the sample daemon$/ do
41
+ kill_all_sample_daemons
42
+ end
43
+
44
+ When /^I send the sample daemon a (\w+) signal$/ do |signal|
45
+ kill_all_sample_daemons(signal.to_sym)
46
+ end
47
+
23
48
  When /^I kill foreground$/ do
49
+ sleep 1 # Give foreground some time to setup signal handling... or tests will break.
24
50
  kill_foreground
25
51
  end
26
52
 
53
+ Then /^foreground should not run$/ do
54
+ unless @foreground
55
+ lambda { Process.kill(0, @foreground) }.should raise_error(Errno::ESRCH),
56
+ "foreground still running with PID #{@foreground}"
57
+ end
58
+ end
59
+
27
60
  Then /^the sample daemon should run$/ do
28
61
  steps %q{
29
62
  Then 1 sample daemon should run
@@ -42,3 +75,9 @@ Then /^(\d+) sample daemons? should run$/ do |expected|
42
75
  actual.should eql(expected),
43
76
  "Expected #{expected} running sample daemon, but got #{actual} instead!"
44
77
  end
78
+
79
+ Then /^the sample daemon should have received a (\w+) signal$/ do |signal|
80
+ steps %Q{
81
+ Then the file "/tmp/foreground_sample_daemon.log" should contain "received #{signal} signal"
82
+ }
83
+ end
data/lib/foreground.rb CHANGED
@@ -1,6 +1,6 @@
1
- require 'shellwords'
2
1
  require 'mixlib/cli'
3
2
  require 'foreground/version'
3
+ require 'foreground/daemon'
4
4
  require 'foreground/cli'
5
5
 
6
6
  module Foreground
@@ -16,26 +16,7 @@ module Foreground
16
16
 
17
17
  def run(argv)
18
18
  cmd = parse_options(argv)
19
- system(cmd.shelljoin)
20
- watch
21
- end
22
-
23
- def watch
24
- ### <proof_of_concept>
25
- #TODO: Unhackify this block of code!
26
- STDOUT.sync = true
27
- puts "hi, there!"
28
- trap(:TERM) do
29
- sleep 3 # Give the daemon time to write its PID file.
30
- if File.exists?(config[:pid_file])
31
- pid = File.read(config[:pid_file]).chomp.to_i
32
- Process.kill(:TERM, pid)
33
- end
34
- end
35
- ### </proof_of_concept>
36
-
37
- #TODO: Implement watch feature!
38
- loop { sleep 1 }
19
+ Daemon.run(cmd, config[:pid_file])
39
20
  end
40
21
  end
41
22
  end
@@ -0,0 +1,48 @@
1
+ require 'foreground'
2
+
3
+ module Foreground
4
+ class Daemon
5
+ @daemon = nil
6
+
7
+ class << self
8
+ attr_accessor :daemon
9
+
10
+ def run(*args)
11
+ @daemon = new(*args)
12
+ @daemon.run
13
+ end
14
+
15
+ def kill(*args)
16
+ @daemon.kill(*args)
17
+ end
18
+ end
19
+
20
+
21
+ def initialize(cmd, pid_file)
22
+ @cmd = cmd
23
+ @pid_file = pid_file
24
+ end
25
+
26
+ def run
27
+ STDOUT.sync = true
28
+ puts "hi, there (foreground #{Foreground::VERSION})!"
29
+ system(*@cmd)
30
+ watch
31
+ end
32
+
33
+ def kill(signal = :TERM)
34
+ Process.kill(signal, pid)
35
+ end
36
+
37
+ def pid
38
+ #TODO: Replace sleep with timeout!
39
+ sleep 0.1 # Give the daemon time to write its PID file.
40
+ File.read(@pid_file).chomp.to_i
41
+ end
42
+
43
+ def watch
44
+ #TODO: Implement watch feature!
45
+ loop { sleep 1 }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ module Foreground
2
+ trap(:TERM) do
3
+ Daemon.kill(:TERM)
4
+ exit
5
+ end
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Foreground
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -3,10 +3,9 @@ require 'spec_helper'
3
3
  module Foreground
4
4
  describe CLI do
5
5
  before do
6
- CLI.stub(:system)
6
+ Daemon.stub(:run)
7
7
  @argv = ['--pid_file', '/tmp/foreground_sample_daemon.pid', 'foreground_sample_daemon']
8
8
  @cli = CLI.new
9
- @cli.stub(:watch) # ...or we'll wait forever.
10
9
  end
11
10
 
12
11
  describe '.run' do
@@ -25,12 +24,7 @@ module Foreground
25
24
  end
26
25
 
27
26
  it 'should run the daemon' do
28
- @cli.should_receive(:system).with('foreground_sample_daemon')
29
- @cli.run(@argv)
30
- end
31
-
32
- it 'should watch the daemon' do
33
- @cli.should_receive(:watch)
27
+ Daemon.should_receive(:run).with(['foreground_sample_daemon'], '/tmp/foreground_sample_daemon.pid')
34
28
  @cli.run(@argv)
35
29
  end
36
30
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ module Foreground
4
+ describe Daemon do
5
+ before do
6
+ Process.stub(:kill)
7
+ @cmd = ['some_daemon', '--with', 'arguments']
8
+ @pid_file = 'some_daemon.pid'
9
+ @pid = 42
10
+ @args = [@cmd, @pid_file]
11
+ @daemon = Daemon.new(@cmd, @pid_file)
12
+ @daemon.stub(:watch) # ...or we'll wait forever.
13
+ @daemon.stub(:system) # ...so that we don't run any stupid commands in our tests.
14
+ end
15
+
16
+ describe '.run' do
17
+ it 'should run and register a new instance' do
18
+ daemon = mock('daemon')
19
+ daemon.should_receive(:run)
20
+ Daemon.should_receive(:new).with(*@args).and_return(daemon)
21
+ Daemon.daemon.should be_nil
22
+ Daemon.run(*@args)
23
+ Daemon.daemon.should be(daemon)
24
+ end
25
+ end
26
+
27
+ describe '.kill' do
28
+ it 'should forward signals to the daemon' do
29
+ Daemon.daemon = mock('daemon')
30
+ Daemon.daemon.should_receive(:kill).with(:FOO)
31
+ Daemon.kill(:FOO)
32
+ end
33
+ end
34
+
35
+ describe '#run' do
36
+ it 'should run and watch the daemon' do
37
+ @daemon.should_receive(:system).with(*@cmd).ordered
38
+ @daemon.should_receive(:watch).ordered
39
+ @daemon.run
40
+ end
41
+ end
42
+
43
+ describe '#kill' do
44
+ it 'should send the daemon a SIGTERM' do
45
+ @daemon.stub(:pid).and_return(42)
46
+ Process.should_receive(:kill).with(:TERM, 42)
47
+ @daemon.kill
48
+ end
49
+ end
50
+
51
+ describe '#pid' do
52
+ it 'should return the daemons PID by PID file' do
53
+ File.should_receive(:read).with(@pid_file).and_return("#{@pid}\n")
54
+ @daemon.pid.should eql(@pid)
55
+ end
56
+ end
57
+ end
58
+ end
metadata CHANGED
@@ -1,137 +1,143 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: foreground
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
6
11
  platform: ruby
7
- authors:
8
- - Björn Albers
12
+ authors:
13
+ - "Bjo\xCC\x88rn Albers"
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-09-06 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: mixlib-cli
16
- requirement: !ruby/object:Gem::Requirement
17
+
18
+ date: 2012-09-08 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ requirement: &id001 !ruby/object:Gem::Requirement
17
22
  none: false
18
- requirements:
23
+ requirements:
19
24
  - - ~>
20
- - !ruby/object:Gem::Version
25
+ - !ruby/object:Gem::Version
26
+ hash: 27
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 2
21
31
  version: 1.2.2
22
- type: :runtime
23
32
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: 1.2.2
30
- - !ruby/object:Gem::Dependency
31
- name: aruba
32
- requirement: !ruby/object:Gem::Requirement
33
+ type: :runtime
34
+ name: mixlib-cli
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ requirement: &id002 !ruby/object:Gem::Requirement
33
38
  none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 25
43
+ segments:
44
+ - 0
45
+ - 4
46
+ - 11
37
47
  version: 0.4.11
38
- type: :development
39
48
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: 0.4.11
46
- - !ruby/object:Gem::Dependency
47
- name: aruba-doubles
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ~>
52
- - !ruby/object:Gem::Version
53
- version: 1.2.1
54
49
  type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
50
+ name: aruba
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ requirement: &id003 !ruby/object:Gem::Requirement
57
54
  none: false
58
- requirements:
55
+ requirements:
59
56
  - - ~>
60
- - !ruby/object:Gem::Version
57
+ - !ruby/object:Gem::Version
58
+ hash: 29
59
+ segments:
60
+ - 1
61
+ - 2
62
+ - 1
61
63
  version: 1.2.1
62
- - !ruby/object:Gem::Dependency
63
- name: guard-cucumber
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: 0.7.5
70
- type: :development
71
64
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
65
+ type: :development
66
+ name: aruba-doubles
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ requirement: &id004 !ruby/object:Gem::Requirement
73
70
  none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 9
75
+ segments:
76
+ - 0
77
+ - 7
78
+ - 5
77
79
  version: 0.7.5
78
- - !ruby/object:Gem::Dependency
79
- name: guard-rspec
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: 0.5.1
86
- type: :development
87
80
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
81
+ type: :development
82
+ name: guard-cucumber
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
85
+ requirement: &id005 !ruby/object:Gem::Requirement
89
86
  none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 9
91
+ segments:
92
+ - 0
93
+ - 5
94
+ - 1
93
95
  version: 0.5.1
94
- - !ruby/object:Gem::Dependency
95
- name: rake
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :development
103
96
  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: rb-fsevent
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ! '>='
116
- - !ruby/object:Gem::Version
117
- version: 0.9.0
118
97
  type: :development
98
+ name: guard-rspec
99
+ version_requirements: *id005
100
+ - !ruby/object:Gem::Dependency
101
+ requirement: &id006 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
119
110
  prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
111
+ type: :development
112
+ name: rake
113
+ version_requirements: *id006
114
+ - !ruby/object:Gem::Dependency
115
+ requirement: &id007 !ruby/object:Gem::Requirement
121
116
  none: false
122
- requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ hash: 59
121
+ segments:
122
+ - 0
123
+ - 9
124
+ - 0
125
125
  version: 0.9.0
126
+ prerelease: false
127
+ type: :development
128
+ name: rb-fsevent
129
+ version_requirements: *id007
126
130
  description: Control daemonizing background processes with launchd.
127
- email:
131
+ email:
128
132
  - bjoernalbers@googlemail.com
129
- executables:
133
+ executables:
130
134
  - foreground
131
135
  - foreground_sample_daemon
132
136
  extensions: []
137
+
133
138
  extra_rdoc_files: []
134
- files:
139
+
140
+ files:
135
141
  - .gitignore
136
142
  - Gemfile
137
143
  - Guardfile
@@ -147,6 +153,8 @@ files:
147
153
  - foreground.gemspec
148
154
  - lib/foreground.rb
149
155
  - lib/foreground/cli.rb
156
+ - lib/foreground/daemon.rb
157
+ - lib/foreground/signal_handlers.rb
150
158
  - lib/foreground/version.rb
151
159
  - script/bootstrap
152
160
  - spec/foreground/cli_spec.rb
@@ -154,35 +162,38 @@ files:
154
162
  - spec/spec_helper.rb
155
163
  homepage: https://github.com/bjoernalbers/foreground
156
164
  licenses: []
165
+
157
166
  post_install_message:
158
167
  rdoc_options: []
159
- require_paths:
168
+
169
+ require_paths:
160
170
  - lib
161
- required_ruby_version: !ruby/object:Gem::Requirement
171
+ required_ruby_version: !ruby/object:Gem::Requirement
162
172
  none: false
163
- requirements:
164
- - - ! '>='
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
- segments:
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ hash: 3
177
+ segments:
168
178
  - 0
169
- hash: -1540966374872401692
170
- required_rubygems_version: !ruby/object:Gem::Requirement
179
+ version: "0"
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
181
  none: false
172
- requirements:
173
- - - ! '>='
174
- - !ruby/object:Gem::Version
175
- version: '0'
176
- segments:
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ hash: 3
186
+ segments:
177
187
  - 0
178
- hash: -1540966374872401692
188
+ version: "0"
179
189
  requirements: []
190
+
180
191
  rubyforge_project:
181
192
  rubygems_version: 1.8.24
182
193
  signing_key:
183
194
  specification_version: 3
184
- summary: foreground-0.0.2
185
- test_files:
195
+ summary: foreground-0.0.3
196
+ test_files:
186
197
  - features/sample_daemon.feature
187
198
  - features/start_stop.feature
188
199
  - features/steps/sample_daemon_steps.rb