test-cmd.rb 2.0.0 → 2.2.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.
- checksums.yaml +4 -4
- data/README.md +46 -1
- data/lib/test/cmd.rb +48 -10
- data/test/cmd_test.rb +29 -0
- data/test-cmd.rb.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e57955db2ea9f64997167878e30449b920c1746751e493b54c1ff549b852d55d
|
|
4
|
+
data.tar.gz: 3e2527cb8deedc6205726ff8900aa2e23c15b69c9662debfb9322ebbc38e7ee3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 782b41654883c063415e13191a28fdaf719123db4c4980a1f69102d23ad2026a61e8ce0b2c927cde4e12b321426cdcf1ecd25fce528fca5d586fe627160c8373
|
|
7
|
+
data.tar.gz: 442fb73c9759f458c448729a2893b507ab0c8e6db2d5d8bea4d465b2b117c01794f9b501bb910460dbbc9a56f627c8147dea53a7e0814c1ae503133b4cc2774a
|
data/README.md
CHANGED
|
@@ -36,14 +36,59 @@ A command is created with
|
|
|
36
36
|
which takes the name or path of a command, and given additional
|
|
37
37
|
arguments with
|
|
38
38
|
[`Test::Command#argv`](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html#argv-instance_method).
|
|
39
|
+
Environment variables can be set for the spawned process with
|
|
40
|
+
[`Test::Command#env`](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html#env-instance_method),
|
|
41
|
+
and standard input with
|
|
42
|
+
[`Test::Command#stdin`](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html#stdin-instance_method).
|
|
39
43
|
The instance has access to the command's process ID, exit status,
|
|
40
44
|
standard output stream, and standard error stream.
|
|
41
45
|
|
|
42
46
|
```ruby
|
|
43
47
|
require "test-cmd"
|
|
44
|
-
Test::Command.new("ls").argv("-l").stdout
|
|
48
|
+
puts Test::Command.new("ls").argv("-l").stdout
|
|
49
|
+
puts Test::Command.new("env").env("FOO" => "bar").stdout
|
|
50
|
+
test_cmd = Test::Command.new("tr", "a-z", "A-Z").stdin("hello").stdout # => "HELLO"
|
|
45
51
|
```
|
|
46
52
|
|
|
53
|
+
<details>
|
|
54
|
+
<summary>Environment</summary>
|
|
55
|
+
<br>
|
|
56
|
+
|
|
57
|
+
Environment variables for the spawned process are set with
|
|
58
|
+
[`Test::Command#env`](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html#env-instance_method),
|
|
59
|
+
which merges the given variables into the child's environment and
|
|
60
|
+
returns the command for chaining.
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
require "test-cmd"
|
|
64
|
+
puts Test::Command
|
|
65
|
+
.new("ruby", "-e", "puts ENV['FOO']")
|
|
66
|
+
.env("FOO" => "42")
|
|
67
|
+
.stdout # => "42\n"
|
|
68
|
+
```
|
|
69
|
+
</details>
|
|
70
|
+
|
|
71
|
+
<details>
|
|
72
|
+
<summary>Standard input</summary>
|
|
73
|
+
<br>
|
|
74
|
+
|
|
75
|
+
Standard input for the spawned process is set with
|
|
76
|
+
[`Test::Command#stdin`](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html#stdin-instance_method).
|
|
77
|
+
Pass a String to write it to the child's standard input, or another
|
|
78
|
+
[Test::Command](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html)
|
|
79
|
+
whose standard output will be used as the standard input.
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
require "test-cmd"
|
|
83
|
+
puts Test::Command.new("cat").stdin("hello world").stdout # => "hello world"
|
|
84
|
+
|
|
85
|
+
# Pipe one command's standard output into another's standard input
|
|
86
|
+
puts Test::Command.new("tr", "a-z", "A-Z")
|
|
87
|
+
.stdin(Test::Command.new("echo", "hello"))
|
|
88
|
+
.stdout # => "HELLO\n"
|
|
89
|
+
```
|
|
90
|
+
</details>
|
|
91
|
+
|
|
47
92
|
<details>
|
|
48
93
|
<summary>Callbacks</summary>
|
|
49
94
|
<br>
|
data/lib/test/cmd.rb
CHANGED
|
@@ -26,6 +26,7 @@ class Test::Command
|
|
|
26
26
|
def initialize(cmd, *argv)
|
|
27
27
|
@cmd = cmd
|
|
28
28
|
@argv = argv.dup
|
|
29
|
+
@env = {}
|
|
29
30
|
@status = nil
|
|
30
31
|
@spawned = false
|
|
31
32
|
@stdout = ""
|
|
@@ -41,25 +42,44 @@ class Test::Command
|
|
|
41
42
|
tap { @argv.concat(argv) }
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
##
|
|
46
|
+
# @param [Hash{String => String}] env
|
|
47
|
+
# Environment variables to set for the spawned command
|
|
48
|
+
# @return [Test::Command]
|
|
49
|
+
def env(env)
|
|
50
|
+
tap { @env.merge!(env) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Presets the standard input that will be sent to the
|
|
55
|
+
# spawned process. Pass a String, or another
|
|
56
|
+
# {Test::Command Test::Command} whose standard output
|
|
57
|
+
# will be used as the standard input.
|
|
58
|
+
# @param [String, Test::Command] data
|
|
59
|
+
# The standard input of the spawned process
|
|
60
|
+
# @example
|
|
61
|
+
# cmd = Test::Command.new("cat").stdin("hello world")
|
|
62
|
+
# puts cmd.stdout
|
|
63
|
+
# @return [Test::Command]
|
|
64
|
+
def stdin(data)
|
|
65
|
+
tap { @stdin = data }
|
|
66
|
+
end
|
|
67
|
+
|
|
44
68
|
##
|
|
45
69
|
# Spawns a command
|
|
46
70
|
# @return [Test::Command]
|
|
47
71
|
def spawn
|
|
48
72
|
return self if @spawned
|
|
73
|
+
@in_r = input_pipe
|
|
49
74
|
tap do
|
|
50
75
|
@spawned = true
|
|
51
76
|
@out, @err = Pipe.pair, Pipe.pair
|
|
52
|
-
##
|
|
53
|
-
# Spawn in the calling thread so the command's fds are
|
|
54
|
-
# wired up before the reader thread starts. We then close
|
|
55
|
-
# our own copies of the write ends so the reader thread
|
|
56
|
-
# observes EOF the moment the child exits, and reads both
|
|
57
|
-
# streams with whole-buffer reads rather than a byte at
|
|
58
|
-
# a time.
|
|
59
77
|
@pid = Process.spawn(
|
|
78
|
+
@env,
|
|
60
79
|
@cmd, *@argv,
|
|
61
|
-
{out: @out.w, err: @err.w
|
|
80
|
+
{in: @in_r, out: @out.w, err: @err.w}
|
|
62
81
|
)
|
|
82
|
+
@in_r.close unless @in_r.equal?(IO::NULL)
|
|
63
83
|
@out.w.close
|
|
64
84
|
@err.w.close
|
|
65
85
|
@producer = Thread.new do
|
|
@@ -76,8 +96,9 @@ class Test::Command
|
|
|
76
96
|
@stderr = ex.message
|
|
77
97
|
@enoent = true
|
|
78
98
|
##
|
|
79
|
-
#
|
|
80
|
-
#
|
|
99
|
+
# Close the read end of any input pipe so a pending
|
|
100
|
+
# writer thread does not block forever.
|
|
101
|
+
@in_r.close unless @in_r.equal?(IO::NULL)
|
|
81
102
|
@status = Process.waitpid2(Process.spawn("false")).last
|
|
82
103
|
end
|
|
83
104
|
|
|
@@ -209,6 +230,23 @@ class Test::Command
|
|
|
209
230
|
|
|
210
231
|
private
|
|
211
232
|
|
|
233
|
+
##
|
|
234
|
+
# Returns a pipe pair used for the command's
|
|
235
|
+
# standard input. When no input is set, the child
|
|
236
|
+
# reads from IO::NULL. When the input is another
|
|
237
|
+
# command, the source command is spawned first and
|
|
238
|
+
# its stdout feeds the pipe.
|
|
239
|
+
# @return [IO]
|
|
240
|
+
# A read end (IO::NULL or a pipe read end)
|
|
241
|
+
def input_pipe
|
|
242
|
+
return IO::NULL if @stdin.nil?
|
|
243
|
+
data = self.class === @stdin ? @stdin.stdout : @stdin.to_s
|
|
244
|
+
r, w = IO.pipe
|
|
245
|
+
w.write(data)
|
|
246
|
+
w.close
|
|
247
|
+
r
|
|
248
|
+
end
|
|
249
|
+
|
|
212
250
|
##
|
|
213
251
|
# Blocks until the spawned command has finished, its
|
|
214
252
|
# output has been read, and its exit status captured.
|
data/test/cmd_test.rb
CHANGED
|
@@ -21,6 +21,35 @@ class Test::Command
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
##
|
|
25
|
+
# Test::Command#env
|
|
26
|
+
class EnvTest < Test
|
|
27
|
+
def test_ruby_env
|
|
28
|
+
assert_equal "42\n",
|
|
29
|
+
::Test::Command.new("ruby", "-e", "puts ENV['FOO']")
|
|
30
|
+
.env("FOO" => "42")
|
|
31
|
+
.stdout
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Test::Command#stdin
|
|
37
|
+
class StdinTest < Test
|
|
38
|
+
def test_ruby_stdin_string
|
|
39
|
+
assert_equal "hello world",
|
|
40
|
+
::Test::Command.new("cat")
|
|
41
|
+
.stdin("hello world")
|
|
42
|
+
.stdout
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_ruby_stdin_command
|
|
46
|
+
assert_equal "42\n",
|
|
47
|
+
::Test::Command.new("cat")
|
|
48
|
+
.stdin(ruby("puts 42"))
|
|
49
|
+
.stdout
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
24
53
|
##
|
|
25
54
|
# Test::Command#{exit_status, status, success?}
|
|
26
55
|
class ExitStatusTest < Test
|
data/test-cmd.rb.gemspec
CHANGED
|
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
|
|
|
5
5
|
gem.authors = ["robert"]
|
|
6
6
|
gem.email = ["robert@r.uby.dev"]
|
|
7
7
|
gem.homepage = "https://github.com/0x1eef/test-cmd.rb#readme"
|
|
8
|
-
gem.version = "2.
|
|
8
|
+
gem.version = "2.2.0"
|
|
9
9
|
gem.required_ruby_version = ">= 3.0"
|
|
10
10
|
gem.licenses = ["0BSD"]
|
|
11
11
|
gem.files = `git ls-files`.split($/)
|