posix-spawn 0.3.10 → 0.3.11

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: 88d9e42c2e091d60ef6bfaa4bee93671bfa7cdf1
4
- data.tar.gz: 0c34d4025ca8b876345fea00bb45a152b6909c10
3
+ metadata.gz: f593fc74a9f8637ea2c1dbfa897a805d6f87c0fe
4
+ data.tar.gz: 89a1a14ec837e06d68e85af34e56e956a976df30
5
5
  SHA512:
6
- metadata.gz: 6204dd64bc24a24a7ac155f8f7a0f018860b850b94efc68a68aa6b286a0de3a179deccb6f96ae7b1410045dface4de63aca14d235b1322bfac61399d825b65ff
7
- data.tar.gz: 988a0574b69a3accddfbb739c89b3da9949146cf7f316f41e2dc364c4ef5bb3cf7ae69858fe9c0318336e946cfc929a1c77178287e742db5a820044f4d680eee
6
+ metadata.gz: 33b05adfe9baf71faabd10e25e434c03e6aac674b4d9d05a6b79e1f8972a34aeacba3bb7b40598355fc9aa4c455049eeb0b3f6c00f7790d906f1ef01bb1799fe
7
+ data.tar.gz: 75ab91856b7ee41fad74bf2be66096d2667f8fbe888d5c7d67ab364c5033438ceb8e1f1a6b0e9f99cd120da4b65ca9486f2177b1bf4aa51ad7463229102e6dc1
data/Rakefile CHANGED
@@ -14,7 +14,13 @@ end
14
14
  # Ruby Extension
15
15
  # ==========================================================
16
16
 
17
- require 'rake/extensiontask'
17
+ begin
18
+ require 'rake/extensiontask'
19
+ rescue LoadError => boom
20
+ warn "ERROR: The rake-compiler gem dependency is missing."
21
+ warn "Please run `bundle install' and try again."
22
+ raise
23
+ end
18
24
  Rake::ExtensionTask.new('posix_spawn_ext', GEMSPEC) do |ext|
19
25
  ext.ext_dir = 'ext'
20
26
  end
@@ -77,6 +77,9 @@ module POSIX
77
77
  # :max => total Maximum number of bytes of output to allow the
78
78
  # process to generate before aborting with a
79
79
  # MaximumOutputExceeded exception.
80
+ # :pgroup_kill => bool Boolean specifying whether to kill the process
81
+ # group (true) or individual process (false, default).
82
+ # Setting this option true implies :pgroup => true.
80
83
  #
81
84
  # Returns a new Child instance whose underlying process has already
82
85
  # executed to completion. The out, err, and status attributes are
@@ -87,6 +90,10 @@ module POSIX
87
90
  @input = @options.delete(:input)
88
91
  @timeout = @options.delete(:timeout)
89
92
  @max = @options.delete(:max)
93
+ if @options.delete(:pgroup_kill)
94
+ @pgroup_kill = true
95
+ @options[:pgroup] = true
96
+ end
90
97
  @options.delete(:chdir) if @options[:chdir].nil?
91
98
  exec! if !@options.delete(:noexec)
92
99
  end
@@ -128,6 +135,11 @@ module POSIX
128
135
  # Total command execution time (wall-clock time)
129
136
  attr_reader :runtime
130
137
 
138
+ # The pid of the spawned child process. This is unlikely to be a valid
139
+ # current pid since Child#exec! doesn't return until the process finishes
140
+ # and is reaped.
141
+ attr_reader :pid
142
+
131
143
  # Determine if the process did exit with a zero exit status.
132
144
  def success?
133
145
  @status && @status.success?
@@ -139,6 +151,7 @@ module POSIX
139
151
  def exec!
140
152
  # spawn the process and hook up the pipes
141
153
  pid, stdin, stdout, stderr = popen4(@env, *(@argv + [@options]))
154
+ @pid = pid
142
155
 
143
156
  # async read from all streams into buffers
144
157
  read_and_write(@input, stdin, stdout, stderr, @timeout, @max)
@@ -148,8 +161,12 @@ module POSIX
148
161
  rescue Object => boom
149
162
  [stdin, stdout, stderr].each { |fd| fd.close rescue nil }
150
163
  if @status.nil?
151
- ::Process.kill('TERM', pid) rescue nil
152
- @status = waitpid(pid) rescue nil
164
+ if !@pgroup_kill
165
+ ::Process.kill('TERM', pid) rescue nil
166
+ else
167
+ ::Process.kill('-TERM', pid) rescue nil
168
+ end
169
+ @status = waitpid(pid) rescue nil
153
170
  end
154
171
  raise
155
172
  ensure
@@ -1,5 +1,5 @@
1
1
  module POSIX
2
2
  module Spawn
3
- VERSION = '0.3.10'
3
+ VERSION = '0.3.11'
4
4
  end
5
5
  end
@@ -1,10 +1,42 @@
1
1
  # coding: UTF-8
2
-
3
2
  require 'test_helper'
4
3
 
5
4
  class ChildTest < Minitest::Test
6
5
  include POSIX::Spawn
7
6
 
7
+ # Become a new process group.
8
+ def setup
9
+ Process.setpgrp
10
+ end
11
+
12
+ # Kill any orphaned processes in our process group before continuing but
13
+ # ignore the TERM signal we receive.
14
+ def teardown
15
+ trap("TERM") { trap("TERM", "DEFAULT") }
16
+ begin
17
+ Process.kill("-TERM", Process.pid)
18
+ Process.wait
19
+ rescue Errno::ECHILD
20
+ end
21
+ end
22
+
23
+ # verify the process is no longer running and has been reaped.
24
+ def assert_process_reaped(pid)
25
+ Process.kill(0, pid)
26
+ assert false, "Process #{pid} still running"
27
+ rescue Errno::ESRCH
28
+ end
29
+
30
+ # verifies that all processes in the given process group are no longer running
31
+ # and have been reaped. The current ruby test process is excluded.
32
+ # XXX It's weird to use the SUT here but the :pgroup option is useful. Could
33
+ # be a IO.popen under Ruby >= 1.9 since it also supports :pgroup.
34
+ def assert_process_group_reaped(pgid)
35
+ command = "ps axo pgid,pid,args | grep '^#{pgid} ' | grep -v '^#{pgid} #$$'"
36
+ procs = POSIX::Spawn::Child.new(command, :pgroup => true).out
37
+ assert procs.empty?, "Processes in group #{pgid} still running:\n#{procs}"
38
+ end
39
+
8
40
  def test_sanity
9
41
  assert_same POSIX::Spawn::Child, Child
10
42
  end
@@ -56,21 +88,45 @@ class ChildTest < Minitest::Test
56
88
  end
57
89
 
58
90
  def test_max
59
- assert_raises MaximumOutputExceeded do
60
- Child.new('yes', :max => 100_000)
61
- end
91
+ child = Child.build('yes', :max => 100_000)
92
+ assert_raises(MaximumOutputExceeded) { child.exec! }
93
+ assert_process_reaped child.pid
94
+ assert_process_group_reaped Process.pid
95
+ end
96
+
97
+ def test_max_pgroup_kill
98
+ child = Child.build('yes', :max => 100_000, :pgroup_kill => true)
99
+ assert_raises(MaximumOutputExceeded) { child.exec! }
100
+ assert_process_reaped child.pid
101
+ assert_process_group_reaped child.pid
62
102
  end
63
103
 
64
104
  def test_max_with_child_hierarchy
65
- assert_raises MaximumOutputExceeded do
66
- Child.new('/bin/sh', '-c', 'yes', :max => 100_000)
67
- end
105
+ child = Child.build('/bin/sh', '-c', 'true && yes', :max => 100_000)
106
+ assert_raises(MaximumOutputExceeded) { child.exec! }
107
+ assert_process_reaped child.pid
108
+ assert_process_group_reaped Process.pid
109
+ end
110
+
111
+ def test_max_with_child_hierarchy_pgroup_kill
112
+ child = Child.build('/bin/sh', '-c', 'true && yes', :max => 100_000, :pgroup_kill => true)
113
+ assert_raises(MaximumOutputExceeded) { child.exec! }
114
+ assert_process_reaped child.pid
115
+ assert_process_group_reaped child.pid
68
116
  end
69
117
 
70
118
  def test_max_with_stubborn_child
71
- assert_raises MaximumOutputExceeded do
72
- Child.new("trap '' TERM; yes", :max => 100_000)
73
- end
119
+ child = Child.build("trap '' TERM; yes", :max => 100_000)
120
+ assert_raises(MaximumOutputExceeded) { child.exec! }
121
+ assert_process_reaped child.pid
122
+ assert_process_group_reaped Process.pid
123
+ end
124
+
125
+ def test_max_with_stubborn_child_pgroup_kill
126
+ child = Child.build("trap '' TERM; yes", :max => 100_000, :pgroup_kill => true)
127
+ assert_raises(MaximumOutputExceeded) { child.exec! }
128
+ assert_process_reaped child.pid
129
+ assert_process_group_reaped child.pid
74
130
  end
75
131
 
76
132
  def test_max_with_partial_output
@@ -80,6 +136,8 @@ class ChildTest < Minitest::Test
80
136
  p.exec!
81
137
  end
82
138
  assert_output_exceeds_repeated_string("y\n", 100_000, p.out)
139
+ assert_process_reaped p.pid
140
+ assert_process_group_reaped Process.pid
83
141
  end
84
142
 
85
143
  def test_max_with_partial_output_long_lines
@@ -88,28 +146,47 @@ class ChildTest < Minitest::Test
88
146
  p.exec!
89
147
  end
90
148
  assert_output_exceeds_repeated_string("nice to meet you\n", 10_000, p.out)
149
+ assert_process_reaped p.pid
150
+ assert_process_group_reaped Process.pid
91
151
  end
92
152
 
93
153
  def test_timeout
94
154
  start = Time.now
95
- assert_raises TimeoutExceeded do
96
- Child.new('sleep', '1', :timeout => 0.05)
97
- end
155
+ child = Child.build('sleep', '1', :timeout => 0.05)
156
+ assert_raises(TimeoutExceeded) { child.exec! }
157
+ assert_process_reaped child.pid
158
+ assert_process_group_reaped Process.pid
159
+ assert (Time.now-start) <= 0.2
160
+ end
161
+
162
+ def test_timeout_pgroup_kill
163
+ start = Time.now
164
+ child = Child.build('sleep', '1', :timeout => 0.05, :pgroup_kill => true)
165
+ assert_raises(TimeoutExceeded) { child.exec! }
166
+ assert_process_reaped child.pid
167
+ assert_process_group_reaped child.pid
98
168
  assert (Time.now-start) <= 0.2
99
169
  end
100
170
 
101
171
  def test_timeout_with_child_hierarchy
102
- assert_raises TimeoutExceeded do
103
- Child.new('/bin/sh', '-c', 'sleep 1', :timeout => 0.05)
104
- end
172
+ child = Child.build('/bin/sh', '-c', 'true && sleep 1', :timeout => 0.05)
173
+ assert_raises(TimeoutExceeded) { child.exec! }
174
+ assert_process_reaped child.pid
175
+ end
176
+
177
+ def test_timeout_with_child_hierarchy_pgroup_kill
178
+ child = Child.build('/bin/sh', '-c', 'true && sleep 1', :timeout => 0.05, :pgroup_kill => true)
179
+ assert_raises(TimeoutExceeded) { child.exec! }
180
+ assert_process_reaped child.pid
181
+ assert_process_group_reaped child.pid
105
182
  end
106
183
 
107
184
  def test_timeout_with_partial_output
108
185
  start = Time.now
109
- p = Child.build('echo Hello; sleep 1', :timeout => 0.05)
110
- assert_raises TimeoutExceeded do
111
- p.exec!
112
- end
186
+ p = Child.build('echo Hello; sleep 1', :timeout => 0.05, :pgroup_kill => true)
187
+ assert_raises(TimeoutExceeded) { p.exec! }
188
+ assert_process_reaped p.pid
189
+ assert_process_group_reaped Process.pid
113
190
  assert (Time.now-start) <= 0.2
114
191
  assert_equal "Hello\n", p.out
115
192
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posix-spawn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.10
4
+ version: 0.3.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Tomayko
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-17 00:00:00.000000000 Z
12
+ date: 2015-04-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake-compiler