nio4r 2.7.0 → 2.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data/changes.md +12 -1
- data/ext/nio4r/org/nio4r/ByteBuffer.java +1 -1
- data/ext/nio4r/org/nio4r/Monitor.java +2 -2
- data/ext/nio4r/org/nio4r/Selector.java +2 -2
- data/lib/nio/version.rb +2 -2
- data/lib/nio4r.rb +2 -0
- data/license.md +5 -2
- data/readme.md +5 -5
- data.tar.gz.sig +0 -0
- metadata +91 -74
- metadata.gz.sig +0 -0
- data/.github/workflows/workflow.yml +0 -61
- data/.gitignore +0 -21
- data/.mailmap +0 -16
- data/.rspec +0 -4
- data/.rubocop.yml +0 -100
- data/Gemfile +0 -19
- data/Rakefile +0 -8
- data/examples/echo_server.rb +0 -54
- data/logo.png +0 -0
- data/nio4r.gemspec +0 -43
- data/rakelib/extension.rake +0 -14
- data/rakelib/rspec.rake +0 -9
- data/rakelib/rubocop.rake +0 -5
- data/spec/nio/acceptables_spec.rb +0 -36
- data/spec/nio/bytebuffer_spec.rb +0 -360
- data/spec/nio/monitor_spec.rb +0 -169
- data/spec/nio/selectables/pipe_spec.rb +0 -53
- data/spec/nio/selectables/ssl_socket_spec.rb +0 -199
- data/spec/nio/selectables/tcp_socket_spec.rb +0 -108
- data/spec/nio/selectables/udp_socket_spec.rb +0 -55
- data/spec/nio/selector_spec.rb +0 -252
- data/spec/spec_helper.rb +0 -23
- data/spec/support/selectable_examples.rb +0 -93
data/nio4r.gemspec
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path("lib/nio/version", __dir__)
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.authors = ["Tony Arcieri"]
|
7
|
-
spec.email = ["bascule@gmail.com"]
|
8
|
-
spec.homepage = "https://github.com/socketry/nio4r"
|
9
|
-
spec.license = "MIT AND (BSD-2-Clause OR GPL-2.0-or-later)"
|
10
|
-
spec.summary = "New IO for Ruby"
|
11
|
-
spec.description = <<-DESCRIPTION.strip.gsub(/\s+/, " ")
|
12
|
-
Cross-platform asynchronous I/O primitives for scalable network clients
|
13
|
-
and servers. Inspired by the Java NIO API, but simplified for ease-of-use.
|
14
|
-
DESCRIPTION
|
15
|
-
|
16
|
-
spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
17
|
-
spec.files = `git ls-files`.split("\n")
|
18
|
-
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
-
spec.name = "nio4r"
|
20
|
-
spec.require_paths = ["lib"]
|
21
|
-
spec.version = NIO::VERSION
|
22
|
-
|
23
|
-
spec.metadata = {
|
24
|
-
"bug_tracker_uri" => "https://github.com/socketry/nio4r/issues",
|
25
|
-
"changelog_uri" => "https://github.com/socketry/nio4r/blob/main/changes.md",
|
26
|
-
"documentation_uri" => "https://www.rubydoc.info/gems/nio4r/#{spec.version}",
|
27
|
-
"source_code_uri" => "https://github.com/socketry/nio4r/tree/v#{spec.version}",
|
28
|
-
"wiki_uri" => "https://github.com/socketry/nio4r/wiki",
|
29
|
-
"funding_uri" => "https://github.com/sponsors/ioquatix/",
|
30
|
-
}
|
31
|
-
|
32
|
-
spec.required_ruby_version = ">= 2.4"
|
33
|
-
|
34
|
-
if defined? JRUBY_VERSION
|
35
|
-
spec.files << "lib/nio4r_ext.jar"
|
36
|
-
spec.platform = "java"
|
37
|
-
else
|
38
|
-
spec.extensions = ["ext/nio4r/extconf.rb"]
|
39
|
-
end
|
40
|
-
|
41
|
-
spec.add_development_dependency "bundler"
|
42
|
-
spec.add_development_dependency "rake"
|
43
|
-
end
|
data/rakelib/extension.rake
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
if defined? JRUBY_VERSION
|
4
|
-
require "rake/javaextensiontask"
|
5
|
-
Rake::JavaExtensionTask.new("nio4r_ext") do |ext|
|
6
|
-
ext.ext_dir = "ext/nio4r"
|
7
|
-
ext.release = '8'
|
8
|
-
end
|
9
|
-
else
|
10
|
-
require "rake/extensiontask"
|
11
|
-
Rake::ExtensionTask.new("nio4r_ext") do |ext|
|
12
|
-
ext.ext_dir = "ext/nio4r"
|
13
|
-
end
|
14
|
-
end
|
data/rakelib/rspec.rake
DELETED
data/rakelib/rubocop.rake
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2012-2017, by Tony Arcieri.
|
5
|
-
# Copyright, 2019-2023, by Samuel Williams.
|
6
|
-
|
7
|
-
require "spec_helper"
|
8
|
-
|
9
|
-
RSpec.describe "NIO acceptables" do
|
10
|
-
shared_context "an NIO acceptable" do
|
11
|
-
let(:selector) { NIO::Selector.new }
|
12
|
-
|
13
|
-
it "selects for read readiness" do
|
14
|
-
waiting_monitor = selector.register(unacceptable_subject, :r)
|
15
|
-
ready_monitor = selector.register(acceptable_subject, :r)
|
16
|
-
|
17
|
-
ready_monitors = selector.select
|
18
|
-
expect(ready_monitors).to include ready_monitor
|
19
|
-
expect(ready_monitors).not_to include waiting_monitor
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
describe TCPServer do
|
24
|
-
let :acceptable_subject do
|
25
|
-
server = TCPServer.new("127.0.0.1", 0)
|
26
|
-
TCPSocket.open("127.0.0.1", server.local_address.ip_port)
|
27
|
-
server
|
28
|
-
end
|
29
|
-
|
30
|
-
let :unacceptable_subject do
|
31
|
-
TCPServer.new("127.0.0.1", 0)
|
32
|
-
end
|
33
|
-
|
34
|
-
it_behaves_like "an NIO acceptable"
|
35
|
-
end
|
36
|
-
end
|
data/spec/nio/bytebuffer_spec.rb
DELETED
@@ -1,360 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2016, by Upekshe Jayasekera.
|
5
|
-
# Copyright, 2016-2017, by Tony Arcieri.
|
6
|
-
# Copyright, 2019-2023, by Samuel Williams.
|
7
|
-
# Copyright, 2020, by Thomas Dziedzic.
|
8
|
-
|
9
|
-
require "spec_helper"
|
10
|
-
|
11
|
-
RSpec.describe NIO::ByteBuffer do
|
12
|
-
let(:capacity) { 256 }
|
13
|
-
let(:example_string) { "Testing 1 2 3..." }
|
14
|
-
subject(:bytebuffer) { described_class.new(capacity) }
|
15
|
-
|
16
|
-
describe "#initialize" do
|
17
|
-
it "raises TypeError if given a bogus argument" do
|
18
|
-
expect { described_class.new(:symbols_are_bogus) }.to raise_error(TypeError)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "#clear" do
|
23
|
-
it "clears the buffer" do
|
24
|
-
bytebuffer << example_string
|
25
|
-
bytebuffer.clear
|
26
|
-
|
27
|
-
expect(bytebuffer.remaining).to eq capacity
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe "#position" do
|
32
|
-
it "defaults to zero" do
|
33
|
-
expect(bytebuffer.position).to be_zero
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "#position=" do
|
38
|
-
let(:example_position) { 42 }
|
39
|
-
|
40
|
-
it "sets the buffer's position to a valid value" do
|
41
|
-
expect(bytebuffer.position).to be_zero
|
42
|
-
bytebuffer.position = example_position
|
43
|
-
expect(bytebuffer.position).to eq example_position
|
44
|
-
end
|
45
|
-
|
46
|
-
it "raises ArgumentError if the specified position is less than zero" do
|
47
|
-
expect { bytebuffer.position = -1 }.to raise_error(ArgumentError)
|
48
|
-
end
|
49
|
-
|
50
|
-
it "raises ArgumentError if the specified position exceeds the limit" do
|
51
|
-
expect { bytebuffer.position = capacity + 1 }.to raise_error(ArgumentError)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe "#limit" do
|
56
|
-
it "defaults to the buffer's capacity" do
|
57
|
-
expect(bytebuffer.limit).to eq capacity
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe "#limit=" do
|
62
|
-
it "sets the buffer's limit to a valid value" do
|
63
|
-
bytebuffer.flip
|
64
|
-
expect(bytebuffer.limit).to be_zero
|
65
|
-
|
66
|
-
new_limit = capacity / 2
|
67
|
-
bytebuffer.limit = new_limit
|
68
|
-
expect(bytebuffer.limit).to eq new_limit
|
69
|
-
end
|
70
|
-
|
71
|
-
it "preserves position and mark if they're less than the new limit" do
|
72
|
-
bytebuffer << "four"
|
73
|
-
bytebuffer.mark
|
74
|
-
bytebuffer << "more"
|
75
|
-
|
76
|
-
bytebuffer.limit = capacity / 2
|
77
|
-
expect(bytebuffer.position).to eq 8
|
78
|
-
bytebuffer.reset
|
79
|
-
expect(bytebuffer.position).to eq 4
|
80
|
-
end
|
81
|
-
|
82
|
-
it "sets position to the new limit if the previous position is beyond the limit" do
|
83
|
-
bytebuffer << "four"
|
84
|
-
bytebuffer.limit = 2
|
85
|
-
expect(bytebuffer.position).to eq 2
|
86
|
-
end
|
87
|
-
|
88
|
-
it "clears the mark if the new limit is before the current mark" do
|
89
|
-
bytebuffer << "four"
|
90
|
-
bytebuffer.mark
|
91
|
-
bytebuffer.limit = 2
|
92
|
-
expect { bytebuffer.reset }.to raise_error(NIO::ByteBuffer::MarkUnsetError)
|
93
|
-
end
|
94
|
-
|
95
|
-
it "raises ArgumentError if specified limit is less than zero" do
|
96
|
-
expect { bytebuffer.limit = -1 }.to raise_error(ArgumentError)
|
97
|
-
end
|
98
|
-
|
99
|
-
it "raises ArgumentError if specified limit exceeds capacity" do
|
100
|
-
expect { bytebuffer.limit = capacity }.not_to raise_error
|
101
|
-
expect { bytebuffer.limit = capacity + 1 }.to raise_error(ArgumentError)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
describe "#capacity" do
|
106
|
-
it "has the requested capacity" do
|
107
|
-
expect(bytebuffer.capacity).to eq capacity
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
describe "#remaining" do
|
112
|
-
it "calculates the number of bytes remaining" do
|
113
|
-
expect(bytebuffer.remaining).to eq capacity
|
114
|
-
bytebuffer << example_string
|
115
|
-
expect(bytebuffer.remaining).to eq(capacity - example_string.length)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
describe "#full?" do
|
120
|
-
it "returns false when there is space remaining in the buffer" do
|
121
|
-
expect(bytebuffer).not_to be_full
|
122
|
-
end
|
123
|
-
|
124
|
-
it "returns true when the buffer is full" do
|
125
|
-
bytebuffer << "X" * capacity
|
126
|
-
expect(bytebuffer).to be_full
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
describe "#get" do
|
131
|
-
it "reads all remaining data if no length is given" do
|
132
|
-
bytebuffer << example_string
|
133
|
-
bytebuffer.flip
|
134
|
-
|
135
|
-
expect(bytebuffer.get).to eq example_string
|
136
|
-
end
|
137
|
-
|
138
|
-
it "reads zeroes from a newly initialized buffer" do
|
139
|
-
expect(bytebuffer.get(capacity)).to eq("\0" * capacity)
|
140
|
-
end
|
141
|
-
|
142
|
-
it "advances position as data is read" do
|
143
|
-
bytebuffer << "First"
|
144
|
-
bytebuffer << "Second"
|
145
|
-
bytebuffer << "Third"
|
146
|
-
bytebuffer.flip
|
147
|
-
|
148
|
-
expect(bytebuffer.position).to be_zero
|
149
|
-
expect(bytebuffer.get(10)).to eq "FirstSecon"
|
150
|
-
expect(bytebuffer.position).to eq 10
|
151
|
-
end
|
152
|
-
|
153
|
-
it "raises NIO::ByteBuffer::UnderflowError if there is not enough data in the buffer" do
|
154
|
-
bytebuffer << example_string
|
155
|
-
bytebuffer.flip
|
156
|
-
|
157
|
-
expect { bytebuffer.get(example_string.length + 1) }.to raise_error(NIO::ByteBuffer::UnderflowError)
|
158
|
-
expect(bytebuffer.get(example_string.length)).to eq example_string
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
describe "#[]" do
|
163
|
-
it "obtains bytes at a given index without altering position" do
|
164
|
-
bytebuffer << example_string
|
165
|
-
expect(bytebuffer[7]).to eq example_string.bytes[7]
|
166
|
-
expect(bytebuffer.position).to eq example_string.length
|
167
|
-
end
|
168
|
-
|
169
|
-
it "raises ArgumentError if the index is less than zero" do
|
170
|
-
expect { bytebuffer[-1] }.to raise_error(ArgumentError)
|
171
|
-
end
|
172
|
-
|
173
|
-
it "raises ArgumentError if the index exceeds the limit" do
|
174
|
-
bytebuffer << example_string
|
175
|
-
bytebuffer.flip
|
176
|
-
expect(bytebuffer[bytebuffer.limit - 1]).to eq example_string.bytes.last
|
177
|
-
expect { bytebuffer[bytebuffer.limit] }.to raise_error(ArgumentError)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe "#<<" do
|
182
|
-
it "adds strings to the buffer" do
|
183
|
-
bytebuffer << example_string
|
184
|
-
expect(bytebuffer.position).to eq example_string.length
|
185
|
-
expect(bytebuffer.limit).to eq capacity
|
186
|
-
end
|
187
|
-
|
188
|
-
it "raises TypeError if given a non-String type" do
|
189
|
-
expect { bytebuffer << 42 }.to raise_error(TypeError)
|
190
|
-
expect { bytebuffer << nil }.to raise_error(TypeError)
|
191
|
-
end
|
192
|
-
|
193
|
-
it "raises NIO::ByteBuffer::OverflowError if the buffer is full" do
|
194
|
-
bytebuffer << "X" * (capacity - 1)
|
195
|
-
expect { bytebuffer << "X" }.not_to raise_error
|
196
|
-
expect { bytebuffer << "X" }.to raise_error(NIO::ByteBuffer::OverflowError)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
describe "#flip" do
|
201
|
-
it "flips the bytebuffer" do
|
202
|
-
bytebuffer << example_string
|
203
|
-
expect(bytebuffer.position).to eql example_string.length
|
204
|
-
|
205
|
-
expect(bytebuffer.flip).to eq bytebuffer
|
206
|
-
|
207
|
-
expect(bytebuffer.position).to be_zero
|
208
|
-
expect(bytebuffer.limit).to eq example_string.length
|
209
|
-
expect(bytebuffer.get).to eq example_string
|
210
|
-
end
|
211
|
-
|
212
|
-
it "sets remaining to the previous position" do
|
213
|
-
bytebuffer << example_string
|
214
|
-
previous_position = bytebuffer.position
|
215
|
-
expect(bytebuffer.remaining).to eq(capacity - previous_position)
|
216
|
-
expect(bytebuffer.flip.remaining).to eq previous_position
|
217
|
-
end
|
218
|
-
|
219
|
-
it "sets limit to the previous position" do
|
220
|
-
bytebuffer << example_string
|
221
|
-
expect(bytebuffer.limit).to eql(capacity)
|
222
|
-
|
223
|
-
previous_position = bytebuffer.position
|
224
|
-
expect(bytebuffer.flip.limit).to eql previous_position
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
describe "#rewind" do
|
229
|
-
it "rewinds the buffer leaving the limit intact" do
|
230
|
-
bytebuffer << example_string
|
231
|
-
expect(bytebuffer.rewind).to eq bytebuffer
|
232
|
-
|
233
|
-
expect(bytebuffer.position).to be_zero
|
234
|
-
expect(bytebuffer.limit).to eq capacity
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
describe "#mark" do
|
239
|
-
it "returns self" do
|
240
|
-
expect(bytebuffer.mark).to eql bytebuffer
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
describe "#reset" do
|
245
|
-
it "returns to a previously marked position" do
|
246
|
-
bytebuffer << "First"
|
247
|
-
expected_position = bytebuffer.position
|
248
|
-
|
249
|
-
expect(bytebuffer.mark).to eq bytebuffer
|
250
|
-
bytebuffer << "Second"
|
251
|
-
expect(bytebuffer.position).not_to eq expected_position
|
252
|
-
expect(bytebuffer.reset.position).to eq expected_position
|
253
|
-
end
|
254
|
-
|
255
|
-
it "raises NIO::ByteBuffer::MarkUnsetError unless mark has been set" do
|
256
|
-
expect { bytebuffer.reset }.to raise_error(NIO::ByteBuffer::MarkUnsetError)
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
describe "#compact" do
|
261
|
-
let(:first_string) { "CompactMe" }
|
262
|
-
let(:second_string) { "Leftover" }
|
263
|
-
|
264
|
-
it "copies data from the current position to the beginning of the buffer" do
|
265
|
-
bytebuffer << first_string << second_string
|
266
|
-
bytebuffer.position = first_string.length
|
267
|
-
bytebuffer.limit = first_string.length + second_string.length
|
268
|
-
bytebuffer.compact
|
269
|
-
|
270
|
-
expect(bytebuffer.position).to eq second_string.length
|
271
|
-
expect(bytebuffer.limit).to eq capacity
|
272
|
-
expect(bytebuffer.flip.get).to eq second_string
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
describe "#each" do
|
277
|
-
it "iterates over data in the buffer" do
|
278
|
-
bytebuffer << example_string
|
279
|
-
bytebuffer.flip
|
280
|
-
|
281
|
-
bytes = []
|
282
|
-
bytebuffer.each { |byte| bytes << byte }
|
283
|
-
expect(bytes).to eq example_string.bytes
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
describe "#inspect" do
|
288
|
-
it "inspects the buffer offsets" do
|
289
|
-
regex = /\A#<NIO::ByteBuffer:.*? @position=0 @limit=#{capacity} @capacity=#{capacity}>\z/
|
290
|
-
expect(bytebuffer.inspect).to match(regex)
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
context "I/O" do
|
295
|
-
let(:addr) { "127.0.0.1" }
|
296
|
-
let(:server) { TCPServer.new(addr, 0) }
|
297
|
-
let(:port) { server.local_address.ip_port }
|
298
|
-
let(:client) { TCPSocket.new(addr, port) }
|
299
|
-
let(:peer) { server_thread.value }
|
300
|
-
|
301
|
-
let(:server_thread) do
|
302
|
-
server
|
303
|
-
|
304
|
-
thread = Thread.new { server.accept }
|
305
|
-
Thread.pass while thread.status && thread.status != "sleep"
|
306
|
-
|
307
|
-
thread
|
308
|
-
end
|
309
|
-
|
310
|
-
before do
|
311
|
-
server_thread
|
312
|
-
client
|
313
|
-
end
|
314
|
-
|
315
|
-
after do
|
316
|
-
server_thread.kill if server_thread.alive?
|
317
|
-
|
318
|
-
server.close rescue nil
|
319
|
-
client.close rescue nil
|
320
|
-
peer.close rescue nil
|
321
|
-
end
|
322
|
-
|
323
|
-
describe "#read_from" do
|
324
|
-
it "reads data into the buffer" do
|
325
|
-
client.write(example_string)
|
326
|
-
expect(bytebuffer.read_from(peer)).to eq example_string.length
|
327
|
-
bytebuffer.flip
|
328
|
-
|
329
|
-
expect(bytebuffer.get).to eq example_string
|
330
|
-
end
|
331
|
-
|
332
|
-
it "raises NIO::ByteBuffer::OverflowError if the buffer is already full" do
|
333
|
-
client.write(example_string)
|
334
|
-
bytebuffer << "X" * capacity
|
335
|
-
expect { bytebuffer.read_from(peer) }.to raise_error(NIO::ByteBuffer::OverflowError)
|
336
|
-
end
|
337
|
-
|
338
|
-
it "returns 0 if no data is available" do
|
339
|
-
expect(bytebuffer.read_from(peer)).to eq 0
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
describe "#write_to" do
|
344
|
-
it "writes data from the buffer" do
|
345
|
-
bytebuffer << example_string
|
346
|
-
bytebuffer.flip
|
347
|
-
|
348
|
-
expect(bytebuffer.write_to(client)).to eq example_string.length
|
349
|
-
client.close
|
350
|
-
|
351
|
-
expect(peer.read(example_string.length)).to eq example_string
|
352
|
-
end
|
353
|
-
|
354
|
-
it "raises NIO::ByteBuffer::UnderflowError if the buffer is out of data" do
|
355
|
-
bytebuffer.flip
|
356
|
-
expect { bytebuffer.write_to(peer) }.to raise_error(NIO::ByteBuffer::UnderflowError)
|
357
|
-
end
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
data/spec/nio/monitor_spec.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2011-2018, by Tony Arcieri.
|
5
|
-
# Copyright, 2012, by Logan Bowers.
|
6
|
-
# Copyright, 2015, by Tiago Cardoso.
|
7
|
-
# Copyright, 2015, by Upekshe Jayasekera.
|
8
|
-
# Copyright, 2018-2023, by Samuel Williams.
|
9
|
-
|
10
|
-
require "spec_helper"
|
11
|
-
require "socket"
|
12
|
-
|
13
|
-
RSpec.describe NIO::Monitor do
|
14
|
-
let(:addr) { "127.0.0.1" }
|
15
|
-
|
16
|
-
let(:reader) { TCPServer.new(addr, 0) }
|
17
|
-
let(:port) { reader.local_address.ip_port }
|
18
|
-
let(:writer) { TCPSocket.new(addr, port) }
|
19
|
-
|
20
|
-
let(:selector) { NIO::Selector.new }
|
21
|
-
|
22
|
-
subject(:monitor) { selector.register(writer, :rw) }
|
23
|
-
subject(:peer) { selector.register(reader, :r) }
|
24
|
-
|
25
|
-
before { reader }
|
26
|
-
before { writer }
|
27
|
-
after { reader.close }
|
28
|
-
after { writer.close }
|
29
|
-
after { selector.close }
|
30
|
-
|
31
|
-
describe "#interests" do
|
32
|
-
it "knows its interests" do
|
33
|
-
expect(peer.interests).to eq(:r)
|
34
|
-
expect(monitor.interests).to eq(:rw)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "#interests=" do
|
39
|
-
it "can set interests to nil" do
|
40
|
-
expect(monitor.interests).not_to eq(nil)
|
41
|
-
monitor.interests = nil
|
42
|
-
expect(monitor.interests).to eq(nil)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "changes the interest set" do
|
46
|
-
expect(monitor.interests).not_to eq(:w)
|
47
|
-
monitor.interests = :w
|
48
|
-
expect(monitor.interests).to eq(:w)
|
49
|
-
end
|
50
|
-
|
51
|
-
it "raises EOFError if interests are changed after the monitor is closed" do
|
52
|
-
monitor.close
|
53
|
-
expect { monitor.interests = :rw }.to raise_error(EOFError)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe "#add_interest" do
|
58
|
-
it "sets a new interest if it isn't currently registered" do
|
59
|
-
monitor.interests = :r
|
60
|
-
expect(monitor.interests).to eq(:r)
|
61
|
-
|
62
|
-
expect(monitor.add_interest(:w)).to eq(:rw)
|
63
|
-
expect(monitor.interests).to eq(:rw)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "acts idempotently" do
|
67
|
-
monitor.interests = :r
|
68
|
-
expect(monitor.interests).to eq(:r)
|
69
|
-
|
70
|
-
expect(monitor.add_interest(:r)).to eq(:r)
|
71
|
-
expect(monitor.interests).to eq(:r)
|
72
|
-
end
|
73
|
-
|
74
|
-
it "raises ArgumentError if given a bogus option" do
|
75
|
-
expect { monitor.add_interest(:derp) }.to raise_error(ArgumentError)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
describe "#remove_interest" do
|
80
|
-
it "removes an interest from the set" do
|
81
|
-
expect(monitor.interests).to eq(:rw)
|
82
|
-
|
83
|
-
expect(monitor.remove_interest(:r)).to eq(:w)
|
84
|
-
expect(monitor.interests).to eq(:w)
|
85
|
-
end
|
86
|
-
|
87
|
-
it "can clear the last interest" do
|
88
|
-
monitor.interests = :w
|
89
|
-
expect(monitor.interests).to eq(:w)
|
90
|
-
|
91
|
-
expect(monitor.remove_interest(:w)).to be_nil
|
92
|
-
expect(monitor.interests).to be_nil
|
93
|
-
end
|
94
|
-
|
95
|
-
it "acts idempotently" do
|
96
|
-
monitor.interests = :w
|
97
|
-
expect(monitor.interests).to eq(:w)
|
98
|
-
|
99
|
-
expect(monitor.remove_interest(:r)).to eq(:w)
|
100
|
-
expect(monitor.interests).to eq(:w)
|
101
|
-
end
|
102
|
-
|
103
|
-
it "raises ArgumentError if given a bogus option" do
|
104
|
-
expect { monitor.add_interest(:derp) }.to raise_error(ArgumentError)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "#io" do
|
109
|
-
it "knows its IO object" do
|
110
|
-
expect(monitor.io).to eq(writer)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe "#selector" do
|
115
|
-
it "knows its selector" do
|
116
|
-
expect(monitor.selector).to eq(selector)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe "#value=" do
|
121
|
-
it "stores arbitrary values" do
|
122
|
-
monitor.value = 42
|
123
|
-
expect(monitor.value).to eq(42)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe "#readiness" do
|
128
|
-
it "knows what operations IO objects are ready for" do
|
129
|
-
# For whatever odd reason this breaks unless we eagerly evaluate monitor
|
130
|
-
reader_peer = peer
|
131
|
-
writer_peer = monitor
|
132
|
-
|
133
|
-
selected = selector.select(0)
|
134
|
-
expect(selected).to include(writer_peer)
|
135
|
-
|
136
|
-
expect(writer_peer.readiness).to eq(:w)
|
137
|
-
expect(writer_peer).not_to be_readable
|
138
|
-
expect(writer_peer).to be_writable
|
139
|
-
|
140
|
-
writer << "testing 1 2 3"
|
141
|
-
|
142
|
-
selected = selector.select(0)
|
143
|
-
expect(selected).to include(reader_peer)
|
144
|
-
|
145
|
-
expect(reader_peer.readiness).to eq(:r)
|
146
|
-
expect(reader_peer).to be_readable
|
147
|
-
expect(reader_peer).not_to be_writable
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
describe "#close" do
|
152
|
-
it "closes" do
|
153
|
-
expect(monitor).not_to be_closed
|
154
|
-
expect(selector.registered?(writer)).to be_truthy
|
155
|
-
|
156
|
-
monitor.close
|
157
|
-
expect(monitor).to be_closed
|
158
|
-
expect(selector.registered?(writer)).to be_falsey
|
159
|
-
end
|
160
|
-
|
161
|
-
it "closes even if the selector has been shutdown" do
|
162
|
-
expect(monitor).not_to be_closed
|
163
|
-
selector.close # forces shutdown
|
164
|
-
expect(monitor).not_to be_closed
|
165
|
-
monitor.close
|
166
|
-
expect(monitor).to be_closed
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2012-2017, by Tony Arcieri.
|
5
|
-
# Copyright, 2012, by Logan Bowers.
|
6
|
-
# Copyright, 2017, by Gregory Longtin.
|
7
|
-
# Copyright, 2023, by Samuel Williams.
|
8
|
-
|
9
|
-
require "spec_helper"
|
10
|
-
|
11
|
-
RSpec.describe "IO.pipe" do
|
12
|
-
let(:pair) { IO.pipe }
|
13
|
-
|
14
|
-
let :unreadable_subject do
|
15
|
-
pair.first
|
16
|
-
end
|
17
|
-
|
18
|
-
let :readable_subject do
|
19
|
-
pipe, peer = pair
|
20
|
-
peer << "data"
|
21
|
-
pipe
|
22
|
-
end
|
23
|
-
|
24
|
-
let :writable_subject do
|
25
|
-
pair.last
|
26
|
-
end
|
27
|
-
|
28
|
-
let :unwritable_subject do
|
29
|
-
_reader, pipe = pair
|
30
|
-
|
31
|
-
# HACK: On OS X 10.8, this str must be larger than PIPE_BUF. Otherwise,
|
32
|
-
# the write is atomic and select() will return writable but write()
|
33
|
-
# will throw EAGAIN if there is too little space to write the string
|
34
|
-
# TODO: Use FFI to lookup the platform-specific size of PIPE_BUF
|
35
|
-
str = "JUNK IN THE TUBES" * 10_000
|
36
|
-
cntr = 0
|
37
|
-
begin
|
38
|
-
pipe.write_nonblock str
|
39
|
-
cntr += 1
|
40
|
-
t = select [], [pipe], [], 0
|
41
|
-
rescue Errno::EPIPE
|
42
|
-
break
|
43
|
-
rescue IO::EWOULDBLOCKWaitWritable
|
44
|
-
skip "windows - can't test due to 'select' not showing correct status"
|
45
|
-
break
|
46
|
-
end while t && t[1].include?(pipe) && cntr < 20
|
47
|
-
|
48
|
-
pipe
|
49
|
-
end
|
50
|
-
|
51
|
-
it_behaves_like "an NIO selectable"
|
52
|
-
it_behaves_like "an NIO selectable stream"
|
53
|
-
end
|