scmd 2.2.0 → 2.3.0
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.
- data/lib/scmd/command.rb +22 -5
- data/lib/scmd/version.rb +1 -1
- data/test/system/command_tests.rb +173 -0
- data/test/unit/command_tests.rb +5 -152
- metadata +9 -7
data/lib/scmd/command.rb
CHANGED
@@ -86,10 +86,10 @@ module Scmd
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def kill
|
89
|
+
def kill(sig = nil)
|
90
90
|
return if !running?
|
91
91
|
|
92
|
-
send_kill
|
92
|
+
send_kill(sig)
|
93
93
|
wait # indefinitely until cmd is killed
|
94
94
|
end
|
95
95
|
|
@@ -146,13 +146,13 @@ module Scmd
|
|
146
146
|
send_signal 'TERM'
|
147
147
|
end
|
148
148
|
|
149
|
-
def send_kill
|
150
|
-
send_signal 'KILL'
|
149
|
+
def send_kill(sig = nil)
|
150
|
+
send_signal(sig || 'KILL')
|
151
151
|
end
|
152
152
|
|
153
153
|
def send_signal(sig)
|
154
154
|
return if !running?
|
155
|
-
|
155
|
+
@child_process.send_signal(sig)
|
156
156
|
end
|
157
157
|
|
158
158
|
def stringify_hash(hash)
|
@@ -201,6 +201,10 @@ module Scmd
|
|
201
201
|
end
|
202
202
|
end
|
203
203
|
|
204
|
+
def send_signal(sig)
|
205
|
+
process_kill(sig, self.pid)
|
206
|
+
end
|
207
|
+
|
204
208
|
def flush_stdout; @stdout.read; end
|
205
209
|
def flush_stderr; @stderr.read; end
|
206
210
|
|
@@ -218,6 +222,19 @@ module Scmd
|
|
218
222
|
io.read_nonblock(size)
|
219
223
|
end
|
220
224
|
|
225
|
+
def process_kill(sig, pid)
|
226
|
+
child_pids(pid).each{ |p| process_kill(sig, p) }
|
227
|
+
::Process.kill(sig, pid)
|
228
|
+
end
|
229
|
+
|
230
|
+
def child_pids(pid)
|
231
|
+
Command.new("#{pgrep} -P #{pid}").run.stdout.split("\n").map(&:to_i)
|
232
|
+
end
|
233
|
+
|
234
|
+
def pgrep
|
235
|
+
@pgrep ||= Command.new('which pgrep').run.stdout.strip
|
236
|
+
end
|
237
|
+
|
221
238
|
end
|
222
239
|
|
223
240
|
end
|
data/lib/scmd/version.rb
CHANGED
@@ -0,0 +1,173 @@
|
|
1
|
+
require "assert"
|
2
|
+
require 'scmd/command'
|
3
|
+
|
4
|
+
class Scmd::Command
|
5
|
+
|
6
|
+
class SystemTests < Assert::Context
|
7
|
+
desc "Scmd::Command"
|
8
|
+
setup do
|
9
|
+
@success_cmd = Scmd::Command.new("echo hi")
|
10
|
+
@failure_cmd = Scmd::Command.new("cd /path/that/does/not/exist")
|
11
|
+
end
|
12
|
+
|
13
|
+
should "run the command and set appropriate result data" do
|
14
|
+
@success_cmd.run
|
15
|
+
|
16
|
+
assert_not_nil @success_cmd.pid
|
17
|
+
assert_equal 0, @success_cmd.exitstatus
|
18
|
+
assert @success_cmd.success?
|
19
|
+
assert_equal "hi\n", @success_cmd.stdout
|
20
|
+
assert_equal '', @success_cmd.stderr
|
21
|
+
|
22
|
+
@failure_cmd.run
|
23
|
+
|
24
|
+
assert_not_nil @failure_cmd.pid
|
25
|
+
assert_not_equal 0, @failure_cmd.exitstatus
|
26
|
+
assert_not @failure_cmd.success?
|
27
|
+
assert_equal '', @failure_cmd.stdout
|
28
|
+
assert_not_equal '', @failure_cmd.stderr
|
29
|
+
end
|
30
|
+
|
31
|
+
should "raise an exception with proper backtrace on `run!`" do
|
32
|
+
err = begin;
|
33
|
+
@failure_cmd.run!
|
34
|
+
rescue Exception => err
|
35
|
+
err
|
36
|
+
end
|
37
|
+
|
38
|
+
assert_kind_of Scmd::RunError, err
|
39
|
+
assert_includes 'No such file or directory', err.message
|
40
|
+
assert_includes 'test/system/command_tests.rb:', err.backtrace.first
|
41
|
+
end
|
42
|
+
|
43
|
+
should "return itself on `run`, `run!`" do
|
44
|
+
assert_equal @success_cmd, @success_cmd.run
|
45
|
+
assert_equal @success_cmd, @success_cmd.run!
|
46
|
+
assert_equal @failure_cmd, @failure_cmd.run
|
47
|
+
end
|
48
|
+
|
49
|
+
should "start and be running until `wait` is called and the cmd exits" do
|
50
|
+
cmd = Scmd::Command.new("sleep .1")
|
51
|
+
assert_not cmd.running?
|
52
|
+
|
53
|
+
cmd.start
|
54
|
+
assert cmd.running?
|
55
|
+
assert_not_nil cmd.pid
|
56
|
+
|
57
|
+
cmd.wait
|
58
|
+
assert_not cmd.running?
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class InputTests < SystemTests
|
64
|
+
desc "that takes input on stdin"
|
65
|
+
setup do
|
66
|
+
@cmd = Scmd::Command.new("sh")
|
67
|
+
end
|
68
|
+
subject { @cmd }
|
69
|
+
|
70
|
+
should "run the command given a single line of input" do
|
71
|
+
subject.run "echo hi"
|
72
|
+
|
73
|
+
assert @cmd.success?
|
74
|
+
assert_equal "hi\n", @cmd.stdout
|
75
|
+
end
|
76
|
+
|
77
|
+
should "run the command given multiple lines of input" do
|
78
|
+
subject.run ["echo hi", "echo err 1>&2"]
|
79
|
+
|
80
|
+
assert @cmd.success?
|
81
|
+
assert_equal "hi\n", @cmd.stdout
|
82
|
+
assert_equal "err\n", @cmd.stderr
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
class LongRunningTests < SystemTests
|
88
|
+
desc "that is long running"
|
89
|
+
setup do
|
90
|
+
@long_cmd = Scmd::Command.new("sleep .3 && echo hi")
|
91
|
+
end
|
92
|
+
|
93
|
+
should "not timeout if wait timeout is longer than cmd time" do
|
94
|
+
assert_nothing_raised do
|
95
|
+
@long_cmd.start
|
96
|
+
@long_cmd.wait(1)
|
97
|
+
end
|
98
|
+
assert @long_cmd.success?
|
99
|
+
assert_equal "hi\n", @long_cmd.stdout
|
100
|
+
end
|
101
|
+
|
102
|
+
should "timeout if wait timeout is shorter than cmd time" do
|
103
|
+
assert_raises(Scmd::TimeoutError) do
|
104
|
+
@long_cmd.start
|
105
|
+
@long_cmd.wait(0.1)
|
106
|
+
end
|
107
|
+
assert_not @long_cmd.success?
|
108
|
+
assert_empty @long_cmd.stdout
|
109
|
+
end
|
110
|
+
|
111
|
+
should "be stoppable" do
|
112
|
+
@long_cmd.start
|
113
|
+
@long_cmd.stop
|
114
|
+
|
115
|
+
assert_not @long_cmd.running?
|
116
|
+
end
|
117
|
+
|
118
|
+
should "be killable" do
|
119
|
+
@long_cmd.start
|
120
|
+
@long_cmd.kill
|
121
|
+
|
122
|
+
assert_not @long_cmd.running?
|
123
|
+
end
|
124
|
+
|
125
|
+
should "be killable with a non-default signal" do
|
126
|
+
@long_cmd.start
|
127
|
+
@long_cmd.kill('INT')
|
128
|
+
|
129
|
+
assert_not @long_cmd.running?
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class BufferDeadlockTests < SystemTests
|
135
|
+
desc "when capturing data from an output buffer"
|
136
|
+
setup do
|
137
|
+
@small_path = File.join(ROOT_PATH, 'test/support/smaller-than-64k.txt')
|
138
|
+
@small_data = File.read(@small_path)
|
139
|
+
@small_cmd = Scmd::Command.new("cat #{@small_path}")
|
140
|
+
|
141
|
+
@big_path = File.join(ROOT_PATH, 'test/support/bigger-than-64k.txt')
|
142
|
+
@big_data = File.read(@big_path)
|
143
|
+
@big_cmd = Scmd::Command.new("cat #{@big_path}")
|
144
|
+
end
|
145
|
+
|
146
|
+
should "not deadlock, just stream the data from the buffer" do
|
147
|
+
@small_cmd.start
|
148
|
+
assert_nothing_raised{ @small_cmd.wait(1) }
|
149
|
+
assert_equal @small_data, @small_cmd.stdout
|
150
|
+
|
151
|
+
@big_cmd.start
|
152
|
+
assert_nothing_raised{ @big_cmd.wait(1) }
|
153
|
+
assert_equal @big_data, @big_cmd.stdout
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
class WithEnvVarTests < SystemTests
|
159
|
+
desc "with environment variables"
|
160
|
+
setup do
|
161
|
+
@cmd = Scmd::Command.new("echo $SCMD_TEST_VAR", {
|
162
|
+
'SCMD_TEST_VAR' => 'test'
|
163
|
+
})
|
164
|
+
end
|
165
|
+
|
166
|
+
should "use them when running the command" do
|
167
|
+
@cmd.run
|
168
|
+
assert_equal "test\n", @cmd.stdout
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
data/test/unit/command_tests.rb
CHANGED
@@ -6,10 +6,9 @@ class Scmd::Command
|
|
6
6
|
class UnitTests < Assert::Context
|
7
7
|
desc "Scmd::Command"
|
8
8
|
setup do
|
9
|
-
@
|
10
|
-
@failure_cmd = Scmd::Command.new("cd /path/that/does/not/exist")
|
9
|
+
@cmd = Scmd::Command.new("echo hi")
|
11
10
|
end
|
12
|
-
subject { @
|
11
|
+
subject { @cmd }
|
13
12
|
|
14
13
|
should have_readers :cmd_str, :env
|
15
14
|
should have_readers :pid, :exitstatus, :stdout, :stderr
|
@@ -41,52 +40,9 @@ class Scmd::Command
|
|
41
40
|
assert_equal '', subject.stderr
|
42
41
|
end
|
43
42
|
|
44
|
-
should "
|
45
|
-
|
46
|
-
|
47
|
-
assert_not_nil @success_cmd.pid
|
48
|
-
assert_equal 0, @success_cmd.exitstatus
|
49
|
-
assert @success_cmd.success?
|
50
|
-
assert_equal "hi\n", @success_cmd.stdout
|
51
|
-
assert_equal '', @success_cmd.stderr
|
52
|
-
|
53
|
-
@failure_cmd.run
|
54
|
-
|
55
|
-
assert_not_nil @failure_cmd.pid
|
56
|
-
assert_not_equal 0, @failure_cmd.exitstatus
|
57
|
-
assert_not @failure_cmd.success?
|
58
|
-
assert_equal '', @failure_cmd.stdout
|
59
|
-
assert_not_equal '', @failure_cmd.stderr
|
60
|
-
end
|
61
|
-
|
62
|
-
should "raise an exception with proper backtrace on `run!`" do
|
63
|
-
err = begin;
|
64
|
-
@failure_cmd.run!
|
65
|
-
rescue Exception => err
|
66
|
-
err
|
67
|
-
end
|
68
|
-
|
69
|
-
assert_kind_of Scmd::RunError, err
|
70
|
-
assert_includes 'No such file or directory', err.message
|
71
|
-
assert_includes 'test/unit/command_tests.rb:', err.backtrace.first
|
72
|
-
end
|
73
|
-
|
74
|
-
should "return itself on `run`, `run!`" do
|
75
|
-
assert_equal @success_cmd, @success_cmd.run
|
76
|
-
assert_equal @success_cmd, @success_cmd.run!
|
77
|
-
assert_equal @failure_cmd, @failure_cmd.run
|
78
|
-
end
|
79
|
-
|
80
|
-
should "start and be running until `wait` is called and the cmd exits" do
|
81
|
-
cmd = Scmd::Command.new("sleep .1")
|
82
|
-
assert_not cmd.running?
|
83
|
-
|
84
|
-
cmd.start
|
85
|
-
assert cmd.running?
|
86
|
-
assert_not_nil cmd.pid
|
87
|
-
|
88
|
-
cmd.wait
|
89
|
-
assert_not cmd.running?
|
43
|
+
should "default its state" do
|
44
|
+
assert_false subject.running?
|
45
|
+
assert_false subject.success?
|
90
46
|
end
|
91
47
|
|
92
48
|
should "do nothing and return when told to wait but not running" do
|
@@ -115,107 +71,4 @@ class Scmd::Command
|
|
115
71
|
|
116
72
|
end
|
117
73
|
|
118
|
-
class InputTests < UnitTests
|
119
|
-
desc "that takes input on stdin"
|
120
|
-
setup do
|
121
|
-
@cmd = Scmd::Command.new("sh")
|
122
|
-
end
|
123
|
-
subject { @cmd }
|
124
|
-
|
125
|
-
should "run the command given a single line of input" do
|
126
|
-
subject.run "echo hi"
|
127
|
-
|
128
|
-
assert @cmd.success?
|
129
|
-
assert_equal "hi\n", @cmd.stdout
|
130
|
-
end
|
131
|
-
|
132
|
-
should "run the command given multiple lines of input" do
|
133
|
-
subject.run ["echo hi", "echo err 1>&2"]
|
134
|
-
|
135
|
-
assert @cmd.success?
|
136
|
-
assert_equal "hi\n", @cmd.stdout
|
137
|
-
assert_equal "err\n", @cmd.stderr
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
class LongRunningTests < UnitTests
|
143
|
-
desc "that is long running"
|
144
|
-
setup do
|
145
|
-
@long_cmd = Scmd::Command.new("sleep .3 && echo hi")
|
146
|
-
end
|
147
|
-
|
148
|
-
should "not timeout if wait timeout is longer than cmd time" do
|
149
|
-
assert_nothing_raised do
|
150
|
-
@long_cmd.start
|
151
|
-
@long_cmd.wait(1)
|
152
|
-
end
|
153
|
-
assert @long_cmd.success?
|
154
|
-
assert_equal "hi\n", @long_cmd.stdout
|
155
|
-
end
|
156
|
-
|
157
|
-
should "timeout if wait timeout is shorter than cmd time" do
|
158
|
-
assert_raises(Scmd::TimeoutError) do
|
159
|
-
@long_cmd.start
|
160
|
-
@long_cmd.wait(0.1)
|
161
|
-
end
|
162
|
-
assert_not @long_cmd.success?
|
163
|
-
assert_empty @long_cmd.stdout
|
164
|
-
end
|
165
|
-
|
166
|
-
should "be stoppable" do
|
167
|
-
@long_cmd.start
|
168
|
-
@long_cmd.stop
|
169
|
-
|
170
|
-
assert_not @long_cmd.running?
|
171
|
-
end
|
172
|
-
|
173
|
-
should "be killable" do
|
174
|
-
@long_cmd.start
|
175
|
-
@long_cmd.kill
|
176
|
-
|
177
|
-
assert_not @long_cmd.running?
|
178
|
-
end
|
179
|
-
|
180
|
-
end
|
181
|
-
|
182
|
-
class BufferDeadlockTests < UnitTests
|
183
|
-
desc "when capturing data from an output buffer"
|
184
|
-
setup do
|
185
|
-
@small_path = File.join(ROOT_PATH, 'test/support/smaller-than-64k.txt')
|
186
|
-
@small_data = File.read(@small_path)
|
187
|
-
@small_cmd = Scmd::Command.new("cat #{@small_path}")
|
188
|
-
|
189
|
-
@big_path = File.join(ROOT_PATH, 'test/support/bigger-than-64k.txt')
|
190
|
-
@big_data = File.read(@big_path)
|
191
|
-
@big_cmd = Scmd::Command.new("cat #{@big_path}")
|
192
|
-
end
|
193
|
-
|
194
|
-
should "not deadlock, just stream the data from the buffer" do
|
195
|
-
@small_cmd.start
|
196
|
-
assert_nothing_raised{ @small_cmd.wait(1) }
|
197
|
-
assert_equal @small_data, @small_cmd.stdout
|
198
|
-
|
199
|
-
@big_cmd.start
|
200
|
-
assert_nothing_raised{ @big_cmd.wait(1) }
|
201
|
-
assert_equal @big_data, @big_cmd.stdout
|
202
|
-
end
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
class WithEnvVarTests < UnitTests
|
207
|
-
desc "with environment variables"
|
208
|
-
setup do
|
209
|
-
@cmd = Scmd::Command.new("echo $SCMD_TEST_VAR", {
|
210
|
-
'SCMD_TEST_VAR' => 'test'
|
211
|
-
})
|
212
|
-
end
|
213
|
-
|
214
|
-
should "use them when running the command" do
|
215
|
-
@cmd.run
|
216
|
-
assert_equal "test\n", @cmd.stdout
|
217
|
-
end
|
218
|
-
|
219
|
-
end
|
220
|
-
|
221
74
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scmd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 2.
|
10
|
+
version: 2.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kelly Redding
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2014-
|
19
|
+
date: 2014-07-31 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
@@ -29,9 +29,9 @@ dependencies:
|
|
29
29
|
- 2
|
30
30
|
- 3
|
31
31
|
version: "2.3"
|
32
|
-
version_requirements: *id001
|
33
32
|
type: :development
|
34
33
|
name: assert
|
34
|
+
version_requirements: *id001
|
35
35
|
prerelease: false
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
@@ -43,9 +43,9 @@ dependencies:
|
|
43
43
|
segments:
|
44
44
|
- 0
|
45
45
|
version: "0"
|
46
|
-
version_requirements: *id002
|
47
46
|
type: :runtime
|
48
47
|
name: posix-spawn
|
48
|
+
version_requirements: *id002
|
49
49
|
prerelease: false
|
50
50
|
description: Build and run system commands.
|
51
51
|
email:
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- test/helper.rb
|
75
75
|
- test/support/bigger-than-64k.txt
|
76
76
|
- test/support/smaller-than-64k.txt
|
77
|
+
- test/system/command_tests.rb
|
77
78
|
- test/unit/command_tests.rb
|
78
79
|
- test/unit/scmd_tests.rb
|
79
80
|
- tmp/.gitkeep
|
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
107
|
requirements: []
|
107
108
|
|
108
109
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.8.
|
110
|
+
rubygems_version: 1.8.25
|
110
111
|
signing_key:
|
111
112
|
specification_version: 3
|
112
113
|
summary: Build and run system commands.
|
@@ -114,5 +115,6 @@ test_files:
|
|
114
115
|
- test/helper.rb
|
115
116
|
- test/support/bigger-than-64k.txt
|
116
117
|
- test/support/smaller-than-64k.txt
|
118
|
+
- test/system/command_tests.rb
|
117
119
|
- test/unit/command_tests.rb
|
118
120
|
- test/unit/scmd_tests.rb
|