rubysh 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +88 -0
- data/Rakefile +10 -0
- data/lib/rubysh/base_command.rb +65 -0
- data/lib/rubysh/base_directive.rb +24 -0
- data/lib/rubysh/command.rb +102 -0
- data/lib/rubysh/error.rb +20 -0
- data/lib/rubysh/fd.rb +43 -0
- data/lib/rubysh/pipe.rb +4 -0
- data/lib/rubysh/pipeline.rb +70 -0
- data/lib/rubysh/redirect.rb +181 -0
- data/lib/rubysh/runner.rb +156 -0
- data/lib/rubysh/subprocess/parallel_io.rb +184 -0
- data/lib/rubysh/subprocess/pipe_wrapper.rb +61 -0
- data/lib/rubysh/subprocess.rb +154 -0
- data/lib/rubysh/triple_less_than.rb +65 -0
- data/lib/rubysh/util.rb +55 -0
- data/lib/rubysh/version.rb +3 -0
- data/lib/rubysh.rb +149 -0
- data/rubysh.gemspec +26 -0
- data/test/_lib.rb +25 -0
- data/test/functional/_lib.rb +7 -0
- data/test/functional/lib/fd-lister +2 -0
- data/test/functional/lib/leaked_fds.rb +83 -0
- data/test/functional/lib/redirect_ordering.rb +15 -0
- data/test/functional/lib/triple_less_than.rb +16 -0
- data/test/integration/_lib.rb +7 -0
- data/test/integration/lib/rubysh.rb +6 -0
- data/test/rubysh +47 -0
- data/test/unit/_lib.rb +7 -0
- data/test/unit/lib/rubysh/command.rb +20 -0
- data/test/unit/lib/rubysh/pipeline.rb +108 -0
- data/test/unit/lib/rubysh/redirect.rb +44 -0
- data/test/unit/lib/rubysh/runner.rb +16 -0
- data/test/unit/lib/rubysh/subprocess/parallel_io.rb +233 -0
- data/test/unit/lib/rubysh/subprocess.rb +37 -0
- data/test/unit/lib/rubysh.rb +74 -0
- metadata +149 -0
@@ -0,0 +1,233 @@
|
|
1
|
+
require File.expand_path('../../../_lib', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module RubyshTest::Unit
|
4
|
+
class ParallelIOTest < UnitTest
|
5
|
+
describe 'when given no readers / writers' do
|
6
|
+
it 'returns immediately from run' do
|
7
|
+
io = Rubysh::Subprocess::ParallelIO.new({}, {})
|
8
|
+
io.run
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'when given only readers' do
|
13
|
+
it 'makes callbacks as data is read' do
|
14
|
+
reader, writer = IO.pipe
|
15
|
+
writer.write('hi')
|
16
|
+
|
17
|
+
count = 0
|
18
|
+
|
19
|
+
io = Rubysh::Subprocess::ParallelIO.new({reader => :reader}, {})
|
20
|
+
io.on_read do |reader_name, data|
|
21
|
+
count += 1
|
22
|
+
|
23
|
+
case count
|
24
|
+
when 1
|
25
|
+
assert_equal('hi', data)
|
26
|
+
writer.write('bye')
|
27
|
+
when 2
|
28
|
+
assert_equal('bye', data)
|
29
|
+
writer.write('done')
|
30
|
+
when 3
|
31
|
+
assert_equal('done', data)
|
32
|
+
writer.close
|
33
|
+
when 4
|
34
|
+
assert_equal(Rubysh::Subprocess::ParallelIO::EOF, data)
|
35
|
+
else
|
36
|
+
assert(false)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
io.run
|
40
|
+
assert_equal(4, count)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'uses the reader name when data is read' do
|
44
|
+
reader, writer = IO.pipe
|
45
|
+
writer.write('hi')
|
46
|
+
|
47
|
+
io = Rubysh::Subprocess::ParallelIO.new({reader => :reader}, {})
|
48
|
+
io.on_read do |reader_name, data|
|
49
|
+
assert_equal(:reader, reader_name)
|
50
|
+
writer.close unless writer.closed?
|
51
|
+
end
|
52
|
+
io.run
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'when given only writers' do
|
57
|
+
it 'makes callbacks as data is written' do
|
58
|
+
reader, writer = IO.pipe
|
59
|
+
|
60
|
+
count = 0
|
61
|
+
|
62
|
+
io = Rubysh::Subprocess::ParallelIO.new({}, {writer => :writer})
|
63
|
+
io.on_write do |writer_name, data, remaining|
|
64
|
+
count += 1
|
65
|
+
|
66
|
+
case count
|
67
|
+
when 1
|
68
|
+
assert_equal('hi', data)
|
69
|
+
assert_equal('', remaining)
|
70
|
+
io.write(:writer, 'bye', false)
|
71
|
+
when 2
|
72
|
+
assert_equal('bye', data)
|
73
|
+
assert_equal('', remaining)
|
74
|
+
io.write(:writer, 'done', false)
|
75
|
+
when 3
|
76
|
+
assert_equal('done', data)
|
77
|
+
assert_equal('', remaining)
|
78
|
+
io.write(:writer, '', true)
|
79
|
+
when 4
|
80
|
+
assert_equal(Rubysh::Subprocess::ParallelIO::EOF, data)
|
81
|
+
assert_equal('', remaining)
|
82
|
+
else
|
83
|
+
assert(false)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
io.write(:writer, 'hi', false)
|
87
|
+
io.run
|
88
|
+
|
89
|
+
assert_equal(4, count)
|
90
|
+
received = reader.read
|
91
|
+
assert_equal('hibyedone', received)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'uses the writer name when data is written' do
|
95
|
+
reader, writer = IO.pipe
|
96
|
+
|
97
|
+
io = Rubysh::Subprocess::ParallelIO.new({}, {writer => :writer})
|
98
|
+
io.on_write do |writer_name, data|
|
99
|
+
assert_equal(:writer, writer_name)
|
100
|
+
end
|
101
|
+
io.run
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'splits up data if it cannot all be written at once' do
|
105
|
+
reader, writer = IO.pipe
|
106
|
+
|
107
|
+
io = Rubysh::Subprocess::ParallelIO.new({}, {writer => :writer})
|
108
|
+
io.on_write do |writer_name, data, remaining|
|
109
|
+
unless data == Rubysh::Subprocess::ParallelIO::EOF
|
110
|
+
assert(remaining.length > 0)
|
111
|
+
assert(data.length > 0)
|
112
|
+
writer.close
|
113
|
+
end
|
114
|
+
end
|
115
|
+
io.write(:writer, '*' * 100000, false)
|
116
|
+
io.run
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'actually writes the correct data if it cannot all be written at once' do
|
120
|
+
reader, writer = IO.pipe
|
121
|
+
input = '*' * 100000
|
122
|
+
received = ''
|
123
|
+
|
124
|
+
io = Rubysh::Subprocess::ParallelIO.new({}, {writer => :writer})
|
125
|
+
io.on_write do |writer_name, data, remaining|
|
126
|
+
unless data == Rubysh::Subprocess::ParallelIO::EOF
|
127
|
+
received << reader.read_nonblock(100000)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
io.write(:writer, input, true)
|
131
|
+
io.run
|
132
|
+
|
133
|
+
assert_equal(input, received)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'when given both writers and readers' do
|
138
|
+
it 'correctly reads and writes data' do
|
139
|
+
reader1, writer1 = IO.pipe
|
140
|
+
reader2, writer2 = IO.pipe
|
141
|
+
|
142
|
+
count1 = 0
|
143
|
+
count2 = 0
|
144
|
+
|
145
|
+
io = Rubysh::Subprocess::ParallelIO.new(
|
146
|
+
{reader1 => :reader1, reader2 => :reader2},
|
147
|
+
{writer1 => :writer1, writer2 => :writer2})
|
148
|
+
|
149
|
+
io.on_read do |reader_name, data|
|
150
|
+
if reader_name == :reader1
|
151
|
+
count1 += 1
|
152
|
+
case count1
|
153
|
+
when 2
|
154
|
+
assert_equal('hi1', data)
|
155
|
+
io.write(:writer1, 'test1', false)
|
156
|
+
when 4
|
157
|
+
assert_equal('test1', data)
|
158
|
+
io.write(:writer1, 'final1', true)
|
159
|
+
when 7
|
160
|
+
assert_equal('final1', data)
|
161
|
+
when 8
|
162
|
+
assert_equal(Rubysh::Subprocess::ParallelIO::EOF, data)
|
163
|
+
else
|
164
|
+
raise "Unexpected count1: #{count1}"
|
165
|
+
end
|
166
|
+
elsif reader_name == :reader2
|
167
|
+
count2 += 1
|
168
|
+
case count2
|
169
|
+
when 2
|
170
|
+
assert_equal('hi2', data)
|
171
|
+
io.write(:writer2, 'test2', false)
|
172
|
+
when 4
|
173
|
+
assert_equal('test2', data)
|
174
|
+
io.write(:writer2, 'final2', true)
|
175
|
+
when 7
|
176
|
+
assert_equal('final2', data)
|
177
|
+
when 8
|
178
|
+
assert_equal(Rubysh::Subprocess::ParallelIO::EOF, data)
|
179
|
+
else
|
180
|
+
raise "Unexpected count2: #{count2}"
|
181
|
+
end
|
182
|
+
else
|
183
|
+
raise "Unrecognized reader"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
io.on_write do |writer_name, data, remaining|
|
188
|
+
if writer_name == :writer1
|
189
|
+
count1 += 1
|
190
|
+
|
191
|
+
case count1
|
192
|
+
when 1
|
193
|
+
assert_equal('hi1', data)
|
194
|
+
when 3
|
195
|
+
assert_equal('test1', data)
|
196
|
+
when 5
|
197
|
+
assert_equal('final1', data)
|
198
|
+
when 6
|
199
|
+
assert_equal(Rubysh::Subprocess::ParallelIO::EOF, data)
|
200
|
+
else
|
201
|
+
raise "Unexpected count1: #{count1}"
|
202
|
+
end
|
203
|
+
elsif writer_name == :writer2
|
204
|
+
count2 += 1
|
205
|
+
|
206
|
+
case count2
|
207
|
+
when 1
|
208
|
+
assert_equal('hi2', data)
|
209
|
+
when 3
|
210
|
+
assert_equal('test2', data)
|
211
|
+
when 5
|
212
|
+
assert_equal('final2', data)
|
213
|
+
when 6
|
214
|
+
assert_equal(Rubysh::Subprocess::ParallelIO::EOF, data)
|
215
|
+
else
|
216
|
+
raise "Unexpected count2: #{count2}"
|
217
|
+
end
|
218
|
+
else
|
219
|
+
raise "Unrecognized writer"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
io.write(:writer1, 'hi1', false)
|
224
|
+
io.write(:writer2, 'hi2', false)
|
225
|
+
|
226
|
+
io.run
|
227
|
+
|
228
|
+
assert_equal(8, count1)
|
229
|
+
assert_equal(8, count2)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path('../../_lib', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module RubyshTest::Unit
|
4
|
+
class SubprocessTest < UnitTest
|
5
|
+
describe 'when running a command' do
|
6
|
+
it 'calls exec with the expected arguments' do
|
7
|
+
read_fd, write_fd = stub_pipe
|
8
|
+
Kernel.expects(:exec).with(['cmd', 'cmd'], 'arg1', 'arg2')
|
9
|
+
|
10
|
+
proc = Rubysh::Subprocess.new(['cmd', 'arg1', 'arg2'])
|
11
|
+
proc.send(:open_exec_status)
|
12
|
+
assert_raises(Rubysh::Error::UnreachableError) do
|
13
|
+
proc.send(:do_run_child)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'with a Redirect' do
|
18
|
+
it 'calls exec with the expected arguments' do
|
19
|
+
runner = mock
|
20
|
+
|
21
|
+
read_fd, write_fd = stub_pipe
|
22
|
+
Kernel.expects(:exec).with(['cmd', 'cmd'], 'arg1', 'arg2')
|
23
|
+
|
24
|
+
redirect = Rubysh::Redirect.new(2, '>', 1)
|
25
|
+
redirect.expects(:apply!).with(runner)
|
26
|
+
|
27
|
+
proc = Rubysh::Subprocess.new(['cmd', 'arg1', 'arg2'],
|
28
|
+
[redirect], [], runner)
|
29
|
+
proc.send(:open_exec_status)
|
30
|
+
assert_raises(Rubysh::Error::UnreachableError) do
|
31
|
+
proc.send(:do_run_child)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path('../_lib', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module RubyshTest::Unit
|
4
|
+
class RubyshTest < UnitTest
|
5
|
+
describe 'when running a command' do
|
6
|
+
# Rubysh('ls', '/tmp')
|
7
|
+
describe 'that is straightline' do
|
8
|
+
it 'prints nicely' do
|
9
|
+
command = Rubysh('ls', '/tmp')
|
10
|
+
expected = 'Command: ls /tmp'
|
11
|
+
assert_equal(expected, command.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'creates a command with the expected arguments' do
|
15
|
+
command = Rubysh('ls', '/tmp')
|
16
|
+
runner = Rubysh::Runner.new(command)
|
17
|
+
command.send(:prepare_subprocess, runner)
|
18
|
+
subprocess = runner.state(command)[:subprocess]
|
19
|
+
assert_equal('ls', subprocess.command)
|
20
|
+
assert_equal(['/tmp'], subprocess.args)
|
21
|
+
assert_equal([], subprocess.directives)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Rubysh('ls', '/tmp') | Rubysh('grep', 'myfile')
|
26
|
+
describe 'with a pipe' do
|
27
|
+
it 'prints nicely' do
|
28
|
+
command = Rubysh('ls', '/tmp') | Rubysh('grep', 'myfile')
|
29
|
+
expected = 'Command: ls /tmp | grep myfile'
|
30
|
+
assert_equal(expected, command.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'creates a command with the expected arguments' do
|
34
|
+
read_fd, write_fd = stub_pipe
|
35
|
+
command = Rubysh('ls', '/tmp') | Rubysh('grep', 'myfile')
|
36
|
+
|
37
|
+
pipeline = command.pipeline
|
38
|
+
assert_equal(2, pipeline.length)
|
39
|
+
|
40
|
+
runner = Rubysh::Runner.new(command)
|
41
|
+
|
42
|
+
left, right = pipeline
|
43
|
+
left.send(:prepare_subprocess, runner)
|
44
|
+
right.send(:prepare_subprocess, runner)
|
45
|
+
|
46
|
+
left_subprocess = runner.state(left)[:subprocess]
|
47
|
+
right_subprocess = runner.state(right)[:subprocess]
|
48
|
+
|
49
|
+
assert_equal([], left_subprocess.directives)
|
50
|
+
assert_equal([], right_subprocess.directives)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Rubysh('ls', '/tmp', Rubysh.stderr > Rubysh.stdout)
|
55
|
+
describe 'with a redirection to another file descriptor' do
|
56
|
+
it 'prints nicely' do
|
57
|
+
command = Rubysh('ls', '/tmp', Rubysh.stderr > Rubysh.stdout)
|
58
|
+
expected = 'Command: ls /tmp 2>&1'
|
59
|
+
assert_equal(expected, command.to_s)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'creates a command with the expected arguments' do
|
63
|
+
command = Rubysh('ls', '/tmp', Rubysh.stderr > Rubysh.stdout)
|
64
|
+
runner = Rubysh::Runner.new(command)
|
65
|
+
command.send(:prepare_subprocess, runner)
|
66
|
+
subprocess = runner.state(command)[:subprocess]
|
67
|
+
assert_equal('ls', subprocess.command)
|
68
|
+
assert_equal(['/tmp'], subprocess.args)
|
69
|
+
assert_equal([Rubysh::Redirect.new(Rubysh::FD.new(2), '>', Rubysh::FD.new(1))], subprocess.directives)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubysh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Greg Brockman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-09-12 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: minitest
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - "="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 3
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: 3.1.0
|
34
|
+
type: :development
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: mocha
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
description: "Rubysh: Ruby subprocesses made easy"
|
51
|
+
email:
|
52
|
+
- gdb@gregbrockman.com
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- .gitignore
|
61
|
+
- Gemfile
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- lib/rubysh.rb
|
66
|
+
- lib/rubysh/base_command.rb
|
67
|
+
- lib/rubysh/base_directive.rb
|
68
|
+
- lib/rubysh/command.rb
|
69
|
+
- lib/rubysh/error.rb
|
70
|
+
- lib/rubysh/fd.rb
|
71
|
+
- lib/rubysh/pipe.rb
|
72
|
+
- lib/rubysh/pipeline.rb
|
73
|
+
- lib/rubysh/redirect.rb
|
74
|
+
- lib/rubysh/runner.rb
|
75
|
+
- lib/rubysh/subprocess.rb
|
76
|
+
- lib/rubysh/subprocess/parallel_io.rb
|
77
|
+
- lib/rubysh/subprocess/pipe_wrapper.rb
|
78
|
+
- lib/rubysh/triple_less_than.rb
|
79
|
+
- lib/rubysh/util.rb
|
80
|
+
- lib/rubysh/version.rb
|
81
|
+
- rubysh.gemspec
|
82
|
+
- test/_lib.rb
|
83
|
+
- test/functional/_lib.rb
|
84
|
+
- test/functional/lib/fd-lister
|
85
|
+
- test/functional/lib/leaked_fds.rb
|
86
|
+
- test/functional/lib/redirect_ordering.rb
|
87
|
+
- test/functional/lib/triple_less_than.rb
|
88
|
+
- test/integration/_lib.rb
|
89
|
+
- test/integration/lib/rubysh.rb
|
90
|
+
- test/rubysh
|
91
|
+
- test/unit/_lib.rb
|
92
|
+
- test/unit/lib/rubysh.rb
|
93
|
+
- test/unit/lib/rubysh/command.rb
|
94
|
+
- test/unit/lib/rubysh/pipeline.rb
|
95
|
+
- test/unit/lib/rubysh/redirect.rb
|
96
|
+
- test/unit/lib/rubysh/runner.rb
|
97
|
+
- test/unit/lib/rubysh/subprocess.rb
|
98
|
+
- test/unit/lib/rubysh/subprocess/parallel_io.rb
|
99
|
+
homepage: ""
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
requirements: []
|
126
|
+
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.8.24
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: "Rubysh makes shelling out easy with a __sh__-like syntax layer for Ruby: irb -r rubysh >> command = Rubysh('echo', 'hello-from-Rubysh') | Rubysh('grep', '--color', 'Rubysh') >> command.run hello-from-Rubysh => Rubysh::Runner: echo hello-from-Rubysh | grep --color Rubysh (exitstatus: 0)"
|
132
|
+
test_files:
|
133
|
+
- test/_lib.rb
|
134
|
+
- test/functional/_lib.rb
|
135
|
+
- test/functional/lib/fd-lister
|
136
|
+
- test/functional/lib/leaked_fds.rb
|
137
|
+
- test/functional/lib/redirect_ordering.rb
|
138
|
+
- test/functional/lib/triple_less_than.rb
|
139
|
+
- test/integration/_lib.rb
|
140
|
+
- test/integration/lib/rubysh.rb
|
141
|
+
- test/rubysh
|
142
|
+
- test/unit/_lib.rb
|
143
|
+
- test/unit/lib/rubysh.rb
|
144
|
+
- test/unit/lib/rubysh/command.rb
|
145
|
+
- test/unit/lib/rubysh/pipeline.rb
|
146
|
+
- test/unit/lib/rubysh/redirect.rb
|
147
|
+
- test/unit/lib/rubysh/runner.rb
|
148
|
+
- test/unit/lib/rubysh/subprocess.rb
|
149
|
+
- test/unit/lib/rubysh/subprocess/parallel_io.rb
|