async-io 1.27.6 → 1.30.1

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/io/host_endpoint.rb +1 -1
  3. data/lib/async/io/stream.rb +16 -42
  4. data/{spec/async/io/trap_spec.rb → lib/async/io/threads.rb} +37 -26
  5. data/lib/async/io/trap.rb +8 -3
  6. data/lib/async/io/version.rb +1 -1
  7. metadata +50 -99
  8. data/.editorconfig +0 -6
  9. data/.gitignore +0 -13
  10. data/.rspec +0 -3
  11. data/.travis.yml +0 -25
  12. data/.yardopts +0 -2
  13. data/Gemfile +0 -20
  14. data/README.md +0 -171
  15. data/async-io.gemspec +0 -30
  16. data/examples/allocations/byteslice.rb +0 -29
  17. data/examples/allocations/memory.rb +0 -16
  18. data/examples/allocations/read_chunks.rb +0 -18
  19. data/examples/chat/client.rb +0 -58
  20. data/examples/chat/server.rb +0 -83
  21. data/examples/defer/worker.rb +0 -29
  22. data/examples/echo/client.rb +0 -23
  23. data/examples/echo/server.rb +0 -58
  24. data/examples/issues/broken_ssl.rb +0 -15
  25. data/examples/issues/pipes.rb +0 -34
  26. data/examples/millions/client.rb +0 -44
  27. data/examples/millions/server.rb +0 -41
  28. data/examples/udp/client.rb +0 -14
  29. data/examples/udp/server.rb +0 -16
  30. data/gems/nio4r-2.3.gemfile +0 -3
  31. data/spec/addrinfo.rb +0 -16
  32. data/spec/async/io/buffer_spec.rb +0 -48
  33. data/spec/async/io/c10k_spec.rb +0 -138
  34. data/spec/async/io/echo_spec.rb +0 -75
  35. data/spec/async/io/endpoint_spec.rb +0 -105
  36. data/spec/async/io/generic_examples.rb +0 -73
  37. data/spec/async/io/generic_spec.rb +0 -107
  38. data/spec/async/io/notification_spec.rb +0 -46
  39. data/spec/async/io/protocol/line_spec.rb +0 -81
  40. data/spec/async/io/shared_endpoint/server_spec.rb +0 -72
  41. data/spec/async/io/shared_endpoint_spec.rb +0 -67
  42. data/spec/async/io/socket/tcp_spec.rb +0 -101
  43. data/spec/async/io/socket/udp_spec.rb +0 -65
  44. data/spec/async/io/socket_spec.rb +0 -149
  45. data/spec/async/io/ssl_server_spec.rb +0 -133
  46. data/spec/async/io/ssl_socket_spec.rb +0 -96
  47. data/spec/async/io/standard_spec.rb +0 -47
  48. data/spec/async/io/stream_context.rb +0 -30
  49. data/spec/async/io/stream_spec.rb +0 -337
  50. data/spec/async/io/tcp_socket_spec.rb +0 -84
  51. data/spec/async/io/udp_socket_spec.rb +0 -56
  52. data/spec/async/io/unix_endpoint_spec.rb +0 -106
  53. data/spec/async/io/unix_socket_spec.rb +0 -66
  54. data/spec/async/io/wrap/http_rb_spec.rb +0 -47
  55. data/spec/async/io/wrap/tcp_spec.rb +0 -79
  56. data/spec/spec_helper.rb +0 -15
@@ -1,133 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async/io/ssl_socket'
24
- require 'async/io/ssl_endpoint'
25
-
26
- require 'async/rspec/ssl'
27
- require_relative 'generic_examples'
28
-
29
- RSpec.describe Async::IO::SSLServer do
30
- include_context Async::RSpec::Reactor
31
-
32
- context 'single host' do
33
- include_context Async::RSpec::SSL::VerifiedContexts
34
- include_context Async::RSpec::SSL::ValidCertificate
35
-
36
- let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6780, reuse_port: true)}
37
- let(:server_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: server_context)}
38
- let(:client_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: client_context)}
39
-
40
- let(:data) {"What one programmer can do in one month, two programmers can do in two months."}
41
-
42
- it "can see through to address" do
43
- expect(server_endpoint.address).to be == endpoint.address
44
- end
45
-
46
- it 'can accept_each connections' do
47
- # Accept a single incoming connection and then finish.
48
- server_task = reactor.async do |task|
49
- server_endpoint.bind do |server|
50
- server.listen(10)
51
-
52
- server.accept_each do |peer, address|
53
- data = peer.read(512)
54
- peer.write(data)
55
- end
56
- end
57
- end
58
-
59
- reactor.async do
60
- client_endpoint.connect do |client|
61
- client.write(data)
62
- client.close_write
63
-
64
- expect(client.read(512)).to be == data
65
- end
66
-
67
- server_task.stop
68
- end
69
- end
70
- end
71
-
72
- context 'multiple hosts' do
73
- let(:hosts) {['test.com', 'example.com']}
74
-
75
- include_context Async::RSpec::SSL::HostCertificates
76
-
77
- let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6782, reuse_port: true)}
78
- let(:server_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: server_context)}
79
- let(:valid_client_endpoint) {Async::IO::SSLEndpoint.new(endpoint, hostname: 'example.com', ssl_context: client_context)}
80
- let(:invalid_client_endpoint) {Async::IO::SSLEndpoint.new(endpoint, hostname: 'fleeb.com', ssl_context: client_context)}
81
-
82
- let(:data) {"What one programmer can do in one month, two programmers can do in two months."}
83
-
84
- it 'can select correct host' do
85
- # Accept a single incoming connection and then finish.
86
- server_task = reactor.async do |task|
87
- server_endpoint.bind do |server|
88
- server.listen(10)
89
-
90
- server.accept_each do |peer, address|
91
- expect(peer.hostname).to be == 'example.com'
92
-
93
- data = peer.read(512)
94
- peer.write(data)
95
- end
96
- end
97
- end
98
-
99
- reactor.async do
100
- valid_client_endpoint.connect do |client|
101
- client.write(data)
102
- client.close_write
103
-
104
- expect(client.read(512)).to be == data
105
- end
106
-
107
- server_task.stop
108
- end
109
- end
110
-
111
- it 'it fails with invalid host' do
112
- # Accept a single incoming connection and then finish.
113
- server_task = reactor.async do |task|
114
- server_endpoint.bind do |server|
115
- server.listen(10)
116
-
117
- server.accept_each do |peer, address|
118
- peer.close
119
- end
120
- end
121
- end
122
-
123
- reactor.async do
124
- expect do
125
- invalid_client_endpoint.connect do |client|
126
- end
127
- end.to raise_exception(OpenSSL::SSL::SSLError, /handshake failure/)
128
-
129
- server_task.stop
130
- end.wait
131
- end
132
- end
133
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async/io/ssl_endpoint'
24
-
25
- require 'async/rspec/ssl'
26
- require_relative 'generic_examples'
27
-
28
- RSpec.describe Async::IO::SSLSocket do
29
- include_context Async::RSpec::Reactor
30
- include_context Async::RSpec::SSL::VerifiedContexts
31
-
32
- it_should_behave_like Async::IO::Generic
33
-
34
- # Shared port for localhost network tests.
35
- let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6779, reuse_port: true, timeout: 10)}
36
- let(:server_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: server_context, timeout: 20)}
37
- let(:client_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: client_context, timeout: 20)}
38
-
39
- let(:data) {"The quick brown fox jumped over the lazy dog."}
40
-
41
- let(:server_task) do
42
- # Accept a single incoming connection and then finish.
43
- reactor.async do |task|
44
- server_endpoint.bind do |server|
45
- server.listen(10)
46
-
47
- begin
48
- server.accept do |peer, address|
49
- expect(peer.timeout).to be == 10
50
-
51
- data = peer.read(512)
52
- peer.write(data)
53
- end
54
- rescue OpenSSL::SSL::SSLError
55
- # ignore.
56
- end
57
- end
58
- end
59
- end
60
-
61
- describe "#connect" do
62
- context "with a trusted certificate" do
63
- include_context Async::RSpec::SSL::ValidCertificate
64
-
65
- it "should start server and send data" do
66
- server_task
67
-
68
- reactor.async do
69
- client_endpoint.connect do |client|
70
- expect(client).to be_connected
71
- expect(client.timeout).to be == 10
72
-
73
- client.write(data)
74
- client.close_write
75
-
76
- expect(client.read(512)).to be == data
77
- end
78
- end
79
- end
80
- end
81
-
82
- context "with an untrusted certificate" do
83
- include_context Async::RSpec::SSL::InvalidCertificate
84
-
85
- it "should fail to connect" do
86
- server_task
87
-
88
- reactor.async do
89
- expect do
90
- client_endpoint.connect
91
- end.to raise_exception(OpenSSL::SSL::SSLError)
92
- end.wait
93
- end
94
- end
95
- end
96
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async/io/standard'
24
-
25
- RSpec.describe Async::IO::STDIN do
26
- include_context Async::RSpec::Reactor
27
-
28
- it "should be able to read" do
29
- expect(subject.read(0)).to be == ""
30
- end
31
- end
32
-
33
- RSpec.describe Async::IO::STDOUT do
34
- include_context Async::RSpec::Reactor
35
-
36
- it "should be able to write" do
37
- expect(subject.write("")).to be == 0
38
- end
39
- end
40
-
41
- RSpec.describe Async::IO::STDERR do
42
- include_context Async::RSpec::Reactor
43
-
44
- it "should be able to write" do
45
- expect(subject.write("")).to be == 0
46
- end
47
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async/rspec/buffer'
24
- require 'async/io/stream'
25
-
26
- RSpec.shared_context Async::IO::Stream do
27
- include_context Async::RSpec::Buffer
28
- subject {described_class.new(buffer)}
29
- let(:io) {subject.io}
30
- end
@@ -1,337 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require 'async/io/socket'
24
- require 'async/clock'
25
-
26
- require_relative 'generic_examples'
27
- require_relative 'stream_context'
28
-
29
- RSpec.describe Async::IO::Stream do
30
- # This constant is part of the public interface, but was renamed to `Async::IO::BLOCK_SIZE`.
31
- describe "::BLOCK_SIZE" do
32
- it "should exist and be reasonable" do
33
- expect(Async::IO::Stream::BLOCK_SIZE).to be_between(1024, 1024*128)
34
- end
35
- end
36
-
37
- context "socket I/O" do
38
- let(:sockets) do
39
- @sockets = Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)
40
- end
41
-
42
- after do
43
- @sockets&.each(&:close)
44
- end
45
-
46
- let(:io) {sockets.first}
47
- subject {described_class.new(sockets.last)}
48
-
49
- it_should_behave_like Async::IO
50
-
51
- describe '#drain_write_buffer' do
52
- include_context Async::RSpec::Reactor
53
- let(:output) {described_class.new(sockets.last)}
54
- subject {described_class.new(sockets.first)}
55
-
56
- let(:buffer_size) {1024*6}
57
-
58
- it "can interleave calls to flush" do
59
- tasks = 2.times.map do |i|
60
- reactor.async do
61
- buffer = i.to_s * buffer_size
62
- 128.times do
63
- output.write(buffer)
64
- output.flush
65
- end
66
- end
67
- end
68
-
69
- reactor.async do
70
- tasks.each(&:wait)
71
- output.close
72
- end
73
-
74
- Async::Task.current.sleep(1)
75
-
76
- while buffer = subject.read(buffer_size)
77
- expect(buffer).to be == (buffer[0] * buffer_size)
78
- end
79
- end
80
-
81
- it "handles write failures" do
82
- subject.close
83
-
84
- task = reactor.async do
85
- output.write("Hello World")
86
- output.flush(deferred: true)
87
- end
88
-
89
- expect do
90
- task.wait
91
- end.to raise_error(Errno::EPIPE)
92
-
93
- write_buffer = output.instance_variable_get(:@write_buffer)
94
- drain_buffer = output.instance_variable_get(:@drain_buffer)
95
-
96
- expect(write_buffer).to be_empty
97
- expect(drain_buffer).to be_empty
98
- end
99
- end
100
-
101
- describe '#close_read' do
102
- subject {described_class.new(sockets.last)}
103
-
104
- it "can close the reading end of the stream" do
105
- expect(subject.io).to receive(:close_read).and_call_original
106
-
107
- subject.close_read
108
-
109
- # Ruby <= 2.4 raises an exception even with exception: false
110
- # expect(stream.read).to be_nil
111
- end
112
-
113
- it "can close the writing end of the stream" do
114
- expect(subject.io).to receive(:close_write).and_call_original
115
-
116
- subject.write("Oh yes!")
117
- subject.close_write
118
-
119
- expect do
120
- subject.write("Oh no!")
121
- subject.flush
122
- end.to raise_error(IOError, /not opened for writing/)
123
- end
124
- end
125
-
126
- describe '#read_exactly' do
127
- it "can read several bytes" do
128
- io.write("hello\nworld\n")
129
-
130
- expect(subject.read_exactly(4)).to be == 'hell'
131
- end
132
-
133
- it "can raise exception if io is eof" do
134
- io.close
135
-
136
- expect do
137
- subject.read_exactly(4)
138
- end.to raise_error(EOFError)
139
- end
140
- end
141
- end
142
-
143
- context "performance (BLOCK_SIZE: #{Async::IO::BLOCK_SIZE} MAXIMUM_READ_SIZE: #{Async::IO::MAXIMUM_READ_SIZE})" do
144
- include_context Async::RSpec::Reactor
145
-
146
- let!(:stream) {described_class.open("/dev/zero")}
147
- after {stream.close}
148
-
149
- it "can read data quickly" do |example|
150
- data = nil
151
-
152
- duration = Async::Clock.measure do
153
- data = stream.read(1024**3)
154
- end
155
-
156
- size = data.bytesize / 1024**2
157
- rate = size / duration
158
-
159
- example.reporter.message "Read #{size.round(2)}MB of data at #{rate.round(2)}MB/s."
160
-
161
- expect(rate).to be > 128
162
- end
163
- end
164
-
165
- context "buffered I/O" do
166
- include_context Async::IO::Stream
167
- include_context Async::RSpec::Memory
168
- include_context Async::RSpec::Reactor
169
-
170
- describe '#read' do
171
- it "should read everything" do
172
- io.write "Hello World"
173
- io.seek(0)
174
-
175
- expect(subject.io).to receive(:read_nonblock).and_call_original.twice
176
-
177
- expect(subject.read).to be == "Hello World"
178
- expect(subject).to be_eof
179
- end
180
-
181
- it "should read only the amount requested" do
182
- io.write "Hello World"
183
- io.seek(0)
184
-
185
- expect(subject.io).to receive(:read_nonblock).and_call_original.once
186
-
187
- expect(subject.read_partial(4)).to be == "Hell"
188
- expect(subject).to_not be_eof
189
-
190
- expect(subject.read_partial(20)).to be == "o World"
191
- expect(subject).to be_eof
192
- end
193
-
194
- context "with large content" do
195
- it "allocates expected amount of bytes" do
196
- io.write("." * 16*1024)
197
- io.seek(0)
198
-
199
- buffer = nil
200
-
201
- expect do
202
- # The read buffer is already allocated, and it will be resized to fit the incoming data. It will be swapped with an empty buffer.
203
- buffer = subject.read(16*1024)
204
- end.to limit_allocations.of(String, count: 1, size: 0)
205
-
206
- expect(buffer.size).to be == 16*1024
207
- end
208
- end
209
- end
210
-
211
- describe '#read_until' do
212
- it "can read a line" do
213
- io.write("hello\nworld\n")
214
- io.seek(0)
215
-
216
- expect(subject.read_until("\n")).to be == 'hello'
217
- expect(subject.read_until("\n")).to be == 'world'
218
- expect(subject.read_until("\n")).to be_nil
219
- end
220
-
221
- context "with 1-byte block size" do
222
- subject! {Async::IO::Stream.new(buffer, block_size: 1)}
223
-
224
- it "can read a line with a multi-byte pattern" do
225
- io.write("hello\r\nworld\r\n")
226
- io.seek(0)
227
-
228
- expect(subject.read_until("\r\n")).to be == 'hello'
229
- expect(subject.read_until("\r\n")).to be == 'world'
230
- expect(subject.read_until("\r\n")).to be_nil
231
- end
232
- end
233
-
234
- context "with large content" do
235
- it "allocates expected amount of bytes" do
236
- subject
237
-
238
- expect do
239
- subject.read_until("b")
240
- end.to limit_allocations.of(String, size: 0, count: 1)
241
- end
242
- end
243
- end
244
-
245
- describe '#flush' do
246
- it "should not call write if write buffer is empty" do
247
- expect(subject.io).to_not receive(:write)
248
-
249
- subject.flush
250
- end
251
-
252
- it "should flush underlying data when it exceeds block size" do
253
- expect(subject.io).to receive(:write).and_call_original.once
254
-
255
- subject.block_size.times do
256
- subject.write("!")
257
- end
258
- end
259
- end
260
-
261
- describe '#read_partial' do
262
- before(:each) do
263
- string = "Hello World!"
264
-
265
- io.write(string * (1 + (Async::IO::BLOCK_SIZE / string.bytesize)))
266
- io.seek(0)
267
- end
268
-
269
- it "should avoid calling read" do
270
- expect(subject.io).to receive(:read_nonblock).and_call_original.once
271
-
272
- expect(subject.read_partial(12)).to be == "Hello World!"
273
- end
274
-
275
- context "with large content" do
276
- it "allocates only the amount required" do
277
- expect do
278
- subject.read(4*1024)
279
- end.to limit_allocations.of(String, count: 2, size: 4*1024+1)
280
- end
281
-
282
- it "allocates exact number of bytes being read" do
283
- expect do
284
- subject.read_partial(subject.block_size * 2)
285
- end.to limit_allocations.of(String, count: 1, size: 0)
286
- end
287
-
288
- it "allocates expected amount of bytes" do
289
- buffer = nil
290
-
291
- expect do
292
- buffer = subject.read_partial
293
- end.to limit_allocations.of(String, count: 1)
294
-
295
- expect(buffer.size).to be == subject.block_size
296
- end
297
- end
298
- end
299
-
300
- describe '#write' do
301
- it "should read one line" do
302
- expect(subject.io).to receive(:write).and_call_original.once
303
-
304
- subject.write "Hello World\n"
305
- subject.flush
306
-
307
- io.seek(0)
308
- expect(subject.read).to be == "Hello World\n"
309
- end
310
- end
311
-
312
- describe '#eof' do
313
- it "should terminate subject" do
314
- expect do
315
- subject.eof!
316
- end.to raise_exception(EOFError)
317
-
318
- expect(subject).to be_eof
319
- end
320
- end
321
-
322
- describe '#close' do
323
- it 'can be closed even if underlying io is closed' do
324
- io.close
325
-
326
- expect(subject.io).to be_closed
327
-
328
- # Put some data in the write buffer
329
- subject.write "."
330
-
331
- expect do
332
- subject.close
333
- end.to_not raise_exception
334
- end
335
- end
336
- end
337
- end