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.
Files changed (221) hide show
  1. checksums.yaml +7 -0
  2. data/.gitbook.yaml +4 -0
  3. data/.github/workflows/test.yml +29 -0
  4. data/.gitignore +59 -0
  5. data/.rubocop.yml +175 -0
  6. data/CHANGELOG.md +393 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +141 -0
  9. data/LICENSE +21 -0
  10. data/README.md +51 -0
  11. data/Rakefile +26 -0
  12. data/TODO.md +201 -0
  13. data/bin/polyphony-debug +87 -0
  14. data/docs/_config.yml +64 -0
  15. data/docs/_includes/head.html +40 -0
  16. data/docs/_includes/title.html +1 -0
  17. data/docs/_sass/custom/custom.scss +10 -0
  18. data/docs/_sass/overrides.scss +0 -0
  19. data/docs/_user-guide/all-about-timers.md +126 -0
  20. data/docs/_user-guide/index.md +9 -0
  21. data/docs/_user-guide/web-server.md +136 -0
  22. data/docs/api-reference/exception.md +27 -0
  23. data/docs/api-reference/fiber.md +425 -0
  24. data/docs/api-reference/index.md +9 -0
  25. data/docs/api-reference/io.md +36 -0
  26. data/docs/api-reference/object.md +99 -0
  27. data/docs/api-reference/polyphony-baseexception.md +33 -0
  28. data/docs/api-reference/polyphony-cancel.md +26 -0
  29. data/docs/api-reference/polyphony-moveon.md +24 -0
  30. data/docs/api-reference/polyphony-net.md +20 -0
  31. data/docs/api-reference/polyphony-process.md +28 -0
  32. data/docs/api-reference/polyphony-resourcepool.md +59 -0
  33. data/docs/api-reference/polyphony-restart.md +18 -0
  34. data/docs/api-reference/polyphony-terminate.md +18 -0
  35. data/docs/api-reference/polyphony-threadpool.md +67 -0
  36. data/docs/api-reference/polyphony-throttler.md +77 -0
  37. data/docs/api-reference/polyphony.md +36 -0
  38. data/docs/api-reference/thread.md +88 -0
  39. data/docs/assets/img/echo-fibers.svg +1 -0
  40. data/docs/assets/img/sleeping-fiber.svg +1 -0
  41. data/docs/faq.md +195 -0
  42. data/docs/favicon.ico +0 -0
  43. data/docs/getting-started/index.md +10 -0
  44. data/docs/getting-started/installing.md +34 -0
  45. data/docs/getting-started/overview.md +486 -0
  46. data/docs/getting-started/tutorial.md +359 -0
  47. data/docs/index.md +94 -0
  48. data/docs/main-concepts/concurrency.md +151 -0
  49. data/docs/main-concepts/design-principles.md +161 -0
  50. data/docs/main-concepts/exception-handling.md +291 -0
  51. data/docs/main-concepts/extending.md +89 -0
  52. data/docs/main-concepts/fiber-scheduling.md +197 -0
  53. data/docs/main-concepts/index.md +9 -0
  54. data/docs/polyphony-logo.png +0 -0
  55. data/examples/adapters/concurrent-ruby.rb +9 -0
  56. data/examples/adapters/pg_client.rb +36 -0
  57. data/examples/adapters/pg_notify.rb +35 -0
  58. data/examples/adapters/pg_pool.rb +43 -0
  59. data/examples/adapters/pg_transaction.rb +31 -0
  60. data/examples/adapters/redis_blpop.rb +12 -0
  61. data/examples/adapters/redis_channels.rb +122 -0
  62. data/examples/adapters/redis_client.rb +19 -0
  63. data/examples/adapters/redis_pubsub.rb +26 -0
  64. data/examples/adapters/redis_pubsub_perf.rb +68 -0
  65. data/examples/core/01-spinning-up-fibers.rb +18 -0
  66. data/examples/core/02-awaiting-fibers.rb +20 -0
  67. data/examples/core/03-interrupting.rb +39 -0
  68. data/examples/core/04-handling-signals.rb +19 -0
  69. data/examples/core/xx-agent.rb +102 -0
  70. data/examples/core/xx-at_exit.rb +29 -0
  71. data/examples/core/xx-caller.rb +12 -0
  72. data/examples/core/xx-channels.rb +45 -0
  73. data/examples/core/xx-daemon.rb +14 -0
  74. data/examples/core/xx-deadlock.rb +8 -0
  75. data/examples/core/xx-deferring-an-operation.rb +14 -0
  76. data/examples/core/xx-erlang-style-genserver.rb +81 -0
  77. data/examples/core/xx-exception-backtrace.rb +40 -0
  78. data/examples/core/xx-fork-cleanup.rb +22 -0
  79. data/examples/core/xx-fork-spin.rb +42 -0
  80. data/examples/core/xx-fork-terminate.rb +27 -0
  81. data/examples/core/xx-forking.rb +24 -0
  82. data/examples/core/xx-move_on.rb +23 -0
  83. data/examples/core/xx-pingpong.rb +18 -0
  84. data/examples/core/xx-queue-async.rb +120 -0
  85. data/examples/core/xx-readpartial.rb +18 -0
  86. data/examples/core/xx-recurrent-timer.rb +12 -0
  87. data/examples/core/xx-resource_delegate.rb +31 -0
  88. data/examples/core/xx-signals.rb +16 -0
  89. data/examples/core/xx-sleep-forever.rb +9 -0
  90. data/examples/core/xx-sleeping.rb +25 -0
  91. data/examples/core/xx-snooze-starve.rb +16 -0
  92. data/examples/core/xx-spin-fork.rb +49 -0
  93. data/examples/core/xx-spin_error_backtrace.rb +33 -0
  94. data/examples/core/xx-state-machine.rb +51 -0
  95. data/examples/core/xx-stop.rb +20 -0
  96. data/examples/core/xx-supervise-process.rb +30 -0
  97. data/examples/core/xx-supervisors.rb +21 -0
  98. data/examples/core/xx-thread-selector-sleep.rb +51 -0
  99. data/examples/core/xx-thread-selector-snooze.rb +46 -0
  100. data/examples/core/xx-thread-sleep.rb +17 -0
  101. data/examples/core/xx-thread-snooze.rb +34 -0
  102. data/examples/core/xx-thread_pool.rb +17 -0
  103. data/examples/core/xx-throttling.rb +18 -0
  104. data/examples/core/xx-timeout.rb +10 -0
  105. data/examples/core/xx-timer-gc.rb +17 -0
  106. data/examples/core/xx-trace.rb +79 -0
  107. data/examples/core/xx-using-a-mutex.rb +21 -0
  108. data/examples/core/xx-worker-thread.rb +30 -0
  109. data/examples/io/tunnel.rb +48 -0
  110. data/examples/io/xx-backticks.rb +11 -0
  111. data/examples/io/xx-echo_client.rb +25 -0
  112. data/examples/io/xx-echo_client_from_stdin.rb +21 -0
  113. data/examples/io/xx-echo_pipe.rb +16 -0
  114. data/examples/io/xx-echo_server.rb +17 -0
  115. data/examples/io/xx-echo_server_with_timeout.rb +34 -0
  116. data/examples/io/xx-echo_stdin.rb +14 -0
  117. data/examples/io/xx-happy-eyeballs.rb +36 -0
  118. data/examples/io/xx-httparty.rb +38 -0
  119. data/examples/io/xx-irb.rb +17 -0
  120. data/examples/io/xx-net-http.rb +15 -0
  121. data/examples/io/xx-open.rb +16 -0
  122. data/examples/io/xx-switch.rb +15 -0
  123. data/examples/io/xx-system.rb +11 -0
  124. data/examples/io/xx-tcpserver.rb +15 -0
  125. data/examples/io/xx-tcpsocket.rb +18 -0
  126. data/examples/io/xx-zip.rb +19 -0
  127. data/examples/performance/fiber_transfer.rb +47 -0
  128. data/examples/performance/fs_read.rb +38 -0
  129. data/examples/performance/mem-usage.rb +56 -0
  130. data/examples/performance/messaging.rb +29 -0
  131. data/examples/performance/multi_snooze.rb +33 -0
  132. data/examples/performance/snooze.rb +39 -0
  133. data/examples/performance/snooze_raw.rb +39 -0
  134. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +74 -0
  135. data/examples/performance/thread-vs-fiber/polyphony_server.rb +45 -0
  136. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  137. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  138. data/examples/performance/thread-vs-fiber/xx-httparty_multi.rb +36 -0
  139. data/examples/performance/thread-vs-fiber/xx-httparty_threaded.rb +29 -0
  140. data/examples/performance/thread_pool_perf.rb +63 -0
  141. data/examples/performance/xx-array.rb +11 -0
  142. data/examples/performance/xx-fiber-switch.rb +9 -0
  143. data/examples/performance/xx-snooze.rb +15 -0
  144. data/examples/xx-spin.rb +32 -0
  145. data/ext/libev/Changes +548 -0
  146. data/ext/libev/LICENSE +37 -0
  147. data/ext/libev/README +59 -0
  148. data/ext/libev/README.embed +3 -0
  149. data/ext/libev/ev.c +5279 -0
  150. data/ext/libev/ev.h +856 -0
  151. data/ext/libev/ev_epoll.c +296 -0
  152. data/ext/libev/ev_kqueue.c +224 -0
  153. data/ext/libev/ev_linuxaio.c +642 -0
  154. data/ext/libev/ev_poll.c +156 -0
  155. data/ext/libev/ev_port.c +192 -0
  156. data/ext/libev/ev_select.c +316 -0
  157. data/ext/libev/ev_vars.h +215 -0
  158. data/ext/libev/ev_win32.c +162 -0
  159. data/ext/libev/ev_wrap.h +216 -0
  160. data/ext/libev/test_libev_win32.c +123 -0
  161. data/ext/polyphony/extconf.rb +20 -0
  162. data/ext/polyphony/fiber.c +109 -0
  163. data/ext/polyphony/libev.c +2 -0
  164. data/ext/polyphony/libev.h +9 -0
  165. data/ext/polyphony/libev_agent.c +882 -0
  166. data/ext/polyphony/polyphony.c +71 -0
  167. data/ext/polyphony/polyphony.h +97 -0
  168. data/ext/polyphony/polyphony_ext.c +21 -0
  169. data/ext/polyphony/queue.c +168 -0
  170. data/ext/polyphony/ring_buffer.c +96 -0
  171. data/ext/polyphony/ring_buffer.h +28 -0
  172. data/ext/polyphony/thread.c +208 -0
  173. data/ext/polyphony/tracing.c +11 -0
  174. data/lib/polyphony.rb +136 -0
  175. data/lib/polyphony/adapters/fs.rb +19 -0
  176. data/lib/polyphony/adapters/irb.rb +52 -0
  177. data/lib/polyphony/adapters/postgres.rb +110 -0
  178. data/lib/polyphony/adapters/process.rb +33 -0
  179. data/lib/polyphony/adapters/redis.rb +67 -0
  180. data/lib/polyphony/adapters/trace.rb +138 -0
  181. data/lib/polyphony/core/channel.rb +46 -0
  182. data/lib/polyphony/core/exceptions.rb +36 -0
  183. data/lib/polyphony/core/global_api.rb +124 -0
  184. data/lib/polyphony/core/resource_pool.rb +117 -0
  185. data/lib/polyphony/core/sync.rb +21 -0
  186. data/lib/polyphony/core/thread_pool.rb +64 -0
  187. data/lib/polyphony/core/throttler.rb +41 -0
  188. data/lib/polyphony/event.rb +17 -0
  189. data/lib/polyphony/extensions/core.rb +174 -0
  190. data/lib/polyphony/extensions/fiber.rb +379 -0
  191. data/lib/polyphony/extensions/io.rb +221 -0
  192. data/lib/polyphony/extensions/openssl.rb +81 -0
  193. data/lib/polyphony/extensions/socket.rb +150 -0
  194. data/lib/polyphony/extensions/thread.rb +108 -0
  195. data/lib/polyphony/net.rb +77 -0
  196. data/lib/polyphony/version.rb +5 -0
  197. data/polyphony.gemspec +40 -0
  198. data/test/coverage.rb +54 -0
  199. data/test/eg.rb +27 -0
  200. data/test/helper.rb +56 -0
  201. data/test/q.rb +24 -0
  202. data/test/run.rb +5 -0
  203. data/test/stress.rb +25 -0
  204. data/test/test_agent.rb +130 -0
  205. data/test/test_event.rb +59 -0
  206. data/test/test_ext.rb +196 -0
  207. data/test/test_fiber.rb +988 -0
  208. data/test/test_global_api.rb +352 -0
  209. data/test/test_io.rb +249 -0
  210. data/test/test_kernel.rb +57 -0
  211. data/test/test_process_supervision.rb +46 -0
  212. data/test/test_queue.rb +112 -0
  213. data/test/test_resource_pool.rb +138 -0
  214. data/test/test_signal.rb +100 -0
  215. data/test/test_socket.rb +34 -0
  216. data/test/test_supervise.rb +103 -0
  217. data/test/test_thread.rb +170 -0
  218. data/test/test_thread_pool.rb +101 -0
  219. data/test/test_throttler.rb +50 -0
  220. data/test/test_trace.rb +68 -0
  221. 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
@@ -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