scmd 3.0.3 → 3.0.4
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 +7 -7
- data/Gemfile +5 -2
- data/README.md +1 -1
- data/bench/results.txt +32 -32
- data/bench/runner.rb +12 -10
- data/lib/scmd.rb +13 -13
- data/lib/scmd/command.rb +52 -37
- data/lib/scmd/command_spy.rb +22 -24
- data/lib/scmd/stored_commands.rb +10 -14
- data/lib/scmd/version.rb +3 -1
- data/scmd.gemspec +10 -7
- data/script/bench.rb +6 -7
- data/test/helper.rb +5 -3
- data/test/support/factory.rb +3 -2
- data/test/system/command_tests.rb +22 -27
- data/test/unit/command_spy_tests.rb +15 -17
- data/test/unit/command_tests.rb +8 -9
- data/test/unit/scmd_tests.rb +17 -22
- data/test/unit/stored_commands_tests.rb +6 -8
- data/tmp/.gitkeep +0 -0
- metadata +58 -44
- data/.gitignore +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
data/README.md
CHANGED
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,42 +1,43 @@
|
|
1
|
-
|
2
|
-
require 'scmd/command'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
3
|
+
require "scmd/version"
|
4
|
+
require "scmd/command"
|
5
5
|
|
6
|
+
module Scmd
|
6
7
|
# Scmd can be run in "test mode". This means that command spies will be used
|
7
8
|
# in place of "live" commands, each time a command is run or started will be
|
8
9
|
# logged in a collection and option-specific spies can be added and used to
|
9
10
|
# "stub" spies with specific attributes in specific contexts.
|
10
11
|
|
11
12
|
def self.new(*args)
|
12
|
-
if !ENV[
|
13
|
+
if !ENV["SCMD_TEST_MODE"]
|
13
14
|
Command.new(*args)
|
14
15
|
else
|
15
|
-
|
16
|
+
commands.get(*args)
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
20
|
def self.commands
|
20
|
-
raise NoMethodError
|
21
|
+
raise NoMethodError unless ENV["SCMD_TEST_MODE"]
|
21
22
|
@commands ||= begin
|
22
|
-
require
|
23
|
+
require "scmd/stored_commands"
|
23
24
|
StoredCommands.new
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
def self.calls
|
28
|
-
raise NoMethodError
|
29
|
+
raise NoMethodError unless ENV["SCMD_TEST_MODE"]
|
29
30
|
@calls ||= []
|
30
31
|
end
|
31
32
|
|
32
33
|
def self.reset
|
33
|
-
raise NoMethodError
|
34
|
-
|
35
|
-
|
34
|
+
raise NoMethodError unless ENV["SCMD_TEST_MODE"]
|
35
|
+
calls.clear
|
36
|
+
commands.remove_all
|
36
37
|
end
|
37
38
|
|
38
39
|
def self.add_command(cmd_str, &block)
|
39
|
-
|
40
|
+
commands.add(cmd_str, &block)
|
40
41
|
end
|
41
42
|
|
42
43
|
class Call < Struct.new(:cmd_str, :input, :cmd)
|
@@ -53,5 +54,4 @@ module Scmd
|
|
53
54
|
set_backtrace(called_from || caller)
|
54
55
|
end
|
55
56
|
end
|
56
|
-
|
57
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,7 +10,6 @@ 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
|
@@ -26,7 +27,11 @@ module Scmd
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def run(input = nil)
|
29
|
-
|
30
|
+
begin
|
31
|
+
run!(input)
|
32
|
+
rescue
|
33
|
+
RunError
|
34
|
+
end
|
30
35
|
self
|
31
36
|
end
|
32
37
|
|
@@ -34,11 +39,13 @@ module Scmd
|
|
34
39
|
start_err_msg, start_err_bt = nil, nil
|
35
40
|
begin
|
36
41
|
start(input)
|
37
|
-
rescue
|
38
|
-
start_err_msg, start_err_bt =
|
42
|
+
rescue => ex
|
43
|
+
start_err_msg, start_err_bt = ex.message, ex.backtrace
|
39
44
|
ensure
|
40
45
|
wait # indefinitely until cmd is done running
|
41
|
-
|
46
|
+
unless success?
|
47
|
+
raise RunError.new(start_err_msg || @stderr, start_err_bt || caller)
|
48
|
+
end
|
42
49
|
end
|
43
50
|
|
44
51
|
self
|
@@ -53,15 +60,15 @@ module Scmd
|
|
53
60
|
while @child_process.check_for_exit
|
54
61
|
begin
|
55
62
|
read_output
|
56
|
-
rescue EOFError
|
63
|
+
rescue EOFError # rubocop:disable Lint/SuppressedException
|
57
64
|
end
|
58
65
|
end
|
59
|
-
@stop_w.write_nonblock(
|
66
|
+
@stop_w.write_nonblock(".")
|
60
67
|
end
|
61
68
|
end
|
62
69
|
|
63
70
|
def wait(timeout = nil)
|
64
|
-
return
|
71
|
+
return unless running?
|
65
72
|
|
66
73
|
wait_for_exit(timeout)
|
67
74
|
if @child_process.running?
|
@@ -78,18 +85,18 @@ module Scmd
|
|
78
85
|
end
|
79
86
|
|
80
87
|
def stop(timeout = nil)
|
81
|
-
return
|
88
|
+
return unless running?
|
82
89
|
|
83
90
|
send_term
|
84
91
|
begin
|
85
92
|
wait(timeout || DEFAULT_STOP_TIMEOUT)
|
86
|
-
rescue TimeoutError
|
93
|
+
rescue TimeoutError
|
87
94
|
kill
|
88
95
|
end
|
89
96
|
end
|
90
97
|
|
91
98
|
def kill(signal = nil)
|
92
|
-
return
|
99
|
+
return unless running?
|
93
100
|
|
94
101
|
send_kill(signal)
|
95
102
|
wait # indefinitely until cmd is killed
|
@@ -108,25 +115,28 @@ module Scmd
|
|
108
115
|
end
|
109
116
|
|
110
117
|
def inspect
|
111
|
-
reference =
|
118
|
+
reference = "0x0%x" % (object_id << 1)
|
112
119
|
"#<#{self.class}:#{reference}"\
|
113
|
-
" @cmd_str=#{
|
120
|
+
" @cmd_str=#{cmd_str.inspect}"\
|
114
121
|
" @exitstatus=#{@exitstatus.inspect}>"
|
115
122
|
end
|
116
123
|
|
117
124
|
private
|
118
125
|
|
119
126
|
def read_output
|
120
|
-
@child_process.read(READ_SIZE)
|
127
|
+
@child_process.read(READ_SIZE) do |out, err|
|
128
|
+
@stdout += out
|
129
|
+
@stderr += err
|
130
|
+
end
|
121
131
|
end
|
122
132
|
|
123
133
|
def wait_for_exit(timeout)
|
124
|
-
ios, _, _ = IO.select([
|
125
|
-
@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)
|
126
136
|
end
|
127
137
|
|
128
138
|
def reset_attrs
|
129
|
-
@stdout, @stderr, @pid, @exitstatus =
|
139
|
+
@stdout, @stderr, @pid, @exitstatus = +"", +"", nil, nil
|
130
140
|
end
|
131
141
|
|
132
142
|
def setup_run
|
@@ -145,15 +155,15 @@ module Scmd
|
|
145
155
|
end
|
146
156
|
|
147
157
|
def send_term
|
148
|
-
send_signal
|
158
|
+
send_signal "TERM"
|
149
159
|
end
|
150
160
|
|
151
161
|
def send_kill(signal = nil)
|
152
|
-
send_signal(signal ||
|
162
|
+
send_signal(signal || "KILL")
|
153
163
|
end
|
154
164
|
|
155
165
|
def send_signal(sig)
|
156
|
-
return
|
166
|
+
return unless running?
|
157
167
|
@child_process.send_signal(sig)
|
158
168
|
end
|
159
169
|
|
@@ -164,14 +174,13 @@ module Scmd
|
|
164
174
|
end
|
165
175
|
|
166
176
|
class ChildProcess
|
167
|
-
|
168
177
|
attr_reader :pid, :stdin, :stdout, :stderr
|
169
178
|
|
170
179
|
def initialize(cmd_str, env, options)
|
171
|
-
@pid, @stdin, @stdout, @stderr = *::POSIX::Spawn
|
180
|
+
@pid, @stdin, @stdout, @stderr = *::POSIX::Spawn.popen4(
|
172
181
|
env,
|
173
182
|
cmd_str,
|
174
|
-
options
|
183
|
+
options,
|
175
184
|
)
|
176
185
|
@wait_pid, @wait_status = nil, nil
|
177
186
|
end
|
@@ -194,25 +203,34 @@ module Scmd
|
|
194
203
|
end
|
195
204
|
|
196
205
|
def write(input)
|
197
|
-
|
206
|
+
unless input.nil?
|
198
207
|
[*input].each{ |line| @stdin.puts line.to_s }
|
199
208
|
@stdin.close
|
200
209
|
end
|
201
210
|
end
|
202
211
|
|
203
212
|
def read(size)
|
204
|
-
ios, _, _ =
|
213
|
+
ios, _, _ =
|
214
|
+
IO.select([@stdout, @stderr], nil, nil, READ_CHECK_TIMEOUT)
|
205
215
|
if ios && block_given?
|
206
|
-
yield
|
216
|
+
yield(
|
217
|
+
read_if_ready(ios, @stdout, size),
|
218
|
+
read_if_ready(ios, @stderr, size)
|
219
|
+
)
|
207
220
|
end
|
208
221
|
end
|
209
222
|
|
210
223
|
def send_signal(sig)
|
211
|
-
process_kill(sig,
|
224
|
+
process_kill(sig, pid)
|
212
225
|
end
|
213
226
|
|
214
|
-
def flush_stdout
|
215
|
-
|
227
|
+
def flush_stdout
|
228
|
+
@stdout.read
|
229
|
+
end
|
230
|
+
|
231
|
+
def flush_stderr
|
232
|
+
@stderr.read
|
233
|
+
end
|
216
234
|
|
217
235
|
def teardown
|
218
236
|
[@stdin, @stdout, @stderr].each{ |fd| fd.close if fd && !fd.closed? }
|
@@ -221,7 +239,7 @@ module Scmd
|
|
221
239
|
private
|
222
240
|
|
223
241
|
def read_if_ready(ready_ios, io, size)
|
224
|
-
ready_ios.include?(io) ? read_by_size(io, size) :
|
242
|
+
ready_ios.include?(io) ? read_by_size(io, size) : ""
|
225
243
|
end
|
226
244
|
|
227
245
|
def read_by_size(io, size)
|
@@ -238,11 +256,8 @@ module Scmd
|
|
238
256
|
end
|
239
257
|
|
240
258
|
def pgrep
|
241
|
-
@pgrep ||= Command.new(
|
259
|
+
@pgrep ||= Command.new("which pgrep").run.stdout.strip
|
242
260
|
end
|
243
|
-
|
244
261
|
end
|
245
|
-
|
246
262
|
end
|
247
|
-
|
248
263
|
end
|