ione 1.2.2 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 941fc3fd59d15b806854ddd8ff0f30f645ff51bf
4
- data.tar.gz: 1b89b672b8b69eda9561138190aed7d35d677538
2
+ SHA256:
3
+ metadata.gz: 9c062de288365ad9e47516ad7b9d3ab028ab9449964c88675c0b913657a8ef07
4
+ data.tar.gz: f349d422d1df3001a9c5fb5df55c329ed60cebc9dad20ddf1a97f52883c53093
5
5
  SHA512:
6
- metadata.gz: f26209edcf366c70ac80a913901f4c6db5a7a15e2e4a9c53e0d73f99b9d1e25e98bd4f0533dc4e88d3ec846abd538b6f80cb9d877ae547091a363121d1a05992
7
- data.tar.gz: 6c5565772dd3994caac71ffd2a63cf25f2c595161eeb501928050123c59b5579cf1d5a45ac4a0e585ca15fd9c82b9c7f1253135c2e6f74cf0e9569a89e66d1dc
6
+ metadata.gz: 4523ab1052502a8da1879f73481f8326b260b478d71c7e9d882b264e2c274092ae0801a43c5884492ea06fd69dd3ac02ab184ef309dbcf1cf5b8d441f37e8bd6
7
+ data.tar.gz: 1e5d1c7f51593988a2270ec4c6b74cd61d72a1dd2c6a69d5c6f4b2053052aa4ee3bd52375c4e419d423110bd70994350662a61850907570db47ea45c24a7575b
@@ -179,10 +179,12 @@ module Ione
179
179
  swap_buffers
180
180
  end
181
181
  read_buffer_length = @read_buffer.bytesize
182
- if start_index < read_buffer_length - @offset && (index = @read_buffer.index(substring, @offset + start_index))
182
+ if start_index + substring.bytesize <= read_buffer_length - @offset && (index = @read_buffer.index(substring, @offset + start_index))
183
183
  index - @offset
184
- elsif (index = @write_buffer.index(substring, start_index - read_buffer_length + @offset))
185
- index + read_buffer_length - @offset
184
+ elsif start_index + substring.bytesize <= read_buffer_length - @offset + @write_buffer.bytesize
185
+ merge_read_buffer
186
+ start_index = read_buffer_length - substring.bytesize if read_buffer_length - substring.bytesize > start_index
187
+ @read_buffer.index(substring, start_index)
186
188
  else
187
189
  nil
188
190
  end
@@ -298,5 +300,11 @@ module Ione
298
300
  @read_buffer = @write_buffer
299
301
  @write_buffer = ''
300
302
  end
303
+
304
+ def merge_read_buffer
305
+ @read_buffer = @read_buffer[@offset, @read_buffer.length - @offset] << @write_buffer
306
+ @write_buffer = ''
307
+ @offset = 0
308
+ end
301
309
  end
302
310
  end
data/lib/ione/future.rb CHANGED
@@ -209,6 +209,8 @@ module Ione
209
209
  end
210
210
  if futures.count == 0
211
211
  resolved([])
212
+ elsif (failed = futures.find { |f| f.respond_to?(:failed?) && f.failed? })
213
+ failed
212
214
  else
213
215
  CombinedFuture.new(futures)
214
216
  end
@@ -231,6 +233,7 @@ module Ione
231
233
  if futures.size == 1 && (fs = futures.first).is_a?(Enumerable)
232
234
  *futures = *fs
233
235
  end
236
+ futures.reject! { |f| f.respond_to?(:resolved?) && f.resolved? }
234
237
  if futures.count == 0
235
238
  ResolvedFuture::NIL
236
239
  elsif futures.count == 1
@@ -262,7 +265,9 @@ module Ione
262
265
  futures = fs
263
266
  end
264
267
  if futures.count == 0
265
- resolved
268
+ ResolvedFuture::NIL
269
+ elsif (done = futures.find { |f| f.respond_to?(:resolved?) && f.resolved? })
270
+ done
266
271
  else
267
272
  FirstFuture.new(futures)
268
273
  end
@@ -594,6 +599,8 @@ module Ione
594
599
  @lock = Mutex.new
595
600
  @state = PENDING_STATE
596
601
  @listeners = []
602
+ @value = nil
603
+ @error = nil
597
604
  end
598
605
 
599
606
  # Registers a listener that will be called when this future completes,
@@ -854,9 +861,9 @@ module Ione
854
861
  looping = more = true
855
862
  while more
856
863
  more = false
857
- @futures.pop.on_complete do |v, e|
858
- if e || @futures.empty? || !looping || !Thread.current.equal?(outer)
859
- await_next(v, e)
864
+ @futures.pop.on_complete do |value, error|
865
+ if error || @futures.empty? || !looping || !Thread.current.equal?(outer)
866
+ await_next(value, error)
860
867
  else
861
868
  more = true
862
869
  end
@@ -874,64 +881,56 @@ module Ione
874
881
  def initialize(futures, initial_value, reducer)
875
882
  super()
876
883
  @futures = Array(futures)
877
- @remaining = @futures.size
878
- @initial_value = initial_value
879
- @accumulator = initial_value.nil? ? NO_INITIAL_VALUE : initial_value
884
+ @initial_value = initial_value.nil? ? NO_INITIAL_VALUE : initial_value
880
885
  @reducer = reducer
881
886
  end
882
-
883
- private
884
-
885
- def reduce_one(value)
886
- unless failed?
887
- @lock.lock
888
- begin
889
- if @accumulator.equal?(NO_INITIAL_VALUE)
890
- @accumulator = value
891
- else
892
- @accumulator = @reducer.call(@accumulator, value)
893
- end
894
- @remaining -= 1
895
- rescue => e
896
- @lock.unlock
897
- fail(e)
898
- else
899
- @lock.unlock
900
- end
901
- unless failed?
902
- if @remaining == 0
903
- resolve(@accumulator)
904
- :done
905
- else
906
- :continue
907
- end
908
- end
909
- end
910
- end
911
887
  end
912
888
 
913
889
  # @private
914
890
  class OrderedReducingFuture < ReducingFuture
915
891
  def initialize(futures, initial_value, reducer)
916
892
  super
917
- if @remaining > 0
918
- reduce_next(0)
893
+ if @futures.empty?
894
+ resolve(@initial_value.equal?(NO_INITIAL_VALUE) ? nil : @initial_value)
895
+ elsif @initial_value.equal?(NO_INITIAL_VALUE)
896
+ @futures.shift.on_complete(&method(:reduce_next))
919
897
  else
920
- resolve(@initial_value)
898
+ reduce_next(@initial_value, nil)
921
899
  end
922
900
  end
923
901
 
924
902
  private
925
903
 
926
- def reduce_next(i)
927
- @futures[i].on_complete do |v, e|
928
- unless failed?
929
- if e
930
- fail(e)
931
- elsif reduce_one(v) == :continue
932
- reduce_next(i + 1)
904
+ def reduce_next(accumulator, e)
905
+ if e
906
+ @futures = nil
907
+ fail(e)
908
+ elsif @futures.empty?
909
+ @futures = nil
910
+ resolve(accumulator)
911
+ else
912
+ outer = Thread.current
913
+ looping = more = true
914
+ while more
915
+ more = false
916
+ @futures.shift.on_complete do |v, ee|
917
+ if ee
918
+ reduce_next(nil, ee)
919
+ else
920
+ begin
921
+ accumulator = @reducer.call(accumulator, v)
922
+ if @futures.empty? || !looping || !Thread.current.equal?(outer)
923
+ reduce_next(accumulator, nil)
924
+ else
925
+ more = true
926
+ end
927
+ rescue => eee
928
+ reduce_next(nil, eee)
929
+ end
930
+ end
933
931
  end
934
932
  end
933
+ looping = false
935
934
  end
936
935
  end
937
936
  end
@@ -940,20 +939,37 @@ module Ione
940
939
  class UnorderedReducingFuture < ReducingFuture
941
940
  def initialize(futures, initial_value, reducer)
942
941
  super
943
- if @remaining > 0
944
- futures.each do |f|
942
+ if @futures.empty?
943
+ resolve(@initial_value.equal?(NO_INITIAL_VALUE) ? nil : @initial_value)
944
+ else
945
+ accumulator = @initial_value
946
+ remaining = @futures.size
947
+ @futures.each do |f|
945
948
  f.on_complete do |v, e|
946
949
  unless failed?
947
950
  if e
948
951
  fail(e)
949
952
  else
950
- reduce_one(v)
953
+ done = false
954
+ @lock.lock
955
+ begin
956
+ accumulator = accumulator.equal?(NO_INITIAL_VALUE) ? v : @reducer.call(accumulator, v)
957
+ remaining -= 1
958
+ done = (remaining == 0)
959
+ rescue => ee
960
+ @lock.unlock
961
+ fail(ee)
962
+ else
963
+ @lock.unlock
964
+ end
965
+ if done
966
+ @futures = nil
967
+ resolve(accumulator)
968
+ end
951
969
  end
952
970
  end
953
971
  end
954
972
  end
955
- else
956
- resolve(@initial_value)
957
973
  end
958
974
  end
959
975
  end
@@ -22,6 +22,7 @@ module Ione
22
22
  @backlog = backlog
23
23
  @unblocker = unblocker
24
24
  @reactor = reactor
25
+ @io = nil
25
26
  @socket_impl = socket_impl || ServerSocket
26
27
  @accept_listeners = []
27
28
  @lock = Mutex.new
@@ -15,10 +15,12 @@ module Ione
15
15
  def initialize(host, port, unblocker)
16
16
  @host = host
17
17
  @port = port
18
+ @io = nil
18
19
  @unblocker = unblocker
19
20
  @state = CONNECTING_STATE
20
21
  @writable = false
21
22
  @lock = Mutex.new
23
+ @data_listener = nil
22
24
  @write_buffer = ByteBuffer.new
23
25
  @closed_promise = Promise.new
24
26
  end
@@ -14,6 +14,7 @@ module Ione
14
14
  @connection_timeout = connection_timeout
15
15
  @clock = clock
16
16
  @socket_impl = socket_impl
17
+ @addrinfos = nil
17
18
  @connected_promise = Promise.new
18
19
  on_closed(&method(:cleanup_on_close))
19
20
  end
@@ -95,8 +95,9 @@ module Ione
95
95
  @clock = options[:clock] || Time
96
96
  @state = PENDING_STATE
97
97
  @error_listeners = []
98
+ @unblocker = nil
98
99
  @io_loop = IoLoopBody.new(@options)
99
- @scheduler = Scheduler.new
100
+ @scheduler = Scheduler.new(@options)
100
101
  @lock = Mutex.new
101
102
  end
102
103
 
@@ -124,7 +125,7 @@ module Ione
124
125
  # after {#stop} has been called, but false when the future returned by
125
126
  # {#stop} completes.
126
127
  def running?
127
- @state == RUNNING_STATE
128
+ (state = @state) == RUNNING_STATE || state == STOPPING_STATE
128
129
  end
129
130
 
130
131
  # Starts the reactor. This will spawn a background thread that will manage
@@ -570,8 +571,8 @@ module Ione
570
571
  ensure
571
572
  @lock.unlock
572
573
  end
573
- timers.each do |timer|
574
- timer.fail(CancelledError.new)
574
+ timers.each do |t|
575
+ t.fail(CancelledError.new)
575
576
  end
576
577
  nil
577
578
  end
@@ -592,8 +593,8 @@ module Ione
592
593
  ensure
593
594
  @lock.unlock
594
595
  end
595
- expired_timers.each do |timer|
596
- timer.fulfill
596
+ expired_timers.each do |t|
597
+ t.fulfill
597
598
  end
598
599
  end
599
600
  end
@@ -604,4 +605,4 @@ module Ione
604
605
  end
605
606
  end
606
607
  end
607
- end
608
+ end
@@ -12,6 +12,7 @@ module Ione
12
12
  @socket_impl = socket_impl
13
13
  @ssl_context = ssl_context
14
14
  @raw_io = io
15
+ @io = nil
15
16
  @connected_promise = Promise.new
16
17
  on_closed(&method(:cleanup_on_close))
17
18
  end
data/lib/ione/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Ione
4
- VERSION = '1.2.2'.freeze
4
+ VERSION = '1.2.5'.freeze
5
5
  end
@@ -37,7 +37,7 @@ describe 'An IO reactor' do
37
37
  fake_server.await_connects(1)
38
38
  fake_server.broadcast('hello world')
39
39
  await { protocol_handler.data.bytesize > 0 }
40
- protocol_handler.data.should == 'hello world'
40
+ protocol_handler.data.should eq('hello world')
41
41
  end
42
42
 
43
43
  it 'receives data on multiple connections' do
@@ -45,7 +45,7 @@ describe 'An IO reactor' do
45
45
  fake_server.await_connects(10)
46
46
  fake_server.broadcast('hello world')
47
47
  await { protocol_handlers.all? { |c| c.data.bytesize > 0 } }
48
- protocol_handlers.sample.data.should == 'hello world'
48
+ protocol_handlers.sample.data.should eq('hello world')
49
49
  end
50
50
  end
51
51
 
@@ -81,7 +81,7 @@ describe 'An IO reactor' do
81
81
  socket = TCPSocket.new(ENV['SERVER_HOST'], port)
82
82
  socket.puts('HELLO')
83
83
  result = socket.read(5)
84
- result.should == 'HELLO'
84
+ result.should eq('HELLO')
85
85
  socket.close
86
86
  end
87
87
  end
@@ -49,6 +49,7 @@ describe 'SSL' do
49
49
  ssl_context = OpenSSL::SSL::SSLContext.new
50
50
  ssl_context.key = OpenSSL::PKey::RSA.new(ssl_key)
51
51
  ssl_context.cert = OpenSSL::X509::Certificate.new(ssl_cert)
52
+ ssl_context.tmp_dh_callback = proc { SslSpec::DH_PARAMS }
52
53
 
53
54
  f = io_reactor.start
54
55
  f = f.flat_map do
@@ -80,8 +81,8 @@ describe 'SSL' do
80
81
  end
81
82
  client.write('hello world')
82
83
  response_received.future.value
83
- server_received_data.to_s.should == 'hello world'
84
- client_received_data.to_s.should == 'dlrow olleh'
84
+ server_received_data.to_s.should eq('hello world')
85
+ client_received_data.to_s.should eq('dlrow olleh')
85
86
  end
86
87
 
87
88
  it 'fails to send a message when not using encryption' do
@@ -95,3 +96,7 @@ describe 'SSL' do
95
96
  client.should be_closed
96
97
  end
97
98
  end
99
+
100
+ module SslSpec
101
+ DH_PARAMS = OpenSSL::PKey::DH.new(File.read(File.expand_path('../../resources/dh.pem', __FILE__)))
102
+ end
@@ -15,28 +15,28 @@ module Ione
15
15
  end
16
16
 
17
17
  it 'can be initialized with bytes' do
18
- described_class.new('hello').length.should == 5
18
+ described_class.new('hello').length.should eq(5)
19
19
  end
20
20
  end
21
21
 
22
22
  describe '#length/#size/#bytesize' do
23
23
  it 'returns the number of bytes in the buffer' do
24
24
  buffer << 'foo'
25
- buffer.length.should == 3
25
+ buffer.length.should eq(3)
26
26
  end
27
27
 
28
28
  it 'is zero initially' do
29
- buffer.length.should == 0
29
+ buffer.length.should eq(0)
30
30
  end
31
31
 
32
32
  it 'is aliased as #size' do
33
33
  buffer << 'foo'
34
- buffer.size.should == 3
34
+ buffer.size.should eq(3)
35
35
  end
36
36
 
37
37
  it 'is aliased as #bytesize' do
38
38
  buffer << 'foo'
39
- buffer.bytesize.should == 3
39
+ buffer.bytesize.should eq(3)
40
40
  end
41
41
  end
42
42
 
@@ -67,19 +67,19 @@ module Ione
67
67
  end
68
68
 
69
69
  it 'stores its bytes as binary' do
70
- buffer.append('hällö').length.should == 7
71
- buffer.to_s.encoding.should == ::Encoding::BINARY
70
+ buffer.append('hällö').length.should eq(7)
71
+ buffer.to_s.encoding.should eq(::Encoding::BINARY)
72
72
  end
73
73
 
74
74
  it 'handles appending with multibyte strings' do
75
75
  buffer.append('hello')
76
76
  buffer.append('würld')
77
- buffer.to_s.should == 'hellowürld'.force_encoding(::Encoding::BINARY)
77
+ buffer.to_s.should eq('hellowürld'.force_encoding(::Encoding::BINARY))
78
78
  end
79
79
 
80
80
  it 'handles appending with another byte buffer' do
81
81
  buffer.append('hello ').append(ByteBuffer.new('world'))
82
- buffer.to_s.should == 'hello world'
82
+ buffer.to_s.should eq('hello world')
83
83
  end
84
84
  end
85
85
 
@@ -105,7 +105,7 @@ module Ione
105
105
  b2 = described_class.new
106
106
  b1.append('foo')
107
107
  b2.append('foo')
108
- b1.should == b2
108
+ b1.should eq(b2)
109
109
  end
110
110
 
111
111
  it 'is equal to another buffer when both are empty' do
@@ -121,7 +121,7 @@ module Ione
121
121
  b2 = described_class.new
122
122
  b1.append('foo')
123
123
  b2.append('foo')
124
- b1.hash.should == b2.hash
124
+ b1.hash.should eq(b2.hash)
125
125
  end
126
126
 
127
127
  it 'is not equal to the hash code of another buffer with other contents' do
@@ -135,26 +135,26 @@ module Ione
135
135
  it 'is equal to the hash code of another buffer when both are empty' do
136
136
  b1 = described_class.new
137
137
  b2 = described_class.new
138
- b1.hash.should == b2.hash
138
+ b1.hash.should eq(b2.hash)
139
139
  end
140
140
  end
141
141
 
142
142
  describe '#to_s' do
143
143
  it 'returns the bytes' do
144
- buffer.append('hello world').to_s.should == 'hello world'
144
+ buffer.append('hello world').to_s.should eq('hello world')
145
145
  end
146
146
  end
147
147
 
148
148
  describe '#to_str' do
149
149
  it 'returns the bytes' do
150
- buffer.append('hello world').to_str.should == 'hello world'
150
+ buffer.append('hello world').to_str.should eq('hello world')
151
151
  end
152
152
  end
153
153
 
154
154
  describe '#inspect' do
155
155
  it 'returns the bytes wrapped in ByteBuffer(...)' do
156
156
  buffer.append("\xca\xfe")
157
- buffer.inspect.should == '#<Ione::ByteBuffer: "\xCA\xFE">'
157
+ buffer.inspect.should eq('#<Ione::ByteBuffer: "\xCA\xFE">')
158
158
  end
159
159
  end
160
160
 
@@ -162,12 +162,12 @@ module Ione
162
162
  it 'discards the specified number of bytes from the front of the buffer' do
163
163
  buffer.append('hello world')
164
164
  buffer.discard(4)
165
- buffer.should == ByteBuffer.new('o world')
165
+ buffer.should eq(ByteBuffer.new('o world'))
166
166
  end
167
167
 
168
168
  it 'returns the byte buffer' do
169
169
  buffer.append('hello world')
170
- buffer.discard(4).should == ByteBuffer.new('o world')
170
+ buffer.discard(4).should eq(ByteBuffer.new('o world'))
171
171
  end
172
172
 
173
173
  it 'raises an error if the number of bytes in the buffer is fewer than the number to discard' do
@@ -185,14 +185,14 @@ module Ione
185
185
  describe '#read' do
186
186
  it 'returns the specified number of bytes, as a string' do
187
187
  buffer.append('hello')
188
- buffer.read(4).should == 'hell'
188
+ buffer.read(4).should eq('hell')
189
189
  end
190
190
 
191
191
  it 'removes the bytes from the buffer' do
192
192
  buffer.append('hello')
193
193
  buffer.read(3)
194
- buffer.should == ByteBuffer.new('lo')
195
- buffer.read(2).should == 'lo'
194
+ buffer.should eq(ByteBuffer.new('lo'))
195
+ buffer.read(2).should eq('lo')
196
196
  end
197
197
 
198
198
  it 'raises an error if there are not enough bytes' do
@@ -208,22 +208,22 @@ module Ione
208
208
 
209
209
  it 'returns a string with binary encoding' do
210
210
  buffer.append('hello')
211
- buffer.read(4).encoding.should == ::Encoding::BINARY
211
+ buffer.read(4).encoding.should eq(::Encoding::BINARY)
212
212
  buffer.append('∆')
213
- buffer.read(2).encoding.should == ::Encoding::BINARY
213
+ buffer.read(2).encoding.should eq(::Encoding::BINARY)
214
214
  end
215
215
  end
216
216
 
217
217
  describe '#read_int' do
218
218
  it 'returns the first four bytes interpreted as an int' do
219
219
  buffer.append("\xca\xfe\xba\xbe\x01")
220
- buffer.read_int.should == 0xcafebabe
220
+ buffer.read_int.should eq(0xcafebabe)
221
221
  end
222
222
 
223
223
  it 'removes the bytes from the buffer' do
224
224
  buffer.append("\xca\xfe\xba\xbe\x01")
225
225
  buffer.read_int
226
- buffer.should == ByteBuffer.new("\x01")
226
+ buffer.should eq(ByteBuffer.new("\x01"))
227
227
  end
228
228
 
229
229
  it 'raises an error if there are not enough bytes' do
@@ -235,13 +235,13 @@ module Ione
235
235
  describe '#read_short' do
236
236
  it 'returns the first two bytes interpreted as a short' do
237
237
  buffer.append("\xca\xfe\x01")
238
- buffer.read_short.should == 0xcafe
238
+ buffer.read_short.should eq(0xcafe)
239
239
  end
240
240
 
241
241
  it 'removes the bytes from the buffer' do
242
242
  buffer.append("\xca\xfe\x01")
243
243
  buffer.read_short
244
- buffer.should == ByteBuffer.new("\x01")
244
+ buffer.should eq(ByteBuffer.new("\x01"))
245
245
  end
246
246
 
247
247
  it 'raises an error if there are not enough bytes' do
@@ -253,14 +253,14 @@ module Ione
253
253
  describe '#read_byte' do
254
254
  it 'returns the first bytes interpreted as an int' do
255
255
  buffer.append("\x10\x01")
256
- buffer.read_byte.should == 0x10
257
- buffer.read_byte.should == 0x01
256
+ buffer.read_byte.should eq(0x10)
257
+ buffer.read_byte.should eq(0x01)
258
258
  end
259
259
 
260
260
  it 'removes the byte from the buffer' do
261
261
  buffer.append("\x10\x01")
262
262
  buffer.read_byte
263
- buffer.should == ByteBuffer.new("\x01")
263
+ buffer.should eq(ByteBuffer.new("\x01"))
264
264
  end
265
265
 
266
266
  it 'raises an error if there are no bytes' do
@@ -269,8 +269,8 @@ module Ione
269
269
 
270
270
  it 'can interpret the byte as signed' do
271
271
  buffer.append("\x81\x02")
272
- buffer.read_byte(true).should == -127
273
- buffer.read_byte(true).should == 2
272
+ buffer.read_byte(true).should eq(-127)
273
+ buffer.read_byte(true).should eq(2)
274
274
  end
275
275
  end
276
276
 
@@ -278,14 +278,14 @@ module Ione
278
278
  it 'changes the bytes at the specified location' do
279
279
  buffer.append('foo bar')
280
280
  buffer.update(4, 'baz')
281
- buffer.to_s.should == 'foo baz'
281
+ buffer.to_s.should eq('foo baz')
282
282
  end
283
283
 
284
284
  it 'handles updates after a read' do
285
285
  buffer.append('foo bar')
286
286
  buffer.read(1)
287
287
  buffer.update(3, 'baz')
288
- buffer.to_s.should == 'oo baz'
288
+ buffer.to_s.should eq('oo baz')
289
289
  end
290
290
 
291
291
  it 'handles updates after multiple reads and appends' do
@@ -295,7 +295,7 @@ module Ione
295
295
  buffer.update(4, 'baz')
296
296
  buffer.append('yyyy')
297
297
  buffer.read(1)
298
- buffer.to_s.should == 'o bbazyyyy'
298
+ buffer.to_s.should eq('o bbazyyyy')
299
299
  end
300
300
 
301
301
  it 'returns itself' do
@@ -335,12 +335,12 @@ module Ione
335
335
  describe '#index' do
336
336
  it 'returns the first index of the specified substring' do
337
337
  buffer.append('fizz buzz')
338
- buffer.index('zz').should == 2
338
+ buffer.index('zz').should eq(2)
339
339
  end
340
340
 
341
341
  it 'returns the first index of the specified substring, after the specified index' do
342
342
  buffer.append('fizz buzz')
343
- buffer.index('zz', 3).should == 7
343
+ buffer.index('zz', 3).should eq(7)
344
344
  end
345
345
 
346
346
  it 'returns nil when the substring is not found' do
@@ -352,7 +352,21 @@ module Ione
352
352
  buffer.append('foo bar')
353
353
  buffer.read(1)
354
354
  buffer.append(' baz baz')
355
- buffer.index('baz', 8).should == 11
355
+ buffer.index('baz', 8).should eq(11)
356
+ end
357
+
358
+ it 'returns the first index when the matching substring spans the read and write buffer' do
359
+ buffer.append('foo bar')
360
+ buffer.read(1)
361
+ buffer.append('bar barbar')
362
+ buffer.index('barbar', 0).should eq(3)
363
+ end
364
+
365
+ it 'returns nil when the substring does not fit in the search space' do
366
+ buffer.append('foo')
367
+ buffer.read(1)
368
+ buffer.append('bar')
369
+ buffer.index('bar', 3).should be_nil
356
370
  end
357
371
  end
358
372