test-cmd.rb 1.1.0 → 2.0.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 +60 -30
- data/Rakefile.rb +1 -0
- data/lib/test/cmd.rb +58 -73
- data/rubydev.svg +185 -0
- data/test/cmd_test.rb +14 -14
- data/test-cmd.rb.gemspec +2 -2
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6c0bc0c0b5e20d51b2455762362ee54301e95c6171a6427dbdf6ea85acdc834
|
|
4
|
+
data.tar.gz: 9bcbbe35149e54547b7e01a678de58a068e4a35a0728d454fa613f26d2d9c3ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec35ba65fefe5c978fe9d467d90f1c9719a984078f2bafb22f1a9e1757ca322533ee99c7f77d4268bbb051986230685058da3da88ca2674567b264d915b5f97b
|
|
7
|
+
data.tar.gz: 517746b21cb1edd44a3d96216ae3b842b88acec38c6b3fad389d3748ef8cacc241f4baf205aa1fe285a2aea92c5775e359a04d6fd89f5f9f2e205a7e13784bf8
|
data/README.md
CHANGED
|
@@ -1,35 +1,76 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://r.uby.dev">
|
|
3
|
+
<img
|
|
4
|
+
src="rubydev.svg"
|
|
5
|
+
width="400"
|
|
6
|
+
height="200"
|
|
7
|
+
border="0"
|
|
8
|
+
alt="a r.uby.dev project"
|
|
9
|
+
>
|
|
10
|
+
</a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
> A [r.uby.dev](https://r.uby.dev) project.
|
|
14
|
+
|
|
15
|
+
Welcome to the canonical test-cmd.rb repository.
|
|
16
|
+
|
|
17
|
+
test-cmd.rb is a Go-inspired, object-oriented interface for running
|
|
18
|
+
commands on UNIX-like systems, in the spirit of Go's `os/exec`. A
|
|
19
|
+
command is built up and then spawned so its standard output and
|
|
20
|
+
error streams, process ID, and exit status are captured in the
|
|
21
|
+
background. Predicates, callbacks, and process control cover the
|
|
22
|
+
common cases with a small and dependency-free API.
|
|
2
23
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
test-cmd.rb can be installed via rubygems.org:
|
|
6
27
|
|
|
7
|
-
|
|
28
|
+
gem install test-cmd.rb
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
8
31
|
|
|
9
32
|
### Commands
|
|
10
33
|
|
|
11
|
-
|
|
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
|
+
The instance has access to the command's process ID, exit status,
|
|
40
|
+
standard output stream, and standard error stream.
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require "test-cmd"
|
|
44
|
+
Test::Command.new("ls").argv("-l").stdout
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
<details>
|
|
48
|
+
<summary>Callbacks</summary>
|
|
49
|
+
<br>
|
|
12
50
|
|
|
13
51
|
The success and failure callbacks provide hooks for when
|
|
14
52
|
a command exits successfully or unsuccessfully. The callbacks
|
|
15
53
|
are passed an instance of
|
|
16
|
-
[Test::
|
|
54
|
+
[Test::Command](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html)
|
|
17
55
|
that has access to the command's process ID, exit status,
|
|
18
|
-
standard
|
|
56
|
+
standard output stream, and standard error stream.
|
|
19
57
|
|
|
20
|
-
```
|
|
58
|
+
```ruby
|
|
21
59
|
require "test-cmd"
|
|
22
|
-
|
|
60
|
+
Test::Command.new("ruby", "-e", "exit 0")
|
|
23
61
|
.success { print "The command [#{_1.pid}] was successful", "\n" }
|
|
24
62
|
.failure { print "The command [#{_1.pid}] was unsuccessful", "\n" }
|
|
25
63
|
```
|
|
64
|
+
</details>
|
|
26
65
|
|
|
27
|
-
|
|
66
|
+
<details>
|
|
67
|
+
<summary>Assertions</summary>
|
|
68
|
+
<br>
|
|
28
69
|
|
|
29
70
|
The following example demonstrates how tests might be written with
|
|
30
|
-
test-unit from the standard library.
|
|
31
|
-
[
|
|
32
|
-
|
|
71
|
+
test-unit from the standard library. A
|
|
72
|
+
[Test::Command](https://r.uby.dev/api-docs/test-cmd.rb/Test/Command.html)
|
|
73
|
+
takes the name or path of a command, alongside any arguments. The tests
|
|
33
74
|
assert against the exit status, standard output stream, and standard error
|
|
34
75
|
stream of the spawned ruby process:
|
|
35
76
|
|
|
@@ -57,29 +98,18 @@ class CmdTest < Test::Unit::TestCase
|
|
|
57
98
|
private
|
|
58
99
|
|
|
59
100
|
def ruby(code)
|
|
60
|
-
|
|
101
|
+
Test::Command.new("ruby", "-e", code)
|
|
61
102
|
end
|
|
62
103
|
end
|
|
63
104
|
```
|
|
105
|
+
</details>
|
|
64
106
|
|
|
65
107
|
## Documentation
|
|
66
108
|
|
|
67
109
|
A complete API reference is available at
|
|
68
|
-
[
|
|
69
|
-
|
|
70
|
-
## Install
|
|
71
|
-
|
|
72
|
-
test-cmd.rb can be installed via rubygems.org:
|
|
73
|
-
|
|
74
|
-
gem install test-cmd.rb
|
|
75
|
-
|
|
76
|
-
## Sources
|
|
77
|
-
|
|
78
|
-
* [github.com/@0x1eef](https://github.com/0x1eef/test-cmd.rb#readme)
|
|
79
|
-
* [gitlab.com/@0x1eef](https://gitlab.com/0x1eef/test-cmd.rb#about)
|
|
110
|
+
[r.uby.dev/api-docs/test-cmd.rb](https://r.uby.dev/api-docs/test-cmd.rb)
|
|
80
111
|
|
|
81
112
|
## License
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
See [LICENSE](./LICENSE)
|
|
114
|
+
This software is released under the terms of the BSD Zero Clause license. <br>
|
|
115
|
+
See [LICENSE](./LICENSE) for details.
|
data/Rakefile.rb
CHANGED
data/lib/test/cmd.rb
CHANGED
|
@@ -4,7 +4,7 @@ end unless defined?(Test)
|
|
|
4
4
|
##
|
|
5
5
|
# test-cmd.rb provides an object oriented interface
|
|
6
6
|
# for spawning a command
|
|
7
|
-
class Test::
|
|
7
|
+
class Test::Command
|
|
8
8
|
##
|
|
9
9
|
# @api private
|
|
10
10
|
class Pipe < Struct.new(:r, :w)
|
|
@@ -22,7 +22,7 @@ class Test::Cmd
|
|
|
22
22
|
# A command to spawn
|
|
23
23
|
# @param [Array<String>] argv
|
|
24
24
|
# Zero or more command-line arguments
|
|
25
|
-
# @return [Test::
|
|
25
|
+
# @return [Test::Command]
|
|
26
26
|
def initialize(cmd, *argv)
|
|
27
27
|
@cmd = cmd
|
|
28
28
|
@argv = argv.dup
|
|
@@ -31,28 +31,54 @@ class Test::Cmd
|
|
|
31
31
|
@stdout = ""
|
|
32
32
|
@stderr = ""
|
|
33
33
|
@enoent = false
|
|
34
|
-
@waiter = Queue.new
|
|
35
34
|
end
|
|
36
35
|
|
|
37
36
|
##
|
|
38
37
|
# @param [Array<String, #to_s>] argv
|
|
39
38
|
# Command-line arguments
|
|
40
|
-
# @return [Test::
|
|
39
|
+
# @return [Test::Command]
|
|
41
40
|
def argv(*argv)
|
|
42
41
|
tap { @argv.concat(argv) }
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
##
|
|
46
45
|
# Spawns a command
|
|
47
|
-
# @return [Test::
|
|
46
|
+
# @return [Test::Command]
|
|
48
47
|
def spawn
|
|
49
48
|
return self if @spawned
|
|
50
49
|
tap do
|
|
51
50
|
@spawned = true
|
|
52
51
|
@out, @err = Pipe.pair, Pipe.pair
|
|
53
|
-
|
|
54
|
-
|
|
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
|
+
@pid = Process.spawn(
|
|
60
|
+
@cmd, *@argv,
|
|
61
|
+
{out: @out.w, err: @err.w, in: IO::NULL}
|
|
62
|
+
)
|
|
63
|
+
@out.w.close
|
|
64
|
+
@err.w.close
|
|
65
|
+
@producer = Thread.new do
|
|
66
|
+
@stdout = @out.r.read
|
|
67
|
+
@stderr = @err.r.read
|
|
68
|
+
Process.wait
|
|
69
|
+
@status = $?
|
|
70
|
+
ensure
|
|
71
|
+
@out.r.close
|
|
72
|
+
@err.r.close
|
|
73
|
+
end
|
|
55
74
|
end
|
|
75
|
+
rescue Errno::ENOENT => ex
|
|
76
|
+
@stderr = ex.message
|
|
77
|
+
@enoent = true
|
|
78
|
+
##
|
|
79
|
+
# Capture a real non-zero status so predicates like
|
|
80
|
+
# #success? work even though the command never spawned.
|
|
81
|
+
@status = Process.waitpid2(Process.spawn("false")).last
|
|
56
82
|
end
|
|
57
83
|
|
|
58
84
|
##
|
|
@@ -60,7 +86,7 @@ class Test::Cmd
|
|
|
60
86
|
# Returns the status of a process
|
|
61
87
|
def status
|
|
62
88
|
spawn
|
|
63
|
-
consume
|
|
89
|
+
consume
|
|
64
90
|
@status
|
|
65
91
|
end
|
|
66
92
|
|
|
@@ -87,7 +113,7 @@ class Test::Cmd
|
|
|
87
113
|
# Returns the contents of stdout
|
|
88
114
|
def stdout
|
|
89
115
|
spawn
|
|
90
|
-
consume
|
|
116
|
+
consume
|
|
91
117
|
@stdout
|
|
92
118
|
end
|
|
93
119
|
|
|
@@ -96,7 +122,7 @@ class Test::Cmd
|
|
|
96
122
|
# Returns the contents of stderr
|
|
97
123
|
def stderr
|
|
98
124
|
spawn
|
|
99
|
-
consume
|
|
125
|
+
consume
|
|
100
126
|
@stderr
|
|
101
127
|
end
|
|
102
128
|
# @endgroup
|
|
@@ -139,7 +165,7 @@ class Test::Cmd
|
|
|
139
165
|
# Returns true when a command can't be found
|
|
140
166
|
def command_not_found?
|
|
141
167
|
spawn
|
|
142
|
-
consume
|
|
168
|
+
consume
|
|
143
169
|
@enoent
|
|
144
170
|
end
|
|
145
171
|
alias_method :not_found?, :command_not_found?
|
|
@@ -149,33 +175,33 @@ class Test::Cmd
|
|
|
149
175
|
# @group Callbacks
|
|
150
176
|
|
|
151
177
|
##
|
|
152
|
-
# @yieldparam [Test::
|
|
153
|
-
# Yields an instance of {Test::
|
|
178
|
+
# @yieldparam [Test::Command] cmd
|
|
179
|
+
# Yields an instance of {Test::Command Test::Command}
|
|
154
180
|
# @example
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
# @return [Test::
|
|
181
|
+
# Test::Command.new("ruby", "-e", "exit 0").success do
|
|
182
|
+
# print "ok pid #{_1.pid}", "\n"
|
|
183
|
+
# end
|
|
184
|
+
# @return [Test::Command]
|
|
159
185
|
def success
|
|
160
186
|
tap do
|
|
161
187
|
spawn
|
|
162
|
-
consume
|
|
188
|
+
consume
|
|
163
189
|
status.success? ? yield(self) : nil
|
|
164
190
|
end
|
|
165
191
|
end
|
|
166
192
|
|
|
167
193
|
##
|
|
168
|
-
# @yieldparam [Test::
|
|
169
|
-
# Yields an instance of {Test::
|
|
194
|
+
# @yieldparam [Test::Command] cmd
|
|
195
|
+
# Yields an instance of {Test::Command Test::Command}
|
|
170
196
|
# @example
|
|
171
|
-
#
|
|
172
|
-
#
|
|
173
|
-
#
|
|
174
|
-
# @return [Test::
|
|
197
|
+
# Test::Command.new("ruby", "-e", "exit 1").failure do
|
|
198
|
+
# print "fail pid #{_1.pid}", "\n"
|
|
199
|
+
# end
|
|
200
|
+
# @return [Test::Command]
|
|
175
201
|
def failure
|
|
176
202
|
tap do
|
|
177
203
|
spawn
|
|
178
|
-
consume
|
|
204
|
+
consume
|
|
179
205
|
status.success? ? nil : yield(self)
|
|
180
206
|
end
|
|
181
207
|
end
|
|
@@ -184,53 +210,12 @@ class Test::Cmd
|
|
|
184
210
|
private
|
|
185
211
|
|
|
186
212
|
##
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# A pipe for stderr
|
|
191
|
-
# @return [Thread]
|
|
192
|
-
# Returns a thread for a spawned command
|
|
193
|
-
def produce(out, err)
|
|
194
|
-
@producer = Thread.new do
|
|
195
|
-
@pid = Process.spawn(@cmd, *@argv, {out: out.w, err: err.w})
|
|
196
|
-
@waiter.push(nil)
|
|
197
|
-
Process.wait
|
|
198
|
-
@status = $?
|
|
199
|
-
rescue Errno::ENOENT => ex
|
|
200
|
-
@cmd, @argv, @stderr = "false", [], ex.message
|
|
201
|
-
@enoent = true
|
|
202
|
-
retry
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
##
|
|
207
|
-
# @param [Thread] thread
|
|
208
|
-
# A thread for a spawned command
|
|
209
|
-
# @param [Test::Cmd::Pipe] out
|
|
210
|
-
# A pipe for stdout
|
|
211
|
-
# @param [Test::Cmd::Pipe] err
|
|
212
|
-
# A pipe for stderr
|
|
213
|
+
# Blocks until the spawned command has finished, its
|
|
214
|
+
# output has been read, and its exit status captured.
|
|
215
|
+
# Safe to call more than once.
|
|
213
216
|
# @return [void]
|
|
214
|
-
def consume
|
|
215
|
-
return
|
|
216
|
-
|
|
217
|
-
loop do
|
|
218
|
-
io, _ = IO.select([out.r, err.r], nil, nil, 0.01)
|
|
219
|
-
io&.include?(out.r) ? @stdout << out.r.read(1) : nil
|
|
220
|
-
io&.include?(err.r) ? @stderr << err.r.read(1) : nil
|
|
221
|
-
break unless thread.alive? || IO.select([out.r, err.r], nil, nil, 0.01)
|
|
222
|
-
end
|
|
223
|
-
ensure
|
|
224
|
-
[out, err].each(&:close)
|
|
225
|
-
@consumed = true
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
module Kernel
|
|
230
|
-
##
|
|
231
|
-
# @param (see Test::Cmd#initialize)
|
|
232
|
-
# @return (see Test::Cmd#initialize)
|
|
233
|
-
def cmd(cmd, *argv)
|
|
234
|
-
Test::Cmd.new(cmd, *argv)
|
|
217
|
+
def consume
|
|
218
|
+
return unless @producer&.alive?
|
|
219
|
+
@producer.join
|
|
235
220
|
end
|
|
236
|
-
end
|
|
221
|
+
end
|
data/rubydev.svg
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560 220" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">r.uby.dev</title>
|
|
3
|
+
<desc id="desc">A wide r.uby.dev wordmark with three small ruby gemstones moving around the text.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="body" x1="45" y1="35" x2="211" y2="224" gradientUnits="userSpaceOnUse">
|
|
6
|
+
<stop offset="0" stop-color="#ff7468"/>
|
|
7
|
+
<stop offset="0.42" stop-color="#cc342d"/>
|
|
8
|
+
<stop offset="1" stop-color="#5b090d"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<linearGradient id="table" x1="75" y1="31" x2="166" y2="104" gradientUnits="userSpaceOnUse">
|
|
11
|
+
<stop offset="0" stop-color="#fff3ec"/>
|
|
12
|
+
<stop offset="0.26" stop-color="#ff8f84"/>
|
|
13
|
+
<stop offset="1" stop-color="#d62f2c"/>
|
|
14
|
+
</linearGradient>
|
|
15
|
+
<linearGradient id="right" x1="150" y1="59" x2="219" y2="195" gradientUnits="userSpaceOnUse">
|
|
16
|
+
<stop offset="0" stop-color="#bb2026"/>
|
|
17
|
+
<stop offset="1" stop-color="#3c0407"/>
|
|
18
|
+
</linearGradient>
|
|
19
|
+
<linearGradient id="center" x1="99" y1="104" x2="146" y2="226" gradientUnits="userSpaceOnUse">
|
|
20
|
+
<stop offset="0" stop-color="#ff6c61"/>
|
|
21
|
+
<stop offset="0.48" stop-color="#c82d2a"/>
|
|
22
|
+
<stop offset="1" stop-color="#8d1618"/>
|
|
23
|
+
</linearGradient>
|
|
24
|
+
<radialGradient id="glow" cx="38%" cy="22%" r="66%">
|
|
25
|
+
<stop offset="0" stop-color="#fff8ed" stop-opacity="0.68"/>
|
|
26
|
+
<stop offset="0.42" stop-color="#ffb0a7" stop-opacity="0.18"/>
|
|
27
|
+
<stop offset="1" stop-color="#ffb0a7" stop-opacity="0"/>
|
|
28
|
+
</radialGradient>
|
|
29
|
+
<linearGradient id="wordmark" x1="0" y1="76" x2="0" y2="138" gradientUnits="userSpaceOnUse">
|
|
30
|
+
<stop offset="0" stop-color="#ffffff"/>
|
|
31
|
+
<stop offset="0.48" stop-color="#e1e4e8"/>
|
|
32
|
+
<stop offset="1" stop-color="#aeb8c2"/>
|
|
33
|
+
</linearGradient>
|
|
34
|
+
<linearGradient id="accentLine" x1="90" y1="0" x2="470" y2="0" gradientUnits="userSpaceOnUse">
|
|
35
|
+
<stop offset="0" stop-color="#30363d"/>
|
|
36
|
+
<stop offset="0.5" stop-color="#cc342d"/>
|
|
37
|
+
<stop offset="1" stop-color="#30363d"/>
|
|
38
|
+
</linearGradient>
|
|
39
|
+
<filter id="shadow" x="-18%" y="-18%" width="136%" height="146%">
|
|
40
|
+
<feDropShadow dx="0" dy="10" stdDeviation="9" flood-color="#000" flood-opacity="0.34"/>
|
|
41
|
+
</filter>
|
|
42
|
+
<filter id="textShadow" x="-20%" y="-45%" width="140%" height="190%">
|
|
43
|
+
<feDropShadow dx="0" dy="7" stdDeviation="6" flood-color="#000" flood-opacity="0.55"/>
|
|
44
|
+
</filter>
|
|
45
|
+
<symbol id="gemstone" viewBox="0 0 256 256">
|
|
46
|
+
<path d="M33 91 78 30h100l45 61-95 134Z" fill="url(#body)" stroke="#250507" stroke-width="7" stroke-linejoin="round"/>
|
|
47
|
+
<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="#e93c35" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
49
|
+
<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="#9f1b1d" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
51
|
+
<path d="M180 135 128 225 223 91Z" fill="#62080d" stroke="#250507" stroke-width="4" stroke-linejoin="round"/>
|
|
52
|
+
<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="#d9322d" opacity="0.72"/>
|
|
54
|
+
<path d="M178 30 128 91 180 135 223 91Z" fill="#8f1519" opacity="0.72"/>
|
|
55
|
+
<path d="M128 91 76 135h104Z" fill="#ff6257" opacity="0.5"/>
|
|
56
|
+
<path d="M49 91 84 43h43L86 96Z" fill="url(#glow)"/>
|
|
57
|
+
<path d="M85 62 101 43h35L93 87Z" fill="#fff6eb" opacity="0.52"/>
|
|
58
|
+
<path d="M97 153h62" fill="none" stroke="#ffaba2" stroke-width="4" stroke-linecap="round" opacity="0.25"/>
|
|
59
|
+
</symbol>
|
|
60
|
+
</defs>
|
|
61
|
+
<style>
|
|
62
|
+
svg {
|
|
63
|
+
color-scheme: light dark;
|
|
64
|
+
--wordmark-fill: #24292f;
|
|
65
|
+
--wordmark-halo: #ffffff;
|
|
66
|
+
--wordmark-outline: #d0d7de;
|
|
67
|
+
--rule: #d0d7de;
|
|
68
|
+
--rule-strong: #8c959f;
|
|
69
|
+
--accent-shadow: #d0d7de;
|
|
70
|
+
}
|
|
71
|
+
.wordmark-halo {
|
|
72
|
+
fill: none;
|
|
73
|
+
stroke: var(--wordmark-halo);
|
|
74
|
+
stroke-width: 16;
|
|
75
|
+
stroke-linejoin: round;
|
|
76
|
+
paint-order: stroke;
|
|
77
|
+
}
|
|
78
|
+
.wordmark-outline {
|
|
79
|
+
fill: none;
|
|
80
|
+
stroke: var(--wordmark-outline);
|
|
81
|
+
stroke-width: 8;
|
|
82
|
+
stroke-linejoin: round;
|
|
83
|
+
paint-order: stroke;
|
|
84
|
+
}
|
|
85
|
+
.wordmark-fill {
|
|
86
|
+
fill: var(--wordmark-fill);
|
|
87
|
+
}
|
|
88
|
+
.tagline {
|
|
89
|
+
fill: var(--wordmark-fill);
|
|
90
|
+
opacity: 0.78;
|
|
91
|
+
}
|
|
92
|
+
.rule {
|
|
93
|
+
stroke: var(--rule);
|
|
94
|
+
}
|
|
95
|
+
.rule-strong {
|
|
96
|
+
stroke: var(--rule-strong);
|
|
97
|
+
}
|
|
98
|
+
.accent-shadow {
|
|
99
|
+
stroke: var(--accent-shadow);
|
|
100
|
+
}
|
|
101
|
+
.gem {
|
|
102
|
+
animation-duration: 8.5s;
|
|
103
|
+
animation-iteration-count: infinite;
|
|
104
|
+
animation-timing-function: ease-in-out;
|
|
105
|
+
}
|
|
106
|
+
.gem-a { animation-name: gem-a; }
|
|
107
|
+
.gem-b { animation-name: gem-b; animation-duration: 9.5s; }
|
|
108
|
+
.gem-c { animation-name: gem-c; animation-duration: 10s; }
|
|
109
|
+
.gem-front {
|
|
110
|
+
opacity: 0;
|
|
111
|
+
animation-duration: 18s;
|
|
112
|
+
animation-iteration-count: infinite;
|
|
113
|
+
animation-timing-function: ease-in-out;
|
|
114
|
+
}
|
|
115
|
+
.gem-front-a { animation-name: gem-front-a; }
|
|
116
|
+
.gem-front-b { animation-name: gem-front-b; animation-delay: -6s; }
|
|
117
|
+
.gem-front-c { animation-name: gem-front-c; animation-delay: -12s; }
|
|
118
|
+
@keyframes gem-a {
|
|
119
|
+
0%, 100% { transform: translate(42px, 82px) rotate(-10deg); }
|
|
120
|
+
35% { transform: translate(66px, 45px) rotate(5deg); }
|
|
121
|
+
70% { transform: translate(84px, 118px) rotate(-18deg); }
|
|
122
|
+
}
|
|
123
|
+
@keyframes gem-b {
|
|
124
|
+
0%, 100% { transform: translate(236px, 13px) rotate(3deg); }
|
|
125
|
+
33% { transform: translate(192px, 28px) rotate(-9deg); }
|
|
126
|
+
66% { transform: translate(292px, 24px) rotate(12deg); }
|
|
127
|
+
}
|
|
128
|
+
@keyframes gem-c {
|
|
129
|
+
0%, 100% { transform: translate(430px, 78px) rotate(8deg); }
|
|
130
|
+
38% { transform: translate(397px, 45px) rotate(-4deg); }
|
|
131
|
+
72% { transform: translate(446px, 117px) rotate(16deg); }
|
|
132
|
+
}
|
|
133
|
+
@media (prefers-reduced-motion: reduce) {
|
|
134
|
+
.gem { animation: none; }
|
|
135
|
+
.gem-front { animation: none; opacity: 0; }
|
|
136
|
+
.gem-a { transform: translate(42px, 82px) rotate(-10deg); }
|
|
137
|
+
.gem-b { transform: translate(236px, 13px) rotate(3deg); }
|
|
138
|
+
.gem-c { transform: translate(430px, 78px) rotate(8deg); }
|
|
139
|
+
}
|
|
140
|
+
@keyframes gem-front-a {
|
|
141
|
+
0%, 42%, 51%, 100% { opacity: 0; }
|
|
142
|
+
45%, 48% { opacity: 0.9; }
|
|
143
|
+
}
|
|
144
|
+
@keyframes gem-front-b {
|
|
145
|
+
0%, 57%, 66%, 100% { opacity: 0; }
|
|
146
|
+
60%, 63% { opacity: 0.86; }
|
|
147
|
+
}
|
|
148
|
+
@keyframes gem-front-c {
|
|
149
|
+
0%, 72%, 81%, 100% { opacity: 0; }
|
|
150
|
+
75%, 78% { opacity: 0.88; }
|
|
151
|
+
}
|
|
152
|
+
</style>
|
|
153
|
+
|
|
154
|
+
<path class="rule" d="M32 38h496M32 182h496" stroke-width="1" opacity="0.62"/>
|
|
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">
|
|
158
|
+
<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>
|
|
161
|
+
</g>
|
|
162
|
+
|
|
163
|
+
<g font-family="system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" text-anchor="middle">
|
|
164
|
+
<text class="wordmark-halo" x="280" y="126" fill="none" stroke="#ffffff"
|
|
165
|
+
font-size="72" font-weight="800"
|
|
166
|
+
filter="url(#textShadow)">r.uby.dev</text>
|
|
167
|
+
<text class="wordmark-outline" x="280" y="126" fill="none" stroke="#d0d7de"
|
|
168
|
+
font-size="72" font-weight="800">r.uby.dev</text>
|
|
169
|
+
<text class="wordmark-fill" x="280" y="126" fill="#24292f"
|
|
170
|
+
font-size="72" font-weight="800">r.uby.dev</text>
|
|
171
|
+
</g>
|
|
172
|
+
|
|
173
|
+
<g filter="url(#shadow)">
|
|
174
|
+
<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>
|
|
177
|
+
</g>
|
|
178
|
+
|
|
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
|
+
<text class="tagline" x="280" y="204"
|
|
183
|
+
font-family="system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
|
|
184
|
+
text-anchor="middle" font-size="18" font-weight="650">Building cool stuff with Ruby</text>
|
|
185
|
+
</svg>
|
data/test/cmd_test.rb
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
require_relative "setup"
|
|
2
2
|
|
|
3
|
-
class Test::
|
|
3
|
+
class Test::Command
|
|
4
4
|
class Test < Test::Unit::TestCase
|
|
5
5
|
private
|
|
6
6
|
|
|
7
7
|
def ruby(str)
|
|
8
|
-
|
|
8
|
+
::Test::Command.new "ruby", "-e", str
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
class Test::
|
|
13
|
+
class Test::Command
|
|
14
14
|
##
|
|
15
|
-
# Test::
|
|
15
|
+
# Test::Command#argv
|
|
16
16
|
class ARGVTest < Test
|
|
17
17
|
def test_ruby_argv
|
|
18
|
-
assert_equal "42\n",
|
|
18
|
+
assert_equal "42\n", ::Test::Command.new("ruby")
|
|
19
19
|
.argv("-e", "warn 42")
|
|
20
20
|
.stderr
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
##
|
|
25
|
-
# Test::
|
|
25
|
+
# Test::Command#{exit_status, status, success?}
|
|
26
26
|
class ExitStatusTest < Test
|
|
27
27
|
def test_ruby_exit_status_success
|
|
28
28
|
assert_equal 0, ruby("exit 0").exit_status
|
|
@@ -38,12 +38,12 @@ class Test::Cmd
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def test_nonexistent_command
|
|
41
|
-
assert_equal false,
|
|
41
|
+
assert_equal false, ::Test::Command.new("/a/path/that/is/not/found").success?
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
##
|
|
46
|
-
# Test::
|
|
46
|
+
# Test::Command#{stdout,stderr}
|
|
47
47
|
class OutputTest < Test
|
|
48
48
|
def test_ruby_stdout
|
|
49
49
|
assert_equal "42\n", ruby("puts 42").stdout
|
|
@@ -68,12 +68,12 @@ class Test::Cmd
|
|
|
68
68
|
|
|
69
69
|
def test_nonexistent_command
|
|
70
70
|
assert_equal "No such file or directory - /a/path/that/is/not/found",
|
|
71
|
-
|
|
71
|
+
::Test::Command.new("/a/path/that/is/not/found").stderr
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
##
|
|
76
|
-
# Test::
|
|
76
|
+
# Test::Command#{success, failure}
|
|
77
77
|
class CallbackTest < Test
|
|
78
78
|
def test_ruby_success_callback
|
|
79
79
|
call_ok, call_fail = [false, false]
|
|
@@ -95,7 +95,7 @@ class Test::Cmd
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
##
|
|
98
|
-
# Test::
|
|
98
|
+
# Test::Command#spawned?
|
|
99
99
|
class SpawnedTest < Test
|
|
100
100
|
def test_spawned_before_spawn
|
|
101
101
|
assert_equal false, ruby("puts 42").spawned?
|
|
@@ -107,11 +107,11 @@ class Test::Cmd
|
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
##
|
|
110
|
-
# Test::
|
|
110
|
+
# Test::Command#command_not_found?
|
|
111
111
|
class CommandNotFoundTest < Test
|
|
112
112
|
def test_command_not_found
|
|
113
|
-
assert_equal true,
|
|
114
|
-
assert_equal true,
|
|
113
|
+
assert_equal true, ::Test::Command.new("/a/path/that/is/not/found").command_not_found?
|
|
114
|
+
assert_equal true, ::Test::Command.new("/a/path/that/is/not/found").not_found?
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
end
|
data/test-cmd.rb.gemspec
CHANGED
|
@@ -4,8 +4,8 @@ Gem::Specification.new do |gem|
|
|
|
4
4
|
gem.name = "test-cmd.rb"
|
|
5
5
|
gem.authors = ["robert"]
|
|
6
6
|
gem.email = ["robert@r.uby.dev"]
|
|
7
|
-
gem.homepage = "https://github.com/
|
|
8
|
-
gem.version = "
|
|
7
|
+
gem.homepage = "https://github.com/0x1eef/test-cmd.rb#readme"
|
|
8
|
+
gem.version = "2.0.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:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- robert
|
|
@@ -96,10 +96,11 @@ files:
|
|
|
96
96
|
- Rakefile.rb
|
|
97
97
|
- lib/test-cmd.rb
|
|
98
98
|
- lib/test/cmd.rb
|
|
99
|
+
- rubydev.svg
|
|
99
100
|
- test-cmd.rb.gemspec
|
|
100
101
|
- test/cmd_test.rb
|
|
101
102
|
- test/setup.rb
|
|
102
|
-
homepage: https://github.com/
|
|
103
|
+
homepage: https://github.com/0x1eef/test-cmd.rb#readme
|
|
103
104
|
licenses:
|
|
104
105
|
- 0BSD
|
|
105
106
|
metadata: {}
|
|
@@ -117,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
117
118
|
- !ruby/object:Gem::Version
|
|
118
119
|
version: '0'
|
|
119
120
|
requirements: []
|
|
120
|
-
rubygems_version: 4.0.
|
|
121
|
+
rubygems_version: 4.0.16
|
|
121
122
|
specification_version: 4
|
|
122
123
|
summary: test-cmd.rb provides an object-oriented interface for spawning a command
|
|
123
124
|
test_files: []
|