summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb293
1 files changed, 293 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb
new file mode 100644
index 0000000..ab0a92f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb
@@ -0,0 +1,293 @@
+
+require 'em_test_helper'
+
+
+
+class TestSpawn < Test::Unit::TestCase
+
+ # Spawn a process that simply stops the reactor.
+ # Assert that the notification runs after the block that calls it.
+ #
+ def test_stop
+ x = nil
+ EM.run {
+ s = EM.spawn {EM.stop}
+ s.notify
+ x = true
+ }
+ assert x
+ end
+
+
+ # Pass a parameter to a spawned process.
+ #
+ def test_parms
+ val = 5
+ EM.run {
+ s = EM.spawn {|v| val *= v; EM.stop}
+ s.notify 3
+ }
+ assert_equal( 15, val )
+ end
+
+ # Pass multiple parameters to a spawned process.
+ #
+ def test_multiparms
+ val = 5
+ EM.run {
+ s = EM.spawn {|v1,v2| val *= (v1 + v2); EM.stop}
+ s.notify 3,4
+ }
+ assert_equal( 35, val )
+ end
+
+
+ # This test demonstrates that a notification does not happen immediately,
+ # but rather is scheduled sometime after the current code path completes.
+ #
+ def test_race
+ x = 0
+ EM.run {
+ s = EM.spawn {x *= 2; EM.stop}
+ s.notify
+ x = 2
+ }
+ assert_equal( 4, x)
+ end
+
+
+ # Spawn a process and notify it 25 times to run fibonacci
+ # on a pair of global variables.
+ #
+ def test_fibonacci
+ x = 1
+ y = 1
+ EM.run {
+ s = EM.spawn {x,y = y,x+y}
+ 25.times {s.notify}
+
+ t = EM.spawn {EM.stop}
+ t.notify
+ }
+ assert_equal( 121393, x)
+ assert_equal( 196418, y)
+ end
+
+ # This one spawns 25 distinct processes, and notifies each one once,
+ # rather than notifying a single process 25 times.
+ #
+ def test_another_fibonacci
+ x = 1
+ y = 1
+ EM.run {
+ 25.times {
+ s = EM.spawn {x,y = y,x+y}
+ s.notify
+ }
+
+ t = EM.spawn {EM.stop}
+ t.notify
+ }
+ assert_equal( 121393, x)
+ assert_equal( 196418, y)
+ end
+
+
+ # Make a chain of processes that notify each other in turn
+ # with intermediate fibonacci results. The final process in
+ # the chain stops the loop and returns the result.
+ #
+ def test_fibonacci_chain
+ a,b = nil
+
+ EM.run {
+ nextpid = EM.spawn {|x,y|
+ a,b = x,y
+ EM.stop
+ }
+
+ 25.times {
+ n = nextpid
+ nextpid = EM.spawn {|x,y| n.notify( y, x+y )}
+ }
+
+ nextpid.notify( 1, 1 )
+ }
+
+ assert_equal( 121393, a)
+ assert_equal( 196418, b)
+ end
+
+
+ # EM#yield gives a spawed process to yield control to other processes
+ # (in other words, to stop running), and to specify a different code block
+ # that will run on its next notification.
+ #
+ def test_yield
+ a = 0
+ EM.run {
+ n = EM.spawn {
+ a += 10
+ EM.yield {
+ a += 20
+ EM.yield {
+ a += 30
+ EM.stop
+ }
+ }
+ }
+ n.notify
+ n.notify
+ n.notify
+ }
+ assert_equal( 60, a )
+ end
+
+ # EM#yield_and_notify behaves like EM#yield, except that it also notifies the
+ # yielding process. This may sound trivial, since the yield block will run very
+ # shortly after with no action by the program, but this actually can be very useful,
+ # because it causes the reactor core to execute once before the yielding process
+ # gets control back. So it can be used to allow heavily-used network connections
+ # to clear buffers, or allow other processes to process their notifications.
+ #
+ # Notice in this test code that only a simple notify is needed at the bottom
+ # of the initial block. Even so, all of the yielded blocks will execute.
+ #
+ def test_yield_and_notify
+ a = 0
+ EM.run {
+ n = EM.spawn {
+ a += 10
+ EM.yield_and_notify {
+ a += 20
+ EM.yield_and_notify {
+ a += 30
+ EM.stop
+ }
+ }
+ }
+ n.notify
+ }
+ assert_equal( 60, a )
+ end
+
+ # resume is an alias for notify.
+ #
+ def test_resume
+ EM.run {
+ n = EM.spawn {EM.stop}
+ n.resume
+ }
+ assert true
+ end
+
+ # run is an idiomatic alias for notify.
+ #
+ def test_run
+ EM.run {
+ (EM.spawn {EM.stop}).run
+ }
+ assert true
+ end
+
+
+ # Clones the ping-pong example from the Erlang tutorial, in much less code.
+ # Illustrates that a spawned block executes in the context of a SpawnableObject.
+ # (Meaning, we can pass self as a parameter to another process that can then
+ # notify us.)
+ #
+ def test_ping_pong
+ n_pongs = 0
+ EM.run {
+ pong = EM.spawn {|x, ping|
+ n_pongs += 1
+ ping.notify( x-1 )
+ }
+ ping = EM.spawn {|x|
+ if x > 0
+ pong.notify x, self
+ else
+ EM.stop
+ end
+ }
+ ping.notify 3
+ }
+ assert_equal( 3, n_pongs )
+ end
+
+ # Illustrates that you can call notify inside a notification, and it will cause
+ # the currently-executing process to be re-notified. Of course, the new notification
+ # won't run until sometime after the current one completes.
+ #
+ def test_self_notify
+ n = 0
+ EM.run {
+ pid = EM.spawn {|x|
+ if x > 0
+ n += x
+ notify( x-1 )
+ else
+ EM.stop
+ end
+ }
+ pid.notify 3
+ }
+ assert_equal( 6, n )
+ end
+
+
+ # Illustrates that the block passed to #spawn executes in the context of a
+ # SpawnedProcess object, NOT in the local context. This can often be deceptive.
+ #
+ class BlockScopeTest
+ attr_reader :var
+ def run
+ # The following line correctly raises a NameError.
+ # The problem is that the programmer expected the spawned block to
+ # execute in the local context, but it doesn't.
+ #
+ # (EM.spawn { do_something }).notify ### NO! BAD!
+
+
+
+ # The following line correctly passes self as a parameter to the
+ # notified process.
+ #
+ (EM.spawn {|obj| obj.do_something }).notify(self)
+
+
+
+ # Here's another way to do it. This works because "myself" is bound
+ # in the local scope, unlike "self," so the spawned block sees it.
+ #
+ myself = self
+ (EM.spawn { myself.do_something }).notify
+
+
+
+ # And we end the loop.
+ # This is a tangential point, but observe that #notify never blocks.
+ # It merely appends a message to the internal queue of a spawned process
+ # and returns. As it turns out, the reactor processes notifications for ALL
+ # spawned processes in the order that #notify is called. So there is a
+ # reasonable expectation that the process which stops the reactor will
+ # execute after the previous ones in this method. HOWEVER, this is NOT
+ # a documented behavior and is subject to change.
+ #
+ (EM.spawn {EM.stop}).notify
+ end
+ def do_something
+ @var ||= 0
+ @var += 100
+ end
+ end
+
+ def test_block_scope
+ bs = BlockScopeTest.new
+ EM.run {
+ bs.run
+ }
+ assert_equal( 200, bs.var )
+ end
+
+end