test-cmd.rb 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92d0cf20a1d63472f4ac178bcb7b74b05d2e5875fe05bccb56f9decbcccd691a
4
- data.tar.gz: '0318fb0281b874accf7f44c6f4a3c2320d3aa4a3405829a36430347f8d65a9c4'
3
+ metadata.gz: e57955db2ea9f64997167878e30449b920c1746751e493b54c1ff549b852d55d
4
+ data.tar.gz: 3e2527cb8deedc6205726ff8900aa2e23c15b69c9662debfb9322ebbc38e7ee3
5
5
  SHA512:
6
- metadata.gz: 4daa335326b92120051a40666c07d8b731f447b45e01a73bc5e1cbb2eac65d53e4e4bb87d3ded462b6375b86e85bbe15419d1b8e64642c5f1a60659427456401
7
- data.tar.gz: '0859457b884abaafc8ea53ecc2fd238c4c111b2f6175f177d85ab48b1378de7bc81556160a06433ba900e59d0513b7e910e060944f99fb7d8db6b57182e5e15f'
6
+ metadata.gz: 782b41654883c063415e13191a28fdaf719123db4c4980a1f69102d23ad2026a61e8ce0b2c927cde4e12b321426cdcf1ecd25fce528fca5d586fe627160c8373
7
+ data.tar.gz: 442fb73c9759f458c448729a2893b507ab0c8e6db2d5d8bea4d465b2b117c01794f9b501bb910460dbbc9a56f627c8147dea53a7e0814c1ae503133b4cc2774a
data/README.md CHANGED
@@ -37,7 +37,9 @@ 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
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).
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).
41
43
  The instance has access to the command's process ID, exit status,
42
44
  standard output stream, and standard error stream.
43
45
 
@@ -45,6 +47,7 @@ standard output stream, and standard error stream.
45
47
  require "test-cmd"
46
48
  puts Test::Command.new("ls").argv("-l").stdout
47
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"
48
51
  ```
49
52
 
50
53
  <details>
@@ -65,6 +68,27 @@ puts Test::Command
65
68
  ```
66
69
  </details>
67
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
+
68
92
  <details>
69
93
  <summary>Callbacks</summary>
70
94
  <br>
data/lib/test/cmd.rb CHANGED
@@ -50,26 +50,36 @@ class Test::Command
50
50
  tap { @env.merge!(env) }
51
51
  end
52
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
+
53
68
  ##
54
69
  # Spawns a command
55
70
  # @return [Test::Command]
56
71
  def spawn
57
72
  return self if @spawned
73
+ @in_r = input_pipe
58
74
  tap do
59
75
  @spawned = true
60
76
  @out, @err = Pipe.pair, Pipe.pair
61
- ##
62
- # Spawn in the calling thread so the command's fds are
63
- # wired up before the reader thread starts. We then close
64
- # our own copies of the write ends so the reader thread
65
- # observes EOF the moment the child exits, and reads both
66
- # streams with whole-buffer reads rather than a byte at
67
- # a time.
68
77
  @pid = Process.spawn(
69
78
  @env,
70
79
  @cmd, *@argv,
71
- {out: @out.w, err: @err.w, in: IO::NULL}
80
+ {in: @in_r, out: @out.w, err: @err.w}
72
81
  )
82
+ @in_r.close unless @in_r.equal?(IO::NULL)
73
83
  @out.w.close
74
84
  @err.w.close
75
85
  @producer = Thread.new do
@@ -86,8 +96,9 @@ class Test::Command
86
96
  @stderr = ex.message
87
97
  @enoent = true
88
98
  ##
89
- # Capture a real non-zero status so predicates like
90
- # #success? work even though the command never spawned.
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)
91
102
  @status = Process.waitpid2(Process.spawn("false")).last
92
103
  end
93
104
 
@@ -219,6 +230,23 @@ class Test::Command
219
230
 
220
231
  private
221
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
+
222
250
  ##
223
251
  # Blocks until the spawned command has finished, its
224
252
  # output has been read, and its exit status captured.
data/test/cmd_test.rb CHANGED
@@ -32,6 +32,24 @@ class Test::Command
32
32
  end
33
33
  end
34
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
+
35
53
  ##
36
54
  # Test::Command#{exit_status, status, success?}
37
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.1.0"
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($/)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-cmd.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - robert