nio4r 1.2.1-java → 2.0.0-java

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +31 -38
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +15 -14
  6. data/CHANGES.md +75 -42
  7. data/Gemfile +10 -5
  8. data/Guardfile +10 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +57 -161
  11. data/Rakefile +2 -1
  12. data/examples/echo_server.rb +1 -0
  13. data/ext/libev/Changes +4 -13
  14. data/ext/libev/ev.c +101 -74
  15. data/ext/libev/ev.h +3 -3
  16. data/ext/libev/ev_epoll.c +6 -3
  17. data/ext/libev/ev_kqueue.c +8 -4
  18. data/ext/libev/ev_poll.c +6 -3
  19. data/ext/libev/ev_port.c +8 -4
  20. data/ext/libev/ev_select.c +4 -2
  21. data/ext/nio4r/bytebuffer.c +421 -0
  22. data/ext/nio4r/extconf.rb +2 -10
  23. data/ext/nio4r/monitor.c +93 -46
  24. data/ext/nio4r/nio4r.h +11 -13
  25. data/ext/nio4r/org/nio4r/ByteBuffer.java +295 -0
  26. data/ext/nio4r/org/nio4r/Monitor.java +164 -0
  27. data/ext/nio4r/org/nio4r/Nio4r.java +22 -391
  28. data/ext/nio4r/org/nio4r/Selector.java +278 -0
  29. data/ext/nio4r/selector.c +55 -53
  30. data/lib/nio.rb +4 -3
  31. data/lib/nio/bytebuffer.rb +222 -0
  32. data/lib/nio/monitor.rb +64 -4
  33. data/lib/nio/selector.rb +52 -20
  34. data/lib/nio/version.rb +1 -1
  35. data/nio4r.gemspec +25 -19
  36. data/spec/nio/acceptables_spec.rb +6 -4
  37. data/spec/nio/bytebuffer_spec.rb +349 -0
  38. data/spec/nio/monitor_spec.rb +122 -79
  39. data/spec/nio/selectables/pipe_spec.rb +5 -1
  40. data/spec/nio/selectables/ssl_socket_spec.rb +15 -12
  41. data/spec/nio/selectables/tcp_socket_spec.rb +42 -31
  42. data/spec/nio/selectables/udp_socket_spec.rb +2 -0
  43. data/spec/nio/selector_spec.rb +10 -4
  44. data/spec/spec_helper.rb +24 -3
  45. data/spec/support/selectable_examples.rb +7 -5
  46. data/tasks/extension.rake +2 -0
  47. data/tasks/rspec.rake +2 -0
  48. data/tasks/rubocop.rake +2 -0
  49. metadata +21 -14
  50. data/.rubocop_todo.yml +0 -35
data/lib/nio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NIO
4
- VERSION = "1.2.1".freeze
4
+ VERSION = "2.0.0"
5
5
  end
data/nio4r.gemspec CHANGED
@@ -1,28 +1,34 @@
1
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  require File.expand_path("../lib/nio/version", __FILE__)
3
4
 
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Tony Arcieri"]
6
- gem.email = ["tony.arcieri@gmail.com"]
7
- gem.description = "New IO for Ruby"
8
- gem.summary = "NIO provides a high performance selector API for monitoring IO objects"
9
- gem.homepage = "https://github.com/celluloid/nio4r"
10
- gem.license = "MIT"
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"
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
11
22
 
12
- gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
13
- gem.files = `git ls-files`.split("\n")
14
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- gem.name = "nio4r"
16
- gem.require_paths = ["lib"]
17
- gem.version = NIO::VERSION
23
+ spec.required_ruby_version = ">= 2.2.2"
18
24
 
19
25
  if defined? JRUBY_VERSION
20
- gem.files << "lib/nio4r_ext.jar"
21
- gem.platform = "java"
26
+ spec.files << "lib/nio4r_ext.jar"
27
+ spec.platform = "java"
22
28
  else
23
- gem.extensions = ["ext/nio4r/extconf.rb"]
29
+ spec.extensions = ["ext/nio4r/extconf.rb"]
24
30
  end
25
31
 
26
- gem.add_development_dependency "rake"
27
- gem.add_development_dependency "bundler"
32
+ spec.add_development_dependency "rake"
33
+ spec.add_development_dependency "bundler"
28
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  RSpec.describe "NIO acceptables" do
@@ -15,16 +17,16 @@ RSpec.describe "NIO acceptables" do
15
17
  end
16
18
 
17
19
  describe TCPServer do
18
- let(:tcp_port) { 23_456 }
20
+ let(:port) { next_available_tcp_port }
19
21
 
20
22
  let :acceptable_subject do
21
- server = TCPServer.new("localhost", tcp_port)
22
- TCPSocket.open("localhost", tcp_port)
23
+ server = TCPServer.new("localhost", port)
24
+ TCPSocket.open("localhost", port)
23
25
  server
24
26
  end
25
27
 
26
28
  let :unacceptable_subject do
27
- TCPServer.new("localhost", tcp_port + 1)
29
+ TCPServer.new("localhost", port + 1)
28
30
  end
29
31
 
30
32
  it_behaves_like "an NIO acceptable"
@@ -0,0 +1,349 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe NIO::ByteBuffer do
6
+ let(:capacity) { 256 }
7
+ let(:example_string) { "Testing 1 2 3..." }
8
+ subject(:bytebuffer) { described_class.new(capacity) }
9
+
10
+ describe "#initialize" do
11
+ it "raises TypeError if given a bogus argument" do
12
+ expect { described_class.new(:symbols_are_bogus) }.to raise_error(TypeError)
13
+ end
14
+ end
15
+
16
+ describe "#clear" do
17
+ it "clears the buffer" do
18
+ bytebuffer << example_string
19
+ bytebuffer.clear
20
+
21
+ expect(bytebuffer.remaining).to eq capacity
22
+ end
23
+ end
24
+
25
+ describe "#position" do
26
+ it "defaults to zero" do
27
+ expect(bytebuffer.position).to be_zero
28
+ end
29
+ end
30
+
31
+ describe "#position=" do
32
+ let(:example_position) { 42 }
33
+
34
+ it "sets the buffer's position to a valid value" do
35
+ expect(bytebuffer.position).to be_zero
36
+ bytebuffer.position = example_position
37
+ expect(bytebuffer.position).to eq example_position
38
+ end
39
+
40
+ it "raises ArgumentError if the specified position is less than zero" do
41
+ expect { bytebuffer.position = -1 }.to raise_error(ArgumentError)
42
+ end
43
+
44
+ it "raises ArgumentError if the specified position exceeds the limit" do
45
+ expect { bytebuffer.position = capacity + 1 }.to raise_error(ArgumentError)
46
+ end
47
+ end
48
+
49
+ describe "#limit" do
50
+ it "defaults to the buffer's capacity" do
51
+ expect(bytebuffer.limit).to eq capacity
52
+ end
53
+ end
54
+
55
+ describe "#limit=" do
56
+ it "sets the buffer's limit to a valid value" do
57
+ bytebuffer.flip
58
+ expect(bytebuffer.limit).to be_zero
59
+
60
+ new_limit = capacity / 2
61
+ bytebuffer.limit = new_limit
62
+ expect(bytebuffer.limit).to eq new_limit
63
+ end
64
+
65
+ it "preserves position and mark if they're less than the new limit" do
66
+ bytebuffer << "four"
67
+ bytebuffer.mark
68
+ bytebuffer << "more"
69
+
70
+ bytebuffer.limit = capacity / 2
71
+ expect(bytebuffer.position).to eq 8
72
+ bytebuffer.reset
73
+ expect(bytebuffer.position).to eq 4
74
+ end
75
+
76
+ it "sets position to the new limit if the previous position is beyond the limit" do
77
+ bytebuffer << "four"
78
+ bytebuffer.limit = 2
79
+ expect(bytebuffer.position).to eq 2
80
+ end
81
+
82
+ it "clears the mark if the new limit is before the current mark" do
83
+ bytebuffer << "four"
84
+ bytebuffer.mark
85
+ bytebuffer.limit = 2
86
+ expect { bytebuffer.reset }.to raise_error(NIO::ByteBuffer::MarkUnsetError)
87
+ end
88
+
89
+ it "raises ArgumentError if specified limit is less than zero" do
90
+ expect { bytebuffer.limit = -1 }.to raise_error(ArgumentError)
91
+ end
92
+
93
+ it "raises ArgumentError if specified limit exceeds capacity" do
94
+ expect { bytebuffer.limit = capacity }.not_to raise_error
95
+ expect { bytebuffer.limit = capacity + 1 }.to raise_error(ArgumentError)
96
+ end
97
+ end
98
+
99
+ describe "#capacity" do
100
+ it "has the requested capacity" do
101
+ expect(bytebuffer.capacity).to eq capacity
102
+ end
103
+ end
104
+
105
+ describe "#remaining" do
106
+ it "calculates the number of bytes remaining" do
107
+ expect(bytebuffer.remaining).to eq capacity
108
+ bytebuffer << example_string
109
+ expect(bytebuffer.remaining).to eq(capacity - example_string.length)
110
+ end
111
+ end
112
+
113
+ describe "#full?" do
114
+ it "returns false when there is space remaining in the buffer" do
115
+ expect(bytebuffer).not_to be_full
116
+ end
117
+
118
+ it "returns true when the buffer is full" do
119
+ bytebuffer << "X" * capacity
120
+ expect(bytebuffer).to be_full
121
+ end
122
+ end
123
+
124
+ describe "#get" do
125
+ it "reads all remaining data if no length is given" do
126
+ bytebuffer << example_string
127
+ bytebuffer.flip
128
+
129
+ expect(bytebuffer.get).to eq example_string
130
+ end
131
+
132
+ it "reads zeroes from a newly initialized buffer" do
133
+ expect(bytebuffer.get(capacity)).to eq("\0" * capacity)
134
+ end
135
+
136
+ it "advances position as data is read" do
137
+ bytebuffer << "First"
138
+ bytebuffer << "Second"
139
+ bytebuffer << "Third"
140
+ bytebuffer.flip
141
+
142
+ expect(bytebuffer.position).to be_zero
143
+ expect(bytebuffer.get(10)).to eq "FirstSecon"
144
+ expect(bytebuffer.position).to eq 10
145
+ end
146
+
147
+ it "raises NIO::ByteBuffer::UnderflowError if there is not enough data in the buffer" do
148
+ bytebuffer << example_string
149
+ bytebuffer.flip
150
+
151
+ expect { bytebuffer.get(example_string.length + 1) }.to raise_error(NIO::ByteBuffer::UnderflowError)
152
+ expect(bytebuffer.get(example_string.length)).to eq example_string
153
+ end
154
+ end
155
+
156
+ describe "#[]" do
157
+ it "obtains bytes at a given index without altering position" do
158
+ bytebuffer << example_string
159
+ expect(bytebuffer[7]).to eq example_string.bytes[7]
160
+ expect(bytebuffer.position).to eq example_string.length
161
+ end
162
+
163
+ it "raises ArgumentError if the index is less than zero" do
164
+ expect { bytebuffer[-1] }.to raise_error(ArgumentError)
165
+ end
166
+
167
+ it "raises ArgumentError if the index exceeds the limit" do
168
+ bytebuffer << example_string
169
+ bytebuffer.flip
170
+ expect(bytebuffer[bytebuffer.limit - 1]).to eq example_string.bytes.last
171
+ expect { bytebuffer[bytebuffer.limit] }.to raise_error(ArgumentError)
172
+ end
173
+ end
174
+
175
+ describe "#<<" do
176
+ it "adds strings to the buffer" do
177
+ bytebuffer << example_string
178
+ expect(bytebuffer.position).to eq example_string.length
179
+ expect(bytebuffer.limit).to eq capacity
180
+ end
181
+
182
+ it "raises NIO::ByteBuffer::OverflowError if the buffer is full" do
183
+ bytebuffer << "X" * (capacity - 1)
184
+ expect { bytebuffer << "X" }.not_to raise_error
185
+ expect { bytebuffer << "X" }.to raise_error(NIO::ByteBuffer::OverflowError)
186
+ end
187
+ end
188
+
189
+ describe "#flip" do
190
+ it "flips the bytebuffer" do
191
+ bytebuffer << example_string
192
+ expect(bytebuffer.position).to eql example_string.length
193
+
194
+ expect(bytebuffer.flip).to eq bytebuffer
195
+
196
+ expect(bytebuffer.position).to be_zero
197
+ expect(bytebuffer.limit).to eq example_string.length
198
+ expect(bytebuffer.get).to eq example_string
199
+ end
200
+
201
+ it "sets remaining to the previous position" do
202
+ bytebuffer << example_string
203
+ previous_position = bytebuffer.position
204
+ expect(bytebuffer.remaining).to eq(capacity - previous_position)
205
+ expect(bytebuffer.flip.remaining).to eq previous_position
206
+ end
207
+
208
+ it "sets limit to the previous position" do
209
+ bytebuffer << example_string
210
+ expect(bytebuffer.limit).to eql(capacity)
211
+
212
+ previous_position = bytebuffer.position
213
+ expect(bytebuffer.flip.limit).to eql previous_position
214
+ end
215
+ end
216
+
217
+ describe "#rewind" do
218
+ it "rewinds the buffer leaving the limit intact" do
219
+ bytebuffer << example_string
220
+ expect(bytebuffer.rewind).to eq bytebuffer
221
+
222
+ expect(bytebuffer.position).to be_zero
223
+ expect(bytebuffer.limit).to eq capacity
224
+ end
225
+ end
226
+
227
+ describe "#mark" do
228
+ it "returns self" do
229
+ expect(bytebuffer.mark).to eql bytebuffer
230
+ end
231
+ end
232
+
233
+ describe "#reset" do
234
+ it "returns to a previously marked position" do
235
+ bytebuffer << "First"
236
+ expected_position = bytebuffer.position
237
+
238
+ expect(bytebuffer.mark).to eq bytebuffer
239
+ bytebuffer << "Second"
240
+ expect(bytebuffer.position).not_to eq expected_position
241
+ expect(bytebuffer.reset.position).to eq expected_position
242
+ end
243
+
244
+ it "raises NIO::ByteBuffer::MarkUnsetError unless mark has been set" do
245
+ expect { bytebuffer.reset }.to raise_error(NIO::ByteBuffer::MarkUnsetError)
246
+ end
247
+ end
248
+
249
+ describe "#compact" do
250
+ let(:first_string) { "CompactMe" }
251
+ let(:second_string) { "Leftover" }
252
+
253
+ it "copies data from the current position to the beginning of the buffer" do
254
+ bytebuffer << first_string << second_string
255
+ bytebuffer.position = first_string.length
256
+ bytebuffer.limit = first_string.length + second_string.length
257
+ bytebuffer.compact
258
+
259
+ expect(bytebuffer.position).to eq second_string.length
260
+ expect(bytebuffer.limit).to eq capacity
261
+ expect(bytebuffer.flip.get).to eq second_string
262
+ end
263
+ end
264
+
265
+ describe "#each" do
266
+ it "iterates over data in the buffer" do
267
+ bytebuffer << example_string
268
+ bytebuffer.flip
269
+
270
+ bytes = []
271
+ bytebuffer.each { |byte| bytes << byte }
272
+ expect(bytes).to eq example_string.bytes
273
+ end
274
+ end
275
+
276
+ describe "#inspect" do
277
+ it "inspects the buffer offsets" do
278
+ regex = /\A#<NIO::ByteBuffer:.*? @position=0 @limit=#{capacity} @capacity=#{capacity}>\z/
279
+ expect(bytebuffer.inspect).to match(regex)
280
+ end
281
+ end
282
+
283
+ context "I/O" do
284
+ let(:addr) { "localhost" }
285
+ let(:port) { next_available_tcp_port }
286
+ let(:server) { TCPServer.new(addr, port) }
287
+ let(:client) { TCPSocket.new(addr, port) }
288
+ let(:peer) { server_thread.value }
289
+
290
+ let(:server_thread) do
291
+ server
292
+
293
+ thread = Thread.new { server.accept }
294
+ Thread.pass while thread.status && thread.status != "sleep"
295
+
296
+ thread
297
+ end
298
+
299
+ before do
300
+ server_thread
301
+ client
302
+ end
303
+
304
+ after do
305
+ server_thread.kill if server_thread.alive?
306
+
307
+ server.close rescue nil
308
+ client.close rescue nil
309
+ peer.close rescue nil
310
+ end
311
+
312
+ describe "#read_from" do
313
+ it "reads data into the buffer" do
314
+ client.write(example_string)
315
+ expect(bytebuffer.read_from(peer)).to eq example_string.length
316
+ bytebuffer.flip
317
+
318
+ expect(bytebuffer.get).to eq example_string
319
+ end
320
+
321
+ it "raises NIO::ByteBuffer::OverflowError if the buffer is already full" do
322
+ client.write(example_string)
323
+ bytebuffer << "X" * capacity
324
+ expect { bytebuffer.read_from(peer) }.to raise_error(NIO::ByteBuffer::OverflowError)
325
+ end
326
+
327
+ it "returns 0 if no data is available" do
328
+ expect(bytebuffer.read_from(peer)).to eq 0
329
+ end
330
+ end
331
+
332
+ describe "#write_to" do
333
+ it "writes data from the buffer" do
334
+ bytebuffer << example_string
335
+ bytebuffer.flip
336
+
337
+ expect(bytebuffer.write_to(client)).to eq example_string.length
338
+ client.close
339
+
340
+ expect(peer.read(example_string.length)).to eq example_string
341
+ end
342
+
343
+ it "raises NIO::ByteBuffer::UnderflowError if the buffer is out of data" do
344
+ bytebuffer.flip
345
+ expect { bytebuffer.write_to(peer) }.to raise_error(NIO::ByteBuffer::UnderflowError)
346
+ end
347
+ end
348
+ end
349
+ end
@@ -1,113 +1,156 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
  require "socket"
3
5
 
4
6
  RSpec.describe NIO::Monitor do
5
- let(:example_peers) do
6
- address = "127.0.0.1"
7
- base_port = 12_345
8
- tries = 10
9
-
10
- server = tries.times do |n|
11
- begin
12
- break TCPServer.new(address, base_port + n)
13
- rescue Errno::EADDRINUSE
14
- retry
15
- end
16
- end
17
-
18
- fail Errno::EADDRINUSE, "couldn't find an open port" unless server
19
- client = TCPSocket.new(address, server.addr[1])
20
- [server, client]
21
- end
7
+ let(:addr) { "localhost" }
8
+ let(:port) { next_available_tcp_port }
22
9
 
23
- let(:reader) { example_peers.first }
24
- let(:writer) { example_peers.last }
10
+ let(:reader) { TCPServer.new(addr, port) }
11
+ let(:writer) { TCPSocket.new(addr, port) }
25
12
 
26
13
  let(:selector) { NIO::Selector.new }
27
14
 
28
- subject { selector.register(reader, :r) }
29
- let(:peer) { selector.register(writer, :rw) }
30
- after { selector.close }
15
+ subject(:monitor) { selector.register(writer, :rw) }
16
+ subject(:peer) { selector.register(reader, :r) }
31
17
 
32
- before { example_peers } # open server and client automatically
33
- after { reader.close }
34
- after { writer.close }
18
+ before { reader }
19
+ before { writer }
20
+ after { reader.close }
21
+ after { writer.close }
22
+ after { selector.close }
35
23
 
36
- it "knows its interests" do
37
- expect(subject.interests).to eq(:r)
38
- expect(peer.interests).to eq(:rw)
24
+ describe "#interests" do
25
+ it "knows its interests" do
26
+ expect(peer.interests).to eq(:r)
27
+ expect(monitor.interests).to eq(:rw)
28
+ end
39
29
  end
40
30
 
41
- it "changes the interest set" do
42
- expect(peer.interests).not_to eq(:w)
43
- peer.interests = :w
44
- expect(peer.interests).to eq(:w)
45
- end
31
+ describe "#interests=" do
32
+ it "changes the interest set" do
33
+ expect(monitor.interests).not_to eq(:w)
34
+ monitor.interests = :w
35
+ expect(monitor.interests).to eq(:w)
36
+ end
46
37
 
47
- it "knows its IO object" do
48
- expect(subject.io).to eq(reader)
38
+ it "raises EOFError if interests are changed after the monitor is closed" do
39
+ monitor.close
40
+ expect { monitor.interests = :rw }.to raise_error(EOFError)
41
+ end
49
42
  end
50
43
 
51
- it "knows its selector" do
52
- expect(subject.selector).to eq(selector)
53
- end
44
+ describe "#add_interest" do
45
+ it "sets a new interest if it isn't currently registered" do
46
+ monitor.interests = :r
47
+ expect(monitor.interests).to eq(:r)
48
+
49
+ expect(monitor.add_interest(:w)).to eq(:rw)
50
+ expect(monitor.interests).to eq(:rw)
51
+ end
52
+
53
+ it "acts idempotently" do
54
+ monitor.interests = :r
55
+ expect(monitor.interests).to eq(:r)
56
+
57
+ expect(monitor.add_interest(:r)).to eq(:r)
58
+ expect(monitor.interests).to eq(:r)
59
+ end
54
60
 
55
- it "stores arbitrary values" do
56
- subject.value = 42
57
- expect(subject.value).to eq(42)
61
+ it "raises ArgumentError if given a bogus option" do
62
+ expect { monitor.add_interest(:derp) }.to raise_error(ArgumentError)
63
+ end
58
64
  end
59
65
 
60
- it "knows what operations IO objects are ready for" do
61
- # For whatever odd reason this breaks unless we eagerly evaluate subject
62
- reader_monitor = subject
63
- writer_monitor = peer
66
+ describe "#remove_interest" do
67
+ it "removes an interest from the set" do
68
+ expect(monitor.interests).to eq(:rw)
69
+
70
+ expect(monitor.remove_interest(:r)).to eq(:w)
71
+ expect(monitor.interests).to eq(:w)
72
+ end
64
73
 
65
- selected = selector.select(0)
66
- expect(selected).to include(writer_monitor)
74
+ it "can clear the last interest" do
75
+ monitor.interests = :w
76
+ expect(monitor.interests).to eq(:w)
67
77
 
68
- expect(writer_monitor.readiness).to eq(:w)
69
- expect(writer_monitor).not_to be_readable
70
- expect(writer_monitor).to be_writable
78
+ expect(monitor.remove_interest(:w)).to be_nil
79
+ expect(monitor.interests).to be_nil
80
+ end
71
81
 
72
- writer << "testing 1 2 3"
82
+ it "acts idempotently" do
83
+ monitor.interests = :w
84
+ expect(monitor.interests).to eq(:w)
73
85
 
74
- selected = selector.select(0)
75
- expect(selected).to include(reader_monitor)
86
+ expect(monitor.remove_interest(:r)).to eq(:w)
87
+ expect(monitor.interests).to eq(:w)
88
+ end
76
89
 
77
- expect(reader_monitor.readiness).to eq(:r)
78
- expect(reader_monitor).to be_readable
79
- expect(reader_monitor).not_to be_writable
90
+ it "raises ArgumentError if given a bogus option" do
91
+ expect { monitor.add_interest(:derp) }.to raise_error(ArgumentError)
92
+ end
80
93
  end
81
94
 
82
- it "changes current interests with #interests=" do
83
- client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
84
- monitor = selector.register(client, :r)
85
- expect(monitor.interests).to eq(:r)
86
- monitor.interests = :w
87
- expect(monitor.interests).to eq(:w)
95
+ describe "#io" do
96
+ it "knows its IO object" do
97
+ expect(monitor.io).to eq(writer)
98
+ end
88
99
  end
89
100
 
90
- it "closes" do
91
- expect(subject).not_to be_closed
92
- expect(selector.registered?(reader)).to be_truthy
101
+ describe "#selector" do
102
+ it "knows its selector" do
103
+ expect(monitor.selector).to eq(selector)
104
+ end
105
+ end
93
106
 
94
- subject.close
95
- expect(subject).to be_closed
96
- expect(selector.registered?(reader)).to be_falsey
107
+ describe "#value=" do
108
+ it "stores arbitrary values" do
109
+ monitor.value = 42
110
+ expect(monitor.value).to eq(42)
111
+ end
97
112
  end
98
113
 
99
- it "closes even if the selector has been shutdown" do
100
- expect(subject).not_to be_closed
101
- selector.close # forces shutdown
102
- expect(subject).not_to be_closed
103
- subject.close
104
- expect(subject).to be_closed
114
+ describe "#readiness" do
115
+ it "knows what operations IO objects are ready for" do
116
+ # For whatever odd reason this breaks unless we eagerly evaluate monitor
117
+ reader_peer = peer
118
+ writer_peer = monitor
119
+
120
+ selected = selector.select(0)
121
+ expect(selected).to include(writer_peer)
122
+
123
+ expect(writer_peer.readiness).to eq(:w)
124
+ expect(writer_peer).not_to be_readable
125
+ expect(writer_peer).to be_writable
126
+
127
+ writer << "testing 1 2 3"
128
+
129
+ selected = selector.select(0)
130
+ expect(selected).to include(reader_peer)
131
+
132
+ expect(reader_peer.readiness).to eq(:r)
133
+ expect(reader_peer).to be_readable
134
+ expect(reader_peer).not_to be_writable
135
+ end
105
136
  end
106
137
 
107
- it "changes the interest set after monitor closed" do
108
- # check for changing the interests on the go after closed expected to fail
109
- expect(subject.interests).not_to eq(:rw)
110
- subject.close # forced shutdown
111
- expect { subject.interests = :rw }.to raise_error(TypeError)
138
+ describe "#close" do
139
+ it "closes" do
140
+ expect(monitor).not_to be_closed
141
+ expect(selector.registered?(writer)).to be_truthy
142
+
143
+ monitor.close
144
+ expect(monitor).to be_closed
145
+ expect(selector.registered?(writer)).to be_falsey
146
+ end
147
+
148
+ it "closes even if the selector has been shutdown" do
149
+ expect(monitor).not_to be_closed
150
+ selector.close # forces shutdown
151
+ expect(monitor).not_to be_closed
152
+ monitor.close
153
+ expect(monitor).to be_closed
154
+ end
112
155
  end
113
156
  end