nio4r 2.0.0.pre → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 +4 -21
  6. data/CHANGES.md +75 -42
  7. data/Gemfile +11 -3
  8. data/Guardfile +10 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +32 -136
  11. data/Rakefile +2 -0
  12. data/examples/echo_server.rb +1 -0
  13. data/ext/libev/Changes +4 -13
  14. data/ext/libev/ev.c +100 -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 +265 -257
  22. data/ext/nio4r/extconf.rb +2 -10
  23. data/ext/nio4r/monitor.c +93 -46
  24. data/ext/nio4r/nio4r.h +5 -15
  25. data/ext/nio4r/org/nio4r/ByteBuffer.java +193 -209
  26. data/ext/nio4r/org/nio4r/Monitor.java +164 -0
  27. data/ext/nio4r/org/nio4r/Nio4r.java +13 -391
  28. data/ext/nio4r/org/nio4r/Selector.java +278 -0
  29. data/ext/nio4r/selector.c +52 -52
  30. data/lib/nio.rb +3 -3
  31. data/lib/nio/bytebuffer.rb +179 -132
  32. data/lib/nio/monitor.rb +64 -4
  33. data/lib/nio/selector.rb +36 -13
  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 +323 -51
  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 +15 -11
  50. data/.rubocop_todo.yml +0 -35
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "set"
2
4
 
3
5
  module NIO
@@ -13,17 +15,33 @@ module NIO
13
15
  @closed = false
14
16
  end
15
17
 
18
+ # Return a symbol representing the backend I/O multiplexing mechanism used.
19
+ # Supported backends are:
20
+ # * :ruby - pure Ruby (i.e IO.select)
21
+ # * :java - Java NIO on JRuby
22
+ # * :epoll - libev w\ Linux epoll
23
+ # * :poll - libev w\ POSIX poll
24
+ # * :kqueue - libev w\ BSD kqueue
25
+ # * :select - libev w\ SysV select
26
+ # * :port - libev w\ I/O completion ports
27
+ # * :unknown - libev w\ unknown backend
28
+ def backend
29
+ :ruby
30
+ end
31
+
16
32
  # Register interest in an IO object with the selector for the given types
17
33
  # of events. Valid event types for interest are:
18
34
  # * :r - is the IO readable?
19
35
  # * :w - is the IO writeable?
20
36
  # * :rw - is the IO either readable or writeable?
21
37
  def register(io, interest)
38
+ io = IO.try_convert(io)
39
+
22
40
  @lock.synchronize do
23
- fail IOError, "selector is closed" if closed?
41
+ raise IOError, "selector is closed" if closed?
24
42
 
25
43
  monitor = @selectables[io]
26
- fail ArgumentError, "already registered as #{monitor.interests.inspect}" if monitor
44
+ raise ArgumentError, "already registered as #{monitor.interests.inspect}" if monitor
27
45
 
28
46
  monitor = Monitor.new(io, interest, self)
29
47
  @selectables[monitor.io] = monitor
@@ -35,7 +53,7 @@ module NIO
35
53
  # Deregister the given IO object from the selector
36
54
  def deregister(io)
37
55
  @lock.synchronize do
38
- monitor = @selectables.delete io
56
+ monitor = @selectables.delete IO.try_convert(io)
39
57
  monitor.close(false) if monitor && !monitor.closed?
40
58
  monitor
41
59
  end
@@ -60,15 +78,14 @@ module NIO
60
78
  monitor.readiness = nil
61
79
  end
62
80
 
63
- ready_readers, ready_writers = Kernel.select readers, writers, [], timeout
64
- return unless ready_readers # timeout or wakeup
81
+ ready_readers, ready_writers = Kernel.select(readers, writers, [], timeout)
82
+ return unless ready_readers # timeout
65
83
 
66
84
  ready_readers.each do |io|
67
85
  if io == @wakeup
68
86
  # Clear all wakeup signals we've received by reading them
69
87
  # Wakeups should have level triggered behavior
70
88
  @wakeup.read(@wakeup.stat.size)
71
- return
72
89
  else
73
90
  monitor = @selectables[io]
74
91
  monitor.readiness = :r
@@ -78,18 +95,16 @@ module NIO
78
95
 
79
96
  ready_writers.each do |io|
80
97
  monitor = @selectables[io]
81
- monitor.readiness = (monitor.readiness == :r) ? :rw : :w
98
+ monitor.readiness = monitor.readiness == :r ? :rw : :w
82
99
  selected_monitors << monitor
83
100
  end
84
101
  end
85
102
 
86
103
  if block_given?
87
- selected_monitors.each do |m|
88
- yield m
89
- end
104
+ selected_monitors.each { |m| yield m }
90
105
  selected_monitors.size
91
106
  else
92
- selected_monitors
107
+ selected_monitors.to_a
93
108
  end
94
109
  end
95
110
 
@@ -119,8 +134,16 @@ module NIO
119
134
  @lock.synchronize do
120
135
  return if @closed
121
136
 
122
- @wakeup.close rescue nil
123
- @waker.close rescue nil
137
+ begin
138
+ @wakeup.close
139
+ rescue IOError
140
+ end
141
+
142
+ begin
143
+ @waker.close
144
+ rescue IOError
145
+ end
146
+
124
147
  @closed = true
125
148
  end
126
149
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NIO
4
- VERSION = "2.0.0.pre".freeze
4
+ VERSION = "2.0.0"
5
5
  end
@@ -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"
@@ -1,76 +1,348 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  RSpec.describe NIO::ByteBuffer do
4
- describe "#Behaviour of ByteBuffer" do
5
- subject { bytebuffer }
6
+ let(:capacity) { 256 }
7
+ let(:example_string) { "Testing 1 2 3..." }
8
+ subject(:bytebuffer) { described_class.new(capacity) }
6
9
 
7
- context "allocates a given size buffer" do
8
- let(:bytebuffer) { NIO::ByteBuffer.new(256, nil, nil) }
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
9
15
 
10
- before :each do
11
- bytebuffer.clear
12
- end
16
+ describe "#clear" do
17
+ it "clears the buffer" do
18
+ bytebuffer << example_string
19
+ bytebuffer.clear
13
20
 
14
- it "Checks the allocation" do
15
- expect(bytebuffer.capacity).to eql(256)
16
- end
21
+ expect(bytebuffer.remaining).to eq capacity
22
+ end
23
+ end
17
24
 
18
- it "checks remaining" do
19
- expect(bytebuffer.remaining).to eql(256)
20
- end
25
+ describe "#position" do
26
+ it "defaults to zero" do
27
+ expect(bytebuffer.position).to be_zero
28
+ end
29
+ end
21
30
 
22
- it "puts a given string to buffer" do
23
- bytebuffer << "Song of Ice & Fire"
24
- expect(bytebuffer.remaining).to eql(238)
25
- end
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
26
88
 
27
- it "reads the content added" do
28
- bytebuffer << "Test"
29
- bytebuffer << "Text"
30
- bytebuffer << "Dumb"
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
31
316
  bytebuffer.flip
32
- expect(bytebuffer.read_next(5)).to eql "TestT"
33
- end
34
317
 
35
- it "rewinds the buffer" do
318
+ expect(bytebuffer.get).to eq example_string
36
319
  end
37
320
 
38
- it "compacts the buffer" do
39
- bytebuffer << "Test"
40
- bytebuffer << " Text"
41
- bytebuffer << "Dumb"
42
- bytebuffer.flip
43
- bytebuffer.read_next 5
44
- bytebuffer.compact
45
- bytebuffer << " RRMARTIN"
46
- bytebuffer.flip
47
- # expect(bytebuffer.limit?).to eql(10)
48
- expect(bytebuffer.get).to eql("TextDumb RRMARTIN")
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)
49
325
  end
50
326
 
51
- it "flips the bytebuffer" do
52
- bytebuffer << "Test"
53
- bytebuffer.flip
54
- expect(bytebuffer.get).to eql("Test")
327
+ it "returns 0 if no data is available" do
328
+ expect(bytebuffer.read_from(peer)).to eq 0
55
329
  end
330
+ end
56
331
 
57
- it "reads the next items" do
58
- bytebuffer << "John Snow"
332
+ describe "#write_to" do
333
+ it "writes data from the buffer" do
334
+ bytebuffer << example_string
59
335
  bytebuffer.flip
60
- bytebuffer.read_next 5
61
- expect(bytebuffer.read_next(4)).to eql("Snow")
62
- end
63
336
 
64
- it "clears the buffer" do
65
- bytebuffer << "Game of Thrones"
66
- bytebuffer.clear
67
- expect(bytebuffer.remaining).to eql(256)
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
68
341
  end
69
342
 
70
- it "gets the content of the bytebuffer" do
71
- bytebuffer << "Test"
343
+ it "raises NIO::ByteBuffer::UnderflowError if the buffer is out of data" do
72
344
  bytebuffer.flip
73
- expect(bytebuffer.get).to eql("Test")
345
+ expect { bytebuffer.write_to(peer) }.to raise_error(NIO::ByteBuffer::UnderflowError)
74
346
  end
75
347
  end
76
348
  end