scmd 2.3.1 → 3.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +6 -6
- data/{LICENSE.txt → LICENSE} +0 -0
- data/README.md +93 -1
- data/bench/results.txt +32 -32
- data/bench/runner.rb +12 -10
- data/lib/scmd.rb +43 -5
- data/lib/scmd/command.rb +65 -44
- data/lib/scmd/command_spy.rb +119 -0
- data/lib/scmd/stored_commands.rb +74 -0
- data/lib/scmd/version.rb +3 -1
- data/scmd.gemspec +12 -7
- data/script/bench.rb +6 -7
- data/test/helper.rb +14 -1
- data/test/support/factory.rb +7 -0
- data/test/system/command_tests.rb +39 -23
- data/test/unit/command_spy_tests.rb +242 -0
- data/test/unit/command_tests.rb +15 -12
- data/test/unit/scmd_tests.rb +90 -12
- data/test/unit/stored_commands_tests.rb +144 -0
- data/tmp/.gitkeep +0 -0
- metadata +68 -71
- data/.gitignore +0 -19
- data/Rakefile +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 67125e428f1adb1fe7f302c3d0276a0384d5b41a74be0a40b52256043dc22632
|
4
|
+
data.tar.gz: c1dcc9b7152046ff0a5af224acc3c31f3124c423e818d048f8879310d2b9d645
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 74b33660ba6eab558d637797e1e03ec67607a0110be680822a22851fa7068d14b60a4d8f2fb1ff58ad1867f1c964bd34c619838efa7187c775eb491b6cef04c3
|
7
|
+
data.tar.gz: 17ea7892c22832d00868a8b9659ba8aabfc57b1c0c3df0a91e41a929d47af039ac40110b3a3b58a3688e09db61a42a531b68903a23f0ec4c8e978bec8deb3002
|
data/Gemfile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
5
|
gemspec
|
4
6
|
|
5
|
-
gem
|
6
|
-
gem
|
7
|
-
gem 'whysoslow'
|
7
|
+
gem "pry"
|
8
|
+
gem "whysoslow"
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
10
|
+
# to release, uncomment this and run `bundle exec ggem r -f`
|
11
|
+
gem 'ggem'
|
data/{LICENSE.txt → LICENSE}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -42,7 +42,7 @@ cmd.start
|
|
42
42
|
|
43
43
|
begin
|
44
44
|
cmd.wait(10)
|
45
|
-
rescue Scmd::
|
45
|
+
rescue Scmd::TimeoutError => err
|
46
46
|
cmd.stop # attempt to stop the cmd nicely, kill if doesn't stop in time
|
47
47
|
cmd.kill # just kill the cmd now
|
48
48
|
end
|
@@ -94,6 +94,98 @@ Raise an exception if not successful with `run!`:
|
|
94
94
|
Scmd.new("cd /path/that/does/not/exist").run! #=> Scmd::Command::Failure
|
95
95
|
```
|
96
96
|
|
97
|
+
### Environment variables
|
98
|
+
|
99
|
+
Pass environment variables:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
cmd = Scmd.new("echo $TEST_VAR", {
|
103
|
+
:env => {
|
104
|
+
'TEST_VAR' => 'hi'
|
105
|
+
}
|
106
|
+
})
|
107
|
+
```
|
108
|
+
|
109
|
+
### Process spawn options
|
110
|
+
|
111
|
+
Pass options:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
reader, writer = IO.pipe
|
115
|
+
# this is an example that uses file descriptor redirection options
|
116
|
+
cmd = Scmd.new("echo test 1>&#{writer.fileno}", {
|
117
|
+
:options => { writer => writer }
|
118
|
+
})
|
119
|
+
reader.gets # => "test\n"
|
120
|
+
```
|
121
|
+
|
122
|
+
For all the possible options see [posix-spawn](https://github.com/rtomayko/posix-spawn#status).
|
123
|
+
|
124
|
+
## Testing
|
125
|
+
|
126
|
+
Scmd comes with some testing utilities built in. Specifically this includes a command spy and a "test mode" API on the main `Scmd` namespace.
|
127
|
+
|
128
|
+
### Command Spy
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
require 'scmd/command_spy'
|
132
|
+
spy = Scmd::CommandSpy.new(cmd_str)
|
133
|
+
spy.exitstatus = 1
|
134
|
+
spy.stdout = 'some test output'
|
135
|
+
Assert.stub(Scmd, :new).with(cmd_str){ spy }
|
136
|
+
|
137
|
+
cmd = Scmd.new(cmd_str) # => spy
|
138
|
+
cmd.run('some input')
|
139
|
+
|
140
|
+
cmd.run_called? # => true
|
141
|
+
cmd.run_calls.size # => 1
|
142
|
+
cmd.run_calls.first.input # => 'some input'
|
143
|
+
```
|
144
|
+
|
145
|
+
The spy is useful for stubbing out system commands that you don't want to call or aren't safe to call in the test suite. It responds to the same API that commands do but doesn't run any system commands.
|
146
|
+
|
147
|
+
### "Test Mode" API
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
Scmd.add_command(cmd_str){ |cmd| cmd.stdout = 'some output' } # => raises NoMethodError
|
151
|
+
|
152
|
+
ENV['SCMD_TEST_MODE'] = '1'
|
153
|
+
Scmd.add_command(cmd_str){ |cmd| cmd.stdout = 'some output' }
|
154
|
+
Scmd.add_command(cmd_str).with({:env => { :SOME_ENV_VAR => '1' }}) do |cmd|
|
155
|
+
cmd.stdout = 'some other output'
|
156
|
+
end
|
157
|
+
Scmd.commands.empty? # => false
|
158
|
+
|
159
|
+
cmd = Scmd.new(cmd_str)
|
160
|
+
cmd.class # => Scmd::CommandSpy
|
161
|
+
cmd.stdout # => 'some output'
|
162
|
+
cmd.run('some input')
|
163
|
+
Scmd.calls.size # => 1
|
164
|
+
Scmd.calls.last.class # => Scmd::Call
|
165
|
+
Scmd.calls.last.cmd_str # => cmd_str
|
166
|
+
Scmd.calls.last.input # => 'some input'
|
167
|
+
Scmd.calls.last.cmd.class # => Scmd::CommandSpy
|
168
|
+
|
169
|
+
cmd = Scmd.new(cmd_str, {:env => { 'SOME_ENV_VAR' => '1' }})
|
170
|
+
cmd.class # => Scmd::CommandSpy
|
171
|
+
cmd.stdout # => 'some other output'
|
172
|
+
cmd.run('some input')
|
173
|
+
Scmd.calls.size # => 2
|
174
|
+
Scmd.calls.last.class # => Scmd::Call
|
175
|
+
Scmd.calls.last.cmd_str # => cmd_str
|
176
|
+
Scmd.calls.last.input # => 'some input'
|
177
|
+
Scmd.calls.last.cmd.class # => Scmd::CommandSpy
|
178
|
+
Scmd.calls.last.cmd.env # => { 'SOME_ENV_VAR' => '1' }
|
179
|
+
|
180
|
+
Scmd.reset
|
181
|
+
Scmd.commands.empty? # => true
|
182
|
+
Scmd.calls.empty? # => true
|
183
|
+
```
|
184
|
+
|
185
|
+
Use these singleton methods on the `Scmd` namespace to add specific command spies in specific contexts and to track command calls (runs, starts). Use `reset` to reset the state of things.
|
186
|
+
|
187
|
+
**Note:** these methods are only available when test mode is enabled (when the `SCMD_TEST_MODE` env var has a non-falsey value). Otherwise these methods will raise `NoMethodError`.
|
188
|
+
|
97
189
|
## Installation
|
98
190
|
|
99
191
|
Add this line to your application's Gemfile:
|
data/bench/results.txt
CHANGED
@@ -2,79 +2,79 @@ echo hi: 1 times
|
|
2
2
|
----------------
|
3
3
|
whysoslow? ..
|
4
4
|
|
5
|
-
mem @ start
|
6
|
-
mem @ finish
|
5
|
+
mem @ start 17 MB ??
|
6
|
+
mem @ finish 17 MB [31m+ 0 MB, 0%[0m
|
7
7
|
|
8
|
-
|
9
|
-
time
|
8
|
+
user system total real
|
9
|
+
time 0.5820000000000001 ms 0.5519999999999999 ms 3.786 ms 3.7030000000000003 ms
|
10
10
|
|
11
11
|
echo hi: 10 times
|
12
12
|
-----------------
|
13
13
|
whysoslow? ..
|
14
14
|
|
15
|
-
mem @ start
|
16
|
-
mem @ finish
|
15
|
+
mem @ start 17 MB ??
|
16
|
+
mem @ finish 17 MB [31m+ 0 MB, 0%[0m
|
17
17
|
|
18
|
-
|
19
|
-
time
|
18
|
+
user system total real
|
19
|
+
time 2.475 ms 3.822 ms 32.675999999999995 ms 34.702 ms
|
20
20
|
|
21
21
|
echo hi: 100 times
|
22
22
|
------------------
|
23
23
|
whysoslow? ..
|
24
24
|
|
25
|
-
mem @ start
|
26
|
-
mem @ finish
|
25
|
+
mem @ start 17 MB ??
|
26
|
+
mem @ finish 18 MB [31m+ 0 MB, 2%[0m
|
27
27
|
|
28
|
-
|
29
|
-
time
|
28
|
+
user system total real
|
29
|
+
time 19.75 ms 31.77 ms 281.766 ms 306.74899999999997 ms
|
30
30
|
|
31
31
|
echo hi: 1000 times
|
32
32
|
-------------------
|
33
33
|
whysoslow? ..
|
34
34
|
|
35
|
-
mem @ start
|
36
|
-
mem @ finish
|
35
|
+
mem @ start 18 MB ??
|
36
|
+
mem @ finish 29 MB [31m+ 11 MB, 61%[0m
|
37
37
|
|
38
|
-
|
39
|
-
time
|
38
|
+
user system total real
|
39
|
+
time 161.98399999999998 ms 271.296 ms 2432.76 ms 2662.98 ms
|
40
40
|
|
41
41
|
cat test/support/bigger-than-64k.txt: 1 times
|
42
42
|
---------------------------------------------
|
43
43
|
whysoslow? ..
|
44
44
|
|
45
|
-
mem @ start
|
46
|
-
mem @ finish
|
45
|
+
mem @ start 31 MB ??
|
46
|
+
mem @ finish 31 MB [31m+ 0 MB, 1%[0m
|
47
47
|
|
48
|
-
|
49
|
-
time
|
48
|
+
user system total real
|
49
|
+
time 0.412 ms 0.504 ms 4.343999999999999 ms 4.654 ms
|
50
50
|
|
51
51
|
cat test/support/bigger-than-64k.txt: 10 times
|
52
52
|
----------------------------------------------
|
53
53
|
whysoslow? ..
|
54
54
|
|
55
|
-
mem @ start
|
56
|
-
mem @ finish
|
55
|
+
mem @ start 31 MB ??
|
56
|
+
mem @ finish 34 MB [31m+ 2 MB, 7%[0m
|
57
57
|
|
58
|
-
|
59
|
-
time
|
58
|
+
user system total real
|
59
|
+
time 3.2880000000000003 ms 4.386 ms 43.447 ms 46.552 ms
|
60
60
|
|
61
61
|
cat test/support/bigger-than-64k.txt: 100 times
|
62
62
|
-----------------------------------------------
|
63
63
|
whysoslow? ..
|
64
64
|
|
65
|
-
mem @ start
|
66
|
-
mem @ finish
|
65
|
+
mem @ start 33 MB ??
|
66
|
+
mem @ finish 56 MB [31m+ 23 MB, 68%[0m
|
67
67
|
|
68
|
-
|
69
|
-
time
|
68
|
+
user system total real
|
69
|
+
time 28.892999999999997 ms 40.599000000000004 ms 407.804 ms 440.11 ms
|
70
70
|
|
71
71
|
cat test/support/bigger-than-64k.txt: 1000 times
|
72
72
|
------------------------------------------------
|
73
73
|
whysoslow? ..
|
74
74
|
|
75
|
-
mem @ start
|
76
|
-
mem @ finish
|
75
|
+
mem @ start 44 MB ??
|
76
|
+
mem @ finish 297 MB [31m+ 254 MB, 582%[0m
|
77
77
|
|
78
|
-
|
79
|
-
time
|
78
|
+
user system total real
|
79
|
+
time 285.236 ms 408.677 ms 4097.647 ms 4428.276999999999 ms
|
80
80
|
|
data/bench/runner.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
require 'scmd'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
3
|
+
require "whysoslow"
|
4
|
+
require "scmd"
|
5
5
|
|
6
|
+
class ScmdBenchRunner
|
6
7
|
attr_reader :result
|
7
8
|
|
8
9
|
def self.run(*args)
|
9
|
-
|
10
|
+
new(*args).run
|
10
11
|
end
|
11
12
|
|
12
13
|
def initialize(printer_io, cmd, num_times = 10)
|
@@ -15,15 +16,16 @@ class ScmdBenchRunner
|
|
15
16
|
num_times.times{ cmd.run! }
|
16
17
|
end
|
17
18
|
|
18
|
-
@printer =
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
@printer =
|
20
|
+
Whysoslow::DefaultPrinter.new(
|
21
|
+
printer_io,
|
22
|
+
title: "#{@cmd.cmd_str}: #{num_times} times",
|
23
|
+
verbose: true,
|
24
|
+
)
|
22
25
|
@runner = Whysoslow::Runner.new(@printer)
|
23
26
|
end
|
24
27
|
|
25
28
|
def run
|
26
|
-
@runner.run
|
29
|
+
@runner.run(&@proc)
|
27
30
|
end
|
28
|
-
|
29
31
|
end
|
data/lib/scmd.rb
CHANGED
@@ -1,10 +1,49 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "scmd/version"
|
4
|
+
require "scmd/command"
|
3
5
|
|
4
6
|
module Scmd
|
7
|
+
# Scmd can be run in "test mode". This means that command spies will be used
|
8
|
+
# in place of "live" commands, each time a command is run or started will be
|
9
|
+
# logged in a collection and option-specific spies can be added and used to
|
10
|
+
# "stub" spies with specific attributes in specific contexts.
|
11
|
+
|
12
|
+
def self.new(*args)
|
13
|
+
if !ENV["SCMD_TEST_MODE"]
|
14
|
+
Command.new(*args)
|
15
|
+
else
|
16
|
+
commands.get(*args)
|
17
|
+
end
|
18
|
+
end
|
5
19
|
|
6
|
-
def self.
|
7
|
-
|
20
|
+
def self.commands
|
21
|
+
raise NoMethodError unless ENV["SCMD_TEST_MODE"]
|
22
|
+
@commands ||= begin
|
23
|
+
require "scmd/stored_commands"
|
24
|
+
StoredCommands.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.calls
|
29
|
+
raise NoMethodError unless ENV["SCMD_TEST_MODE"]
|
30
|
+
@calls ||= []
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.reset
|
34
|
+
raise NoMethodError unless ENV["SCMD_TEST_MODE"]
|
35
|
+
calls.clear
|
36
|
+
commands.remove_all
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.add_command(cmd_str, &block)
|
40
|
+
commands.add(cmd_str, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
class Call < Struct.new(:cmd_str, :input, :cmd)
|
44
|
+
def initialize(cmd_spy, input)
|
45
|
+
super(cmd_spy.cmd_str, input, cmd_spy)
|
46
|
+
end
|
8
47
|
end
|
9
48
|
|
10
49
|
TimeoutError = Class.new(::RuntimeError)
|
@@ -15,5 +54,4 @@ module Scmd
|
|
15
54
|
set_backtrace(called_from || caller)
|
16
55
|
end
|
17
56
|
end
|
18
|
-
|
19
57
|
end
|
data/lib/scmd/command.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thread"
|
4
|
+
require "posix-spawn"
|
5
|
+
require "scmd"
|
4
6
|
|
5
7
|
# Scmd::Command is a base wrapper for handling system commands. Initialize it
|
6
8
|
# with with a string specifying the command to execute. You can then run the
|
@@ -8,23 +10,28 @@ require 'scmd'
|
|
8
10
|
# create a more custom command wrapper.
|
9
11
|
|
10
12
|
module Scmd
|
11
|
-
|
12
13
|
class Command
|
13
14
|
READ_SIZE = 10240 # bytes
|
14
15
|
READ_CHECK_TIMEOUT = 0.001 # seconds
|
15
16
|
DEFAULT_STOP_TIMEOUT = 3 # seconds
|
16
17
|
|
17
|
-
attr_reader :cmd_str, :env
|
18
|
+
attr_reader :cmd_str, :env, :options
|
18
19
|
attr_reader :pid, :exitstatus, :stdout, :stderr
|
19
20
|
|
20
|
-
def initialize(cmd_str,
|
21
|
+
def initialize(cmd_str, opts = nil)
|
22
|
+
opts ||= {}
|
21
23
|
@cmd_str = cmd_str
|
22
|
-
@env = stringify_hash(env || {})
|
24
|
+
@env = stringify_hash(opts[:env] || {})
|
25
|
+
@options = opts[:options] || {}
|
23
26
|
reset_attrs
|
24
27
|
end
|
25
28
|
|
26
29
|
def run(input = nil)
|
27
|
-
|
30
|
+
begin
|
31
|
+
run!(input)
|
32
|
+
rescue
|
33
|
+
RunError
|
34
|
+
end
|
28
35
|
self
|
29
36
|
end
|
30
37
|
|
@@ -32,11 +39,13 @@ module Scmd
|
|
32
39
|
start_err_msg, start_err_bt = nil, nil
|
33
40
|
begin
|
34
41
|
start(input)
|
35
|
-
rescue
|
36
|
-
start_err_msg, start_err_bt =
|
42
|
+
rescue => ex
|
43
|
+
start_err_msg, start_err_bt = ex.message, ex.backtrace
|
37
44
|
ensure
|
38
45
|
wait # indefinitely until cmd is done running
|
39
|
-
|
46
|
+
unless success?
|
47
|
+
raise RunError.new(start_err_msg || @stderr, start_err_bt || caller)
|
48
|
+
end
|
40
49
|
end
|
41
50
|
|
42
51
|
self
|
@@ -51,15 +60,15 @@ module Scmd
|
|
51
60
|
while @child_process.check_for_exit
|
52
61
|
begin
|
53
62
|
read_output
|
54
|
-
rescue EOFError
|
63
|
+
rescue EOFError # rubocop:disable Lint/SuppressedException
|
55
64
|
end
|
56
65
|
end
|
57
|
-
@stop_w.write_nonblock(
|
66
|
+
@stop_w.write_nonblock(".")
|
58
67
|
end
|
59
68
|
end
|
60
69
|
|
61
70
|
def wait(timeout = nil)
|
62
|
-
return
|
71
|
+
return unless running?
|
63
72
|
|
64
73
|
wait_for_exit(timeout)
|
65
74
|
if @child_process.running?
|
@@ -76,20 +85,20 @@ module Scmd
|
|
76
85
|
end
|
77
86
|
|
78
87
|
def stop(timeout = nil)
|
79
|
-
return
|
88
|
+
return unless running?
|
80
89
|
|
81
90
|
send_term
|
82
91
|
begin
|
83
92
|
wait(timeout || DEFAULT_STOP_TIMEOUT)
|
84
|
-
rescue TimeoutError
|
93
|
+
rescue TimeoutError
|
85
94
|
kill
|
86
95
|
end
|
87
96
|
end
|
88
97
|
|
89
|
-
def kill(
|
90
|
-
return
|
98
|
+
def kill(signal = nil)
|
99
|
+
return unless running?
|
91
100
|
|
92
|
-
send_kill(
|
101
|
+
send_kill(signal)
|
93
102
|
wait # indefinitely until cmd is killed
|
94
103
|
end
|
95
104
|
|
@@ -106,32 +115,35 @@ module Scmd
|
|
106
115
|
end
|
107
116
|
|
108
117
|
def inspect
|
109
|
-
reference =
|
118
|
+
reference = "0x0%x" % (object_id << 1)
|
110
119
|
"#<#{self.class}:#{reference}"\
|
111
|
-
" @cmd_str=#{
|
120
|
+
" @cmd_str=#{cmd_str.inspect}"\
|
112
121
|
" @exitstatus=#{@exitstatus.inspect}>"
|
113
122
|
end
|
114
123
|
|
115
124
|
private
|
116
125
|
|
117
126
|
def read_output
|
118
|
-
@child_process.read(READ_SIZE)
|
127
|
+
@child_process.read(READ_SIZE) do |out, err|
|
128
|
+
@stdout += out
|
129
|
+
@stderr += err
|
130
|
+
end
|
119
131
|
end
|
120
132
|
|
121
133
|
def wait_for_exit(timeout)
|
122
|
-
ios, _, _ = IO.select([
|
123
|
-
@stop_r.read_nonblock(1) if ios
|
134
|
+
ios, _, _ = IO.select([@stop_r], nil, nil, timeout)
|
135
|
+
@stop_r.read_nonblock(1) if ios&.include?(@stop_r)
|
124
136
|
end
|
125
137
|
|
126
138
|
def reset_attrs
|
127
|
-
@stdout, @stderr, @pid, @exitstatus =
|
139
|
+
@stdout, @stderr, @pid, @exitstatus = +"", +"", nil, nil
|
128
140
|
end
|
129
141
|
|
130
142
|
def setup_run
|
131
143
|
reset_attrs
|
132
144
|
@stop_r, @stop_w = IO.pipe
|
133
145
|
@read_output_thread = nil
|
134
|
-
@child_process = ChildProcess.new(@cmd_str, @env)
|
146
|
+
@child_process = ChildProcess.new(@cmd_str, @env, @options)
|
135
147
|
end
|
136
148
|
|
137
149
|
def teardown_run
|
@@ -143,15 +155,15 @@ module Scmd
|
|
143
155
|
end
|
144
156
|
|
145
157
|
def send_term
|
146
|
-
send_signal
|
158
|
+
send_signal "TERM"
|
147
159
|
end
|
148
160
|
|
149
|
-
def send_kill(
|
150
|
-
send_signal(
|
161
|
+
def send_kill(signal = nil)
|
162
|
+
send_signal(signal || "KILL")
|
151
163
|
end
|
152
164
|
|
153
165
|
def send_signal(sig)
|
154
|
-
return
|
166
|
+
return unless running?
|
155
167
|
@child_process.send_signal(sig)
|
156
168
|
end
|
157
169
|
|
@@ -162,11 +174,14 @@ module Scmd
|
|
162
174
|
end
|
163
175
|
|
164
176
|
class ChildProcess
|
165
|
-
|
166
177
|
attr_reader :pid, :stdin, :stdout, :stderr
|
167
178
|
|
168
|
-
def initialize(cmd_str, env)
|
169
|
-
@pid, @stdin, @stdout, @stderr = *::POSIX::Spawn
|
179
|
+
def initialize(cmd_str, env, options)
|
180
|
+
@pid, @stdin, @stdout, @stderr = *::POSIX::Spawn.popen4(
|
181
|
+
env,
|
182
|
+
cmd_str,
|
183
|
+
options,
|
184
|
+
)
|
170
185
|
@wait_pid, @wait_status = nil, nil
|
171
186
|
end
|
172
187
|
|
@@ -188,25 +203,34 @@ module Scmd
|
|
188
203
|
end
|
189
204
|
|
190
205
|
def write(input)
|
191
|
-
|
206
|
+
unless input.nil?
|
192
207
|
[*input].each{ |line| @stdin.puts line.to_s }
|
193
208
|
@stdin.close
|
194
209
|
end
|
195
210
|
end
|
196
211
|
|
197
212
|
def read(size)
|
198
|
-
ios, _, _ =
|
213
|
+
ios, _, _ =
|
214
|
+
IO.select([@stdout, @stderr], nil, nil, READ_CHECK_TIMEOUT)
|
199
215
|
if ios && block_given?
|
200
|
-
yield
|
216
|
+
yield(
|
217
|
+
read_if_ready(ios, @stdout, size),
|
218
|
+
read_if_ready(ios, @stderr, size)
|
219
|
+
)
|
201
220
|
end
|
202
221
|
end
|
203
222
|
|
204
223
|
def send_signal(sig)
|
205
|
-
process_kill(sig,
|
224
|
+
process_kill(sig, pid)
|
206
225
|
end
|
207
226
|
|
208
|
-
def flush_stdout
|
209
|
-
|
227
|
+
def flush_stdout
|
228
|
+
@stdout.read
|
229
|
+
end
|
230
|
+
|
231
|
+
def flush_stderr
|
232
|
+
@stderr.read
|
233
|
+
end
|
210
234
|
|
211
235
|
def teardown
|
212
236
|
[@stdin, @stdout, @stderr].each{ |fd| fd.close if fd && !fd.closed? }
|
@@ -215,7 +239,7 @@ module Scmd
|
|
215
239
|
private
|
216
240
|
|
217
241
|
def read_if_ready(ready_ios, io, size)
|
218
|
-
ready_ios.include?(io) ? read_by_size(io, size) :
|
242
|
+
ready_ios.include?(io) ? read_by_size(io, size) : ""
|
219
243
|
end
|
220
244
|
|
221
245
|
def read_by_size(io, size)
|
@@ -232,11 +256,8 @@ module Scmd
|
|
232
256
|
end
|
233
257
|
|
234
258
|
def pgrep
|
235
|
-
@pgrep ||= Command.new(
|
259
|
+
@pgrep ||= Command.new("which pgrep").run.stdout.strip
|
236
260
|
end
|
237
|
-
|
238
261
|
end
|
239
|
-
|
240
262
|
end
|
241
|
-
|
242
263
|
end
|