polyphony 0.43.8
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.
- checksums.yaml +7 -0
- data/.gitbook.yaml +4 -0
- data/.github/workflows/test.yml +29 -0
- data/.gitignore +59 -0
- data/.rubocop.yml +175 -0
- data/CHANGELOG.md +393 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +141 -0
- data/LICENSE +21 -0
- data/README.md +51 -0
- data/Rakefile +26 -0
- data/TODO.md +201 -0
- data/bin/polyphony-debug +87 -0
- data/docs/_config.yml +64 -0
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/title.html +1 -0
- data/docs/_sass/custom/custom.scss +10 -0
- data/docs/_sass/overrides.scss +0 -0
- data/docs/_user-guide/all-about-timers.md +126 -0
- data/docs/_user-guide/index.md +9 -0
- data/docs/_user-guide/web-server.md +136 -0
- data/docs/api-reference/exception.md +27 -0
- data/docs/api-reference/fiber.md +425 -0
- data/docs/api-reference/index.md +9 -0
- data/docs/api-reference/io.md +36 -0
- data/docs/api-reference/object.md +99 -0
- data/docs/api-reference/polyphony-baseexception.md +33 -0
- data/docs/api-reference/polyphony-cancel.md +26 -0
- data/docs/api-reference/polyphony-moveon.md +24 -0
- data/docs/api-reference/polyphony-net.md +20 -0
- data/docs/api-reference/polyphony-process.md +28 -0
- data/docs/api-reference/polyphony-resourcepool.md +59 -0
- data/docs/api-reference/polyphony-restart.md +18 -0
- data/docs/api-reference/polyphony-terminate.md +18 -0
- data/docs/api-reference/polyphony-threadpool.md +67 -0
- data/docs/api-reference/polyphony-throttler.md +77 -0
- data/docs/api-reference/polyphony.md +36 -0
- data/docs/api-reference/thread.md +88 -0
- data/docs/assets/img/echo-fibers.svg +1 -0
- data/docs/assets/img/sleeping-fiber.svg +1 -0
- data/docs/faq.md +195 -0
- data/docs/favicon.ico +0 -0
- data/docs/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +34 -0
- data/docs/getting-started/overview.md +486 -0
- data/docs/getting-started/tutorial.md +359 -0
- data/docs/index.md +94 -0
- data/docs/main-concepts/concurrency.md +151 -0
- data/docs/main-concepts/design-principles.md +161 -0
- data/docs/main-concepts/exception-handling.md +291 -0
- data/docs/main-concepts/extending.md +89 -0
- data/docs/main-concepts/fiber-scheduling.md +197 -0
- data/docs/main-concepts/index.md +9 -0
- data/docs/polyphony-logo.png +0 -0
- data/examples/adapters/concurrent-ruby.rb +9 -0
- data/examples/adapters/pg_client.rb +36 -0
- data/examples/adapters/pg_notify.rb +35 -0
- data/examples/adapters/pg_pool.rb +43 -0
- data/examples/adapters/pg_transaction.rb +31 -0
- data/examples/adapters/redis_blpop.rb +12 -0
- data/examples/adapters/redis_channels.rb +122 -0
- data/examples/adapters/redis_client.rb +19 -0
- data/examples/adapters/redis_pubsub.rb +26 -0
- data/examples/adapters/redis_pubsub_perf.rb +68 -0
- data/examples/core/01-spinning-up-fibers.rb +18 -0
- data/examples/core/02-awaiting-fibers.rb +20 -0
- data/examples/core/03-interrupting.rb +39 -0
- data/examples/core/04-handling-signals.rb +19 -0
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-at_exit.rb +29 -0
- data/examples/core/xx-caller.rb +12 -0
- data/examples/core/xx-channels.rb +45 -0
- data/examples/core/xx-daemon.rb +14 -0
- data/examples/core/xx-deadlock.rb +8 -0
- data/examples/core/xx-deferring-an-operation.rb +14 -0
- data/examples/core/xx-erlang-style-genserver.rb +81 -0
- data/examples/core/xx-exception-backtrace.rb +40 -0
- data/examples/core/xx-fork-cleanup.rb +22 -0
- data/examples/core/xx-fork-spin.rb +42 -0
- data/examples/core/xx-fork-terminate.rb +27 -0
- data/examples/core/xx-forking.rb +24 -0
- data/examples/core/xx-move_on.rb +23 -0
- data/examples/core/xx-pingpong.rb +18 -0
- data/examples/core/xx-queue-async.rb +120 -0
- data/examples/core/xx-readpartial.rb +18 -0
- data/examples/core/xx-recurrent-timer.rb +12 -0
- data/examples/core/xx-resource_delegate.rb +31 -0
- data/examples/core/xx-signals.rb +16 -0
- data/examples/core/xx-sleep-forever.rb +9 -0
- data/examples/core/xx-sleeping.rb +25 -0
- data/examples/core/xx-snooze-starve.rb +16 -0
- data/examples/core/xx-spin-fork.rb +49 -0
- data/examples/core/xx-spin_error_backtrace.rb +33 -0
- data/examples/core/xx-state-machine.rb +51 -0
- data/examples/core/xx-stop.rb +20 -0
- data/examples/core/xx-supervise-process.rb +30 -0
- data/examples/core/xx-supervisors.rb +21 -0
- data/examples/core/xx-thread-selector-sleep.rb +51 -0
- data/examples/core/xx-thread-selector-snooze.rb +46 -0
- data/examples/core/xx-thread-sleep.rb +17 -0
- data/examples/core/xx-thread-snooze.rb +34 -0
- data/examples/core/xx-thread_pool.rb +17 -0
- data/examples/core/xx-throttling.rb +18 -0
- data/examples/core/xx-timeout.rb +10 -0
- data/examples/core/xx-timer-gc.rb +17 -0
- data/examples/core/xx-trace.rb +79 -0
- data/examples/core/xx-using-a-mutex.rb +21 -0
- data/examples/core/xx-worker-thread.rb +30 -0
- data/examples/io/tunnel.rb +48 -0
- data/examples/io/xx-backticks.rb +11 -0
- data/examples/io/xx-echo_client.rb +25 -0
- data/examples/io/xx-echo_client_from_stdin.rb +21 -0
- data/examples/io/xx-echo_pipe.rb +16 -0
- data/examples/io/xx-echo_server.rb +17 -0
- data/examples/io/xx-echo_server_with_timeout.rb +34 -0
- data/examples/io/xx-echo_stdin.rb +14 -0
- data/examples/io/xx-happy-eyeballs.rb +36 -0
- data/examples/io/xx-httparty.rb +38 -0
- data/examples/io/xx-irb.rb +17 -0
- data/examples/io/xx-net-http.rb +15 -0
- data/examples/io/xx-open.rb +16 -0
- data/examples/io/xx-switch.rb +15 -0
- data/examples/io/xx-system.rb +11 -0
- data/examples/io/xx-tcpserver.rb +15 -0
- data/examples/io/xx-tcpsocket.rb +18 -0
- data/examples/io/xx-zip.rb +19 -0
- data/examples/performance/fiber_transfer.rb +47 -0
- data/examples/performance/fs_read.rb +38 -0
- data/examples/performance/mem-usage.rb +56 -0
- data/examples/performance/messaging.rb +29 -0
- data/examples/performance/multi_snooze.rb +33 -0
- data/examples/performance/snooze.rb +39 -0
- data/examples/performance/snooze_raw.rb +39 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +74 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +45 -0
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
- data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
- data/examples/performance/thread-vs-fiber/xx-httparty_multi.rb +36 -0
- data/examples/performance/thread-vs-fiber/xx-httparty_threaded.rb +29 -0
- data/examples/performance/thread_pool_perf.rb +63 -0
- data/examples/performance/xx-array.rb +11 -0
- data/examples/performance/xx-fiber-switch.rb +9 -0
- data/examples/performance/xx-snooze.rb +15 -0
- data/examples/xx-spin.rb +32 -0
- data/ext/libev/Changes +548 -0
- data/ext/libev/LICENSE +37 -0
- data/ext/libev/README +59 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/ev.c +5279 -0
- data/ext/libev/ev.h +856 -0
- data/ext/libev/ev_epoll.c +296 -0
- data/ext/libev/ev_kqueue.c +224 -0
- data/ext/libev/ev_linuxaio.c +642 -0
- data/ext/libev/ev_poll.c +156 -0
- data/ext/libev/ev_port.c +192 -0
- data/ext/libev/ev_select.c +316 -0
- data/ext/libev/ev_vars.h +215 -0
- data/ext/libev/ev_win32.c +162 -0
- data/ext/libev/ev_wrap.h +216 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/ext/polyphony/extconf.rb +20 -0
- data/ext/polyphony/fiber.c +109 -0
- data/ext/polyphony/libev.c +2 -0
- data/ext/polyphony/libev.h +9 -0
- data/ext/polyphony/libev_agent.c +882 -0
- data/ext/polyphony/polyphony.c +71 -0
- data/ext/polyphony/polyphony.h +97 -0
- data/ext/polyphony/polyphony_ext.c +21 -0
- data/ext/polyphony/queue.c +168 -0
- data/ext/polyphony/ring_buffer.c +96 -0
- data/ext/polyphony/ring_buffer.h +28 -0
- data/ext/polyphony/thread.c +208 -0
- data/ext/polyphony/tracing.c +11 -0
- data/lib/polyphony.rb +136 -0
- data/lib/polyphony/adapters/fs.rb +19 -0
- data/lib/polyphony/adapters/irb.rb +52 -0
- data/lib/polyphony/adapters/postgres.rb +110 -0
- data/lib/polyphony/adapters/process.rb +33 -0
- data/lib/polyphony/adapters/redis.rb +67 -0
- data/lib/polyphony/adapters/trace.rb +138 -0
- data/lib/polyphony/core/channel.rb +46 -0
- data/lib/polyphony/core/exceptions.rb +36 -0
- data/lib/polyphony/core/global_api.rb +124 -0
- data/lib/polyphony/core/resource_pool.rb +117 -0
- data/lib/polyphony/core/sync.rb +21 -0
- data/lib/polyphony/core/thread_pool.rb +64 -0
- data/lib/polyphony/core/throttler.rb +41 -0
- data/lib/polyphony/event.rb +17 -0
- data/lib/polyphony/extensions/core.rb +174 -0
- data/lib/polyphony/extensions/fiber.rb +379 -0
- data/lib/polyphony/extensions/io.rb +221 -0
- data/lib/polyphony/extensions/openssl.rb +81 -0
- data/lib/polyphony/extensions/socket.rb +150 -0
- data/lib/polyphony/extensions/thread.rb +108 -0
- data/lib/polyphony/net.rb +77 -0
- data/lib/polyphony/version.rb +5 -0
- data/polyphony.gemspec +40 -0
- data/test/coverage.rb +54 -0
- data/test/eg.rb +27 -0
- data/test/helper.rb +56 -0
- data/test/q.rb +24 -0
- data/test/run.rb +5 -0
- data/test/stress.rb +25 -0
- data/test/test_agent.rb +130 -0
- data/test/test_event.rb +59 -0
- data/test/test_ext.rb +196 -0
- data/test/test_fiber.rb +988 -0
- data/test/test_global_api.rb +352 -0
- data/test/test_io.rb +249 -0
- data/test/test_kernel.rb +57 -0
- data/test/test_process_supervision.rb +46 -0
- data/test/test_queue.rb +112 -0
- data/test/test_resource_pool.rb +138 -0
- data/test/test_signal.rb +100 -0
- data/test/test_socket.rb +34 -0
- data/test/test_supervise.rb +103 -0
- data/test/test_thread.rb +170 -0
- data/test/test_thread_pool.rb +101 -0
- data/test/test_throttler.rb +50 -0
- data/test/test_trace.rb +68 -0
- metadata +482 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class SpinTest < MiniTest::Test
|
6
|
+
def test_that_spin_returns_a_fiber
|
7
|
+
result = nil
|
8
|
+
fiber = spin { result = 42 }
|
9
|
+
|
10
|
+
assert_kind_of Fiber, fiber
|
11
|
+
assert_nil result
|
12
|
+
suspend
|
13
|
+
assert_equal 42, result
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_that_spin_accepts_fiber_argument
|
17
|
+
result = nil
|
18
|
+
fiber = Fiber.current.spin { result = 42 }
|
19
|
+
|
20
|
+
assert_nil result
|
21
|
+
suspend
|
22
|
+
assert_equal 42, result
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_that_spined_fiber_saves_result
|
26
|
+
fiber = spin { 42 }
|
27
|
+
|
28
|
+
assert_kind_of Fiber, fiber
|
29
|
+
assert_nil fiber.result
|
30
|
+
suspend
|
31
|
+
assert_equal 42, fiber.result
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_that_spined_fiber_can_be_interrupted
|
35
|
+
fiber = spin do
|
36
|
+
sleep(1)
|
37
|
+
42
|
38
|
+
end
|
39
|
+
spin { fiber.interrupt }
|
40
|
+
suspend
|
41
|
+
assert_nil fiber.result
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_spin_without_tag
|
45
|
+
f = spin { }
|
46
|
+
assert_kind_of Fiber, f
|
47
|
+
assert_nil f.tag
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_spin_with_tag
|
51
|
+
f = spin(:foo) { }
|
52
|
+
assert_kind_of Fiber, f
|
53
|
+
assert_equal :foo, f.tag
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ExceptionTest < MiniTest::Test
|
58
|
+
def test_cross_fiber_backtrace
|
59
|
+
error = nil
|
60
|
+
frames = []
|
61
|
+
spin do
|
62
|
+
spin do
|
63
|
+
spin do
|
64
|
+
raise 'foo'
|
65
|
+
end
|
66
|
+
suspend
|
67
|
+
rescue Exception => e
|
68
|
+
frames << 2
|
69
|
+
raise e
|
70
|
+
end
|
71
|
+
suspend
|
72
|
+
rescue Exception => e
|
73
|
+
frames << 3
|
74
|
+
raise e
|
75
|
+
end#.await
|
76
|
+
5.times { snooze }
|
77
|
+
rescue Exception => e
|
78
|
+
error = e
|
79
|
+
ensure
|
80
|
+
assert_kind_of RuntimeError, error
|
81
|
+
assert_equal [2, 3], frames
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_cross_fiber_backtrace_with_dead_calling_fiber
|
85
|
+
error = nil
|
86
|
+
spin do
|
87
|
+
spin do
|
88
|
+
spin do
|
89
|
+
raise 'foo'
|
90
|
+
end.await
|
91
|
+
end.await
|
92
|
+
end.await
|
93
|
+
rescue Exception => e
|
94
|
+
error = e
|
95
|
+
ensure
|
96
|
+
assert_kind_of RuntimeError, error
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class MoveOnAfterTest < MiniTest::Test
|
101
|
+
def test_move_on_after
|
102
|
+
t0 = Time.now
|
103
|
+
v = move_on_after(0.01) do
|
104
|
+
sleep 1
|
105
|
+
:foo
|
106
|
+
end
|
107
|
+
t1 = Time.now
|
108
|
+
|
109
|
+
assert t1 - t0 < 0.1
|
110
|
+
assert_nil v
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_move_on_after_with_value
|
114
|
+
t0 = Time.now
|
115
|
+
v = move_on_after(0.01, with_value: :bar) do
|
116
|
+
sleep 1
|
117
|
+
:foo
|
118
|
+
end
|
119
|
+
t1 = Time.now
|
120
|
+
|
121
|
+
assert t1 - t0 < 0.1
|
122
|
+
assert_equal :bar, v
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_move_on_after_without_block
|
126
|
+
t0 = Time.now
|
127
|
+
f = move_on_after(0.01, with_value: 'foo')
|
128
|
+
assert_kind_of Fiber, f
|
129
|
+
assert_equal Fiber.current, f.parent
|
130
|
+
v = sleep 1
|
131
|
+
t1 = Time.now
|
132
|
+
assert t1 - t0 < 0.1
|
133
|
+
assert_equal 'foo', v
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class CancelAfterTest < MiniTest::Test
|
138
|
+
def test_cancel_after
|
139
|
+
t0 = Time.now
|
140
|
+
|
141
|
+
assert_raises Polyphony::Cancel do
|
142
|
+
cancel_after(0.01) do
|
143
|
+
sleep 1
|
144
|
+
:foo
|
145
|
+
end
|
146
|
+
end
|
147
|
+
t1 = Time.now
|
148
|
+
assert t1 - t0 < 0.1
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_cancel_after_without_block
|
152
|
+
t0 = Time.now
|
153
|
+
f = cancel_after(0.01)
|
154
|
+
assert_kind_of Fiber, f
|
155
|
+
assert_equal Fiber.current, f.parent
|
156
|
+
assert_raises Polyphony::Cancel do
|
157
|
+
sleep 1
|
158
|
+
end
|
159
|
+
t1 = Time.now
|
160
|
+
assert t1 - t0 < 0.1
|
161
|
+
end
|
162
|
+
|
163
|
+
class CustomException < Exception
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_cancel_after_with_custom_exception
|
167
|
+
assert_raises CustomException do
|
168
|
+
cancel_after(0.01, with_exception: CustomException) do
|
169
|
+
sleep 1
|
170
|
+
:foo
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
begin
|
175
|
+
e = nil
|
176
|
+
cancel_after(0.01, with_exception: 'foo') do
|
177
|
+
sleep 1
|
178
|
+
:foo
|
179
|
+
end
|
180
|
+
rescue => e
|
181
|
+
ensure
|
182
|
+
assert_kind_of RuntimeError, e
|
183
|
+
assert_equal 'foo', e.message
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
class SpinLoopTest < MiniTest::Test
|
190
|
+
def test_spin_loop
|
191
|
+
buffer = []
|
192
|
+
counter = 0
|
193
|
+
f = spin_loop do
|
194
|
+
buffer << (counter += 1)
|
195
|
+
snooze
|
196
|
+
end
|
197
|
+
|
198
|
+
assert_kind_of Fiber, f
|
199
|
+
assert_equal [], buffer
|
200
|
+
snooze
|
201
|
+
assert_equal [1], buffer
|
202
|
+
snooze
|
203
|
+
assert_equal [1, 2], buffer
|
204
|
+
snooze
|
205
|
+
assert_equal [1, 2, 3], buffer
|
206
|
+
f.stop
|
207
|
+
snooze
|
208
|
+
assert !f.running?
|
209
|
+
assert_equal [1, 2, 3], buffer
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_spin_loop_location
|
213
|
+
location = /^#{__FILE__}:#{__LINE__ + 1}/
|
214
|
+
f = spin_loop { snooze }
|
215
|
+
|
216
|
+
assert_match location, f.location
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_spin_loop_tag
|
220
|
+
f = spin_loop(:my_loop) { snooze }
|
221
|
+
|
222
|
+
assert_equal :my_loop, f.tag
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_spin_loop_with_rate
|
226
|
+
buffer = []
|
227
|
+
counter = 0
|
228
|
+
t0 = Time.now
|
229
|
+
f = spin_loop(rate: 10) { buffer << (counter += 1) }
|
230
|
+
sleep 0.2
|
231
|
+
f.stop
|
232
|
+
elapsed = Time.now - t0
|
233
|
+
expected = (elapsed * 10).to_i
|
234
|
+
assert counter >= expected - 1 && counter <= expected + 1
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class ThrottledLoopTest < MiniTest::Test
|
239
|
+
def test_throttled_loop
|
240
|
+
buffer = []
|
241
|
+
counter = 0
|
242
|
+
t0 = Time.now
|
243
|
+
f = spin do
|
244
|
+
throttled_loop(10) { buffer << (counter += 1) }
|
245
|
+
end
|
246
|
+
sleep 0.3
|
247
|
+
f.stop
|
248
|
+
elapsed = Time.now - t0
|
249
|
+
expected = (elapsed * 10).to_i
|
250
|
+
assert counter >= expected - 1 && counter <= expected + 1
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_throttled_loop_with_count
|
254
|
+
buffer = []
|
255
|
+
counter = 0
|
256
|
+
f = spin do
|
257
|
+
throttled_loop(50, count: 5) { buffer << (counter += 1) }
|
258
|
+
end
|
259
|
+
f.await
|
260
|
+
assert_equal [1, 2, 3, 4, 5], buffer
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class GlobalAPIEtcTest < MiniTest::Test
|
265
|
+
def test_after
|
266
|
+
buffer = []
|
267
|
+
f = after(0.001) { buffer << 2 }
|
268
|
+
snooze
|
269
|
+
assert_equal [], buffer
|
270
|
+
sleep 0.001
|
271
|
+
assert_equal [2], buffer
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_every
|
275
|
+
buffer = []
|
276
|
+
t0 = Time.now
|
277
|
+
f = spin do
|
278
|
+
every(0.1) { buffer << 1 }
|
279
|
+
end
|
280
|
+
sleep 0.5
|
281
|
+
f.stop
|
282
|
+
elapsed = Time.now - t0
|
283
|
+
expected = (elapsed / 0.1).to_i
|
284
|
+
assert buffer.size >= expected - 2 && buffer.size <= expected + 2
|
285
|
+
end
|
286
|
+
|
287
|
+
def test_sleep
|
288
|
+
t0 = Time.now
|
289
|
+
sleep 0.1
|
290
|
+
elapsed = Time.now - t0
|
291
|
+
assert (0.05..0.15).include? elapsed
|
292
|
+
|
293
|
+
f = spin { sleep }
|
294
|
+
snooze
|
295
|
+
assert f.running?
|
296
|
+
snooze
|
297
|
+
assert f.running?
|
298
|
+
f.stop
|
299
|
+
snooze
|
300
|
+
assert !f.running?
|
301
|
+
end
|
302
|
+
|
303
|
+
def test_snooze
|
304
|
+
values = []
|
305
|
+
3.times.map do |i|
|
306
|
+
spin do
|
307
|
+
3.times do
|
308
|
+
snooze
|
309
|
+
values << i
|
310
|
+
end
|
311
|
+
suspend
|
312
|
+
end
|
313
|
+
end
|
314
|
+
suspend
|
315
|
+
|
316
|
+
assert_equal [0, 1, 2, 0, 1, 2, 0, 1, 2], values
|
317
|
+
end
|
318
|
+
|
319
|
+
def test_defer
|
320
|
+
values = []
|
321
|
+
spin { values << 1 }
|
322
|
+
spin { values << 2 }
|
323
|
+
spin { values << 3 }
|
324
|
+
suspend
|
325
|
+
|
326
|
+
assert_equal [1, 2, 3], values
|
327
|
+
end
|
328
|
+
|
329
|
+
def test_suspend
|
330
|
+
values = []
|
331
|
+
spin do
|
332
|
+
values << :foo
|
333
|
+
suspend
|
334
|
+
end
|
335
|
+
suspend
|
336
|
+
|
337
|
+
assert_equal [:foo], values
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_schedule_and_suspend
|
341
|
+
values = []
|
342
|
+
3.times.map do |i|
|
343
|
+
spin do
|
344
|
+
values << i
|
345
|
+
suspend
|
346
|
+
end
|
347
|
+
end
|
348
|
+
suspend
|
349
|
+
|
350
|
+
assert_equal [0, 1, 2], values
|
351
|
+
end
|
352
|
+
end
|
data/test/test_io.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class IOTest < MiniTest::Test
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
@i, @o = IO.pipe
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_that_io_op_yields_to_other_fibers
|
12
|
+
count = 0
|
13
|
+
msg = nil
|
14
|
+
[
|
15
|
+
spin do
|
16
|
+
@o.write('hello')
|
17
|
+
@o.close
|
18
|
+
end,
|
19
|
+
|
20
|
+
spin do
|
21
|
+
while count < 5
|
22
|
+
sleep 0.01
|
23
|
+
count += 1
|
24
|
+
end
|
25
|
+
end,
|
26
|
+
|
27
|
+
spin { msg = @i.read }
|
28
|
+
].each(&:await)
|
29
|
+
assert_equal 5, count
|
30
|
+
assert_equal 'hello', msg
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_write_multiple_arguments
|
34
|
+
i, o = IO.pipe
|
35
|
+
count = o.write('a', 'b', "\n", 'c')
|
36
|
+
assert_equal 4, count
|
37
|
+
o.close
|
38
|
+
assert_equal "ab\nc", i.read
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_that_double_chevron_method_returns_io
|
42
|
+
assert_equal @o, @o << 'foo'
|
43
|
+
|
44
|
+
@o << 'bar' << 'baz'
|
45
|
+
@o.close
|
46
|
+
assert_equal 'foobarbaz', @i.read
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_wait_io
|
50
|
+
results = []
|
51
|
+
i, o = IO.pipe
|
52
|
+
f = spin do
|
53
|
+
loop do
|
54
|
+
result = i.orig_read_nonblock(8192, exception: false)
|
55
|
+
results << result
|
56
|
+
case result
|
57
|
+
when :wait_readable
|
58
|
+
Thread.current.agent.wait_io(i, false)
|
59
|
+
else
|
60
|
+
break result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
snooze
|
66
|
+
o.write('foo')
|
67
|
+
o.close
|
68
|
+
|
69
|
+
result = f.await
|
70
|
+
|
71
|
+
assert_equal 'foo', f.await
|
72
|
+
assert_equal [:wait_readable, 'foo'], results
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_readpartial
|
76
|
+
i, o = IO.pipe
|
77
|
+
|
78
|
+
o << 'hi'
|
79
|
+
assert_equal 'hi', i.readpartial(3)
|
80
|
+
|
81
|
+
o << 'hi'
|
82
|
+
assert_equal 'h', i.readpartial(1)
|
83
|
+
assert_equal 'i', i.readpartial(1)
|
84
|
+
|
85
|
+
spin {
|
86
|
+
sleep 0.01
|
87
|
+
o << 'hi'
|
88
|
+
}
|
89
|
+
assert_equal 'hi', i.readpartial(2)
|
90
|
+
o.close
|
91
|
+
|
92
|
+
assert_raises(EOFError) { i.readpartial(1) }
|
93
|
+
end
|
94
|
+
|
95
|
+
# see https://github.com/digital-fabric/polyphony/issues/30
|
96
|
+
def test_reopened_tempfile
|
97
|
+
file = Tempfile.new
|
98
|
+
file << 'hello: world'
|
99
|
+
file.close
|
100
|
+
|
101
|
+
buf = nil
|
102
|
+
File.open(file, 'r:bom|utf-8') do |f|
|
103
|
+
buf = f.read(16384)
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_equal 'hello: world', buf
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class IOClassMethodsTest < MiniTest::Test
|
111
|
+
def test_binread
|
112
|
+
s = IO.binread(__FILE__)
|
113
|
+
assert_kind_of String, s
|
114
|
+
assert !s.empty?
|
115
|
+
assert_equal IO.orig_binread(__FILE__), s
|
116
|
+
|
117
|
+
s = IO.binread(__FILE__, 100)
|
118
|
+
assert_equal 100, s.bytesize
|
119
|
+
assert_equal IO.orig_binread(__FILE__, 100), s
|
120
|
+
|
121
|
+
s = IO.binread(__FILE__, 100, 2)
|
122
|
+
assert_equal 100, s.bytesize
|
123
|
+
assert_equal 'frozen', s[0..5]
|
124
|
+
end
|
125
|
+
|
126
|
+
BIN_DATA = "\x00\x01\x02\x03"
|
127
|
+
|
128
|
+
def test_binwrite
|
129
|
+
fn = '/tmp/test_binwrite'
|
130
|
+
FileUtils.rm(fn) rescue nil
|
131
|
+
|
132
|
+
len = IO.binwrite(fn, BIN_DATA)
|
133
|
+
assert_equal 4, len
|
134
|
+
s = IO.binread(fn)
|
135
|
+
assert_equal BIN_DATA, s
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_foreach
|
139
|
+
skip "IO.foreach is not yet implemented"
|
140
|
+
lines = []
|
141
|
+
IO.foreach(__FILE__) { |l| lines << l }
|
142
|
+
assert_equal "# frozen_string_literal: true\n", lines[0]
|
143
|
+
assert_equal "end\n", lines[-1]
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_read_class_method
|
147
|
+
s = IO.read(__FILE__)
|
148
|
+
assert_kind_of String, s
|
149
|
+
assert(!s.empty?)
|
150
|
+
assert_equal IO.orig_read(__FILE__), s
|
151
|
+
|
152
|
+
s = IO.read(__FILE__, 100)
|
153
|
+
assert_equal 100, s.bytesize
|
154
|
+
assert_equal IO.orig_read(__FILE__, 100), s
|
155
|
+
|
156
|
+
s = IO.read(__FILE__, 100, 2)
|
157
|
+
assert_equal 100, s.bytesize
|
158
|
+
assert_equal 'frozen', s[0..5]
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_readlines
|
162
|
+
lines = IO.readlines(__FILE__)
|
163
|
+
assert_equal "# frozen_string_literal: true\n", lines[0]
|
164
|
+
assert_equal "end\n", lines[-1]
|
165
|
+
end
|
166
|
+
|
167
|
+
WRITE_DATA = "foo\nbar קוקו"
|
168
|
+
|
169
|
+
def test_write_class_method
|
170
|
+
fn = '/tmp/test_write'
|
171
|
+
FileUtils.rm(fn) rescue nil
|
172
|
+
|
173
|
+
len = IO.write(fn, WRITE_DATA)
|
174
|
+
assert_equal WRITE_DATA.bytesize, len
|
175
|
+
s = IO.read(fn)
|
176
|
+
assert_equal WRITE_DATA, s
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_popen
|
180
|
+
counter = 0
|
181
|
+
timer = spin { throttled_loop(200) { counter += 1 } }
|
182
|
+
|
183
|
+
IO.popen('sleep 0.05') { |io| io.read(8192) }
|
184
|
+
assert(counter >= 5)
|
185
|
+
|
186
|
+
result = nil
|
187
|
+
IO.popen('echo "foo"') { |io| result = io.read(8192) }
|
188
|
+
assert_equal "foo\n", result
|
189
|
+
ensure
|
190
|
+
timer&.stop
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_kernel_gets
|
194
|
+
counter = 0
|
195
|
+
timer = spin { throttled_loop(200) { counter += 1 } }
|
196
|
+
|
197
|
+
i, o = IO.pipe
|
198
|
+
orig_stdin = $stdin
|
199
|
+
$stdin = i
|
200
|
+
spin do
|
201
|
+
sleep 0.01
|
202
|
+
o.puts 'foo'
|
203
|
+
o.close
|
204
|
+
end
|
205
|
+
|
206
|
+
assert(counter >= 0)
|
207
|
+
assert_equal "foo\n", gets
|
208
|
+
ensure
|
209
|
+
$stdin = orig_stdin
|
210
|
+
timer&.stop
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_kernel_gets_with_argv
|
214
|
+
ARGV << __FILE__
|
215
|
+
|
216
|
+
s = StringIO.new(IO.orig_read(__FILE__))
|
217
|
+
|
218
|
+
while (l = s.gets)
|
219
|
+
assert_equal l, gets
|
220
|
+
end
|
221
|
+
ensure
|
222
|
+
ARGV.delete __FILE__
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_kernel_puts
|
226
|
+
orig_stdout = $stdout
|
227
|
+
o = eg(
|
228
|
+
'@buf': +'',
|
229
|
+
write: ->(*args) { args.each { |a| @buf << a } },
|
230
|
+
flush: -> {},
|
231
|
+
buf: -> { @buf }
|
232
|
+
)
|
233
|
+
|
234
|
+
$stdout = o
|
235
|
+
|
236
|
+
puts 'foobar'
|
237
|
+
assert_equal "foobar\n", o.buf
|
238
|
+
ensure
|
239
|
+
$stdout = orig_stdout
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_read_large_file
|
243
|
+
fn = '/tmp/test.txt'
|
244
|
+
File.open(fn, 'w') { |f| f << ('*' * 1e6) }
|
245
|
+
s = IO.read(fn)
|
246
|
+
assert_equal 1e6, s.bytesize
|
247
|
+
assert s == IO.orig_read(fn)
|
248
|
+
end
|
249
|
+
end
|