foreground 0.0.2 → 0.0.3

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