test-cmd.rb 2.2.0 → 2.5.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/LICENSE +21 -15
- data/README.md +52 -49
- data/lib/test/command/version.rb +8 -0
- data/lib/test/{cmd.rb → command.rb} +45 -4
- data/lib/test-cmd.rb +1 -1
- data/rubydev.svg +108 -36
- data/test/cmd_test.rb +45 -0
- data/test/setup.rb +1 -1
- data/test-cmd.rb.gemspec +14 -4
- metadata +17 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0790a7e8e68709204aeb42611543db79e3575615d6ed77ee0b166fff7827825
|
|
4
|
+
data.tar.gz: 7965cd9baf53560ff8648f0652876b3d31a969528b1419b5b975406ec37c13fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0f7c63d381cbbd8c603c8fc7a1c22e7c73509b0a51965107c2fe2c23adf3c2b8cf5f243712b4f297f90aeb0535c01477ddd4518c10be6afd6a29b248c30af18
|
|
7
|
+
data.tar.gz: aee5d21c5948e17b29f0df43636bb18d396d6919efdc3a0fe6c03628ba85d5f3ae0e04cd03e61e51c33ad30aa88a2e7a840051cc704d3ac384957ea75d6f1cd1
|
data/LICENSE
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
granted
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
OF
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robert Gleeson <robert@r.uby.dev>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -10,16 +10,19 @@
|
|
|
10
10
|
</a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
>
|
|
13
|
+
> [r.uby.dev](https://r.uby.dev) project.
|
|
14
14
|
|
|
15
15
|
Welcome to the canonical test-cmd.rb repository.
|
|
16
16
|
|
|
17
17
|
test-cmd.rb is a Go-inspired, object-oriented interface for running
|
|
18
18
|
commands on UNIX-like systems, in the spirit of Go's `os/exec`. A
|
|
19
|
-
command
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
command can be built by chaining multiple method calls that impact
|
|
20
|
+
different command attributes. The environment the command executes
|
|
21
|
+
with can be set, its standard input stream can be written to, and
|
|
22
|
+
the standard output and standard error streams are captured so the
|
|
23
|
+
parent process can read them.
|
|
24
|
+
|
|
25
|
+
It has zero runtime dependencies (stdlib-only).
|
|
23
26
|
|
|
24
27
|
## Install
|
|
25
28
|
|
|
@@ -31,37 +34,51 @@ test-cmd.rb can be installed via rubygems.org:
|
|
|
31
34
|
|
|
32
35
|
### Commands
|
|
33
36
|
|
|
34
|
-
A command is created with
|
|
35
|
-
[`Test::Command#initialize`](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html#initialize-instance_method)
|
|
36
|
-
which takes the name or path of a command, and given additional
|
|
37
|
-
arguments with
|
|
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).
|
|
43
|
-
The instance has access to the command's process ID, exit status,
|
|
44
|
-
standard output stream, and standard error stream.
|
|
45
37
|
|
|
46
38
|
```ruby
|
|
47
39
|
require "test-cmd"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Call the 'ls' command
|
|
43
|
+
p Test::Command
|
|
44
|
+
.new("ls")
|
|
45
|
+
.argv("-l")
|
|
46
|
+
.stdout
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Call the env command with the given environment
|
|
50
|
+
# variables set.
|
|
51
|
+
p Test::Command
|
|
52
|
+
.new("env")
|
|
53
|
+
.env("FOO" => "bar")
|
|
54
|
+
.stdout
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Write to the stdin stream of the command that
|
|
58
|
+
# is executed.
|
|
59
|
+
p Test::Command
|
|
60
|
+
.new("tr", "a-z", "A-Z")
|
|
61
|
+
.stdin("hello")
|
|
62
|
+
.stdout # => "HELLO"
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Limit how much data is read from stdout and
|
|
66
|
+
# stderr. At most 20k chars is read from both:
|
|
67
|
+
p Test::Command
|
|
68
|
+
.new("git")
|
|
69
|
+
.limit(stdout: 20_000, stderr: 20_000)
|
|
70
|
+
.argv("diff", "--cached")
|
|
71
|
+
.stdout
|
|
51
72
|
```
|
|
52
73
|
|
|
53
74
|
<details>
|
|
54
75
|
<summary>Environment</summary>
|
|
55
76
|
<br>
|
|
56
77
|
|
|
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
78
|
```ruby
|
|
63
79
|
require "test-cmd"
|
|
64
|
-
|
|
80
|
+
|
|
81
|
+
p Test::Command
|
|
65
82
|
.new("ruby", "-e", "puts ENV['FOO']")
|
|
66
83
|
.env("FOO" => "42")
|
|
67
84
|
.stdout # => "42\n"
|
|
@@ -72,18 +89,16 @@ puts Test::Command
|
|
|
72
89
|
<summary>Standard input</summary>
|
|
73
90
|
<br>
|
|
74
91
|
|
|
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
92
|
```ruby
|
|
82
93
|
require "test-cmd"
|
|
83
|
-
puts Test::Command.new("cat").stdin("hello world").stdout # => "hello world"
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
|
|
95
|
+
p Test::Command
|
|
96
|
+
.new("cat")
|
|
97
|
+
.stdin("hello world")
|
|
98
|
+
.stdout # => "hello world"
|
|
99
|
+
|
|
100
|
+
p Test::Command
|
|
101
|
+
.new("tr", "a-z", "A-Z")
|
|
87
102
|
.stdin(Test::Command.new("echo", "hello"))
|
|
88
103
|
.stdout # => "HELLO\n"
|
|
89
104
|
```
|
|
@@ -93,13 +108,6 @@ puts Test::Command.new("tr", "a-z", "A-Z")
|
|
|
93
108
|
<summary>Callbacks</summary>
|
|
94
109
|
<br>
|
|
95
110
|
|
|
96
|
-
The success and failure callbacks provide hooks for when
|
|
97
|
-
a command exits successfully or unsuccessfully. The callbacks
|
|
98
|
-
are passed an instance of
|
|
99
|
-
[Test::Command](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html)
|
|
100
|
-
that has access to the command's process ID, exit status,
|
|
101
|
-
standard output stream, and standard error stream.
|
|
102
|
-
|
|
103
111
|
```ruby
|
|
104
112
|
require "test-cmd"
|
|
105
113
|
Test::Command.new("ruby", "-e", "exit 0")
|
|
@@ -121,7 +129,7 @@ stream of the spawned ruby process:
|
|
|
121
129
|
|
|
122
130
|
```ruby
|
|
123
131
|
require "test/unit"
|
|
124
|
-
require "test
|
|
132
|
+
require "test-cmd"
|
|
125
133
|
|
|
126
134
|
class CmdTest < Test::Unit::TestCase
|
|
127
135
|
def test_ruby_stdout
|
|
@@ -149,12 +157,7 @@ end
|
|
|
149
157
|
```
|
|
150
158
|
</details>
|
|
151
159
|
|
|
152
|
-
## Documentation
|
|
153
|
-
|
|
154
|
-
A complete API reference is available at
|
|
155
|
-
[r.uby.dev/api-docs/test-cmd.rb](https://r.uby.dev/api-docs/test-cmd.rb)
|
|
156
|
-
|
|
157
160
|
## License
|
|
158
161
|
|
|
159
|
-
This software is released under the
|
|
160
|
-
See [LICENSE](
|
|
162
|
+
This software is released under the MIT license. <br>
|
|
163
|
+
See [LICENSE](LICENSE) for details.
|
|
@@ -5,6 +5,8 @@ end unless defined?(Test)
|
|
|
5
5
|
# test-cmd.rb provides an object oriented interface
|
|
6
6
|
# for spawning a command
|
|
7
7
|
class Test::Command
|
|
8
|
+
require_relative "command/version"
|
|
9
|
+
|
|
8
10
|
##
|
|
9
11
|
# @api private
|
|
10
12
|
class Pipe < Struct.new(:r, :w)
|
|
@@ -32,6 +34,7 @@ class Test::Command
|
|
|
32
34
|
@stdout = ""
|
|
33
35
|
@stderr = ""
|
|
34
36
|
@enoent = false
|
|
37
|
+
@limit = {stdout: nil, stderr: nil}
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
##
|
|
@@ -65,6 +68,19 @@ class Test::Command
|
|
|
65
68
|
tap { @stdin = data }
|
|
66
69
|
end
|
|
67
70
|
|
|
71
|
+
##
|
|
72
|
+
# Limits how many bytes of each stream are captured.
|
|
73
|
+
# A stream output beyond the limit is discarded, keeping
|
|
74
|
+
# memory bounded even for a command that streams a lot.
|
|
75
|
+
# @param [Integer, nil] stdout
|
|
76
|
+
# Max bytes of stdout to read, or nil for unlimited
|
|
77
|
+
# @param [Integer, nil] stderr
|
|
78
|
+
# Max bytes of stderr to read, or nil for unlimited
|
|
79
|
+
# @return [Test::Command]
|
|
80
|
+
def limit(stdout: nil, stderr: nil)
|
|
81
|
+
tap { @limit = {stdout:, stderr:} }
|
|
82
|
+
end
|
|
83
|
+
|
|
68
84
|
##
|
|
69
85
|
# Spawns a command
|
|
70
86
|
# @return [Test::Command]
|
|
@@ -76,15 +92,15 @@ class Test::Command
|
|
|
76
92
|
@out, @err = Pipe.pair, Pipe.pair
|
|
77
93
|
@pid = Process.spawn(
|
|
78
94
|
@env,
|
|
79
|
-
@cmd, *@argv,
|
|
95
|
+
@cmd, *@argv.map(&:to_s),
|
|
80
96
|
{in: @in_r, out: @out.w, err: @err.w}
|
|
81
97
|
)
|
|
82
98
|
@in_r.close unless @in_r.equal?(IO::NULL)
|
|
83
99
|
@out.w.close
|
|
84
100
|
@err.w.close
|
|
85
101
|
@producer = Thread.new do
|
|
86
|
-
@stdout = @out.r
|
|
87
|
-
@stderr = @err.r
|
|
102
|
+
@stdout = read_limited(@out.r, @limit[:stdout])
|
|
103
|
+
@stderr = read_limited(@err.r, @limit[:stderr])
|
|
88
104
|
Process.wait
|
|
89
105
|
@status = $?
|
|
90
106
|
ensure
|
|
@@ -230,6 +246,31 @@ class Test::Command
|
|
|
230
246
|
|
|
231
247
|
private
|
|
232
248
|
|
|
249
|
+
##
|
|
250
|
+
# Reads up to +max+ bytes from a stream, then drains
|
|
251
|
+
# the remainder so a child writing more than the limit
|
|
252
|
+
# does not block on a full pipe. When +max+ is nil the
|
|
253
|
+
# entire stream is read.
|
|
254
|
+
# @param [IO] io
|
|
255
|
+
# @param [Integer, nil] max
|
|
256
|
+
# @return [String]
|
|
257
|
+
def read_limited(io, max)
|
|
258
|
+
return io.read if max.nil?
|
|
259
|
+
buf = +""
|
|
260
|
+
remaining = max
|
|
261
|
+
while remaining > 0 && (chunk = io.read(remaining))
|
|
262
|
+
buf << chunk
|
|
263
|
+
remaining -= chunk.bytesize
|
|
264
|
+
end
|
|
265
|
+
##
|
|
266
|
+
# Drain whatever is left so the child can exit:
|
|
267
|
+
# the drained bytes are discarded, keeping memory
|
|
268
|
+
# bounded.
|
|
269
|
+
while (chunk = io.read(16_384))
|
|
270
|
+
end
|
|
271
|
+
buf
|
|
272
|
+
end
|
|
273
|
+
|
|
233
274
|
##
|
|
234
275
|
# Returns a pipe pair used for the command's
|
|
235
276
|
# standard input. When no input is set, the child
|
|
@@ -256,4 +297,4 @@ class Test::Command
|
|
|
256
297
|
return unless @producer&.alive?
|
|
257
298
|
@producer.join
|
|
258
299
|
end
|
|
259
|
-
end
|
|
300
|
+
end
|
data/lib/test-cmd.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
require_relative "test/
|
|
1
|
+
require_relative "test/command"
|
data/rubydev.svg
CHANGED
|
@@ -1,30 +1,78 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560 220" role="img" aria-labelledby="title desc">
|
|
2
2
|
<title id="title">r.uby.dev</title>
|
|
3
|
-
<desc id="desc">A wide r.uby.dev wordmark with
|
|
3
|
+
<desc id="desc">A wide r.uby.dev wordmark with ruby, sapphire, and emerald gemstones moving around the text.</desc>
|
|
4
4
|
<defs>
|
|
5
5
|
<linearGradient id="body" x1="45" y1="35" x2="211" y2="224" gradientUnits="userSpaceOnUse">
|
|
6
|
-
<stop offset="0" stop-color="#
|
|
7
|
-
<stop offset="0.42" stop-color="#
|
|
6
|
+
<stop offset="0" stop-color="#e0796f"/>
|
|
7
|
+
<stop offset="0.42" stop-color="#a7312b"/>
|
|
8
8
|
<stop offset="1" stop-color="#5b090d"/>
|
|
9
9
|
</linearGradient>
|
|
10
10
|
<linearGradient id="table" x1="75" y1="31" x2="166" y2="104" gradientUnits="userSpaceOnUse">
|
|
11
11
|
<stop offset="0" stop-color="#fff3ec"/>
|
|
12
|
-
<stop offset="0.26" stop-color="#
|
|
13
|
-
<stop offset="1" stop-color="#
|
|
12
|
+
<stop offset="0.26" stop-color="#d9867d"/>
|
|
13
|
+
<stop offset="1" stop-color="#a32d2a"/>
|
|
14
14
|
</linearGradient>
|
|
15
15
|
<linearGradient id="right" x1="150" y1="59" x2="219" y2="195" gradientUnits="userSpaceOnUse">
|
|
16
|
-
<stop offset="0" stop-color="#
|
|
16
|
+
<stop offset="0" stop-color="#911b20"/>
|
|
17
17
|
<stop offset="1" stop-color="#3c0407"/>
|
|
18
18
|
</linearGradient>
|
|
19
19
|
<linearGradient id="center" x1="99" y1="104" x2="146" y2="226" gradientUnits="userSpaceOnUse">
|
|
20
|
-
<stop offset="0" stop-color="#
|
|
21
|
-
<stop offset="0.48" stop-color="#
|
|
22
|
-
<stop offset="1" stop-color="#
|
|
20
|
+
<stop offset="0" stop-color="#c8554f"/>
|
|
21
|
+
<stop offset="0.48" stop-color="#992522"/>
|
|
22
|
+
<stop offset="1" stop-color="#6d1315"/>
|
|
23
23
|
</linearGradient>
|
|
24
24
|
<radialGradient id="glow" cx="38%" cy="22%" r="66%">
|
|
25
25
|
<stop offset="0" stop-color="#fff8ed" stop-opacity="0.68"/>
|
|
26
|
-
<stop offset="0.42" stop-color="#
|
|
27
|
-
<stop offset="1" stop-color="#
|
|
26
|
+
<stop offset="0.42" stop-color="#d89a92" stop-opacity="0.18"/>
|
|
27
|
+
<stop offset="1" stop-color="#d89a92" stop-opacity="0"/>
|
|
28
|
+
</radialGradient>
|
|
29
|
+
<linearGradient id="body-blue" x1="45" y1="35" x2="211" y2="224" gradientUnits="userSpaceOnUse">
|
|
30
|
+
<stop offset="0" stop-color="#7aa5d9"/>
|
|
31
|
+
<stop offset="0.42" stop-color="#35669e"/>
|
|
32
|
+
<stop offset="1" stop-color="#162f4a"/>
|
|
33
|
+
</linearGradient>
|
|
34
|
+
<linearGradient id="table-blue" x1="75" y1="31" x2="166" y2="104" gradientUnits="userSpaceOnUse">
|
|
35
|
+
<stop offset="0" stop-color="#edf4ff"/>
|
|
36
|
+
<stop offset="0.26" stop-color="#7d9ed9"/>
|
|
37
|
+
<stop offset="1" stop-color="#2a5ca3"/>
|
|
38
|
+
</linearGradient>
|
|
39
|
+
<linearGradient id="right-blue" x1="150" y1="59" x2="219" y2="195" gradientUnits="userSpaceOnUse">
|
|
40
|
+
<stop offset="0" stop-color="#1f4a91"/>
|
|
41
|
+
<stop offset="1" stop-color="#071b3c"/>
|
|
42
|
+
</linearGradient>
|
|
43
|
+
<linearGradient id="center-blue" x1="99" y1="104" x2="146" y2="226" gradientUnits="userSpaceOnUse">
|
|
44
|
+
<stop offset="0" stop-color="#4f78c8"/>
|
|
45
|
+
<stop offset="0.48" stop-color="#254a99"/>
|
|
46
|
+
<stop offset="1" stop-color="#132a6d"/>
|
|
47
|
+
</linearGradient>
|
|
48
|
+
<radialGradient id="glow-blue" cx="38%" cy="22%" r="66%">
|
|
49
|
+
<stop offset="0" stop-color="#edf6ff" stop-opacity="0.68"/>
|
|
50
|
+
<stop offset="0.42" stop-color="#92aed9" stop-opacity="0.18"/>
|
|
51
|
+
<stop offset="1" stop-color="#92aed9" stop-opacity="0"/>
|
|
52
|
+
</radialGradient>
|
|
53
|
+
<linearGradient id="body-green" x1="45" y1="35" x2="211" y2="224" gradientUnits="userSpaceOnUse">
|
|
54
|
+
<stop offset="0" stop-color="#79d9a0"/>
|
|
55
|
+
<stop offset="0.42" stop-color="#359e66"/>
|
|
56
|
+
<stop offset="1" stop-color="#164a2f"/>
|
|
57
|
+
</linearGradient>
|
|
58
|
+
<linearGradient id="table-green" x1="75" y1="31" x2="166" y2="104" gradientUnits="userSpaceOnUse">
|
|
59
|
+
<stop offset="0" stop-color="#edfff4"/>
|
|
60
|
+
<stop offset="0.26" stop-color="#7dd9a5"/>
|
|
61
|
+
<stop offset="1" stop-color="#2aa360"/>
|
|
62
|
+
</linearGradient>
|
|
63
|
+
<linearGradient id="right-green" x1="150" y1="59" x2="219" y2="195" gradientUnits="userSpaceOnUse">
|
|
64
|
+
<stop offset="0" stop-color="#1b9147"/>
|
|
65
|
+
<stop offset="1" stop-color="#043c1c"/>
|
|
66
|
+
</linearGradient>
|
|
67
|
+
<linearGradient id="center-green" x1="99" y1="104" x2="146" y2="226" gradientUnits="userSpaceOnUse">
|
|
68
|
+
<stop offset="0" stop-color="#4fc87d"/>
|
|
69
|
+
<stop offset="0.48" stop-color="#259950"/>
|
|
70
|
+
<stop offset="1" stop-color="#136d30"/>
|
|
71
|
+
</linearGradient>
|
|
72
|
+
<radialGradient id="glow-green" cx="38%" cy="22%" r="66%">
|
|
73
|
+
<stop offset="0" stop-color="#edfff4" stop-opacity="0.68"/>
|
|
74
|
+
<stop offset="0.42" stop-color="#92d9b0" stop-opacity="0.18"/>
|
|
75
|
+
<stop offset="1" stop-color="#92d9b0" stop-opacity="0"/>
|
|
28
76
|
</radialGradient>
|
|
29
77
|
<linearGradient id="wordmark" x1="0" y1="76" x2="0" y2="138" gradientUnits="userSpaceOnUse">
|
|
30
78
|
<stop offset="0" stop-color="#ffffff"/>
|
|
@@ -33,7 +81,7 @@
|
|
|
33
81
|
</linearGradient>
|
|
34
82
|
<linearGradient id="accentLine" x1="90" y1="0" x2="470" y2="0" gradientUnits="userSpaceOnUse">
|
|
35
83
|
<stop offset="0" stop-color="#30363d"/>
|
|
36
|
-
<stop offset="0.5" stop-color="#
|
|
84
|
+
<stop offset="0.5" stop-color="#a7312b"/>
|
|
37
85
|
<stop offset="1" stop-color="#30363d"/>
|
|
38
86
|
</linearGradient>
|
|
39
87
|
<filter id="shadow" x="-18%" y="-18%" width="136%" height="146%">
|
|
@@ -45,17 +93,47 @@
|
|
|
45
93
|
<symbol id="gemstone" viewBox="0 0 256 256">
|
|
46
94
|
<path d="M33 91 78 30h100l45 61-95 134Z" fill="url(#body)" stroke="#250507" stroke-width="7" stroke-linejoin="round"/>
|
|
47
95
|
<path d="M78 30h100l-50 61Z" fill="url(#table)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
48
|
-
<path d="M33 91h95l-52 44Z" fill="#
|
|
96
|
+
<path d="M33 91h95l-52 44Z" fill="#b03a33" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
49
97
|
<path d="M223 91h-95l52 44Z" fill="url(#right)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
50
|
-
<path d="M76 135 128 225 33 91Z" fill="#
|
|
98
|
+
<path d="M76 135 128 225 33 91Z" fill="#7a1a1b" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
51
99
|
<path d="M180 135 128 225 223 91Z" fill="#62080d" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
52
100
|
<path d="M76 135h104l-52 90Z" fill="url(#center)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
53
|
-
<path d="M78 30 128 91 76 135 33 91Z" fill="#
|
|
54
|
-
<path d="M178 30 128 91 180 135 223 91Z" fill="#
|
|
55
|
-
<path d="M128 91 76 135h104Z" fill="#
|
|
101
|
+
<path d="M78 30 128 91 76 135 33 91Z" fill="#a32d29" opacity="0.72"/>
|
|
102
|
+
<path d="M178 30 128 91 180 135 223 91Z" fill="#6f1416" opacity="0.72"/>
|
|
103
|
+
<path d="M128 91 76 135h104Z" fill="#c44f47" opacity="0.5"/>
|
|
56
104
|
<path d="M49 91 84 43h43L86 96Z" fill="url(#glow)"/>
|
|
57
105
|
<path d="M85 62 101 43h35L93 87Z" fill="#fff6eb" opacity="0.52"/>
|
|
58
|
-
<path d="M97 153h62" fill="none" stroke="#
|
|
106
|
+
<path d="M97 153h62" fill="none" stroke="#cf8d85" stroke-width="4" stroke-linecap="round" opacity="0.25"/>
|
|
107
|
+
</symbol>
|
|
108
|
+
<symbol id="gemstone-blue" viewBox="0 0 256 256">
|
|
109
|
+
<path d="M33 91 78 30h100l45 61-95 134Z" fill="url(#body-blue)" stroke="#250507" stroke-width="7" stroke-linejoin="round"/>
|
|
110
|
+
<path d="M78 30h100l-50 61Z" fill="url(#table-blue)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
111
|
+
<path d="M33 91h95l-52 44Z" fill="#336fb0" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
112
|
+
<path d="M223 91h-95l52 44Z" fill="url(#right-blue)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
113
|
+
<path d="M76 135 128 225 33 91Z" fill="#1a3a7a" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
114
|
+
<path d="M180 135 128 225 223 91Z" fill="#081a62" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
115
|
+
<path d="M76 135h104l-52 90Z" fill="url(#center-blue)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
116
|
+
<path d="M78 30 128 91 76 135 33 91Z" fill="#2d5ba3" opacity="0.72"/>
|
|
117
|
+
<path d="M178 30 128 91 180 135 223 91Z" fill="#142d6f" opacity="0.72"/>
|
|
118
|
+
<path d="M128 91 76 135h104Z" fill="#4774c4" opacity="0.5"/>
|
|
119
|
+
<path d="M49 91 84 43h43L86 96Z" fill="url(#glow-blue)"/>
|
|
120
|
+
<path d="M85 62 101 43h35L93 87Z" fill="#edf4ff" opacity="0.52"/>
|
|
121
|
+
<path d="M97 153h62" fill="none" stroke="#85a5cf" stroke-width="4" stroke-linecap="round" opacity="0.25"/>
|
|
122
|
+
</symbol>
|
|
123
|
+
<symbol id="gemstone-green" viewBox="0 0 256 256">
|
|
124
|
+
<path d="M33 91 78 30h100l45 61-95 134Z" fill="url(#body-green)" stroke="#250507" stroke-width="7" stroke-linejoin="round"/>
|
|
125
|
+
<path d="M78 30h100l-50 61Z" fill="url(#table-green)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
126
|
+
<path d="M33 91h95l-52 44Z" fill="#33b070" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
127
|
+
<path d="M223 91h-95l52 44Z" fill="url(#right-green)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
128
|
+
<path d="M76 135 128 225 33 91Z" fill="#1a7a4b" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
129
|
+
<path d="M180 135 128 225 223 91Z" fill="#086220" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
130
|
+
<path d="M76 135h104l-52 90Z" fill="url(#center-green)" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
131
|
+
<path d="M78 30 128 91 76 135 33 91Z" fill="#2da36a" opacity="0.72"/>
|
|
132
|
+
<path d="M178 30 128 91 180 135 223 91Z" fill="#146f3d" opacity="0.72"/>
|
|
133
|
+
<path d="M128 91 76 135h104Z" fill="#47c478" opacity="0.5"/>
|
|
134
|
+
<path d="M49 91 84 43h43L86 96Z" fill="url(#glow-green)"/>
|
|
135
|
+
<path d="M85 62 101 43h35L93 87Z" fill="#edfff4" opacity="0.52"/>
|
|
136
|
+
<path d="M97 153h62" fill="none" stroke="#85cf9f" stroke-width="4" stroke-linecap="round" opacity="0.25"/>
|
|
59
137
|
</symbol>
|
|
60
138
|
</defs>
|
|
61
139
|
<style>
|
|
@@ -99,22 +177,22 @@
|
|
|
99
177
|
stroke: var(--accent-shadow);
|
|
100
178
|
}
|
|
101
179
|
.gem {
|
|
102
|
-
animation-duration:
|
|
180
|
+
animation-duration: 6.5s;
|
|
103
181
|
animation-iteration-count: infinite;
|
|
104
182
|
animation-timing-function: ease-in-out;
|
|
105
183
|
}
|
|
106
184
|
.gem-a { animation-name: gem-a; }
|
|
107
|
-
.gem-b { animation-name: gem-b; animation-duration:
|
|
108
|
-
.gem-c { animation-name: gem-c; animation-duration:
|
|
185
|
+
.gem-b { animation-name: gem-b; animation-duration: 7.5s; }
|
|
186
|
+
.gem-c { animation-name: gem-c; animation-duration: 8s; }
|
|
109
187
|
.gem-front {
|
|
110
188
|
opacity: 0;
|
|
111
|
-
animation-duration:
|
|
189
|
+
animation-duration: 12s;
|
|
112
190
|
animation-iteration-count: infinite;
|
|
113
191
|
animation-timing-function: ease-in-out;
|
|
114
192
|
}
|
|
115
193
|
.gem-front-a { animation-name: gem-front-a; }
|
|
116
|
-
.gem-front-b { animation-name: gem-front-b; animation-delay: -
|
|
117
|
-
.gem-front-c { animation-name: gem-front-c; animation-delay: -
|
|
194
|
+
.gem-front-b { animation-name: gem-front-b; animation-delay: -4s; }
|
|
195
|
+
.gem-front-c { animation-name: gem-front-c; animation-delay: -8s; }
|
|
118
196
|
@keyframes gem-a {
|
|
119
197
|
0%, 100% { transform: translate(42px, 82px) rotate(-10deg); }
|
|
120
198
|
35% { transform: translate(66px, 45px) rotate(5deg); }
|
|
@@ -151,13 +229,10 @@
|
|
|
151
229
|
}
|
|
152
230
|
</style>
|
|
153
231
|
|
|
154
|
-
<
|
|
155
|
-
<path class="rule-strong" d="M90 32h380" stroke-width="2" stroke-linecap="round" opacity="0.72"/>
|
|
156
|
-
|
|
157
|
-
<g filter="url(#shadow)" opacity="0.92">
|
|
232
|
+
<g opacity="0.92">
|
|
158
233
|
<g class="gem gem-a"><use href="#gemstone" width="78" height="78"/></g>
|
|
159
|
-
<g class="gem gem-b"><use href="#gemstone" width="84" height="84"/></g>
|
|
160
|
-
<g class="gem gem-c"><use href="#gemstone" width="78" height="78"/></g>
|
|
234
|
+
<g class="gem gem-b"><use href="#gemstone-blue" width="84" height="84"/></g>
|
|
235
|
+
<g class="gem gem-c"><use href="#gemstone-green" width="78" height="78"/></g>
|
|
161
236
|
</g>
|
|
162
237
|
|
|
163
238
|
<g font-family="system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" text-anchor="middle">
|
|
@@ -170,15 +245,12 @@
|
|
|
170
245
|
font-size="72" font-weight="800">r.uby.dev</text>
|
|
171
246
|
</g>
|
|
172
247
|
|
|
173
|
-
<g
|
|
248
|
+
<g>
|
|
174
249
|
<g class="gem gem-a"><use class="gem-front gem-front-a" href="#gemstone" width="78" height="78"/></g>
|
|
175
|
-
<g class="gem gem-b"><use class="gem-front gem-front-b" href="#gemstone" width="84" height="84"/></g>
|
|
176
|
-
<g class="gem gem-c"><use class="gem-front gem-front-c" href="#gemstone" width="78" height="78"/></g>
|
|
250
|
+
<g class="gem gem-b"><use class="gem-front gem-front-b" href="#gemstone-blue" width="84" height="84"/></g>
|
|
251
|
+
<g class="gem gem-c"><use class="gem-front gem-front-c" href="#gemstone-green" width="78" height="78"/></g>
|
|
177
252
|
</g>
|
|
178
253
|
|
|
179
|
-
<path class="accent-shadow" d="M90 176h380" stroke-width="8" stroke-linecap="round"/>
|
|
180
|
-
<path d="M90 176h380" stroke="url(#accentLine)" stroke-width="4" stroke-linecap="round"/>
|
|
181
|
-
<circle cx="280" cy="176" r="5" fill="#cc342d"/>
|
|
182
254
|
<text class="tagline" x="280" y="204"
|
|
183
255
|
font-family="system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
|
|
184
256
|
text-anchor="middle" font-size="18" font-weight="650">Building cool stuff with Ruby</text>
|
data/test/cmd_test.rb
CHANGED
|
@@ -19,6 +19,14 @@ class Test::Command
|
|
|
19
19
|
.argv("-e", "warn 42")
|
|
20
20
|
.stderr
|
|
21
21
|
end
|
|
22
|
+
|
|
23
|
+
def test_ruby_argv_to_s
|
|
24
|
+
arg = Object.new
|
|
25
|
+
def arg.to_s = "-e"
|
|
26
|
+
assert_equal "42\n", ::Test::Command.new("ruby")
|
|
27
|
+
.argv(arg, "warn 42")
|
|
28
|
+
.stderr
|
|
29
|
+
end
|
|
22
30
|
end
|
|
23
31
|
|
|
24
32
|
##
|
|
@@ -50,6 +58,43 @@ class Test::Command
|
|
|
50
58
|
end
|
|
51
59
|
end
|
|
52
60
|
|
|
61
|
+
##
|
|
62
|
+
# Test::Command#limit
|
|
63
|
+
class LimitTest < Test
|
|
64
|
+
def test_ruby_limit_stdout
|
|
65
|
+
assert_equal "happy",
|
|
66
|
+
::Test::Command.new("printf", "%s", "happy days")
|
|
67
|
+
.limit(stdout: 5)
|
|
68
|
+
.stdout
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_ruby_limit_stderr
|
|
72
|
+
assert_equal "smile",
|
|
73
|
+
::Test::Command.new("sh", "-c", "echo smile >&2")
|
|
74
|
+
.limit(stderr: 5)
|
|
75
|
+
.stderr
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_ruby_limit_nil_is_unlimited
|
|
79
|
+
expect = "a" * 20_000
|
|
80
|
+
assert_equal expect,
|
|
81
|
+
::Test::Command.new("ruby", "-e", "print 'a' * 20_000")
|
|
82
|
+
.stdout
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_ruby_limit_drains_remaining_output
|
|
86
|
+
cmd = ::Test::Command.new("sh", "-c", "printf 'happy days'")
|
|
87
|
+
.limit(stdout: 5)
|
|
88
|
+
.spawn
|
|
89
|
+
assert_equal true, cmd.spawned?
|
|
90
|
+
##
|
|
91
|
+
# A full pipe would block forever: the
|
|
92
|
+
# drain lets the child finish and capture
|
|
93
|
+
# its status
|
|
94
|
+
assert_equal 0, cmd.exit_status
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
53
98
|
##
|
|
54
99
|
# Test::Command#{exit_status, status, success?}
|
|
55
100
|
class ExitStatusTest < Test
|
data/test/setup.rb
CHANGED
data/test-cmd.rb.gemspec
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require File.join(__dir__, "lib", "test", "command", "version.rb")
|
|
4
|
+
|
|
3
5
|
Gem::Specification.new do |gem|
|
|
4
6
|
gem.name = "test-cmd.rb"
|
|
5
|
-
gem.authors = ["
|
|
7
|
+
gem.authors = ["Robert Gleeson"]
|
|
6
8
|
gem.email = ["robert@r.uby.dev"]
|
|
7
9
|
gem.homepage = "https://github.com/0x1eef/test-cmd.rb#readme"
|
|
8
|
-
gem.version =
|
|
10
|
+
gem.version = Test::Command::VERSION
|
|
9
11
|
gem.required_ruby_version = ">= 3.0"
|
|
10
|
-
gem.licenses = ["
|
|
12
|
+
gem.licenses = ["MIT"]
|
|
11
13
|
gem.files = `git ls-files`.split($/)
|
|
12
14
|
gem.require_paths = ["lib"]
|
|
13
|
-
gem.summary =
|
|
15
|
+
gem.summary = <<~SUMMARY
|
|
16
|
+
test-cmd.rb is a Go-inspired, object-oriented interface for running
|
|
17
|
+
commands on UNIX-like systems, in the spirit of Go's `os/exec`. A
|
|
18
|
+
command can be built by chaining multiple method calls that impact
|
|
19
|
+
different command attributes. The environment the command executes
|
|
20
|
+
with can be set, its standard input stream can be written to, and
|
|
21
|
+
the standard output and standard error streams are captured so the
|
|
22
|
+
parent process can read them.
|
|
23
|
+
SUMMARY
|
|
14
24
|
gem.description = gem.summary
|
|
15
25
|
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
|
16
26
|
gem.add_development_dependency "yard", "~> 0.9"
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: test-cmd.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Robert Gleeson
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -79,7 +79,12 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '13.1'
|
|
82
|
-
description: test-cmd.rb
|
|
82
|
+
description: test-cmd.rb is a Go-inspired, object-oriented interface for running commands
|
|
83
|
+
on UNIX-like systems, in the spirit of Go's `os/exec`. A command can be built by
|
|
84
|
+
chaining multiple method calls that impact different command attributes. The environment
|
|
85
|
+
the command executes with can be set, its standard input stream can be written to,
|
|
86
|
+
and the standard output and standard error streams are captured so the parent process
|
|
87
|
+
can read them.
|
|
83
88
|
email:
|
|
84
89
|
- robert@r.uby.dev
|
|
85
90
|
executables: []
|
|
@@ -95,14 +100,15 @@ files:
|
|
|
95
100
|
- README.md
|
|
96
101
|
- Rakefile.rb
|
|
97
102
|
- lib/test-cmd.rb
|
|
98
|
-
- lib/test/
|
|
103
|
+
- lib/test/command.rb
|
|
104
|
+
- lib/test/command/version.rb
|
|
99
105
|
- rubydev.svg
|
|
100
106
|
- test-cmd.rb.gemspec
|
|
101
107
|
- test/cmd_test.rb
|
|
102
108
|
- test/setup.rb
|
|
103
109
|
homepage: https://github.com/0x1eef/test-cmd.rb#readme
|
|
104
110
|
licenses:
|
|
105
|
-
-
|
|
111
|
+
- MIT
|
|
106
112
|
metadata: {}
|
|
107
113
|
rdoc_options: []
|
|
108
114
|
require_paths:
|
|
@@ -120,5 +126,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
120
126
|
requirements: []
|
|
121
127
|
rubygems_version: 4.0.16
|
|
122
128
|
specification_version: 4
|
|
123
|
-
summary: test-cmd.rb
|
|
129
|
+
summary: test-cmd.rb is a Go-inspired, object-oriented interface for running commands
|
|
130
|
+
on UNIX-like systems, in the spirit of Go's `os/exec`. A command can be built by
|
|
131
|
+
chaining multiple method calls that impact different command attributes. The environment
|
|
132
|
+
the command executes with can be set, its standard input stream can be written to,
|
|
133
|
+
and the standard output and standard error streams are captured so the parent process
|
|
134
|
+
can read them.
|
|
124
135
|
test_files: []
|