scmd 3.0.3 → 3.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/scmd/command_spy.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "scmd"
|
4
4
|
|
5
|
+
module Scmd
|
5
6
|
class CommandSpy
|
6
|
-
|
7
7
|
attr_reader :cmd_str, :env, :options
|
8
8
|
attr_reader :run_calls, :run_bang_calls, :start_calls
|
9
9
|
attr_reader :wait_calls, :stop_calls, :kill_calls
|
@@ -20,12 +20,12 @@ module Scmd
|
|
20
20
|
|
21
21
|
@running = false
|
22
22
|
|
23
|
-
@stdout, @stderr, @pid, @exitstatus =
|
23
|
+
@stdout, @stderr, @pid, @exitstatus = "", "", 1, 0
|
24
24
|
end
|
25
25
|
|
26
26
|
def run(input = nil)
|
27
27
|
@run_calls.push(InputCall.new(input))
|
28
|
-
Scmd.calls.push(Scmd::Call.new(self, input)) if ENV[
|
28
|
+
Scmd.calls.push(Scmd::Call.new(self, input)) if ENV["SCMD_TEST_MODE"]
|
29
29
|
self
|
30
30
|
end
|
31
31
|
|
@@ -35,7 +35,7 @@ module Scmd
|
|
35
35
|
|
36
36
|
def run!(input = nil)
|
37
37
|
@run_bang_calls.push(InputCall.new(input))
|
38
|
-
Scmd.calls.push(Scmd::Call.new(self, input)) if ENV[
|
38
|
+
Scmd.calls.push(Scmd::Call.new(self, input)) if ENV["SCMD_TEST_MODE"]
|
39
39
|
self
|
40
40
|
end
|
41
41
|
|
@@ -45,7 +45,7 @@ module Scmd
|
|
45
45
|
|
46
46
|
def start(input = nil)
|
47
47
|
@start_calls.push(InputCall.new(input))
|
48
|
-
Scmd.calls.push(Scmd::Call.new(self, input)) if ENV[
|
48
|
+
Scmd.calls.push(Scmd::Call.new(self, input)) if ENV["SCMD_TEST_MODE"]
|
49
49
|
@running = true
|
50
50
|
end
|
51
51
|
|
@@ -92,21 +92,21 @@ module Scmd
|
|
92
92
|
@cmd_str.to_s
|
93
93
|
end
|
94
94
|
|
95
|
-
def ==(
|
96
|
-
if
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
95
|
+
def ==(other)
|
96
|
+
if other.is_a?(CommandSpy)
|
97
|
+
cmd_str == other.cmd_str &&
|
98
|
+
env == other.env &&
|
99
|
+
options == other.options &&
|
100
|
+
run_calls == other.run_calls &&
|
101
|
+
run_bang_calls == other.run_bang_calls &&
|
102
|
+
start_calls == other.start_calls &&
|
103
|
+
wait_calls == other.wait_calls &&
|
104
|
+
stop_calls == other.stop_calls &&
|
105
|
+
kill_calls == other.kill_calls &&
|
106
|
+
pid == other.pid &&
|
107
|
+
exitstatus == other.exitstatus &&
|
108
|
+
stdout == other.stdout &&
|
109
|
+
stderr == other.stderr
|
110
110
|
else
|
111
111
|
super
|
112
112
|
end
|
@@ -115,7 +115,5 @@ module Scmd
|
|
115
115
|
InputCall = Struct.new(:input)
|
116
116
|
TimeoutCall = Struct.new(:timeout)
|
117
117
|
SignalCall = Struct.new(:signal)
|
118
|
-
|
119
118
|
end
|
120
|
-
|
121
119
|
end
|
data/lib/scmd/stored_commands.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "scmd/command_spy"
|
4
4
|
|
5
|
+
module Scmd
|
5
6
|
class StoredCommands
|
6
|
-
|
7
7
|
attr_reader :hash
|
8
8
|
|
9
9
|
def initialize
|
@@ -30,16 +30,15 @@ module Scmd
|
|
30
30
|
@hash.empty?
|
31
31
|
end
|
32
32
|
|
33
|
-
def ==(
|
34
|
-
if
|
35
|
-
|
33
|
+
def ==(other)
|
34
|
+
if other.is_a?(StoredCommands)
|
35
|
+
hash == other.hash
|
36
36
|
else
|
37
37
|
super
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
class Stub
|
42
|
-
|
43
42
|
attr_reader :cmd_str, :hash
|
44
43
|
|
45
44
|
def initialize(cmd_str)
|
@@ -62,17 +61,14 @@ module Scmd
|
|
62
61
|
CommandSpy.new(@cmd_str, opts).tap(&block)
|
63
62
|
end
|
64
63
|
|
65
|
-
def ==(
|
66
|
-
if
|
67
|
-
|
68
|
-
|
64
|
+
def ==(other)
|
65
|
+
if other.is_a?(Stub)
|
66
|
+
cmd_str == other.cmd_str &&
|
67
|
+
hash == other.hash
|
69
68
|
else
|
70
69
|
super
|
71
70
|
end
|
72
71
|
end
|
73
|
-
|
74
72
|
end
|
75
|
-
|
76
73
|
end
|
77
|
-
|
78
74
|
end
|
data/lib/scmd/version.rb
CHANGED
data/scmd.gemspec
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require "scmd/version"
|
5
7
|
|
@@ -8,18 +10,19 @@ Gem::Specification.new do |gem|
|
|
8
10
|
gem.version = Scmd::VERSION
|
9
11
|
gem.authors = ["Kelly Redding", "Collin Redding"]
|
10
12
|
gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
|
11
|
-
gem.summary =
|
12
|
-
gem.description =
|
13
|
+
gem.summary = "Build and run system commands."
|
14
|
+
gem.description = "Build and run system commands."
|
13
15
|
gem.homepage = "http://github.com/redding/scmd"
|
14
|
-
gem.license =
|
16
|
+
gem.license = "MIT"
|
17
|
+
|
18
|
+
gem.files = `git ls-files | grep "^[^.]"`.split($INPUT_RECORD_SEPARATOR)
|
15
19
|
|
16
|
-
gem.files = `git ls-files`.split($/)
|
17
20
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
21
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
22
|
gem.require_paths = ["lib"]
|
20
23
|
|
21
|
-
gem.add_development_dependency("
|
24
|
+
gem.add_development_dependency("much-style-guide", ["~> 0.6.0"])
|
25
|
+
gem.add_development_dependency("assert", ["~> 2.19.2"])
|
22
26
|
|
23
27
|
gem.add_dependency("posix-spawn", ["~> 0.3.11"])
|
24
|
-
|
25
28
|
end
|
data/script/bench.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# $ bundle exec ruby script/bench.rb
|
2
4
|
|
3
|
-
|
5
|
+
require_relative "../bench/runner"
|
4
6
|
|
5
7
|
class ScmdBenchLogger
|
6
|
-
|
7
8
|
def initialize(file_path)
|
8
|
-
@file = File.open(file_path,
|
9
|
+
@file = File.open(file_path, "w")
|
9
10
|
@ios = [@file, $stdout]
|
10
11
|
yield self
|
11
12
|
@file.close
|
@@ -17,10 +18,9 @@ class ScmdBenchLogger
|
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
|
-
def
|
21
|
+
def respond_to_missing?(*args)
|
21
22
|
@ios.first.respond_to?(args.first.to_s) ? true : super
|
22
23
|
end
|
23
|
-
|
24
24
|
end
|
25
25
|
|
26
26
|
def run_cmd(logger, *args)
|
@@ -33,7 +33,7 @@ def run_cmd(logger, *args)
|
|
33
33
|
GC.start
|
34
34
|
end
|
35
35
|
|
36
|
-
ScmdBenchLogger.new(
|
36
|
+
ScmdBenchLogger.new("bench/results.txt") do |logger|
|
37
37
|
run_cmd(logger, Scmd.new("echo hi"), 1)
|
38
38
|
run_cmd(logger, Scmd.new("echo hi"), 10)
|
39
39
|
run_cmd(logger, Scmd.new("echo hi"), 100)
|
@@ -44,4 +44,3 @@ ScmdBenchLogger.new('bench/results.txt') do |logger|
|
|
44
44
|
run_cmd(logger, Scmd.new("cat test/support/bigger-than-64k.txt"), 100)
|
45
45
|
run_cmd(logger, Scmd.new("cat test/support/bigger-than-64k.txt"), 1000)
|
46
46
|
end
|
47
|
-
|
data/test/helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# this file is automatically required when you run `assert`
|
2
4
|
# put any test helpers here
|
3
5
|
|
@@ -6,14 +8,14 @@ ROOT_PATH = File.expand_path("../..", __FILE__)
|
|
6
8
|
$LOAD_PATH.unshift(ROOT_PATH)
|
7
9
|
|
8
10
|
# require pry for debugging (`binding.pry`)
|
9
|
-
require
|
11
|
+
require "pry"
|
10
12
|
|
11
|
-
require
|
13
|
+
require "test/support/factory"
|
12
14
|
|
13
15
|
# 1.8.7 backfills
|
14
16
|
|
15
17
|
# Array#sample
|
16
|
-
if !(a =
|
18
|
+
if !(a = []).respond_to?(:sample) && a.respond_to?(:choice)
|
17
19
|
class Array
|
18
20
|
alias_method :sample, :choice
|
19
21
|
end
|
data/test/support/factory.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "assert"
|
2
|
-
require
|
4
|
+
require "scmd/command"
|
3
5
|
|
4
6
|
class Scmd::Command
|
5
|
-
|
6
7
|
class SystemTests < Assert::Context
|
7
8
|
desc "Scmd::Command"
|
8
9
|
setup do
|
@@ -17,27 +18,28 @@ class Scmd::Command
|
|
17
18
|
assert_equal 0, @success_cmd.exitstatus
|
18
19
|
assert @success_cmd.success?
|
19
20
|
assert_equal "hi\n", @success_cmd.stdout
|
20
|
-
assert_equal
|
21
|
+
assert_equal "", @success_cmd.stderr
|
21
22
|
|
22
23
|
@failure_cmd.run
|
23
24
|
|
24
25
|
assert_not_nil @failure_cmd.pid
|
25
26
|
assert_not_equal 0, @failure_cmd.exitstatus
|
26
27
|
assert_not @failure_cmd.success?
|
27
|
-
assert_equal
|
28
|
-
assert_not_equal
|
28
|
+
assert_equal "", @failure_cmd.stdout
|
29
|
+
assert_not_equal "", @failure_cmd.stderr
|
29
30
|
end
|
30
31
|
|
31
32
|
should "raise an exception with proper backtrace on `run!`" do
|
32
|
-
err =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
err =
|
34
|
+
begin
|
35
|
+
@failure_cmd.run!
|
36
|
+
rescue => ex
|
37
|
+
ex
|
38
|
+
end
|
37
39
|
|
38
40
|
assert_kind_of Scmd::RunError, err
|
39
|
-
assert_includes
|
40
|
-
assert_includes
|
41
|
+
assert_includes "No such file or directory", err.message
|
42
|
+
assert_includes "test/system/command_tests.rb:", err.backtrace.first
|
41
43
|
end
|
42
44
|
|
43
45
|
should "return itself on `run`, `run!`" do
|
@@ -57,7 +59,6 @@ class Scmd::Command
|
|
57
59
|
cmd.wait
|
58
60
|
assert_not cmd.running?
|
59
61
|
end
|
60
|
-
|
61
62
|
end
|
62
63
|
|
63
64
|
class InputTests < SystemTests
|
@@ -65,7 +66,7 @@ class Scmd::Command
|
|
65
66
|
setup do
|
66
67
|
@cmd = Scmd::Command.new("sh")
|
67
68
|
end
|
68
|
-
subject
|
69
|
+
subject{ @cmd }
|
69
70
|
|
70
71
|
should "run the command given a single line of input" do
|
71
72
|
subject.run "echo hi"
|
@@ -81,7 +82,6 @@ class Scmd::Command
|
|
81
82
|
assert_equal "hi\n", @cmd.stdout
|
82
83
|
assert_equal "err\n", @cmd.stderr
|
83
84
|
end
|
84
|
-
|
85
85
|
end
|
86
86
|
|
87
87
|
class LongRunningTests < SystemTests
|
@@ -124,21 +124,20 @@ class Scmd::Command
|
|
124
124
|
|
125
125
|
should "be killable with a non-default signal" do
|
126
126
|
@long_cmd.start
|
127
|
-
@long_cmd.kill(
|
127
|
+
@long_cmd.kill("INT")
|
128
128
|
|
129
129
|
assert_not @long_cmd.running?
|
130
130
|
end
|
131
|
-
|
132
131
|
end
|
133
132
|
|
134
133
|
class BufferDeadlockTests < SystemTests
|
135
134
|
desc "when capturing data from an output buffer"
|
136
135
|
setup do
|
137
|
-
@small_path = File.join(ROOT_PATH,
|
136
|
+
@small_path = File.join(ROOT_PATH, "test/support/smaller-than-64k.txt")
|
138
137
|
@small_data = File.read(@small_path)
|
139
138
|
@small_cmd = Scmd::Command.new("cat #{@small_path}")
|
140
139
|
|
141
|
-
@big_path = File.join(ROOT_PATH,
|
140
|
+
@big_path = File.join(ROOT_PATH, "test/support/bigger-than-64k.txt")
|
142
141
|
@big_data = File.read(@big_path)
|
143
142
|
@big_cmd = Scmd::Command.new("cat #{@big_path}")
|
144
143
|
end
|
@@ -152,15 +151,14 @@ class Scmd::Command
|
|
152
151
|
assert_nothing_raised{ @big_cmd.wait(1) }
|
153
152
|
assert_equal @big_data, @big_cmd.stdout
|
154
153
|
end
|
155
|
-
|
156
154
|
end
|
157
155
|
|
158
156
|
class WithEnvVarTests < SystemTests
|
159
157
|
desc "with environment variables"
|
160
158
|
setup do
|
161
159
|
@cmd = Scmd::Command.new("echo $SCMD_TEST_VAR", {
|
162
|
-
:
|
163
|
-
})
|
160
|
+
env: { "SCMD_TEST_VAR" => "test" },
|
161
|
+
},)
|
164
162
|
end
|
165
163
|
|
166
164
|
should "use them when running the command" do
|
@@ -168,7 +166,6 @@ class Scmd::Command
|
|
168
166
|
assert @cmd.success?
|
169
167
|
assert_equal "test\n", @cmd.stdout
|
170
168
|
end
|
171
|
-
|
172
169
|
end
|
173
170
|
|
174
171
|
class WithOptionsTests < SystemTests
|
@@ -177,8 +174,8 @@ class Scmd::Command
|
|
177
174
|
@path = "/"
|
178
175
|
# `chdir` is the only one that reliably worked
|
179
176
|
@cmd = Scmd::Command.new("pwd", {
|
180
|
-
:
|
181
|
-
})
|
177
|
+
options: { chdir: @path },
|
178
|
+
},)
|
182
179
|
end
|
183
180
|
|
184
181
|
should "use them when running the command" do
|
@@ -188,7 +185,5 @@ class Scmd::Command
|
|
188
185
|
assert_not_equal "#{Dir.pwd}\n", @cmd.stdout
|
189
186
|
assert_equal "#{@path}\n", @cmd.stdout
|
190
187
|
end
|
191
|
-
|
192
188
|
end
|
193
|
-
|
194
189
|
end
|
@@ -1,20 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "assert"
|
2
|
-
require
|
4
|
+
require "scmd/command_spy"
|
3
5
|
|
4
6
|
class Scmd::CommandSpy
|
5
|
-
|
6
7
|
class UnitTests < Assert::Context
|
7
8
|
desc "Scmd::CommandSpy"
|
8
9
|
setup do
|
9
10
|
@spy_class = Scmd::CommandSpy
|
10
11
|
end
|
11
|
-
|
12
12
|
end
|
13
13
|
|
14
14
|
class InitTests < UnitTests
|
15
15
|
setup do
|
16
|
-
@orig_scmd_test_mode = ENV[
|
17
|
-
ENV[
|
16
|
+
@orig_scmd_test_mode = ENV["SCMD_TEST_MODE"]
|
17
|
+
ENV["SCMD_TEST_MODE"] = "1"
|
18
18
|
Scmd.reset
|
19
19
|
|
20
20
|
@cmd_str = Factory.string
|
@@ -22,7 +22,7 @@ class Scmd::CommandSpy
|
|
22
22
|
end
|
23
23
|
teardown do
|
24
24
|
Scmd.reset
|
25
|
-
ENV[
|
25
|
+
ENV["SCMD_TEST_MODE"] = @orig_scmd_test_mode
|
26
26
|
end
|
27
27
|
subject{ @spy }
|
28
28
|
|
@@ -54,17 +54,17 @@ class Scmd::CommandSpy
|
|
54
54
|
|
55
55
|
assert_equal 1, subject.pid
|
56
56
|
assert_equal 0, subject.exitstatus
|
57
|
-
assert_equal
|
58
|
-
assert_equal
|
57
|
+
assert_equal "", subject.stdout
|
58
|
+
assert_equal "", subject.stderr
|
59
59
|
end
|
60
60
|
|
61
61
|
should "allow specifying env and options" do
|
62
62
|
opts = { Factory.string => Factory.string }
|
63
63
|
cmd = Scmd::Command.new(Factory.string, {
|
64
|
-
:
|
65
|
-
:
|
66
|
-
})
|
67
|
-
exp = {
|
64
|
+
env: { SCMD_TEST_VAR: 1 },
|
65
|
+
options: opts,
|
66
|
+
},)
|
67
|
+
exp = { "SCMD_TEST_VAR" => "1" }
|
68
68
|
assert_equal exp, cmd.env
|
69
69
|
assert_equal opts, cmd.options
|
70
70
|
end
|
@@ -162,7 +162,7 @@ class Scmd::CommandSpy
|
|
162
162
|
end
|
163
163
|
|
164
164
|
should "not track global run, run! or start calls if not in test mode" do
|
165
|
-
ENV.delete(
|
165
|
+
ENV.delete("SCMD_TEST_MODE")
|
166
166
|
|
167
167
|
input = Factory.string
|
168
168
|
# if `Scmd.calls` is called when not in test mode it will raise an error,
|
@@ -174,7 +174,7 @@ class Scmd::CommandSpy
|
|
174
174
|
subject.start(input)
|
175
175
|
end
|
176
176
|
|
177
|
-
ENV[
|
177
|
+
ENV["SCMD_TEST_MODE"] = "1"
|
178
178
|
end
|
179
179
|
|
180
180
|
should "track its wait calls" do
|
@@ -232,13 +232,11 @@ class Scmd::CommandSpy
|
|
232
232
|
:pid,
|
233
233
|
:exitstatus,
|
234
234
|
:stdout,
|
235
|
-
:stderr
|
235
|
+
:stderr,
|
236
236
|
].sample
|
237
237
|
Assert.stub(spy2, a){ Factory.string }
|
238
238
|
|
239
239
|
assert_not_equal spy1, spy2
|
240
240
|
end
|
241
|
-
|
242
241
|
end
|
243
|
-
|
244
242
|
end
|